diff --git a/_pytest/logging.py b/_pytest/logging.py index ecd50f12844..88a50e22cb1 100644 --- a/_pytest/logging.py +++ b/_pytest/logging.py @@ -82,13 +82,14 @@ def add_option_ini(option, dest, default=None, type=None, **kwargs): @contextmanager -def catching_logs(handler, formatter=None, level=logging.NOTSET): +def catching_logs(handler, formatter=None, level=None): """Context manager that prepares the whole logging machinery properly.""" root_logger = logging.getLogger() if formatter is not None: handler.setFormatter(formatter) - handler.setLevel(level) + if level is not None: + handler.setLevel(level) # Adding the same handler twice would confuse logging system. # Just don't do that. @@ -96,12 +97,14 @@ def catching_logs(handler, formatter=None, level=logging.NOTSET): if add_new_handler: root_logger.addHandler(handler) - orig_level = root_logger.level - root_logger.setLevel(min(orig_level, level)) + if level is not None: + orig_level = root_logger.level + root_logger.setLevel(level) try: yield handler finally: - root_logger.setLevel(orig_level) + if level is not None: + root_logger.setLevel(orig_level) if add_new_handler: root_logger.removeHandler(handler) @@ -166,14 +169,10 @@ def clear(self): def set_level(self, level, logger=None): """Sets the level for capturing of logs. - By default, the level is set on the handler used to capture - logs. Specify a logger name to instead set the level of any - logger. + :param int level: the logger to level. + :param str logger: the logger to update the level. If not given, the root logger level is updated. """ - if logger is None: - logger = self.handler - else: - logger = logging.getLogger(logger) + logger = logging.getLogger(logger) logger.setLevel(level) @contextmanager @@ -259,6 +258,7 @@ def __init__(self, config): self.formatter = logging.Formatter( get_option_ini(config, 'log_format'), get_option_ini(config, 'log_date_format')) + self.log_level = get_actual_log_level(config, 'log_level') if config.getini('log_cli'): log_cli_handler = logging.StreamHandler(sys.stderr) @@ -269,8 +269,7 @@ def __init__(self, config): log_cli_formatter = logging.Formatter( log_cli_format, datefmt=log_cli_date_format) - log_cli_level = get_actual_log_level( - config, 'log_cli_level', 'log_level') or logging.WARNING + log_cli_level = get_actual_log_level(config, 'log_cli_level', 'log_level') self.log_cli_handler = log_cli_handler # needed for a single unittest self.live_logs_context = catching_logs(log_cli_handler, formatter=log_cli_formatter, @@ -281,8 +280,7 @@ def __init__(self, config): log_file = get_option_ini(config, 'log_file') if log_file: - self.log_file_level = get_actual_log_level( - config, 'log_file_level') or logging.WARNING + self.log_file_level = get_actual_log_level(config, 'log_file_level') log_file_format = get_option_ini( config, 'log_file_format', 'log_format') @@ -303,7 +301,7 @@ def __init__(self, config): def _runtest_for(self, item, when): """Implements the internals of pytest_runtest_xxx() hook.""" with catching_logs(LogCaptureHandler(), - formatter=self.formatter) as log_handler: + formatter=self.formatter, level=self.log_level) as log_handler: if not hasattr(item, 'catch_log_handlers'): item.catch_log_handlers = {} item.catch_log_handlers[when] = log_handler diff --git a/testing/logging/test_reporting.py b/testing/logging/test_reporting.py index d10967a5e98..1a896514a4b 100644 --- a/testing/logging/test_reporting.py +++ b/testing/logging/test_reporting.py @@ -35,7 +35,7 @@ def test_foo(): logger.info('text going to logger') assert False ''') - result = testdir.runpytest() + result = testdir.runpytest('--log-level=INFO') assert result.ret == 1 result.stdout.fnmatch_lines(['*- Captured *log call -*', '*text going to logger*']) @@ -58,7 +58,7 @@ def test_foo(): logger.info('text going to logger from call') assert False ''') - result = testdir.runpytest() + result = testdir.runpytest('--log-level=INFO') assert result.ret == 1 result.stdout.fnmatch_lines(['*- Captured *log setup -*', '*text going to logger from setup*', @@ -79,7 +79,7 @@ def teardown_function(function): logger.info('text going to logger from teardown') assert False ''') - result = testdir.runpytest() + result = testdir.runpytest('--log-level=INFO') assert result.ret == 1 result.stdout.fnmatch_lines(['*- Captured *log call -*', '*text going to logger from call*', @@ -168,9 +168,9 @@ def test_log_cli_default_level(testdir): import logging def test_log_cli(request): plugin = request.config.pluginmanager.getplugin('logging-plugin') - assert plugin.log_cli_handler.level == logging.WARNING - logging.getLogger('catchlog').info("This log message won't be shown") - logging.getLogger('catchlog').warning("This log message will be shown") + assert plugin.log_cli_handler.level == logging.NOTSET + logging.getLogger('catchlog').info("INFO message won't be shown") + logging.getLogger('catchlog').warning("WARNING message will be shown") print('PASSED') ''') testdir.makeini(''' @@ -185,15 +185,9 @@ def test_log_cli(request): 'test_log_cli_default_level.py PASSED', ]) result.stderr.fnmatch_lines([ - "* This log message will be shown" + '*WARNING message will be shown*', ]) - for line in result.errlines: - try: - assert "This log message won't be shown" in line - pytest.fail("A log message was shown and it shouldn't have been") - except AssertionError: - continue - + assert "INFO message won't be shown" not in result.stderr.str() # make sure that that we get a '0' exit code for the testsuite assert result.ret == 0 @@ -307,7 +301,7 @@ def test_log_file(request): log_file = testdir.tmpdir.join('pytest.log').strpath - result = testdir.runpytest('-s', '--log-file={0}'.format(log_file)) + result = testdir.runpytest('-s', '--log-file={0}'.format(log_file), '--log-file-level=WARNING') # fnmatch_lines does an assertion internally result.stdout.fnmatch_lines([ @@ -356,6 +350,16 @@ def test_log_file(request): assert "This log message won't be shown" not in contents +def test_log_level_not_changed_by_default(testdir): + testdir.makepyfile(''' + import logging + def test_log_file(): + assert logging.getLogger().level == logging.WARNING + ''') + result = testdir.runpytest('-s') + result.stdout.fnmatch_lines('* 1 passed in *') + + def test_log_file_ini(testdir): log_file = testdir.tmpdir.join('pytest.log').strpath @@ -363,6 +367,7 @@ def test_log_file_ini(testdir): """ [pytest] log_file={0} + log_file_level=WARNING """.format(log_file)) testdir.makepyfile(''' import pytest