Skip to content

Commit d48736a

Browse files
authored
Add xray propagators that prioritizes xray environment variable (#1032)
1 parent 1348bf3 commit d48736a

File tree

6 files changed

+318
-39
lines changed

6 files changed

+318
-39
lines changed

aws-xray-propagator/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,8 @@ otelJava.moduleName.set("io.opentelemetry.contrib.awsxray.propagator")
1010
dependencies {
1111
api("io.opentelemetry:opentelemetry-api")
1212
compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi")
13+
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
14+
testImplementation("io.opentelemetry:opentelemetry-sdk-trace")
15+
testImplementation("io.opentelemetry:opentelemetry-sdk-testing")
16+
testImplementation("uk.org.webcompere:system-stubs-jupiter:2.0.2")
1317
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.awsxray.propagator;
7+
8+
import io.opentelemetry.context.propagation.TextMapPropagator;
9+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
10+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurablePropagatorProvider;
11+
12+
/**
13+
* A {@link ConfigurablePropagatorProvider} which allows enabling the {@link
14+
* AwsXrayLambdaPropagator} with the propagator name {@code xray-lambda}.
15+
*/
16+
public final class AwsXrayLambdaConfigurablePropagator implements ConfigurablePropagatorProvider {
17+
@Override
18+
public TextMapPropagator getPropagator(ConfigProperties config) {
19+
return AwsXrayLambdaPropagator.getInstance();
20+
}
21+
22+
@Override
23+
public String getName() {
24+
return "xray-lambda";
25+
}
26+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.awsxray.propagator;
7+
8+
import io.opentelemetry.api.trace.Span;
9+
import io.opentelemetry.context.Context;
10+
import io.opentelemetry.context.propagation.TextMapGetter;
11+
import io.opentelemetry.context.propagation.TextMapPropagator;
12+
import io.opentelemetry.context.propagation.TextMapSetter;
13+
import java.util.Collections;
14+
import java.util.List;
15+
import java.util.Map;
16+
import java.util.Set;
17+
import javax.annotation.Nullable;
18+
19+
/**
20+
* Implementation of the AWS X-Ray Trace Header propagation protocol but with special handling for
21+
* Lambda's {@code _X_AMZN_TRACE_ID} environment variable and {@code com.amazonaws.xray.traceHeader}
22+
* system property.
23+
*
24+
* <p>To register the X-Ray propagator together with default propagator when using the SDK:
25+
*
26+
* <pre>{@code
27+
* OpenTelemetrySdk.builder()
28+
* .setPropagators(
29+
* ContextPropagators.create(
30+
* TextMapPropagator.composite(
31+
* W3CTraceContextPropagator.getInstance(),
32+
* AwsXrayLambdaPropagator.getInstance())))
33+
* .build();
34+
* }</pre>
35+
*/
36+
public final class AwsXrayLambdaPropagator implements TextMapPropagator {
37+
38+
private static final String AWS_TRACE_HEADER_ENV_KEY = "_X_AMZN_TRACE_ID";
39+
private static final String AWS_TRACE_HEADER_PROP = "com.amazonaws.xray.traceHeader";
40+
private final AwsXrayPropagator xrayPropagator = AwsXrayPropagator.getInstance();
41+
private static final AwsXrayLambdaPropagator INSTANCE = new AwsXrayLambdaPropagator();
42+
43+
private AwsXrayLambdaPropagator() {
44+
// singleton
45+
}
46+
47+
public static AwsXrayLambdaPropagator getInstance() {
48+
return INSTANCE;
49+
}
50+
51+
@Override
52+
public List<String> fields() {
53+
return xrayPropagator.fields();
54+
}
55+
56+
@Override
57+
public <C> void inject(Context context, @Nullable C carrier, TextMapSetter<C> setter) {
58+
xrayPropagator.inject(context, carrier, setter);
59+
}
60+
61+
@Override
62+
public <C> Context extract(Context context, @Nullable C carrier, TextMapGetter<C> getter) {
63+
Context xrayContext = xrayPropagator.extract(context, carrier, getter);
64+
65+
if (Span.fromContext(context).getSpanContext().isValid()) {
66+
return xrayContext;
67+
}
68+
69+
String traceHeader = System.getProperty(AWS_TRACE_HEADER_PROP);
70+
if (isEmptyOrNull(traceHeader)) {
71+
traceHeader = System.getenv(AWS_TRACE_HEADER_ENV_KEY);
72+
}
73+
if (isEmptyOrNull(traceHeader)) {
74+
return xrayContext;
75+
}
76+
return xrayPropagator.extract(
77+
xrayContext,
78+
Collections.singletonMap(AwsXrayPropagator.TRACE_HEADER_KEY, traceHeader),
79+
MapGetter.INSTANCE);
80+
}
81+
82+
private static boolean isEmptyOrNull(@Nullable String value) {
83+
return value == null || value.isEmpty();
84+
}
85+
86+
private enum MapGetter implements TextMapGetter<Map<String, String>> {
87+
INSTANCE;
88+
89+
@Override
90+
public Set<String> keys(Map<String, String> map) {
91+
return map.keySet();
92+
}
93+
94+
@Override
95+
@Nullable
96+
public String get(@Nullable Map<String, String> map, String s) {
97+
return map == null ? null : map.get(s);
98+
}
99+
}
100+
}

aws-xray-propagator/src/main/java/io/opentelemetry/contrib/awsxray/propagator/AwsXrayPropagator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
* ContextPropagators.create(
3939
* TextMapPropagator.composite(
4040
* W3CTraceContextPropagator.getInstance(),
41-
* AWSXrayPropagator.getInstance())))
41+
* AwsXrayPropagator.getInstance())))
4242
* .build();
4343
* }</pre>
4444
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.awsxray.propagator;
7+
8+
import static io.opentelemetry.contrib.awsxray.propagator.AwsXrayPropagator.TRACE_HEADER_KEY;
9+
import static org.assertj.core.api.Assertions.assertThat;
10+
11+
import io.opentelemetry.api.trace.Span;
12+
import io.opentelemetry.api.trace.SpanContext;
13+
import io.opentelemetry.api.trace.TraceFlags;
14+
import io.opentelemetry.api.trace.TraceState;
15+
import io.opentelemetry.api.trace.Tracer;
16+
import io.opentelemetry.context.Context;
17+
import io.opentelemetry.context.propagation.TextMapPropagator;
18+
import io.opentelemetry.sdk.trace.ReadableSpan;
19+
import io.opentelemetry.sdk.trace.SdkTracerProvider;
20+
import io.opentelemetry.sdk.trace.data.LinkData;
21+
import java.util.Collections;
22+
import java.util.Map;
23+
import org.junit.jupiter.api.BeforeEach;
24+
import org.junit.jupiter.api.Test;
25+
import org.junit.jupiter.api.extension.ExtendWith;
26+
import uk.org.webcompere.systemstubs.environment.EnvironmentVariables;
27+
import uk.org.webcompere.systemstubs.jupiter.SystemStub;
28+
import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension;
29+
import uk.org.webcompere.systemstubs.properties.SystemProperties;
30+
31+
@ExtendWith(SystemStubsExtension.class)
32+
class AwsXrayLambdaPropagatorTest extends AwsXrayPropagatorTest {
33+
34+
@SystemStub final EnvironmentVariables environmentVariables = new EnvironmentVariables();
35+
@SystemStub final SystemProperties systemProperties = new SystemProperties();
36+
37+
private Tracer tracer;
38+
39+
@Override
40+
TextMapPropagator propagator() {
41+
return AwsXrayLambdaPropagator.getInstance();
42+
}
43+
44+
@BeforeEach
45+
public void setup() {
46+
tracer = SdkTracerProvider.builder().build().get("awsxray");
47+
}
48+
49+
@Test
50+
void extract_fromEnvironmentVariable() {
51+
environmentVariables.set(
52+
"_X_AMZN_TRACE_ID",
53+
"Root=1-00000001-d188f8fa79d48a391a778fa6;Parent=53995c3f42cd8ad8;Sampled=1;Foo=Bar");
54+
55+
assertThat(
56+
getSpanContext(propagator().extract(Context.current(), Collections.emptyMap(), GETTER)))
57+
.isEqualTo(
58+
SpanContext.createFromRemoteParent(
59+
"00000001d188f8fa79d48a391a778fa6",
60+
SPAN_ID,
61+
TraceFlags.getSampled(),
62+
TraceState.getDefault()));
63+
}
64+
65+
@Test
66+
void extract_fromSystemProperty() {
67+
systemProperties.set(
68+
"com.amazonaws.xray.traceHeader",
69+
"Root=1-00000001-d188f8fa79d48a391a778fa6;Parent=53995c3f42cd8ad8;Sampled=1;Foo=Bar");
70+
71+
assertThat(
72+
getSpanContext(propagator().extract(Context.current(), Collections.emptyMap(), GETTER)))
73+
.isEqualTo(
74+
SpanContext.createFromRemoteParent(
75+
"00000001d188f8fa79d48a391a778fa6",
76+
SPAN_ID,
77+
TraceFlags.getSampled(),
78+
TraceState.getDefault()));
79+
}
80+
81+
@Test
82+
void extract_systemPropertyBeforeEnvironmentVariable() {
83+
environmentVariables.set(
84+
"_X_AMZN_TRACE_ID",
85+
"Root=1-00000001-240000000000000000000001;Parent=1600000000000001;Sampled=1;Foo=Bar");
86+
systemProperties.set(
87+
"com.amazonaws.xray.traceHeader",
88+
"Root=1-00000002-240000000000000000000002;Parent=1600000000000002;Sampled=1;Foo=Baz");
89+
90+
assertThat(
91+
getSpanContext(propagator().extract(Context.current(), Collections.emptyMap(), GETTER)))
92+
.isEqualTo(
93+
SpanContext.createFromRemoteParent(
94+
"00000002240000000000000000000002",
95+
"1600000000000002",
96+
TraceFlags.getSampled(),
97+
TraceState.getDefault()));
98+
}
99+
100+
@Test
101+
void addLink_SystemProperty() {
102+
Map<String, String> carrier =
103+
Collections.singletonMap(
104+
TRACE_HEADER_KEY,
105+
"Root=1-00000001-240000000000000000000001;Parent=1600000000000001;Sampled=1");
106+
environmentVariables.set(
107+
"_X_AMZN_TRACE_ID",
108+
"Root=1-00000002-240000000000000000000002;Parent=1600000000000002;Sampled=1;Foo=Bar");
109+
systemProperties.set(
110+
"com.amazonaws.xray.traceHeader",
111+
"Root=1-00000003-240000000000000000000003;Parent=1600000000000003;Sampled=1;Foo=Baz");
112+
113+
Context extract = propagator().extract(Context.current(), carrier, GETTER);
114+
ReadableSpan span =
115+
(ReadableSpan)
116+
tracer
117+
.spanBuilder("test")
118+
.setParent(extract)
119+
.addLink(
120+
Span.fromContext(propagator().extract(extract, carrier, GETTER))
121+
.getSpanContext())
122+
.startSpan();
123+
assertThat(span.getParentSpanContext())
124+
.isEqualTo(
125+
SpanContext.createFromRemoteParent(
126+
"00000003240000000000000000000003",
127+
"1600000000000003",
128+
TraceFlags.getSampled(),
129+
TraceState.getDefault()));
130+
131+
assertThat(span.toSpanData().getLinks())
132+
.isEqualTo(
133+
Collections.singletonList(
134+
LinkData.create(
135+
SpanContext.createFromRemoteParent(
136+
"00000001240000000000000000000001",
137+
"1600000000000001",
138+
TraceFlags.getSampled(),
139+
TraceState.getDefault()))));
140+
}
141+
}

0 commit comments

Comments
 (0)