128 lines
3.6 KiB
Python
128 lines
3.6 KiB
Python
"""
|
||
日志工具模块
|
||
使用Python标准库logging,兼容Python 3.8.6
|
||
"""
|
||
import logging
|
||
import sys
|
||
from pathlib import Path
|
||
from datetime import datetime
|
||
from config.settings import LOG_LEVEL, LOG_FORMAT, LOG_DIR
|
||
|
||
|
||
def get_log_filename(base_name: str = 'crawler') -> Path:
|
||
"""
|
||
根据当前月份生成日志文件名
|
||
|
||
Args:
|
||
base_name: 日志文件基础名称
|
||
|
||
Returns:
|
||
日志文件路径,格式为: base_name-yyyy-mm.log
|
||
"""
|
||
current_date = datetime.now()
|
||
filename = f"{base_name}-{current_date.strftime('%Y-%m')}.log"
|
||
return LOG_DIR / filename
|
||
|
||
|
||
class MonthlyRotatingFileHandler(logging.FileHandler):
|
||
"""
|
||
按月轮转的日志处理器
|
||
文件名格式为: base_name-yyyy-mm.log
|
||
当月份变化时,自动切换到新的日志文件
|
||
"""
|
||
|
||
def __init__(self, base_name: str = 'crawler', encoding: str = 'utf-8', delay: bool = False):
|
||
"""
|
||
初始化按月轮转的日志处理器
|
||
|
||
Args:
|
||
base_name: 日志文件基础名称
|
||
encoding: 文件编码
|
||
delay: 是否延迟打开文件
|
||
"""
|
||
self.base_name = base_name
|
||
self.current_month = None
|
||
self.log_dir = LOG_DIR
|
||
self.log_dir.mkdir(parents=True, exist_ok=True)
|
||
|
||
# 获取当前月份的日志文件
|
||
log_file = get_log_filename(base_name)
|
||
self.current_month = datetime.now().strftime('%Y-%m')
|
||
|
||
super().__init__(str(log_file), mode='a', encoding=encoding, delay=delay)
|
||
|
||
def emit(self, record):
|
||
"""
|
||
发送日志记录
|
||
在发送前检查月份是否变化,如果变化则切换到新的日志文件
|
||
"""
|
||
current_month = datetime.now().strftime('%Y-%m')
|
||
|
||
# 如果月份变化,切换到新的日志文件
|
||
if current_month != self.current_month:
|
||
self.do_rollover()
|
||
self.current_month = current_month
|
||
|
||
super().emit(record)
|
||
|
||
def do_rollover(self):
|
||
"""
|
||
执行日志轮转
|
||
关闭当前文件,打开新月份的日志文件
|
||
"""
|
||
if self.stream:
|
||
self.stream.close()
|
||
self.stream = None
|
||
|
||
# 生成新月份的日志文件名
|
||
new_log_file = get_log_filename(self.base_name)
|
||
self.baseFilename = str(new_log_file)
|
||
|
||
# 打开新的日志文件
|
||
if not self.delay:
|
||
self.stream = self._open()
|
||
|
||
|
||
def setup_logger(name: str = 'crawler', level: str = LOG_LEVEL) -> logging.Logger:
|
||
"""
|
||
配置并返回logger实例
|
||
|
||
Args:
|
||
name: logger名称
|
||
level: 日志级别
|
||
|
||
Returns:
|
||
logging.Logger实例
|
||
"""
|
||
logger = logging.getLogger(name)
|
||
logger.setLevel(getattr(logging, level.upper(), logging.INFO))
|
||
|
||
# 避免重复添加handler
|
||
if logger.handlers:
|
||
return logger
|
||
|
||
# 控制台输出
|
||
console_handler = logging.StreamHandler(sys.stdout)
|
||
console_handler.setLevel(getattr(logging, level.upper(), logging.INFO))
|
||
console_formatter = logging.Formatter(LOG_FORMAT)
|
||
console_handler.setFormatter(console_formatter)
|
||
logger.addHandler(console_handler)
|
||
|
||
# 文件输出 - 按月轮转,文件名格式为: crawler-yyyy-mm.log
|
||
file_handler = MonthlyRotatingFileHandler(
|
||
base_name=name,
|
||
encoding='utf-8',
|
||
delay=False
|
||
)
|
||
file_handler.setLevel(logging.INFO)
|
||
file_formatter = logging.Formatter(LOG_FORMAT)
|
||
file_handler.setFormatter(file_formatter)
|
||
logger.addHandler(file_handler)
|
||
|
||
return logger
|
||
|
||
|
||
# 创建默认logger
|
||
logger = setup_logger()
|
||
|