Files
plm-lottery/app/logging_config.py
T
davideandClaude Sonnet 5.5 575f1a5354 Readiness, Electrum status, audit browsing and logs (B-92, API-37, API-46, API-47, AUD-34, AUD-41, ARC-02, ARC-09, ARC-61, ELC-29, SEC-48)
- GET /health/ready (Electrum connected, header < 1800s, scheduler heartbeat < 30s) is the
  container healthcheck; the scheduler beats on every tick and draw-wait iteration
- GET /admin/electrum and an Electrum indicator in /admin (server, header age, last rotation)
- GET /admin/audit-log: before_id, event_type, round_id, user_id, from, to; the panel gets filters
- registration writes user_registered in its transaction
- the app's own logs also go to stdout; uvicorn trusts X-Forwarded-For from Caddy's fixed
  address only, so the access log shows the client's IP

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-18 01:15:22 +02:00

37 lines
1.5 KiB
Python

import logging
import sys
from logging.handlers import RotatingFileHandler
from pathlib import Path
LOG_DIR = Path(__file__).resolve().parent.parent / "logs"
LOG_FILE = LOG_DIR / "app.log"
_FORMAT = "%(asctime)s %(levelname)s [%(name)s] %(message)s"
def setup_logging() -> None:
"""Route all app + uvicorn logging to logs/app.log so errors are traceable
without depending on however the process happens to be launched."""
LOG_DIR.mkdir(exist_ok=True)
handler = RotatingFileHandler(LOG_FILE, maxBytes=10 * 1024 * 1024, backupCount=5)
handler.setFormatter(logging.Formatter(_FORMAT))
# B-92 (ARC-09): also on stdout, so `docker compose logs app` shows the app's own messages
# (scheduler, listener, reconcilers) next to uvicorn's and the startup output. Only on the
# root logger: uvicorn's loggers already print to the console by themselves.
console = logging.StreamHandler(sys.stdout)
console.setFormatter(logging.Formatter(_FORMAT))
root = logging.getLogger()
root.setLevel(logging.INFO)
root.addHandler(handler)
root.addHandler(console)
# "uvicorn.error"/"uvicorn.access" have propagate=False in uvicorn's default
# logging config, so they never reach the root handler above and need their
# own. The parent "uvicorn" logger is deliberately skipped: uvicorn.error
# already bubbles into it, so attaching there too would double every line.
for logger_name in ("uvicorn.error", "uvicorn.access"):
logging.getLogger(logger_name).addHandler(handler)