diff --git a/changelog/7431.feature.rst b/changelog/7431.feature.rst new file mode 100644 index 000000000..24a64a2ae --- /dev/null +++ b/changelog/7431.feature.rst @@ -0,0 +1 @@ +``--log-disable`` CLI option added to disable individual loggers. diff --git a/doc/en/how-to/logging.rst b/doc/en/how-to/logging.rst index c99835a2b..9957a9bb8 100644 --- a/doc/en/how-to/logging.rst +++ b/doc/en/how-to/logging.rst @@ -55,6 +55,13 @@ These options can also be customized through ``pytest.ini`` file: log_format = %(asctime)s %(levelname)s %(message)s log_date_format = %Y-%m-%d %H:%M:%S +Specific loggers can be disabled via ``--log-disable={logger_name}``. +This argument can be passed multiple times: + +.. code-block:: bash + + pytest --log-disable=main --log-disable=testing + Further it is possible to disable reporting of captured content (stdout, stderr and logs) on failed tests completely with: diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index f9091399f..4e3d12475 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -297,6 +297,13 @@ def pytest_addoption(parser: Parser) -> None: default=None, help="Auto-indent multiline messages passed to the logging module. Accepts true|on, false|off or an integer.", ) + group.addoption( + "--log-disable", + action="append", + default=[], + dest="logger_disable", + help="Disable a logger by name. Can be passed multipe times.", + ) _HandlerType = TypeVar("_HandlerType", bound=logging.Handler) @@ -594,6 +601,15 @@ class LoggingPlugin: get_option_ini(config, "log_auto_indent"), ) self.log_cli_handler.setFormatter(log_cli_formatter) + self._disable_loggers(loggers_to_disable=config.option.logger_disable) + + def _disable_loggers(self, loggers_to_disable: List[str]) -> None: + if not loggers_to_disable: + return + + for name in loggers_to_disable: + logger = logging.getLogger(name) + logger.disabled = True def _create_formatter(self, log_format, log_date_format, auto_indent): # Color option doesn't exist if terminal plugin is disabled. diff --git a/testing/logging/test_reporting.py b/testing/logging/test_reporting.py index 323ff7b24..3cd8599b4 100644 --- a/testing/logging/test_reporting.py +++ b/testing/logging/test_reporting.py @@ -1165,3 +1165,72 @@ def test_log_file_cli_subdirectories_are_successfully_created( result = pytester.runpytest("--log-file=foo/bar/logf.log") assert "logf.log" in os.listdir(expected) assert result.ret == ExitCode.OK + + +def test_disable_loggers(testdir): + testdir.makepyfile( + """ + import logging + import os + disabled_log = logging.getLogger('disabled') + test_log = logging.getLogger('test') + def test_logger_propagation(caplog): + with caplog.at_level(logging.DEBUG): + disabled_log.warning("no log; no stderr") + test_log.debug("Visible text!") + assert caplog.record_tuples == [('test', 10, 'Visible text!')] + """ + ) + result = testdir.runpytest("--log-disable=disabled", "-s") + assert result.ret == ExitCode.OK + assert not result.stderr.lines + + +def test_disable_loggers_does_not_propagate(testdir): + testdir.makepyfile( + """ + import logging + import os + + parent_logger = logging.getLogger("parent") + child_logger = parent_logger.getChild("child") + + def test_logger_propagation_to_parent(caplog): + with caplog.at_level(logging.DEBUG): + parent_logger.warning("some parent logger message") + child_logger.warning("some child logger message") + assert len(caplog.record_tuples) == 1 + assert caplog.record_tuples[0][0] == "parent" + assert caplog.record_tuples[0][2] == "some parent logger message" + """ + ) + + result = testdir.runpytest("--log-disable=parent.child", "-s") + assert result.ret == ExitCode.OK + assert not result.stderr.lines + + +def test_log_disabling_works_with_log_cli(testdir): + testdir.makepyfile( + """ + import logging + disabled_log = logging.getLogger('disabled') + test_log = logging.getLogger('test') + + def test_log_cli_works(caplog): + test_log.info("Visible text!") + disabled_log.warning("This string will be suppressed.") + """ + ) + result = testdir.runpytest( + "--log-cli-level=DEBUG", + "--log-disable=disabled", + ) + assert result.ret == ExitCode.OK + result.stdout.fnmatch_lines( + "INFO test:test_log_disabling_works_with_log_cli.py:6 Visible text!" + ) + result.stdout.no_fnmatch_line( + "WARNING disabled:test_log_disabling_works_with_log_cli.py:7 This string will be suppressed." + ) + assert not result.stderr.lines