diff --git a/OOMAnalyser.html b/OOMAnalyser.html index c6eb273..561e1eb 100644 --- a/OOMAnalyser.html +++ b/OOMAnalyser.html @@ -897,6 +897,7 @@ window.onerror = function (msg, url, lineNo, columnNo, error) {
  • Add support for manually triggered OOM (suggested by Mikko Rantalainen)
  • Add support for systems w/o swap (suggested by Mikko Rantalainen)
  • Add support for newer kernels (suggested by Mikko Rantalainen)
  • +
  • Add support for journalctl output (suggested by Mikko Rantalainen)
  • ...
  • diff --git a/OOMAnalyser.py b/OOMAnalyser.py index 48831fd..850713b 100644 --- a/OOMAnalyser.py +++ b/OOMAnalyser.py @@ -525,6 +525,7 @@ class OOMEntity: oom_lines = self._remove_non_oom_lines(oom_lines) oom_lines = self._remove_kernel_colon(oom_lines) cols_to_strip = self._number_of_columns_to_strip(oom_lines[self._get_CPU_index(oom_lines)]) + oom_lines = self._journalctl_add_leading_columns_to_meminfo(oom_lines, cols_to_strip) oom_lines = self._strip_needless_columns(oom_lines, cols_to_strip) oom_lines = self._rsyslog_unescape_lf(oom_lines) @@ -536,6 +537,34 @@ class OOMEntity: else: self.state = OOMEntityState.started + def _journalctl_add_leading_columns_to_meminfo(self, oom_lines, cols_to_add): + """ + Add leading columns to handle line breaks in journalctl output correctly. + + The output of the "Mem-Info:" block contains line breaks. journalctl breaks these lines accordingly, but + inserts at the beginning spaces instead of date and time. As a result, removing the needless columns no longer + works correctly. + + This function adds columns back in the affected rows so that the removal works cleanly over all rows. + + @see: _rsyslog_unescape_lf() + """ + pattern = r'^\s+ (active_file|unevictable|slab_reclaimable|mapped|free):.+$' + rec = re.compile(pattern) + + add_cols = "" + for i in range(cols_to_add): + add_cols += "Col{} ".format(i) + + expanded_lines = [] + for line in oom_lines: + match = rec.search(line) + if match: + line = "{} {}".format(add_cols, line.strip()) + expanded_lines.append(line) + + return expanded_lines + def _get_CPU_index(self, lines): """ Return the index of the first line with "CPU: " @@ -605,12 +634,17 @@ class OOMEntity: def _rsyslog_unescape_lf(self, oom_lines): """ - Rsyslog replaces line breaks with their octal representation #012. + Split lines at '#012' (octal representation of LF). + + The output of the "Mem-Info:" block contains line breaks. Rsyslog replaces these line breaks with their octal + representation #012. This breaks the removal of needless columns as well as the detection of the OOM values. + + Splitting the lines (again) solves this issue. This feature can be controlled inside the rsyslog configuration with the directives $EscapeControlCharactersOnReceive, $Escape8BitCharactersOnReceive and $ControlCharactersEscapePrefix. - The replacement is only in second line (active_anon:....) of the Mem-Info block. + @see: _journalctl_add_leading_columns_to_meminfo() """ lines = [] diff --git a/test.py b/test.py index 12ca95f..3381dd4 100755 --- a/test.py +++ b/test.py @@ -304,6 +304,37 @@ Killed process 6576 (java) total-vm:33914892kB, anon-rss:20629004kB, file-rss:0k self.assertEqual(self.get_error_text(), "ERROR: Failed to extract kernel version from OOM text") self.click_reset() + def test_035_leading_journalctl_input(self): + """Test loading input from journalctl """ + # prepare example + example_lines = OOMAnalyser.OOMDisplay.example_rhel7.split('\n') + res = [] + + # unescape #012 - see OOMAnalyser.OOMEntity._rsyslog_unescape_lf() + for line in example_lines: + if '#012' in line: + res.extend(line.split('#012')) + else: + res.append(line) + example_lines = res + res = [] + + # add date/time prefix except for "Mem-Info:" block + pattern = r'^ (active_file|unevictable|slab_reclaimable|mapped|free):.+$' + rec = re.compile(pattern) + for line in example_lines: + match = rec.search(line) + if match: + line = " {}".format(line) + else: + line = "Apr 01 14:13:32 mysrv kernel: {}".format(line) + res.append(line) + example = "\n".join(res) + + self.analyse_oom(example) + self.check_results_rhel7() + self.click_reset() + def test_040_trigger_proc_space(self): """Test trigger process name contains a space""" example = OOMAnalyser.OOMDisplay.example_rhel7