eolas / app /logging_setup.py
rahulraj1406's picture
v0.2.0: hardening pass — cache, rate limit, structured logs, link UX
a19bf9a
Raw
History Blame Contribute Delete
2.13 kB
"""
Logging setup — structured JSON in production, human-readable in dev.
Switch with env var LOG_FORMAT=json|text (default: text for local dev).
Every /chat request gets a `request_id` UUID; pass it via `extra={...}` on
every log call inside the request, and downstream tooling can trace the
question through retrieve → LLM → response.
"""
from __future__ import annotations
import json
import logging
import os
import sys
from datetime import datetime, timezone
# These keys are part of the default LogRecord and are not user payload —
# everything else found on the record was added via `extra=` and should ship.
_STANDARD_ATTRS = {
"name", "msg", "args", "levelname", "levelno", "pathname", "filename",
"module", "exc_info", "exc_text", "stack_info", "lineno", "funcName",
"created", "msecs", "relativeCreated", "thread", "threadName",
"processName", "process", "message", "taskName",
}
class JsonFormatter(logging.Formatter):
def format(self, record: logging.LogRecord) -> str:
payload: dict = {
"ts": datetime.now(timezone.utc).isoformat(),
"level": record.levelname,
"logger": record.name,
"msg": record.getMessage(),
}
for k, v in record.__dict__.items():
if k not in _STANDARD_ATTRS and not k.startswith("_"):
payload[k] = v
if record.exc_info:
payload["exc"] = self.formatException(record.exc_info)
return json.dumps(payload, ensure_ascii=False, default=str)
def configure_logging() -> None:
fmt = os.getenv("LOG_FORMAT", "text").lower()
handler = logging.StreamHandler(sys.stdout)
if fmt == "json":
handler.setFormatter(JsonFormatter())
else:
handler.setFormatter(logging.Formatter(
"%(asctime)s %(levelname)s %(name)s %(message)s"
))
root = logging.getLogger()
# Replace any pre-existing handlers so uvicorn's default doesn't double-log.
for h in list(root.handlers):
root.removeHandler(h)
root.addHandler(handler)
root.setLevel(os.getenv("LOG_LEVEL", "INFO").upper())