从Python的日志记录器中移除处理器

90 投票
5 回答
118380 浏览
提问于 2025-04-17 02:40

我在玩Python的日志系统时,发现了一个奇怪的现象。在一个循环中从Logger对象中移除处理器时,发现我的循环只移除了所有处理器中的一个,最后一个处理器在调用.removeHandler时可以顺利移除。在这个过程中没有出现任何错误信息。

这是我的测试代码:

import logging
import sys
logging.basicConfig()
dbg = logging.getLogger('dbg')
dbg.setLevel(logging.DEBUG)

testLogger = logging.getLogger('mylogger')
sh = logging.StreamHandler(sys.stdout)
fh = logging.FileHandler('mylogfile.log')
dbg.debug('before adding handlers: %d handlers'%len(testLogger.handlers))
testLogger.addHandler(fh)
testLogger.addHandler(sh)

dbg.debug('before removing. %d handlers: %s'%(len(testLogger.handlers), 
                                              str(testLogger.handlers)))
for h in testLogger.handlers:
    dbg.debug('removing handler %s'%str(h))
    testLogger.removeHandler(h)
    dbg.debug('%d more to go'%len(testLogger.handlers))

#HERE I EXPECT THAT NO HANDLER WILL REMAIN    
dbg.debug('after removing: %d handlers: %s'%(len(testLogger.handlers), 
                                              str(testLogger.handlers)))
if len(testLogger.handlers) > 0:
    #Why is this happening?
    testLogger.removeHandler(testLogger.handlers[0])
dbg.debug('after manually removing the last handler: %d handlers'%len(testLogger.handlers))    

我本来期望在循环结束时,testLogger对象里没有任何处理器,但最后一次调用.removeHandler似乎没有成功,从下面的输出可以看出来。不过,额外再调用一次这个函数时,处理器就如预期一样被移除了。以下是输出结果:

DEBUG:dbg:before adding handlers: 0 handlers
DEBUG:dbg:before removing. 2 handlers: [<logging.FileHandler instance at 0x021263F0>, <logging.StreamHandler instance at 0x021262B0>]
DEBUG:dbg:removing handler <logging.FileHandler instance at 0x021263F0>
DEBUG:dbg:1 more to go
DEBUG:dbg:after removing: 1 handlers: [<logging.StreamHandler instance at 0x021262B0>]
DEBUG:dbg:after manually removing the last handler: 0 handlers

更有趣的是,如果我把原来的循环替换成下面这个,循环就能正常工作,循环结束时testLogger对象里不会剩下任何处理器。这里是修改后的循环:

while len(testLogger.handlers) > 0:
    h = testLogger.handlers[0]
    dbg.debug('removing handler %s'%str(h))
    testLogger.removeHandler(h)
    dbg.debug('%d more to go'%len(testLogger.handlers))

这是什么原因造成的呢?这是个bug,还是我漏掉了什么?

5 个回答

18

如果你不想把它们全部删除(感谢这个建议@CatPlusPlus):

testLogger.handlers = [
    h for h in testLogger.handlers if not isinstance(h, logging.StreamHandler)]
24

不要直接修改那些没有文档说明的 .handler:

选项 1

logging.getLogger().removeHandler(logging.getLogger().handlers[0])

这样你可以通过官方的方式准确地移除已经存在的处理程序对象。或者,你也可以移除所有的处理程序:

logger = logging.getLogger()
while logger.hasHandlers():
    logger.removeHandler(logger.handlers[0])

选项 2

logging.config.dictConfig(config={'level': logging.DEBUG, 'handlers': []})

这个方法不仅可以移除处理程序,还能防止它的创建。这样列表的根部就会有 [] 这个空的处理程序。

162

这不是特定于日志记录器的行为。绝对不要在你正在遍历的列表上进行修改(比如插入或删除元素)。如果需要修改,最好先复制一份。在这种情况下,使用 testLogger.handlers.clear() 就可以解决问题。

撰写回答