Skip to content

Python

The opentelemetry-instrument launcher starts the OpenTelemetry SDK and patches Django, Flask, FastAPI, requests, httpx, SQLAlchemy, psycopg, Redis, Celery and other libraries before your code runs.

Use this page when you run a Python service and want it in FuseScope.

Signal Status What is sent
Traces Stable Spans for requests, HTTP calls, database and messaging.
Metrics Stable HTTP server/client metrics, plus what your code records.
Logs Development Records from the standard logging module, with trace IDs.
  • Python 3.10 or later.
  • A virtual environment with your app’s dependencies installed.
  • A FuseScope collector you can reach on port 4317 or 4318. See OTLP.
  1. Install the distro and the OTLP exporter, then let opentelemetry-bootstrap add the instrumentation packages for the libraries it finds installed:

    Terminal window
    pip install opentelemetry-distro opentelemetry-exporter-otlp
    opentelemetry-bootstrap -a install

    Run opentelemetry-bootstrap with no arguments to only list what it would install, for example to add them to requirements.txt.

  2. Configure the SDK:

    Terminal window
    export OTEL_SERVICE_NAME=checkout
    export OTEL_RESOURCE_ATTRIBUTES=deployment.environment.name=production,service.version=1.4.2
    export OTEL_EXPORTER_OTLP_ENDPOINT=http://fusescope-collector.example.com:4318
    export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
    export OTEL_TRACES_EXPORTER=otlp
    export OTEL_METRICS_EXPORTER=otlp
    export OTEL_LOGS_EXPORTER=otlp
  3. Prefix your usual command with opentelemetry-instrument:

    Terminal window
    opentelemetry-instrument python app.py

To skip some libraries, set OTEL_PYTHON_DISABLED_INSTRUMENTATIONS, for example redis,kafka. Every setting is in the configuration reference.

Server Command
Django dev server opentelemetry-instrument python manage.py runserver --noreload
Flask dev server Run with use_reloader=False; the reloader breaks instrumentation.
Uvicorn, one process opentelemetry-instrument uvicorn main:app
Gunicorn + Uvicorn workers opentelemetry-instrument gunicorn --workers 4 --worker-class uvicorn.workers.UvicornWorker main:app
Gunicorn, sync workers Initialise in post_fork, below.

Pre-fork servers start the SDK in the parent and then fork, which can break metric export in the workers. With Gunicorn, initialise OpenTelemetry in each worker instead, and start Gunicorn without opentelemetry-instrument:

gunicorn.conf.py
from opentelemetry.instrumentation.auto_instrumentation import initialize
def post_fork(server, worker):
initialize()
Terminal window
gunicorn --config gunicorn.conf.py --workers 4 app:app

Each worker gets its own service.instance.id, so don’t set one in OTEL_RESOURCE_ATTRIBUTES. Details are in troubleshooting.

Terminal window
OTEL_EXPORTER_OTLP_ENDPOINT=http://fusescope-collector.example.com:4317
OTEL_EXPORTER_OTLP_PROTOCOL=grpc

The http:// scheme tells the gRPC exporter to use plain text.

Run opentelemetry-bootstrap after your dependencies are installed, so it sees them:

Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt \
opentelemetry-distro opentelemetry-exporter-otlp \
&& opentelemetry-bootstrap -a install
COPY . .
CMD ["opentelemetry-instrument", "gunicorn", "--workers", "4", "--worker-class", "uvicorn.workers.UvicornWorker", "--bind", "0.0.0.0:8000", "main:app"]
Terminal window
docker run \
-e OTEL_SERVICE_NAME=checkout \
-e OTEL_RESOURCE_ATTRIBUTES=deployment.environment.name=production \
-e OTEL_EXPORTER_OTLP_ENDPOINT=http://fusescope-collector.example.com:4318 \
-e OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf \
-e OTEL_LOGS_EXPORTER=otlp \
checkout:latest

Set the variables in the pod spec. The downward API adds the pod name and namespace:

deployment.yaml
env:
- name: OTEL_SERVICE_NAME
value: checkout
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: http://fusescope-otel-collector.fusescope.svc.cluster.local:4318
- name: OTEL_EXPORTER_OTLP_PROTOCOL
value: http/protobuf
- name: OTEL_LOGS_EXPORTER
value: otlp
- name: K8S_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: K8S_NAMESPACE_NAME
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: OTEL_RESOURCE_ATTRIBUTES
value: deployment.environment.name=production,k8s.pod.name=$(K8S_POD_NAME),k8s.namespace.name=$(K8S_NAMESPACE_NAME)

$(VAR) only expands variables defined earlier in the list, so keep OTEL_RESOURCE_ATTRIBUTES last.

Records from the standard logging module (and libraries built on it, such as structlog with the stdlib backend) are exported over OTLP with the trace and span IDs of the active span.

Setting Effect
OTEL_LOGS_EXPORTER=otlp Export logs over OTLP.
OTEL_PYTHON_LOG_AUTO_INSTRUMENTATION Attach the OpenTelemetry handler to logging. On by default since OpenTelemetry Python 1.40.0; set false to turn off.
OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true Same, for releases before 1.40.0, where it is off by default.
OTEL_PYTHON_LOG_CORRELATION=true Also add otelTraceID and otelSpanID to each record, for your own log format.

The handler comes from opentelemetry-instrumentation-logging. If logs don’t arrive, check that it is installed (pip show opentelemetry-instrumentation-logging). If a collector already reads your stdout, set OTEL_LOGS_EXPORTER=none so each line is stored once.

  1. Send a few requests to the service.
  2. Open Services. The service appears under its OTEL_SERVICE_NAME within a minute.
  3. Open the Traces explorer and filter by service.name.

Nothing arrives? Add console to an exporter (OTEL_TRACES_EXPORTER=otlp,console) to see spans in the terminal, and check the endpoint and protocol match.