mitol-django-observability
MIT Open Learning Django plugin for OpenTelemetry tracing, structured logging (structlog), and alerting-as-code.
Installation
Add to INSTALLED_APPS:
"mitol.observability.apps.ObservabilityConfig",
See the RFC for full documentation.
Celery integration
To propagate structured log context (request ID, user ID, …) from web workers into Celery tasks and ensure Celery workers emit JSON logs through the same structlog pipeline, install the celery extra and follow the steps below.
1. Install the extra
pip install "mitol-django-observability[celery]"
This adds django-structlog[celery] (context propagation) and opentelemetry-instrumentation-celery (OTel task spans, auto-discovered).
2. Django settings
MIDDLEWARE = [
# …
"django_structlog.middlewares.RequestMiddleware",
]
DJANGO_STRUCTLOG_CELERY_ENABLED = True
3. Celery application
from celery import Celery
from celery.signals import setup_logging
from django_structlog.celery.steps import DjangoStructLogInitStep
from mitol.observability.celery import setup_celery_logging
app = Celery("yourproject")
@setup_logging.connect
def on_setup_logging(**kwargs):
# Prevent Celery from overriding structlog's logging config in workers.
setup_celery_logging(**kwargs)
# Propagate web-request context into task execution.
app.steps["worker"].add(DjangoStructLogInitStep)
How it works
setup_celery_loggingcallsconfigure_structlog(force=True), re-applying the structlog pipeline after Celery resets logging during worker boot.DjangoStructLogInitStepinstalls signal handlers that bind the request context (captured byRequestMiddleware) to the structlog context vars before each task runs and clears it after.opentelemetry-instrumentation-celeryis auto-discovered via theopentelemetry_instrumentorentry-point group — no extra configuration required.
Postgres integration
To emit OTel spans for database queries, install the postgres extra:
pip install "mitol-django-observability[postgres]"
This adds two things:
opentelemetry-instrumentation-psycopg, auto-discovered via theopentelemetry_instrumentorentry-point group — no extra configuration required.psycopg[c], the compiled binding, which is the implementation you want in production. The section below explains why the extra decides this on your behalf.
Build prerequisites
psycopg[c] is compiled from source at install time, so the build environment needs a C compiler, Python development headers, libpq-dev, and pg_config on PATH. Images built on mitodl/ol-python-base already provide all four.
CI runners generally do not. GitHub's ubuntu-24.04 image, for instance, ships PostgreSQL but not libpq-dev, so add it to your Aptfile (or equivalent) alongside the other -dev packages:
libpq-dev
If you are in an environment that genuinely cannot compile, install psycopg[binary] explicitly in your application — a pre-compiled wheel that bundles its own libpq and libssl. Those bundled libraries do not receive OS security updates, which is why it is not the default here.
Why the extra decides the driver
The psycopg instrumentation targets psycopg 3, so installing this extra puts psycopg 3 in your environment. That has a consequence which is easy to miss: it changes which driver Django uses. Django's Postgres backend prefers psycopg 3 over psycopg2 whenever psycopg 3 is importable (django/db/backends/postgresql/base.py):
try:
try:
import psycopg as Database
except ImportError:
import psycopg2 as Database
An application running on psycopg2 therefore switches to psycopg 3 the moment this instrumentation is installed, with no change to its own code and nothing in its dependency diff that names a driver.
Since the extra is already making that decision, it specifies the implementation too. psycopg 3 ships three interchangeable bindings to libpq, and a bare psycopg dependency selects the slowest:
| dependency | binding | notes |
|---|---|---|
psycopg |
pure Python, via ctypes |
upstream calls it "much slower"; intended for local development and small tasks |
psycopg[c] |
compiled, linked against the system libpq | psycopg's recommendation for production — system libpq/libssl upgrades carry through |
psycopg[binary] |
compiled, bundles its own libpq/libssl | no build tools needed, but the bundled libraries do not receive OS security updates |
Extras union across dependency resolution, so requesting psycopg[c] here installs the compiled binding regardless of how the application declares psycopg itself.
Check what a deployed process actually resolved to — psycopg selects the best available implementation at import, so this reflects what is installed rather than what was requested:
>>> import psycopg
>>> psycopg.pq.__impl__
'c'
See psycopg's installation guide and pq module implementations for the full comparison.
Metadata
Release files for mitol-django-observability 2026.8.19
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mitol_django_observability-2026.8.19.tar.gz | 19.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mitol_django_observability-2026.8.19-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 47.1 kB
Release files / mitol_django_observability-2026.8.19.tar.gz
| Download URL | mitol_django_observability-2026.8.19.tar.gz |
|---|---|
| Size | 19.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3bf3afbd16c2f0ae087ed7911f301f31e3361ed170acdc9ef29e3cee8a8d0192
|
|
BLAKE2b-256 checksum How to use checksums |
b360ef5807301c6aa3a6ddbe2f48118a98d134f5fc324632f7715f270f3bb504
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / mitol_django_observability-2026.8.19-py3-none-any.whl
| Download URL | mitol_django_observability-2026.8.19-py3-none-any.whl |
|---|---|
| Size | 27.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
442741f6b6d3ddacd85f5d32c8581c18899af2f7f299a74bc8a4116f0ae3bb8a
|
|
BLAKE2b-256 checksum How to use checksums |
80dd6e6608392f587f29a950b7e6b27ef4748458dfdfcac70e7fb81907d56deb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|