Advanced Tracing (OTEL) Examples
This documentation provides advanced use cases and examples, including manual context propagation, custom decorators, custom sampling/filtering, and more. These scenarios address real-world needs such as asynchronous execution, multi-service flows and specialized exporters or decorators for observability platforms like Arize.1. Manual Context Propagation
Context Propagation in OpenTelemetry ensures that the current tracing context (i.e., the currently active span and its metadata) is available whenever you switch threads, tasks, or processes. This is particularly relevant if your code spans across asynchronous tasks or crosses microservice boundaries. In typical usage, OTEL instrumentation libraries handle context propagation automatically. However, there are cases where you need to do it manually, especially in asynchronous workflows or custom instrumentation.Propagation with Async Functions
When dealing with Pythonasync/await code, you can manually pass context if an automated instrumentation doesn’t handle it, or if you have custom logic. The steps are:
- Extract the current context (e.g., from an incoming HTTP request).
- Create a new span as a child of that context.
- Pass or embed the context into the async function so it can be reused.
Propagation Across Different Microservices
When making HTTP or gRPC calls to another microservice, we typically propagate the current tracing context through HTTP headers. If you’re using the built-in instrumentation (likeopentelemetry-instrumentation-requests or opentelemetry-instrumentation-httpx), it’s handled automatically. For a custom approach, you do the following:
- Inject the current span context into HTTP headers before sending the request.
- On the receiving microservice, extract the context from the incoming headers.
Propagation with Concurrent Threads
When you submit tasks to aThreadPoolExecutor or any other concurrency mechanism, each task runs in a separate thread. If you rely on a tracer’s current context (which stores the active span or baggage), it won’t automatically follow your tasks to those worker threads. By manually capturing the context in the main thread and then attaching it in each worker thread, you preserve the association between the tasks and the original trace context.
Example
Below is a detailed, annotated example to show how you can:- Capture the current context before submitting tasks to the executor.
- Attach that context within each worker thread (using
attach). - Run your task logic (e.g., processing questions).
- Detach the context when the task is complete (using
detach).
2. Creating Custom Decorators
Decorators are a convenient way to instrument functions and methods across your codebase without having to insert tracing calls repeatedly. A custom decorator can:- Start a new span before the function call.
- Add attributes/events with function arguments (inputs).
- Return the function’s result (outputs) and annotate or log it in the span.
- End the span.
3. Filtering Spans Based on Specific Attributes
In large-scale applications, you may not need to record every single span. Instead, you might want to selectively sample:- Spans of a particular service or component.
- Spans that meet certain business criteria (e.g.,
user.idin a specific subset). - Only error or slow spans.
Custom Sampling Basics
Sampler Interface
In OTEL Python, you create a custom sampler by subclassing theSampler interface from opentelemetry.sdk.trace.sampling. You then implement:
should_sample(...)- Decides whether the span is recorded (Sampled) or dropped (NotSampled).
- You can look at the attributes, span name, span kind, parent context, etc.
Sampling Result
When implementingshould_sample, you must return a SamplingResult, which indicates:
- Sampling Decision:
Decision.RECORD_AND_SAMPLE,Decision.RECORD_ONLY, orDecision.DROP. - Attributes: You can optionally modify or add attributes in the returned
SamplingResult(e.g., a reason for sampling).
4. Inheriting Context Attributes in Manual Spans
When using OpenTelemetry, context attributes are not automatically attached to spans created manually usingstart_as_current_span. This differs from auto-instrumentation (e.g., _llm_context), where attributes from the context are explicitly merged into the span during its creation.
To ensure context attributes are available in manual spans, you must explicitly retrieve and set them:
example usage
5. Health Check Pattern for Tracer Initialization
Validates endpoint connectivity before initializing the tracer provider, providing graceful fallback when services are unavailable. This pattern prevents retry loops and ensures your application continues to function even when tracing services are down.Pattern Implementation
Usage Example
Benefits
- Fail Fast: Quick 3-second validation prevents long waits
- Graceful Degradation: Application continues when tracing is down
- Resource Efficient: Prevents retry loops and reduces memory usage
- Operational Safety: No impact on application performance during outages