Skip to content

Commit f132e90

Browse files
committed
refactor(otel): Accept builder, remove custom sampling
1 parent 0835dcc commit f132e90

7 files changed

Lines changed: 44 additions & 225 deletions

File tree

examples/src/main/java/software/amazon/lambda/durable/examples/general/OtelExample.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import software.amazon.lambda.durable.DurableContext;
1010
import software.amazon.lambda.durable.DurableHandler;
1111
import software.amazon.lambda.durable.examples.types.GreetingRequest;
12-
import software.amazon.lambda.durable.otel.DeterministicIdGenerator;
1312
import software.amazon.lambda.durable.otel.OpenTelemetryDurablePlugin;
1413

1514
/**
@@ -40,12 +39,8 @@ public class OtelExample extends DurableHandler<GreetingRequest, String> {
4039

4140
@Override
4241
protected DurableConfig createConfiguration() {
43-
var idGenerator = new DeterministicIdGenerator();
44-
var tracerProvider = SdkTracerProvider.builder()
45-
.setIdGenerator(idGenerator)
46-
.addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create()))
47-
.build();
48-
var otelPlugin = new OpenTelemetryDurablePlugin(tracerProvider, idGenerator);
42+
var otelPlugin = new OpenTelemetryDurablePlugin(
43+
SdkTracerProvider.builder().addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())));
4944

5045
return DurableConfig.builder().withPlugins(otelPlugin).build();
5146
}

otel-plugin/src/main/java/software/amazon/lambda/durable/otel/OpenTelemetryDurablePlugin.java

Lines changed: 24 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
import io.opentelemetry.api.trace.TraceFlags;
1212
import io.opentelemetry.api.trace.TraceState;
1313
import io.opentelemetry.api.trace.Tracer;
14-
import io.opentelemetry.api.trace.TracerProvider;
1514
import io.opentelemetry.context.Context;
1615
import io.opentelemetry.context.Scope;
1716
import io.opentelemetry.sdk.trace.SdkTracerProvider;
17+
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
1818
import java.time.Instant;
1919
import java.util.concurrent.ConcurrentHashMap;
2020
import org.slf4j.Logger;
@@ -52,17 +52,15 @@ public class OpenTelemetryDurablePlugin implements DurableExecutionPlugin {
5252
private static final Logger logger = LoggerFactory.getLogger(OpenTelemetryDurablePlugin.class);
5353
private static final String INSTRUMENTATION_NAME = "aws-durable-execution-sdk-java";
5454

55-
private final TracerProvider tracerProvider;
55+
private final SdkTracerProvider tracerProvider;
5656
private final Tracer tracer;
5757
private final DeterministicIdGenerator idGenerator;
5858
private final ContextExtractor contextExtractor;
59-
private final double samplingRate;
6059
private final boolean enableMdc;
6160

6261
// Per-invocation state
6362
private volatile Span invocationSpan;
6463
private volatile String executionArn;
65-
private volatile boolean sampled = true;
6664

6765
// Thread-safe storage for operation spans (keyed by operationId) — open spans that need ending
6866
private final ConcurrentHashMap<String, Span> operationSpans = new ConcurrentHashMap<>();
@@ -75,75 +73,52 @@ public class OpenTelemetryDurablePlugin implements DurableExecutionPlugin {
7573
private final ConcurrentHashMap<String, SpanContext> operationContexts = new ConcurrentHashMap<>();
7674

7775
/**
78-
* Creates an OTel plugin with default settings: X-Ray context extraction, 100% sampling, MDC enabled.
76+
* Creates an OTel plugin with default settings: X-Ray context extraction, MDC enabled.
7977
*
80-
* @param tracerProvider the OTel tracer provider (should use {@link DeterministicIdGenerator})
81-
* @param idGenerator the deterministic ID generator (same instance configured in the tracer provider)
78+
* @param tracerProviderBuilder the tracer provider builder (ID generator will be overridden)
8279
*/
83-
public OpenTelemetryDurablePlugin(TracerProvider tracerProvider, DeterministicIdGenerator idGenerator) {
84-
this(tracerProvider, idGenerator, new XRayContextExtractor(), 1.0, true);
80+
public OpenTelemetryDurablePlugin(SdkTracerProviderBuilder tracerProviderBuilder) {
81+
this(tracerProviderBuilder, new XRayContextExtractor(), true);
8582
}
8683

8784
/**
88-
* Creates an OTel plugin with a custom context extractor and sampling rate, MDC enabled.
85+
* Creates an OTel plugin with a custom context extractor, MDC enabled.
8986
*
90-
* @param tracerProvider the OTel tracer provider
91-
* @param idGenerator the deterministic ID generator
87+
* @param tracerProviderBuilder the tracer provider builder (ID generator will be overridden)
9288
* @param contextExtractor extracts parent trace context from the Lambda environment
93-
* @param samplingRate value between 0.0 and 1.0 — fraction of executions to trace
9489
*/
9590
public OpenTelemetryDurablePlugin(
96-
TracerProvider tracerProvider,
97-
DeterministicIdGenerator idGenerator,
98-
ContextExtractor contextExtractor,
99-
double samplingRate) {
100-
this(tracerProvider, idGenerator, contextExtractor, samplingRate, true);
91+
SdkTracerProviderBuilder tracerProviderBuilder, ContextExtractor contextExtractor) {
92+
this(tracerProviderBuilder, contextExtractor, true);
10193
}
10294

10395
/**
10496
* Creates an OTel plugin with full configuration.
10597
*
106-
* @param tracerProvider the OTel tracer provider
107-
* @param idGenerator the deterministic ID generator
98+
* <p>The plugin internally creates a {@link DeterministicIdGenerator} and sets it on the provided builder before
99+
* building the tracer provider. Customers configure exporters, span processors, and samplers on the builder — the
100+
* plugin handles ID generation.
101+
*
102+
* @param tracerProviderBuilder the tracer provider builder (ID generator will be overridden)
108103
* @param contextExtractor extracts parent trace context from the Lambda environment
109-
* @param samplingRate value between 0.0 and 1.0 — fraction of executions to trace
110104
* @param enableMdc if true, injects trace_id/span_id into SLF4J MDC for log correlation
111105
*/
112106
public OpenTelemetryDurablePlugin(
113-
TracerProvider tracerProvider,
114-
DeterministicIdGenerator idGenerator,
115-
ContextExtractor contextExtractor,
116-
double samplingRate,
117-
boolean enableMdc) {
118-
this.tracerProvider = tracerProvider;
119-
this.idGenerator = idGenerator;
107+
SdkTracerProviderBuilder tracerProviderBuilder, ContextExtractor contextExtractor, boolean enableMdc) {
108+
this.idGenerator = new DeterministicIdGenerator();
109+
this.tracerProvider = tracerProviderBuilder.setIdGenerator(idGenerator).build();
120110
this.tracer = tracerProvider.get(INSTRUMENTATION_NAME);
121111
this.contextExtractor = contextExtractor;
122-
this.samplingRate = samplingRate;
123112
this.enableMdc = enableMdc;
124113
}
125114

126-
/**
127-
* Creates an OTel plugin using a pre-configured {@link SdkTracerProvider}.
128-
*
129-
* @param sdkTracerProvider the SDK tracer provider
130-
* @param idGenerator the deterministic ID generator
131-
*/
132-
public OpenTelemetryDurablePlugin(SdkTracerProvider sdkTracerProvider, DeterministicIdGenerator idGenerator) {
133-
this((TracerProvider) sdkTracerProvider, idGenerator);
134-
}
135-
136115
// ─── Invocation hooks ────────────────────────────────────────────────
137116

138117
@Override
139118
public void onInvocationStart(InvocationInfo info) {
140119
this.executionArn = info.executionArn();
141120
idGenerator.setExecutionArn(info.executionArn());
142121

143-
// Determine sampling (consistent across all invocations of same execution)
144-
this.sampled = SamplingUtil.shouldSampleExecution(info.executionArn(), samplingRate);
145-
if (!sampled) return;
146-
147122
// Extract parent context from Lambda environment (X-Ray, W3C, etc.)
148123
var extractedParentContext = contextExtractor.extract();
149124

@@ -162,7 +137,7 @@ public void onInvocationStart(InvocationInfo info) {
162137

163138
@Override
164139
public void onInvocationEnd(InvocationEndInfo info) {
165-
if (!sampled || invocationSpan == null) return;
140+
if (invocationSpan == null) return;
166141

167142
// End any operation spans that are still open (operations that didn't complete in this invocation)
168143
for (var entry : operationSpans.entrySet()) {
@@ -196,19 +171,17 @@ public void onInvocationEnd(InvocationEndInfo info) {
196171
invocationSpan = null;
197172

198173
// Flush spans before Lambda freezes
199-
if (tracerProvider instanceof SdkTracerProvider sdkProvider) {
200-
var flushResult = sdkProvider.forceFlush().join(5, java.util.concurrent.TimeUnit.SECONDS);
201-
if (!flushResult.isSuccess()) {
202-
logger.warn("OTel span flush failed or timed out — some spans may be lost");
203-
}
174+
var flushResult = tracerProvider.forceFlush().join(5, java.util.concurrent.TimeUnit.SECONDS);
175+
if (!flushResult.isSuccess()) {
176+
logger.warn("OTel span flush failed or timed out — some spans may be lost");
204177
}
205178
}
206179

207180
// ─── Operation hooks ─────────────────────────────────────────────────
208181

209182
@Override
210183
public void onOperationStart(OperationInfo info) {
211-
if (!sampled || info.id() == null) return;
184+
if (info.id() == null) return;
212185

213186
idGenerator.setNextSpanOperationId(info.id());
214187

@@ -239,7 +212,7 @@ public void onOperationStart(OperationInfo info) {
239212

240213
@Override
241214
public void onOperationEnd(OperationEndInfo info) {
242-
if (!sampled || info.id() == null) return;
215+
if (info.id() == null) return;
243216

244217
// End the operation span that was started in onOperationStart
245218
var span = operationSpans.remove(info.id());
@@ -257,7 +230,6 @@ public void onOperationEnd(OperationEndInfo info) {
257230

258231
@Override
259232
public void onUserFunctionStart(UserFunctionStartInfo info) {
260-
if (!sampled) return;
261233
var key = attemptKey(info.id(), info.attempt());
262234

263235
// Use the operation span as parent for the attempt span
@@ -295,7 +267,6 @@ public void onUserFunctionStart(UserFunctionStartInfo info) {
295267

296268
@Override
297269
public void onUserFunctionEnd(UserFunctionEndInfo info) {
298-
if (!sampled) return;
299270
var key = attemptKey(info.id(), info.attempt());
300271

301272
// Close scope first (must happen on same thread as makeCurrent)

otel-plugin/src/main/java/software/amazon/lambda/durable/otel/SamplingUtil.java

Lines changed: 0 additions & 55 deletions
This file was deleted.

otel-plugin/src/test/java/software/amazon/lambda/durable/otel/MdcSpanEnricherTest.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,11 @@ void inject_withNullArn_doesNotSetArn() {
5454
void plugin_withMdcEnabled_setsArnInMdc() {
5555
// Test MDC through the full plugin lifecycle (where makeCurrent is called on same thread)
5656
var spanExporter = InMemorySpanExporter.create();
57-
var idGenerator = new DeterministicIdGenerator();
58-
var tracerProvider = SdkTracerProvider.builder()
59-
.setIdGenerator(idGenerator)
60-
.addSpanProcessor(SimpleSpanProcessor.create(spanExporter))
61-
.build();
6257

6358
var plugin = new OpenTelemetryDurablePlugin(
64-
tracerProvider, idGenerator, () -> io.opentelemetry.context.Context.root(), 1.0, true);
59+
SdkTracerProvider.builder().addSpanProcessor(SimpleSpanProcessor.create(spanExporter)),
60+
() -> io.opentelemetry.context.Context.root(),
61+
true);
6562

6663
plugin.onInvocationStart(new InvocationInfo("req-1", "arn:exec-mdc-test", true));
6764

otel-plugin/src/test/java/software/amazon/lambda/durable/otel/OpenTelemetryDurablePluginTest.java

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,15 @@ class OpenTelemetryDurablePluginTest {
1717

1818
private InMemorySpanExporter spanExporter;
1919
private OpenTelemetryDurablePlugin plugin;
20-
private DeterministicIdGenerator idGenerator;
2120

2221
@BeforeEach
2322
void setUp() {
2423
spanExporter = InMemorySpanExporter.create();
25-
idGenerator = new DeterministicIdGenerator();
26-
27-
var tracerProvider = SdkTracerProvider.builder()
28-
.setIdGenerator(idGenerator)
29-
.addSpanProcessor(SimpleSpanProcessor.create(spanExporter))
30-
.build();
3124

3225
plugin = new OpenTelemetryDurablePlugin(
33-
tracerProvider, idGenerator, () -> io.opentelemetry.context.Context.root(), 1.0, false);
26+
SdkTracerProvider.builder().addSpanProcessor(SimpleSpanProcessor.create(spanExporter)),
27+
() -> io.opentelemetry.context.Context.root(),
28+
false);
3429
}
3530

3631
@Test
@@ -215,16 +210,13 @@ void operationNotCompleted_spanEndedAtInvocationEnd() {
215210
}
216211

217212
@Test
218-
void sampling_disabledExecution_producesNoSpans() {
213+
void sampling_disabled_producesNoSpans() {
219214
spanExporter = InMemorySpanExporter.create();
220215
var sampledPlugin = new OpenTelemetryDurablePlugin(
221216
SdkTracerProvider.builder()
222-
.setIdGenerator(idGenerator)
223-
.addSpanProcessor(SimpleSpanProcessor.create(spanExporter))
224-
.build(),
225-
idGenerator,
217+
.setSampler(io.opentelemetry.sdk.trace.samplers.Sampler.alwaysOff())
218+
.addSpanProcessor(SimpleSpanProcessor.create(spanExporter)),
226219
() -> io.opentelemetry.context.Context.root(),
227-
0.0, // 0% sampling — nothing should be traced
228220
false);
229221

230222
sampledPlugin.onInvocationStart(new InvocationInfo("req-1", "arn:exec1", true));

otel-plugin/src/test/java/software/amazon/lambda/durable/otel/SamplingUtilTest.java

Lines changed: 0 additions & 74 deletions
This file was deleted.

0 commit comments

Comments
 (0)