LoggingPlugin: Expose setting log_file_handler

- This patch allows to set log_file (path) from hook

Signed-off-by: Thomas Hisch
Signed-off-by: Andras Mitzki <andras.mitzki@balabit.com>
This commit is contained in:
Andras Mitzki 2019-02-08 22:35:50 +01:00
parent a131cd6c3b
commit e3824d23bc
5 changed files with 94 additions and 11 deletions

View File

@ -16,6 +16,7 @@ Allan Feldman
Aly Sivji
Anatoly Bubenkoff
Anders Hovmöller
Andras Mitzki
Andras Tim
Andrea Cimatoribus
Andreas Zeidler

View File

@ -0,0 +1 @@
With the help of new ``set_log_path()`` method there is a way to set ``log_file`` paths from hooks.

View File

@ -198,6 +198,9 @@ option names are:
* ``log_file_format``
* ``log_file_date_format``
You can call ``set_log_path()`` to customize the log_file path dynamically. This functionality
is considered **experimental**.
.. _log_release_notes:
Release notes

View File

@ -13,6 +13,7 @@ import six
import pytest
from _pytest.compat import dummy_context_manager
from _pytest.config import create_terminal_writer
from _pytest.pathlib import Path
DEFAULT_LOG_FORMAT = "%(filename)-25s %(lineno)4d %(levelname)-8s %(message)s"
@ -399,22 +400,21 @@ class LoggingPlugin(object):
)
self.log_level = get_actual_log_level(config, "log_level")
self.log_file_level = get_actual_log_level(config, "log_file_level")
self.log_file_format = get_option_ini(config, "log_file_format", "log_format")
self.log_file_date_format = get_option_ini(
config, "log_file_date_format", "log_date_format"
)
self.log_file_formatter = logging.Formatter(
self.log_file_format, datefmt=self.log_file_date_format
)
log_file = get_option_ini(config, "log_file")
if log_file:
self.log_file_level = get_actual_log_level(config, "log_file_level")
log_file_format = get_option_ini(config, "log_file_format", "log_format")
log_file_date_format = get_option_ini(
config, "log_file_date_format", "log_date_format"
)
# Each pytest runtests session will write to a clean logfile
self.log_file_handler = logging.FileHandler(
log_file, mode="w", encoding="UTF-8"
)
log_file_formatter = logging.Formatter(
log_file_format, datefmt=log_file_date_format
)
self.log_file_handler.setFormatter(log_file_formatter)
self.log_file_handler.setFormatter(self.log_file_formatter)
else:
self.log_file_handler = None
@ -468,6 +468,27 @@ class LoggingPlugin(object):
log_cli_handler, formatter=log_cli_formatter, level=log_cli_level
)
def set_log_path(self, fname):
"""Public method, which can set filename parameter for
Logging.FileHandler(). Also creates parent directory if
it does not exist.
.. warning::
Please considered as an experimental API.
"""
fname = Path(fname)
if not fname.is_absolute():
fname = Path(self._config.rootdir, fname)
if not fname.parent.exists():
fname.parent.mkdir(exist_ok=True, parents=True)
self.log_file_handler = logging.FileHandler(
str(fname), mode="w", encoding="UTF-8"
)
self.log_file_handler.setFormatter(self.log_file_formatter)
def _log_cli_enabled(self):
"""Return True if log_cli should be considered enabled, either explicitly
or because --log-cli-level was given in the command-line.
@ -490,6 +511,15 @@ class LoggingPlugin(object):
@contextmanager
def _runtest_for(self, item, when):
with self._runtest_for_main(item, when):
if self.log_file_handler is not None:
with catching_logs(self.log_file_handler, level=self.log_file_level):
yield
else:
yield
@contextmanager
def _runtest_for_main(self, item, when):
"""Implements the internals of pytest_runtest_xxx() hook."""
with catching_logs(
LogCaptureHandler(), formatter=self.formatter, level=self.log_level

View File

@ -1002,3 +1002,51 @@ def test_log_in_hooks(testdir):
assert "sessionstart" in contents
assert "runtestloop" in contents
assert "sessionfinish" in contents
def test_log_set_path(testdir):
report_dir_base = testdir.tmpdir.strpath
testdir.makeini(
"""
[pytest]
log_file_level = DEBUG
log_cli=true
"""
)
testdir.makeconftest(
"""
import os
import pytest
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_runtest_setup(item):
config = item.config
logging_plugin = config.pluginmanager.get_plugin("logging-plugin")
report_file = os.path.join({}, item._request.node.name)
logging_plugin.set_log_path(report_file)
yield
""".format(
repr(report_dir_base)
)
)
testdir.makepyfile(
"""
import logging
logger = logging.getLogger("testcase-logger")
def test_first():
logger.info("message from test 1")
assert True
def test_second():
logger.debug("message from test 2")
assert True
"""
)
testdir.runpytest()
with open(os.path.join(report_dir_base, "test_first"), "r") as rfh:
content = rfh.read()
assert "message from test 1" in content
with open(os.path.join(report_dir_base, "test_second"), "r") as rfh:
content = rfh.read()
assert "message from test 2" in content