修改日志结构

This commit is contained in:
xinzhu.yin
2026-04-29 16:43:31 +08:00
parent 377bba2a0b
commit afd83448ed
6 changed files with 216 additions and 46 deletions

122
app/logging_setup.py Normal file
View File

@@ -0,0 +1,122 @@
from __future__ import annotations
import logging
import os
from datetime import datetime
from typing import Optional
# stdlib 级别 → PQLogGUI 标签
_LEVEL_TO_GUI = {
logging.DEBUG: "debug",
logging.INFO: "info",
logging.WARNING: "warning",
logging.ERROR: "error",
logging.CRITICAL: "error",
}
# PQLogGUI 标签 → stdlib 级别
GUI_LEVEL_TO_LOG = {
"debug": logging.DEBUG,
"info": logging.INFO,
"success": logging.INFO,
"warning": logging.WARNING,
"error": logging.ERROR,
"separator": logging.INFO,
"blank": logging.DEBUG,
}
# 用于在 LogRecord 上做 GUI 来源标记,避免回环
_FROM_GUI_FLAG = "_from_gui"
# 文件日志:头一行元信息,第二行正文,记录之间空行隔开,方便阅读
_FILE_LOG_FORMAT = "%(asctime)s [%(levelname)s] %(name)s\n%(message)s\n"
_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
_initialized = False
def _resolve_log_dir(log_dir: Optional[str]) -> str:
if log_dir:
return log_dir
# 默认放在工作目录下的 log/,方便用户直接打开查看
return os.path.join(os.getcwd(), "log")
def setup_logging(
level: int = logging.INFO,
log_dir: Optional[str] = None,
) -> str:
"""配置全局 logging返回日志目录。可重复调用第二次起为空操作。
- 不再向终端输出,避免污染控制台。
- 日志文件按日期命名:``log/YYYY-MM-DD.log``。
"""
global _initialized
root = logging.getLogger()
if _initialized:
return _resolve_log_dir(log_dir)
resolved = _resolve_log_dir(log_dir)
os.makedirs(resolved, exist_ok=True)
file_formatter = logging.Formatter(_FILE_LOG_FORMAT, datefmt=_DATE_FORMAT)
root.setLevel(level)
# 移除既有 handler避免重复 / basicConfig 默认控制台输出
for handler in list(root.handlers):
root.removeHandler(handler)
today = datetime.now().strftime("%Y-%m-%d")
log_file = os.path.join(resolved, f"{today}.log")
file_handler = logging.FileHandler(log_file, encoding="utf-8", delay=True)
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(file_formatter)
root.addHandler(file_handler)
_initialized = True
logging.getLogger(__name__).info("日志系统初始化完成 dir=%s", resolved)
return resolved
class TkLogHandler(logging.Handler):
"""把 stdlib logging 的记录转发到 ``PQLogGUI`` 面板。
带 ``_from_gui`` 标记的记录会被忽略,避免 GUI → file → GUI 回环。
"""
def __init__(self, log_gui):
super().__init__()
self._log_gui = log_gui
def emit(self, record: logging.LogRecord) -> None: # noqa: D401
if getattr(record, _FROM_GUI_FLAG, False):
return
try:
message = self.format(record)
except Exception:
try:
message = record.getMessage()
except Exception:
return
gui_level = _LEVEL_TO_GUI.get(record.levelno, "info")
try:
# PQLogGUI.log 内部已处理跨线程
self._log_gui.log(message, level=gui_level, _from_logging=True)
except Exception:
self.handleError(record)
def attach_gui_handler(log_gui) -> TkLogHandler:
"""把 ``PQLogGUI`` 注册为 root logger 的 handler。已存在则替换。"""
root = logging.getLogger()
# 移除旧的 TkLogHandler保证只挂一个
for h in list(root.handlers):
if isinstance(h, TkLogHandler):
root.removeHandler(h)
handler = TkLogHandler(log_gui)
handler.setLevel(logging.INFO)
handler.setFormatter(logging.Formatter("%(name)s: %(message)s"))
root.addHandler(handler)
return handler