mbednarski
mbednarski

Reputation: 798

Disabling specific logger in pytest

In my project I'm parsing some PDF files using pdfplumber. During tests execution (pytest) I sometimes would like to see logs from my code for debugging purposes. This can be done by setting --log-cli-level=DEBUG. However, this turns on messages from all code, also pdfplumber - which is very verbose and makes debugging difficult. Is there a way to selectively enable/disable loggers during test run?

pytest 4.6.3
python 3.7.3

Thanks for help!

Upvotes: 11

Views: 13039

Answers (4)

Samuel Rodríguez
Samuel Rodríguez

Reputation: 177

This is more targeted than most answers here:

def test_foo(caplog):
    caplog.set_level(logging.CRITICAL, logger="root.baz")
    pass

where "root.baz" is to be replaced with the name of the logger (e.g. "uvicorn"). or if using the default one it can just be ignored:

def test_bar(caplog):
    with caplog.at_level(logging.INFO):
        pass

or, if you want even more fine-grained control, you can use caplog as a context manager:

def test_bar(caplog):
    # < code with normal logs
    ...
    # >

    # < code with target logging level
    with caplog.at_level(logging.CRITICAL, logger="root.baz"):
        pass
    # >

See the full docs.

Upvotes: 0

Alexander Fasching
Alexander Fasching

Reputation: 621

Pytest does not support this by default, but you can add a custom option to your conftest.py to turn off specific loggers.

import pytest
import logging

def pytest_addoption(parser):
    """Add a command line option to disable logger."""
    parser.addoption(
        "--log-disable", action="append", default=[], help="disable specific loggers"
    )

def pytest_configure(config):
    """Disable the loggers."""
    for name in config.getoption("--log-disable", default=[]):
        logger = logging.getLogger(name)
        logger.propagate = False

For pytest 7.3.0 and later:

pytest now supports --log-disable without a custom option. https://docs.pytest.org/en/7.4.x/how-to/logging.html

Upvotes: 10

Att Righ
Att Righ

Reputation: 1799

I has doing this on a per-test basis with this:

ddef with_logs(**loggers):
    def all_loggers():
        import logging_tree

        def visit(node):
            yield node[0]
            for child in node[2]:
                yield from visit(child)

        return set(visit(logging_tree.nodes.tree()))

    unknown_loggers = set(loggers) - all_loggers()
    if unknown_loggers:
        raise ValueError(f"Unknown loggers: {unknown_loggers}")

    def decorate(f):
        def disable(logger):
            logging.getLogger(logger).setLevel(logging.INFO)

        def enable(logger):
            logging.getLogger(logger).setLevel(logging.DEBUG)

        disabled_loggers = []
        for k, v in loggers.items():
            if not v:
                disabled_loggers.append(k)

        @functools.wraps(f)
        def wrapped(*args, **kwargs):
            for logger in disabled_loggers:
                disable(logger)
            try:
                return f(*args, **kwargs)
            finally:
                for logger in disabled_loggers:
                    enable(logger)

        return wrapped

    return decorate

...

@with_logs(logger1=False, logger2=False)
def test_x():
    


Upvotes: 0

The Pjot
The Pjot

Reputation: 1859

No, pytest won't be able to do this for you as far as I know and can see. What I can think of is introducing your own environment variables and change log levels accordingly. Something like this:

import os
import logging


logger = logging.getLogger('mylogger')

if os.environ.get('mylogger_level'):
    logger.setLevel(os.environ.get('mylogger_level'))

Upvotes: 2

Related Questions