63 lines
1.6 KiB
Python
63 lines
1.6 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import logging
|
|
from typing import Dict
|
|
|
|
from saqr.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 runtime/logs/<category>/<name>.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
|