Files
my-v0-server/app/core/logging.py
T
2026-09-29 00:47:57 +08:00

63 lines
1.9 KiB
Python

"""统一配置应用、Uvicorn 和第三方库日志"""
import logging
import sys
from pathlib import Path
from loguru import logger
LOG_PATH = Path(__file__).resolve().parents[2] / "logs" / "app.log"
LOG_FORMAT = "{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {extra[thread_id]} | {extra[node]} | {message}"
_initialized = False
class InterceptHandler(logging.Handler):
"""将标准 logging 转发到 Loguru,兼容第三方库"""
def emit(self, record: logging.LogRecord) -> None:
# FastAPI 已记录的异常不再由 Uvicorn 重复打印堆栈
if record.exc_info and getattr(record.exc_info[1], "_logged_by_api", False):
return
try:
level = logger.level(record.levelname).name
except ValueError:
level = record.levelno
logger.opt(exception=record.exc_info).log(level, record.getMessage())
def setup_logging() -> None:
"""应用启动或独立测试入口调用,多次调用不会重复添加输出"""
global _initialized
if _initialized:
return
LOG_PATH.parent.mkdir(parents=True, exist_ok=True)
logger.remove()
logger.configure(extra={"thread_id": "-", "node": "-"})
options = {
"level": "INFO",
"format": LOG_FORMAT,
"enqueue": True,
"diagnose": False,
"backtrace": False,
}
logger.add(sys.stderr, colorize=True, **options)
logger.add(
LOG_PATH,
rotation="20 MB",
retention="14 days",
compression="zip",
encoding="utf-8",
**options,
)
# 清理已有输出处理器,让标准日志只经过根处理器输出一次
logging.basicConfig(handlers=[InterceptHandler()], level=logging.INFO, force=True)
for name in list(logging.root.manager.loggerDict):
existing = logging.getLogger(name)
existing.handlers.clear()
existing.propagate = True
_initialized = True