- Explicit span lifecycle with
start_span()/end() - The recommended Python context manager
start_as_current_span - Nesting spans to form parent/child relationships
- Moving span creation inside functions
- Using the context manager as a decorator for concise function-level instrumentation
configure_tracer() helper that returns a configured tracer (for example via trace.get_tracer(...) after installing a TracerProvider and a span exporter). See the OpenTelemetry Python docs for setup details: https://opentelemetry.io/docs/instrumentation/python/
1) Creating a span with start_span / span.end()
You can explicitly start and end a span. This is straightforward but becomes noisy as code grows. Example (payment.py):trace_idties all spans in a trace (for example, one user request).span_iduniquely identifies the current span.parent_idisnullfor root spans.kindfor in-process spans isSpanKind.INTERNAL. Other kinds includeCLIENTandSERVER.
2) Using the Python context manager (recommended)
Starting and ending spans manually gets error-prone. The recommended pattern in Python istracer.start_as_current_span("name") used as a context manager. It automatically sets the active span and ends it when the block exits (including on exceptions).
Basic example:
with block is part of the "Starting Payment" span. Use context managers to ensure deterministic start/stop semantics and less boilerplate.
3) Nesting spans to create parent/child relationships
Nest context-managed spans to model the call hierarchy. The outer span is the parent of the inner span and they share the sametrace_id.
Example:
- Both spans have the same
trace_id. - The
"Starting Payment"span’sparent_idis the"Payment Service"span’sspan_id. - Trace backends can visualize these parent/child relationships to show execution flow.
4) Moving span creation inside functions
Place spans inside the functions they represent so instrumentation is local to the code being traced. This ensures any call to the function automatically creates the span. Example:5) Adding spans for smaller operations
Instrument smaller operations as child spans so traces reveal finer-grained behavior:parent_id relationships, providing clear observability into the operation.
Use context managers for deterministic start/stop semantics and for clearer scope handling. They automatically end spans even if exceptions occur.
6) Using decorators to start a span for a function
You can reuse the context manager as a decorator to start a span every time a function is invoked. Important: the tracer instance used by the decorator must be available at function-definition time.If using
@tracer.start_as_current_span("...") as a decorator, initialize the tracer (for example with tracer = configure_tracer()) before defining the decorated functions. Otherwise the decorator won’t have access to the tracer and will raise an error.- The decorator starts and ends the span on each function invocation.
- Decorators are concise and readable for simple functions.
- For dynamic span names or attributes, prefer explicit context managers inside the function body.
Quick comparison
| Method | When to use | Example |
|---|---|---|
tracer.start_span() + span.end() | Simple explicit control, rarely recommended for complex code | span = tracer.start_span("name"); ...; span.end() |
with tracer.start_as_current_span("name"): | Recommended Python pattern for deterministic end and clear scope | with tracer.start_as_current_span("name"): do_work() |
@tracer.start_as_current_span("name") (decorator) | Concise instrumentation for small, self-contained functions (tracer must be initialized before decoration) | @tracer.start_as_current_span("name")\ndef f(): ... |
Summary
- Manual spans (
start_span/end) work but are verbose. - Use
tracer.start_as_current_span("name")as a context manager for safer, idiomatic Python instrumentation. - Nest context managers to create parent/child spans and show the call hierarchy.
- Move span creation into functions to ensure instrumentation is applied consistently.
- Use the context manager as a decorator for short, self-contained functions — but initialize the tracer before defining decorated functions.
- OpenTelemetry Python documentation: https://opentelemetry.io/docs/instrumentation/python/
- OpenTelemetry specification: https://opentelemetry.io/technical-specification/trace/