OpenTelemetry Python API
OpenTelemetry Python API package. Current version: 1.40.0 (Mar 2026). TWO separate packages: 'opentelemetry-api' (interfaces + no-op implementations) and 'opentelemetry-sdk' (actual implementation). Installing only opentelemetry-api produces silent no-ops — all tracing calls succeed but nothing is recorded or exported. Must also install opentelemetry-sdk AND configure a TracerProvider AND call trace.set_tracer_provider(). Exporters (OTLP, Jaeger, Zipkin) are separate packages. All packages must be the same version.
Warnings
- breaking Installing only 'opentelemetry-api' gives silent no-ops. All tracing calls succeed without errors but nothing is recorded. Must also install 'opentelemetry-sdk'.
- breaking trace.get_tracer() returns NoopTracer until trace.set_tracer_provider() is called. Forgetting this line means all spans are silently discarded. Warning: 'No TracerProvider configured, using NoopTracerProvider' only shows with debug logging enabled.
- breaking Exporters are separate packages not included in opentelemetry-sdk. OTLP requires 'opentelemetry-exporter-otlp'. Jaeger requires 'opentelemetry-exporter-jaeger'. ConsoleSpanExporter is included in SDK.
- breaking All opentelemetry-* packages must be the same version. Mixing versions (e.g. api=1.20, sdk=1.25) causes ImportError or unexpected behavior.
- gotcha Auto-instrumentation packages (opentelemetry-instrumentation-fastapi etc.) are in the separate 'opentelemetry-python-contrib' repo — not in the main SDK. Must be installed separately.
- gotcha provider.shutdown() must be called on app exit to flush buffered spans. Without it, spans in the BatchSpanProcessor queue are lost on process exit.
- gotcha service.name resource attribute is not set by default. Without it, traces appear as 'unknown_service' in your backend.
Install
-
pip install opentelemetry-api opentelemetry-sdk -
pip install opentelemetry-exporter-otlp -
pip install opentelemetry-instrumentation-fastapi opentelemetry-instrumentation-sqlalchemy
Imports
- TracerProvider setup
from opentelemetry import trace 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.sdk.resources import Resource # Configure provider with resource and exporter resource = Resource.create({'service.name': 'my-service'}) provider = TracerProvider(resource=resource) exporter = OTLPSpanExporter(endpoint='http://localhost:4317') processor = BatchSpanProcessor(exporter) provider.add_span_processor(processor) # MUST register globally before any tracing trace.set_tracer_provider(provider) # Get tracer tracer = trace.get_tracer(__name__) # Use tracer with tracer.start_as_current_span('my-operation') as span: span.set_attribute('user.id', '42') # do work
Quickstart
# pip install opentelemetry-api opentelemetry-sdk
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter
from opentelemetry.sdk.resources import Resource
# Setup — do this once at app startup
resource = Resource.create({'service.name': 'my-service', 'service.version': '1.0.0'})
provider = TracerProvider(resource=resource)
# ConsoleSpanExporter for local dev — replace with OTLPSpanExporter in production
provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))
# Register globally — MUST be before any trace.get_tracer() calls
trace.set_tracer_provider(provider)
# Get tracer
tracer = trace.get_tracer(__name__)
# Create spans
with tracer.start_as_current_span('main-operation') as span:
span.set_attribute('env', 'production')
with tracer.start_as_current_span('db-query') as child:
child.set_attribute('db.system', 'postgresql')
child.set_attribute('db.statement', 'SELECT * FROM users')
# do DB work
# Flush remaining spans on shutdown
provider.shutdown()