pytest-catchlog icon indicating copy to clipboard operation
pytest-catchlog copied to clipboard

Handling of loggers with propagate=False

Open blueyed opened this issue 8 years ago • 5 comments

What is the recommended way to handle loggers that are configured with propagate=False, which appears to cause the message not to reach the caplog handler?

I can manually change the propagate setting in the test (setup), but there might be a better way?!

import logging
logging.getLogger('project.app').propagate = True

Could pytest-catchlog take care of this automatically, e.g. by attaching its handler to all loggers with propagate=False?

Would it be feasible to have something like caplog.with_logger, where you could pass in a logger name and caplog would attach it's handler there directly?

blueyed avatar May 30 '16 13:05 blueyed

This seems to do the trick:

@pytest.yield_fixture
def caplog(caplog):
    import logging

    restore = []
    for logger in logging.Logger.manager.loggerDict.values():
        try:
            if not logger.propagate:
                restore += [(logger, logger.propagate)]
                logger.propagate = True
        except AttributeError:
            pass
    yield caplog

    for logger, value in restore:
        logger.propagate = value

blueyed avatar May 30 '16 19:05 blueyed

It might be better to add the caplog handler to the loggers with propagate=False (logger.addHandler(caplog.handler)), but that resulted in some ValueError: I/O operation on closed file, and the handler is probably not meant to be used like that.

blueyed avatar Jun 17 '16 17:06 blueyed

Sound good! I wish I had more spare time time to investigate that...

abusalimov avatar Jun 17 '16 17:06 abusalimov

FWIW, this is the current function I am using:

@pytest.yield_fixture
def caplog(caplog):
    import logging

    restore = []
    for logger in logging.Logger.manager.loggerDict.values():
        try:
            if not logger.propagate:
                logger.propagate = True
                restore += [logger]
        except AttributeError:
            pass
    yield caplog
    for logger in restore:
        logger.propagate = False

blueyed avatar Jul 07 '16 14:07 blueyed

My solution is:

@contextlib.contextmanager
def caplog_for_logger(caplog, logger_name):
    caplog.clear()
    caplog.set_level(logging.CRITICAL)  # Mute the root-logger
    logger = logging.getLogger(logger_name)
    logger.addHandler(caplog.handler)
    yield
    logger.removeHandler(caplog.handler)

def test_foobar(self, caplog):
        with caplog_for_logger(caplog, 'my-logger-name):
            # testing ....

marc1n avatar May 30 '18 16:05 marc1n