Skip to content

Commit 126b32b

Browse files
committed
add Exemplar support for OpenTelemetry tracing (review)
1 parent ef6e57f commit 126b32b

File tree

33 files changed

+404
-209
lines changed

33 files changed

+404
-209
lines changed

benchmark/README.md

+9
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ The main outcomes of the benchmarks:
1212
* Simpleclient Histograms are 10-100X faster than Codahale and original client Summaries.
1313
* Simpleclient `Gauge.Child.set` is relatively slow, especially when done concurrently.
1414
* Label lookups in both Prometheus clients are relatively slow.
15+
* The `System.currentTimeMillis()` call in the `DefaultExemplarSampler` takes about 17ns on Linux.
1516

1617
Accordingly, in terms of client instrumentation performance I suggest the following:
1718
* It's cheap to extensively instrument your code with Simpleclient Counters/Gauges/Summaries without labels, or Codahale Counters.
@@ -158,4 +159,12 @@ The closest to the original client's `Summary` is Codahale's
158159
Note the high error bars for the original client, it got slower with each iteration
159160
so I suspect a flaw in the test setup.
160161

162+
### Exemplars
161163

164+
The `ExemplarsBenchmark` was run on a Linux laptop with a 4 Core Intel i7-8550U CPU with OpenJDK 1.8.0_282-b08.
165+
166+
java -jar target/benchmarks.jar ExemplarsBenchmark
167+
Benchmark Mode Samples Score Error Units
168+
i.p.b.ExemplarsBenchmark.testCounter avgt 200 28.252 ± 0.312 ns/op
169+
i.p.b.ExemplarsBenchmark.testCounterWithExemplars avgt 200 46.042 ± 0.781 ns/op
170+
i.p.b.ExemplarsBenchmark.testCounterWithoutExemplars avgt 200 29.322 ± 0.475 ns/op

benchmark/pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
<dependency>
5050
<groupId>io.prometheus</groupId>
5151
<artifactId>simpleclient</artifactId>
52-
<version>${project.version}</version>
52+
<version>0.10.1-SNAPSHOT</version>
5353
</dependency>
5454
<dependency>
5555
<groupId>com.codahale.metrics</groupId>

benchmark/src/main/java/io/prometheus/benchmark/ExemplarsBenchmark.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package io.prometheus.benchmark;
22

33
import io.prometheus.client.Counter;
4-
import io.prometheus.client.exemplars.impl.DefaultExemplarSampler;
5-
import io.prometheus.client.exemplars.tracer.common.SpanContext;
4+
import io.prometheus.client.exemplars.DefaultExemplarSampler;
5+
import io.prometheus.client.exemplars.tracer.common.SpanContextSupplier;
66
import org.openjdk.jmh.annotations.Benchmark;
77
import org.openjdk.jmh.annotations.BenchmarkMode;
88
import org.openjdk.jmh.annotations.Mode;
@@ -33,7 +33,7 @@ public void setup() {
3333
.name("counter_with_exemplars_total")
3434
.help("Total number of requests.")
3535
.labelNames("path")
36-
.withExemplars(new DefaultExemplarSampler(new MockSpanContext()))
36+
.withExemplars(new DefaultExemplarSampler(new MockSpanContextSupplier()))
3737
.create();
3838

3939
counterWithoutExemplars = Counter.build()
@@ -65,7 +65,7 @@ public void testCounterWithoutExemplars() {
6565
counterWithoutExemplars.labels("test").inc();
6666
}
6767

68-
private static class MockSpanContext implements SpanContext {
68+
private static class MockSpanContextSupplier implements SpanContextSupplier {
6969

7070
@Override
7171
public String getTraceId() {

exemplars_parent/exemplars/src/main/java/io/prometheus/client/exemplars/api/ExemplarSampler.java

-4
This file was deleted.

exemplars_parent/exemplars/src/test/java/io/prometheus/client/exemplars/api/ExemplarTest.java

-86
This file was deleted.

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
</developers>
4444

4545
<modules>
46-
<module>exemplars_parent</module>
46+
<module>tracer_parent</module>
4747
<module>simpleclient</module>
4848
<module>simpleclient_common</module>
4949
<module>simpleclient_caffeine</module>

simpleclient/pom.xml

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@
3737
<dependencies>
3838
<!-- The simpleclient has no required 3rd party runtime dependencies by design, so that
3939
it can be included into any other project without having to worry about conflicts.
40-
All transitive dependencies of exemplars are scoped as provided and optional at runtime. -->
40+
All transitive dependencies of tracer libraries are scoped as provided and optional at runtime. -->
4141
<dependency>
4242
<groupId>io.prometheus</groupId>
43-
<artifactId>exemplars</artifactId>
43+
<artifactId>tracer</artifactId>
4444
<version>${project.version}</version>
4545
</dependency>
4646
<!-- Test Dependencies Follow -->

simpleclient/src/main/java/io/prometheus/client/CKMSQuantiles.java

-1
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,6 @@ private class Item {
258258
public final double value;
259259
public int g;
260260
public final int delta;
261-
// TODO Exemplars here would be cool :)
262261

263262
public Item(double value, int lower_delta, int delta) {
264263
this.value = value;

simpleclient/src/main/java/io/prometheus/client/Collector.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
package io.prometheus.client;
33

4-
import io.prometheus.client.exemplars.api.Exemplar;
4+
import io.prometheus.client.exemplars.Exemplar;
55

66
import java.util.ArrayList;
77
import java.util.List;

simpleclient/src/main/java/io/prometheus/client/Counter.java

+13-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package io.prometheus.client;
22

3-
import io.prometheus.client.exemplars.api.CounterExemplarSampler;
4-
import io.prometheus.client.exemplars.api.Exemplar;
5-
import io.prometheus.client.exemplars.api.ExemplarConfig;
6-
import io.prometheus.client.exemplars.api.Value;
3+
import io.prometheus.client.exemplars.CounterExemplarSampler;
4+
import io.prometheus.client.exemplars.Exemplar;
5+
import io.prometheus.client.exemplars.ExemplarConfig;
6+
import io.prometheus.client.exemplars.Value;
77

88
import java.util.ArrayList;
99
import java.util.Collections;
@@ -101,6 +101,9 @@ public Counter create() {
101101
return new Counter(this);
102102
}
103103

104+
/**
105+
* Enable exemplars and provide a custom {@link CounterExemplarSampler}.
106+
*/
104107
public Builder withExemplars(CounterExemplarSampler exemplarSampler) {
105108
if (exemplarSampler == null) {
106109
throw new NullPointerException();
@@ -109,10 +112,16 @@ public Builder withExemplars(CounterExemplarSampler exemplarSampler) {
109112
return this;
110113
}
111114

115+
/**
116+
* Enable exemplars using the default {@link CounterExemplarSampler} as configured in {@link ExemplarConfig}.
117+
*/
112118
public Builder withExemplars() {
113119
return withExemplars(ExemplarConfig.getDefaultExemplarSampler());
114120
}
115121

122+
/**
123+
* Disable exemplars.
124+
*/
116125
public Builder withoutExemplars() {
117126
return withExemplars(ExemplarConfig.getNoopExemplarSampler());
118127
}

simpleclient/src/main/java/io/prometheus/client/Histogram.java

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package io.prometheus.client;
22

3-
import io.prometheus.client.exemplars.api.Exemplar;
4-
import io.prometheus.client.exemplars.api.ExemplarConfig;
5-
import io.prometheus.client.exemplars.api.HistogramExemplarSampler;
3+
import io.prometheus.client.exemplars.Exemplar;
4+
import io.prometheus.client.exemplars.ExemplarConfig;
5+
import io.prometheus.client.exemplars.HistogramExemplarSampler;
66

77
import java.io.Closeable;
88
import java.util.ArrayList;
@@ -138,6 +138,9 @@ public Builder exponentialBuckets(double start, double factor, int count) {
138138
return this;
139139
}
140140

141+
/**
142+
* Enable exemplars and provide a custom {@link HistogramExemplarSampler}.
143+
*/
141144
public Builder withExemplars(HistogramExemplarSampler exemplarSampler) {
142145
if (exemplarSampler == null) {
143146
throw new NullPointerException();
@@ -146,10 +149,16 @@ public Builder withExemplars(HistogramExemplarSampler exemplarSampler) {
146149
return this;
147150
}
148151

152+
/**
153+
* Enable exemplars using the default {@link HistogramExemplarSampler} as configured in {@link ExemplarConfig}.
154+
*/
149155
public Builder withExemplars() {
150156
return withExemplars(ExemplarConfig.getDefaultExemplarSampler());
151157
}
152158

159+
/**
160+
* Disable exemplars.
161+
*/
153162
public Builder withoutExemplars() {
154163
return withExemplars(ExemplarConfig.getNoopExemplarSampler());
155164
}

exemplars_parent/exemplars/src/main/java/io/prometheus/client/exemplars/api/CounterExemplarSampler.java simpleclient/src/main/java/io/prometheus/client/exemplars/CounterExemplarSampler.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.prometheus.client.exemplars.api;
1+
package io.prometheus.client.exemplars;
22

33
/**
44
* Exemplar sampler for counter metrics.
+16-15
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
package io.prometheus.client.exemplars.impl;
1+
package io.prometheus.client.exemplars;
22

3-
import io.prometheus.client.exemplars.api.Exemplar;
4-
import io.prometheus.client.exemplars.api.ExemplarSampler;
5-
import io.prometheus.client.exemplars.api.Value;
6-
import io.prometheus.client.exemplars.tracer.common.SpanContext;
3+
import io.prometheus.client.exemplars.Exemplar;
4+
import io.prometheus.client.exemplars.ExemplarSampler;
5+
import io.prometheus.client.exemplars.Value;
6+
import io.prometheus.client.exemplars.tracer.common.SpanContextSupplier;
77

8-
import static io.prometheus.client.exemplars.api.Exemplar.SPAN_ID;
9-
import static io.prometheus.client.exemplars.api.Exemplar.TRACE_ID;
8+
import static io.prometheus.client.exemplars.Exemplar.SPAN_ID;
9+
import static io.prometheus.client.exemplars.Exemplar.TRACE_ID;
1010

1111
/**
1212
* Default Exemplar sampler.
@@ -15,20 +15,20 @@
1515
*/
1616
public class DefaultExemplarSampler implements ExemplarSampler {
1717

18-
private final SpanContext spanContext;
18+
private final SpanContextSupplier spanContextSupplier;
1919
// Choosing a prime number for the retention interval makes behavior more predictable,
2020
// because it is unlikely that retention happens at the exact same time as a Prometheus scrape.
2121
private final long minRetentionIntervalMs = 7109;
2222
private final Clock clock;
2323

24-
public DefaultExemplarSampler(SpanContext spanContext) {
25-
this.spanContext = spanContext;
24+
public DefaultExemplarSampler(SpanContextSupplier spanContextSupplier) {
25+
this.spanContextSupplier = spanContextSupplier;
2626
this.clock = new SystemClock();
2727
}
2828

2929
// for unit tests only
30-
DefaultExemplarSampler(SpanContext spanContext, Clock clock) {
31-
this.spanContext = spanContext;
30+
DefaultExemplarSampler(SpanContextSupplier spanContextSupplier, Clock clock) {
31+
this.spanContextSupplier = spanContextSupplier;
3232
this.clock = clock;
3333
}
3434

@@ -44,9 +44,10 @@ public Exemplar sample(double value, double bucketFrom, double bucketTo, Exempla
4444

4545
private Exemplar doSample(double value, Exemplar previous) {
4646
long timestampMs = clock.currentTimeMillis();
47-
if (previous == null || timestampMs - previous.getTimestampMs() > minRetentionIntervalMs) {
48-
String traceId = spanContext.getTraceId();
49-
String spanId = spanContext.getSpanId();
47+
if (previous == null || previous.getTimestampMs() == null
48+
|| timestampMs - previous.getTimestampMs() > minRetentionIntervalMs) {
49+
String traceId = spanContextSupplier.getTraceId();
50+
String spanId = spanContextSupplier.getSpanId();
5051
if (traceId != null && spanId != null) {
5152
return new Exemplar(value, timestampMs, TRACE_ID, traceId, SPAN_ID, spanId);
5253
}

0 commit comments

Comments
 (0)