# Django

> Instrument Django applications with OpenTelemetry for automatic request tracing, ORM query monitoring, and full-stack observability — zero code changes required

Source: https://last9.io/docs/integrations/frameworks/python/django/

Use OpenTelemetry to instrument your Django application and send telemetry data to Last9.
`opentelemetry-instrument` handles instrumentation at startup — no code changes required.

## Prerequisites

- Python 3.8 or higher
- Django 3.2 or higher
- Last9 account with OTLP credentials

## Installation

### 1. Install packages

```shell
pip install opentelemetry-distro opentelemetry-exporter-otlp
```

Then auto-detect and install instrumentation packages for your dependencies:

```shell
opentelemetry-bootstrap -a install
```

For a typical Django stack this installs:

```
opentelemetry-instrumentation-django
opentelemetry-instrumentation-psycopg2      # PostgreSQL
opentelemetry-instrumentation-redis         # Redis cache / sessions
opentelemetry-instrumentation-celery        # Celery tasks
opentelemetry-instrumentation-requests      # Outbound HTTP
opentelemetry-instrumentation-logging
opentelemetry-instrumentation-urllib3
```

### 2. Set environment variables

```shell
export OTEL_SERVICE_NAME=django-app
export OTEL_EXPORTER_OTLP_ENDPOINT="$last9_otlp_endpoint"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=$last9_otlp_auth_header"
export OTEL_TRACES_EXPORTER=otlp
export OTEL_METRICS_EXPORTER=otlp
export OTEL_TRACES_SAMPLER="always_on"
export OTEL_RESOURCE_ATTRIBUTES="deployment.environment=production"
export OTEL_LOG_LEVEL=error
```

### 3. Run with `opentelemetry-instrument`

**Dev server**

```shell
opentelemetry-instrument python manage.py runserver
```

**Gunicorn (production)**

Gunicorn forks worker processes after boot, which resets OTel's global state. Use a `post_fork` hook to re-initialize in each worker.

Create `gunicorn.conf.py`:

```python
from opentelemetry import metrics, trace
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

workers = 4
bind = "0.0.0.0:8000"

def post_fork(server, worker):
    resource = Resource.create({"service.name": "django-app"})

    tp = TracerProvider(resource=resource)
    tp.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
    trace.set_tracer_provider(tp)

    reader = PeriodicExportingMetricReader(OTLPMetricExporter())
    mp = MeterProvider(resource=resource, metric_readers=[reader])
    metrics.set_meter_provider(mp)
```

```shell
opentelemetry-instrument gunicorn myproject.wsgi -c gunicorn.conf.py
```

**uWSGI**

```shell
opentelemetry-instrument uwsgi --http :8000 --module myproject.wsgi --master --processes 4
```

**ASGI (Uvicorn)**

For Django Channels or async Django:

```shell
opentelemetry-instrument uvicorn myproject.asgi:application --host 0.0.0.0 --port 8000
```

## What Gets Instrumented Automatically

| Component     | Package                                  | What's traced                                  |
| ------------- | ---------------------------------------- | ---------------------------------------------- |
| HTTP requests | `opentelemetry-instrumentation-django`   | View method, URL pattern, status code, latency |
| Django ORM    | `opentelemetry-instrumentation-django`   | SQL queries, table, operation type             |
| PostgreSQL    | `opentelemetry-instrumentation-psycopg2` | Query text, db name, server address/port       |
| Redis         | `opentelemetry-instrumentation-redis`    | Command, key, latency                          |
| Celery        | `opentelemetry-instrumentation-celery`   | Task name, queue, retry count, result          |
| Outbound HTTP | `opentelemetry-instrumentation-requests` | Method, URL, status code                       |

## Docker

```dockerfile
FROM python:3.12-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt && opentelemetry-bootstrap -a install

COPY . .

ENV OTEL_SERVICE_NAME=django-app
ENV OTEL_TRACES_SAMPLER=always_on

CMD ["opentelemetry-instrument", "gunicorn", "myproject.wsgi", "-c", "gunicorn.conf.py"]
```

## Kubernetes Deployment

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: django-app
spec:
  template:
    spec:
      containers:
        - name: django-app
          image: your-registry/django-app:latest
          command:
            [
              "opentelemetry-instrument",
              "gunicorn",
              "myproject.wsgi",
              "-c",
              "gunicorn.conf.py",
            ]
          env:
            - name: OTEL_SERVICE_NAME
              value: "django-app"
            - name: OTEL_EXPORTER_OTLP_ENDPOINT
              valueFrom:
                secretKeyRef:
                  name: last9-credentials
                  key: endpoint
            - name: OTEL_EXPORTER_OTLP_HEADERS
              valueFrom:
                secretKeyRef:
                  name: last9-credentials
                  key: auth-header
            - name: OTEL_RESOURCE_ATTRIBUTES
              value: "deployment.environment=production"
```

## View Traces and Metrics

After running your application, navigate to [Trace Explorer](https://app.last9.io/traces) and [Metrics Explorer](https://app.last9.io/metrics) in Last9.
