Monitor Django With Datadog APM
Instrument a Django app with Datadog APM: install ddtrace, run Gunicorn under ddtrace-run, configure service, env, and version tags, trace Celery, PostgreSQL, and Redis, correlate logs with traces, and control cost with sampling.
Problem statement
Logs tell you something went wrong; APM shows where time goes in each request: which view, which SQL queries, which Redis calls and outbound HTTP requests, and how a slow endpoint's latency breaks down. Datadog's Python tracer (ddtrace) instruments Django, Celery, database drivers, caches, and HTTP clients automatically. Getting useful data in production means running the agent, tagging traces consistently, correlating logs, and keeping volume (and cost) under control.
- Run the Datadog Agent on each host (or as a DaemonSet or sidecar) with APM enabled.
pip install ddtraceand start Gunicorn withddtrace-run gunicorn config.wsgi:application.- Set unified service tags:
DD_SERVICE,DD_ENV,DD_VERSION(the release SHA). - Start Celery workers with
ddtrace-runtoo; traces link from the web request to the task. - Enable log correlation (
DD_LOGS_INJECTION=true) so log lines carry trace IDs. - Control volume with sampling rules and by dropping health-check traces.
Step-by-step solution
1. Run the agent
On a VM, install the agent and enable APM in datadog.yaml (apm_config: enabled: true). In Docker Compose, run the datadog/agent container with DD_APM_ENABLED=true and point the app at it with DD_AGENT_HOST=datadog-agent. In Kubernetes, use the Datadog Helm chart or Operator, which runs the agent as a DaemonSet and can inject configuration into pods.
2. Instrument the app
pip install ddtrace
# systemd unit
Environment=DD_SERVICE=shop-web DD_ENV=production DD_VERSION=3f2a1c9
Environment=DD_LOGS_INJECTION=true DD_TRACE_SAMPLE_RATE=0.2
ExecStart=/srv/app/.venv/bin/ddtrace-run /srv/app/.venv/bin/gunicorn -c gunicorn.conf.py config.wsgi:application
ddtrace-run patches supported libraries at startup: Django (views, middleware, templates), psycopg, Redis, Celery, requests, httpx, and more. With Gunicorn preload_app, make sure tracing is initialised in each worker (ddtrace handles forking, but check its docs for your version).
3. Trace Celery
DD_SERVICE=shop-worker ddtrace-run celery -A config worker -l info
The tracer propagates context in task headers, so a request that queues a task shows the task as a linked trace. Beat can be traced as shop-beat.
4. Correlate logs and traces
With DD_LOGS_INJECTION=true, dd.trace_id and dd.span_id are added to log records; include them in your formatter (JSON logging picks them up). Ship logs with the agent's log collection, and Datadog links each trace to the log lines it produced.
5. Custom spans and tags
from ddtrace import tracer
def checkout(request):
with tracer.trace("checkout.price_cart", resource="price_cart") as span:
span.set_tag("cart.items", len(cart))
total = price_cart(cart)
Tag business context (plan, tenant) carefully: high-cardinality tags such as user IDs can increase cost; use them on spans, not as metric tags.
6. Control volume
- Sampling:
DD_TRACE_SAMPLE_RATEor rules per service and endpoint; keep errors and slow traces with retention filters. - Drop noise: ignore health-check URLs (
DD_TRACE_DJANGO_...settings or a filter) so they do not dominate. - Watch ingestion in Datadog's usage pages after enabling.
Verification check: within minutes of deploying, the service appears in Datadog APM with requests, latency, and errors; a slow request's flame graph shows SQL spans with query text (obfuscated values); a log line from that request links to its trace.
Explanation
APM tracers wrap framework and library calls to record spans: timed operations with metadata, nested to show causality. ddtrace does this automatically for Django and its common dependencies by patching them at startup, and sends spans to the local agent, which batches them to Datadog. Unified service tags (service, env, version) let Datadog group spans and compare deployments, and injecting trace IDs into logs links the two data sources. Sampling keeps the volume affordable while retaining the traces that matter most.
Edge cases / notes
- OpenTelemetry. Datadog accepts OTLP; if you want vendor-neutral instrumentation, use OpenTelemetry SDKs and send to the agent.
- Profiling.
DD_PROFILING_ENABLED=trueadds continuous CPU and memory profiling, useful for hot paths. - Serverless platforms need Datadog's specific extensions or layers.
- PII. SQL query values are obfuscated by default; avoid tagging spans with personal data.
Next steps
- Vendor-neutral tracing: Django OpenTelemetry Tracing.
- What to monitor overall: Django Monitoring Checklist.
FAQ
How do I add Datadog APM to Django?
Install ddtrace, run the Datadog Agent with APM enabled, and start Gunicorn with ddtrace-run.
Does ddtrace trace Celery tasks?
Yes. Start workers with ddtrace-run and traces propagate from web requests to tasks.
How do I link logs to traces in Datadog?
Set DD_LOGS_INJECTION=true and include the trace ID fields in your log format.
How do I keep Datadog costs down?
Sample traces, filter health checks, and avoid high-cardinality metric tags.