ForcePilot/backend/package/yuxi/utils/logging_config.py
Kris 03d9dd0543 feat: 新增反馈渠道字段、插件状态表与日志敏感信息脱敏
1. 为消息反馈表新增channel字段并完善相关接口返回
2. 创建渠道插件状态存储表与对应索引
3. 调整路由规则agent_config_id字段类型为字符串
4. 添加日志敏感信息脱敏功能
5. 优化消息记录仓库方法,新增响应时间统计和查询接口
2026-05-13 16:50:28 +08:00

121 lines
3.9 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.

import logging
import os
import re
import sys
from loguru import logger as loguru_logger
from yuxi.utils.datetime_utils import shanghai_now
SAVE_DIR = os.getenv("SAVE_DIR") or "saves"
DATETIME = shanghai_now().strftime("%Y-%m-%d")
LOG_FILE = f"{SAVE_DIR}/logs/yuxi-{DATETIME}.log"
_SENSITIVE_RE_PATTERNS = [
(re.compile(r'(token[=:]\s*["\']?)([^\s"\'&,]+)', re.IGNORECASE), r"\1***"),
(re.compile(r'(secret[=:]\s*["\']?)([^\s"\'&,]+)', re.IGNORECASE), r"\1***"),
(re.compile(r'(password[=:]\s*["\']?)([^\s"\'&,]+)', re.IGNORECASE), r"\1***"),
(re.compile(r'(api_key[=:]\s*["\']?)([^\s"\'&,]+)', re.IGNORECASE), r"\1***"),
(re.compile(r"(Authorization:\s*)(Bearer\s+\S+)", re.IGNORECASE), r"\1***"),
(re.compile(r"(X-API-Key:\s*)(\S+)", re.IGNORECASE), r"\1***"),
(re.compile(r"'(app_secret|app_key|bot_token|access_token)':\s*'([^']+)'"), r"'\1': '***'"),
(re.compile(r'"(app_secret|app_key|bot_token|access_token)":\s*"([^"]+)"'), r'"\1": "***"'),
]
def _redact_sensitive_in_message(record: dict) -> None:
message = record.get("message", "")
if isinstance(message, str):
for pattern, replacement in _SENSITIVE_RE_PATTERNS:
message = pattern.sub(replacement, message)
record["message"] = message
class LoguruHandler(logging.Handler):
"""将 Python logging 桥接到 loguru 的 handler"""
def emit(self, record: logging.LogRecord):
level_map = {
logging.DEBUG: "DEBUG",
logging.INFO: "INFO",
logging.WARNING: "WARNING",
logging.ERROR: "ERROR",
logging.CRITICAL: "CRITICAL",
}
level = level_map.get(record.levelno, "DEBUG")
try:
msg = self.format(record)
except Exception:
msg = record.getMessage()
loguru_logger.opt(depth=1, exception=record.exc_info).log(level, msg)
def _setup_logging_bridge():
"""配置 logging 到 loguru 的桥接,捕获第三方库日志(如 LightRAG"""
loguru_handler = LoguruHandler()
loguru_handler.setLevel(logging.DEBUG)
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
loguru_handler.setFormatter(formatter)
# 桥接 LightRAG 日志
lightrag_logger = logging.getLogger("lightrag")
lightrag_logger.addHandler(loguru_handler)
lightrag_logger.setLevel(logging.DEBUG)
lightrag_logger.propagate = False # 避免重复
# 桥接其他常见第三方库(降低级别减少噪音)
for lib in ["httpx", "openai", "neo4j", "urllib3"]:
lib_logger = logging.getLogger(lib)
lib_logger.addHandler(loguru_handler)
lib_logger.setLevel(logging.WARNING)
lib_logger.propagate = False
def setup_logger(name, level="DEBUG", console=True):
"""使用 loguru 设置日志记录器"""
os.makedirs(f"{SAVE_DIR}/logs", exist_ok=True)
# 移除默认的 handler
loguru_logger.remove()
# 添加文件日志(无颜色)
loguru_logger.add(
LOG_FILE,
level=level,
format="{time:YYYY-MM-DD HH:mm:ss} - {level} - {file}:{line} - {message}",
encoding="utf-8",
rotation="10 MB",
retention="30 days",
compression="zip",
enqueue=True,
filter=_redact_sensitive_in_message,
)
# 添加控制台日志(有颜色)
if console:
loguru_logger.add(
sys.stderr,
level=level,
format=(
"<green>{time:MM-DD HH:mm:ss}</green> "
"<level>{level}</level> "
"<cyan>{file}:{line}</cyan>: "
"<level>{message}</level>"
),
colorize=True,
enqueue=True,
filter=_redact_sensitive_in_message,
)
return loguru_logger
# 设置根日志记录器
logger = setup_logger("Yuxi")
# 初始化 logging 桥接
_setup_logging_bridge()
__all__ = ["logger"]