Strukturiertes Logging bedeutet, Log-Ereignisse als maschinenlesbare Datensätze mit benannten Feldern auszugeben, meist als JSON statt als Freitext. Jedes Ereignis enthält Zeitstempel, Schweregrad, Meldung und Kontext wie Service-Name, Trace-ID und Request-Attribute. So lassen sich Logs ohne fehleranfälliges Parsing filtern, aggregieren und mit Traces korrelieren.
Was ist strukturiertes Logging?
Klassische Logs sind Textzeilen für Menschen: 2026-10-15 09:12:03 ERROR payment failed for order 8812 after 3 retries. Um eine Frage wie „Wie viele Zahlungsfehler pro Anbieter in der letzten Stunde?“ zu beantworten, muss jemand einen regulären Ausdruck für genau diesen Satz schreiben und pflegen. Strukturiertes Logging gibt dasselbe Ereignis als Datensatz mit expliziten Feldern aus:
{"timestamp":"2026-10-15T09:12:03.512Z","severity":"ERROR",
"message":"payment failed","service.name":"checkout",
"order.id":"8812","payment.provider":"acme","retry.count":3,
"trace_id":"4bf92f3577b34da6a3ce929d0e0e4736",
"span_id":"00f067aa0ba902b7"}
Jetzt ist jedes Feld direkt abfragbar, die Meldung bleibt stabil, auch wenn jemand sie umformuliert, und die Trace-ID verknüpft die Log-Zeile mit dem Request, der sie erzeugt hat. JSON ist das verbreitetste Format; logfmt (key=value-Paare) ist eine schlankere Alternative, die im Terminal gut lesbar bleibt.
Warum strukturiertes Logging wichtig ist
- Zuverlässige Abfragen: Ein Filter auf
payment.providerfunktioniert unabhängig von Formulierung und Logging-Bibliothek. - Aggregation: Zählwerte, Raten und Verteilungen lassen sich aus Log-Feldern berechnen, Logs werden so zur analytischen Datenquelle.
- Korrelation: Mit
trace_idundspan_idin jedem Datensatz springen Sie von einem langsamen Span zu seinen Logs und zurück. - Günstigere Verarbeitung: Pipelines können Felder gezielt routen, sampeln oder maskieren, statt mit Mustern zu raten.
- Datenschutz: Sensible Felder lassen sich über ihren Namen erkennen und schwärzen.
Das Log-Datenmodell von OpenTelemetry
OpenTelemetry definiert ein gemeinsames Datenmodell für Logs, damit Datensätze aus jeder Sprache und Quelle einheitlich verarbeitet werden können. Die wichtigsten Felder:
| Feld | Zweck |
|---|---|
| Timestamp / ObservedTimestamp | Wann das Ereignis eintrat und wann es erfasst wurde |
| SeverityText / SeverityNumber | Ursprünglicher Level-Name und normalisierte Zahl von 1 bis 24 (TRACE 1–4, DEBUG 5–8, INFO 9–12, WARN 13–16, ERROR 17–20, FATAL 21–24) |
| Body | Die eigentliche Meldung, als String oder strukturierter Wert |
| Attributes | Ereignisspezifische Schlüssel-Wert-Paare |
| TraceId / SpanId / TraceFlags | Verknüpfung mit dem aktiven Trace-Kontext |
| Resource / InstrumentationScope | Wer den Log erzeugt hat, z. B. service.name, k8s.pod.name |
Log Bridges von OpenTelemetry verbinden bestehende Bibliotheken wie Log4j, Logback, Pythons logging oder Winston mit dem SDK. Der Trace-Kontext wird so automatisch angehängt, und Logs werden gemeinsam mit Traces und Metriken per OTLP exportiert.
Gute Log-Felder gestalten
- Einheitliche Namen verwenden, über alle Services hinweg und idealerweise an den Semantic Conventions von OpenTelemetry ausgerichtet (
http.route,user_agent.original,exception.type). - Die Meldung konstant halten und variable Daten in Felder auslagern. „payment failed“ mit
order.idals Feld ist besser als eine Meldung, in der die ID steckt. - Datentypen stabil halten. Ist
retry.countin einem Service eine Zahl und im anderen ein String, scheitern Aggregationen. - Ereignisse loggen, keine Erzählungen. Ein gut strukturierter Datensatz am Ende einer Operation ist oft nützlicher als zehn Zeilen für jeden Zwischenschritt.
- Level bewusst wählen. ERROR sollte bedeuten, dass eventuell jemand handeln muss; routinemäßige Retries sind WARN oder INFO.
Bestehende Systeme auf strukturierte Logs umstellen
Kaum ein Team kann alle Log-Anweisungen auf einmal umschreiben, und das ist auch nicht nötig. Eine pragmatische Migration hat drei Schritte. Erstens stellen Sie die Log-Ausgabe jedes Services auf einen JSON-Encoder oder -Formatter um; die meisten Bibliotheken unterstützen das per Konfiguration, und die bisherigen Meldungen landen einfach im Feld message. Zweitens ergänzen Sie gemeinsamen Kontext automatisch: Service-Name, Umgebung, Version und den aktiven Trace-Kontext über eine OpenTelemetry Log Bridge oder eine Logging-Middleware. Drittens überarbeiten Sie die wichtigsten Log-Anweisungen, typischerweise Fehler und zentrale fachliche Ereignisse, sodass ihre variablen Teile zu Feldern werden. Ältere Komponenten, die Sie nicht ändern können, parsen Sie einmalig im Collector oder in der Pipeline und geben ab dort strukturierte Datensätze weiter.
Typische Fehler
- JSON in eine Textmeldung einbetten, wodurch escapte Strings entstehen, die niemand abfragen kann.
- Personenbezogene Daten, Secrets oder vollständige Request-Bodies loggen; maskieren Sie diese an der Quelle oder in der Pipeline.
- Jedes Log-Feld zu einem indexierten Label oder einer Metrik-Dimension machen, was die Kardinalität und die Kosten explodieren lässt.
- Unterschiedliche Zeitformate und Zeitzonen zwischen Services; verwenden Sie ISO 8601 in UTC oder Epoch-Nanosekunden.
- DEBUG-Logging in Produktion mit vollem Volumen aktiv lassen, sodass wenige laute Services den Ingest dominieren.
Wie Obseria unterstützt
Obseria Log Analytics speichert Logs mit Schema-on-Read in Obseria Lake. JSON-Felder sind damit ohne vorab definierte Indizes abfragbar, unstrukturierte Zeilen lassen sich bei Bedarf zur Abfragezeit parsen. Obseria Pipeline maskiert personenbezogene Daten, entfernt Rauschen und routet Logs beim Ingest; Logs mit Trace-IDs werden automatisch mit ihren Traces verknüpft. Eine Abfrage wie fetch logs | filter service == "checkout" | summarize count(), by:{payment.provider} läuft direkt auf den strukturierten Feldern. Wie Logs mit den anderen Signalen zusammenspielen, lesen Sie unter Was ist Observability?.