From 1acf11b986a203647dd3c9c76db93ec93d3d4a04 Mon Sep 17 00:00:00 2001 From: kitrady Date: Tue, 30 Jul 2024 13:36:30 -0500 Subject: [PATCH] adding non-fatal stack trace analyzer --- src/alogamous/__main__.py | 2 + src/alogamous/stack_trace_analyzer.py | 32 +++++++ tests/stack_trace_analyzer_test.py | 127 ++++++++++++++++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 src/alogamous/stack_trace_analyzer.py create mode 100644 tests/stack_trace_analyzer_test.py diff --git a/src/alogamous/__main__.py b/src/alogamous/__main__.py index dca1efc..55216d1 100644 --- a/src/alogamous/__main__.py +++ b/src/alogamous/__main__.py @@ -9,6 +9,7 @@ line_count_analyzer, log_line_parser, loginfo_analyzer, + stack_trace_analyzer, startup_header_analyzer, warning_analyzer, ) @@ -44,6 +45,7 @@ format_analyzer.FormatAnalyzer(line_parser), warning_analyzer.WarningAnalyzer(), loginfo_analyzer.InfoAnalyzer(line_parser), + stack_trace_analyzer.StackTraceAnalyzer(line_parser), startup_header_analyzer.StartupHeaderAnalyzer(line_parser), warning_analyzer.WarningAnalyzer(), ], diff --git a/src/alogamous/stack_trace_analyzer.py b/src/alogamous/stack_trace_analyzer.py new file mode 100644 index 0000000..1a0cb15 --- /dev/null +++ b/src/alogamous/stack_trace_analyzer.py @@ -0,0 +1,32 @@ +from alogamous import analyzer, log_line_parser + + +class StackTraceAnalyzer(analyzer.Analyzer): + def __init__(self, line_parser): + self.parser = line_parser + self.stack_trace = False + self.stack_trace_counter = 0 + self.stack_trace_lines = [] + self.non_fatal_trace = False + + def read_log_line(self, line): + line_type = self.parser.parse(line)["type"] + if line_type == log_line_parser.LineType.UNSTRUCTURED_LINE and line.count("Traceback") == 1: + self.stack_trace = True + self.stack_trace_counter += 1 + self.stack_trace_lines.append(line) + elif self.stack_trace: + if line_type == log_line_parser.LineType.UNSTRUCTURED_LINE: + self.stack_trace_lines.append(line) + elif line_type == log_line_parser.LineType.HEADER_LINE: + self.stack_trace = False + elif line_type == log_line_parser.LineType.LOG_LINE: + self.stack_trace = False + self.non_fatal_trace = True + + def report(self, out_stream): + if self.non_fatal_trace: + out_stream.write(f"{self.stack_trace_counter} non-fatal stack trace(s) found:\n- ") + out_stream.write("\n- ".join(self.stack_trace_lines)) + else: + out_stream.write("No non-fatal stack traces were found") diff --git a/tests/stack_trace_analyzer_test.py b/tests/stack_trace_analyzer_test.py new file mode 100644 index 0000000..6e2b07c --- /dev/null +++ b/tests/stack_trace_analyzer_test.py @@ -0,0 +1,127 @@ +from io import StringIO + +from alogamous import log_line_parser, stack_trace_analyzer + + +def test_stacktrace_where_service_dies(): + parser = log_line_parser.LogLineParser( + ["datetime", "source", "level", "message"], " - ", "====================================================" + ) + stacktrace_checker = stack_trace_analyzer.StackTraceAnalyzer(parser) + in_stream = """2024-07-23 21:13:53,862 - root - INFO - Closing client connection. +2024-07-23 21:13:53,862 - root - INFO - Closing client connection. +Traceback (most recent call last): + File "", line 1938, in _run_module_as_main + File "", line 88, in _run_code + File ".build/2811/execution/execution_service", line 973, in + app.run(life_cycle_runner.run, life_cycle_runner.stop) + File ".build/2811/app/application.py", line 219, in run + run(self.start(my_date, main, stop)) + File ".build/2811/.venv/lib/python3.11/runners.py", line 190, in run + return runner.run(main) + ^^^^^^^^^^^^^^^^ + File ".build/2811/.venv/lib/python3.11/runners.py", line 118, in run + return self._loop.run_until_complete(task) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File ".build/2811/.venv/lib/python3.11/events.py", line 653, in run_until_done + return future.result() + ^^^^^^^^^^^^^^^ + File "2811/app/application.py", line 421, in start + await self.task + File "2811/messages/app/runner.py", line 449, in run + await asyncio.gather(*self.running_tasks) + File "2811/messages/processor.py", line 340, in run + await self.dispatch(message) + File ".build/2811/execution/execution_service", line 1315, in market_test + if symbol and obj.region != 'NORTHAMERICA' + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +AttributeError: 'NoneType' object has no attribute 'region'""" + out_stream = StringIO() + for line in in_stream.splitlines(): + stacktrace_checker.read_log_line(line) + stacktrace_checker.report(out_stream) + assert out_stream.getvalue() == "No non-fatal stack traces were found" + + +def test_stacktrace_where_service_lives(): + parser = log_line_parser.LogLineParser( + ["datetime", "source", "level", "message"], " - ", "====================================================" + ) + stacktrace_checker = stack_trace_analyzer.StackTraceAnalyzer(parser) + in_stream = """2024-07-23 21:13:53,862 - root - INFO - Closing client connection. +2024-07-23 21:13:53,862 - root - INFO - Closing client connection. +Traceback (most recent call last): + File "", line 1938, in _run_module_as_main + File "", line 88, in _run_code + File ".build/2811/execution/execution_service", line 973, in + app.run(life_cycle_runner.run, life_cycle_runner.stop) + File ".build/2811/app/application.py", line 219, in run + run(self.start(my_date, main, stop)) + File ".build/2811/.venv/lib/python3.11/runners.py", line 190, in run + return runner.run(main) + ^^^^^^^^^^^^^^^^ + File ".build/2811/.venv/lib/python3.11/runners.py", line 118, in run + return self._loop.run_until_complete(task) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File ".build/2811/.venv/lib/python3.11/events.py", line 653, in run_until_done + return future.result() + ^^^^^^^^^^^^^^^ + File "2811/app/application.py", line 421, in start + await self.task + File "2811/messages/app/runner.py", line 449, in run + await asyncio.gather(*self.running_tasks) + File "2811/messages/processor.py", line 340, in run + await self.dispatch(message) + File ".build/2811/execution/execution_service", line 1315, in market_test + if symbol and obj.region != 'NORTHAMERICA' + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +AttributeError: 'NoneType' object has no attribute 'region' +2024-07-23 21:13:54,091 - root - ERROR - Caught exception N/A. Message: Unclosed client session""" + out_stream = StringIO() + for line in in_stream.splitlines(): + stacktrace_checker.read_log_line(line) + stacktrace_checker.report(out_stream) + assert ( + out_stream.getvalue() + == """1 non-fatal stack trace(s) found: +- Traceback (most recent call last): +- File "", line 1938, in _run_module_as_main +- File "", line 88, in _run_code +- File ".build/2811/execution/execution_service", line 973, in +- app.run(life_cycle_runner.run, life_cycle_runner.stop) +- File ".build/2811/app/application.py", line 219, in run +- run(self.start(my_date, main, stop)) +- File ".build/2811/.venv/lib/python3.11/runners.py", line 190, in run +- return runner.run(main) +- ^^^^^^^^^^^^^^^^ +- File ".build/2811/.venv/lib/python3.11/runners.py", line 118, in run +- return self._loop.run_until_complete(task) +- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +- File ".build/2811/.venv/lib/python3.11/events.py", line 653, in run_until_done +- return future.result() +- ^^^^^^^^^^^^^^^ +- File "2811/app/application.py", line 421, in start +- await self.task +- File "2811/messages/app/runner.py", line 449, in run +- await asyncio.gather(*self.running_tasks) +- File "2811/messages/processor.py", line 340, in run +- await self.dispatch(message) +- File ".build/2811/execution/execution_service", line 1315, in market_test +- if symbol and obj.region != 'NORTHAMERICA' +- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +- AttributeError: 'NoneType' object has no attribute 'region'""" + ) + + +def test_no_stacktrace(): + parser = log_line_parser.LogLineParser( + ["datetime", "source", "level", "message"], " - ", "====================================================" + ) + stacktrace_checker = stack_trace_analyzer.StackTraceAnalyzer(parser) + in_stream = """2024-07-23 21:13:53,862 - root - INFO - Closing client connection. + 2024-07-23 21:13:53,862 - root - INFO - Closing client connection.""" + out_stream = StringIO() + for line in in_stream.splitlines(): + stacktrace_checker.read_log_line(line) + stacktrace_checker.report(out_stream) + assert out_stream.getvalue() == "No non-fatal stack traces were found"