"""
日志配置模块
使用 Loguru 作为日志框架,提供统一的日志配置和管理。
支持日志文件轮转:按大小(30MB)和日期切割。
Loguru 的优势:
- 简单易用,无需复杂配置
- 自动格式化,支持彩色输出
- 内置日志轮转功能
- 性能优秀
- 支持结构化日志
"""
import sys
from pathlib import Path
from typing import Optional
from loguru import logger
def setup_logger(
log_level: Optional[str] = None,
log_dir: Optional[Path] = None,
max_file_size: Optional[str] = None,
retention_days: Optional[int] = None,
enable_console: Optional[bool] = None,
) -> None:
"""
配置和初始化 Loguru 日志系统
这个函数会:
1. 移除默认的日志处理器
2. 添加控制台输出(可选)
3. 添加文件输出,支持自动轮转
Args:
log_level: 日志级别(DEBUG, INFO, WARNING, ERROR, CRITICAL)
如果为 None,则从配置文件读取
log_dir: 日志文件存储目录,默认为项目根目录下的 logs 文件夹
max_file_size: 单个日志文件的最大大小,达到后会自动切割
支持格式:30 MB, 100 KB, 1 GB 等
retention_days: 日志文件保留天数,超过此天数的日志文件会被自动删除
enable_console: 是否启用控制台输出
Example:
>>> setup_logger()
>>> logger.info("这是一条日志")
"""
# 延迟导入配置,避免循环依赖
from core.config import settings
# 从配置读取日志级别,如果没有指定
if log_level is None:
log_level = settings.logging_level.upper()
# 从配置读取文件大小限制
if max_file_size is None:
max_file_size = settings.logging_max_file_size
# 从配置读取保留天数
if retention_days is None:
retention_days = settings.logging_retention_days
# 从配置读取是否启用控制台输出
if enable_console is None:
enable_console = settings.logging_enable_console
# 确定日志目录
if log_dir is None:
# 从配置读取日志目录,默认为项目根目录下的 logs 文件夹
log_dir_name = settings.logging_dir
# 如果是绝对路径,直接使用
if Path(log_dir_name).is_absolute():
log_dir = Path(log_dir_name)
else:
# 相对路径:使用项目根目录
# __file__ 是 backend/logger/logging.py,需要向上两级到项目根目录
project_root = Path(__file__).parent.parent
log_dir = project_root / log_dir_name
else:
log_dir = Path(log_dir)
# 确保日志目录存在
# 如果 log_dir 是一个文件,先删除它
if log_dir.exists() and log_dir.is_file():
logger.warning(f"日志路径 {log_dir} 是一个文件,将其重命名为 {log_dir}_backup")
log_dir.rename(log_dir.parent / f"{log_dir.name}_backup")
log_dir.mkdir(parents=True, exist_ok=True)
# 移除 Loguru 的默认处理器
# Loguru 默认会输出到 stderr,我们需要移除它以便自定义配置
logger.remove()
# 配置日志格式
log_format = (
"{time:YYYY-MM-DD HH:mm:ss.SSS} | "
"{level: <8} | "
"{name}:{function}:{line} | "
"{message}"
)
# 添加控制台输出(可选)
if enable_console:
logger.add(
sys.stderr,
format=log_format,
level=log_level,
colorize=True,
backtrace=True,
diagnose=True,
)
# 添加文件输出 - 所有级别的日志
logger.add(
log_dir / "huoyan_{time:YYYY-MM-DD}.log",
format=log_format,
level=log_level,
rotation="00:00",
retention=f"{retention_days} days",
compression="zip",
encoding="utf-8",
backtrace=True,
diagnose=True,
enqueue=True,
)
# 单独记录错误日志
logger.add(
log_dir / "huoyan_error_{time:YYYY-MM-DD}.log",
format=log_format,
level="ERROR",
rotation="00:00",
retention=f"{retention_days} days",
compression="zip",
encoding="utf-8",
backtrace=True,
diagnose=True,
enqueue=True,
)
# 记录配置信息
logger.info(f"日志系统已初始化")
logger.info(f"日志级别: {log_level}")
logger.info(f"日志目录: {log_dir}")
logger.info(f"文件大小限制: {max_file_size}")
logger.info(f"日志保留天数: {retention_days} 天")
def get_logger(name: Optional[str] = None):
"""
获取日志记录器实例
Args:
name: 日志记录器的名称,通常是 __name__(模块名)
Returns:
Loguru 日志记录器实例
"""
if name:
return logger.bind(name=name)
return logger
__all__ = ["logger", "setup_logger", "get_logger"]