Skip to content

Commit

Permalink
Add optional SpanContext parameter to ExemplarSampler (#929)
Browse files Browse the repository at this point in the history
Signed-off-by: Fabian Stäber <[email protected]>
  • Loading branch information
fstab authored Mar 21, 2024
1 parent 7f02651 commit fc447f8
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public Builder sampleIntervalMilliseconds(int sampleIntervalMilliseconds) {
return this;
}

public ExemplarsProperties builder() {
public ExemplarsProperties build() {
return new ExemplarsProperties(minRetentionPeriodSeconds, maxRetentionPeriodSeconds, sampleIntervalMilliseconds);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,23 @@ public class ExemplarSampler {
// to be overwritten by automatic exemplar sampling. exemplars.lengt == customExemplars.length
private final AtomicBoolean acceptingNewExemplars = new AtomicBoolean(true);
private final AtomicBoolean acceptingNewCustomExemplars = new AtomicBoolean(true);
private final SpanContext spanContext; // may be null, in that case SpanContextSupplier.getSpanContext() is used.

public ExemplarSampler(ExemplarSamplerConfig config) {
this(config, null);
}

/**
* Constructor with an additional {code spanContext} argument.
* This is useful for testing, but may also be useful in some production scenarios.
* If {@code spanContext != null} that spanContext is used and {@link SpanContextSupplier} is not used.
* If {@code spanContext == null} the {@link SpanContextSupplier#getSpanContext()} is called to find a span context.
*/
public ExemplarSampler(ExemplarSamplerConfig config, SpanContext spanContext) {
this.config = config;
this.exemplars = new Exemplar[config.getNumberOfExemplars()];
this.customExemplars = new Exemplar[exemplars.length];
this.spanContext = spanContext;
}

public Exemplars collect() {
Expand Down Expand Up @@ -307,8 +319,8 @@ private long updateExemplar(int index, double value, long now) {
}

private Labels doSampleExemplar() {
SpanContext spanContext = this.spanContext != null ? this.spanContext : SpanContextSupplier.getSpanContext();
try {
SpanContext spanContext = SpanContextSupplier.getSpanContext();
if (spanContext != null) {
if (spanContext.isCurrentSpanSampled()) {
String spanId = spanContext.getCurrentSpanId();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,18 +76,15 @@ public void tearDown() {
public void testIsSampled() throws Exception {
SpanContext context = new SpanContext();
context.isSampled = false;
SpanContextSupplier.setSpanContext(context);
ExemplarSampler sampler = new ExemplarSampler(makeConfig());
ExemplarSampler sampler = new ExemplarSampler(makeConfig(), context);
Thread.sleep(tick); // t = 1 tick
sampler.observe(0.3); // no sampled, because isSampled() returns false
assertExemplars(sampler); // empty
}

@Test
public void testDefaultConfigHasFourExemplars() throws Exception {
SpanContext context = new SpanContext();
SpanContextSupplier.setSpanContext(context);
ExemplarSampler sampler = new ExemplarSampler(makeConfig());
ExemplarSampler sampler = new ExemplarSampler(makeConfig(), new SpanContext());
Thread.sleep(tick); // t = 1 tick
sampler.observe(0.3);
Thread.sleep(sampleInterval + tick); // t = 12 tick
Expand All @@ -104,9 +101,7 @@ public void testDefaultConfigHasFourExemplars() throws Exception {

@Test
public void testEmptyBuckets() throws Exception {
SpanContext context = new SpanContext();
SpanContextSupplier.setSpanContext(context);
ExemplarSampler sampler = new ExemplarSampler(makeConfig(Double.POSITIVE_INFINITY));
ExemplarSampler sampler = new ExemplarSampler(makeConfig(Double.POSITIVE_INFINITY), new SpanContext());
Thread.sleep(tick); // t = 1 tick
sampler.observe(0.8); // observed in the +Inf bucket
Thread.sleep(sampleInterval + tick); // t = 12 tick
Expand All @@ -117,9 +112,7 @@ public void testEmptyBuckets() throws Exception {

@Test
public void testDefaultExemplarsBuckets() throws Exception {
SpanContext context = new SpanContext();
SpanContextSupplier.setSpanContext(context);
ExemplarSampler sampler = new ExemplarSampler(makeConfig(0.2, 0.4, 0.6, 0.8, 1.0, Double.POSITIVE_INFINITY));
ExemplarSampler sampler = new ExemplarSampler(makeConfig(0.2, 0.4, 0.6, 0.8, 1.0, Double.POSITIVE_INFINITY), new SpanContext());
Scheduler.awaitInitialization();
Thread.sleep(tick); // t = 1 tick
sampler.observe(0.3);
Expand Down Expand Up @@ -150,9 +143,7 @@ public void testCustomExemplarsNoBuckets() throws Exception {

@Test
public void testDefaultExemplarsNoBuckets() throws Exception {
SpanContext context = new SpanContext();
SpanContextSupplier.setSpanContext(context);
ExemplarSampler sampler = new ExemplarSampler(makeConfig());
ExemplarSampler sampler = new ExemplarSampler(makeConfig(), new SpanContext());
Scheduler.awaitInitialization();
Thread.sleep(tick); // t = 1 tick
sampler.observe(1); // observed
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package io.prometheus.metrics.core.exemplars;

import io.prometheus.metrics.config.ExemplarsProperties;
import io.prometheus.metrics.model.snapshots.Exemplar;
import io.prometheus.metrics.model.snapshots.Exemplars;
import io.prometheus.metrics.tracer.common.SpanContext;
import io.prometheus.metrics.tracer.initializer.SpanContextSupplier;
import org.junit.*;

import static io.prometheus.metrics.model.snapshots.Exemplar.TRACE_ID;

public class SpanContextSupplierTest {

public SpanContext makeSpanContext(String traceId, String spanId) {

return new SpanContext() {
@Override
public String getCurrentTraceId() {
return traceId;
}

@Override
public String getCurrentSpanId() {
return spanId;
}

@Override
public boolean isCurrentSpanSampled() {
return true;
}

@Override
public void markCurrentSpanAsExemplar() {
}
};
}

SpanContext spanContextA = makeSpanContext("A", "a");
SpanContext spanContextB = makeSpanContext("B", "b");
SpanContext origSpanContext;

ExemplarSamplerConfig config = new ExemplarSamplerConfig(
10, // min retention period in milliseconds
20, // max retention period in milliseconds
5, // sample interval in millisecnods
1, // number of exemplars
null // histogram upper bounds
);

@Before
public void setUp() {
origSpanContext = SpanContextSupplier.getSpanContext();
}

@After
public void tearDown() {
SpanContextSupplier.setSpanContext(origSpanContext);
}

/**
* Test: When a {@link SpanContext} is provided as a constructor argument to the {@link ExemplarSampler},
* then that {@link SpanContext} is used, not the one from the {@link SpanContextSupplier}.
*/
@Test
public void testConstructorInjection() {
ExemplarsProperties properties = ExemplarsProperties.builder().build();
ExemplarSamplerConfig config = new ExemplarSamplerConfig(properties, 1);
ExemplarSampler exemplarSampler = new ExemplarSampler(config, spanContextA);

SpanContextSupplier.setSpanContext(spanContextB);
exemplarSampler.observe(1.0);
Exemplars exemplars = exemplarSampler.collect();
Assert.assertEquals(1, exemplars.size());
Exemplar exemplar = exemplars.get(0);
Assert.assertEquals("A", exemplar.getLabels().get(TRACE_ID));
}

/**
* When the global {@link SpanContext} is updated via {@link SpanContextSupplier#setSpanContext(SpanContext)},
* the {@link ExemplarSampler} recognizes the update (unless a {@link ExemplarSampler} was provided as
* constructor argument to {@link ExemplarSampler}).
*/
@Test
public void testUpdateSpanContext() throws InterruptedException {
ExemplarSampler exemplarSampler = new ExemplarSampler(config);

SpanContextSupplier.setSpanContext(spanContextB);
exemplarSampler.observe(1.0);
Exemplars exemplars = exemplarSampler.collect();
Assert.assertEquals(1, exemplars.size());
Exemplar exemplar = exemplars.get(0);
Assert.assertEquals("B", exemplar.getLabels().get(TRACE_ID));

Thread.sleep(15); // more than the minimum retention period defined in config above.

SpanContextSupplier.setSpanContext(spanContextA);
exemplarSampler.observe(1.0);
exemplars = exemplarSampler.collect();
Assert.assertEquals(1, exemplars.size());
exemplar = exemplars.get(0);
Assert.assertEquals("A", exemplar.getLabels().get(TRACE_ID));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

public interface SpanContext {

public static final String EXEMPLAR_ATTRIBUTE_NAME = "exemplar";
public static final String EXEMPLAR_ATTRIBUTE_VALUE = "true";
String EXEMPLAR_ATTRIBUTE_NAME = "exemplar";
String EXEMPLAR_ATTRIBUTE_VALUE = "true";

/**
* @return the current trace id, or {@code null} if this call is not happening within a span context.
Expand Down

0 comments on commit fc447f8

Please sign in to comment.