Python 日志记录:文件处理程序不记录子模块日志记录调用

Python logging: file handler not recording submodule logging calls

我有一个包含 1 个 StreamHandler 的基本日志记录配置文件。将其导入主 python 脚本后,我明确添加了一个新的 FileHandler。该文件处理程序成功记录了我从主文件进行的所有 log.[info,warn,...] 调用,但由于某种原因,它忽略了从我导入主文件的子模块进行的所有调用。

我该如何解决这个问题?我在 Ubuntu 20.04 上使用 Python 3.7。 这是一个可重现的例子:

main.py:

#!/usr/bin/env python

import logging
import logging.config

# Import basic logging config (which only contains a StreamHandler)
logging.config.fileConfig('logging.conf')
log = logging.getLogger(__name__)

# Add a FileHandler dedicated to the current main/executable file
fh = logging.FileHandler(filename='test.log', mode='w')
fh.setLevel(logging.INFO)

# Use the same format as the (only) existing StreamHandler
fh.setFormatter(logging.getLogger().handlers[0].formatter)
log.addHandler(fh)

# Successfully prints to console and log file
log.info("Info message from main")


import my_lib

# Succesfully prints to console, but not to log file
foo = my_lib.MyClass()

my_lib.py:

#!/usr/bin/env python

import logging
log = logging.getLogger(__name__)

class MyClass:    
    def __init__(self):
        log.info("Info message from submodule")
        log.warn("Warning message from submodule")
        log.error("Error message from submodule")

logging.conf:

[loggers]
keys=root

[handlers]
keys=consoleHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=INFO
handlers=consoleHandler

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

[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s

上下文:我的 python 项目有多个“主要”可执行文件,我想为每个文件关联一个单独的日志文件。但是,控制台输出对所有人来说都是相同的,因此我使用通用的 StreamHandler 创建了一个通用的日志记录配置文件。

问题是由于您将 FileHandler 附加到 __main__ 模块 (main.py) 并将 StreamHandler 附加到根记录器。 my_lib 中记录的消息由 my_lib 记录器的处理程序处理(有 none),然后是所有祖先的处理程序 - 在这种情况下,这意味着根记录器的处理程序,即StreamHandler 写入控制台。由于 __main__ 记录器不是 my_lib 记录器的祖先,因此不要求其处理程序(只是 FileHandler)处理记录到 my_lib 记录器的事件。

要解决此问题,最好使用 dictConfig() 而不是 fileConfig():例如,在 logging.json:

中使用此配置
{
    "version": 1,
    "disable_existing_loggers": false,
    "formatters": {
        "simple": {
            "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
        }
    },
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
            "stream": "ext://sys.stdout",
            "level": "INFO",
            "formatter": "simple"
        },
        "file": {
            "class": "logging.FileHandler",
            "filename": "tbd.log",
            "mode": "w",
            "level": "INFO",
            "formatter": "simple"
        }
    },
    "root": {
        "level": "INFO",
        "handlers": ["console", "file"]        
    }
}

和这个稍微修改过的主程序main.py:

#!/usr/bin/env python
import json
import logging
import logging.config
import os

with open('logging.json') as f:
    config = json.load(f)
lfn = os.path.basename(__file__).replace(".py", ".log")
config['handlers']['file']['filename'] = lfn
logging.config.dictConfig(config)
log = logging.getLogger(__name__)

log.info("Info message from main")

import my_lib

foo = my_lib.MyClass()

我在控制台上得到这个:

2022-05-30 13:49:55,353 - __main__ - INFO - Info message from main
2022-05-30 13:49:55,354 - my_lib - INFO - Info message from submodule
2022-05-30 13:49:55,354 - my_lib - WARNING - Warning message from submodule
2022-05-30 13:49:55,354 - my_lib - ERROR - Error message from submodule

文件中的这个 main.log:

2022-05-30 13:49:55,353 - __main__ - INFO - Info message from main
2022-05-30 13:49:55,354 - my_lib - INFO - Info message from submodule
2022-05-30 13:49:55,354 - my_lib - WARNING - Warning message from submodule
2022-05-30 13:49:55,354 - my_lib - ERROR - Error message from submodule