【问题标题】:Python logging: Why is __init__ called twice?Python 日志记录:为什么 __init__ 被调用两次?
【发布时间】:2011-05-17 14:08:55
【问题描述】:

我正在尝试将 python 日志记录与配置文件和自己的处理程序一起使用。这在某种程度上有效。真正让我困惑的是__init__ 被调用了两次,__del__ 被调用了一次。当我删除整个配置文件并直接在代码中创建处理程序时,__init__ 被调用一次,而__del__ 永远不会被调用。

我的问题:

  1. 为什么__init__ 被调用了两次?
  2. 为什么__del__ 的调用频率低于__init__

代码:

#!/bin/env python

import logging
import logging.handlers
import logging.config

class Test1TimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler):
    def __init__(self,filename):
        print "init called"
        logging.handlers.TimedRotatingFileHandler.__init__(self,filename, when='S', interval=86400, backupCount=8, encoding=None)

    def __del__(self):
        print "del called"
        if hasattr(logging.handlers.TimedRotatingFileHandler,"__del__"):
            logging.handlers.TimedRotatingFileHandler.__del__(self)

logging.config.fileConfig('/root/test1.conf')
logger = logging.getLogger("test1")

配置文件:

[formatters]
keys: simple

[handlers]
keys: file

[loggers]
keys: root

[formatter_simple]
format: "%(message)s"

[handler_file]
class: test1.Test1TimedRotatingFileHandler
args: ("/root/test1.log",)
level=INFO

[logger_root]
level: INFO
handlers: file
qualname: test1

输出如下所示:

init called
init called
del called

按照 Sentinal 的建议使用调试器获取堆栈跟踪揭示了这一点:

第一次通话:

> /root/test1.py(12)__init__()
-> print "init called"
(Pdb) where
  /root/test1.py(21)<module>()
-> logging.config.fileConfig('/root/test1.conf')
  /usr/local/python/2.6.4/lib/python2.6/logging/config.py(84)fileConfig()
-> handlers = _install_handlers(cp, formatters)
  /usr/local/python/2.6.4/lib/python2.6/logging/config.py(156)_install_handlers()
-> klass = _resolve(klass)
  /usr/local/python/2.6.4/lib/python2.6/logging/config.py(94)_resolve()
-> found = __import__(used)
  /root/test1.py(21)<module>()
-> logging.config.fileConfig('/root/test1.conf')
  /usr/local/python/2.6.4/lib/python2.6/logging/config.py(84)fileConfig()
-> handlers = _install_handlers(cp, formatters)
  /usr/local/python/2.6.4/lib/python2.6/logging/config.py(159)_install_handlers()
-> h = klass(*args)
> /root/test1.py(12)__init__()
-> print "init called"
(Pdb) c
init called

第二次通话:

> /root/test1.py(12)__init__()
-> print "init called"
(Pdb) w
  /root/test1.py(21)<module>()
-> logging.config.fileConfig('/root/test1.conf')
  /usr/local/python/2.6.4/lib/python2.6/logging/config.py(84)fileConfig()
-> handlers = _install_handlers(cp, formatters)
  /usr/local/python/2.6.4/lib/python2.6/logging/config.py(159)_install_handlers()
-> h = klass(*args)
> /root/test1.py(12)__init__()
-> print "init called"

【问题讨论】:

  • """为什么 init 被调用两次?""" - 使用调试器并检查它为什么被调用两次。查看调用堆栈可能会有所帮助...

标签: python logging handler config init


【解决方案1】:
  1. 为什么会调用两次 init?

如果您遵循logging 模块的代码,您会看到在加载日志记录配置文件时,它会实例化所有处理程序(第一次实例化)。

在您的代码中,您将处理程序声明为 test1.Test1TimedRotatingFileHandler,因此当它尝试导入您的处理程序时,它会解析 test1 模块中的代码...因此它会重新创建处理程序!!

更正后的代码将使用__name__ == '__main__'进行保护:

#!/bin/env python

import logging
import logging.handlers
import logging.config

class Test1TimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler):
    def __init__(self,filename):
        print "init called"
        logging.handlers.TimedRotatingFileHandler.__init__(self,filename, when='S', interval=86400, backupCount=8, encoding=None)

    def __del__(self):
        print "del called"
        if hasattr(logging.handlers.TimedRotatingFileHandler,"__del__"):
            logging.handlers.TimedRotatingFileHandler.__del__(self)

if __name__ == "__main__":
    logging.config.fileConfig('./test1.conf')
    logger = logging.getLogger("test1")

2 。为什么 del 的调用频率低于 init?

一般情况下,__del__ 操作符被称为 when-python-wants,更准确地说,它是在垃圾收集器决定对对象进行垃圾收集时调用;这不一定是在你发布它之后。

【讨论】:

  • 而析构函数不一定在Python退出时调用。
  • @Cedric:尤其是在处理轮换日志文件时,当构造函数被调用两次时会适得其反。我打算在我的处理程序的构造函数中做一个 doRollover() 。当 init 被调用两次时,这会导致两次翻转……那该怎么办?这是错误、功能还是什么?
  • 不是真正的错误,python 只保留 1 个处理程序,另一个似乎“丢失”...
  • @Cedric_ 谢谢。你有什么建议可以解决这个问题吗?
  • 嘿,我想你在我阅读日志代码和处理我的代码时修正了你的答案:)
【解决方案2】:

您的日志记录配置代码周围缺少if __name__ == "__main__": 保护。当logging 导入您的test1 模块以查找类引用时,它会第二次执行。

或者,在配置文件中使用名称__main__.Test1TimedRotatingFileHandler,或者将配置代码和处理程序类放在不同的文件中。

【讨论】:

  • 太棒了!就是这样!非常感谢!
猜你喜欢
  • 2011-10-07
  • 1970-01-01
  • 2011-06-23
  • 2017-03-11
  • 1970-01-01
  • 2018-09-22
  • 2021-07-13
  • 2021-11-25
  • 2015-12-24
相关资源
最近更新 更多