【问题标题】:Is it better to use root logger or named logger in Python在 Python 中使用根记录器还是命名记录器更好
【发布时间】:2014-04-09 12:30:34
【问题描述】:

我正在尝试找出跨多个模块使用 python 登录的最佳实践。我在这里看到:http://docs.python.org/2/howto/logging#logging-from-multiple-modules 关于如何使用根记录器跨多个模块进行记录。正如链接指出的那样,您无法确定消息来自应用程序的哪个位置,因为它们都显示名称为“root”。

在我看来有两种选择(假设我的模块不在包结构中,而只是同一个文件夹中的一堆模块):

1) 使用示例中的根记录器,但更改日志格式以包含文件名:

# myapp.py
import logging
import mylib

def main():
    logging.basicConfig(format='%(asctime)s %(filename)s %(levelname)s: %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p', level=logging.INFO)  #filename='myapp.log', 
    logging.info('Started')
    mylib.do_something()
    logging.info('Finished')

if __name__ == '__main__':
    main()

#mylib.py
import logging

def do_something():
    logging.info('Do something')




In [4]: import myapp

In [5]: myapp.main()
03/06/2014 12:22:07 PM myapp.py INFO: Started
03/06/2014 12:22:07 PM mylib.py INFO: Do something
03/06/2014 12:22:07 PM myapp.py INFO: Finished

2) 在主应用程序中使用根记录器,但在子模块中使用命名记录器,并在日志格式中使用“名称”而不是“文件名”:

# myapp.py
import logging
import mylib

def main():
    #logging.basicConfig(format='%(asctime)s %(filename)s %(levelname)s: %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p', level=logging.INFO)  #filename='myapp.log', 
    logging.basicConfig(format='%(asctime)s %(name)s %(levelname)s: %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p', level=logging.INFO)  #filename='myapp.log', 
    logging.info('Started')
    mylib.do_something()
    logging.info('Finished')

if __name__ == '__main__':
    main()

#mylib.py
import logging

def do_something():
    #logging.info('Do something')
    logger = logging.getLogger(__name__)
    logger.info('Do something')



In [3]: import myapp

In [4]: myapp.main()
03/06/2014 12:27:29 PM root INFO: Started
03/06/2014 12:27:29 PM mylib INFO: Do something
03/06/2014 12:27:29 PM root INFO: Finished

【问题讨论】:

    标签: python logging


    【解决方案1】:

    Vinay Sajip(日志模块的维护者)创建了一个 recommendation here,这与您的选项 #2 类似,除了您可以在任何地方使用命名记录器,即使在 myapp 中也是如此:

    import logging
    import mylib
    logger = logging.getLogger(__name__)
    
    def main():
        logging.basicConfig(format='%(asctime)s %(module)s %(levelname)s: %(message)s',
                            datefmt='%m/%d/%Y %I:%M:%S %p', level=logging.INFO)  
        logger.info('Started')
        mylib.do_something()
        logger.info('Finished')
    
    if __name__ == '__main__':
        main()
    

    产生

    03/06/2014 12:59:25 PM myapp INFO: Started
    03/06/2014 12:59:25 PM mylib INFO: Do something
    03/06/2014 12:59:25 PM myapp INFO: Finished
    

    请注意,如果您使用%(module)s 而不是%(name)s,那么您会得到myapp,而不是之前得到rootmyapp.py__main__

    这种对称性——总是使用logger——如果myapp 有时被称为脚本,有时被作为模块导入,则可能特别有用。

    【讨论】:

    • 谢谢!我试过这个,它有效,但我不明白为什么。 (在 myapp 和 mylib 中都有名为“_name_”的记录器。)如果 myapp 像在我的示例中那样被导入,则 myapp 中的 _name_ 是“myapp”。 mylib 中的 _name_ 是“mylib” 所以在每个文件中,当 logger = logging.getLogger(_name_) 时,他们不是创建了两个不同的记录器吗?它是否有效,因为每个单独记录器的输出仍被传递到根记录器,然后到控制台?
    • 另外,如果 myapp 有时被称为脚本或作为模块导入,您能否详细说明您的最后一条评论,即始终使用“记录器”很有用。你的意思是使用 logger = logging.getLogger(..) 比直接使用根记录器更好:logging.info("..")?
    • 是的,你得到了两个不同的记录器。 This flowchart 显示记录器(默认情况下)将记录传播到父记录器。所以根记录器的处理程序有机会处理记录。
    • 假设您在主脚本 myapp 中使用 logging.info,但在模块中使用 logger.info。假设稍后您创建一个导入 myapp 的新脚本。现在日志输出将使用root 作为%(name)s,只要有来自myapp 的日志消息,而为了保持一致性,您宁愿让它报告myapp。如果您改为始终使用logger.info,即使在主脚本中也是如此,那么您的代码可以在未来证明它有可能有一天会被用作模块。
    • 感谢流程图,以及您对使用记录器优势的解释。现在对我来说两者都有意义。不确定我是否应该开始一个新问题,但是:如果在这个例子中我想要多个处理程序,即 INFO 级别转到标准输出和一个文件,而 ERROR 级别发送到一个电子邮件地址,有没有办法做到这一点?我假设它不是 logging.addHandler()?也许 logging.config.fileConfig()?
    猜你喜欢
    • 2020-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多