diff --git a/loguru/_logger.py b/loguru/_logger.py index 3d246de0..5e03b9e9 100644 --- a/loguru/_logger.py +++ b/loguru/_logger.py @@ -2107,7 +2107,7 @@ def _log(self, level, from_decorator, options, message, args, kwargs): "function": co_name, "level": RecordLevel(level_name, level_no, level_icon), "line": f_lineno, - "message": str(message), + "message": message, "module": splitext(file_name)[0], "name": name, "process": RecordProcess(process.ident, process.name), @@ -2130,23 +2130,25 @@ def _log(self, level, from_decorator, options, message, args, kwargs): ) kwargs.update(record=log_record) + if core.patcher: + core.patcher(log_record) + + for patcher in patchers: + patcher(log_record) + if colors: if args or kwargs: - colored_message = Colorizer.prepare_message(message, args, kwargs) + colored_message = Colorizer.prepare_message(log_record["message"], args, kwargs) else: - colored_message = Colorizer.prepare_simple_message(str(message)) + colored_message = Colorizer.prepare_simple_message(str(log_record["message"])) log_record["message"] = colored_message.stripped elif args or kwargs: colored_message = None - log_record["message"] = message.format(*args, **kwargs) + log_record["message"] = log_record["message"].format(*args, **kwargs) else: colored_message = None - if core.patcher: - core.patcher(log_record) - - for patcher in patchers: - patcher(log_record) + log_record["message"] = str(log_record["message"]) for handler in core.handlers.values(): handler.emit(log_record, level_id, from_decorator, raw, colored_message) diff --git a/tests/test_patch.py b/tests/test_patch.py index 684974ef..be9382b7 100644 --- a/tests/test_patch.py +++ b/tests/test_patch.py @@ -1,3 +1,7 @@ +import re + +import pytest + from loguru import logger @@ -83,3 +87,46 @@ def patch_3(record): logger.patch(patch_1).patch(patch_2).patch(patch_3).info("Test") assert writer.read() == "12 Test\n" + + +@pytest.mark.parametrize( + ("colorize", "colors", "expected"), + [ + (False, False, "A\n"), + (False, True, "A\n"), + (True, False, "A\n"), + (True, True, "\x1b[31mA\x1b[0m\n"), + ], +) +def test_colorful_patch(colorize, colors, expected, writer): + logger.add(writer, format="{message}", colorize=colorize) + + logger_patched = logger.patch(lambda r: r.update(message=f"{r['message']}")) + logger_patched.opt(colors=colors).debug("A") + + assert writer.read() == expected + + +@pytest.mark.parametrize( + ("colorize", "colors", "expected"), + [ + (False, False, "A W, M\n"), + (False, True, "A W, M\n"), + (True, False, "A W, M\n"), + (True, True, "A \x1b[31mW\x1b[0m, \x1b[31mM\x1b[0m\n"), + ], +) +def test_automatic_colorful_patch(colorize, colors, expected, writer): + # regex matches on single curly braces and substitutes + # a color tag in-place + _regex_pattern = re.compile(r"(\{[^{}]*\})(?!\})") + _regex_repl = r"\1" + + def patch(r): + r.update(message=re.sub(_regex_pattern, _regex_repl, r["message"])) + + logger.add(writer, format="{message}", colorize=colorize) + logger_patched = logger.patch(patch) + logger_patched.opt(colors=colors).debug("A {}, {a}", "W", a="M") + + assert writer.read() == expected