Files
Profeto/src/core/log_buffer.py
T
Profeto Agent 11efe91ce9 fix: 全方位审查问题修复(P0~P3)
P0: ORM-迁移同步
- 新增 RawEvent/IngestFailure/DataQualityCheck/DataLineage 4 个模型类
- MatchStats 补充 xg_source/xg_updated_at/xg_source_record_id 字段
- 修复 server_default=_utcnow → func.now()(4 处)
- BigInteger 导入

P1:
- log_buffer.py 移除 threading.Lock(asyncio 单线程下无需锁)
- 确认 context_builder/understat/injuries 等已有修复

P2:
- Dockerfile 新增非 root 用户 + .dockerignore
- 前端 fetchDashboard 修复 total 字段(改用 items.length)
- 前端 fetchSystemConfig 改用真实 /admin/settings 端点
- 修复 validation.py return self"" → return self

P3:
- predict.py 缓存加 _CACHE_MAX_SIZE=200 淘汰
- eval.py get_eval_summary 加 limit 参数(默认 1000)+ 返回 total_settled
- orchestrator.py agent provider 配置缓存 60s
2026-09-19 04:38:53 +00:00

79 lines
2.6 KiB
Python

"""内存日志缓冲:供后台「系统日志」页查看应用运行日志。
把应用日志(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))