【问题标题】:How to use logging.getLogger(__name__) in multiple modules如何在多个模块中使用 logging.getLogger(__name__)
【发布时间】:2018-11-15 18:43:39
【问题描述】:

来自logging howto for Python 2.7(我的重点):

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

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__

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()

auxiliary_module.py

import logging

# create logger
module_logger = logging.getLogger(__name__) 

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

【问题讨论】:

    标签: python logging


    【解决方案1】:

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

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

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

    尝试以下方法: 更改类的 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__
    

    【讨论】:

    • 谢谢,我是按照这些思路思考的(请参阅编辑后的最小示例)。但是我们真的需要明确定义层次结构吗?对我来说,logging howto 中的语句“这意味着记录器名称跟踪包/模块层次结构......”会暗示其他情况。
    • 这确实令人困惑,当我开始记录时,我也在为同样的事情苦苦挣扎。但是,模块/包层次结构与记录器层次结构不同。后者纯粹基于点表示的字符串,而前者依赖于文件夹结构以及您的主模块名称将始终为__main__这一事实。即使您以基于__name__ 的名称创建和配置父记录器的方式创建所述层次结构,您最终也会在主模块或辅助中的专用类记录器等结构方面遇到问题。
    • 那么,既然你必须至少配置一个记录器,为什么不按照建议明确配置根记录器呢?这样你就可以在任何地方安全地使用getLogger(__name__)。这就是我最终做的事情。
    • 确实,如果我只是从主模块中的getLogger() 中删除名称参数并在辅助模块中使用getLogger(__name__),则配置将应用于根记录器,并且辅助记录器消息也出现。我想这更多是一个相对的导入问题,而不是日志记录问题,并且与__name__ 的行为有关(如here 解释)。
    【解决方案2】:

    正如@shmee 在this answer 中指出的那样,记录器层次结构必须在记录器名称中明确定义,使用点表示法。也就是说,如果 main_module.py 中的记录器名称是例如'a',那么auxiliary_module.py中的logger名字必须是'a.b'(不仅仅是'b'),才能继承logger'a'的配置。 getLogger() documentation中也提到了这一点。

    但是,在使用 __name__ 时应自动处理此问题,如 logging how-to 中所述:

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

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

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

    主模块和辅助模块都在同一级别(即在同一文件夹中)。因此,正如here 所解释的那样,主模块的__name__ 将是'__main__'(因为它是顶级脚本),而辅助模块的__name__ 将是'auxiliary_module'(即文件名),而不是 '__main__.auxiliary_module'

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

    因此,为了使示例正常运行,我们有多种选择:

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

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

    【讨论】:

    • 由于某种原因,在 main_module.pyauxiliary_module.py 中使用 logger = logging.getLogger() 看起来也可以正常工作。有谁知道这样可以吗,或者下面是否隐藏了一些非常糟糕的东西?
    • @Aenaon:根据docs getLogger()(不带输入参数)将返回root 记录器。因此,确实,您看到来自main_moduleauxiliary_module 的日志输出,但是这两条消息都来自root 记录器。使用getLogger(__name__) 的目的是让我们知道日志消息来自哪个模块。
    • @Aenaon:澄清一​​下,我们希望看到的是2021-04-13 10:27:58 - auxiliary_module - INFO - message from auxiliary module而不是2021-04-13 10:27:58 - root - INFO - message from auxiliary module
    【解决方案3】:

    我认为更简单、更专业的解决方案是更改文件夹结构。创建另一个名为 packages(或其他东西)的文件夹并将辅助模块移到那里。

    记录器会推断模块层次结构并在需要的地方添加点。

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

    【讨论】:

    • 是的,这就是你通常的做法,as described in the docs。但是,问题是关于理解底层机制,尤其是关于理解 为什么 如果将 auxiliary_module 与顶级脚本 (main_module )。
    • 改变结构有帮助吗?如果我调用python main_module.py,那么__name__ 仍将解析为__main__,而packages.auxiliary_module 中的auxiliary_module.py。然后我仍然有记录器不是同一个父母的孩子的问题。
    猜你喜欢
    • 2016-04-16
    • 2015-02-06
    • 1970-01-01
    • 2020-07-22
    • 2021-11-06
    • 2013-09-24
    • 2016-12-21
    • 1970-01-01
    • 2018-09-08
    相关资源
    最近更新 更多