Skip to content

Commit c8d23c2

Browse files
authored
logging: Improve formatting of multiline message (#5312)
logging: Improve formatting of multiline message
2 parents c5de8e8 + ea3ebec commit c8d23c2

File tree

3 files changed

+64
-0
lines changed

3 files changed

+64
-0
lines changed

changelog/5312.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improved formatting of multiline log messages in python3.

src/_pytest/logging.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,36 @@ def format(self, record):
7777
return super(ColoredLevelFormatter, self).format(record)
7878

7979

80+
if not six.PY2:
81+
# Formatter classes don't support format styles in PY2
82+
83+
class PercentStyleMultiline(logging.PercentStyle):
84+
"""A logging style with special support for multiline messages.
85+
86+
If the message of a record consists of multiple lines, this style
87+
formats the message as if each line were logged separately.
88+
"""
89+
90+
@staticmethod
91+
def _update_message(record_dict, message):
92+
tmp = record_dict.copy()
93+
tmp["message"] = message
94+
return tmp
95+
96+
def format(self, record):
97+
if "\n" in record.message:
98+
lines = record.message.splitlines()
99+
formatted = self._fmt % self._update_message(record.__dict__, lines[0])
100+
# TODO optimize this by introducing an option that tells the
101+
# logging framework that the indentation doesn't
102+
# change. This allows to compute the indentation only once.
103+
indentation = _remove_ansi_escape_sequences(formatted).find(lines[0])
104+
lines[0] = formatted
105+
return ("\n" + " " * indentation).join(lines)
106+
else:
107+
return self._fmt % record.__dict__
108+
109+
80110
def get_option_ini(config, *names):
81111
for name in names:
82112
ret = config.getoption(name) # 'default' arg won't work as expected
@@ -444,6 +474,9 @@ def _create_formatter(self, log_format, log_date_format):
444474
)
445475
else:
446476
formatter = logging.Formatter(log_format, log_date_format)
477+
478+
if not six.PY2:
479+
formatter._style = PercentStyleMultiline(formatter._style._fmt)
447480
return formatter
448481

449482
def _setup_cli_logging(self):

testing/logging/test_formatter.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
import logging
33

44
import py.io
5+
import six
56

7+
import pytest
68
from _pytest.logging import ColoredLevelFormatter
79

810

@@ -35,3 +37,31 @@ class option(object):
3537
formatter = ColoredLevelFormatter(tw, logfmt)
3638
output = formatter.format(record)
3739
assert output == ("dummypath 10 INFO Test Message")
40+
41+
42+
@pytest.mark.skipif(
43+
six.PY2, reason="Formatter classes don't support format styles in PY2"
44+
)
45+
def test_multiline_message():
46+
from _pytest.logging import PercentStyleMultiline
47+
48+
logfmt = "%(filename)-25s %(lineno)4d %(levelname)-8s %(message)s"
49+
50+
record = logging.LogRecord(
51+
name="dummy",
52+
level=logging.INFO,
53+
pathname="dummypath",
54+
lineno=10,
55+
msg="Test Message line1\nline2",
56+
args=(),
57+
exc_info=False,
58+
)
59+
# this is called by logging.Formatter.format
60+
record.message = record.getMessage()
61+
62+
style = PercentStyleMultiline(logfmt)
63+
output = style.format(record)
64+
assert output == (
65+
"dummypath 10 INFO Test Message line1\n"
66+
" line2"
67+
)

0 commit comments

Comments
 (0)