Python logging MemoryHandler内存泄漏问题排查求助
内存泄漏排查:MemoryHandler导致RES内存持续增长
代码片段
main.py
import time from v1 import log, ServiceLogger from logging.handlers import MemoryHandler class Service: h = MemoryHandler(1000) id = None log = None def __init__(self, id: int): self.id = id self.log = ServiceLogger(log) log.addHandler(self.h) def do(self): print("Do some job per service!") def service_exec(): service = Service(id=4) service.do() if __name__ == '__main__': while True: for i in range(10): service_exec() time.sleep(1)
v1.py
import logging log = logging.getLogger('.'.join(__name__.split('.')[:-1])) class ServiceLogger(logging.LoggerAdapter): def log(self, level, msg, *args, **kwargs): print("Custom logger hello!")
问题描述
程序运行15-30分钟后,top命令中的RES内存指标持续无上限增长。移除log.addHandler(self.h)语句后,内存增长问题消失。
更新:将MemoryHandler移至全局变量空间并添加到日志器后,内存仍持续增长。
问题原因
- 重复添加Handler:日志器
log是全局单例对象,原代码中每次创建Service实例都会调用log.addHandler(self.h),导致日志器中累积了大量MemoryHandler实例,每个实例都会占用内存缓存日志。 - MemoryHandler未配置目标Handler:
MemoryHandler的核心逻辑是缓存日志记录,达到容量阈值后转发给目标Handler并清空缓存。但你的代码中未指定target参数,缓存的日志永远不会被转发或清理,只会不断累积占用内存。 - 全局Handler的缓存累积:即使将
MemoryHandler移到全局,只要没有正确配置目标Handler或触发刷新逻辑,缓存的日志记录会持续堆积,最终导致内存泄漏。
解决方法
1. 为MemoryHandler指定目标Handler
创建MemoryHandler时必须传入target参数,让它在达到缓存阈值时自动转发日志并清空缓存。示例如下:
# 全局初始化目标Handler(比如文件Handler) import logging from logging.handlers import MemoryHandler file_handler = logging.FileHandler('service.log') formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') file_handler.setFormatter(formatter) # 初始化MemoryHandler并绑定目标 global_memory_handler = MemoryHandler(capacity=1000, target=file_handler) log.addHandler(global_memory_handler)
2. 避免重复添加Handler
确保MemoryHandler仅被添加到日志器一次,删除Service构造函数中的log.addHandler(self.h)语句,改为在全局初始化时完成Handler的添加。
3. 手动触发缓存刷新(可选)
如果需要在特定时机清理缓存,可以调用MemoryHandler.flush()方法,比如在每次任务执行完成后:
def service_exec(): service = Service(id=4) service.do() global_memory_handler.flush() # 手动刷新缓存,立即转发日志
4. 程序退出时清理资源
在程序终止前调用close()方法,确保缓存中剩余的日志被转发,同时释放Handler占用的资源:
if __name__ == '__main__': try: while True: for i in range(10): service_exec() time.sleep(1) except KeyboardInterrupt: global_memory_handler.close() file_handler.close()
内容的提问来源于stack exchange,提问作者Denis Prot
相关产品推荐
相关产品推荐

