The root logger previously had no handler at all, so every module's logger.info() call (user creation, claims, password resets, ...) was silently dropped, not just unviewable. Adds a RotatingFileHandler writing into the existing /data volume so log content also survives container restarts/redeploys, plus /admin/logs (tail + line-count picker + full-file download) alongside the existing Users & Frames admin page.
35 lines
1.3 KiB
Python
35 lines
1.3 KiB
Python
"""Root-logger configuration: a rotating file handler under the same
|
|
/data volume as the sqlite DB and legacy config.json, so the admin log
|
|
viewer has something to read and log content survives container
|
|
restarts -- a redeploy happens on every push to main touching
|
|
server/**, which would make an in-memory-only log buffer nearly
|
|
useless in practice. Before this, the root logger had no handler at
|
|
all, so every module's logger.info() call (user creation, claims,
|
|
password resets, ...) was silently dropped rather than merely
|
|
un-viewable -- this fixes that too, not just adds a viewer."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import os
|
|
from logging.handlers import RotatingFileHandler
|
|
from pathlib import Path
|
|
|
|
LOG_PATH = Path(os.environ.get("LOG_PATH", "/data/server.log"))
|
|
|
|
|
|
def configure_logging() -> None:
|
|
LOG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
handler = RotatingFileHandler(LOG_PATH, maxBytes=2_000_000, backupCount=3)
|
|
handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(name)s: %(message)s"))
|
|
root = logging.getLogger()
|
|
root.addHandler(handler)
|
|
root.setLevel(os.environ.get("LOG_LEVEL", "INFO"))
|
|
|
|
|
|
def read_log_tail(lines: int) -> str:
|
|
if not LOG_PATH.exists():
|
|
return ""
|
|
text = LOG_PATH.read_text(errors="replace")
|
|
return "\n".join(text.splitlines()[-lines:])
|