OpenTelemetry FastAPI Instrumentation
OpenTelemetry FastAPI Instrumentation provides automatic and manual instrumentation for FastAPI web frameworks. It allows for comprehensive application performance monitoring (APM), distributed tracing, and observability by collecting traces and metrics from HTTP requests, database queries, and external API calls with minimal code changes. The library is currently in beta (version 0.61b0) and is part of the broader `opentelemetry-python-contrib` project, which sees frequent updates across its various instrumentations.
Warnings
- gotcha The `opentelemetry-instrumentation-fastapi` library is currently in beta (indicated by the `b0` suffix in the version). This means its API and behavior might be subject to breaking changes in future releases before reaching a stable `1.0.0` version.
- breaking Support for FastAPI versions earlier than 0.92 has been dropped. Using older FastAPI versions will lead to `FastAPIInstrumentor` failing to properly instrument your application or causing unexpected errors.
- gotcha When running FastAPI with `uvicorn --reload` (development mode) or with multiple worker processes (e.g., `uvicorn --workers N` or `gunicorn` with `uvicorn.workers.UvicornWorker`), OpenTelemetry SDK initialization can cause issues due to Python's process forking model. Background threads and shared resources from the parent process are not correctly replicated in child workers.
- gotcha The order of instrumentation is critical. `FastAPIInstrumentor.instrument_app(app)` must be called after the FastAPI application instance (`app`) has been created but before the application starts processing requests. Improper ordering can lead to incomplete tracing or errors.
- gotcha OpenTelemetry semantic conventions, which define naming for attributes and spans, can evolve. For example, `aws.region` was changed to `cloud.region`. This can affect how your telemetry data is displayed in older or un-updated observability backends.
Install
-
pip install opentelemetry-instrumentation-fastapi opentelemetry-exporter-otlp
Imports
- FastAPIInstrumentor
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
Quickstart
import os
from contextlib import asynccontextmanager
from fastapi import FastAPI
from opentelemetry import trace
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
# Configure OpenTelemetry SDK
# It is recommended to initialize OpenTelemetry within FastAPI's lifespan events
# when using multi-process servers (e.g., uvicorn --workers > 1 or gunicorn).
# For simple single-process development, top-level initialization is sufficient.
def setup_tracing():
resource = Resource.create(attributes={
"service.name": os.environ.get("OTEL_SERVICE_NAME", "my-fastapi-app")
})
tracer_provider = TracerProvider(resource=resource)
# Use OTLP HTTP exporter for traces
# OTLP endpoint can be configured via environment variable OTEL_EXPORTER_OTLP_ENDPOINT
# Default is http://localhost:4318/v1/traces for HTTP
otlp_exporter = OTLPSpanExporter(
endpoint=os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4318/v1/traces")
)
span_processor = BatchSpanProcessor(otlp_exporter)
tracer_provider.add_span_processor(span_processor)
trace.set_tracer_provider(tracer_provider)
print("OpenTelemetry tracing initialized.")
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup logic: Initialize OpenTelemetry
setup_tracing()
FastAPIInstrumentor.instrument_app(app)
yield
# Shutdown logic: Flush and shutdown exporter
provider = trace.get_tracer_provider()
if hasattr(provider, 'shutdown'):
provider.shutdown()
print("OpenTelemetry tracing shutdown.")
# Initialize FastAPI app with lifespan events
app = FastAPI(lifespan=lifespan)
@app.get("/hello")
async def read_root():
return {"message": "Hello, World!"}
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id, "message": "Item fetched"}
# To run this application:
# 1. Save it as main.py
# 2. Run from your terminal:
# OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318/v1/traces" OTEL_SERVICE_NAME="my-fastapi-app" uvicorn main:app --port 8000 --reload --lifespan on
# Note: For production, omit --reload and manage workers via gunicorn post_fork hooks if using multiple workers.
# 3. Access in browser: http://localhost:8000/hello or http://localhost:8000/items/123