自动化日志封装
最后编辑于 2025 年 4 月 19 日
# _*_ coding: utf-8 _*_
"""
日志模块封装说明:
1. 支持控制台彩色日志和文件日志分离
2. 支持日志文件按大小滚动备份
3. 支持第三方库日志级别控制
4. 支持异步日志处理(可选)
5. 通过单例模式确保配置只初始化一次
"""
import os
import logging
import colorlog # 彩色日志支持
from logging.handlers import RotatingFileHandler, QueueHandler, QueueListener
import queue # 异步日志队列支持
# ======================== 配置类(集中管理所有日志参数) ========================
class LogConfig:
"""
日志模块配置类,通过修改以下参数即可全局生效
注意:路径配置建议使用绝对路径,确保跨平台兼容性
"""
# 日志存储目录(默认在项目根目录下的log文件夹)
LOG_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "log")
# 主日志文件名(固定名称方便轮换)
LOG_FILE = os.path.join(LOG_DIR, "request.log")
# 单个日志文件最大大小(5MB)
MAX_BYTES = 5 * 1024 * 1024
# 最大保留备份文件数量
BACKUP_COUNT = 7
# 控制台输出日志级别(INFO及以上)
CONSOLE_LEVEL = logging.DEBUG
# 文件记录日志级别(DEBUG及以上)
FILE_LEVEL = logging.DEBUG
# 第三方库的日志记录级别(WARNING及以上)
THIRD_PARTY_LEVEL = logging.WARNING
# 需要特别处理的第三方库列表
THIRD_PARTY_LIBS = ['selenium', 'urllib3', 'allure']
# 是否启用异步日志处理(生产环境建议开启)
ASYNC_LOGGING = False
# ================================ 过滤器(控制第三方库日志输出) ======================================
class ThirdPartyFilter(logging.Filter):
"""
第三方库日志过滤器
功能:根据配置过滤第三方库的低级别日志
"""
def filter(self, record):
"""
过滤逻辑:
1. 当记录来自配置的第三方库时
2. 仅允许 >= 配置级别(WARNING)的日志通过
3. 其他日志不做过滤
"""
if record.name in LogConfig.THIRD_PARTY_LIBS:
return record.levelno >= LogConfig.THIRD_PARTY_LEVEL
return True
# ============================= 日志核心模块(单例模式保证只初始化一次) ==================================
class RecordLog:
"""
日志系统核心类
特性:
- 单例模式保证配置只加载一次
- 自动创建日志目录
- 分离控制台和文件处理器
- 支持异步日志处理
"""
_initialized = False # 单例模式标记
_log_queue = queue.Queue(-1) if LogConfig.ASYNC_LOGGING else None # 异步队列(无限容量)
@classmethod
def init_logging(cls):
"""
初始化日志系统(程序入口调用一次即可)
执行流程:
1. 检查是否已初始化
2. 创建日志目录
3. 配置根记录器
4. 添加控制台和文件处理器
5. 配置第三方库日志
"""
if cls._initialized:
return
cls._initialized = True
# ------------------------ 目录准备 ------------------------
# 递归创建日志目录(exist_ok=True防止目录已存在时报错)
os.makedirs(LogConfig.LOG_DIR, exist_ok=True)
# ------------------------ 根记录器配置 ------------------------
logger = logging.getLogger()
logger.handlers.clear() # 清除已有处理器
logger.setLevel(logging.DEBUG) # 根记录器设置为最低级别
# ------------------------ 控制台处理器 ------------------------
# 创建带颜色的控制台格式器
console_formatter = cls._color_formatter()
# 创建控制台处理器并配置
console_handler = logging.StreamHandler()
console_handler.setFormatter(console_formatter)
console_handler.setLevel(LogConfig.CONSOLE_LEVEL)
console_handler.addFilter(ThirdPartyFilter()) # 添加第三方库过滤器
# ------------------------ 文件处理器 ------------------------
# 创建文件格式器
file_formatter = cls._file_formatter()
# 创建按大小滚动的文件处理器
file_handler = RotatingFileHandler(
filename=LogConfig.LOG_FILE,
maxBytes=LogConfig.MAX_BYTES,
backupCount=LogConfig.BACKUP_COUNT,
encoding='utf-8'
)
file_handler.setFormatter(file_formatter)
file_handler.setLevel(LogConfig.FILE_LEVEL)
# ------------------------ 异步日志处理 ------------------------
if LogConfig.ASYNC_LOGGING:
"""
异步日志处理逻辑:
1. 使用QueueHandler将日志放入队列
2. 使用QueueListener从队列取出日志并处理
优点:避免I/O操作阻塞主线程
"""
# 创建异步处理器
async_console = QueueHandler(cls._log_queue)
async_file = QueueHandler(cls._log_queue)
# 绑定实际处理器
async_console.addHandler(console_handler)
async_file.addHandler(file_handler)
# 添加异步处理器到根记录器
logger.addHandler(async_console)
logger.addHandler(async_file)
# 启动异步监听器
QueueListener(cls._log_queue, console_handler, file_handler).start()
else:
# 直接添加处理器
logger.addHandler(console_handler)
logger.addHandler(file_handler)
# ------------------------ 第三方库配置 ------------------------
# 遍历配置的第三方库,设置日志级别并禁止传播
for lib in LogConfig.THIRD_PARTY_LIBS:
lib_logger = logging.getLogger(lib)
lib_logger.setLevel(LogConfig.THIRD_PARTY_LEVEL)
lib_logger.propagate = False # 重要!阻止日志传播到根记录器
# 禁止根记录器传播(避免重复记录)
logger.propagate = False
@classmethod
def _color_formatter(cls):
"""创建控制台彩色日志格式器"""
return colorlog.ColoredFormatter(
# 格式说明:
# %(log_color)s 开始颜色
# %(levelname)-8s 左对齐的级别名称(8字符宽度)
# %(reset)s 重置颜色
# %(cyan)s 文件名颜色
# [%(asctime)s] 格式化的时间戳
# %(filename)s:%(lineno)d 文件名和行号
# - %(message)s 日志内容
"%(log_color)s%(levelname)-5s [%(asctime)s] %(module)s:%(lineno)d - %(message)s%(reset)s",
datefmt="%Y-%m-%d %H:%M:%S", # 时间格式
log_colors={
'DEBUG': 'cyan', # DEBUG级别颜色
'INFO': 'green', # INFO级别颜色
'WARNING': 'yellow', # WARNING级别颜色
'ERROR': 'red', # ERROR级别颜色
'CRITICAL': 'red,bg_white' # CRITICAL级别颜色(白底红字)
}
)
"""
"%(log_color)s%(levelname)-5s [%(asctime)s] %(module)s:%(lineno)d - %(message)s%(reset)s"
"%(log_color)s%(levelname)-8s [%(asctime)s] %(filename)s:%(lineno)d - %(message)s%(reset)s"
"""
@classmethod
def _file_formatter(cls):
"""创建文件日志格式器(无颜色)"""
return logging.Formatter(
# 格式说明:
# %(levelname)-8s 左对齐的级别名称
# [%(asctime)s] 时间戳
# %(filename)s:%(lineno)d 文件名和行号
# - %(message)s 日志内容
"%(levelname)-5s [%(asctime)s] %(module)s:%(lineno)d - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
"""
"%(levelname)-8s [%(asctime)s] %(filename)s:%(lineno)d - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
"""
# ======================== 使用示例 ========================
# if __name__ == "__main__":
# """
# 示例使用流程:
# 1. 初始化日志系统
# 2. 获取当前模块的日志记录器
# 3. 输出不同级别的日志
# """
# 初始化日志(程序启动时调用一次)
# RecordLog.init_logging()
# 获取当前模块的日志记录器
# logger = logging.getLogger(__name__)
# 测试日志输出
# logger.debug("Debug信息(文件可见,控制台不可见)")
# logger.info("Info信息(控制台可见)")
# logger.warning("Warning信息")
# logger.error("Error信息")
# logger.critical("Critical严重信息")
# 测试第三方库日志过滤(需要真实场景验证)
# selenium_logger = logging.getLogger('selenium')
# selenium_logger.debug("Selenium Debug信息(应被过滤)")
# selenium_logger.warning("Selenium Warning信息(应被记录)")