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.

Datadog APM for Djangoddtrace in appauto-instrumentsDatadog Agentreceives tracesTagsservice, env, versionDashboardslatency, errors, SQLThe tracer records spans for views, queries, and calls; the agent ships them with shared tags.
  1. Run the Datadog Agent on each host (or as a DaemonSet or sidecar) with APM enabled.
  2. pip install ddtrace and start Gunicorn with ddtrace-run gunicorn config.wsgi:application.
  3. Set unified service tags: DD_SERVICE, DD_ENV, DD_VERSION (the release SHA).
  4. Start Celery workers with ddtrace-run too; traces link from the web request to the task.
  5. Enable log correlation (DD_LOGS_INJECTION=true) so log lines carry trace IDs.
  6. 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).

environment variables that make traces usefulDD_SERVICE=shop-webDD_ENV=productionDD_VERSION=3f2a1c9DD_LOGS_INJECTION=trueDD_TRACE_SAMPLE_RATE=0.21231One service name per process type: web, worker, beat.2Release SHA lets you compare latency before and after deploys.3Sample 20 percent of traces to control volume and cost.Consistent tags turn raw traces into per-release comparisonsdjango-deployment.com
Set the same DD_ENV and DD_VERSION on web and worker processes.

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_RATE or 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.
Datadog APM setupAgent with APM on every host or nodeDD_AGENT_HOST setddtrace-run for web, worker, beatdistinct DD_SERVICEDD_ENV and DD_VERSION on allcompare releasesLog injection enabledtraces link to logsSampling and health-check filteringcost under controlUnified tags and log correlation are what make APM data actionabledjango-deployment.com
Create monitors on p95 latency and error rate per service after the first week.

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=true adds 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

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.

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.

Keep going