Log-Management auf einem einzelnen Server
Werbung
Wenn auf einem einzelnen Server etwas schiefgeht, sind die Logs normalerweise alle da. Sie sind nur wild verstreut über /var/log/nginx/, /var/log/postgresql/, das systemd-Journal und jeden noch so obskuren Pfad, in den die Applikation selbst ihre Daten schreibt.
Die relevante Zeile zu finden bedeutet in erster Linie, genau zu wissen, an welchem dieser fünf Orte man überhaupt suchen muss. Wenn die App ihre Logs als freien Text (Free-Text) anstatt in strukturiertem JSON ausspuckt, erfordert eine simple Suche nach "alle Requests von dieser IP in der letzten Stunde, sowohl im Nginx als auch in der App" mehrere grep-Kommandos mit völlig unterschiedlichen Formaten. Das ist technisch nicht kaputt. Es ist nur deutlich langsamer als es sein müsste – besonders dann, wenn man unter Zeitdruck debuggen muss.
Ein voll ausgewachsener ELK-Stack (Elasticsearch, Logstash, Kibana) löst dieses Problem absolut. Aber er ist schlichtweg viel zu wuchtig für einen einzelnen Server. Elasticsearch allein fordert einen gewaltigen Batzen Arbeitsspeicher, nur um überhaupt im Leerlauf (Idle) zu laufen. Der sinnvolle Mittelweg für einen einzelnen Server ist wesentlich schlanker.
Strukturierte Logs machen alles andere einfacher
Die absolut wirkungsvollste Änderung, die man vornehmen kann, ist, die Application-Logs in ein strukturiertes Format (JSON) anstelle von unformatiertem Text zu zwingen. Ein Vergleich:
2026-10-28 14:32:01 ERROR Failed to process order 4821 for user 1029: timeout
{"time":"2026-10-28T14:32:01Z","level":"error","msg":"failed to process order","order_id":4821,"user_id":1029,"reason":"timeout"}
Das zweite Format ist für das bloße Auge im Terminal nicht unbedingt lesbarer. Aber es ist abfragbar (queryable). "Jeder Fehler für User 1029" oder "jeder Timeout in der letzten Stunde" wird zu einem hochpräzisen Filter auf ein bestimmtes Feld – anstelle eines unscharfen Regex, das versehentlich auch völlig irrelevanten Text mit den gleichen Ziffern matcht. Die meisten modernen Logging-Bibliotheken unterstützen strukturierten JSON-Output über eine simple Konfigurationsänderung. Dafür muss man keinen Code neu schreiben.
journald leistet bereits mehr, als die Leute nutzen
Für alles, was als systemd-Service läuft (was auf einem modernen Linux-Server praktisch alles ist), zentralisiert journalctl stdout und stderr bereits vollautomatisch mit strikten Zeitstempeln (Timestamps). Zudem bietet es Filterfunktionen, die erstaunlich viele Admins komplett ignorieren:
# Alles von der Applikation, nur die letzte Stunde
journalctl -u myapp --since "1 hour ago"
# Live-Logs von mehreren Services gleichzeitig verfolgen
journalctl -u myapp -u nginx -f
# Ausschließlich Fehler, über das gesamte System hinweg
journalctl -p err --since today
Dieser letzte Befehl – das filtern nach Priorität quer über das komplette System – ist etwas, das eine verstreute Sammlung von .log-Dateien ohne externe Tools schlichtweg überhaupt nicht leisten kann. Wenn die Logs der Applikation direkt als JSON nach stdout geschrieben werden (anstatt in eine separate Datei), ist der journalctl-Output für diese Unit bereits perfekt strukturiert. Man kann ihn nahtlos in jq pipen, um gezielt nach spezifischen Feldern zu filtern.
Ein leichtgewichtiger Stack, wenn man unbedingt Dashboards will
Wer etwas sucht, das näher an einem echten Log-Viewer dran ist, aber ohne den massiven Fußabdruck von Elasticsearch auskommt, für den ist Grafana Loki plus Promtail aktuell die absolute Standardempfehlung.
Promtail hängt sich an die Logfiles (oder liest das systemd-Journal direkt aus) und schickt alles an Loki. Grafana fragt Loki dann ab und stellt die Logs direkt neben den Metrik-Dashboards von Prometheus dar. Lokis Ressourcenbedarf ist gering genug, dass es völlig problemlos neben der Applikation auf demselben kleinen VPS laufen kann. Das ist der Hauptgrund, warum es hier so perfekt reinpasst, während Elasticsearch sofort aus dem Rahmen fallen würde.
# promtail-config.yml (minimal)
scrape_configs:
- job_name: journal
journal:
max_age: 12h
relabel_configs:
- source_labels: ['__journal__systemd_unit']
target_label: 'unit'
Die Log-Rotation nicht vergessen
Das alles nützt absolut nichts, wenn die Festplatte leise und heimlich zu 100 % vollläuft – und zwar mit Logs, die sich ohnehin niemand ansieht. logrotate ist für Standarddienste meist schon vorkonfiguriert. Application-Logs, die in einen Custom-Pfad geschrieben werden, brauchen aber zwingend einen eigenen Eintrag:
# /etc/logrotate.d/myapp
/var/log/myapp/*.log {
daily
rotate 14
compress
missingok
notifempty
}
Für das systemd-Journal selbst verhindert ein hartes Größenlimit, dass das Journal über Jahre hinweg still und heimlich ins Unermessliche wächst:
# /etc/systemd/journald.conf
SystemMaxUse=1G
Das eigentliche Ziel
Nichts davon muss ein übertriebenes Enterprise-Setup sein. Das einzige Ziel ist, dass bei einem Ausfall nachts um 2 Uhr die Frage "Was ist wann passiert?" genau einen Anlaufpunkt hat. Man braucht exakt so viel Struktur, um nach dem filtern zu können, was wirklich wichtig ist (eine User-ID, ein bestimmter Fehlertyp, ein präzises Zeitfenster). Niemand will endlos durch rohen Text scrollen in der Hoffnung, dass die gesuchte Zeile zufällig ins Auge springt.
Strukturiertes Logging gepaart mit den eingebauten Filtern von journalctl deckt das meiste davon völlig kostenlos ab. Loki und Grafana setzen dem Ganzen lediglich ein durchsuchbares User-Interface obendrauf, wenn "kostenlos" allein nicht mehr reicht.
Werbung