Tracing (OpenTelemetry)¶
Instrument every request¶
# Middleware/__init__.py
from endocore.middleware import tracing_middleware
middlewares = [tracing_middleware()]
This opens one span per request (named "GET /v1/user/42"), tagged with
http.method, http.target, http.status_code. A raised HTTPError sets
http.status_code from its status but only marks the span as an error
for 5xx — a NotFound isn't a server failure and shouldn't render as one in
your trace backend. Any other unhandled exception is recorded on the span
and marks it as an error, matching what logging_middleware already logs.
With no exporter configured, spans are still created — useful on its own for the trace-id/log correlation below — but never shipped anywhere.
Ship spans somewhere¶
# extensions.py
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from endocore.extensions.tracing import TracingExtension
extensions = [TracingExtension(exporter=OTLPSpanExporter(), service_name="my-app")]
EndoCore doesn't depend on any specific exporter package — install whichever
one your collector needs (opentelemetry-exporter-otlp, a console exporter
for local debugging, etc.) and pass the built instance in. TracingExtension
sets it as the global TracerProvider and flushes pending spans on shutdown.
Correlating traces with logs¶
Every request already gets an X-Request-ID (see Logging).
tracing_middleware additionally stashes the span's trace id on
request.scope["trace_id"], so a handler (or another middleware) can log
both side by side: