TeX 53.6%
Python 46.2%
1"""Structured logging: console + per-run JSONL file under logs/."""23from __future__ import annotations45import json6import logging7import sys8import time9from pathlib import Path101112class JsonlHandler(logging.Handler):13 def __init__(self, path: Path):14 super().__init__()15 path.parent.mkdir(parents=True, exist_ok=True)16 self._f = open(path, "a")1718 def emit(self, record: logging.LogRecord) -> None:19 entry = {20 "ts": time.strftime("%Y-%m-%dT%H:%M:%S%z"),21 "level": record.levelname,22 "name": record.name,23 "msg": record.getMessage(),24 }25 if hasattr(record, "extra_fields"):26 entry.update(record.extra_fields)27 self._f.write(json.dumps(entry) + "\n")28 self._f.flush()293031def get_logger(name: str, logfile: Path | None = None) -> logging.Logger:32 logger = logging.getLogger(name)33 if logger.handlers:34 return logger35 logger.setLevel(logging.INFO)36 sh = logging.StreamHandler(sys.stderr)37 sh.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(name)s: %(message)s"))38 logger.addHandler(sh)39 if logfile is not None:40 logger.addHandler(JsonlHandler(logfile))41 return logger42