from __future__ import annotations import json import logging from typing import Dict from core.paths import CONFIG_DIR, LOGS_DIR _LOGGER_CACHE: Dict[str, logging.Logger] = {} def _load_log_cfg() -> dict: cfg_path = CONFIG_DIR / "logging.json" try: with open(cfg_path, "r") as f: return json.load(f) except Exception: return {} def _level_from_name(name: str) -> int: return getattr(logging, str(name).upper(), logging.INFO) def get_logger(category: str, name: str) -> logging.Logger: """Return a cached logger that writes to logs//.log.""" key = f"{category}.{name}" if key in _LOGGER_CACHE: return _LOGGER_CACHE[key] log_cfg = _load_log_cfg() log_dir = LOGS_DIR / category log_dir.mkdir(parents=True, exist_ok=True) logger = logging.getLogger(key) level_name = ( log_cfg.get("categories", {}).get(category) or log_cfg.get("level", "INFO") ) logger.setLevel(_level_from_name(level_name)) logger.propagate = False if logger.handlers: logger.handlers.clear() fmt = logging.Formatter( log_cfg.get("format", "%(asctime)s | %(name)s | %(levelname)s | %(message)s") ) if log_cfg.get("file", True): fh = logging.FileHandler(log_dir / f"{name}.log", encoding="utf-8") fh.setFormatter(fmt) logger.addHandler(fh) if log_cfg.get("console", False): sh = logging.StreamHandler() sh.setFormatter(fmt) logger.addHandler(sh) _LOGGER_CACHE[key] = logger return logger