【问题标题】:Python logging in interactive sessions交互式会话中的 Python 日志记录
【发布时间】:2011-02-19 14:36:48
【问题描述】:

我正在尝试在我的 Python 2.7 应用程序中实现日志记录,并且发现它非常有用。但是,我注意到当以交互方式运行 Python 时,每条日志消息都会打印多次。打印消息的次数与我之前运行脚本的次数相同,因此似乎在脚本末尾没有正确清理记录器(我猜)。考虑以下示例:

import sys
import logging

def main(argv=None):

    log = logging.getLogger('test')
    log.setLevel(logging.DEBUG)

    console_handler = logging.StreamHandler()
    console_handler.setFormatter(logging.Formatter("%(message)s"))
    log.addHandler(console_handler)

    log.info('Starting something...')
    log.info('Doing something...')
    log.info('Finished something.')

    logging.shutdown()

if __name__=='__main__':
    sys.exit(main(sys.argv[1:]))

打字

>>> import file.py
>>> file.main()

产生以下结果:

Starting something...
Doing something...
Finished something.

然后再次输入file.main() 会产生:

Starting something...
Starting something...
Doing something...
Doing something...
Finished something.
Finished something.

重复第三次将给出三个或每个消息,依此类推。有谁知道为什么会发生这种情况 - 这是日志模块的预期行为吗?如果是,我该如何更改?如预期的那样,如果作为脚本 (python file.py) 运行,上述脚本只会打印每条消息中的一条。

【问题讨论】:

    标签: python logging interactive


    【解决方案1】:

    是的,您正在创建和重用记录器的单个实例。添加到该记录器的每个处理程序也会记录一条消息。

    您可能希望日志记录设置在模块级别或单独的函数中,以便您只运行一次。

    可能是这样的:

    import atexit
    import sys
    import logging
    
    log = logging.getLogger('test')
    log.setLevel(logging.DEBUG)
    
    console_handler = logging.StreamHandler()
    console_handler.setFormatter(logging.Formatter("%(message)s"))
    log.addHandler(console_handler)
    
    def shutdown_logging():
        logging.shutdown()    
    
    atexit.register(shutdown_logging)
    
    def main(argv=None):
        log.info('Starting something...')
        log.info('Doing something...')
        log.info('Finished something.')
    
    
    if __name__=='__main__':
        sys.exit(main(sys.argv[1:]))
    

    【讨论】:

      【解决方案2】:

      试试这个解决方法:

      if len(logging.root.handlers) == 0:
           log.add_handler(console_handler)
      

      日志模块使用全局静态记录器对象,当您在解释器中时,该对象会在您的会话中持续存在。因此,每次您调用add_handler 时,您都在添加一个全新的流处理程序,但不会删除旧的。日志记录只是遍历其处理程序并将输出发送到每个处理程序,因此每次运行时都会将相同内容的新副本发送到控制台。

      【讨论】:

      • 好的,谢谢。我假设logging.shutdown() 的目的是删除任何添加到根记录器的处理程序。但是,现在查看了文档,它只是用于刷新和关闭任何处理程序(而不是删除它们)。
      【解决方案3】:

      您可以在每次重新加载配置时删除所有处理程序,在您的情况下,在调用 file.main() 之前:

      file.logging.getLogger('test').handlers = []
      

      注意(主观):

      正如@stderr 所写,鼓励在模块级别定义您的记录器。但是,我认为在应用程序入口点设置它们也是一个好习惯,所以在这里我只会在 if __name__=='__main__' 之后添加处理程序,或者在你的情况下,在你的 (I)Python 控制台中。这样,导入模块并不会创建各种处理程序,而是仅由选择执行模块某些功能的人创建。

      【讨论】:

        【解决方案4】:

        这是@beer_monk 解决方案的替代方案,即使您在其他地方接触了根处理程序,它也可以工作。我在这里使用了__name__ 而不是'test',这样可以更容易地在其他模块中重用代码,也许可以通过将其制成一个以__name__ 作为参数的函数。

        if not __name__ in logging.Logger.manager.loggerDict:
            new_logger = logging.getLogger(__name__)
            new_logger.setLevel(logging.DEBUG)
        
            console_handler = logging.StreamHandler()
            console_handler.setFormatter(logging.Formatter("%(message)s"))
            new_logger.addHandler(console_handler)
        log = logging.getLogger(__name__)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-12-24
          • 1970-01-01
          • 2010-10-29
          • 2017-08-28
          • 2013-12-15
          • 1970-01-01
          • 2010-10-31
          相关资源
          最近更新 更多