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")