logging: Make it possible to add cli colors to custom log levels

Closes #8803
PR #8804

Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
Co-authored-by: Terje Runde <terje.runde@flir.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Terje Runde 2021-08-09 15:48:47 +02:00 committed by GitHub
parent df033f3ab1
commit 2439729413
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 61 additions and 12 deletions

View File

@ -309,6 +309,7 @@ Tanvi Mehta
Tarcisio Fischer Tarcisio Fischer
Tareq Alayan Tareq Alayan
Ted Xiao Ted Xiao
Terje Runde
Thomas Grainger Thomas Grainger
Thomas Hisch Thomas Hisch
Tim Hoffmann Tim Hoffmann

View File

@ -0,0 +1,9 @@
It is now possible to add colors to custom log levels on cli log.
By using :func:`add_color_level <_pytest.logging.add_color_level` from a ``pytest_configure`` hook, colors can be added::
logging_plugin = config.pluginmanager.get_plugin('logging-plugin')
logging_plugin.log_cli_handler.formatter.add_color_level(logging.INFO, 'cyan')
logging_plugin.log_cli_handler.formatter.add_color_level(logging.SPAM, 'blue')
See :ref:`log_colors` for more information.

View File

@ -219,6 +219,30 @@ option names are:
You can call ``set_log_path()`` to customize the log_file path dynamically. This functionality You can call ``set_log_path()`` to customize the log_file path dynamically. This functionality
is considered **experimental**. is considered **experimental**.
.. _log_colors:
Customizing Colors
^^^^^^^^^^^^^^^^^^
Log levels are colored if colored terminal output is enabled. Changing
from default colors or putting color on custom log levels is supported
through ``add_color_level()``. Example:
.. code-block:: python
@pytest.hookimpl
def pytest_configure(config):
logging_plugin = config.pluginmanager.get_plugin("logging-plugin")
# Change color on existing log level
logging_plugin.log_cli_handler.formatter.add_color_level(logging.INFO, "cyan")
# Add color to a custom log level (a custom log level `SPAM` is already set up)
logging_plugin.log_cli_handler.formatter.add_color_level(logging.SPAM, "blue")
.. warning::
This feature and its API are considered **experimental** and might change
between releases without a deprecation notice.
.. _log_release_notes: .. _log_release_notes:
Release notes Release notes

View File

@ -63,28 +63,43 @@ class ColoredLevelFormatter(logging.Formatter):
def __init__(self, terminalwriter: TerminalWriter, *args, **kwargs) -> None: def __init__(self, terminalwriter: TerminalWriter, *args, **kwargs) -> None:
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self._terminalwriter = terminalwriter
self._original_fmt = self._style._fmt self._original_fmt = self._style._fmt
self._level_to_fmt_mapping: Dict[int, str] = {} self._level_to_fmt_mapping: Dict[int, str] = {}
for level, color_opts in self.LOGLEVEL_COLOROPTS.items():
self.add_color_level(level, *color_opts)
def add_color_level(self, level: int, *color_opts: str) -> None:
"""Add or update color opts for a log level.
:param level:
Log level to apply a style to, e.g. ``logging.INFO``.
:param color_opts:
ANSI escape sequence color options. Capitalized colors indicates
background color, i.e. ``'green', 'Yellow', 'bold'`` will give bold
green text on yellow background.
.. warning::
This is an experimental API.
"""
assert self._fmt is not None assert self._fmt is not None
levelname_fmt_match = self.LEVELNAME_FMT_REGEX.search(self._fmt) levelname_fmt_match = self.LEVELNAME_FMT_REGEX.search(self._fmt)
if not levelname_fmt_match: if not levelname_fmt_match:
return return
levelname_fmt = levelname_fmt_match.group() levelname_fmt = levelname_fmt_match.group()
for level, color_opts in self.LOGLEVEL_COLOROPTS.items(): formatted_levelname = levelname_fmt % {"levelname": logging.getLevelName(level)}
formatted_levelname = levelname_fmt % {
"levelname": logging.getLevelName(level)
}
# add ANSI escape sequences around the formatted levelname # add ANSI escape sequences around the formatted levelname
color_kwargs = {name: True for name in color_opts} color_kwargs = {name: True for name in color_opts}
colorized_formatted_levelname = terminalwriter.markup( colorized_formatted_levelname = self._terminalwriter.markup(
formatted_levelname, **color_kwargs formatted_levelname, **color_kwargs
) )
self._level_to_fmt_mapping[level] = self.LEVELNAME_FMT_REGEX.sub( self._level_to_fmt_mapping[level] = self.LEVELNAME_FMT_REGEX.sub(
colorized_formatted_levelname, self._fmt colorized_formatted_levelname, self._fmt
) )
def format(self, record: logging.LogRecord) -> str: def format(self, record: logging.LogRecord) -> str:
fmt = self._level_to_fmt_mapping.get(record.levelno, self._original_fmt) fmt = self._level_to_fmt_mapping.get(record.levelno, self._original_fmt)