Producing quality traces requires correct instrumentation. This episode covers auto-instrumentation for various languages, creating manual spans, W3C context propagation, and best practices for span naming and attributes so traces are easy to read and query.

Tempo provides the storage, but traces don't appear without instrumentation. In this episode you'll learn to produce quality traces with OpenTelemetry: from practical auto-instrumentation, creating manual spans for special logic, to context propagation that unifies traces across services.
By the end of the episode you'll know how to build applications that produce complete traces with consistent names and attributes — the raw material for TraceQL queries in episode 15.
Auto-instrumentation automatically handles popular frameworks without changing business code:
opentelemetry-javaagent.jar run with the -javaagent flag.opentelemetry-instrumentation-* packages and the opentelemetry-instrument command.@opentelemetry/instrumentation with an auto-loader.OpenTelemetry.AutoInstrumentation package.pip install opentelemetry-distro opentelemetry-instrumentation-flask
opentelemetry-instrument python3 app.pyThe opentelemetry-instrument python3 app.py command runs the application with HTTP tracing active without touching the application code.
For logic not covered by auto-instrumentation, create spans manually:
from opentelemetry import trace
tracer = trace.get_tracer("orders")
def process(order_id):
with tracer.start_as_current_span("orders.process") as span:
span.set_attribute("order.id", order_id)
result = charge(order_id)
span.set_status(trace.Status(trace.StatusCode.OK))
return resultThe construct with tracer.start_as_current_span("orders.process") ensures the span is automatically closed and becomes a child of the active span.
order.id.span.add_event("charge.attempt", {"attempt": 1})
try:
charge(order_id)
except Exception as exc:
span.record_exception(exc)
span.set_status(trace.Status(trace.StatusCode.ERROR, str(exc)))The pattern span.record_exception(exc) stores the error inside the trace, making debugging in episode 21 much faster.
For one trace to connect across services, context must be forwarded via HTTP headers:
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01The traceparent header carries the version, TraceID, SpanID, and sampling flags. Auto-instrumentation usually handles context injection and extraction automatically.
python3 -c "from opentelemetry import propagate; print(propagate.get_global_text_map_propagator())"The python3 -c ... command displays the global propagator currently active in the Python process.
verb.noun format like orders.process and checkout.pay — not arbitrary function names.http.method, http.route, db.system.Warning
Don't put PII data in span attributes. Attributes are indexed for queries and can appear on dashboards — follow the redaction practices discussed in episode 33.
In episode 14 you understood auto-instrumentation for Java, Python, Node.js, and .NET, how to create manual spans with attributes, events, exceptions, and status, W3C context propagation with propagators and baggage, and best practices for span naming and attributes.
The key takeaways:
with context so they always close.In the next episode 15 we'll discuss TraceQL — Tempo's query language for finding traces by span attributes, span relationships, comparison and logic operators, up to advanced queries and metrics from traces. The traces you produce will soon be explorable with precision.