Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to suppress third party logs in pytest

We've just switched from nose to pytest and there doesn't seem to be an option to suppress third party logging. In nose config, we had the following line:

logging-filter=-matplotlib,-chardet.charsetprober,-PIL,-fiona.env,-fiona._env

Some of those logs are very chatty, especially matplotlib and we don't want to see the output, just output from our logs.

I can't find an equivalent setting in pytest though. Is it possible? Am I missing something? Thanks.

like image 373
jon_two Avatar asked Feb 03 '23 14:02

jon_two


2 Answers

The way I do it is by creating a list of logger names for which logs have to be disabled in conftest.py.

For example, if I want to disable a logger called app, then I can write a conftest.py as below:

import logging

disable_loggers = ['app']

def pytest_configure():
    for logger_name in disable_loggers:
        logger = logging.getLogger(logger_name)
        logger.disabled = True

And then run my test:

import logging

def test_brake():
    logger = logging.getLogger("app")
    logger.error("Hello there")
    assert True

collecting ... collected 1 item

test_car.py::test_brake PASSED
[100%]

============================== 1 passed in 0.01s ===============================

Then, Hello there is not there because the logger with the name app was disabled in conftest.py.

However, if I change my logger name in the test to app2 and run the test again:

import logging

def test_brake():
    logger = logging.getLogger("app2")
    logger.error("Hello there")
    assert True

collecting ... collected 1 item

test_car.py::test_brake -------------------------------- live log call --------------------------------- ERROR app2:test_car.py:5 Hello there PASSED
[100%]

============================== 1 passed in 0.01s ===============================

As you can see, Hello there is in because a logger with app2 is not disabled.

Conclusion

Basically, you could do the same, but just add your undesired logger names to conftest.py as below:

import logging

disable_loggers = ['matplotlib', 'chardet.charsetprober', <add more yourself>]

def pytest_configure():
    for logger_name in disable_loggers:
        logger = logging.getLogger(logger_name)
        logger.disabled = True
like image 72
Anatolii Avatar answered Feb 06 '23 14:02

Anatolii


Apart from the ability to tune logging levels or not show any log output at all which I'm sure you've read in the docs, the only way that comes to mind is to configure your logging in general.

Assuming that all of those packages use the standard library logging facilities, you have various options of configuring what gets logged. Please take a look at the advanced tutorial for a good overview of your options.

If you don't want to configure logging for your application in general but only during testing, you might do so using the pytest_configure or pytest_sessionstart hooks which you might place in a conftest.py at the root of your test file hierarchy.

Then I see three options:

  1. The brute force way is to use the default behaviour of fileConfig or dictConfig to disable all existing loggers. In your conftest.py:

    import logging.config
    
    
    def pytest_sessionstart():
        # This is the default so an empty dictionary should work, too.
        logging.config.dictConfig({'disable_existing_loggers': True})
    
  2. The more subtle approach is to change the level of individual loggers or disable them. As an example:

    import logging.config
    
    
    def pytest_sessionstart():
        logging.config.dictConfig({
            'disable_existing_loggers': False,
            'loggers': {
                # Add any noisy loggers here with a higher loglevel.
                'matplotlib': {'level': 'ERROR'}
            }
        })
    
  3. Lastly, you can use the pytest_addoption hook to add a command line option similar to the one you mention. Again, at the root of your test hierarchy put the following in a conftest.py:

    def pytest_addoption(parser):
        parser.addoption(
            "--logging-filter",
            help="Provide a comma-separated list of logger names that will be "
            "disabled."
        )
    
    
    def pytest_sessionstart(pytestconfig):
        for logger_name in pytestconfig.getoption("--logging-filter").split(","):
            # Use `logger_name.trim()[1:]` if you want the `-name` CLI syntax.
            logger = logging.getLogger(logger_name.trim())
            logger.disabled = True
    

    You can then call pytest in the following way:

    pytest --logging-filter matplotlib,chardet,...
    

The default approach by pytest is to hide all logs but provide the caplog fixture to inspect log output in your test cases. This is quite powerful if you are looking for specific log lines. So the question is also why you need to see those logs at all in your test suite?

like image 39
Midnighter Avatar answered Feb 06 '23 15:02

Midnighter