【问题标题】:Logger hierarchy and the root logger when logging with multiple modules使用多个模块进行记录时的记录器层次结构和根记录器
【发布时间】:2023-03-31 21:55:02
【问题描述】:

我有这个设置:

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

这是我的两个文件的代码,分别是 main.pymodule.py

ma​​in.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() def main(): 之后添加logger = logging.getLogger() 之后它才会这样记录(即我想要什么):

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.py 中定义的记录器设置。是否需要在 main 中显式设置根记录器(使用logger = logging.getLogger())才能使继承工作?我没有正确配置我的文件夹结构以使module.py 的记录器继承main.py 的记录器设置,还是文件夹结构无关紧要?

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

【问题讨论】:

  • 拥有一个名为module.py 的文件和一个包含__init__.py 的同名目录(没有.py)不是一个好主意。可能你把自己和任何阅读代码的人都弄糊涂了。可能有一条规则,说明真正要导入的内容,但我认为这不是一个好主意。
  • 代码确实有效,我并不觉得它特别令人困惑,举个例子,它确实突出了我试图传达的内容。我可以将它重命名为 module_folder,但它会弄乱下面的答案。我认为根据提供的信息,人们应该能够弄清楚......
  • 令我困惑的不是代码,而是文件名,您有一个名为 module.py 的文件和一个名为 module 的目录,其中包含 __init__.py。我想大多数人都不知道如果您执行import module 会查看什么文件。它会读取和编译module.pymodule/__init__.py 还是两者,如果两者都按什么顺序。如果您有from module import module,将查看什么文件。避免使用与目录同名的目录会更容易。但你是对的,你得到了答案。所以也许只有我。
  • 更正:避免使用与 filename.py 同名的目录使其更容易。在我的机器上,module/__init__.py 被导入,module.py 被忽略,所以我什至无法重现你的问题。

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


【解决方案1】:

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

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

  1. 由于import logging 而从标准库运行logging.py
  2. 运行module.py 以履行from module import module
  3. main 中的logger 属性设置为名为@9​​87654328@ 的Logger
  4. 创建test 函数
  5. 创建main 函数
  6. 运行主函数

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

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

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

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

logger = logging.getLogger('__main__.' + __name__)

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

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

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

TL;DR

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

【讨论】:

  • 我还是有点糊涂,不过没关系。第一个问题:第 3 行“将 main 中的 logger 属性设置为名为 main 的 Logger。” - 在 test() 的定义之前是指“logger = logging.getLogger(name)”吗?如果是这样,为什么它会设置一个名为 main 的记录器,因为它在 main 执行之前被调用。第二个问题:在 test() 的定义之前,我什至应该有那个“logger = logging.getLogger(name)”吗?第三个问题,我是否应该在 main() 中执行“logger = logging.getLogger() 来定义根记录器?我想看看我是否理解你在说什么。
  • 是的,#3 指的是logger = logging.getLogger(__name__)。当您将模块作为命令行脚本运行时,模块的名称为__main__。对于模块中任何地方对__name__ 的任何引用都是如此。 __name__ 实际上由解释器设置并分配给模块的全局字典。
  • 您通常希望为整个模块分配一个全局可访问的记录器,以便在代码的早期分配一个位置,以便您可以使用它。您不想每次需要时都获取记录器,因此分配给全局变量是标准做法。
  • 您可以在main 函数中配置根记录器,但将logger = logging.getLogger(__name__) 保留在模块级别。这将确保您的模块级记录器具有与其他模块相同的所有设置。
  • 希望这有助于回答您的问题。如果没有,请继续询问。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-02
  • 2020-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多