Skip to content

FastAPI Instrumentation

Snippets related to FastAPI python package

Configuring environment

Development Environment

You can run otel-tui as OpenTelemetry Collector, which acts as a terminal OpenTelemetry viewer

docker run -p 4317:4317 -p 4318:4318 --rm -it --name otel-tui ymtdzzz/otel-tui:latest

Install packages

Create the virtual environment and install the dependencies:

uv venv && source .venv/bin/activate

requirements.txt
opentelemetry-distro
opentelemetry-exporter-otlp
fastapi
uvicorn

Quick install with uv

uv pip install -r https://emdneto.github.io/opentelemetry-by-example/python/fastapi/requirements.txt
opentelemetry-bootstrap -a requirements | uv pip install -r -

Zero-code Instrumentation

snippet_uvicorn_auto.py
1
2
3
4
5
6
7
8
from fastapi import FastAPI

app = FastAPI()


@app.get("/foobar")
async def root():
    return {"message": "Hello World"}

Run this snippet

opentelemetry-instrument --service_name=fastapi-uvicorn-zerocode uvicorn snippet_uvicorn_auto:app --port 3000 --workers 1

Programmatic Auto-Instrumentation

Using Instrumentors

snippet_uvicorn_manual.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import fastapi
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
    OTLPSpanExporter,
)

# Creates a resource and adds it to the tracer provider
resource = Resource.create({"service.name": "fastapi-uvicorn-manual"})
provider = TracerProvider(resource=resource)
trace.set_tracer_provider(provider)

# Adds span processor with the OTLP exporter to the tracer provider
provider.add_span_processor(
    BatchSpanProcessor(
        OTLPSpanExporter(endpoint="http://localhost:4317", insecure=True)
    )
)

app = fastapi.FastAPI()


@app.get("/foobar")
async def foobar():
    return {"message": "hello world"}


FastAPIInstrumentor.instrument_app(app)

Run this snippet

uvicorn snippet_uvicorn_manual:app --port 3000 --workers 1

Using Auto-Instrumentation Initialize

snippet_uvicorn_programmatic.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from opentelemetry.instrumentation import auto_instrumentation

auto_instrumentation.initialize()  # Need to initialize first before importing FastAPI

import uvicorn

import requests
from fastapi import FastAPI
from opentelemetry.trace import get_tracer

tracer = get_tracer(__name__)

app = FastAPI()


@app.get("/foobar")
async def root():
    with tracer.start_as_current_span("foobar_span") as span:
        span.set_attribute("endpoint", "/foobar")
        span.add_event("Handling request to /foobar")
        # Simulate some processing
        requests.get("http://httpbin.org/get?hello=world")
    return {"message": "Hello World"}


uvicorn.run(app, host="0.0.0.0", port=3000)

Run this Python snippet

uv run https://emdneto.github.io/opentelemetry-by-example/python/fastapi/snippet_uvicorn_programmatic.py

Using Stable HTTP Semantic Conventions

By default opentelemetry-instrumentation-fastapi emit telemetry using an old Semantic Convention version. If you want to use new attributes defined in the OpenTelemetry HTTP Stable Semantic Convetions, you should opt-in as following:

snippet_uvicorn_auto.py
1
2
3
4
5
6
7
8
from fastapi import FastAPI

app = FastAPI()


@app.get("/foobar")
async def root():
    return {"message": "Hello World"}

Run this snippet

OTEL_SEMCONV_STABILITY_OPT_IN="http" opentelemetry-instrument --service_name fastapi-semconv uvicorn snippet_uvicorn_auto:app --port 3000 --workers 1