Python全局变量the_message_lines异常清空问题排查
问题根本原因
这个现象是Python logging配置加载机制带来的命名空间隔离导致的,核心问题有两个:
- 你从未手动将自定义的
MemoryHandler实例添加到logger的处理链中,代码中仅定义了类,没有执行logger.addHandler()的注册逻辑。 - 你在
emit方法中打断点能看到日志被追加,是因为log_utils.load_log_config('logging.ini')读取配置文件时,会根据配置项动态导入、实例化配置中指定的MemoryHandler类。但这个动态加载出来的类,和你当前入口脚本(__main__模块)里写的MemoryHandler不是同一个对象,它操作的the_message_lines是动态导入生成的模块副本里的独立列表,和你在main逻辑中访问的顶层the_message_lines是两块完全不相关的内存空间。
这就直接对应了你观察到的现象:emit方法里操作的是另一个列表,你在主流程里读的自定义列表从始至终没有被写入过任何内容,自然始终为空,程序运行到最后拿不到任何日志。
另外你当前的实现用全局变量存储日志,本身就很容易因为模块导入路径差异出现变量不共享的问题,继承StreamHandler时如果不指定流,默认还会往标准错误流输出内容,不符合内存存储的预期。
修复方法
不要依赖配置文件动态加载入口脚本里定义的自定义Handler,手动实例化后注册到logger即可,修改主逻辑代码:
if __name__ == '__main__': log_utils.load_log_config('logging.ini') logger = log_utils.get_logger() # 实例化自定义内存处理器,设置格式后注册到logger mem_handler = MemoryHandler() mem_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')) logger.addHandler(mem_handler) logger.warning('beginning') for i in range(3): lines = MemoryHandler.get_lines() logger.warning('remaining %d seconds', i, extra={'same_line':True}) logger.warning('end') for line in MemoryHandler.get_lines(): print(line)
优化建议
可以把存储日志的列表直接绑定为MemoryHandler的类属性,替换掉全局变量,彻底规避跨模块导入的变量隔离问题,改造后的类代码如下:
class MemoryHandler(logging.StreamHandler): """ Handler that keeps all log messages in memory until the beginning of the day or the size exceeds a value """ _message_lines = [] def emit(self, record: logging.LogRecord): try: msg = self.format(record) self._message_lines.append(msg) self.flush() except (KeyboardInterrupt, SystemExit): raise except: self.handleError(record) @classmethod def get_lines(cls): return cls._message_lines @classmethod def reset_lines(cls): cls._message_lines.clear()
如果一定要通过logging.ini配置这个Handler,就把MemoryHandler类单独拆到独立的公共模块中,不要写在程序入口脚本里,配置文件中写全类的导入路径即可避免动态加载时生成独立副本的问题。
内容的提问来源于stack exchange,提问作者jordanthompson
相关产品推荐
相关产品推荐

