Skip to content

Commit 4c846c8

Browse files
committed
WIP Support multiline log messages
1 parent e63c7a1 commit 4c846c8

File tree

1 file changed

+61
-16
lines changed

1 file changed

+61
-16
lines changed

src/_pytest/logging.py

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
from __future__ import division
44
from __future__ import print_function
55

6+
import copy
67
import logging
78
import re
9+
import sys
810
from contextlib import contextmanager
911

1012
import py
@@ -19,9 +21,10 @@
1921
DEFAULT_LOG_DATE_FORMAT = "%H:%M:%S"
2022

2123

22-
class ColoredLevelFormatter(logging.Formatter):
24+
class CustomFormatter(logging.Formatter):
2325
"""
24-
Colorize the %(levelname)..s part of the log format passed to __init__.
26+
Colorize the %(levelname)..s part of the log format passed to __init__
27+
and support proper aligning of multline log messages.
2528
"""
2629

2730
LOGLEVEL_COLOROPTS = {
@@ -36,11 +39,7 @@ class ColoredLevelFormatter(logging.Formatter):
3639
LEVELNAME_FMT_REGEX = re.compile(r"%\(levelname\)([+-]?\d*s)")
3740

3841
def __init__(self, terminalwriter, *args, **kwargs):
39-
super(ColoredLevelFormatter, self).__init__(*args, **kwargs)
40-
if six.PY2:
41-
self._original_fmt = self._fmt
42-
else:
43-
self._original_fmt = self._style._fmt
42+
super(CustomFormatter, self).__init__(*args, **kwargs)
4443
self._level_to_fmt_mapping = {}
4544

4645
levelname_fmt_match = self.LEVELNAME_FMT_REGEX.search(self._fmt)
@@ -62,13 +61,62 @@ def __init__(self, terminalwriter, *args, **kwargs):
6261
colorized_formatted_levelname, self._fmt
6362
)
6463

64+
def formatMessage(self, record):
65+
fmt = self._level_to_fmt_mapping.get(record.levelno, self._fmt)
66+
67+
rdict = copy.copy(record.__dict__)
68+
# improve this hacky code, which calculates the
69+
# 'continuation'-column for every record with a multiline message.
70+
rdict["message"] = "MARKER"
71+
logstr = self._fmt % rdict
72+
continuation_col = logstr.find("MARKER")
73+
rdict["message"] = record.message
74+
75+
record.continuation_col = continuation_col
76+
return fmt % rdict
77+
6578
def format(self, record):
66-
fmt = self._level_to_fmt_mapping.get(record.levelno, self._original_fmt)
6779
if six.PY2:
68-
self._fmt = fmt
80+
fmtmessage = self._format27(record)
6981
else:
70-
self._style._fmt = fmt
71-
return super(ColoredLevelFormatter, self).format(record)
82+
fmtmessage = super(CustomFormatter, self).format(record)
83+
84+
if "\n" in fmtmessage:
85+
return ("\n" + record.continuation_col * " ").join(fmtmessage.splitlines())
86+
return fmtmessage
87+
88+
def _format27(self, record):
89+
"""
90+
Copied from stdlib version of logging.Formatter.format.
91+
92+
Use self.formatMessage instead of directly using old style str
93+
formatting as used in the stdlib.
94+
"""
95+
record.message = record.getMessage()
96+
if self.usesTime():
97+
record.asctime = self.formatTime(record, self.datefmt)
98+
99+
s = self.formatMessage(record)
100+
101+
if record.exc_info:
102+
# Cache the traceback text to avoid converting it multiple times
103+
# (it's constant anyway)
104+
if not record.exc_text:
105+
record.exc_text = self.formatException(record.exc_info)
106+
if record.exc_text:
107+
if s[-1:] != "\n":
108+
s = s + "\n"
109+
try:
110+
s = s + record.exc_text
111+
except UnicodeError:
112+
# Sometimes filenames have non-ASCII chars, which can lead
113+
# to errors when s is Unicode and record.exc_text is str
114+
# See issue 8924.
115+
# We also use replace for when there are multiple
116+
# encodings, e.g. UTF-8 for the filesystem and latin-1
117+
# for a script. See issue 13232.
118+
s = s + record.exc_text.decode(sys.getfilesystemencoding(), "replace")
119+
return s
72120

73121

74122
def get_option_ini(config, *names):
@@ -547,11 +595,8 @@ def _setup_cli_logging(self):
547595
log_cli_date_format = get_option_ini(
548596
self._config, "log_cli_date_format", "log_date_format"
549597
)
550-
if (
551-
self._config.option.color != "no"
552-
and ColoredLevelFormatter.LEVELNAME_FMT_REGEX.search(log_cli_format)
553-
):
554-
log_cli_formatter = ColoredLevelFormatter(
598+
if self._config.option.color != "no":
599+
log_cli_formatter = CustomFormatter(
555600
create_terminal_writer(self._config),
556601
log_cli_format,
557602
datefmt=log_cli_date_format,

0 commit comments

Comments
 (0)