如何在多个模块中使用 logging.getLogger(__name__)

新手上路,请多包涵

来自 logging Python 2.7 的指南(我的重点):

命名记录器时使用的一个好的约定是使用模块级记录器,在每个使用日志记录的模块中,命名如下:

logger = logging.getLogger(__name__)

这意味着记录器名称跟踪包/模块层次结构,而且很明显,仅从记录器名称记录事件的位置。

听起来是个好建议。

现在, logging cookbook 提供了多个模块的示例,它使用硬编码记录器名称而不是 __name__ 常量。在示例中的“主模块”中,我们发现

logger = logging.getLogger('spam_application')

在“辅助模块”中我们发现

module_logger = logging.getLogger('spam_application.auxiliary')

我将这个示例逐字复制到具有以下结构的包文件夹中:

 cookbook-example
|- __init__.py
|- main_module.py
|- auxiliary_module.py

这运行没有问题,从主模块和辅助模块产生预期的日志输出,但事情是这样的:

如果我现在按照 logging howto 的建议,用 __name__ 常量替换硬编码的记录器名称,食谱示例将崩溃:我只从主模块获取日志消息,但辅助模块什么也没有。

我一定错过了一些明显的东西。知道我做错了什么吗?

笔记:

有很多非常相似的问题和相关答案,例如: 123456 等等。但是,这些似乎都没有解决这个具体问题。

- 编辑 -

这是一个基于食谱示例的最小示例,其中显式名称字符串替换为 __name__

主模块.py

 import logging
import auxiliary_module

# create and configure main logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# create console handler with a higher log level
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
# create formatter and add it to the handler
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
# add the handler to the logger
logger.addHandler(handler)

logger.info('message from main module')
auxiliary_module.some_function()

辅助模块.py

 import logging

# create logger
module_logger = logging.getLogger(__name__)

def some_function():
    module_logger.info('message from auxiliary module')

原文由 djvg 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.3k
2 个回答

正如@shmee 在 这个答案 中指出的那样,记录器层次结构必须使用点符号在记录器名称中明确定义。 That is, if the logger name in main_module.py is eg 'a' , then the logger name in auxiliary_module.py must be 'a.b' (not just 'b' ),以使其继承记录器的配置 'a'getLogger() 文档 中也提到了这一点。

然而,这应该在使用 __name__ 时自动处理,如 logging 操作方法 中所述:

这意味着记录器名称跟踪包/模块层次结构,并且仅从记录器名称记录事件的位置直观明显。

问题是,要使其正常工作,您需要以正确的方式使用 __name__ ,而我没有这样做。

我的示例中的问题在于 cookbook-example 包文件夹中的文件组织:

主模块和辅助模块都处于同一级别(即在同一文件夹中)。因此,如此 所述,主模块的 __name__ 将是 '__main__' (因为它是顶级脚本),而 __name__ -d2辅助模块将是 'auxiliary_module' (即文件名),而不是 '__main__.auxiliary_module'

因此,辅助模块中的记录器将是根记录器的子项,而不是 '__main__' 记录器的子项,因此它将继承根记录器配置(仍具有默认日志记录级别 WARNING ) 而不是主模块中指定的配置。

所以,为了让这个例子工作,我们有几个选择:

  1. 模块中的 getLogger(__name__) 替换为 getLogger() 。这会将配置应用到根记录器,因此也应用到辅助模块记录器,正如@shmee 所建议的那样。

  2. 辅助 模块中的 getLogger(__name__) 替换为 getLogger('__main__.' + __name__) 。结果将等同于原始食谱示例(除了主记录器现在称为 '__main__' 而不是 'spam_application' )。

原文由 djvg 发布,翻译遵循 CC BY-SA 4.0 许可协议

记录器的命名是您所缺少的。在示例中,在主模块中创建了一个名为 spam_application 的记录器。然后创建处理程序和格式化程序并将其添加到该记录器。

auxiliary_module 中创建的记录器的名称以 spam_application 开头。 spam_application.auxiliary 。这有效地创建了一个记录器层次结构,除非明确禁用,否则它们会传播到各自的父级。在食谱示例中,此层次结构为 spam_appliclation <- spam_application.auxiliary <- spam_application.auxiliary.Auxiliarylogger <- module_logger <- self.logger

如果将显式记录器名称替换为 __name__ 您最终会在主模块中配置一个名为 __main__ 的记录器,该模块配置了处理程序,但辅助记录器的命名不是以创建层次结构的方式,因此您的 auxiliary_module 记录器传播到未配置处理程序的隐式根记录器。

尝试以下操作:按如下方式更改类的 init 方法:

 def __init__(self):
    self.logger = logging.getLogger('spam_application.auxiliary.Auxiliary')
    print self.logger.parent
    self.logger.info('creating an instance of Auxiliary')

然后运行你的主模块一次

self.logger = logging.getLogger('spam_application.auxiliary.Auxiliary')

和一次

self.logger = logging.getLogger(__name__)

输出应如下所示:

 <Logger spam_application.auxiliary (WARNING)> # with explicit logger name
<RootLogger root (WARNING)>                   # using __name__

原文由 shmee 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题