Skip to content

Commit 3a72011

Browse files
committed
Introduce Span Links and TraceContextPropagationMode for advanced trace context handling.
- Refactor `TracingOpenTelemetryAspect` to support span links via extracted trace context. - Add `TraceContextPropagationMode` with `PARENT` and `LINK` options, configurable via environment variable. - Implement `ExtractedTraceContext` to encapsulate parent/linked contexts. - Update context extractors (SNS, SQS, S3, DynamoDB, Kinesis, API Gateway) to return `ExtractedTraceContext`. - Enhance span creation to include multiple links where applicable. - Deprecate and remove certain tests for unsupported scenarios.
1 parent 371ae83 commit 3a72011

15 files changed

Lines changed: 248 additions & 148 deletions

powertools-tracing-opentelemetry/src/main/java/software/amazon/lambda/powertools/tracing/opentelemetry/TracingOpenTelemetry.java

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,16 @@
1818

1919
import io.opentelemetry.api.common.Attributes;
2020
import io.opentelemetry.api.trace.Span;
21+
import io.opentelemetry.api.trace.SpanBuilder;
22+
import io.opentelemetry.api.trace.SpanContext;
2123
import io.opentelemetry.api.trace.SpanKind;
2224
import io.opentelemetry.api.trace.Tracer;
2325
import io.opentelemetry.context.Context;
2426
import io.opentelemetry.context.propagation.TextMapGetter;
2527
import io.opentelemetry.context.propagation.TextMapPropagator;
2628
import io.opentelemetry.context.propagation.TextMapSetter;
29+
import java.util.Collections;
30+
import java.util.List;
2731
import java.util.Objects;
2832
import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor;
2933
import software.amazon.lambda.powertools.tracing.opentelemetry.context.LambdaEventContextExtractorResolver;
@@ -101,26 +105,33 @@ public SpanScope addSpan(String name, SpanKind kind, Attributes attributes) {
101105
Objects.requireNonNull(name, "name must not be null");
102106
Objects.requireNonNull(kind, "kind must not be null");
103107
Objects.requireNonNull(attributes, "attributes must not be null");
104-
105-
Span span = tracer.spanBuilder(name)
106-
.setSpanKind(kind)
107-
.setAllAttributes(attributes)
108-
.startSpan();
109-
110-
return new SpanScope(span);
108+
return addSpan(name, kind, attributes, Context.current());
111109
}
112110

111+
113112
public SpanScope addSpan(String name, SpanKind kind, Attributes attributes, Context parentContext) {
114113

115114
Objects.requireNonNull(parentContext, "parentContext must not be null");
115+
return addSpan(name, kind, attributes, parentContext, Collections.emptyList());
116+
}
116117

117-
Span span = tracer.spanBuilder(name)
118+
public SpanScope addSpan(
119+
String spanName,
120+
SpanKind spanKind,
121+
Attributes attributes,
122+
Context parentContext,
123+
List<SpanContext> spanContexts
124+
) {
125+
126+
SpanBuilder spanBuilder = tracer
127+
.spanBuilder(spanName)
128+
.setSpanKind(spanKind)
118129
.setParent(parentContext)
119-
.setSpanKind(kind)
120-
.setAllAttributes(attributes)
121-
.startSpan();
130+
.setAllAttributes(attributes);
131+
132+
spanContexts.forEach(spanBuilder::addLink);
122133

123-
return new SpanScope(span);
134+
return new SpanScope(spanBuilder.startSpan());
124135
}
125136

126137

powertools-tracing-opentelemetry/src/main/java/software/amazon/lambda/powertools/tracing/opentelemetry/context/ApiGatewayTraceContextExtractor.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import io.opentelemetry.api.trace.Span;
55
import io.opentelemetry.context.Context;
66
import io.opentelemetry.context.propagation.TextMapPropagator;
7+
import java.util.List;
78
import java.util.Map;
89
import java.util.stream.Collectors;
910
import software.amazon.lambda.powertools.tracing.opentelemetry.provider.OpenTelemetryProvider;
@@ -17,21 +18,23 @@ public boolean supports(Object event) {
1718
}
1819

1920
@Override
20-
public Context extract(Object event, Context parentContext, TextMapPropagator propagator) {
21+
public ExtractedTraceContext extract(Object event, Context parentContext, TextMapPropagator propagator) {
2122

2223
APIGatewayProxyRequestEvent apiGatewayEvent = (APIGatewayProxyRequestEvent) event;
2324

2425
Map<String, String> headers = apiGatewayEvent.getHeaders();
2526

2627
if (headers == null || headers.isEmpty()) {
27-
return parentContext;
28+
return new ExtractedTraceContext(parentContext, List.of());
2829
}
2930

30-
return propagator.extract(
31+
Context context = propagator.extract(
3132
parentContext,
3233
headers,
3334
OpenTelemetryProvider.textMapGetter()
3435
);
36+
37+
return new ExtractedTraceContext(context, List.of());
3538
}
3639

3740
@Override

powertools-tracing-opentelemetry/src/main/java/software/amazon/lambda/powertools/tracing/opentelemetry/context/DynamoDbTraceContextExtractor.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import io.opentelemetry.api.trace.Span;
55
import io.opentelemetry.context.Context;
66
import io.opentelemetry.context.propagation.TextMapPropagator;
7+
import java.util.List;
78
import java.util.Objects;
89

910
public final class DynamoDbTraceContextExtractor implements LambdaEventContextExtractor {
@@ -14,7 +15,7 @@ public boolean supports(Object event) {
1415
}
1516

1617
@Override
17-
public Context extract(Object event, Context parentContext, TextMapPropagator propagator) {
18+
public ExtractedTraceContext extract(Object event, Context parentContext, TextMapPropagator propagator) {
1819

1920
/*
2021
* DynamoDB Streams records do not expose message attributes
@@ -25,7 +26,7 @@ public Context extract(Object event, Context parentContext, TextMapPropagator pr
2526
* defined by a dedicated propagation strategy if supported in
2627
* the future.
2728
*/
28-
return parentContext;
29+
return new ExtractedTraceContext(parentContext, List.of());
2930
}
3031

3132
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package software.amazon.lambda.powertools.tracing.opentelemetry.context;
2+
3+
import io.opentelemetry.api.trace.SpanContext;
4+
import io.opentelemetry.context.Context;
5+
import java.util.List;
6+
7+
public final class ExtractedTraceContext {
8+
9+
private final Context parentContext;
10+
private final List<SpanContext> spanContexts;
11+
12+
public ExtractedTraceContext(Context parentContext, List<SpanContext> spanContexts) {
13+
this.parentContext = parentContext;
14+
this.spanContexts = spanContexts;
15+
}
16+
17+
public Context context() {
18+
return parentContext;
19+
}
20+
21+
public List<SpanContext> spanContexts() {
22+
return spanContexts;
23+
}
24+
}

powertools-tracing-opentelemetry/src/main/java/software/amazon/lambda/powertools/tracing/opentelemetry/context/KinesisTraceContextExtractor.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import io.opentelemetry.api.trace.Span;
55
import io.opentelemetry.context.Context;
66
import io.opentelemetry.context.propagation.TextMapPropagator;
7+
import java.util.List;
78

89
public final class KinesisTraceContextExtractor
910
implements LambdaEventContextExtractor {
@@ -14,7 +15,7 @@ public boolean supports(Object event) {
1415
}
1516

1617
@Override
17-
public Context extract(Object event, Context parentContext, TextMapPropagator propagator) {
18+
public ExtractedTraceContext extract(Object event, Context parentContext, TextMapPropagator propagator) {
1819

1920
/*
2021
* Kinesis records do not expose message attributes
@@ -26,7 +27,7 @@ public Context extract(Object event, Context parentContext, TextMapPropagator pr
2627
* the future.
2728
*/
2829

29-
return parentContext;
30+
return new ExtractedTraceContext(parentContext, List.of());
3031
}
3132

3233
@Override

powertools-tracing-opentelemetry/src/main/java/software/amazon/lambda/powertools/tracing/opentelemetry/context/LambdaEventContextExtractor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public interface LambdaEventContextExtractor {
88

99
boolean supports(Object event);
1010

11-
Context extract(Object event, Context parentContext, TextMapPropagator propagator);
12-
1311
void enrichSpan(Object event, Span span);
12+
13+
ExtractedTraceContext extract(Object event, Context parentContext, TextMapPropagator propagator);
1414
}

powertools-tracing-opentelemetry/src/main/java/software/amazon/lambda/powertools/tracing/opentelemetry/context/LambdaEventContextExtractorResolver.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public static LambdaEventContextExtractorResolver create() {
2727
);
2828
}
2929

30-
public Context extract(Object event, Context parentContext, TextMapPropagator propagator) {
30+
public ExtractedTraceContext extract(Object event, Context parentContext, TextMapPropagator propagator) {
3131

3232
return extractors.stream()
3333
.filter(extractor -> extractor.supports(event))
@@ -37,7 +37,7 @@ public Context extract(Object event, Context parentContext, TextMapPropagator pr
3737
event,
3838
parentContext,
3939
propagator))
40-
.orElse(parentContext);
40+
.orElse(new ExtractedTraceContext(parentContext, List.of()));
4141
}
4242

4343
public void enrichSpan(Object event, Span span) {

powertools-tracing-opentelemetry/src/main/java/software/amazon/lambda/powertools/tracing/opentelemetry/context/S3TraceContextExtractor.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import io.opentelemetry.api.trace.Span;
66
import io.opentelemetry.context.Context;
77
import io.opentelemetry.context.propagation.TextMapPropagator;
8+
import java.util.List;
89
import java.util.Objects;
910

1011
public final class S3TraceContextExtractor implements LambdaEventContextExtractor {
@@ -15,7 +16,7 @@ public boolean supports(Object event) {
1516
}
1617

1718
@Override
18-
public Context extract(Object event, Context parentContext, TextMapPropagator propagator) {
19+
public ExtractedTraceContext extract(Object event, Context parentContext, TextMapPropagator propagator) {
1920

2021
/*
2122
* S3 event notifications do not expose message attributes
@@ -25,7 +26,7 @@ public Context extract(Object event, Context parentContext, TextMapPropagator pr
2526
* Do not assume that traceparent/tracestate are embedded
2627
* inside the S3 event payload.
2728
*/
28-
return parentContext;
29+
return new ExtractedTraceContext(parentContext, List.of());
2930
}
3031

3132
@Override

powertools-tracing-opentelemetry/src/main/java/software/amazon/lambda/powertools/tracing/opentelemetry/context/SnsTraceContextExtractor.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
import com.amazonaws.services.lambda.runtime.events.SNSEvent;
44
import io.opentelemetry.api.trace.Span;
5+
import io.opentelemetry.api.trace.SpanContext;
56
import io.opentelemetry.context.Context;
67
import io.opentelemetry.context.propagation.TextMapPropagator;
8+
import java.util.ArrayList;
9+
import java.util.List;
710
import java.util.Map;
811
import java.util.stream.Collectors;
912
import software.amazon.lambda.powertools.tracing.opentelemetry.provider.OpenTelemetryProvider;
@@ -16,14 +19,16 @@ public boolean supports(Object event) {
1619
}
1720

1821
@Override
19-
public Context extract(Object event, Context parentContext, TextMapPropagator propagator) {
22+
public ExtractedTraceContext extract(Object event, Context parentContext, TextMapPropagator propagator) {
2023

2124
SNSEvent snsEvent = (SNSEvent) event;
2225

2326
if (snsEvent.getRecords() == null || snsEvent.getRecords().isEmpty()) {
24-
return parentContext;
27+
return new ExtractedTraceContext(parentContext, List.of());
2528
}
2629

30+
List<SpanContext> spanContexts = new ArrayList<>();
31+
2732
for (SNSEvent.SNSRecord record : snsEvent.getRecords()) {
2833

2934
if (record == null || record.getSNS() == null) {
@@ -50,17 +55,23 @@ public Context extract(Object event, Context parentContext, TextMapPropagator pr
5055
}
5156

5257
Context extractedContext = propagator.extract(
53-
parentContext,
58+
Context.root(),
5459
propagationAttributes,
5560
OpenTelemetryProvider.textMapGetter()
5661
);
5762

58-
if (extractedContext != parentContext) {
59-
return extractedContext;
63+
SpanContext spanContext = Span.fromContext(extractedContext).getSpanContext();
64+
65+
if (spanContext.isValid()) {
66+
spanContexts.add(spanContext);
6067
}
6168
}
6269

63-
return parentContext;
70+
Context parent = spanContexts.isEmpty()
71+
? parentContext
72+
: Context.root().with(Span.wrap(spanContexts.get(0)));
73+
74+
return new ExtractedTraceContext(parent, spanContexts);
6475
}
6576

6677
@Override

powertools-tracing-opentelemetry/src/main/java/software/amazon/lambda/powertools/tracing/opentelemetry/context/SqsTraceContextExtractor.java

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
import com.amazonaws.services.lambda.runtime.events.SQSEvent;
44
import io.opentelemetry.api.trace.Span;
5+
import io.opentelemetry.api.trace.SpanContext;
56
import io.opentelemetry.context.Context;
67
import io.opentelemetry.context.propagation.TextMapPropagator;
8+
import java.util.ArrayList;
9+
import java.util.List;
710
import java.util.Map;
811
import java.util.stream.Collectors;
912
import software.amazon.lambda.powertools.tracing.opentelemetry.provider.OpenTelemetryProvider;
@@ -16,14 +19,16 @@ public boolean supports(Object event) {
1619
}
1720

1821
@Override
19-
public Context extract(Object event, Context parentContext, TextMapPropagator propagator) {
22+
public ExtractedTraceContext extract(Object event, Context parentContext, TextMapPropagator propagator) {
2023

2124
SQSEvent sqsEvent = (SQSEvent) event;
2225

2326
if (sqsEvent.getRecords() == null || sqsEvent.getRecords().isEmpty()) {
24-
return parentContext;
27+
return new ExtractedTraceContext(parentContext, List.of());
2528
}
2629

30+
List<SpanContext> spanContexts = new ArrayList<>();
31+
2732
for (SQSEvent.SQSMessage message : sqsEvent.getRecords()) {
2833

2934
if (message == null || message.getMessageAttributes() == null) {
@@ -46,18 +51,23 @@ public Context extract(Object event, Context parentContext, TextMapPropagator pr
4651
));
4752

4853
Context extractedContext = propagator.extract(
49-
parentContext,
54+
Context.root(),
5055
propagationAttributes,
5156
OpenTelemetryProvider.textMapGetter()
5257
);
5358

54-
if (Span.fromContext(extractedContext).getSpanContext().isValid()) {
59+
SpanContext spanContext = Span.fromContext(extractedContext).getSpanContext();
5560

56-
return extractedContext;
61+
if (spanContext.isValid()) {
62+
spanContexts.add(spanContext);
5763
}
5864
}
5965

60-
return parentContext;
66+
Context parent = spanContexts.isEmpty()
67+
? parentContext
68+
: Context.root().with(Span.wrap(spanContexts.get(0)));
69+
70+
return new ExtractedTraceContext(parent, spanContexts);
6171
}
6272

6373
@Override

0 commit comments

Comments
 (0)