Logging ist kaputt
(loggingsucks.com)- In modernen verteilten Systemen haben herkömmliche Logging-Ansätze eine strukturelle Grenze darin, die Wahrheit abzubilden
- Logs sind weiterhin auf die Single-Server-Umgebung von 2005 ausgelegt und verlieren dadurch den Kontext von Requests, die über mehrere Services, Datenbanken und Caches laufen
- Einfache Stringsuche versteht Struktur, Beziehungen und Korrelationen nicht und erschwert so die Ursachenanalyse
- Die Lösung besteht darin, pro Request ein einzelnes „Wide Event“ (oder „Canonical Log Line“) mit dem gesamten Kontext zu hinterlassen
- Dadurch werden Logs von reinem Text zu einem analysierbaren Datenbestand
Das grundlegende Problem von Logging
- Bestehende Logs wurden unter der Annahme des monolithischen Server-Zeitalters entwickelt und spiegeln die heutige verteilte Service-Architektur nicht wider
- Ein einzelner Request durchläuft mehrere Services, DBs, Caches und Queues, doch Logs werden weiterhin aus Sicht eines einzelnen Servers geschrieben
- Im Beispiel entstehen pro Request 13 Zeilen; bei 10.000 gleichzeitigen Nutzern sind das 130.000 Zeilen pro Sekunde, von denen die meisten bedeutungslos sind
- Was man bei einem Problem braucht, ist Kontext, doch genau der fehlt in heutigen Logs
Die Grenzen der Stringsuche
- Wenn ein Nutzer meldet: „Die Zahlung funktioniert nicht“, ist es selbst bei einer Suche nach E-Mail oder
user_idwegen der fehlenden einheitlichen Struktur schwer, brauchbare Ergebnisse zu bekommen- Dieselbe Benutzer-ID wird in zig verschiedenen Formen protokolliert, etwa als
user-123,user_id=user-123oder{"userId":"user-123"}
- Dieselbe Benutzer-ID wird in zig verschiedenen Formen protokolliert, etwa als
- Unterschiedliche Log-Formate zwischen Services machen das Nachverfolgen zusammenhängender Events unmöglich
- Das Kernproblem ist, dass Logs auf das Schreiben (write) ausgelegt sind und nicht für Abfragen (query) optimiert wurden
Definition zentraler Begriffe
- Structured Logging: Protokollierung als Key-Value-Format (JSON) statt als String
- Cardinality: Anzahl eindeutiger Werte eines Felds; bei
user_idzum Beispiel sehr hoch - Dimensionality: Anzahl der Felder in einem Log-Event; je mehr Felder, desto größer die Analysemöglichkeiten
- Wide Event / Canonical Log Line: Ein einzelnes Log-Event mit reichhaltigem Kontext pro Request
- Die meisten Logging-Systeme begrenzen Daten mit hoher Kardinalität aus Kostengründen, obwohl sie in der Praxis für Debugging am nützlichsten sind
Die Grenzen von OpenTelemetry
- OpenTelemetry (OTel) ist ein Protokoll- und SDK-Set, das nur einen Standard für Datenerfassung und -übertragung bereitstellt
- OTel übernimmt jedoch Folgendes nicht
- Es entscheidet nicht, was geloggt werden soll
- Es fügt keinen Business-Kontext (z. B. Abo-Stufe oder Warenkorbwert) automatisch hinzu
- Es verändert nicht die Denkweise von Entwicklern beim Logging
- Selbst bei Verwendung derselben Bibliothek unterscheidet sich die Debugging-Erfahrung drastisch zwischen bewusst mit Kontext angereicherter Instrumentierung und einfacher Instrumentierung
- OTel ist lediglich die Transportleitung (plumbing), und was hindurchfließt, müssen Entwickler selbst festlegen
Der Wide-Event-/Canonical-Log-Line-Ansatz
- Statt Logging danach auszurichten, „was der Code tut“, sollte man erfassen, „was mit dem Request passiert ist“
- Für jeden Request wird pro Service ein einzelnes umfangreiches Event erzeugt
- Es kann mehr als 50 Felder enthalten, darunter Request-, Nutzer-, Zahlungs-, Fehler- und Umgebungsinformationen
- Das Beispiel-JSON enthält
user_id,subscription_tier,service_version,error_codeund damit den vollständigen Debugging-Kontext - So lässt sich mit einer einzigen Suche sofort analysieren, warum Zahlungen von Premium-Nutzern fehlschlagen
Query-Nutzung von Wide Events
- Wide Events werden nicht per einfacher Textsuche behandelt, sondern über strukturierte Datenabfragen
- Auf Basis hochkardinaler und hochdimensionaler Daten wird Debugging auf dem Niveau von Echtzeitanalysen möglich
- Beispiel: Eine Query wie „Aggregiere die Fehlerrate bei Zahlungen von Premium-Nutzern der letzten Stunde nach Fehlercode“ lässt sich sofort ausführen
Implementierungsmuster
- Das Event wird über den gesamten Lebenszyklus eines Requests hinweg aufgebaut und am Ende genau einmal ausgegeben
- In der Middleware werden Basisfelder wie
request_id,timestamp,methodundpathinitialisiert - Im Handler werden Nutzer-, Warenkorb-, Zahlungs- und Fehlerinformationen schrittweise ergänzt
- In der Middleware werden Basisfelder wie
- Am Ende wird mit
logger.info(event)ein einzelnes JSON-Event protokolliert
Kostenkontrolle durch Sampling
- Wenn pro Request 50 oder mehr Felder geloggt werden, steigen die Kosten stark an, daher ist Sampling nötig
- Einfaches zufälliges Sampling birgt das Risiko, Fehler zu übersehen
- Vorgeschlagen wird eine Tail-Sampling-Strategie
- Fehler (z. B. 500er) werden immer gespeichert
- Langsame Requests (ab p99) werden immer gespeichert
- Sitzungen von VIP-Nutzern oder mit bestimmten Flags werden immer gespeichert
- Der Rest wird nur zu 1–5 % zufällig gesampelt
- So lassen sich Kosten senken und zugleich kritische Events erhalten
Häufige Missverständnisse
- Structured Logging ≠ Wide Event: Ein JSON-Format allein reicht nicht aus, entscheidend ist der Kontext
- OpenTelemetry im Einsatz ≠ vollständige Observability: Standardisiert wird nur die Erfassung, was aufgezeichnet wird, bleibt Aufgabe der Entwickler
- Nicht dasselbe wie Tracing: Tracing zeigt den Ablauf zwischen Services, Wide Events liefern Kontext innerhalb eines Services
- Die Trennung „Logs für Debugging, Metriken für Dashboards“ ist unnötig — Wide Events decken beide Anwendungsfälle ab
- Die Annahme, hochgradig kardinale Daten seien teuer, ist veraltet; moderne Datenbanken wie ClickHouse oder BigQuery verarbeiten sie effizient
Die Wirkung der Einführung von Wide Events
- Debugging wandelt sich von Archäologie zu Analytics
- Statt zur Suche nach „fehlgeschlagener Nutzerzahlung“ die Logs von 50 Services mit grep zu durchsuchen,
entsteht ein analysegetriebener Ansatz auf Basis einer einzigen Query, etwa: „Zeige die Fehlerrate von Zahlungen bei Premium-Nutzern nach Fehlercode“ - Das Ergebnis: Logs werden von einem Werkzeug, das die Unwahrheit sagt, zu einem Datenbestand, der die Wahrheit liefert
1 Kommentare
Hacker-News-Kommentare
Der Text war schwer zu lesen und wirkte, als wäre er mit AI unterstützt worden. Trotzdem war die Botschaft wertvoll, und etwas mehr Kürze wäre wohl besser gewesen.
Dazu hatte ich in letzter Zeit folgende Gedanken.
Bei diesem Thema kommt man an Charity Majors nicht vorbei. Sie verbreitet seit über 10 Jahren die Konzepte „wide events“ und „observability“ und hat Honeycomb.io auf genau dieser Philosophie aufgebaut.
Heute lässt sich dieser Ansatz mit verschiedenen Tools umsetzen. Wichtig ist, wide events mit structured logs oder traces zu erfassen und Werkzeuge mit starker Visualisierung wie Zeitreihen und Histogrammen zu nutzen
Ich stimme einem Teil der Aussagen zu, aber beim Ansatz, nur ein einziges wide event zu hinterlassen, gibt es Fallstricke. Wenn mitten im Request eine Ausnahme oder ein Timeout auftritt, bleibt unter Umständen gar nichts übrig.
Außerdem gehen Logs des Standard-Logging-Frameworks der Sprache oder von Abhängigkeiten verloren.
Deshalb sollte das besser als zusätzliche Ebene über bestehenden Logs verwendet werden. Man vergibt IDs auf Request-/Session-Ebene und aggregiert dann z. B. in ClickHouse
log.error(data)undwide_event.attach(error, data)sind im Kern dasselbeDie Präsentation und die interaktiven Beispiele waren hervorragend. Am Ende lässt sich das aber auf „Fügt strukturierte Tags zu Logs hinzu“ zusammenfassen.
Bei wide logs fühlt sich der Nutzen im Verhältnis zu der zusätzlichen Komplexität und schlechteren Lesbarkeit nicht besonders groß an.
Oft reicht schon
grep "uid=user-123" application.log— muss man da wirklich noch die Versandart des Nutzers mitschreiben?(Zur Info: Im Android-Brave-Browser funktionierten die Checkboxen nicht)
grep '"uid": "user-123"'suchen. Mit der Option--contextsieht man außerdem die umgebenden ZeilenIch habe in der Halbleiterfertigung mit Systemen gearbeitet, die tausende Teilnehmer auf Message-Bussen hatten. Dabei fielen 300–400 MB Logs pro Stunde an, aber das ließ sich mit grep und CLI-Tools allein gut beherrschen.
Logs waren nur die Zeitreihe von Ereignissen, und Detailanalysen liefen über Oracle-Abfragen. Logs sind ein Werkzeug, um die Kausalität von Vorfällen zu verstehen
Logs sagen, „wann“ und „was“ passiert ist; das „warum“ findet man in der Kombination aus Code, Daten und Ereignissen
Persönlich finde ich Interfaces wie den ELK-Stack für intuitive Exploration eher unpraktisch. Logs muss man wichtig intuitiv lesend verfolgen können
Der Rat am Ende des Textes, „alle Fehler, Ausnahmen und langsamen Requests zu loggen“, ist eine gefährliche Idee.
Wenn etwa eine Abhängigkeit langsam wird, kann sich das Log-Volumen leicht verhundertfachen.
In Störungssituationen sollte ein Service eher weniger Arbeit leisten, um sich zu erholen — eine Log-Explosion führt dagegen leicht zu Kaskadenausfällen
Je höher das Log-Volumen, desto stärker passt sich die Sampling-Rate automatisch an, sodass das System nicht überlastet wird
In moderner Software ist es schwer, mit einem einzelnen Log vollständig zu beschreiben, „was passiert ist“.
Deshalb braucht man vertikale Korrelation (Vertical correlation) und horizontale Korrelation (Horizontal correlation).
Zwischen oberen und unteren Schichten im Stack sollte derselbe Korrelationswert geteilt werden, und bei Kommunikation zwischen Systemen braucht man Korrelation zwischen den Peers.
Solche Werte in APIs oder Protokolle einzubauen, ist schwierig, aber wenn man Transaktions-IDs früh mitplant, wird vollständiges Tracing möglich
Für einen einzelnen Text eine eigene Domain zu registrieren, halte ich für wenig nachhaltig.
Da jedes Jahr Verlängerungsgebühren anfallen, sind ein persönlicher Blog oder eine Subdomain aus meiner Sicht sinnvoller.
So etwas wie logging-sucks.boristane.com fände ich beispielsweise gut
Zur Aussage „Logs sind ein Relikt aus dem monolithischen Zeitalter“ denke ich, dass lokale Logs weiterhin sinnvoll sind.
Ihre ursprüngliche Aufgabe ist es, die Vorgänge eines lokalen Prozesses festzuhalten; um zu verstehen, was auf anderen Servern passiert, braucht man Transaction Tracing.
Schon mit Logs an den richtigen Stellen kann man die eigentliche Ursache finden
Logs mit reichhaltigem Kontext lassen sich zusammen mit Analyse-Engines auch für Produktverbesserungen nutzen
Der Aussage „Logge nicht, was der Code tut, sondern was mit dem Request passiert“ stimme ich zu, aber der Autor wirkte auf mich unerfahren.
Ich nenne das „bug parts logging“ und denke, dass dabei Frühwarnsignale wie Pfade, Häufigkeiten und Zeiten enthalten sein sollten.
Logging ist nicht dasselbe wie Metriken oder Audit. Wenn Logging fehlschlägt, muss die Verarbeitung weitergehen; ein fehlgeschlagenes Audit ist dagegen kritisch.
Wie beim „historian“-Konzept in SCADA-Systemen sollte man Beobachtbares (observables) und Bewertendes (evaluatives) unterscheiden.
Feingranulare Ereignisse eines Kraftstoffsensors sind etwa für Diagnosen nützlich, aber für die Frage „Reicht es bis zum Ziel?“ unnötig.
Am Ende ist entscheidend, klar zu definieren, was beobachtet und was bewertet werden soll
Speicherung, Transformation und Abfrage unterscheiden sich zwar, aber Konsumptionspunkte und Mechanismen können gleich entworfen werden.
Dadurch wird das Systemdesign einfacher, und langfristig gespeicherte Logs lassen sich später erneut verarbeiten