使用多个模块进行记录时,Python记录器层次结构和根记录器

时间:2019-10-01 16:12:13

标签: python python-3.x inheritance logging hierarchy

我有此设置:

main.py
/module
/module/__init__.py (empty)
/module.py

这是我的两个文件main.pymodule.py的代码:

main.py

import logging
from module import module

logger = logging.getLogger(__name__)

def test():
    logger.warning('in main.py/test')

def main():
    handler = logging.StreamHandler()
    handler.setLevel(logging.INFO)
    formatter = logging.Formatter('%(asctime)s %(name)s/%(module)s [%(levelname)s]: %(message)s', '%Y-%m-%d %H:%M:%S')
    handler.setFormatter(formatter)
    logger.addHandler(handler)

    logger.warning('in main.py/main')
    module.something()

if __name__ == "__main__":
    main()    

module.py

import logging
logger = logging.getLogger(__name__)

def something():
    logger.warning('in module.py/something')

所以,我注意到的是,这将输出以下内容(注意模块记录器如何不格式化):

2019-10-01 09:03:40 __main__/main [WARNING]: in main.py/main
in module.py/something

似乎只有在我在main.py中进行编辑之后,才能将logger = logging.getLogger( __ name __ )更改为logger = logging.getLogger(),或者在{{1之后添加logger = logging.getLogger() }}这样记录(这就是我想要的):

def main():

那是为什么?我认为,由于2019-10-01 09:04:13 root/main [WARNING]: in main.py/main 2019-10-01 09:04:13 module.module/module [WARNING]: in module.py/something 正在导入main.py,因此它在层次结构上自然更高,因此module.py将继承module.py中定义的记录器设置。是否需要在main中显式设置root记录器(使用main.py才能使继承起作用?我是否没有正确配置文件夹结构以使logger = logging.getLogger()的记录器继承module.py的记录器设置,还是文件夹结构无关?

我问的原因是因为我认为应该始终使用main.py(甚至在logger = logging.getLogger( __ name __ )中使用),然后根据导入结构(或文件夹结构?)来确定层次结构和记录器将相应地继承。我之所以做出这个假设,是因为如果我将main.py导入另一个程序怎么办?我想我的意思是,我想使日志记录尽可能通用,以便可以将一个模块导入另一个模块,并且该模块始终继承父级的记录器设置。有没有一种方法可以显示所有模块的基础层次结构以便进行调试/学习?

1 个答案:

答案 0 :(得分:4)

日志记录层次结构与程序中的文件结构无关。层次结构仅由记录器的名称确定。配置记录器时,除非其名称另有明确说明,否则所有名称以其名称前缀开头的记录器都是其子级,并继承其配置。

在您的示例中,日志记录设置与执行顺序和您选择的名称有更多的关系。程序运行时,它将执行以下操作:

  1. 由于logging.py而从标准库中运行import logging
  2. 运行module.py以完成from module import module
  3. logger中的main属性设置为名为Logger的{​​{1}}。
  4. 创建一个__main__函数
  5. 创建一个test函数
  6. 运行主要功能

此事件序列的一些后果:

  • mainmodule.logger之前创建。这不会影响您所看到的行为,但是在这种情况下值得注意。
  • 如果您调用main.logger作为脚本,则
  • main.logger被命名为__main__。如果您将其命名为main,例如,您看到的行为不会改变。来自main
  • python -m main显然与module不在同一层次结构中。两者都是根记录器沿不同分支的后代。

最后一项实际上是您的问题的答案。如果要让程序中的所有记录器共享相同的默认记录方法,则应配置根记录器,或确保它们具有相同的名称前缀,然后将其配置为好像是根记录器一样。

您可以使所有记录器都继承自main。在main中,您会这样做

module/module.py

这里的问题是名称logger = logging.getLogger('__main__.' + __name__) 是硬编码的。您无法保证它将是__main____main__。您可以尝试在main中使用import main,因此可以进行module,但这不能按预期工作。如果main.__name__ + '.' + __name__main的身份运行,则导入它实际上会创建一个具有完全独立的日志记录层次结构的第二个模块对象。

这就是为什么根记录器没有名称的原因。它提供了您想要的可维护性和一致性。您不必为了找出根名称而跳绳。

话虽这么说,您仍然应该将__main__记录到main.py__main__记录器中。根记录器仅应在导入保护中设置。这样,如果将main导入为常规模块,它将尊重正在其下运行的驱动程序的日志记录设置。

TL; DR

通常在程序的驱动程序中设置匿名根记录程序。不要尝试从main或驱动程序模块名称继承记录器。