Integrations
Monitor a Python app with OpenTelemetry
Install opentelemetry-distro and start FastAPI or Django with opentelemetry-instrument. Requests, queries, errors and logs reach Lampwick, no code changes.
On this page
OpenTelemetry's zero-code instrumentation for Python sends requests, database queries, errors and logging records from a FastAPI or Django app to Lampwick. You start the app with opentelemetry-instrument and set environment variables; the code stays as it is. The general reference is OpenTelemetry.
Tested with Python 3.9.6, opentelemetry-distro 0.62b1 (opentelemetry-sdk and opentelemetry-exporter-otlp-proto-http 1.41.1), FastAPI 0.128 on uvicorn 0.39, and Django 4.2 with SQLite, on 10 November 2026.
Requirements
- Python 3.9 or later.
- The project's ingest token, from the Connect your app card on the project's Settings page. Only team owners see it.
Let your agent do it
Paste this into your coding agent, in the app's repository:
Add OpenTelemetry to this Python app so it reports to Lampwick: follow https://lampwick.dev/docs/python.md exactly (opentelemetry-distro, opentelemetry-bootstrap, opentelemetry-instrument, the environment variables). Read the ingest token from the environment, never commit it.
Install the packages
In the app's virtualenv, after your app's own dependencies:
pip install opentelemetry-distro opentelemetry-exporter-otlp-proto-http
opentelemetry-bootstrap -a install
opentelemetry-bootstrap looks at what's installed and adds the matching instrumentations: for our FastAPI app opentelemetry-instrumentation-fastapi, -asgi, -sqlite3 and -logging, for Django opentelemetry-instrumentation-django. Run it again after you add a framework or database driver. To pin them, opentelemetry-bootstrap -a requirements prints the list for requirements.txt.
Set the environment variables
export OTEL_EXPORTER_OTLP_ENDPOINT=https://app.lampwick.dev
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_HEADERS=x-lampwick-token=YOUR_PROJECT_TOKEN
export OTEL_SERVICE_NAME=my-app
export OTEL_METRICS_EXPORTER=none
export OTEL_LOGS_EXPORTER=otlp
YOUR_PROJECT_TOKEN is the project's ingest token. OTEL_LOGS_EXPORTER=otlp is all logs need: the logging instrumentation sends every record that passes your loggers' level.
FastAPI
Start uvicorn through opentelemetry-instrument:
opentelemetry-instrument uvicorn main:app --port 8000
The app we tested:
import logging
import sqlite3
from fastapi import FastAPI
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("shop")
app = FastAPI()
db = sqlite3.connect("shop.db", check_same_thread=False)
db.execute("CREATE TABLE IF NOT EXISTS orders (id INTEGER PRIMARY KEY, total INTEGER)")
db.execute("INSERT OR IGNORE INTO orders VALUES (1, 10), (2, 20)")
db.commit()
@app.get("/orders/{order_id}")
def show_order(order_id: int):
cursor = db.cursor()
cursor.execute("SELECT id, total FROM orders WHERE id = ?", (order_id,))
row = cursor.fetchone()
logger.info("order %s looked up", order_id)
return {"id": row[0], "total": row[1]} if row else None
@app.get("/boom")
def boom():
raise RuntimeError("Checkout failed for order 42")
@app.get("/handled")
def handled():
try:
1 / 0
except ZeroDivisionError:
logger.exception("could not compute the discount")
return {"ok": True}
After opening /orders/1, /orders/2, /boom and /handled, the project shows:
- Requests:
GET /orders/{order_id}(2),GET /boom(status 500) andGET /handled. - Queries:
SELECT id, total FROM orders WHERE id = ?, linked to its request. - Issues:
RuntimeError: Checkout failed for order 42, unhandled, atmain.py:27; andZeroDivisionError: division by zero, handled, fromlogger.exception. - Logs: 2 ×
order … looked up(info) andcould not compute the discount(error), each linked to its request.
Django
Django's instrumentation needs DJANGO_SETTINGS_MODULE in the environment, and the development server's autoreloader has to be off:
export DJANGO_SETTINGS_MODULE=mysite.settings
opentelemetry-instrument python manage.py runserver --noreload
With views that run a query (through django.db.connection or the ORM), log with logging and raise an exception, the project shows:
- Requests:
GET /orders/<int:order_id>andGET /boom(status 500), named after the URL pattern. - Queries: every SQL statement, linked to its request, plus the queries Django runs at startup (without a request).
- Issues:
RuntimeError: Checkout failed for order 42, unhandled, once per request. Django also logs it asInternal Server Error: /boomon thedjango.requestlogger; that line shows under Logs, and Lampwick counts the error once.
We tested runserver. With gunicorn or uWSGI, the worker processes fork after the SDK starts; the OpenTelemetry docs describe a post_fork hook for that, which we haven't tested.
What's missing
connection.execute()onsqlite3isn't traced, onlycursor.execute(). Django and SQLAlchemy use cursors, so this only affects rawsqlite3code.- Info logs need a level. Python's root logger drops records below
WARNINGunless you configure it (logging.basicConfig(level=logging.INFO), orLOGGINGin Django). OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLEDis the old switch. It still works, but logs a deprecation warning into your logs; leave it out.
Troubleshooting
| What you see | Cause and fix |
|---|---|
| Django requests don't arrive, other spans do | DJANGO_SETTINGS_MODULE isn't set in the environment opentelemetry-instrument runs in, or the autoreloader is on: add --noreload. |
Skipping installation of LoggingHandler … to avoid duplicate logs in your logs | OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true is set. Remove it. |
NotOpenSSLWarning: urllib3 v2 only supports OpenSSL 1.1.1+ | macOS's system Python is built with LibreSSL. The export still works; use a Python from python.org or Homebrew to get rid of it. |
| Queries are missing | The driver's instrumentation isn't installed. Run opentelemetry-bootstrap -a install again after installing the driver. |