Files
aiagent/backend/app/core/logging_config.py

77 lines
2.5 KiB
Python
Raw Normal View History

"""
JSON 结构化日志配置 用于 ELK 日志聚合
用法: main.py 启动时调用 setup_json_logging() 即可
会在 logs/ 目录下并行输出 app.json.logJSON 格式
现有文本格式日志不受影响
"""
from __future__ import annotations
import json
import logging
import os
from datetime import datetime, timezone
from logging.handlers import RotatingFileHandler
from pathlib import Path
from typing import Any
from app.core.config import settings
class JsonFormatter(logging.Formatter):
"""将日志记录格式化为单行 JSON便于 Filebeat → Elasticsearch 采集。"""
def format(self, record: logging.LogRecord) -> str:
log_entry: dict[str, Any] = {
"timestamp": datetime.fromtimestamp(
record.created, tz=timezone.utc
).isoformat(),
"level": record.levelname,
"logger": record.name,
"message": record.getMessage(),
"module": record.module,
"function": record.funcName,
"line": record.lineno,
}
# 异常信息
if record.exc_info and record.exc_info[1]:
log_entry["exception"] = self.formatException(record.exc_info)
# 上下文字段(如 request_id / user_id
for key in ("request_id", "user_id", "workspace_id", "client_ip", "method", "path", "status_code", "duration_ms"):
val = getattr(record, key, None)
if val is not None:
log_entry[key] = val
return json.dumps(log_entry, ensure_ascii=False, default=str)
def setup_json_logging() -> None:
"""为 root logger 添加 JSON 格式的 RotatingFileHandler。
日志写入 LOG_DIR/app.json.log大小达到 LOG_MAX_BYTES 时轮转
"""
log_dir = Path(settings.LOG_DIR)
log_dir.mkdir(parents=True, exist_ok=True)
json_log_path = log_dir / "app.json.log"
# 避免重复添加uvicorn reload 时会重新执行 startup
root = logging.getLogger()
for h in root.handlers:
if isinstance(h, RotatingFileHandler) and str(json_log_path) in getattr(h, 'baseFilename', ''):
return
handler = RotatingFileHandler(
json_log_path,
maxBytes=settings.LOG_MAX_BYTES,
backupCount=settings.LOG_BACKUP_COUNT,
encoding="utf-8",
)
handler.setFormatter(JsonFormatter())
handler.setLevel(getattr(logging, settings.LOG_LEVEL.upper(), logging.INFO))
root.addHandler(handler)
logging.getLogger(__name__).info("JSON 日志已启用 → %s", json_log_path)