1. 为消息反馈表新增channel字段并完善相关接口返回 2. 创建渠道插件状态存储表与对应索引 3. 调整路由规则agent_config_id字段类型为字符串 4. 添加日志敏感信息脱敏功能 5. 优化消息记录仓库方法,新增响应时间统计和查询接口
121 lines
3.9 KiB
Python
121 lines
3.9 KiB
Python
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"]
|