Pytest caplog无法捕获日志问题求助(附复现代码)
问题背景
我在使用Pytest的caplog插件时遇到了无法捕获日志的问题,以下是最小复现代码、执行输出和环境信息:
复现代码
import logging import logging.config def test_logging(caplog): LOGGING_CONFIG = { "version": 1, "disable_existing_loggers": False, "formatters": { "default": { "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s", }, }, "handlers": { "console": { "class": "logging.StreamHandler", "formatter": "default", "level": "DEBUG", }, }, "loggers": { "": { # root logger "handlers": ["console"], "level": "DEBUG", "propagate": True, }, }, } logging.config.dictConfig(LOGGING_CONFIG) logger = logging.getLogger("root_module.sub1.sub2") logger.setLevel(logging.DEBUG) assert logger.propagate is True assert logger.getEffectiveLevel() == logging.DEBUG with caplog.at_level(logging.DEBUG): logger.debug("🔥 DEBUG msg") logger.info("📘 INFO msg") logger.warning("⚠️ WARNING msg") logger.error("❌ ERROR msg") logger.critical("💀 CRITICAL msg") print("🔥 caplog.messages:", caplog.messages) # Final assertion assert any("CRITICAL" in r or "💀" in r for r in caplog.messages)
执行pytest -s输出
tests/test_logs.py 2025-04-16 17:06:03,983 - root_module.sub1.sub2 - DEBUG - 🔥 DEBUG msg 2025-04-16 17:06:03,983 - root_module.sub1.sub2 - INFO - 📘 INFO msg 2025-04-16 17:06:03,983 - root_module.sub1.sub2 - WARNING - ⚠️ WARNING msg 2025-04-16 17:06:03,983 - root_module.sub1.sub2 - ERROR - ❌ ERROR msg 2025-04-16 17:06:03,983 - root_module.sub1.sub2 - CRITICAL - 💀 CRITICAL msg 🔥 caplog.messages: []
环境信息
- Pytest版本:8.3.5
- setup.cfg配置:
[tool:pytest] testpaths = tests
预期caplog.records包含全部5条日志,但实际为空列表。
问题原因
调用logging.config.dictConfig()时,你重新配置了root logger的handlers,仅保留了自定义的console handler,而Pytest的caplog插件原本会自动向root logger添加一个专属的日志handler,这个handler被你的配置覆盖移除了,所以caplog无法再捕获任何日志。
解决方案
方案1:在dictConfig中保留caplog的handler
在配置root logger的handlers时,把caplog的handler也包含进去。修改LOGGING_CONFIG中的root logger部分:
"loggers": { "": { # root logger "handlers": ["console"] + [h for h in logging.getLogger().handlers if hasattr(h, 'caplog')], "level": "DEBUG", "propagate": True, }, },
这样既能保留你的console输出,也不会移除caplog的handler。
方案2:避免重新配置root logger,只配置目标logger
如果不需要修改root logger的配置,可以只针对root_module.sub1.sub2这个logger进行配置,而不是覆盖root logger:
LOGGING_CONFIG = { "version": 1, "disable_existing_loggers": False, "formatters": { "default": { "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s", }, }, "handlers": { "console": { "class": "logging.StreamHandler", "formatter": "default", "level": "DEBUG", }, }, "loggers": { "root_module.sub1.sub2": { # 仅配置目标logger "handlers": ["console"], "level": "DEBUG", "propagate": True, }, }, }
这样root logger的caplog handler会被保留,日志会通过propagate传递到root logger,从而被caplog捕获。
方案3:在dictConfig后重新添加caplog的handler
如果必须重新配置root logger,可以在调用dictConfig之后,把caplog的handler重新添加到目标logger或root logger上:
logging.config.dictConfig(LOGGING_CONFIG) # 获取caplog的handler并添加到目标logger for handler in caplog.handlers: logger.addHandler(handler)
这样目标logger的日志会同时发送到console和caplog的handler,保证捕获生效。
内容的提问来源于stack exchange,提问作者Elgin Cahangirov

