Docs menu

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.

Updated View as Markdown

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:

Terminal
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

Terminal
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:

Terminal
opentelemetry-instrument uvicorn main:app --port 8000

The app we tested:

Python
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) and GET /handled.
  • Queries: SELECT id, total FROM orders WHERE id = ?, linked to its request.
  • Issues: RuntimeError: Checkout failed for order 42, unhandled, at main.py:27; and ZeroDivisionError: division by zero, handled, from logger.exception.
  • Logs: 2 × order … looked up (info) and could 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:

Terminal
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> and GET /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 as Internal Server Error: /boom on the django.request logger; 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() on sqlite3 isn't traced, only cursor.execute(). Django and SQLAlchemy use cursors, so this only affects raw sqlite3 code.
  • Info logs need a level. Python's root logger drops records below WARNING unless you configure it (logging.basicConfig(level=logging.INFO), or LOGGING in Django).
  • OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED is the old switch. It still works, but logs a deprecation warning into your logs; leave it out.

Troubleshooting

What you seeCause and fix
Django requests don't arrive, other spans doDJANGO_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 logsOTEL_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 missingThe driver's instrumentation isn't installed. Run opentelemetry-bootstrap -a install again after installing the driver.