Skip to content

Commit 2439729

Browse files
terjrnicoddemusTerje Rundepre-commit-ci[bot]
authored
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>
1 parent df033f3 commit 2439729

File tree

4 files changed

+62
-13
lines changed

4 files changed

+62
-13
lines changed

AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ Tanvi Mehta
309309
Tarcisio Fischer
310310
Tareq Alayan
311311
Ted Xiao
312+
Terje Runde
312313
Thomas Grainger
313314
Thomas Hisch
314315
Tim Hoffmann

changelog/8803.improvement.rst

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

doc/en/how-to/logging.rst

+24
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,30 @@ option names are:
219219
You can call ``set_log_path()`` to customize the log_file path dynamically. This functionality
220220
is considered **experimental**.
221221

222+
.. _log_colors:
223+
224+
Customizing Colors
225+
^^^^^^^^^^^^^^^^^^
226+
227+
Log levels are colored if colored terminal output is enabled. Changing
228+
from default colors or putting color on custom log levels is supported
229+
through ``add_color_level()``. Example:
230+
231+
.. code-block:: python
232+
233+
@pytest.hookimpl
234+
def pytest_configure(config):
235+
logging_plugin = config.pluginmanager.get_plugin("logging-plugin")
236+
237+
# Change color on existing log level
238+
logging_plugin.log_cli_handler.formatter.add_color_level(logging.INFO, "cyan")
239+
240+
# Add color to a custom log level (a custom log level `SPAM` is already set up)
241+
logging_plugin.log_cli_handler.formatter.add_color_level(logging.SPAM, "blue")
242+
.. warning::
243+
244+
This feature and its API are considered **experimental** and might change
245+
between releases without a deprecation notice.
222246
.. _log_release_notes:
223247

224248
Release notes

src/_pytest/logging.py

+28-13
Original file line numberDiff line numberDiff line change
@@ -63,28 +63,43 @@ class ColoredLevelFormatter(logging.Formatter):
6363

6464
def __init__(self, terminalwriter: TerminalWriter, *args, **kwargs) -> None:
6565
super().__init__(*args, **kwargs)
66+
self._terminalwriter = terminalwriter
6667
self._original_fmt = self._style._fmt
6768
self._level_to_fmt_mapping: Dict[int, str] = {}
6869

70+
for level, color_opts in self.LOGLEVEL_COLOROPTS.items():
71+
self.add_color_level(level, *color_opts)
72+
73+
def add_color_level(self, level: int, *color_opts: str) -> None:
74+
"""Add or update color opts for a log level.
75+
76+
:param level:
77+
Log level to apply a style to, e.g. ``logging.INFO``.
78+
:param color_opts:
79+
ANSI escape sequence color options. Capitalized colors indicates
80+
background color, i.e. ``'green', 'Yellow', 'bold'`` will give bold
81+
green text on yellow background.
82+
83+
.. warning::
84+
This is an experimental API.
85+
"""
86+
6987
assert self._fmt is not None
7088
levelname_fmt_match = self.LEVELNAME_FMT_REGEX.search(self._fmt)
7189
if not levelname_fmt_match:
7290
return
7391
levelname_fmt = levelname_fmt_match.group()
7492

75-
for level, color_opts in self.LOGLEVEL_COLOROPTS.items():
76-
formatted_levelname = levelname_fmt % {
77-
"levelname": logging.getLevelName(level)
78-
}
79-
80-
# add ANSI escape sequences around the formatted levelname
81-
color_kwargs = {name: True for name in color_opts}
82-
colorized_formatted_levelname = terminalwriter.markup(
83-
formatted_levelname, **color_kwargs
84-
)
85-
self._level_to_fmt_mapping[level] = self.LEVELNAME_FMT_REGEX.sub(
86-
colorized_formatted_levelname, self._fmt
87-
)
93+
formatted_levelname = levelname_fmt % {"levelname": logging.getLevelName(level)}
94+
95+
# add ANSI escape sequences around the formatted levelname
96+
color_kwargs = {name: True for name in color_opts}
97+
colorized_formatted_levelname = self._terminalwriter.markup(
98+
formatted_levelname, **color_kwargs
99+
)
100+
self._level_to_fmt_mapping[level] = self.LEVELNAME_FMT_REGEX.sub(
101+
colorized_formatted_levelname, self._fmt
102+
)
88103

89104
def format(self, record: logging.LogRecord) -> str:
90105
fmt = self._level_to_fmt_mapping.get(record.levelno, self._original_fmt)

0 commit comments

Comments
 (0)