# -*- coding: utf-8 -*- """ 日志配置模块 提供灵活的日志系统配置功能 """ import logging import os import sys from logging.handlers import TimedRotatingFileHandler from datetime import datetime # 默认日志配置 DEFAULT_LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' DEFAULT_DATE_FORMAT = '%Y-%m-%d %H:%M:%S' DEFAULT_LOG_LEVEL = logging.INFO # 全局日志记录器缓存 _loggers = {} def setup_logger( name='backtrader', log_file=None, level=DEFAULT_LOG_LEVEL, log_format=DEFAULT_LOG_FORMAT, date_format=DEFAULT_DATE_FORMAT, console=True, file_level=None, log_dir='logs', backup_count=7 ): """ 配置日志记录器 Args: name: 日志记录器名称 log_file: 日志文件路径,如果为None则自动生成 level: 日志级别 log_format: 日志格式 date_format: 日期格式 console: 是否输出到控制台 file_level: 文件日志级别,如果为None则使用level log_dir: 日志文件目录 backup_count: 日志文件保留数量 Returns: logging.Logger: 配置好的日志记录器 """ # 如果日志记录器已存在,则直接返回 if name in _loggers: return _loggers[name] # 创建日志记录器 logger = logging.getLogger(name) logger.setLevel(level) # 避免重复添加处理器 if logger.handlers: logger.handlers.clear() # 创建格式化器 formatter = logging.Formatter(log_format, datefmt=date_format) # 添加控制台处理器 if console: console_handler = logging.StreamHandler(sys.stdout) console_handler.setLevel(level) console_handler.setFormatter(formatter) logger.addHandler(console_handler) # 添加文件处理器 if log_file or log_dir: # 确保日志目录存在 if log_dir and not os.path.exists(log_dir): try: os.makedirs(log_dir) except Exception as e: logger.error(f'创建日志目录失败: {str(e)}') # 生成日志文件名 if not log_file: timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') log_file = os.path.join(log_dir, f'{name}_{timestamp}.log') # 使用TimedRotatingFileHandler实现按时间轮换 try: file_handler = TimedRotatingFileHandler( log_file, when='midnight', interval=1, backupCount=backup_count, encoding='utf-8' ) # 设置文件日志级别 file_handler.setLevel(file_level or level) file_handler.setFormatter(formatter) logger.addHandler(file_handler) logger.info(f'日志文件已配置: {log_file}') except Exception as e: logger.error(f'配置日志文件失败: {str(e)}') # 保存到缓存 _loggers[name] = logger return logger def get_logger(name='backtrader'): """ 获取日志记录器 Args: name: 日志记录器名称 Returns: logging.Logger: 日志记录器 """ if name not in _loggers: # 如果日志记录器不存在,则创建默认配置的日志记录器 return setup_logger(name) return _loggers[name] def set_global_level(level): """ 设置所有已创建日志记录器的全局级别 Args: level: 日志级别 """ for logger_name, logger in _loggers.items(): logger.setLevel(level) # 设置所有处理器的级别 for handler in logger.handlers: handler.setLevel(level) def shutdown_loggers(): """ 关闭所有日志记录器 """ logging.shutdown() _loggers.clear() def log_exception(logger_name='backtrader', message='发生异常', exc_info=True): """ 记录异常信息 Args: logger_name: 日志记录器名称 message: 异常消息 exc_info: 是否记录异常堆栈信息 """ logger = get_logger(logger_name) logger.error(message, exc_info=exc_info) def create_specialized_logger( name, log_file=None, level=DEFAULT_LOG_LEVEL, console=True, **kwargs ): """ 创建专门用途的日志记录器 Args: name: 日志记录器名称 log_file: 日志文件路径 level: 日志级别 console: 是否输出到控制台 **kwargs: 其他参数 Returns: logging.Logger: 配置好的日志记录器 """ # 构建专用日志格式 specialized_format = kwargs.get( 'log_format', f'[%(asctime)s] [{name.upper()}] [%(levelname)s] %(message)s' ) return setup_logger( name=name, log_file=log_file, level=level, log_format=specialized_format, console=console, **kwargs )