68 lines
1.8 KiB
Python
68 lines
1.8 KiB
Python
"""Unified logging with RotatingFileHandler for all Sanad modules."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import sys
|
|
from logging.handlers import RotatingFileHandler
|
|
from pathlib import Path
|
|
|
|
from Project.Sanad.config import LOGS_DIR
|
|
|
|
_MAX_BYTES = 10 * 1024 * 1024 # 10 MB
|
|
_BACKUP_COUNT = 3
|
|
_FMT = "%(asctime)s [%(name)s] %(levelname)s %(message)s"
|
|
_formatter = logging.Formatter(_FMT)
|
|
|
|
# Callback for the WebSocket log stream — set by log_stream.py at import time.
|
|
_ws_push_fn = None
|
|
|
|
|
|
def set_ws_push(fn):
|
|
"""Register the push function from dashboard.websockets.log_stream."""
|
|
global _ws_push_fn
|
|
_ws_push_fn = fn
|
|
|
|
|
|
class _WSHandler(logging.Handler):
|
|
"""Forwards every log record to the WebSocket log stream."""
|
|
|
|
def emit(self, record: logging.LogRecord):
|
|
if _ws_push_fn is not None:
|
|
try:
|
|
_ws_push_fn(self.format(record))
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
def get_logger(name: str, *, to_console: bool = True) -> logging.Logger:
|
|
"""Return a module-level logger that writes to logs/<name>.log (rotating)."""
|
|
logger = logging.getLogger(f"sanad.{name}")
|
|
if logger.handlers:
|
|
return logger
|
|
|
|
logger.setLevel(logging.DEBUG)
|
|
logger.propagate = False
|
|
|
|
LOGS_DIR.mkdir(parents=True, exist_ok=True)
|
|
fh = RotatingFileHandler(
|
|
LOGS_DIR / f"{name}.log", maxBytes=_MAX_BYTES, backupCount=_BACKUP_COUNT
|
|
)
|
|
fh.setFormatter(_formatter)
|
|
fh.setLevel(logging.DEBUG)
|
|
logger.addHandler(fh)
|
|
|
|
if to_console:
|
|
sh = logging.StreamHandler(sys.stdout)
|
|
sh.setFormatter(_formatter)
|
|
sh.setLevel(logging.INFO)
|
|
logger.addHandler(sh)
|
|
|
|
# WebSocket stream handler
|
|
wsh = _WSHandler()
|
|
wsh.setFormatter(_formatter)
|
|
wsh.setLevel(logging.INFO)
|
|
logger.addHandler(wsh)
|
|
|
|
return logger
|