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