201 lines
5.1 KiB
Python
201 lines
5.1 KiB
Python
# -*- 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)
|
||
|
||
# 关键修复:禁止传播到父记录器,避免重复输出
|
||
logger.propagate = False
|
||
|
||
# 避免重复添加处理器
|
||
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
|
||
)
|