一、笔记概述
本文是对自定义日志捕获处理器 LogCapture 类的详细解析笔记,该类基于 Python 内置 logging 和 io 模块,核心功能是将指定日志器的 INFO 级别及以上日志,重定向到内存中捕获(而非打印到控制台/文件),适用于日志验证、单元测试、日志二次处理等场景。
二、核心依赖
代码依赖 Python 内置模块,使用前需提前导入,否则会报 NameError:
import logging # 核心日志功能依赖import io # 内存字符串流(日志存储载体)依赖
三、类结构与核心方法解析
3.1 初始化方法 init(核心逻辑)
完成日志捕获的全部配置:创建存储载体、配置处理器、绑定日志器,逐行解析如下:
def __init__(self):# 1. 创建内存字符串流,作为日志存储容器(替代控制台/文件)self.log_capture_string = io.StringIO()# 2. 创建流处理器,将日志输出定向到内存字符串流(核心重定向)self.log_handler = logging.StreamHandler(self.log_capture_string)# 3. 处理器级别:仅处理 INFO 及以上级别(INFO/WARNING/ERROR/CRITICAL)self.log_handler.setLevel(logging.INFO)# 4. 定义日志格式:时间-日志器名称-级别-消息(规范日志结构)self.log_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')self.log_handler.setFormatter(self.log_formatter) # 绑定格式# 5. 绑定自定义日志器(qwen_agent_logger),设置级别并添加处理器self.logger = logging.getLogger('qwen_agent_logger')self.logger.setLevel(logging.INFO)self.logger.addHandler(self.log_handler)# 6. 绑定根日志器(全局默认日志器),捕获全局日志self.root_logger = logging.getLogger() # 无参数获取根日志器self.root_logger.setLevel(logging.INFO)self.root_logger.addHandler(self.log_handler)
关键原理:logging.StreamHandler 默认输出到控制台,传入 io.StringIO 对象后,实现日志的内存级重定向,这是捕获日志的核心。
3.2 get_log 方法(获取日志)
def get_log(self):return self.log_capture_string.getvalue()
功能:读取内存中存储的所有日志,返回字符串格式;
特点:仅读取、不清空,多次调用返回相同结果。
3.3 clear_log 方法(清空日志)
def clear_log(self):self.log_capture_string.truncate(0) # 截断流,清空内容self.log_capture_string.seek(0) # 指针移至开头,避免写入错位
注意:必须同时调用 truncate(0) 和seek(0),否则后续写入会出现空字符占位问题。
四、核心设计逻辑
双层日志捕获:同时绑定「自定义日志器 + 根日志器」,确保业务日志和全局日志都能被捕获;
级别双过滤:日志器和处理器均设置为 INFO 级别,只有同时高于两者级别的日志才会被捕获;
高效无开销:基于
io.StringIO内存操作,无文件IO开销,适合高频捕获场景;格式标准化:固定日志格式,便于后续解析、筛选日志内容。
五、关键注意事项(避坑重点)
5.1 避免重复添加处理器
多次创建 LogCapture 实例,会重复给日志器添加处理器,导致一条日志被多次捕获。
解决方案:初始化时清空日志器原有处理器:
# 绑定日志器前添加清空操作self.logger = logging.getLogger('qwen_agent_logger')self.logger.handlers.clear() # 清空自定义日志器原有处理器self.root_logger = logging.getLogger()self.root_logger.handlers.clear() # 清空根日志器原有处理器
5.2 日志级别过滤规则
日志器级别 ≤ 处理器级别,处理器的级别过滤才会生效;若日志器级别高于处理器,会先过滤掉低级别日志(例:日志器设为 WARNING,处理器设为 INFO,无法捕获 INFO 日志)。
5.3 根日志器的全局影响
根日志器是全局共享的,修改其级别和处理器会影响整个项目的日志输出(包括第三方库日志)。若只需捕获业务日志,建议仅绑定 qwen_agent_logger,不要修改根日志器。
5.4 StringIO 生命周期
LogCapture 实例销毁时,内部 StringIO 也会被回收,需通过 get_log() 及时保存日志(如写入文件),避免丢失。
六、简单使用示例
import loggingimport ioclass LogCapture:# 上述完整类代码...# 1. 创建日志捕获实例log_capture = LogCapture()# 2. 输出测试日志log_capture.logger.info('qwen_agent_logger 的 INFO 日志') # 自定义日志器logging.info('根日志器的 INFO 日志') # 根日志器logging.warning('根日志器的 WARNING 日志') # 高于 INFO 级别(会捕获)logging.debug('DEBUG 日志(会被过滤)') # 低于 INFO 级别(不捕获)# 3. 获取日志print("捕获的日志:")print(log_capture.get_log())# 4. 清空日志log_capture.clear_log()print("清空后日志:", log_capture.get_log()) # 输出空字符串# 5. 捕获新日志log_capture.logger.error('新的 ERROR 日志')print("新捕获日志:", log_capture.get_log())
七、总结
该类是轻量级、高性能的日志捕获工具,无外部依赖,核心是日志的内存重定向;
核心价值:脱离控制台/文件捕获日志,适合日志验证、单元测试等场景;
避坑关键:避免重复添加处理器、合理设置日志级别、谨慎修改根日志器。
(注:文档部分内容可能由 AI 生成)
