Skip to content

Commit 004988d

Browse files
committed
Improve logs filtering
1 parent b3cb093 commit 004988d

File tree

1 file changed

+80
-23
lines changed

1 file changed

+80
-23
lines changed

swattool/logsview.py

+80-23
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import requests
1111
from simple_term_menu import TerminalMenu # type: ignore
1212

13+
from . import swatbotrest
1314
from . import swatbuild
1415
from . import utils
1516
from .webrequests import Session
@@ -27,6 +28,39 @@
2728
WHITE = "\x1b[1;37m"
2829

2930

31+
class _Highlight:
32+
# pylint: disable=too-few-public-methods
33+
def __init__(self, keyword: str, color: str, in_menu: bool):
34+
self.keyword = keyword
35+
self.color = color
36+
self.in_menu = in_menu
37+
38+
39+
class _Filter:
40+
# pylint: disable=too-few-public-methods
41+
def __init__(self, pat: re.Pattern, enabled: bool, color: Optional[str],
42+
in_menu: bool):
43+
self.pat = pat
44+
self.enabled = enabled
45+
self.color = color
46+
self.in_menu = in_menu
47+
48+
def match(self, line: str) -> tuple[bool, Optional[_Highlight]]:
49+
"""Check if the filter matches a given line."""
50+
if not self.enabled:
51+
return (False, None)
52+
53+
match = self.pat.match(line)
54+
if not match:
55+
return (False, None)
56+
57+
if not self.color:
58+
return (True, None)
59+
60+
hl = _Highlight(match.group("keyword"), self.color, self.in_menu)
61+
return (True, hl)
62+
63+
3064
def show_logs_menu(build: swatbuild.Build) -> bool:
3165
"""Show a menu allowing to select log file to analyze."""
3266
def get_failure_line(failure, logname):
@@ -51,22 +85,22 @@ def get_failure_line(failure, logname):
5185

5286

5387
def _format_log_line(linenum: int, text: str, colorized_line: Optional[int],
54-
highlight_lines: dict[int, tuple[str, str]]):
88+
highlight_lines: dict[int, _Highlight]):
5589
if linenum == colorized_line:
5690
if linenum in highlight_lines:
57-
linecolor = highlight_lines[linenum][1]
91+
linecolor = highlight_lines[linenum].color
5892
else:
5993
linecolor = CYAN
6094
text = f"{linecolor}{text}{RESET}"
6195
elif linenum in highlight_lines:
62-
pat = highlight_lines[linenum][0]
63-
color = highlight_lines[linenum][1]
96+
pat = highlight_lines[linenum].keyword
97+
color = highlight_lines[linenum].color
6498
text = re.sub(pat, f"{color}{pat}{RESET}", text)
6599
return text
66100

67101

68102
def _format_log_preview_line(linenum: int, text: str, colorized_line: int,
69-
highlight_lines: dict[int, tuple[str, str]]):
103+
highlight_lines: dict[int, _Highlight]):
70104
preview_text = text.replace('\t', ' ')
71105
formatted_text = _format_log_line(linenum, preview_text, colorized_line,
72106
highlight_lines)
@@ -85,33 +119,55 @@ def _get_preview_window(linenum: int, lines: list[str], preview_height: int
85119

86120

87121
def _format_log_preview(linenum: int, lines: list[str],
88-
highlight_lines: dict[int, tuple[str, str]],
122+
highlight_lines: dict[int, _Highlight],
89123
preview_height: int) -> str:
90124
start, end = _get_preview_window(linenum, lines, preview_height)
91125
lines = [_format_log_preview_line(i, t, linenum, highlight_lines)
92126
for i, t in enumerate(lines[start: end], start=start + 1)]
93127
return "\n".join(lines)
94128

95129

96-
def _get_log_highlights(loglines: list[str]) -> dict[int, tuple[str, str]]:
97-
pats = [(re.compile(r"(.*\s|^)(?P<keyword>\S*error):", flags=re.I),
98-
RED),
99-
(re.compile(r"(.*\s|^)(?P<keyword>\S*warning):", flags=re.I),
100-
YELLOW),
101-
]
130+
def _get_log_highlights(loglines: list[str], failure: swatbuild.Failure
131+
) -> dict[int, _Highlight]:
132+
status = failure.build.status
133+
test = failure.build.test
134+
filters = [
135+
# Toaster specific rules:
136+
# - Do nothing on "except xxxError:" (likely python code output).
137+
# - Match on "selenium .*exception:".
138+
# - Match on generic errors, but do not show in menu.
139+
_Filter(re.compile(r".*except\s*\S*error:", flags=re.I),
140+
test == "toaster", None, False),
141+
_Filter(re.compile(r"(.*\s|^)(?P<keyword>selenium\.\S*exception):",
142+
flags=re.I),
143+
test == "toaster", RED, status == swatbotrest.Status.ERROR),
144+
_Filter(re.compile(r"(.*\s|^)(?P<keyword>\S*error):", flags=re.I),
145+
test == "toaster", RED, status == swatbotrest.Status.ERROR),
146+
147+
# Generic rules:
148+
# - Match on "error:", show in menu if build status is error.
149+
# - Match on "warning:", show in menu if build status is warning.
150+
_Filter(re.compile(r"(.*\s|^)(?P<keyword>\S*error):", flags=re.I),
151+
True, RED, status == swatbotrest.Status.ERROR),
152+
_Filter(re.compile(r"(.*\s|^)(?P<keyword>\S*warning):",
153+
flags=re.I),
154+
True, YELLOW, status == swatbotrest.Status.WARNING),
155+
]
102156

103157
highlight_lines = {}
104158
for linenum, line in enumerate(loglines, start=1):
105-
for (pat, color) in pats:
106-
match = pat.match(line)
107-
if match:
108-
highlight_lines[linenum] = (match.group("keyword"), color)
159+
for filtr in filters:
160+
matched, highlight = filtr.match(line)
161+
if matched:
162+
if highlight:
163+
highlight_lines[linenum] = highlight
164+
break
109165

110166
return highlight_lines
111167

112168

113169
def _show_log(loglines: list[str], selected_line: Optional[int],
114-
highlight_lines: dict[int, tuple[str, str]],
170+
highlight_lines: dict[int, _Highlight],
115171
preview_height: Optional[int]):
116172
colorlines = [_format_log_line(i, t, selected_line, highlight_lines)
117173
for i, t in enumerate(loglines, start=1)]
@@ -150,20 +206,21 @@ def show_log_menu(failure: swatbuild.Failure, logname: str) -> bool:
150206

151207
utils.clear()
152208
loglines = logdata.splitlines()
153-
highlight_lines = _get_log_highlights(loglines)
209+
highlights = _get_log_highlights(loglines, failure)
154210

155211
entries = ["View entire log file|",
156212
"View entire log file in default editor|",
157-
*[f"On line {line: 6d}: {highlight_lines[line][0]}|{line}"
158-
for line in sorted(highlight_lines)]
213+
*[f"On line {line: 6d}: {highlights[line].keyword}|{line}"
214+
for line in sorted(highlights)
215+
if highlights[line].in_menu]
159216
]
160217

161218
preview_size = 0.6
162219
termheight = shutil.get_terminal_size((80, 20)).lines
163220
preview_height = int(preview_size * termheight)
164221

165222
def preview(line):
166-
return _format_log_preview(int(line), loglines, highlight_lines,
223+
return _format_log_preview(int(line), loglines, highlights,
167224
preview_height)
168225

169226
title = f"{failure.build.format_short_description()}: " \
@@ -178,9 +235,9 @@ def preview(line):
178235
return True
179236

180237
if entry == 0:
181-
_show_log(loglines, None, highlight_lines, None)
238+
_show_log(loglines, None, highlights, None)
182239
elif entry == 1:
183240
utils.launch_in_system_defaultshow_in_less(logdata)
184241
else:
185242
_, _, num = entries[entry].partition('|')
186-
_show_log(loglines, int(num), highlight_lines, preview_height)
243+
_show_log(loglines, int(num), highlights, preview_height)

0 commit comments

Comments
 (0)