Ein Backend kann technisch stabil laufen – und trotzdem schwer zu betreiben sein. Oft liegt es an Logs: Nachrichten sind uneinheitlich, wichtige Informationen fehlen oder landen irgendwo im Text. Genau hier hilft Structured Logging: Log-Einträge bestehen nicht nur aus einem Satz, sondern aus klaren Feldern (z. B. level, request_id, user_id). Damit lassen sich Fehler schneller eingrenzen, Probleme besser korrelieren und Log-Daten verlässlicher auswerten.
Dieser Artikel erklärt verständlich, wie strukturierte Logs funktionieren, welche Felder sich in Webanwendungen bewährt haben und wie sich der Wechsel Schritt für Schritt umsetzen lässt – ohne die Entwickler:innen mit „Log-Wildwuchs“ zu überfordern.
Was strukturierte Logs von „normalen“ Logs unterscheidet
Text-Logs: gut fürs Lesen, schlecht fürs Filtern
Klassische Logs sind Textzeilen wie: „User 42 konnte Bestellung 123 nicht speichern“. Das ist für Menschen lesbar, aber für Systeme schwer zu verarbeiten. Wer später nach allen Fehlern rund um „Bestellung 123“ sucht, ist auf exakte Textmuster angewiesen. Kleine Unterschiede in Formulierungen reichen, damit Treffer fehlen.
Strukturierte Logs sind anders: Sie bestehen aus festen Schlüsseln und Werten (Key-Value), häufig im JSON-Format. Ein Log-Eintrag enthält dann z. B. order_id, user_id, error_code und einen klaren message-Text. Das wirkt zunächst ausführlicher, spart aber später enorm Zeit.
Maschinenlesbar heißt nicht „unlesbar“
Ein verbreitetes Missverständnis: Strukturierte Logs seien nur für Tools gedacht. In der Praxis lassen sie sich weiterhin gut lesen – vor allem, wenn die Felder konsistent sind. Viele Log-Viewer können JSON zudem formatiert darstellen, sodass relevante Felder sofort sichtbar sind.
Welche Probleme Structured Logging im Alltag wirklich löst
Fehler schneller eingrenzen
Wenn ein Fehler in Produktion auftritt, ist die zentrale Frage: Unter welchen Bedingungen passierte das? Strukturierte Logs liefern diese Bedingungen als Felder. Statt im Text zu raten, kann gezielt gefiltert werden: „zeige alle ERROR-Logs mit route=/checkout und order_id=123“.
Zusammenhänge erkennen: Requests, Jobs und Services verbinden
In modernen Anwendungen verteilt sich Arbeit oft: Ein HTTP-Request stößt einen Hintergrundjob an, der wiederum andere Services anspricht. Ohne gemeinsamen Bezug entsteht ein Puzzle. Mit einer gemeinsamen Kennung (z. B. einer Request-ID) lassen sich Einträge über Komponenten hinweg verbinden. Das ist besonders wertvoll, wenn mehrere Systeme beteiligt sind.
Weniger Lärm durch konsistente Levels und Felder
Ein häufiges Problem sind „ERROR“-Logs für erwartbare Situationen oder Debug-Ausgaben in Produktion. Strukturierte Logs fördern Disziplin: Das Schema legt nahe, wann ein Feld gesetzt wird und welches level passt. Das reduziert Alarmmüdigkeit und erleichtert Monitoring.
Felder, die sich in Web-Backends bewährt haben
Die Basis: message, level, timestamp
Auch strukturierte Logs brauchen einen klaren Nachrichtentext, der schnell verständlich ist. Dazu kommt ein Level (z. B. debug/info/warn/error) sowie ein Zeitstempel. Viele Logger fügen Zeitstempel automatisch hinzu; wichtig ist vor allem, dass das Format einheitlich bleibt.
Request-Kontext: route, method, status_code und Laufzeit
Für HTTP-Anwendungen sind Felder rund um den Request entscheidend:
- request_id: eine eindeutige Kennung pro Request, idealerweise auch in Antworten/Headers sichtbar
- method: GET/POST/…
- route oder path: z. B. /api/orders
- status_code: z. B. 200, 404, 500
- duration_ms: Laufzeit in Millisekunden
Gerade request_id ist ein Gamechanger: Damit lassen sich alle Logs zu einem Problemfall bündeln, auch wenn zwischendurch in andere Funktionen oder Services verzweigt wird.
Business-Kontext: user_id, tenant_id, order_id
Neben Technikdaten braucht es häufig Fachkontext. Dazu zählen IDs wie user_id, account_id, tenant_id (bei Multi-Tenant-Systemen) oder order_id. Wichtig: Nicht alles muss immer geloggt werden. Entscheidend ist, dass die Felder vorhanden sind, wenn sie relevant sind.
Fehler sauber abbilden: error.type, error.message, error.stack
Fehler sollten nicht nur als Text erscheinen. Sinnvoll ist eine strukturierte Gruppe, z. B. error.type (Exception-Klasse), error.message (kurz), error.stack (Stacktrace). In einigen Sprachen/Frameworks ist das Standard, aber es lohnt sich zu prüfen, ob Stacktraces wirklich nur bei ERROR geloggt werden – sonst blähen sie Logs unnötig auf.
So lässt sich der Umstieg ohne Big-Bang umsetzen
Startpunkt: ein kleines, festes Schema definieren
Bevor im Team jeder eigene Felder erfindet, hilft ein minimales Schema, das überall gilt. Das Schema muss nicht perfekt sein, aber stabil. Typisch ist: message, level, timestamp, service, env, request_id, route, method, status_code, duration_ms.
Wer bereits eine gute Basis in Sachen Fehlerbehandlung hat, kann das gut verzahnen. Passend dazu hilft der Artikel API-Fehler richtig behandeln – robuste Webanwendungen Schritt für Schritt, weil dort klar wird, wie Fehler konsistent in Antworten und Logs landen.
Middleware/Interceptor nutzen: Kontext automatisch anhängen
Damit nicht jede Controller-Methode dieselben Felder manuell setzt, sollte der Kontext an einer zentralen Stelle erzeugt werden: Middleware (z. B. Express), Filter (z. B. in Spring), Middleware/Kernel in PHP-Frameworks oder ASGI/WSGI-Middleware in Python. Dort werden request_id, route, method und die Laufzeit gemessen und in jeden Log-Eintrag eingefügt.
Log-Nachrichten standardisieren: weniger kreativ, mehr klar
Log-Text ist weiterhin wichtig – aber er sollte kurz und konsistent sein. Ein gutes Muster ist: „Aktion + Ergebnis“. Beispiele: „order.create failed“, „payment.capture succeeded“. Details gehören in Felder wie order_id, provider, amount oder error.type. So bleiben Logs filterbar und trotzdem verständlich.
Praktische Vorgehensliste für die nächsten 30–60 Minuten
- Entscheiden, welches Format ausgegeben wird (oft JSON pro Zeile).
- Ein Mini-Schema festlegen: message, level, service, env, request_id.
- Middleware/zentralen Hook einbauen, der request_id erzeugt oder übernimmt.
- In der Request-Log-Zeile route, method, status_code und duration_ms ergänzen.
- Bei Fehlern: error.type, error.message und bei Bedarf error.stack loggen.
- 2–3 kritische Endpunkte umbauen und prüfen, ob Suchen/Filtern spürbar leichter wird.
Typische Stolpersteine und wie sie vermieden werden
Zu viele Felder: Logs werden teuer und unübersichtlich
Strukturierte Logs verführen dazu, „alles“ mitzuschreiben. Das ist selten sinnvoll. Besser: Wenige Standardfelder plus gezielte Business-IDs. Zusätzliche Details können bei Bedarf temporär per Debug-Level aktiv sein. Das Ziel ist nicht Vollständigkeit, sondern schnelle Diagnose.
Personenbezogene Daten und Secrets in Logs
Ein großes Risiko sind Daten, die nicht in Logs gehören: Passwörter, Tokens, Session-Cookies, vollständige Kreditkartendaten oder komplette Request-Bodies. Hier hilft eine klare Regel: Logs dürfen Identifikatoren (z. B. user_id) enthalten, aber keine sensiblen Inhalte. Für HTTP gilt: Header und Body nur sehr selektiv loggen und Felder wie Authorization konsequent maskieren.
Wenn in der Anwendung ohnehin mit Tokens gearbeitet wird, sollte klar sein, dass diese nicht im Klartext ins Logging gehören. Als Hintergrundwissen ist HTTP-Request-Header verstehen – wichtige Felder im Alltag hilfreich, um typische „gefährliche“ Header sicher einzuordnen.
Unklare Levels: alles ist WARN oder ERROR
Wenn jedes Problem als ERROR geloggt wird, ist nichts mehr priorisierbar. Ein praktikables Bild:
- info: normale, erwartbare Ereignisse (Start/Stop, erfolgreiche Requests, Jobs beendet)
- warn: unerwartet, aber System funktioniert (Retry, Fremdsystem langsam, Validierung knapp verfehlt)
- error: Request/Job kann nicht korrekt abgeschlossen werden
Wer zusätzlich Fehler sauber in APIs abbildet, kann Log-Level und HTTP-Statuscodes besser synchron halten. Dazu passt HTTP Statuscodes verstehen – Fehler sauber behandeln.
Vergleich: strukturierte vs. freie Log-Zeilen
| Aspekt | Freie Text-Logs | Structured Logging |
|---|---|---|
| Suche & Filter | abhängig von Textmustern | Filter nach Feldern (z. B. user_id, status_code) |
| Konsistenz | oft unterschiedlich je Entwickler:in | Schema fördert gleiche Felder & Schreibweisen |
| Fehleranalyse | Kontext fehlt häufig | Kontext steckt in Feldern, leichter korrelierbar |
| Aufwand beim Start | sehr niedrig | initial höher (Schema, Middleware, Standards) |
Häufige Fragen aus der Praxis
Reicht es nicht, einfach „mehr zu loggen“?
Mehr Text führt selten zu mehr Klarheit. Ohne Struktur sind zusätzliche Zeilen schwer zu filtern und erzeugen Rauschen. Struktur hilft, gezielt die richtigen Einträge zu finden – selbst wenn weniger geloggt wird.
Muss jedes Log im JSON-Format sein?
Nicht zwingend, aber JSON ist verbreitet und gut von Tools lesbar. Wichtig ist vor allem: feste Felder, konsistente Namen und keine „versteckten“ Informationen nur im Nachrichtentext.
Was ist der kleinste sinnvolle Einstieg?
Ein zentraler Request-Logger mit request_id, route, status_code und duration_ms bringt meist den größten Effekt. Danach können schrittweise Business-IDs ergänzt werden, wo sie wirklich helfen.
Wie passt das zu Monitoring und Alerts?
Strukturierte Logs sind eine gute Basis für Regeln wie „zähle ERROR pro route“ oder „zeige alle WARN mit provider=stripe“. Monitoring wird dadurch weniger „Bauchgefühl“ und stärker datengetrieben, ohne dass dafür komplexe neue Systeme nötig sind.
Welche Rolle spielt Korrelations-ID vs. Request-ID?
Viele Teams nutzen beide Begriffe ähnlich. Praktisch ist: Eine ID pro eingehenden Request (request_id) und optional eine übergreifende ID, die mehrere Requests/Jobs verbindet (correlation_id). Wichtig ist weniger der Name als die konsequente Nutzung.

