187 lines
6.3 KiB
Python
187 lines
6.3 KiB
Python
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") |