Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 34 additions & 39 deletions otel-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,68 +215,63 @@ With Lambda's `LoggingConfig: JSON` (required for durable functions), CloudWatch

## Configuration

### Constructor Options

```java
// Default: ADOT Java agent global provider, X-Ray context extraction, MDC enabled
new InvocationOtelPlugin();

// Custom tracer provider pipeline
new InvocationOtelPlugin(tracerProviderBuilder);

// Custom context extractor, MDC enabled
new InvocationOtelPlugin(tracerProviderBuilder, contextExtractor);

// Full configuration
new InvocationOtelPlugin(tracerProviderBuilder, contextExtractor, enableMdc);
```
Both plugins take a required `SdkTracerProviderBuilder` (your exporter/processor pipeline) plus an optional
`OtelPluginConfig` built with a named-field builder. This replaces the older telescoping constructors, giving readable,
type-safe call sites, and matches the `OtelPluginConfig` object in the JavaScript and Python SDKs.

### InvocationOtelPlugin

```java
// Default: ADOT Java agent global provider, X-Ray context extraction, MDC enabled
new InvocationOtelPlugin();

// Custom tracer provider pipeline
// Custom tracer provider pipeline, all other options defaulted
new InvocationOtelPlugin(tracerProviderBuilder);

// Custom context extractor, MDC enabled
new InvocationOtelPlugin(tracerProviderBuilder, contextExtractor);

// Full configuration
new InvocationOtelPlugin(tracerProviderBuilder, contextExtractor, enableMdc);
// Full configuration via the builder
new InvocationOtelPlugin(
tracerProviderBuilder,
OtelPluginConfig.builder()
.contextExtractor(new XRayContextExtractor())
.enableMdc(true)
.workflowSpanName("Workflow")
.instrumentationName("aws-durable-execution-sdk-java")
.build());
```

| Parameter | Description | Default |
|-----------|-------------|---------|
| `tracerProviderBuilder` | `SdkTracerProviderBuilder` with your exporter/processor configured | Not used by `new InvocationOtelPlugin()`; the default constructor uses the ADOT Java agent provider |
| `contextExtractor` | Extracts parent trace context from the Lambda environment | `XRayContextExtractor` |
| `enableMdc` | If true, injects `trace_id`/`span_id`/`traceSampled` into SLF4J MDC | `true` |

### ExecutionOtelPlugin

The `ExecutionOtelPlugin` renders the Workflow span as the trace root with operations as siblings of the invocation span. It supports the same constructor options:
The `ExecutionOtelPlugin` renders the Workflow span as the trace root with operations as siblings of the invocation
span. It takes the same `(SdkTracerProviderBuilder, OtelPluginConfig)` constructor:

```java
// Default: ADOT Java agent global provider, X-Ray context extraction, MDC enabled
new ExecutionOtelPlugin();

// Custom tracer provider pipeline
// Custom tracer provider pipeline, all other options defaulted
new ExecutionOtelPlugin(tracerProviderBuilder);

// Custom context extractor, MDC enabled
new ExecutionOtelPlugin(tracerProviderBuilder, contextExtractor);

// Full configuration
new ExecutionOtelPlugin(tracerProviderBuilder, contextExtractor, enableMdc, workflowSpanName);
// Full configuration via the builder
new ExecutionOtelPlugin(
tracerProviderBuilder,
OtelPluginConfig.builder()
.enableMdc(false)
.workflowSpanName("Workflow")
.build());
```

| Parameter | Description | Default |
### OtelPluginConfig options

| Builder method | Description | Default |
|-----------|-------------|---------|
| `tracerProviderBuilder` | `SdkTracerProviderBuilder` with your exporter/processor configured | Not used by `new ExecutionOtelPlugin()`; the default constructor uses the ADOT Java agent provider |
| `contextExtractor` | Extracts parent trace context from the Lambda environment | `XRayContextExtractor` |
| `enableMdc` | If true, injects `trace_id`/`span_id`/`traceSampled` into SLF4J MDC | `true` |
| `workflowSpanName` | Name for the Workflow root span | `"Workflow"` |
| `contextExtractor(...)` | Extracts parent trace context from the Lambda environment | `new XRayContextExtractor()` |
| `enableMdc(...)` | If true, injects `trace_id`/`span_id`/`traceSampled` into SLF4J MDC | `true` |
| `workflowSpanName(...)` | Name for the Workflow span | `"Workflow"` |
| `instrumentationName(...)` | Instrumentation scope name registered with the tracer | `"aws-durable-execution-sdk-java"` |

> The `tracerProviderBuilder` argument is not used by the no-arg `new InvocationOtelPlugin()` /
> `new ExecutionOtelPlugin()` constructors; those use the ADOT Java agent's global provider. A `null` passed to any
> `OtelPluginConfig` builder setter falls back to that option's default.

## Known Limitations

Expand Down
10 changes: 8 additions & 2 deletions otel-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,18 @@
<version>${opentelemetry.version}</version>
</dependency>

<!-- OpenTelemetry SDK (provided — users bring their own SDK configuration) -->
<!-- OpenTelemetry SDK (compile — needed for the auto-configured OTLP provider default;
the ADOT agent / a custom builder still override it at runtime). -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
<version>${opentelemetry.version}</version>
<scope>provided</scope>
</dependency>
<!-- OTLP/HTTP span exporter for the auto-configured provider (ProviderSource.AUTO_OTLP). -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
<version>${opentelemetry.version}</version>
</dependency>
<!-- OpenTelemetry Java agent supplies this SPI when loading OTEL_JAVAAGENT_EXTENSIONS. -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import io.opentelemetry.api.trace.TraceFlags;
import io.opentelemetry.api.trace.TraceState;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.TracerProvider;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
Expand Down Expand Up @@ -82,15 +81,14 @@
public class ExecutionOtelPlugin implements DurableExecutionPlugin {

private static final Logger logger = LoggerFactory.getLogger(ExecutionOtelPlugin.class);
private static final String INSTRUMENTATION_NAME = "aws-durable-execution-sdk-java";
private static final String DEFAULT_WORKFLOW_SPAN_NAME = "Workflow";

private final SdkTracerProvider sdkTracerProvider;
private final Tracer tracer;
private final DeterministicIdGenerator idGenerator;
private final ContextExtractor contextExtractor;
private final boolean enableMdc;
private final String workflowSpanName;
private final ProviderSource providerSource;

// Per-invocation state
private volatile Span workflowSpan;
Expand All @@ -117,7 +115,7 @@ public class ExecutionOtelPlugin implements DurableExecutionPlugin {
* @param tracerProviderBuilder the tracer provider builder (ID generator will be overridden)
*/
public ExecutionOtelPlugin(SdkTracerProviderBuilder tracerProviderBuilder) {
this(tracerProviderBuilder, new XRayContextExtractor(), true, DEFAULT_WORKFLOW_SPAN_NAME);
this(tracerProviderBuilder, OtelPluginConfig.defaults());
}

/**
Expand All @@ -127,51 +125,63 @@ public ExecutionOtelPlugin(SdkTracerProviderBuilder tracerProviderBuilder) {
* {@code OtelPluginAutoConfigurationCustomizerProvider}.
*/
public ExecutionOtelPlugin() {
this(getDefaultTracerProvider(), createDefaultIdGenerator());
this(OtelPluginConfig.defaults());
}

/**
* Creates a Workflow-rooted OTel plugin with a custom context extractor, MDC enabled, root span named
* {@code "Workflow"}.
* Creates a Workflow-rooted OTel plugin from the given tracer provider builder and configuration.
*
* @param tracerProviderBuilder the tracer provider builder (ID generator will be overridden)
* @param contextExtractor extracts parent trace context from the Lambda environment
*/
public ExecutionOtelPlugin(SdkTracerProviderBuilder tracerProviderBuilder, ContextExtractor contextExtractor) {
this(tracerProviderBuilder, contextExtractor, true, DEFAULT_WORKFLOW_SPAN_NAME);
}

/**
* Creates a Workflow-rooted OTel plugin with full configuration.
* <p>Customers configure exporters and span processors on the builder; all other tunables (context extractor, MDC
* toggle, Workflow span name, instrumentation scope name) come from {@link OtelPluginConfig}. Use
* {@link OtelPluginConfig#builder()} for readable, named configuration:
*
* <pre>{@code
* var plugin = new ExecutionOtelPlugin(
* SdkTracerProvider.builder().addSpanProcessor(SimpleSpanProcessor.create(exporter)),
* OtelPluginConfig.builder().enableMdc(false).workflowSpanName("Workflow").build());
* }</pre>
*
* @param tracerProviderBuilder the tracer provider builder (ID generator will be overridden)
* @param contextExtractor extracts parent trace context from the Lambda environment
* @param enableMdc if true, injects traceId/spanId/otelTraceSampled into SLF4J MDC for log correlation
* @param workflowSpanName the name for the Workflow root span
* @param config the plugin configuration
*/
public ExecutionOtelPlugin(
SdkTracerProviderBuilder tracerProviderBuilder,
ContextExtractor contextExtractor,
boolean enableMdc,
String workflowSpanName) {
public ExecutionOtelPlugin(SdkTracerProviderBuilder tracerProviderBuilder, OtelPluginConfig config) {
this.idGenerator = new DeterministicIdGenerator();

this.sdkTracerProvider =
tracerProviderBuilder.setIdGenerator(idGenerator).build();
this.tracer = sdkTracerProvider.get(INSTRUMENTATION_NAME);
this.contextExtractor = contextExtractor;
this.enableMdc = enableMdc;
this.workflowSpanName = workflowSpanName != null ? workflowSpanName : DEFAULT_WORKFLOW_SPAN_NAME;
this.tracer = sdkTracerProvider.get(config.instrumentationName());
this.contextExtractor = config.contextExtractor();
this.enableMdc = config.enableMdc();
this.workflowSpanName = config.workflowSpanName();
this.providerSource = ProviderSource.EXPLICIT;
}

private ExecutionOtelPlugin(TracerProvider tracerProvider, DeterministicIdGenerator idGenerator) {
this.idGenerator = idGenerator;
this.sdkTracerProvider = OtelPluginSupport.getSdkTracerProviderForFlush(tracerProvider, "ExecutionOtelPlugin");
this.tracer = tracerProvider.get(INSTRUMENTATION_NAME);
/**
* Creates a Workflow-rooted OTel plugin from configuration alone (no caller-supplied tracer provider builder).
*
* <p>The provider is taken from {@link OtelPluginConfig#providerSource()}: {@link ProviderSource#GLOBAL} uses the
* ADOT/global provider, otherwise the default {@link ProviderSource#AUTO_OTLP} builds a plugin-owned OTLP/HTTP
* provider (matching the JavaScript and Python SDK plugins). {@link ProviderSource#EXPLICIT} is rejected here —
* supply a {@code SdkTracerProviderBuilder} via the two-arg constructor for that.
*
* @param config the plugin configuration
* @throws IllegalArgumentException if {@code config.providerSource()} is {@link ProviderSource#EXPLICIT}
*/
public ExecutionOtelPlugin(OtelPluginConfig config) {
this.contextExtractor = config.contextExtractor();
this.enableMdc = config.enableMdc();
this.workflowSpanName = config.workflowSpanName();

var setup = OtelPluginSupport.resolveConfiguredProvider(config, "ExecutionOtelPlugin");
this.providerSource = setup.source();
this.idGenerator = setup.idGenerator();
this.sdkTracerProvider = setup.sdkTracerProvider();
this.tracer = setup.tracer();
}

this.contextExtractor = new XRayContextExtractor();
this.enableMdc = true;
this.workflowSpanName = DEFAULT_WORKFLOW_SPAN_NAME;
/** The tier that produced this plugin's tracer provider. */
public ProviderSource providerSource() {
return providerSource;
}

// ─── Invocation hooks ────────────────────────────────────────────────
Expand Down Expand Up @@ -581,12 +591,4 @@ private static String attemptKey(String operationId, Integer attempt) {
private static ExtractedContext extractCurrentSpanContext() {
return OtelPluginSupport.extractCurrentSpanContext();
}

private static TracerProvider getDefaultTracerProvider() {
return OtelPluginSupport.getDefaultTracerProvider("ExecutionOtelPlugin");
}

private static DeterministicIdGenerator createDefaultIdGenerator() {
return OtelPluginSupport.createDefaultIdGenerator();
}
}
Loading