Skip to content

Commit 8e31bf4

Browse files
add SdkTracerProvider.setScopeConfigurator() and support (#7021)
Co-authored-by: Jack Berg <[email protected]>
1 parent 05bf32c commit 8e31bf4

File tree

7 files changed

+161
-17
lines changed

7 files changed

+161
-17
lines changed

sdk/trace/src/main/java/io/opentelemetry/sdk/trace/ExtendedSdkTracer.java

-4
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,12 @@
1212

1313
/** {@link ExtendedSdkTracer} is SDK implementation of {@link ExtendedTracer}. */
1414
final class ExtendedSdkTracer extends SdkTracer implements ExtendedTracer {
15-
// TODO: add dedicated API for updating scope config.
16-
@SuppressWarnings("FieldCanBeFinal") // For now, allow updating reflectively.
17-
private boolean tracerEnabled;
1815

1916
ExtendedSdkTracer(
2017
TracerSharedState sharedState,
2118
InstrumentationScopeInfo instrumentationScopeInfo,
2219
TracerConfig tracerConfig) {
2320
super(sharedState, instrumentationScopeInfo, tracerConfig);
24-
this.tracerEnabled = tracerConfig.isEnabled();
2521
}
2622

2723
@Override

sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracer.java

+12-4
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,9 @@ class SdkTracer implements Tracer {
3030

3131
private final TracerSharedState sharedState;
3232
private final InstrumentationScopeInfo instrumentationScopeInfo;
33-
34-
// TODO: add dedicated API for updating scope config.
35-
@SuppressWarnings("FieldCanBeFinal") // For now, allow updating reflectively.
36-
private boolean tracerEnabled;
33+
// deliberately not volatile because of performance concerns
34+
// - which means its eventually consistent
35+
protected boolean tracerEnabled;
3736

3837
SdkTracer(
3938
TracerSharedState sharedState,
@@ -79,4 +78,13 @@ public SpanBuilder spanBuilder(String spanName) {
7978
InstrumentationScopeInfo getInstrumentationScopeInfo() {
8079
return instrumentationScopeInfo;
8180
}
81+
82+
// Visible for testing
83+
boolean isEnabled() {
84+
return tracerEnabled;
85+
}
86+
87+
void updateTracerConfig(TracerConfig tracerConfig) {
88+
this.tracerEnabled = tracerConfig.isEnabled();
89+
}
8290
}

sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkTracerProvider.java

+23-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import io.opentelemetry.sdk.internal.ComponentRegistry;
1515
import io.opentelemetry.sdk.internal.ScopeConfigurator;
1616
import io.opentelemetry.sdk.resources.Resource;
17+
import io.opentelemetry.sdk.trace.internal.SdkTracerProviderUtil;
1718
import io.opentelemetry.sdk.trace.internal.TracerConfig;
1819
import io.opentelemetry.sdk.trace.samplers.Sampler;
1920
import java.io.Closeable;
@@ -30,7 +31,9 @@ public final class SdkTracerProvider implements TracerProvider, Closeable {
3031
static final String DEFAULT_TRACER_NAME = "";
3132
private final TracerSharedState sharedState;
3233
private final ComponentRegistry<SdkTracer> tracerSdkComponentRegistry;
33-
private final ScopeConfigurator<TracerConfig> tracerConfigurator;
34+
// deliberately not volatile because of performance concerns
35+
// - which means its eventually consistent
36+
private ScopeConfigurator<TracerConfig> tracerConfigurator;
3437

3538
/**
3639
* Returns a new {@link SdkTracerProviderBuilder} for {@link SdkTracerProvider}.
@@ -100,6 +103,25 @@ public Sampler getSampler() {
100103
return sharedState.getSampler();
101104
}
102105

106+
/**
107+
* Updates the tracer configurator, which computes {@link TracerConfig} for each {@link
108+
* InstrumentationScopeInfo}.
109+
*
110+
* <p>This method is experimental so not public. You may reflectively call it using {@link
111+
* SdkTracerProviderUtil#setTracerConfigurator(SdkTracerProvider, ScopeConfigurator)}.
112+
*
113+
* @see TracerConfig#configuratorBuilder()
114+
*/
115+
void setTracerConfigurator(ScopeConfigurator<TracerConfig> tracerConfigurator) {
116+
this.tracerConfigurator = tracerConfigurator;
117+
this.tracerSdkComponentRegistry
118+
.getComponents()
119+
.forEach(
120+
sdkTracer ->
121+
sdkTracer.updateTracerConfig(
122+
getTracerConfig(sdkTracer.getInstrumentationScopeInfo())));
123+
}
124+
103125
/**
104126
* Attempts to stop all the activity for {@link Tracer}s created by this provider. Calls {@link
105127
* SpanProcessor#shutdown()} for all registered {@link SpanProcessor}s.

sdk/trace/src/main/java/io/opentelemetry/sdk/trace/internal/SdkTracerProviderUtil.java

+16
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
99
import io.opentelemetry.sdk.internal.ScopeConfigurator;
10+
import io.opentelemetry.sdk.trace.SdkTracerProvider;
1011
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
1112
import java.lang.reflect.InvocationTargetException;
1213
import java.lang.reflect.Method;
@@ -24,6 +25,21 @@ public final class SdkTracerProviderUtil {
2425

2526
private SdkTracerProviderUtil() {}
2627

28+
/** Reflectively set the {@link ScopeConfigurator} to the {@link SdkTracerProvider}. */
29+
public static void setTracerConfigurator(
30+
SdkTracerProvider sdkTracerProvider, ScopeConfigurator<TracerConfig> scopeConfigurator) {
31+
try {
32+
Method method =
33+
SdkTracerProvider.class.getDeclaredMethod(
34+
"setTracerConfigurator", ScopeConfigurator.class);
35+
method.setAccessible(true);
36+
method.invoke(sdkTracerProvider, scopeConfigurator);
37+
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
38+
throw new IllegalStateException(
39+
"Error calling setTracerConfigurator on SdkTracerProvider", e);
40+
}
41+
}
42+
2743
/** Reflectively set the {@link ScopeConfigurator} to the {@link SdkTracerProviderBuilder}. */
2844
public static void setTracerConfigurator(
2945
SdkTracerProviderBuilder sdkTracerProviderBuilder,

sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkTracerProviderTest.java

+32
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818
import io.opentelemetry.sdk.common.Clock;
1919
import io.opentelemetry.sdk.common.CompletableResultCode;
2020
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
21+
import io.opentelemetry.sdk.internal.ScopeConfigurator;
2122
import io.opentelemetry.sdk.resources.Resource;
23+
import io.opentelemetry.sdk.trace.internal.SdkTracerProviderUtil;
24+
import io.opentelemetry.sdk.trace.internal.TracerConfig;
2225
import io.opentelemetry.sdk.trace.samplers.Sampler;
2326
import java.util.function.Supplier;
2427
import org.junit.jupiter.api.BeforeEach;
@@ -184,6 +187,35 @@ void propagatesInstrumentationScopeInfoToTracer() {
184187
assertThat(((SdkTracer) tracer).getInstrumentationScopeInfo()).isEqualTo(expected);
185188
}
186189

190+
@Test
191+
void propagatesEnablementToTracerDirectly() {
192+
propagatesEnablementToTracer(true);
193+
}
194+
195+
@Test
196+
void propagatesEnablementToTracerByUtil() {
197+
propagatesEnablementToTracer(false);
198+
}
199+
200+
void propagatesEnablementToTracer(boolean directly) {
201+
SdkTracer tracer = (SdkTracer) tracerFactory.get("test");
202+
boolean isEnabled = tracer.isEnabled();
203+
ScopeConfigurator<TracerConfig> flipConfigurator =
204+
new ScopeConfigurator<TracerConfig>() {
205+
@Override
206+
public TracerConfig apply(InstrumentationScopeInfo scopeInfo) {
207+
return isEnabled ? TracerConfig.disabled() : TracerConfig.enabled();
208+
}
209+
};
210+
// all in the same thread, so should see enablement change immediately
211+
if (directly) {
212+
tracerFactory.setTracerConfigurator(flipConfigurator);
213+
} else {
214+
SdkTracerProviderUtil.setTracerConfigurator(tracerFactory, flipConfigurator);
215+
}
216+
assertThat(tracer.isEnabled()).isEqualTo(!isEnabled);
217+
}
218+
187219
@Test
188220
void build_SpanLimits() {
189221
SpanLimits initialSpanLimits = SpanLimits.builder().build();

sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkTracerTest.java

+9
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import io.opentelemetry.sdk.trace.data.SpanData;
1717
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
1818
import io.opentelemetry.sdk.trace.export.SpanExporter;
19+
import io.opentelemetry.sdk.trace.internal.TracerConfig;
1920
import java.util.Collection;
2021
import java.util.concurrent.atomic.AtomicLong;
2122
import org.junit.jupiter.api.Test;
@@ -50,6 +51,14 @@ void getInstrumentationScopeInfo() {
5051
assertThat(tracer.getInstrumentationScopeInfo()).isEqualTo(instrumentationScopeInfo);
5152
}
5253

54+
@Test
55+
void updateEnabled() {
56+
tracer.updateTracerConfig(TracerConfig.disabled());
57+
assertThat(tracer.isEnabled()).isFalse();
58+
tracer.updateTracerConfig(TracerConfig.enabled());
59+
assertThat(tracer.isEnabled()).isTrue();
60+
}
61+
5362
@Test
5463
void propagatesInstrumentationScopeInfoToSpan() {
5564
ReadableSpan readableSpan = (ReadableSpan) tracer.spanBuilder("spanName").startSpan();

sdk/trace/src/testIncubating/java/io/opentelemetry/sdk/trace/TracerConfigTest.java

+69-8
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,8 @@
1313
import static io.opentelemetry.sdk.trace.internal.TracerConfig.enabled;
1414

1515
import io.opentelemetry.api.common.Attributes;
16-
import io.opentelemetry.api.incubator.trace.ExtendedTracer;
1716
import io.opentelemetry.api.trace.Span;
1817
import io.opentelemetry.api.trace.SpanId;
19-
import io.opentelemetry.api.trace.Tracer;
2018
import io.opentelemetry.context.Scope;
2119
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
2220
import io.opentelemetry.sdk.internal.ScopeConfigurator;
@@ -42,9 +40,9 @@ void disableScopes() throws InterruptedException {
4240
.addSpanProcessor(SimpleSpanProcessor.create(exporter))
4341
.build();
4442

45-
Tracer tracerA = tracerProvider.get("tracerA");
46-
Tracer tracerB = tracerProvider.get("tracerB");
47-
Tracer tracerC = tracerProvider.get("tracerC");
43+
ExtendedSdkTracer tracerA = (ExtendedSdkTracer) tracerProvider.get("tracerA");
44+
ExtendedSdkTracer tracerB = (ExtendedSdkTracer) tracerProvider.get("tracerB");
45+
ExtendedSdkTracer tracerC = (ExtendedSdkTracer) tracerProvider.get("tracerC");
4846

4947
Span parent;
5048
Span child;
@@ -92,9 +90,9 @@ void disableScopes() throws InterruptedException {
9290
.hasParentSpanId(parent.getSpanContext().getSpanId())
9391
.hasAttributes(Attributes.builder().put("c", "1").build()));
9492
// tracerA and tracerC are enabled, tracerB is disabled.
95-
assertThat(((ExtendedTracer) tracerA).isEnabled()).isTrue();
96-
assertThat(((ExtendedTracer) tracerB).isEnabled()).isFalse();
97-
assertThat(((ExtendedTracer) tracerA).isEnabled()).isTrue();
93+
assertThat(tracerA.isEnabled()).isTrue();
94+
assertThat(tracerB.isEnabled()).isFalse();
95+
assertThat(tracerC.isEnabled()).isTrue();
9896
}
9997

10098
@ParameterizedTest
@@ -158,4 +156,67 @@ private static Stream<Arguments> tracerConfiguratorArgs() {
158156
Arguments.of(enableStartsWithD, scopeDog, enabled()),
159157
Arguments.of(enableStartsWithD, scopeDuck, enabled()));
160158
}
159+
160+
@Test
161+
void setScopeConfigurator() {
162+
// 1. Initially, configure all tracers to be enabled except tracerB
163+
InMemorySpanExporter exporter = InMemorySpanExporter.create();
164+
SdkTracerProvider tracerProvider =
165+
SdkTracerProvider.builder()
166+
.addTracerConfiguratorCondition(nameEquals("tracerB"), disabled())
167+
.addSpanProcessor(SimpleSpanProcessor.create(exporter))
168+
.build();
169+
170+
ExtendedSdkTracer tracerA = (ExtendedSdkTracer) tracerProvider.get("tracerA");
171+
ExtendedSdkTracer tracerB = (ExtendedSdkTracer) tracerProvider.get("tracerB");
172+
ExtendedSdkTracer tracerC = (ExtendedSdkTracer) tracerProvider.get("tracerC");
173+
174+
// verify isEnabled()
175+
assertThat(tracerA.isEnabled()).isTrue();
176+
assertThat(tracerB.isEnabled()).isFalse();
177+
assertThat(tracerC.isEnabled()).isTrue();
178+
179+
// verify spans are emitted as expected
180+
tracerA.spanBuilder("spanA").startSpan().end();
181+
tracerB.spanBuilder("spanB").startSpan().end();
182+
tracerC.spanBuilder("spanC").startSpan().end();
183+
assertThat(exporter.getFinishedSpanItems())
184+
.satisfiesExactlyInAnyOrder(
185+
span -> assertThat(span).hasName("spanA"), span -> assertThat(span).hasName("spanC"));
186+
exporter.reset();
187+
188+
// 2. Update config to disable all tracers
189+
tracerProvider.setTracerConfigurator(
190+
ScopeConfigurator.<TracerConfig>builder().setDefault(TracerConfig.disabled()).build());
191+
192+
// verify isEnabled()
193+
assertThat(tracerA.isEnabled()).isFalse();
194+
assertThat(tracerB.isEnabled()).isFalse();
195+
assertThat(tracerC.isEnabled()).isFalse();
196+
197+
// verify spans are emitted as expected
198+
tracerA.spanBuilder("spanA").startSpan().end();
199+
tracerB.spanBuilder("spanB").startSpan().end();
200+
tracerC.spanBuilder("spanC").startSpan().end();
201+
assertThat(exporter.getFinishedSpanItems()).isEmpty();
202+
203+
// 3. Update config to restore original
204+
tracerProvider.setTracerConfigurator(
205+
ScopeConfigurator.<TracerConfig>builder()
206+
.addCondition(nameEquals("tracerB"), disabled())
207+
.build());
208+
209+
// verify isEnabled()
210+
assertThat(tracerA.isEnabled()).isTrue();
211+
assertThat(tracerB.isEnabled()).isFalse();
212+
assertThat(tracerC.isEnabled()).isTrue();
213+
214+
// verify spans are emitted as expected
215+
tracerA.spanBuilder("spanA").startSpan().end();
216+
tracerB.spanBuilder("spanB").startSpan().end();
217+
tracerC.spanBuilder("spanC").startSpan().end();
218+
assertThat(exporter.getFinishedSpanItems())
219+
.satisfiesExactly(
220+
span -> assertThat(span).hasName("spanA"), span -> assertThat(span).hasName("spanC"));
221+
}
161222
}

0 commit comments

Comments
 (0)