AWS X-Ray Propagator for OpenTelemetry
The `opentelemetry-propagator-aws-xray` library provides an OpenTelemetry `TextMapPropagator` that enables propagation of trace context in AWS X-Ray compatible headers. It allows OpenTelemetry Python applications to integrate with existing AWS X-Ray traces by injecting and extracting the `X-Amzn-Trace-Id` header. The current version is 1.0.2, and it follows the release cadence of the broader `opentelemetry-python-contrib` project, which typically sees updates aligned with core OpenTelemetry Python releases.
Warnings
- gotcha The `AWSXRayPropagator` primarily handles the `X-Amzn-Trace-Id` header format. For OpenTelemetry to generate trace IDs that are natively compatible with AWS X-Ray (i.e., epoch time + 24-byte unique identifier), you *must* configure your `TracerProvider` with `AwsXRayIdGenerator`.
- gotcha If you are relying on automatic instrumentation or environment variables for configuration, ensure the `OTEL_PROPAGATORS` environment variable is set correctly. The value `awsxray` needs to be included.
- gotcha AWS X-Ray trace IDs have a different format (version, timestamp, random ID) compared to W3C Trace Context trace IDs (16-byte random ID). While `AWSXRayPropagator` translates between these formats when headers are exchanged, ensure your entire tracing setup (ID generation, sampling, exporter) is compatible with X-Ray if deep integration is required.
Install
-
pip install opentelemetry-propagator-aws-xray
Imports
- AWSXRayPropagator
from opentelemetry.propagators.aws.xray import AWSXRayPropagator
- AwsXRayIdGenerator
from opentelemetry.sdk.trace.id_generator import AwsXRayIdGenerator
Quickstart
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor
from opentelemetry.sdk.trace.id_generator import AwsXRayIdGenerator
from opentelemetry.propagators.aws.xray import AWSXRayPropagator
from opentelemetry.propagate import set_global_textmap_propagator
from opentelemetry.trace import get_tracer_provider, set_tracer_provider, get_tracer, get_current_span
# 1. Configure TracerProvider with AwsXRayIdGenerator
# This ensures new traces generated by this application use AWS X-Ray compatible IDs.
provider = TracerProvider(id_generator=AwsXRayIdGenerator())
# For demonstration, we'll use a ConsoleSpanExporter.
# In a real application, you'd use an OTLPSpanExporter or similar.
processor = SimpleSpanProcessor(ConsoleSpanExporter())
provider.add_span_processor(processor)
set_tracer_provider(provider)
# 2. Set the global propagator to AWSXRayPropagator
# This enables automatic injection/extraction of X-Ray headers.
set_global_textmap_propagator(AWSXRayPropagator())
# 3. Obtain a tracer and create spans
tracer = get_tracer(__name__)
print("\n--- Creating an initial span and injecting context ---")
with tracer.start_as_current_span("my-xray-root-span") as root_span:
print(f"Root Span ID: {root_span.context.span_id:x}")
print(f"Root Trace ID: {root_span.context.trace_id:x}")
# Simulate injecting trace context into outgoing headers
carrier = {}
AWSXRayPropagator().inject(carrier)
print("Injected headers (simulating HTTP request outgoing):", carrier)
print("\n--- Simulating a new request receiving injected headers ---")
# Simulate receiving injected headers in a new context
# We start a new span, but its context will be derived from the extracted headers.
# Let's assume 'carrier' contains the headers from the previous step
received_carrier = carrier.copy()
# When a new span is started, OpenTelemetry automatically extracts context
# from the global propagator if it's available in the current context (e.g., from a request).
# For this example, we manually extract and then demonstrate a child span.
# Manually extract context for clarity, though it's often done automatically by instrumentations.
extracted_context = AWSXRayPropagator().extract(received_carrier)
with tracer.start_as_current_span("my-xray-child-span", context=extracted_context) as child_span:
print(f"Child Span ID: {child_span.context.span_id:x}")
print(f"Child Trace ID: {child_span.context.trace_id:x}")
print(f"Is child span linked to root? {root_span.context.trace_id == child_span.context.trace_id}")
# Further nested span
with tracer.start_as_current_span("nested-operation"):
print(" Inside nested operation.")
print("\n--- End of quickstart ---")