journalctl-not: NOT-Filter für journalctl

Ein Wrapper um journalctl, der Einträge nach Unit, Identifier, Feldwert oder Nachrichtenmuster ausblendet und die normale journalctl-Ausgabe mit Farben, Pager, -n und -f beibehält.

Das Problem

journalctl kann Filter mit UND und ODER verknüpfen, kennt aber kein NOT. Es gibt keine Option, um einen lauten Dienst, einen Syslog-Identifier oder eine wiederkehrende Meldung auszublenden. Der übliche Umweg ist journalctl | grep -v. Dabei gehen Farben und Pager verloren, solange SYSTEMD_COLORS nicht gesetzt ist, grep trifft Zeilen innerhalb mehrzeiliger Meldungen, und -n 20 zeigt weniger als 20 Zeilen, weil grep erst nach dem Zählen durch journalctl filtert. Der andere Umweg ist journalctl -o json | jq 'select(...)'. Das filtert korrekt, gibt aber rohes JSON aus.

Der Bedarf ist belegt:

  • systemd/systemd#2720, "RFE: add NOT operator to journal matching", ist seit 2016 offen und hat 131 Reaktionen und 22 Kommentare. Die Antwort des Maintainers war, NOT stehe auf der TODO-Liste, sei mit den Journal-Indizes aber schwer umzusetzen. Die Anfrage geht auf freedesktop-Bug 55868 aus dem Jahr 2012 zurück.
  • Die Super-User-Frage "How to exclude journalctl entries using a filter?" hat einen Score von 26 und etwa 39.000 Aufrufe.
  • systemd 256 hat -T/--exclude-identifier eingeführt. Das deckt nur Syslog-Identifier ab und fehlt in Ubuntu 24.04 (systemd 255), Debian 12 (252) und RHEL 9 (252).

Wie es funktioniert

journalctl-not reicht alle journalctl-Optionen durch, führt den Befehl mit -o json aus, verwirft jeden Eintrag, auf den ein Ausschluss passt, und gibt den Rest aus. Die Ausschlüsse sind FIELD!=VALUE, --exclude-unit und --exclude-user-unit (mit denselben Feldern, die journalctl -u verwendet), -T/--exclude-identifier wie in systemd 256 und --exclude-grep für einen regulären Ausdruck auf der Meldung. Ein Eintrag wird ausgeblendet, sobald ein Ausschluss passt.

Die Textausgabe ist eine Portierung des Formatierungscodes von systemd (logs-show.c und strip_tab_ansi()). Für die unterstützten Ausgabemodi ist das Ergebnis deshalb byte-genau dasselbe wie bei journalctl. -n N zählt die Einträge, die nach den Ausschlüssen übrig bleiben: Das Werkzeug liest das Journal rückwärts, bis es N davon hat. Mit -f gibt es diese aus und folgt dann ab dem neuesten Eintrag.

Im folgenden Beispiel wurden sieben Einträge mit systemd-cat geschrieben. Die Zeilen "health check ok" haben Debug-Priorität (7):

$ journalctl -t demo-app -t demo-sync --no-hostname
Nov 01 06:45:23 demo-app[97742]: request took 812 ms
Nov 01 06:45:23 demo-app[97746]: health check ok
Nov 01 06:45:23 demo-app[97750]: health check ok
Nov 01 06:45:23 demo-app[97754]: upstream timed out
Nov 01 06:45:23 demo-sync[97758]: Starting nightly sync
Nov 01 06:45:23 demo-app[97762]: disk full, retrying
Nov 01 06:45:23 demo-app[97766]: health check ok
$ journalctl-not -t demo-app -t demo-sync -n 3 PRIORITY!=7 -T demo-sync --no-hostname
Nov 01 06:45:23 demo-app[97742]: request took 812 ms
Nov 01 06:45:23 demo-app[97754]: upstream timed out
Nov 01 06:45:23 demo-app[97762]: disk full, retrying

-n 3 liefert die letzten drei Einträge, die übrig bleiben. Dasselbe -n 3 mit nachgeschaltetem grep -v hätte eine Zeile ausgegeben. Das Werkzeug braucht Python 3.9 oder neuer und hat keine Abhängigkeiten außerhalb der Standardbibliothek.

Tests

Die Unit-Tests decken das Parsen der Argumente, die Ausschlussregeln, die Darstellung von Zeitstempeln, mehrzeiligen Meldungen, Binärfeldern und Farben sowie vollständige Läufe gegen ein Ersatz-journalctl ab, das eine JSON-Fixture abspielt. Diese Läufe prüfen, dass -n die verbleibenden Einträge zählt und dass -f nach dem neuesten Cursor weiterliest.

tests/golden.sh schreibt Testeinträge ins echte Journal und vergleicht journalctl-not ohne Ausschlüsse byte-genau mit journalctl: in acht Ausgabemodi, mit Farben, in zwei Zeitzonen sowie über das gesamte Journal der letzten sechs Stunden und das Kernel-Log. Außerdem vergleicht es Ausschlüsse mit gleichbedeutenden journalctl-Filtern, zum Beispiel PRIORITY!=7 mit -p info. Das CI führt die Unit-Tests und den Golden-Test auf Ubuntu 24.04 (systemd 255) mit Python 3.9, 3.12 und 3.13 aus. Beim Schreiben des Golden-Tests fielen zwei Eigenheiten von journalctl 255 auf, die journalctl-not nicht nachbildet: Mit -n N --until T und weniger als N Treffern gibt journalctl auch Einträge nach T aus, und mit --grep und -n gibt es sie in umgekehrter Reihenfolge aus, die neuesten zuerst.

Grenzen

  • Unterstützt sind nur die Ausgabemodi short, short-precise, short-iso, short-iso-precise, short-full, short-unix, short-monotonic, cat und json. Andere werden mit einem Fehler abgelehnt.
  • -x/--catalog wird mit einer Warnung ignoriert. Mit Farben fügt journalctl anklickbare Links ein und hebt Treffer von --grep hervor, journalctl-not nicht.
  • Monats- und Wochentagsnamen sind immer englisch.
  • Die Ausschlüsse laufen in Python, nachdem journalctl die Einträge gelesen hat. Die Ausgabe eines ganzen Journals mit 600.000 Zeilen dauert etwa 3,5-mal so lange wie mit journalctl. Bei -f oder -n 100 fällt der Unterschied nicht auf.
  • FIELD!=VALUE vergleicht den ganzen Wert und behält Einträge, die das Feld nicht haben.
  • Die Formatierung folgt systemd 255. Neuere Versionen können Details wie Farben ändern.

Code und Installationsanleitung liegen unter github.com/Jwrede/journalctl-not.