Skip to content

Python OpenTelemetry instrumentation

Osuite ingests OpenTelemetry data over OTLP. For Python, the OpenTelemetry distro (opentelemetry-distro) auto-instruments common frameworks, database drivers, and HTTP clients with no code changes, and the opentelemetry-instrument wrapper starts your process with that instrumentation attached. This page covers the shared setup; the per-framework guides below add the run command and gotchas specific to each stack.

There are two paths, and every Python guide in this section uses one or both of them.

  • Zero-code, with opentelemetry-instrument — install the distro, run opentelemetry-bootstrap to add the instrumentors for the libraries you already use, then launch your app through the opentelemetry-instrument wrapper. No import or SDK code in your application. This is the default for Django, FastAPI, and Flask.
  • Programmatic SDK setup — configure the providers and exporters yourself in a small module and register the instrumentors from code. Use this when you cannot change the launch command, or when the process forks after startup (Gunicorn, uWSGI, Celery), where the exporter’s background thread must be created in each worker rather than inherited across the fork.
  • Python 3.8 or newer.
  • A running Python service you can restart.
  • Your Osuite ingest token and region, taken from your Osuite account.
  1. Install the distro and the OTLP exporter.

    Terminal window
    pip install opentelemetry-distro opentelemetry-exporter-otlp
  2. Let bootstrap detect your installed libraries and add the matching instrumentors.

    Terminal window
    opentelemetry-bootstrap -a install

opentelemetry-bootstrap -a install reads your environment, finds supported libraries (Django, FastAPI, Flask, Celery, SQLAlchemy, psycopg, redis, requests, and more), and installs the corresponding opentelemetry-instrumentation-* packages. Rerun it whenever you add a dependency you want traced.

Set these environment variables wherever the service runs. They are read by both the opentelemetry-instrument wrapper and the programmatic SDK.

Terminal window
export OTEL_SERVICE_NAME="<service-name>"
export OTEL_RESOURCE_ATTRIBUTES="service.environment=production"
export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.osuite.io:443"
export OTEL_EXPORTER_OTLP_HEADERS="x-osuite-ingest-token=<your-ingest-token>"
export OTEL_LOGS_EXPORTER="otlp"
export OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED="true"
  • OTEL_SERVICE_NAME is how the service appears in Osuite. service.environment in OTEL_RESOURCE_ATTRIBUTES separates production, staging, and development.
  • OTEL_LOGS_EXPORTER=otlp and OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true route records from the standard logging module to Osuite with the active trace ID attached, so logs correlate with traces. Traces and metrics export over OTLP by default.

Prefix your normal start command with opentelemetry-instrument:

Terminal window
opentelemetry-instrument python app.py

The wrapper configures the SDK from the environment variables above and activates every instrumentor that bootstrap installed. Your framework, outbound HTTP calls, and database queries are traced without touching application code. See each framework guide for the exact command and reload/fork caveats.

When the wrapper does not fit — a forking server, or an entry point you cannot prefix — configure the SDK in a module imported before your application code. The exporters read OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_HEADERS from the environment, and the resource is populated from OTEL_SERVICE_NAME and OTEL_RESOURCE_ATTRIBUTES.

import logging
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._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter
def configure_telemetry():
tracer_provider = TracerProvider()
tracer_provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
trace.set_tracer_provider(tracer_provider)
logger_provider = LoggerProvider()
logger_provider.add_log_record_processor(BatchLogRecordProcessor(OTLPLogExporter()))
logging.getLogger().addHandler(LoggingHandler(logger_provider=logger_provider))

In programmatic mode the instrumentors are not activated for you — call each one after configure_telemetry(), for example DjangoInstrumentor().instrument() or FastAPIInstrumentor.instrument_app(app). The framework guides show the exact call.

What you should see

Start the service under opentelemetry-instrument, send a request, and the service name appears in APM within a minute, emitting request-rate and latency data. Logs written through the standard logging module appear in the Logs explorer with the trace ID attached.