"""内存日志缓冲:供后台「系统日志」页查看应用运行日志。 把应用日志(stdout)同时捕获到进程内环形缓冲(deque),提供级别/关键字/条数 过滤查询。缓冲在进程重启后清零;需要持久化的审计请另行落库。 """ from __future__ import annotations import logging from collections import deque _BUFFER: deque[dict] = deque(maxlen=2000) _LEVEL_ORDER = {"DEBUG": 10, "INFO": 20, "WARNING": 30, "ERROR": 40, "CRITICAL": 50} class MemoryLogHandler(logging.Handler): """把日志记录写入内存环形缓冲。""" def __init__(self) -> None: super().__init__() # format() 会在有 exc_info 时自动附带异常堆栈文本 self.setFormatter(logging.Formatter("%(message)s")) def emit(self, record: logging.LogRecord) -> None: try: entry = { "ts": record.created, "level": record.levelname, "logger": record.name, "message": self.format(record), } _BUFFER.append(entry) except Exception: # noqa: BLE001 日志采集绝不影响业务 self.handleError(record) class _SQLNoiseFilter(logging.Filter): """过滤 SQLAlchemy 的 DEBUG/INFO 回显(只留警告以上)。""" def filter(self, record: logging.LogRecord) -> bool: return not ( record.name.startswith("sqlalchemy.") and record.levelno < logging.WARNING ) def get_entries( min_level: str | None = None, keyword: str | None = None, limit: int = 200, ) -> list[dict]: """按条件查询缓冲日志,最新在前。""" min_no = _LEVEL_ORDER.get((min_level or "").upper(), 0) kw = (keyword or "").strip().lower() items = list(_BUFFER) items.reverse() out: list[dict] = [] for e in items: if _LEVEL_ORDER.get(e["level"], 0) < min_no: continue if kw and kw not in e["message"].lower() and kw not in e["logger"].lower(): continue out.append(e) if len(out) >= limit: break return out def setup_memory_logging(level: str = "INFO") -> None: """挂载内存 handler 到 root logger(幂等),并确保 root 级别不低于 INFO。""" root = logging.getLogger() if any(isinstance(h, MemoryLogHandler) for h in root.handlers): return handler = MemoryLogHandler() handler.setLevel(logging.INFO) handler.addFilter(_SQLNoiseFilter()) root.addHandler(handler) if root.level == logging.NOTSET or root.level > logging.INFO: root.setLevel(getattr(logging, level.upper(), logging.INFO))