Skip to content

Commit

Permalink
Add support for logging in collection-phase
Browse files Browse the repository at this point in the history
The logging plugin does not output log messages generated during the
collection-phase when live-logging is enabled. This fixes this.

Fixes pytest-dev#3964
  • Loading branch information
twmr committed Sep 15, 2018
1 parent 53c9124 commit 9b7120a
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 9 deletions.
2 changes: 2 additions & 0 deletions changelog/3964.rst
@@ -0,0 +1,2 @@
Log messages generated in the collection phase are now shown when
live-logging is enabled.
32 changes: 23 additions & 9 deletions src/_pytest/logging.py
Expand Up @@ -414,7 +414,6 @@ def __init__(self, config):
else:
self.log_file_handler = None

# initialized during pytest_runtestloop
self.log_cli_handler = None

def _log_cli_enabled(self):
Expand All @@ -425,6 +424,22 @@ def _log_cli_enabled(self):
"--log-cli-level"
) is not None or self._config.getini("log_cli")

@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_collection(self):
# This has to be called before the first log message is logged,
# so we can access the terminal reporter plugin.
self._setup_cli_logging()

# TODO write to file support needed?
with self.live_logs_context():
with catching_logs(
LogCaptureHandler(), formatter=self.formatter, level=self.log_level
):
if self.log_cli_handler:
self.log_cli_handler.set_when("collection")

yield

@contextmanager
def _runtest_for(self, item, when):
"""Implements the internals of pytest_runtest_xxx() hook."""
Expand Down Expand Up @@ -484,8 +499,7 @@ def pytest_runtest_logfinish(self):
@pytest.hookimpl(hookwrapper=True)
def pytest_runtestloop(self, session):
"""Runs all collected test items."""
self._setup_cli_logging()
with self.live_logs_context:
with self.live_logs_context():
if self.log_file_handler is not None:
with closing(self.log_file_handler):
with catching_logs(
Expand All @@ -496,10 +510,7 @@ def pytest_runtestloop(self, session):
yield # run all the tests

def _setup_cli_logging(self):
"""Sets up the handler and logger for the Live Logs feature, if enabled.
This must be done right before starting the loop so we can access the terminal reporter plugin.
"""
"""Sets up the handler and logger for the Live Logs feature, if enabled."""
terminal_reporter = self._config.pluginmanager.get_plugin("terminalreporter")
if self._log_cli_enabled() and terminal_reporter is not None:
capture_manager = self._config.pluginmanager.get_plugin("capturemanager")
Expand Down Expand Up @@ -529,11 +540,14 @@ def _setup_cli_logging(self):
self._config, "log_cli_level", "log_level"
)
self.log_cli_handler = log_cli_handler
self.live_logs_context = catching_logs(
self.live_logs_context = lambda: catching_logs(
log_cli_handler, formatter=log_cli_formatter, level=log_cli_level
)
else:
self.live_logs_context = dummy_context_manager()
self.live_logs_context = lambda: dummy_context_manager()
# Note that the lambda for the live_logs_context is needed because
# live_logs_context can otherwise not be entered multiple times due
# to limitations of contextlib.contextmanager


class _LiveLoggingStreamHandler(logging.StreamHandler):
Expand Down
20 changes: 20 additions & 0 deletions testing/logging/test_reporting.py
Expand Up @@ -908,3 +908,23 @@ def section(self, *args, **kwargs):
else:
assert MockCaptureManager.calls == []
assert out_file.getvalue() == "\nsome message\n"


def test_collection_live_logging(testdir):
testdir.makepyfile(
"""
import logging
logging.getLogger().info("Normal message")
"""
)

result = testdir.runpytest("--log-cli-level=INFO")
result.stdout.fnmatch_lines(
[
"collecting*",
"*--- live log collection ---*",
"*Normal message*",
"collected 0 items",
]
)

0 comments on commit 9b7120a

Please sign in to comment.