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

djv*_*jvg 17 python logging

从Python 2.7 的logginghowto(我的重点):

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

logger = logging.getLogger(__name__)

这意味着记录器名称跟踪包/模块的层次结构,并且从记录器名称中记录事件的地方就很明显了。

听起来像个好建议。

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

logger = logging.getLogger('spam_application')

在“辅助模块”中

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

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

cookbook-example
|- __init__.py
|- main_module.py
|- auxiliary_module.py
Run Code Online (Sandbox Code Playgroud)

这没有问题,可以从主模块和辅助模块产生预期的日志记录输出,但实际上是这样:

如果现在__name__按照logginghowto的建议,用常数替换硬编码的记录器名称,则菜谱示例将崩溃:我仅从主模块获取记录消息,而从辅助模块中获取不到任何消息。

我一定想念一些明显的东西。有什么想法我做错了吗?

注意:

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

- 编辑 -

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

main_module.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()
Run Code Online (Sandbox Code Playgroud)

assistant_module.py

import logging

# create logger
module_logger = logging.getLogger(__name__) 

def some_function():
    module_logger.info('message from auxiliary module')
Run Code Online (Sandbox Code Playgroud)

djv*_*jvg 17

正如@shmee在此答案中指出的那样,必须在记录器名称中使用点符号显式定义记录器层次结构。也就是说,如果in中的记录器名称main_module.py'a',则in的记录器名称auxiliary_module.py必须为'a.b'(而不仅仅是'b'),以便它继承logger的配置'a'文档中也提到了这一点getLogger()

然而,这应该会自动使用时的照顾__name__,如在提到logging如何做

这意味着记录器名称跟踪程序包/模块的层次结构,并且从记录器名称中记录事件的地方很直观。

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

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

主模块和辅助模块都处于同一级别(即在同一文件夹中)。所以,作为解释这里__name__主模块将是'__main__'(因为它是顶级脚本),以及__name__用于辅助模块会'auxiliary_module'(即文件名),NOT '__main__.auxiliary_module'

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

因此,为使示例正常工作,我们有几种选择:

  1. 更换getLogger(__name__)主要的模块getLogger()。如@shmee所建议的那样,这会将配置应用于根记录器,从而也应用于辅助模块记录器。

  2. 替换getLogger(__name__)辅助由模块 getLogger('__main__.' + __name__)。结果将与原始食谱示例相同(除了现在调用主记录器 '__main__'代替'spam_application')。

  • 由于某种原因,在“main_module.py”和“auxiliary_module.py”中使用“logger =logging.getLogger()”看起来也可以工作。有谁知道这是否可以,或者下面是否隐藏着一些非常糟糕的东西? (2认同)
  • @Aenaon:根据[文档](https://docs.python.org/3/library/logging.html#logging.getLogger)`getLogger()`(没有输入参数)将返回`root`记录器。因此,实际上,您*将*看到来自“main_module”和“auxiliary_module”的日志输出,*但是*两条消息都来自“root”记录器。使用 getLogger(__name__) 的目的是让我们知道日志消息源自哪个模块。 (2认同)

shm*_*mee 7

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

auxiliary_module记录器中,创建的名称以spam_applicationresp 开头。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')
Run Code Online (Sandbox Code Playgroud)

然后使用

self.logger = logging.getLogger('spam_application.auxiliary.Auxiliary')
Run Code Online (Sandbox Code Playgroud)

并与

self.logger = logging.getLogger(__name__)
Run Code Online (Sandbox Code Playgroud)

输出应如下所示:

<Logger spam_application.auxiliary (WARNING)> # with explicit logger name
<RootLogger root (WARNING)>                   # using __name__
Run Code Online (Sandbox Code Playgroud)

  • 这确实令人困惑,当我开始登录时,我也在为同样的事情苦苦挣扎。但是,模块/包层次结构与记录器层次结构不同。后者纯粹基于点符号字符串,而前者依赖于文件夹结构以及主模块的名称始终为 `__main__` 的事实。即使您以基于`__name__` 的名称创建所述层次结构的方式创建和配置您的父记录器,您最终还是会遇到主模块或辅助中的专用类记录器等结构的问题。 (3认同)
  • 那么,既然您无论如何都必须至少配置一个记录器,为什么不按照建议显式配置根记录器呢?这样你就可以在任何地方安全地使用 `getLogger(__name__)`。这就是我最终做的事情。 (2认同)

归档时间:

查看次数:

10361 次

最近记录:

7 年,3 月 前