Skip to content

Commit

Permalink
logging: Improve formatting of multiline message (#5312)
Browse files Browse the repository at this point in the history
logging: Improve formatting of multiline message
  • Loading branch information
nicoddemus committed May 31, 2019
2 parents c5de8e8 + ea3ebec commit c8d23c2
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 0 deletions.
1 change: 1 addition & 0 deletions changelog/5312.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Improved formatting of multiline log messages in python3.
33 changes: 33 additions & 0 deletions src/_pytest/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,36 @@ def format(self, record):
return super(ColoredLevelFormatter, self).format(record)


if not six.PY2:
# Formatter classes don't support format styles in PY2

class PercentStyleMultiline(logging.PercentStyle):
"""A logging style with special support for multiline messages.
If the message of a record consists of multiple lines, this style
formats the message as if each line were logged separately.
"""

@staticmethod
def _update_message(record_dict, message):
tmp = record_dict.copy()
tmp["message"] = message
return tmp

def format(self, record):
if "\n" in record.message:
lines = record.message.splitlines()
formatted = self._fmt % self._update_message(record.__dict__, lines[0])
# TODO optimize this by introducing an option that tells the
# logging framework that the indentation doesn't
# change. This allows to compute the indentation only once.
indentation = _remove_ansi_escape_sequences(formatted).find(lines[0])
lines[0] = formatted
return ("\n" + " " * indentation).join(lines)
else:
return self._fmt % record.__dict__


def get_option_ini(config, *names):
for name in names:
ret = config.getoption(name) # 'default' arg won't work as expected
Expand Down Expand Up @@ -444,6 +474,9 @@ def _create_formatter(self, log_format, log_date_format):
)
else:
formatter = logging.Formatter(log_format, log_date_format)

if not six.PY2:
formatter._style = PercentStyleMultiline(formatter._style._fmt)
return formatter

def _setup_cli_logging(self):
Expand Down
30 changes: 30 additions & 0 deletions testing/logging/test_formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
import logging

import py.io
import six

import pytest
from _pytest.logging import ColoredLevelFormatter


Expand Down Expand Up @@ -35,3 +37,31 @@ class option(object):
formatter = ColoredLevelFormatter(tw, logfmt)
output = formatter.format(record)
assert output == ("dummypath 10 INFO Test Message")


@pytest.mark.skipif(
six.PY2, reason="Formatter classes don't support format styles in PY2"
)
def test_multiline_message():
from _pytest.logging import PercentStyleMultiline

logfmt = "%(filename)-25s %(lineno)4d %(levelname)-8s %(message)s"

record = logging.LogRecord(
name="dummy",
level=logging.INFO,
pathname="dummypath",
lineno=10,
msg="Test Message line1\nline2",
args=(),
exc_info=False,
)
# this is called by logging.Formatter.format
record.message = record.getMessage()

style = PercentStyleMultiline(logfmt)
output = style.format(record)
assert output == (
"dummypath 10 INFO Test Message line1\n"
" line2"
)

0 comments on commit c8d23c2

Please sign in to comment.