如何在多个模块

时间:2018-06-06 07:14:49

标签: python logging

从Python的logging howto(我的重点):

  

命名记录器时使用的一个好习惯是使用模块级   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

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

如果我现在用__name__常量替换硬编码的记录器名称,那么就像logging howto所推荐的那样,菜谱示例会崩溃:我只从主模块获取记录消息,但没有来自辅助模块。

我必须遗漏一些明显的东西。我有什么想法吗?

注意:

有许多非常相似的问题和相关答案,例如:12345,{{3 }}, 还有很多。 但是,这些似乎都没有解决这个具体问题。

- 编辑 -

这是一个基于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()

auxiliary_module.py

import logging

# create logger
module_logger = logging.getLogger(__name__) 

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

3 个答案:

答案 0 :(得分:10)

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

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

  

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

问题是,要实现这一点,您需要以正确的方式使用__name__,而我并没有这样做。

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

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

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

因此,为了使示例有效,我们有几个选项:

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

  2. 辅助模块中的getLogger(__name__)替换为 getLogger('__main__.' + __name__)。结果将是等效的 到原始的cookbook-example(除了现在调用主记录器 '__main__'代替'spam_application')。

答案 1 :(得分:4)

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

auxiliary_module记录器中创建的名称以spam_application resp开头。 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__

答案 2 :(得分:0)

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

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

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