Skip to content

Commit 76f3349

Browse files
committed
Add color-coded log formatter with level-aware ANSI colors
Extract log formatting into a dedicated _PyflybyFormatter class that applies color based on log level: blue for info, yellow for warnings, and red for errors/critical. Previously all interactive output used a single hardcoded yellow prefix regardless of severity. The formatter implements separate formatInteractive and formatPlain methods to keep the color logic out of the handler's emit method.
1 parent beedbdb commit 76f3349

File tree

1 file changed

+37
-9
lines changed

1 file changed

+37
-9
lines changed

lib/python/pyflyby/_log.py

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,63 @@
44

55

66

7+
from __future__ import print_function
8+
79
import builtins
810
from contextlib import nullcontext
911
import logging
10-
from logging import Handler, Logger
12+
from logging import Formatter, Handler, Logger
1113
import os
1214
from prompt_toolkit import patch_stdout
1315
import sys
1416

1517

18+
class _PyflybyFormatter(Formatter):
19+
_ANSI_RESET = "\033[0m"
20+
_COLORS = {
21+
"blue": "\033[34m",
22+
"yellow": "\033[33m",
23+
"red": "\033[31m",
24+
}
25+
26+
def _color_for_level(self, levelno):
27+
if levelno >= logging.ERROR:
28+
return self._COLORS["red"]
29+
elif levelno >= logging.WARNING:
30+
return self._COLORS["yellow"]
31+
else:
32+
return self._COLORS["blue"]
33+
34+
def formatInteractive(self, record):
35+
color = self._color_for_level(record.levelno)
36+
prefix = "%s%s[PYFLYBY]%s " % (self._ANSI_RESET, color, self._ANSI_RESET)
37+
msg = super().format(record)
38+
return "".join(["%s%s\n" % (prefix, line) for line in msg.splitlines()])
39+
40+
def formatPlain(self, record):
41+
msg = super().format(record)
42+
return "".join(["[PYFLYBY] %s\n" % line for line in msg.splitlines()])
43+
44+
1645
class _PyflybyHandler(Handler):
1746

1847
_pre_log_function = None
1948
_logged_anything_during_context = False
2049

21-
_interactive_prefix = "\033[0m\033[33m[PYFLYBY]\033[0m "
22-
_noninteractive_prefix = "[PYFLYBY] "
50+
def __init__(self):
51+
super().__init__()
52+
self.setFormatter(_PyflybyFormatter())
2353

2454
def emit(self, record):
2555
try:
56+
formatter = self.formatter
2657
if _is_ipython() or _is_interactive(sys.stderr):
27-
prefix = self._interactive_prefix
58+
msg = formatter.formatInteractive(record)
2859
patch_stdout_c = patch_stdout.patch_stdout(raw=True)
2960
else:
30-
prefix = self._noninteractive_prefix
61+
msg = formatter.formatPlain(record)
3162
patch_stdout_c = nullcontext()
3263

33-
msg = self.format(record)
34-
msg = ''.join(["%s%s\n" % (prefix, line) for line in msg.splitlines()])
3564
with patch_stdout_c:
3665
sys.stderr.write(msg)
3766
sys.stderr.flush()
@@ -75,8 +104,7 @@ class PyflybyLogger(Logger):
75104

76105
def __init__(self, name, level):
77106
Logger.__init__(self, name)
78-
handler = _PyflybyHandler()
79-
self.addHandler(handler)
107+
self.addHandler(_PyflybyHandler())
80108
self.set_level(level)
81109

82110
def set_level(self, level):

0 commit comments

Comments
 (0)