Skip to content

Repository files navigation

Django Observability Stack

A self-contained demonstration of structured application logging and log-based observability for a Django backend. The entire backend and monitoring stack runs locally with one Docker Compose command.

Purpose

This project shows how a Python web application can emit structured JSON logs, collect them directly from Docker, store them in Loki, and turn them into useful Grafana dashboards without adding a separate metrics system.

The sample API intentionally introduces variable response times and occasional HTTP errors. This makes it possible to generate realistic traffic and explore:

  • request throughput;
  • response status distribution;
  • 5xx error ratio;
  • p50, p95, and p99 request latency;
  • traffic and average latency by endpoint;
  • individual requests using fields such as request_id, path, and status_code.

Architecture

HTTP client
    |
    v
Django + Gunicorn
    |
    | structured JSON on stdout/stderr
    v
Docker logging stream
    |
    v
Grafana Alloy  --->  Grafana Loki  <---  Grafana
  discovery          log storage         queries and dashboards

The stack contains four services:

Service Role Published port
backend Django REST API served by Gunicorn 8000
alloy Discovers the backend container and forwards its logs Internal only
loki Stores and queries logs with 30-day retention Internal only
grafana Provides log exploration and dashboards 3000

Alloy collects only the backend service from this Compose project. Loki and Alloy are not exposed to the host network. Grafana's Loki datasource and the Backend HTTP Overview dashboard are provisioned automatically.

Requirements

  • Docker Engine
  • Docker Compose v2
  • curl and Bash for the optional traffic generator

Quick start

Create the local environment file:

cp .env.example .env

Replace at least these placeholder values in .env:

SECRET_KEY=replace-with-a-long-random-secret
GRAFANA_ADMIN_PASSWORD=replace-with-a-strong-password

Build and start the complete stack:

docker compose up -d --build

Check that every service is running:

docker compose ps

The services are available at:

Sign in to Grafana with GRAFANA_ADMIN_USER and GRAFANA_ADMIN_PASSWORD from .env.

Generate test traffic

The included script rotates between the health, users, and products endpoints. The second argument is the number of requests and the third is concurrency:

./generate-traffic.sh http://127.0.0.1:8000 1000 20

The API deliberately returns an error for roughly 10% of requests so that error panels and status-code series contain meaningful data.

Explore logs

Open Grafana, select Explore, choose the Loki datasource, and run:

{service_name="backend"}

To see only successfully parsed HTTP request events:

{service_name="backend"}
| json
| message="http_request_finished"
| __error__=""

The provisioned dashboard is available under:

Dashboards > Observability > Backend HTTP Overview

It includes request rate, status-code rate, latency percentiles, 5xx ratio, top endpoints, and average endpoint latency.

Structured log format

Each completed request produces one JSON event similar to:

{
  "levelname": "INFO",
  "name": "api.middleware",
  "message": "http_request_finished",
  "request_id": "7f46d6a8-0f2a-4ecb-aadc-ae18af65e93c",
  "ip_addr": "172.21.0.1",
  "duration_ms": 77,
  "status_code": 200,
  "method": "GET",
  "path": "/api/health/"
}

High-cardinality values such as request_id remain inside the JSON body and are extracted at query time. Only bounded fields such as service, environment, platform, and log level are stored as Loki stream labels.

Persistence

Named Docker volumes persist data between container replacements:

Volume Contents
backend-data SQLite database
loki-data Loki indexes, chunks, and WAL
alloy-data Log-reading positions
grafana-data Grafana database and user state

Stop the stack without deleting its data:

docker compose down

Reset the project and permanently delete all persisted data:

docker compose down -v
docker compose up -d --build

Useful commands

Follow logs from the complete stack:

docker compose logs -f backend alloy loki grafana

Follow the backend logs:

docker compose logs -f backend

Restart services without deleting data:

docker compose restart

Recreate a service after changing its configuration:

docker compose up -d --force-recreate alloy

Security note

Alloy requires access to /var/run/docker.sock for Docker discovery and log collection. Access to this socket is effectively privileged host access, even when it is mounted read-only. Use only trusted images and configuration, never publish Alloy publicly, and consider a restricted Docker socket proxy for a production deployment.

The default setup is intended for local development and demonstration. Before exposing it publicly, use strong secrets, HTTPS, a production database, proper host validation, backups, and network-level access controls.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages