Python
This guide covers adding OpenTelemetry instrumentation to Python applications. Python has mature OTel support with auto-instrumentation for popular frameworks and libraries.
Prerequisites
Section titled “Prerequisites”- Python 3.8+
- OTel Collector running at
localhost:4317(gRPC) orlocalhost:4318(HTTP) - pip or poetry for package management
Install dependencies
Section titled “Install dependencies”Install the core SDK and OTLP gRPC exporter:
pip install opentelemetry-sdk \ opentelemetry-exporter-otlp-proto-grpc \ opentelemetry-apiFor auto-instrumentation, also install:
pip install opentelemetry-distro opentelemetry-instrumentationopentelemetry-bootstrap -a installThe opentelemetry-bootstrap command detects installed libraries and installs matching instrumentation packages automatically.
SDK setup
Section titled “SDK setup”Configure traces, metrics, and logs in a setup_telemetry function called at application startup:
from opentelemetry import trace, metricsfrom opentelemetry.sdk.trace import TracerProviderfrom opentelemetry.sdk.trace.export import BatchSpanProcessorfrom opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporterfrom opentelemetry.sdk.metrics import MeterProviderfrom opentelemetry.sdk.metrics.export import PeriodicExportingMetricReaderfrom opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporterfrom opentelemetry.sdk.resources import Resourcefrom opentelemetry.sdk._logs import LoggerProviderfrom opentelemetry.sdk._logs.export import BatchLogRecordProcessorfrom opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter
def setup_telemetry(service_name: str = "my-service"): resource = Resource.create({ "service.name": service_name, "service.version": "1.0.0", "deployment.environment": "development", })
# Traces tracer_provider = TracerProvider(resource=resource) tracer_provider.add_span_processor( BatchSpanProcessor( OTLPSpanExporter(endpoint="localhost:4317", insecure=True) ) ) trace.set_tracer_provider(tracer_provider)
# Metrics metric_reader = PeriodicExportingMetricReader( OTLPMetricExporter(endpoint="localhost:4317", insecure=True), export_interval_millis=2000, ) meter_provider = MeterProvider( resource=resource, metric_readers=[metric_reader] ) metrics.set_meter_provider(meter_provider)
# Logs logger_provider = LoggerProvider(resource=resource) logger_provider.add_log_record_processor( BatchLogRecordProcessor( OTLPLogExporter(endpoint="localhost:4317", insecure=True) ) )
return tracer_provider, meter_provider, logger_providerCall setup_telemetry() before any other application code runs.
Auto-instrumentation
Section titled “Auto-instrumentation”The fastest way to instrument a Python application is with the opentelemetry-instrument CLI wrapper:
opentelemetry-instrument \ --service_name my-service \ --exporter_otlp_endpoint localhost:4317 \ --exporter_otlp_insecure true \ python app.pyThis automatically patches supported libraries (requests, urllib3, Flask, Django, FastAPI, SQLAlchemy, psycopg2, redis, and more) without any code changes.
To see which instrumentations are available for your installed packages:
opentelemetry-bootstrap -a requirementsManual instrumentation
Section titled “Manual instrumentation”Creating spans
Section titled “Creating spans”from opentelemetry import trace
tracer = trace.get_tracer(__name__)
def process_order(order_id: str): with tracer.start_as_current_span("process_order") as span: span.set_attribute("order.id", order_id) span.set_attribute("order.type", "standard")
# Child span with tracer.start_as_current_span("validate_payment"): validate_payment(order_id)
span.add_event("order_processed", {"order.id": order_id})Recording metrics
Section titled “Recording metrics”from opentelemetry import metrics
meter = metrics.get_meter(__name__)
request_counter = meter.create_counter( name="http.server.request_count", description="Number of HTTP requests", unit="1",)
request_duration = meter.create_histogram( name="http.server.duration", description="HTTP request duration", unit="ms",)
def handle_request(): request_counter.add(1, {"http.method": "GET", "http.route": "/api/orders"}) # ... handle request, then record duration request_duration.record(45.2, {"http.method": "GET", "http.route": "/api/orders"})Emitting logs
Section titled “Emitting logs”import loggingfrom opentelemetry.sdk._logs import LoggerProviderfrom opentelemetry._logs import set_logger_providerfrom opentelemetry.instrumentation.logging import LoggingInstrumentor
# Bridge Python logging to OTelLoggingInstrumentor().instrument(set_logging_format=True)
logger = logging.getLogger(__name__)logger.info("Order processed", extra={"order.id": "12345"})Framework integration
Section titled “Framework integration”FastAPI
Section titled “FastAPI”from fastapi import FastAPIfrom opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
app = FastAPI()
# Auto-instrument FastAPIFastAPIInstrumentor.instrument_app(app)
@app.get("/orders/{order_id}")async def get_order(order_id: str): return {"order_id": order_id}Alternatively, use the ASGI middleware directly:
from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware
app = FastAPI()app = OpenTelemetryMiddleware(app)from flask import Flaskfrom opentelemetry.instrumentation.flask import FlaskInstrumentor
app = Flask(__name__)FlaskInstrumentor().instrument_app(app)
@app.route("/orders/<order_id>")def get_order(order_id): return {"order_id": order_id}Django
Section titled “Django”INSTALLED_APPS = [ "opentelemetry.instrumentation.django", # ... other apps]Or instrument programmatically:
from opentelemetry.instrumentation.django import DjangoInstrumentorDjangoInstrumentor().instrument()Gen-AI semantic conventions
Section titled “Gen-AI semantic conventions”For AI/ML applications, use the OpenTelemetry semantic conventions for generative AI to capture LLM interactions:
with tracer.start_as_current_span("llm.chat") as span: span.set_attribute("gen_ai.system", "openai") span.set_attribute("gen_ai.request.model", "gpt-4") span.set_attribute("gen_ai.request.temperature", 0.7) span.set_attribute("gen_ai.request.max_tokens", 1000)
response = client.chat.completions.create(...)
span.set_attribute("gen_ai.response.model", response.model) span.set_attribute("gen_ai.usage.input_tokens", response.usage.prompt_tokens) span.set_attribute("gen_ai.usage.output_tokens", response.usage.completion_tokens)Environment variables
Section titled “Environment variables”| Variable | Description | Example |
|---|---|---|
OTEL_SERVICE_NAME | Service name | my-service |
OTEL_EXPORTER_OTLP_ENDPOINT | Collector endpoint | http://localhost:4317 |
OTEL_EXPORTER_OTLP_INSECURE | Disable TLS for gRPC | true |
OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED | Bridge Python logs to OTel | true |
OTEL_TRACES_SAMPLER | Sampler type | parentbased_traceidratio |
OTEL_TRACES_SAMPLER_ARG | Sampler argument | 0.1 |
OTEL_PYTHON_DISABLED_INSTRUMENTATIONS | Comma-separated list of instrumentations to skip | flask,django |
Related links
Section titled “Related links”- Applications overview
- Auto-instrumentation
- Manual instrumentation
- Agent traces
- OpenTelemetry Python documentation — Official OTel Python SDK reference
- Python instrumentation libraries — Available auto-instrumentation packages