Files
FishServer/fish_api/app/logging_config.py

83 lines
2.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""将 loguru 与标准 logging 桥接,使 uvicorn / FastAPI / Starlette 日志走同一套格式。"""
from __future__ import annotations
import json
import logging
import os
import sys
from typing import Any, Optional, Union
from loguru import logger
_loguru_sink_configured = False
def format_json_pretty(
data: Any,
*,
indent: int = 2,
max_chars: Optional[int] = 24_000,
) -> str:
"""将对象格式化为带缩进、保留中文的 JSON 字符串,供 loguru 多行输出。
``max_chars`` 用于避免单条日志过大;超长时截断并标注。
"""
try:
s = json.dumps(data, ensure_ascii=False, indent=indent, default=str)
except (TypeError, ValueError):
return repr(data)
if max_chars is not None and len(s) > max_chars:
return s[:max_chars] + "\n... (truncated, max_chars=" + str(max_chars) + ")"
return s
class InterceptHandler(logging.Handler):
"""将标准 logging 记录转发到 loguru。"""
def emit(self, record: logging.LogRecord) -> None:
try:
level = logger.level(record.levelname).name # type: Union[str, int]
except ValueError:
level = record.levelno
logger.opt(depth=6, exception=record.exc_info).log(level, record.getMessage())
def setup_logging() -> None:
"""配置 loguru sink并把 uvicorn / fastapi 等 logger 接到 loguru。
可在模块 import 时与 lifespan 启动时各调用一次;后者用于覆盖 uvicorn 启动后写入的 handler。
"""
global _loguru_sink_configured
level = os.environ.get("LOG_LEVEL", "INFO").upper()
if not _loguru_sink_configured:
logger.remove()
logger.add(
sys.stderr,
level=level,
colorize=sys.stderr.isatty(),
format=(
"<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
"<level>{level: <8}</level> | "
"<level>{message}</level>"
),
)
_loguru_sink_configured = True
intercept = InterceptHandler()
logging.root.handlers = [intercept]
logging.root.setLevel(logging.DEBUG)
for name in (
"uvicorn",
"uvicorn.error",
"uvicorn.access",
"uvicorn.asgi",
"fastapi",
"starlette",
"starlette.requests",
):
lg = logging.getLogger(name)
lg.handlers.clear()
lg.propagate = True