From 517c30ba0e793342efdc4b9a790ebe5a66582e2d Mon Sep 17 00:00:00 2001 From: Ross Date: Mon, 1 Dec 2025 16:43:40 +0000 Subject: [PATCH] Enhance Docker setup with logging and monitoring features - Added logging configuration to WSGI for persistent log collection. - Updated Docker Compose files for local and development environments, including Redis, PostgreSQL, and Grafana services. - Introduced Promtail configuration for log scraping and integration with Loki. - Created application log directory in Dockerfile for log persistence. --- docker/docker-compose.dev.yml | 45 +++++++++++++++++++ ...r-compose.yml => docker-compose.local.yml} | 0 docker/promtail-config.yml | 18 ++++++++ rad/Dockerfile.prod | 6 +++ rad/wsgi.py | 30 +++++++++++++ 5 files changed, 99 insertions(+) rename docker/{docker-compose.yml => docker-compose.local.yml} (100%) create mode 100644 docker/promtail-config.yml diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index cc688c95..92b8ec23 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -15,10 +15,55 @@ services: command: [] volumes: - ../:/usr/src/app:cached + # Mount the app log directory so logs are visible on the host at ./logs + - ../logs:/var/log/rad nginx: # Development nginx override: mount a simplified config that does not # reference LetsEncrypt certs so the container can start without real # certificates present on the host. + image: nginx:1.25-alpine volumes: - ../deploy/nginx/dev.conf:/etc/nginx/conf.d/default.conf:ro + loki: + image: grafana/loki:2.8.2 + command: -config.file=/etc/loki/local-config.yaml + ports: + - "3100:3100" + volumes: + - ./loki-config/local-config.yaml:/etc/loki/local-config.yaml:ro + - ./loki-data:/loki + restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "wget -qO- http://localhost:3100/ready || exit 1"] + interval: 5s + timeout: 3s + retries: 6 + + promtail: + image: grafana/promtail:2.8.2 + volumes: + - ../logs:/var/log/rad:ro + - ./promtail-config.yml:/etc/promtail/promtail-config.yml:ro + command: -config.file=/etc/promtail/promtail-config.yml + depends_on: + - loki + restart: unless-stopped + + grafana: + image: grafana/grafana:10.2.0 + ports: + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin + volumes: + - grafana-data:/var/lib/grafana + depends_on: + loki: + condition: service_healthy + restart: unless-stopped + +volumes: + grafana-data: + loki-data: + diff --git a/docker/docker-compose.yml b/docker/docker-compose.local.yml similarity index 100% rename from docker/docker-compose.yml rename to docker/docker-compose.local.yml diff --git a/docker/promtail-config.yml b/docker/promtail-config.yml new file mode 100644 index 00000000..43135394 --- /dev/null +++ b/docker/promtail-config.yml @@ -0,0 +1,18 @@ +server: + http_listen_port: 9080 + grpc_listen_port: 0 + +positions: + filename: /tmp/positions.yaml + +clients: + - url: http://loki:3100/loki/api/v1/push + +scrape_configs: + - job_name: rad_app_logs + static_configs: + - targets: + - localhost + labels: + job: rad_app + __path__: /var/log/rad/*.log diff --git a/rad/Dockerfile.prod b/rad/Dockerfile.prod index c25ef6b9..f65b66f8 100644 --- a/rad/Dockerfile.prod +++ b/rad/Dockerfile.prod @@ -57,6 +57,12 @@ ENV PATH="/opt/venv/bin:$PATH" # `--chown` flag during COPY and avoid an expensive recursive `chown -R`. RUN useradd -m appuser +# Create directory for application logs and ensure ownership is correct. +# Do this as root before switching to `appuser` so the directory exists +# and can be mounted by the dev compose override. +RUN mkdir -p /var/log/rad \ + && chown -R appuser:appuser /var/log/rad + # Copy project files into image and set ownership during copy (faster than # a separate `chown -R` step). This keeps layer reuse efficient. COPY --chown=appuser:appuser . /usr/src/app diff --git a/rad/wsgi.py b/rad/wsgi.py index 23078214..78186fbb 100644 --- a/rad/wsgi.py +++ b/rad/wsgi.py @@ -10,6 +10,36 @@ https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/ import os from django.core.wsgi import get_wsgi_application +from loguru import logger +import sys +from pathlib import Path + +# Configure Loguru sinks early so app imports see consistent logging. +# Log to stdout (so `docker logs` shows messages) and to a file at +# `/var/log/rad/app.log` for persistent collection / host tailing. +try: + # ensure directory exists + log_dir = Path("/var/log/rad") + log_dir.mkdir(parents=True, exist_ok=True) + # Add stdout sink (if not already added by other code) + logger.remove() # remove default handlers to avoid duplicate lines + logger.add(sys.stdout, level="INFO", enqueue=True, backtrace=False, diagnose=False) + # Add rotating file sink for persistence in container. Use JSON lines + # (serialize=True) to make logs easy to query in Loki/Grafana. + logger.add( + str(log_dir / "app.log"), + rotation="10 MB", + retention="10 days", + level="DEBUG", + enqueue=True, + serialize=True, + ) +except Exception: + # Best-effort only — don't crash WSGI startup if logging can't be configured + try: + logger.add(sys.stderr) + except Exception: + pass os.environ.setdefault("DJANGO_SETTINGS_MODULE", "rad.settings")