Python 日志记录多个模块的日志记录器在主程序之外无法工作。

16
我的目标是从多个模块记录日志,同时只在主程序中配置记录器。如this answer所示,应该包含


logging.config.fileConfig('/path/to/logging.conf') 

在主程序中,然后在所有其他模块中包括。
logger = logging.getLogger(__name__)

我相信这就是我下面所做的,但我得到了意外的行为。

c.py

# c.py
import logging
import logging.config
import d

logging.config.fileConfig("logging.conf")
logger = logging.getLogger(__name__)

logger.warning('logging from c')
d.foo()

d.py

# d.py
import logging

logger = logging.getLogger(__name__)

# this will print when d is imported
logger.warning('logging from d on import')

def foo():
    # this does not print
    logger.warning("logging from d on call foo()")

输出

$ python c.py
logging from d on import
logging from c

我期望的是,当在 c.py 中执行d.foo()时,会从d记录一条消息,但事实并非如此。这很令人困惑,因为当在d模块级别调用记录器时,它会将消息记录到控制台,但是当从foo()内部调用时却没有记录。

配置文件

[loggers]
keys=root

[handlers]
keys=consoleHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)

[formatter_simpleFormatter]
format=%(message)s
datefmt=

进一步分析

我注意到,如果我删除这行代码

logging.config.fileConfig("logging.conf")

c.py,然后从d.foo()记录日志按预期工作。因此,要么配置文件中有问题,要么由于我提供了配置文件,导致d.py中的记录器出现问题。

问题

  1. 为什么在模块级别调用时会从d记录消息,但不会从d.foo()内部记录?
  2. 如何实现从多个模块记录日志的目标,同时只在主程序中配置日志记录?
2个回答

20

问题在于

import d

在...之前

logging.config.fileConfig("logging.conf")

正如@davidejones指出的那样。这就是为什么:

文档中所述,当调用logging.config.fileConfig()时,默认行为是禁用任何现有的记录器。因此,当发生import d时,d中的logger被初始化,然后当调用logging.config.fileConfig()时,d中的logger被禁用,这就是为什么当调用d.foo()时我们没有看到任何日志记录。

解决方法

logging.config.fileConfig()接受一个参数disable_existing_loggers,默认为True。使用

logging.config.fileConfig("logging.conf", disable_existing_loggers=False)

输出结果为

>>> python c.py
logging from d on import
logging from c
logging from d on call foo()

果然如此。


啊,好的,我忘记了禁用记录器设置,很高兴你找到了需要的东西,这似乎是一个更好的解决方案。 - davidejones
1
谢谢。你肯定帮助我找到了正确的方向。非常感激。 - Filip Kilibarda

5
我认为导入d模块是在配置记录器之前进行的,那么这样做可能会得到你想要的结果?
# c.py
import logging
import logging.config

logging.config.fileConfig("logging.conf")
logger = logging.getLogger(__name__)

import d


logger.warning('logging from c')
d.foo()

它给我这些结果

logging from d on import
logging from c
logging from d on call foo()

编辑

从代码来看,将日志设置单独放在一个文件中可能更有意义,然后您只需在主文件中导入一次即可,其他模块也会有它,这样代码看起来就不会太杂乱。因此,可以创建一个名为logsetup.py的文件,内容如下:

import logging
import logging.config

logging.config.fileConfig("logging.conf")

然后c.py看起来像这样,仍然会产生相同的结果。
# c.py
import logsetup
import logging
import d

logger = logging.getLogger(__name__)

logger.warning('logging from c')
d.foo()

我明白了。我猜当我在配置记录器之前import d时,d中的记录器没有处理程序,因此它不会在任何地方打印。但是,如果我根本不包括语句logging.config.fileConfig("logging.conf"),我希望两个记录器都不会打印任何内容(但事实并非如此,它们都会打印到控制台)。但我想在这种情况下,两个记录器最终都会有一个默认的控制台处理程序... - Filip Kilibarda
是的,我认为这是一个公平的评估。我更新了我的答案,稍微以不同的方式来实现它。我认为也许最好有一个单独的文件来设置记录器。就像Django或Flask这样的框架可能会有一个settings.py文件来处理这种事情一样,希望它有所帮助。 - davidejones
我喜欢你的第二种方法。如果我没有从其他人那里得到其他观点,过一段时间后我会接受那个答案。感谢您的帮助。 - Filip Kilibarda
嘿@FilipKilibarda,你最终找到其他解决方案了吗? - davidejones
1
嘿@davidejones,我已经用我认为最直接的解决方案回答了自己的问题。 - Filip Kilibarda

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接