Skip to content

Commit 582c632

Browse files
committed
feat(otel): Add X-Ray e2e integration tests for span validation
1 parent cfe09b4 commit 582c632

6 files changed

Lines changed: 495 additions & 0 deletions

File tree

examples/pom.xml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,18 @@
5050
<version>1.63.0</version>
5151
</dependency>
5252

53+
<!-- OTLP gRPC exporter (required for X-Ray via ADOT collector) -->
54+
<dependency>
55+
<groupId>io.opentelemetry</groupId>
56+
<artifactId>opentelemetry-exporter-otlp</artifactId>
57+
<version>1.63.0</version>
58+
</dependency>
59+
<dependency>
60+
<groupId>io.grpc</groupId>
61+
<artifactId>grpc-netty-shaded</artifactId>
62+
<version>1.72.0</version>
63+
</dependency>
64+
5365
<!-- AWS Lambda Java Core -->
5466
<dependency>
5567
<groupId>com.amazonaws</groupId>
@@ -91,6 +103,11 @@
91103
<artifactId>sts</artifactId>
92104
<scope>test</scope>
93105
</dependency>
106+
<dependency>
107+
<groupId>software.amazon.awssdk</groupId>
108+
<artifactId>xray</artifactId>
109+
<scope>test</scope>
110+
</dependency>
94111
<dependency>
95112
<groupId>org.junit.jupiter</groupId>
96113
<artifactId>junit-jupiter</artifactId>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
package software.amazon.lambda.durable.examples.otel;
4+
5+
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
6+
import io.opentelemetry.sdk.trace.SdkTracerProvider;
7+
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
8+
import software.amazon.lambda.durable.DurableConfig;
9+
import software.amazon.lambda.durable.DurableContext;
10+
import software.amazon.lambda.durable.DurableHandler;
11+
import software.amazon.lambda.durable.examples.types.GreetingRequest;
12+
import software.amazon.lambda.durable.otel.OpenTelemetryDurablePlugin;
13+
14+
/**
15+
* OTel + X-Ray example: simple steps in a single invocation.
16+
*
17+
* <p>Exports spans via OTLP gRPC to the ADOT collector sidecar, which forwards to X-Ray. Used by
18+
* {@code OtelXRayIntegrationTest} to verify spans appear correctly in X-Ray.
19+
*
20+
* <p>Expected trace structure in X-Ray:
21+
*
22+
* <pre>
23+
* durable.invocation
24+
* ├── durable.step:create-greeting
25+
* │ └── durable.step:create-greeting [attempt 1]
26+
* └── durable.step:transform
27+
* └── durable.step:transform [attempt 1]
28+
* </pre>
29+
*/
30+
public class OtelXRayStepExample extends DurableHandler<GreetingRequest, String> {
31+
32+
@Override
33+
protected DurableConfig createConfiguration() {
34+
var otlpExporter = OtlpGrpcSpanExporter.builder()
35+
.setEndpoint("http://localhost:4317")
36+
.build();
37+
38+
var otelPlugin = new OpenTelemetryDurablePlugin(
39+
SdkTracerProvider.builder().addSpanProcessor(SimpleSpanProcessor.create(otlpExporter)));
40+
41+
return DurableConfig.builder().withPlugins(otelPlugin).build();
42+
}
43+
44+
@Override
45+
public String handleRequest(GreetingRequest input, DurableContext context) {
46+
context.getLogger().info("Starting OTel X-Ray step example for {}", input.getName());
47+
48+
var greeting = context.step("create-greeting", String.class, stepCtx -> "Hello, " + input.getName());
49+
50+
var result = context.step("transform", String.class, stepCtx -> greeting.toUpperCase() + "!");
51+
52+
context.getLogger().info("OTel X-Ray step example complete: {}", result);
53+
return result;
54+
}
55+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
package software.amazon.lambda.durable.examples.otel;
4+
5+
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
6+
import io.opentelemetry.sdk.trace.SdkTracerProvider;
7+
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
8+
import java.time.Duration;
9+
import software.amazon.lambda.durable.DurableConfig;
10+
import software.amazon.lambda.durable.DurableContext;
11+
import software.amazon.lambda.durable.DurableHandler;
12+
import software.amazon.lambda.durable.examples.types.GreetingRequest;
13+
import software.amazon.lambda.durable.otel.OpenTelemetryDurablePlugin;
14+
15+
/**
16+
* OTel + X-Ray example: step → wait → step pattern that forces multiple Lambda invocations.
17+
*
18+
* <p>This handler exercises the critical multi-invocation tracing scenario:
19+
*
20+
* <ol>
21+
* <li>Invocation 1: "before-wait" step completes → wait suspends execution
22+
* <li>Invocation 2: replays "before-wait" (no-op) → wait completes → "after-wait" step runs
23+
* </ol>
24+
*
25+
* <p>Used by {@code OtelXRayIntegrationTest} to verify that deterministic trace IDs correctly stitch spans from
26+
* multiple invocations into a single X-Ray trace.
27+
*
28+
* <p>Expected trace structure in X-Ray:
29+
*
30+
* <pre>
31+
* durable.invocation (invocation 1)
32+
* durable.invocation (invocation 2)
33+
* durable.step:before-wait
34+
* │ └── durable.step:before-wait [attempt 1]
35+
* durable.wait:pause
36+
* durable.step:after-wait
37+
* └── durable.step:after-wait [attempt 1]
38+
* </pre>
39+
*
40+
* <p>All spans share the same deterministic trace ID derived from the execution ARN.
41+
*/
42+
public class OtelXRayWaitExample extends DurableHandler<GreetingRequest, String> {
43+
44+
@Override
45+
protected DurableConfig createConfiguration() {
46+
var otlpExporter = OtlpGrpcSpanExporter.builder()
47+
.setEndpoint("http://localhost:4317")
48+
.build();
49+
50+
var otelPlugin = new OpenTelemetryDurablePlugin(
51+
SdkTracerProvider.builder().addSpanProcessor(SimpleSpanProcessor.create(otlpExporter)));
52+
53+
return DurableConfig.builder().withPlugins(otelPlugin).build();
54+
}
55+
56+
@Override
57+
public String handleRequest(GreetingRequest input, DurableContext context) {
58+
context.getLogger().info("Starting OTel X-Ray wait example for {}", input.getName());
59+
60+
var before = context.step("before-wait", String.class, stepCtx -> "Prepared: " + input.getName());
61+
62+
// This wait forces Lambda to suspend and re-invoke after the duration
63+
context.wait("pause", Duration.ofSeconds(5));
64+
65+
var after = context.step("after-wait", String.class, stepCtx -> before + " | Resumed and completed");
66+
67+
context.getLogger().info("OTel X-Ray wait example complete: {}", after);
68+
return after;
69+
}
70+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
receivers:
2+
otlp:
3+
protocols:
4+
grpc:
5+
endpoint: "localhost:4317"
6+
7+
exporters:
8+
awsxray:
9+
region: "us-west-2"
10+
11+
service:
12+
pipelines:
13+
traces:
14+
receivers: [otlp]
15+
exporters: [awsxray]

0 commit comments

Comments
 (0)