Files
ai-talk-callback/app/logger.py
2025-12-11 16:18:38 +08:00

187 lines
6.3 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 logging.handlers
from pathlib import Path
from typing import Optional
from app.config import settings
class LoggerManager:
"""日志管理器"""
_loggers = {}
_initialized = False
@classmethod
def setup_logging(cls):
"""设置日志系统"""
if cls._initialized:
return
# 创建日志目录
log_dir = Path(settings.log_file).parent
log_dir.mkdir(parents=True, exist_ok=True)
# 配置根日志器
root_logger = logging.getLogger()
root_logger.setLevel(getattr(logging, settings.log_level.upper()))
# 清除现有处理器
root_logger.handlers.clear()
# 创建控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(getattr(logging, settings.log_level.upper()))
# 控制台使用普通格式化器
console_formatter = logging.Formatter(
fmt=settings.log_format,
datefmt=settings.log_date_format
)
console_handler.setFormatter(console_formatter)
# 创建文件处理器(按时间轮转)
file_handler = logging.handlers.TimedRotatingFileHandler(
filename=settings.log_file,
when='midnight', # 每天午夜轮转
interval=1, # 每天一次
backupCount=settings.log_backup_count,
encoding='utf-8',
)
# 创建后设置 suffix 属性
file_handler.suffix = "%Y%m%d" # 设置文件后缀格式,如 app.log.20231215
file_handler.setLevel(settings.log_level) # 文件记录所有级别
# 文件使用普通格式化器
file_formatter = logging.Formatter(
fmt=settings.log_format,
datefmt=settings.log_date_format
)
file_handler.setFormatter(file_formatter)
# 添加处理器到根日志器
root_logger.addHandler(console_handler)
root_logger.addHandler(file_handler)
# 设置第三方库的日志级别
logging.getLogger("uvicorn").setLevel(logging.INFO)
logging.getLogger("uvicorn.access").setLevel(logging.WARNING)
logging.getLogger("sqlalchemy.engine").setLevel(logging.WARNING)
logging.getLogger("httpx").setLevel(logging.WARNING)
logging.getLogger("watchfiles").setLevel(logging.WARNING)
cls._initialized = True
# 记录日志系统启动
logger = cls.get_logger("LoggerManager")
logger.info("🚀 日志系统初始化完成")
logger.info(f"📁 日志文件: {settings.log_file}")
logger.info(f"📊 日志级别: {settings.log_level}")
@classmethod
def get_logger(cls, name: str) -> logging.Logger:
"""获取指定名称的日志器"""
if not cls._initialized:
cls.setup_logging()
if name not in cls._loggers:
cls._loggers[name] = logging.getLogger(name)
return cls._loggers[name]
def get_logger(name: Optional[str] = None) -> logging.Logger:
"""获取日志器的便捷函数"""
if name is None:
# 自动获取调用者的模块名
import inspect
frame = inspect.currentframe().f_back
name = frame.f_globals.get('__name__', 'unknown')
return LoggerManager.get_logger(name)
# 预定义的日志器
app_logger = get_logger("app")
api_logger = get_logger("api")
db_logger = get_logger("database")
external_api_logger = get_logger("external_api")
# 各模块专用日志器
main_logger = get_logger("main")
routes_logger = get_logger("routes")
celery_logger = get_logger("celery")
celery_tasks_logger = get_logger("celery_tasks")
callback_service_logger = get_logger("callback_service")
redis_logger = get_logger("redis")
# 通用文件日志器设置函数
def setup_celery_file_logger(logger_name: str, log_file: str, level: str = "INFO"):
"""为Celery组件设置专用的文件日志器,同时保持控制台输出"""
# 创建日志目录
log_dir = Path(log_file).parent
log_dir.mkdir(parents=True, exist_ok=True)
# 获取日志器
logger = get_logger(logger_name)
logger.setLevel(getattr(logging, level.upper()))
# 清除现有文件处理器(避免重复添加)
for handler in logger.handlers[:]:
if isinstance(handler, logging.handlers.TimedRotatingFileHandler):
logger.removeHandler(handler)
# 创建文件处理器
file_handler = logging.handlers.TimedRotatingFileHandler(
filename=log_file,
when='midnight',
interval=1,
backupCount=7,
encoding='utf-8',
)
file_handler.suffix = "%Y%m%d"
file_handler.setLevel(getattr(logging, level.upper()))
# 格式化器
formatter = logging.Formatter(
fmt='%(asctime)s - %(name)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
file_handler.setFormatter(formatter)
# 添加文件处理器
logger.addHandler(file_handler)
logger.propagate = True
return logger
# 各模块专用日志器映射
CELERY_LOGGERS = {
"celery_tasks": ("logs/celery_tasks.log", "INFO"),
"celery": ("logs/celery_app.log", "INFO"),
"celery.beat": ("logs/celery_beat.log", "WARNING"),
"celery.worker": ("logs/celery_worker.log", "WARNING"),
}
# 初始化所有Celery文件日志器
_celery_file_loggers = {}
for logger_name, (log_file, level) in CELERY_LOGGERS.items():
_celery_file_loggers[logger_name] = setup_celery_file_logger(logger_name, log_file, level)
# 向后兼容的默认日志器
logger = app_logger
# 通用获取器函数
def get_celery_file_logger(logger_name: str):
"""获取指定名称的Celery文件日志器"""
return _celery_file_loggers.get(logger_name, get_logger(logger_name))
# 简化的便捷函数
def get_main_logger(): return main_logger
def get_routes_logger(): return routes_logger
def get_celery_logger(): return get_celery_file_logger("celery")
def get_celery_tasks_logger(): return get_celery_file_logger("celery_tasks")
def get_callback_service_logger(): return callback_service_logger
def get_redis_logger(): return redis_logger
def get_celery_beat_logger(): return get_celery_file_logger("celery.beat")
def get_celery_worker_logger(): return get_celery_file_logger("celery.worker")