-
Notifications
You must be signed in to change notification settings - Fork 154
Add xray propagators that prioritizes xray environment variable #1032
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bb8954c
bc9b8ab
4a323a2
026bd4e
2fafee5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.contrib.awsxray.propagator; | ||
|
||
import io.opentelemetry.context.propagation.TextMapPropagator; | ||
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; | ||
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurablePropagatorProvider; | ||
|
||
/** | ||
* A {@link ConfigurablePropagatorProvider} which allows enabling the {@link | ||
* AwsXrayLambdaPropagator} with the propagator name {@code xray-lambda}. | ||
*/ | ||
public final class AwsXrayLambdaConfigurablePropagator implements ConfigurablePropagatorProvider { | ||
@Override | ||
public TextMapPropagator getPropagator(ConfigProperties config) { | ||
return AwsXrayLambdaPropagator.getInstance(); | ||
} | ||
|
||
@Override | ||
public String getName() { | ||
return "xray-lambda"; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.contrib.awsxray.propagator; | ||
|
||
import io.opentelemetry.api.trace.Span; | ||
import io.opentelemetry.context.Context; | ||
import io.opentelemetry.context.propagation.TextMapGetter; | ||
import io.opentelemetry.context.propagation.TextMapPropagator; | ||
import io.opentelemetry.context.propagation.TextMapSetter; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import javax.annotation.Nullable; | ||
|
||
/** | ||
* Implementation of the AWS X-Ray Trace Header propagation protocol but with special handling for | ||
* Lambda's {@code _X_AMZN_TRACE_ID} environment variable and {@code com.amazonaws.xray.traceHeader} | ||
* system property. | ||
* | ||
* <p>To register the X-Ray propagator together with default propagator when using the SDK: | ||
* | ||
* <pre>{@code | ||
* OpenTelemetrySdk.builder() | ||
* .setPropagators( | ||
* ContextPropagators.create( | ||
* TextMapPropagator.composite( | ||
* W3CTraceContextPropagator.getInstance(), | ||
* AwsXrayLambdaPropagator.getInstance()))) | ||
* .build(); | ||
* }</pre> | ||
*/ | ||
public final class AwsXrayLambdaPropagator implements TextMapPropagator { | ||
|
||
private static final String AWS_TRACE_HEADER_ENV_KEY = "_X_AMZN_TRACE_ID"; | ||
private static final String AWS_TRACE_HEADER_PROP = "com.amazonaws.xray.traceHeader"; | ||
private final AwsXrayPropagator xrayPropagator = AwsXrayPropagator.getInstance(); | ||
private static final AwsXrayLambdaPropagator INSTANCE = new AwsXrayLambdaPropagator(); | ||
|
||
private AwsXrayLambdaPropagator() { | ||
// singleton | ||
} | ||
|
||
public static AwsXrayLambdaPropagator getInstance() { | ||
return INSTANCE; | ||
} | ||
|
||
@Override | ||
public List<String> fields() { | ||
return xrayPropagator.fields(); | ||
} | ||
|
||
@Override | ||
public <C> void inject(Context context, @Nullable C carrier, TextMapSetter<C> setter) { | ||
xrayPropagator.inject(context, carrier, setter); | ||
tylerbenson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
@Override | ||
public <C> Context extract(Context context, @Nullable C carrier, TextMapGetter<C> getter) { | ||
Context xrayContext = xrayPropagator.extract(context, carrier, getter); | ||
|
||
if (Span.fromContext(context).getSpanContext().isValid()) { | ||
return xrayContext; | ||
} | ||
|
||
String traceHeader = System.getProperty(AWS_TRACE_HEADER_PROP); | ||
if (isEmptyOrNull(traceHeader)) { | ||
traceHeader = System.getenv(AWS_TRACE_HEADER_ENV_KEY); | ||
} | ||
if (isEmptyOrNull(traceHeader)) { | ||
return xrayContext; | ||
} | ||
return xrayPropagator.extract( | ||
xrayContext, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Considering 2 things:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for your great help for Lambda support. Would you help me understand the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
My initial POC implemented the original spec which separated the propagators as you suggested. After adding the clause mentioned in that PR, the spec became broken which I didn't discover until I had time to implement the final spec. If you need more detail of how it was broken, please review the description in open-telemetry/semantic-conventions#778 and let me know if that's unclear. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi, @tylerbenson There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was planning on removing that line since the span linking is no longer required by the spec. Are you expecting something different? I don't understand how OTEL_SDK_DISABLED is relevant here. Are you suggesting this propagator interferes with that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AWS services would like to support the similar functionality as OTEL_SDK_DISABLED does. Take the example of Lambda, if user does not enable Active tracing, the environment variable would have the same trace context with from API GW request. With AWS w3c support, it means OTel in Lambda only need xxxx-Lambda propagator in the future. Currently we know 2 issues AWS can consider to enhance:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Why would you/we want this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It is the existing behavior, would be update to be same as OTEL_SDK_DISABLED does. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
Collections.singletonMap(AwsXrayPropagator.TRACE_HEADER_KEY, traceHeader), | ||
MapGetter.INSTANCE); | ||
} | ||
|
||
private static boolean isEmptyOrNull(@Nullable String value) { | ||
return value == null || value.isEmpty(); | ||
} | ||
|
||
private enum MapGetter implements TextMapGetter<Map<String, String>> { | ||
INSTANCE; | ||
|
||
@Override | ||
public Set<String> keys(Map<String, String> map) { | ||
return map.keySet(); | ||
} | ||
|
||
@Override | ||
@Nullable | ||
public String get(@Nullable Map<String, String> map, String s) { | ||
return map == null ? null : map.get(s); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.contrib.awsxray.propagator; | ||
|
||
import static io.opentelemetry.contrib.awsxray.propagator.AwsXrayPropagator.TRACE_HEADER_KEY; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import io.opentelemetry.api.trace.Span; | ||
import io.opentelemetry.api.trace.SpanContext; | ||
import io.opentelemetry.api.trace.TraceFlags; | ||
import io.opentelemetry.api.trace.TraceState; | ||
import io.opentelemetry.api.trace.Tracer; | ||
import io.opentelemetry.context.Context; | ||
import io.opentelemetry.context.propagation.TextMapPropagator; | ||
import io.opentelemetry.sdk.trace.ReadableSpan; | ||
import io.opentelemetry.sdk.trace.SdkTracerProvider; | ||
import io.opentelemetry.sdk.trace.data.LinkData; | ||
import java.util.Collections; | ||
import java.util.Map; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; | ||
import uk.org.webcompere.systemstubs.jupiter.SystemStub; | ||
import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; | ||
import uk.org.webcompere.systemstubs.properties.SystemProperties; | ||
|
||
@ExtendWith(SystemStubsExtension.class) | ||
class AwsXrayLambdaPropagatorTest extends AwsXrayPropagatorTest { | ||
|
||
@SystemStub final EnvironmentVariables environmentVariables = new EnvironmentVariables(); | ||
@SystemStub final SystemProperties systemProperties = new SystemProperties(); | ||
|
||
private Tracer tracer; | ||
|
||
@Override | ||
TextMapPropagator propagator() { | ||
return AwsXrayLambdaPropagator.getInstance(); | ||
} | ||
|
||
@BeforeEach | ||
public void setup() { | ||
tracer = SdkTracerProvider.builder().build().get("awsxray"); | ||
} | ||
|
||
@Test | ||
void extract_fromEnvironmentVariable() { | ||
environmentVariables.set( | ||
"_X_AMZN_TRACE_ID", | ||
"Root=1-00000001-d188f8fa79d48a391a778fa6;Parent=53995c3f42cd8ad8;Sampled=1;Foo=Bar"); | ||
|
||
assertThat( | ||
getSpanContext(propagator().extract(Context.current(), Collections.emptyMap(), GETTER))) | ||
.isEqualTo( | ||
SpanContext.createFromRemoteParent( | ||
"00000001d188f8fa79d48a391a778fa6", | ||
SPAN_ID, | ||
TraceFlags.getSampled(), | ||
TraceState.getDefault())); | ||
} | ||
|
||
@Test | ||
void extract_fromSystemProperty() { | ||
systemProperties.set( | ||
"com.amazonaws.xray.traceHeader", | ||
"Root=1-00000001-d188f8fa79d48a391a778fa6;Parent=53995c3f42cd8ad8;Sampled=1;Foo=Bar"); | ||
|
||
assertThat( | ||
getSpanContext(propagator().extract(Context.current(), Collections.emptyMap(), GETTER))) | ||
.isEqualTo( | ||
SpanContext.createFromRemoteParent( | ||
"00000001d188f8fa79d48a391a778fa6", | ||
SPAN_ID, | ||
TraceFlags.getSampled(), | ||
TraceState.getDefault())); | ||
} | ||
|
||
@Test | ||
void extract_systemPropertyBeforeEnvironmentVariable() { | ||
environmentVariables.set( | ||
"_X_AMZN_TRACE_ID", | ||
"Root=1-00000001-240000000000000000000001;Parent=1600000000000001;Sampled=1;Foo=Bar"); | ||
systemProperties.set( | ||
"com.amazonaws.xray.traceHeader", | ||
"Root=1-00000002-240000000000000000000002;Parent=1600000000000002;Sampled=1;Foo=Baz"); | ||
|
||
assertThat( | ||
getSpanContext(propagator().extract(Context.current(), Collections.emptyMap(), GETTER))) | ||
.isEqualTo( | ||
SpanContext.createFromRemoteParent( | ||
"00000002240000000000000000000002", | ||
"1600000000000002", | ||
TraceFlags.getSampled(), | ||
TraceState.getDefault())); | ||
} | ||
|
||
@Test | ||
void addLink_SystemProperty() { | ||
Map<String, String> carrier = | ||
Collections.singletonMap( | ||
TRACE_HEADER_KEY, | ||
"Root=1-00000001-240000000000000000000001;Parent=1600000000000001;Sampled=1"); | ||
environmentVariables.set( | ||
"_X_AMZN_TRACE_ID", | ||
"Root=1-00000002-240000000000000000000002;Parent=1600000000000002;Sampled=1;Foo=Bar"); | ||
systemProperties.set( | ||
"com.amazonaws.xray.traceHeader", | ||
"Root=1-00000003-240000000000000000000003;Parent=1600000000000003;Sampled=1;Foo=Baz"); | ||
|
||
Context extract = propagator().extract(Context.current(), carrier, GETTER); | ||
ReadableSpan span = | ||
(ReadableSpan) | ||
tracer | ||
.spanBuilder("test") | ||
.setParent(extract) | ||
.addLink( | ||
Span.fromContext(propagator().extract(extract, carrier, GETTER)) | ||
.getSpanContext()) | ||
.startSpan(); | ||
assertThat(span.getParentSpanContext()) | ||
.isEqualTo( | ||
SpanContext.createFromRemoteParent( | ||
"00000003240000000000000000000003", | ||
"1600000000000003", | ||
TraceFlags.getSampled(), | ||
TraceState.getDefault())); | ||
|
||
assertThat(span.toSpanData().getLinks()) | ||
.isEqualTo( | ||
Collections.singletonList( | ||
LinkData.create( | ||
SpanContext.createFromRemoteParent( | ||
"00000001240000000000000000000001", | ||
"1600000000000001", | ||
TraceFlags.getSampled(), | ||
TraceState.getDefault())))); | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.