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

Mik*_*ike 8 python inheritance logging hierarchy python-3.x

我有这个设置:

main.py
/module
/module/__init__.py (empty)
/module.py
Run Code Online (Sandbox Code Playgroud)

这里是我的两个文件的代码,main.pymodule.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()    
Run Code Online (Sandbox Code Playgroud)

模块.py

import logging
logger = logging.getLogger(__name__)

def something():
    logger.warning('in module.py/something')
Run Code Online (Sandbox Code Playgroud)

所以,我注意到这会输出以下内容(注意模块记录器没有格式):

2019-10-01 09:03:40 __main__/main [WARNING]: in main.py/main
in module.py/something
Run Code Online (Sandbox Code Playgroud)

它只是看起来像后,才进行编辑在main.py以变化logger = logging.getLogger( __ name __ )logger = logging.getLogger() 添加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
Run Code Online (Sandbox Code Playgroud)

这是为什么?我认为,因为main.py是导入module.py,它在层次结构上自然更高,所以module.py会继承 中定义的记录器设置main.py。是否需要logger = logging.getLogger()在 main 中显式设置根记录器(with )以使继承工作?是我没有正确配置我的文件夹结构以使module.py的记录器继承main.py的记录器设置,还是文件夹结构无关紧要?

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

Mad*_*ist 8

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

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

  1. logging.py从标准库运行,因为import logging
  2. 运行module.py以完成from module import module
  3. logger属性设置mainLogger命名的__main__.
  4. 创建test函数
  5. 创建main函数
  6. 运行主函数

这一系列事件的一些后果:

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

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

你可以让所有的记录器都继承自main. 在module/module.py,你会做

logger = logging.getLogger('__main__.' + __name__)
Run Code Online (Sandbox Code Playgroud)

这里的问题是名称__main__是硬编码的。你不能保证它会是__main__vs main。你可以试一试import mainmodule这样你就可以做main.__name__ + '.' + __name__,但这不会按预期工作。如果main作为 运行__main__,导入它实际上会创建一个具有完全独立的日志记录层次结构的第二个模块对象。

这就是根记录器没有名称的原因。它提供了您想要的可维护性和一致性。您不必费力地找出根名称。

话虽如此,您仍然应该main.py记录到__main__main记录器。根记录器应该只在导入保护中设置。这样,如果main作为常规模块导入,它将尊重正在运行的驱动程序的日志记录设置。

TL; 博士

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