OpenTelemetry Starlette Instrumentation
This library provides automatic and manual instrumentation for the Starlette web framework, enabling the collection of telemetry data like traces and metrics for HTTP requests. It's an active component of the OpenTelemetry Python Contrib project, currently in a beta release phase, indicating ongoing development and potential for API evolution.
Warnings
- gotcha The library `opentelemetry-instrumentation-starlette` is currently in beta (`0.62b0`). This means the API is subject to change without adhering to strict semantic versioning, and stability guarantees are limited. Production usage should monitor release notes for breaking changes.
- breaking The OpenTelemetry SDK (including `TracerProvider` and exporters) *must* be initialized and configured before any instrumented library (like Starlette) is imported or used. Failing to do so can result in instrumentation hooks not being applied, leading to no telemetry data being collected.
- gotcha When running Starlette applications with pre-forking ASGI servers like Gunicorn (especially with multiple workers), OpenTelemetry's automatic metrics generation may be unreliable or broken. This is due to Python's forking model and how background threads (e.g., in `PeriodicExportingMetricReader`) interact with child processes.
- gotcha The order of OpenTelemetry instrumentation relative to other Starlette middleware can be crucial. If other middleware modifies the ASGI scope in a way that interferes with the instrumentation, or if instrumentation needs to capture attributes set by other middleware, placement matters.
- deprecated Older versions of `opentelemetry-instrumentation-starlette` (prior to `1.34.0/0.55b0`) had known issues with capturing custom headers correctly, particularly with Starlette versions >= 0.15.0. While fixed in newer versions, using outdated instrumentation might lead to missing header attributes.
- gotcha To prevent storing sensitive data or to reduce noise, certain URLs or HTTP headers should be excluded from tracing. If not configured, sensitive paths (e.g., health checks, client secrets) might generate unnecessary spans or expose data.
Install
-
pip install opentelemetry-instrumentation-starlette opentelemetry-sdk opentelemetry-exporter-otlp uvicorn starlette
Imports
- StarletteInstrumentor
from opentelemetry.instrumentation.starlette import StarletteInstrumentor
Quickstart
import os
from opentelemetry import trace
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.starlette import StarletteInstrumentor
from starlette.applications import Starlette
from starlette.responses import PlainTextResponse
from starlette.routing import Route
# Configure OpenTelemetry SDK
resource = Resource.create({
SERVICE_NAME: os.environ.get('OTEL_SERVICE_NAME', 'starlette-app')
})
tracer_provider = TracerProvider(resource=resource)
otlp_exporter = OTLPSpanExporter(
endpoint=os.environ.get('OTEL_EXPORTER_OTLP_ENDPOINT', 'localhost:4317'),
insecure=True # Use secure=False for production with TLS
)
span_processor = BatchSpanProcessor(otlp_exporter)
tracer_provider.add_span_processor(span_processor)
trace.set_tracer_provider(tracer_provider)
# Define Starlette application
async def homepage(request):
with trace.get_current_span() as span:
span.set_attribute("custom_attribute", "hello from homepage")
return PlainTextResponse("Hello, world!")
async def user_detail(request):
user_id = request.path_params['user_id']
return PlainTextResponse(f"User ID: {user_id}")
routes = [
Route("/", homepage),
Route("/users/{user_id:int}", user_detail),
]
app = Starlette(routes=routes)
# Instrument the Starlette application
StarletteInstrumentor().instrument_app(app)
# To run: uvicorn your_app_file_name:app --port 8000
# Make sure an OTLP collector is running at localhost:4317 (or specified endpoint)