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
我有一个包含 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