Skip to content

Commit

Permalink
No longer change the level of any logger unless requested explicitly
Browse files Browse the repository at this point in the history
  • Loading branch information
nicoddemus committed Jan 20, 2018
1 parent 5ad1313 commit 8dcd271
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 32 deletions.
32 changes: 15 additions & 17 deletions _pytest/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,26 +82,29 @@ 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.
add_new_handler = handler not in root_logger.handlers

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)

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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,
Expand All @@ -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')
Expand All @@ -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
Expand Down
35 changes: 20 additions & 15 deletions testing/logging/test_reporting.py
Original file line number Diff line number Diff line change
Expand Up @@ -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*'])
Expand All @@ -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*',
Expand All @@ -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*',
Expand Down Expand Up @@ -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('''
Expand All @@ -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

Expand Down Expand Up @@ -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([
Expand Down Expand Up @@ -356,13 +350,24 @@ 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

testdir.makeini(
"""
[pytest]
log_file={0}
log_file_level=WARNING
""".format(log_file))
testdir.makepyfile('''
import pytest
Expand Down

0 comments on commit 8dcd271

Please sign in to comment.