Repository navigation
Handling of loggers with propagate=False #3697
Description
Activity
- addedplatform: macmac platform-specific problemmac platform-specific problem
on Jul 19, 2018 I suggest to remove the auto-added label os:mac, as this is an issue on Ubuntu 16.04 as well.
- removedplatform: macmac platform-specific problemmac platform-specific problem
on Jul 19, 2018 - addedtype: enhancementnew feature or API change, should be merged into features branchnew feature or API change, should be merged into features branchplugin: loggingrelated to the logging builtin pluginrelated to the logging builtin plugin
on Oct 20, 2018 When using the
caplog.recordsorcaplog.record_tuplesthere is nothing captured for a logger that does not propagate up to therootlogger. When testing the log output, is there any work-around for this or does a solution require mocking of logging handlers?Reacted by Eduardo Mucelli, momo and Tuukka MustonenNot sure, we only capture what we get from the
rootlogger... @Thisch might give some input here.So the simplest work around in a pytest test is to enable the propagation during the test, only for any test that must check the presence or content of a log message (other tests might confirm that propagation is disabled by default). If I had time, a PR on the docs might help, but here is what I have time for now, e.g.
def test_app_logger_content(caplog): logger = logging.getLogger('app') # 'app' is already configured without propagation logger.propagate = True # log capture only works for propagation to the root logger logger.info('foo') logger.error('err') assert caplog.record_tuples == [ ('app', logging.INFO, 'foo'), ('app', logging.ERROR, 'err') ] def test_app_logger_does_not_propagate(caplog): logger = logging.getLogger('app') assert not logger.propagate logger.info('foo') logger.error('err') assert not caplog.records
Reacted by Zhuyi Xue, Sergi Soler i Segura, Ashley Blackmore, albedojohn, Eduardo Mucelli, Piotr G, Daniel Mardini and Edgar Ramírez MondragónIf you have only a few loggers which don't propagate, probably you can use a session-scoped autouse fixture to enable propagation for those loggers.
I think this is an issue. Enabling propagating for logger results in a possible false positive result of a test.
Say, I would like to ensure a message goes to exact logger I want it to go. There is no way at the moment to test that.
It would be helpful to be able to tell to
caplogwhat logger I'm interested exactly. So, it will capture that logger and everything that goes there.Reacted by Ruaridh Williamson, wderose, auxsvr, 1Mark, momo, abakadaba and warsawnv@lig If we add support for specifying a logger in
_pytest.logging.catching_logsyou can use the following code to tell caplog the logger you're interested inimport logging from _pytest.logging import catching_logs def test_app_logger_content_nocatchlog(caplog): logger = logging.getLogger('app') logger.propagate = False logger.info('foo') logger.error('err') assert ('app', logging.INFO, 'foo') not in caplog.record_tuples assert ('app', logging.ERROR, 'err') not in caplog.record_tuples def test_app_logger_content_catchlog(caplog): logger = logging.getLogger('app') logger.propagate = False caplog.set_level(logging.INFO) with catching_logs(caplog.handler, logger=logger): logger.info('foo') logger.error('err') assert caplog.record_tuples == [ ('app', logging.INFO, 'foo'), ('app', logging.ERROR, 'err') ]
Reacted by Varun Shahdadpuri@Thisch looks really useful
one thing. I'd assume this would work either
def test_app_logger_content_catchlog(caplog): logger = logging.getLogger('app') logger.propagate = False caplog.set_level(logging.INFO) with catching_logs(caplog.handler, logger=logger): logger.info('foo') logger.error('err') assert caplog.record_tuples == [ ('app', logging.INFO, 'foo'), ('app', logging.ERROR, 'err'), ]
notice that
caplogkeeps records aftercatching_logscontext manager has exitedYeh it would be ideal if there was a proper replacement in pytest for
unittest.TestCase.assertLogs, i.e.with pytest.logs(...
Can you not just copy what that does, i.e. temporarily overriding the propagate and handler?class _AssertLogsContext(_BaseTestCaseContext): """A context manager used to implement TestCase.assertLogs().""" LOGGING_FORMAT = "%(levelname)s:%(name)s:%(message)s" def __init__(self, test_case, logger_name, level): _BaseTestCaseContext.__init__(self, test_case) self.logger_name = logger_name if level: self.level = logging._nameToLevel.get(level, level) else: self.level = logging.INFO self.msg = None def __enter__(self): if isinstance(self.logger_name, logging.Logger): logger = self.logger = self.logger_name else: logger = self.logger = logging.getLogger(self.logger_name) formatter = logging.Formatter(self.LOGGING_FORMAT) handler = _CapturingHandler() handler.setFormatter(formatter) self.watcher = handler.watcher self.old_handlers = logger.handlers[:] self.old_level = logger.level self.old_propagate = logger.propagate logger.handlers = [handler] logger.setLevel(self.level) logger.propagate = False return handler.watcher def __exit__(self, exc_type, exc_value, tb): self.logger.handlers = self.old_handlers self.logger.propagate = self.old_propagate self.logger.setLevel(self.old_level) if exc_type is not None: # let unexpected exceptions pass through return False if len(self.watcher.records) == 0: self._raiseFailure( "no logs of level {} or higher triggered on {}" .format(logging.getLevelName(self.level), self.logger.name))
This is unfortunately still relevant and the workaround mentioned above by @lig and @Thisch, using the internal
catching_logs()function is no longer available. The "logger" parameter was removed in 3862b0b.
Are there any other ways to achieve the effect? I'm a bit puzzled how to fix our unit tests for a project where there is a logger which doesn't propagate to root.Reacted by Serge Matveenko and Albert Tugushev19 remaining items
- added 6 commits that reference this issue
on Jun 18, 2026 - added a commit that references this issue
on Jun 23, 2026 - added a commit that references this issue
on Jul 4, 2026 - added 2 commits that reference this issue
on Jul 6, 2026 - added a commit that references this issue
on Jul 19, 2026 - added a commit that references this issue
on Aug 17, 2026 - added a commit that references this issue
on Aug 31, 2026
Hi,
There is a 2-year old issue on the old repo and inspired by #3003 I thought it might be easier if I at least add some reference here:
eisensheng/pytest-catchlog#44