How to Create a Mock OpenTelemetry Trace in Python
Create a mock OpenTelemetry trace in memory to test span creation, attributes, and parent-child relationships without exporting to a backend.
pip install opentelemetry-api opentelemetry-sdk
Python code
30 linesfrom opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
def create_mock_trace():
tracer_provider = TracerProvider()
span_exporter = InMemorySpanExporter()
tracer_provider.add_span_processor(SimpleSpanProcessor(span_exporter))
trace.set_tracer_provider(tracer_provider)
tracer = trace.get_tracer("mock-tracer")
with tracer.start_as_current_span("parent-span") as parent_span:
parent_span.set_attribute("operation", "demo")
with tracer.start_as_current_span("child-span") as child_span:
child_span.set_attribute("data", "sample")
spans = span_exporter.get_finished_spans()
for span in spans:
print(f"Span: {span.name}")
print(f" Trace ID: {span.context.trace_id}")
print(f" Span ID: {span.context.span_id}")
print(f" Parent ID: {span.parent.span_id if span.parent else 'None'}")
print(f" Attributes: {dict(span.attributes)}")
print()
if __name__ == "__main__":
create_mock_trace()
Output
Span: parent-span
Trace ID: 12345678901234567890123456789012
Span ID: 1234567890123456
Parent ID: None
Attributes: {'operation': 'demo'}
Span: child-span
Trace ID: 12345678901234567890123456789012
Span ID: 1234567890123456
Parent ID: 1234567890123456
Attributes: {'data': 'sample'}
How it works
This setup uses InMemorySpanExporter to capture spans in memory without sending them anywhere — perfect for tests and local debugging. The TracerProvider configures the SDK and the SimpleSpanProcessor exports spans synchronously as soon as they finish. Calling trace.set_tracer_provider makes this provider global so trace.get_tracer returns the expected tracer. The context manager start_as_current_span automatically creates parent-child relationships, and calling get_finished_spans returns the spans in completion order. This pattern is identical to production tracing code, so the output shows real span IDs and attributes.
Common mistakes
- Forgetting to set the tracer provider with `trace.set_tracer_provider` before creating spans
- Using `start_span` without entering the context manager, so spans never finish or get exported
- Expecting spans to appear in the exporter before the `with` block exits
- Not calling `get_finished_spans` after the context, so the in-memory list stays empty
Variations
- Replace `SimpleSpanProcessor` with a `BatchSpanProcessor` and add a `flush` call to mimic production export timing
- Use `trace.get_current_span()` inside the context to fetch the active span and attach attributes to it
Real-world use cases
- Unit testing a custom instrumentation library without needing a running Jaeger, Zipkin, or OTLP collector.
- Verifying that parent-child span relationships and attributes are correct before deploying changes to production.
- Simulating distributed trace behavior in local development or CI pipelines where network export is unavailable.
Sponsored
More from Observability & SRE
- Adding a Correlation ID to Log Context in Python medium
- Calculate Error Rate from Log Stream in Python easy
- Check if a Timestamp Falls in a Daily Maintenance Window in Python easy
- Export Metrics with OTLP Mock in Python medium
- Generate Mock CPU and Memory Metrics in Python easy
- Generate Prometheus Text Exposition Format in Python easy
Keep learning
Related tutorials and quizzes for this topic.