从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所推荐的那样,菜谱示例会崩溃:我只从主模块获取记录消息,但没有来自辅助模块。
我必须遗漏一些明显的东西。我有什么想法吗?
注意:
有许多非常相似的问题和相关答案,例如:1,2,3,4,5,{{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')
答案 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
)而不是主模块中指定的配置。
因此,为了使示例有效,我们有几个选项:
将{em> main 模块中的getLogger(__name__)
替换为getLogger()
。
这会将配置应用于根记录器,因此也适用于
辅助模块记录器,如@shmee所建议。
将辅助模块中的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.Auxiliary
或logger <- 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