Instrument a FastAPI service with OpenTelemetry in 15 minutes.
Wire the OTel SDK, point it at the collector, and verify your first distributed trace — copy/paste guided, end to end.
Bruno Pires·May 09, 2026·1 min read
This is a short, copy/paste walk-through. By the end you'll have a FastAPI service producing real OpenTelemetry spans, exporting them to a collector, and verifying the first trace in Datadog (or any OTLP-compatible backend).
Prereqs
- Python 3.11+
- An OTLP-capable backend reachable from your laptop (we'll use a local
otel/opentelemetry-collectorcontainer).
1. Install the SDK
pip install \
opentelemetry-api \
opentelemetry-sdk \
opentelemetry-exporter-otlp \
opentelemetry-instrumentation-fastapi
2. Bootstrap the tracer
# app/observability.py
from opentelemetry import trace
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
OTLPSpanExporter,
)
def setup_tracing(service_name: str) -> None:
resource = Resource.create({"service.name": service_name})
provider = TracerProvider(resource=resource)
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
trace.set_tracer_provider(provider)
3. Wire it into FastAPI
# app/main.py
from fastapi import FastAPI
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from app.observability import setup_tracing
setup_tracing("orders-api")
app = FastAPI()
FastAPIInstrumentor.instrument_app(app)
@app.post("/orders")
async def create_order():
return {"ok": True}
That's it. Every request to /orders now produces a span with HTTP attributes
(http.method, http.route, http.status_code) and the FastAPI route handler.
4. Run the collector locally
docker run --rm -p 4317:4317 \
-v $(pwd)/otelcol.yaml:/etc/otelcol/config.yaml \
otel/opentelemetry-collector:latest
A minimal otelcol.yaml that prints spans to stdout:
receivers:
otlp:
protocols:
grpc:
exporters:
logging:
loglevel: debug
service:
pipelines:
traces:
receivers: [otlp]
exporters: [logging]
5. Send a request, watch the span
curl -X POST http://localhost:8000/orders
Within a couple of seconds the collector should log a span named
POST /orders. You just shipped your first trace.
Next steps
- Add custom spans inside your handler with
tracer.start_as_current_span(). - Plug a real backend (Datadog, Honeycomb, Tempo) into the collector
exporterssection. - Run Horion against the service to see your traces score.