Logging-Kontext & Single-Line JSON-Logging¶
Die Microservices der Open Data Infrastruktur (ODI) nutzen ein zentralisiertes, strukturiertes Logging auf Basis von Pino / NestJS-Pino. Auf allen produktiven und stagingnahen Umgebungen ist das JSON-Logging aktiviert (LOG_JSON=true).
Konzept: Single-Line JSON-Logging¶
In verteilten Systemen zerschneiden traditionelle mehrzeilige Logs (z. B. Stacktraces über 20 Zeilen) das Log-Stream-Aggregat (Loki/Promtail), was zu unvollständigen Log-Fragmenten und erschwerter Fehlersuche führt.
In den ODI-Diensten gilt daher die strikte Regel: Jedes Fehler-Event wird als genau ein durchsuchbares JSON-Objekt auf einer einzigen Zeile ausgegeben.
Vorteile von Single-Line JSON:¶
- Ein Log-Eintrag = Ein Fehler-Event: Kein Zerschneiden von Stacktraces in Loki/Grafana.
- Vollständigkeit: Stacktraces (
error.stack), Anfragedaten (request) und verschachtelte Fehlerursachen (internalError) bleiben vollständig im Objekt erhalten. - Automatische Auswertbarkeit: Logql-Queries in Grafana können jedes Feld direkt indizieren und filtern.
Aufbau des JSON-Log-Objekts¶
Ein Fehler-Log-Eintrag besitzt folgende standardisierte Struktur:
{
"level": 50,
"time": "27.07.2026, 13:05:00",
"context": "CkanClient",
"requestId": "b92f180a-912c-4c6e-821f-0e1234567890",
"timestamp": "27.07.2026, 13:05:00",
"request": {
"path": "/backend/datasets/sync",
"method": "POST",
"requestId": "b92f180a-912c-4c6e-821f-0e1234567890"
},
"error": {
"name": "ServerError",
"message": "Connection failed to Ckan",
"context": "CkanClient",
"stack": "Error: Connection failed to Ckan\n at CkanClient.post (/app/dist/ckan.client.js:42:11)\n at processTicksAndRejections (node:internal/process/task_queues:95:5)",
"type": "INTERNAL"
},
"msg": "[ServerError] Connection failed to Ckan | at CkanClient.post (/app/dist/ckan.client.js:42:11)"
}
Erklärung der Log-Felder¶
1. context (Dynamischer Log-Kontext)¶
Anstatt des generischen Filter-Namens (z. B. AllExceptionsFilter) wird im Feld context dynamisch der tatsächliche Ursprung im Code ermittelt:
* Befindet sich am Fehler-Objekt ein context-Attribut (z. B. bei custom ServerError oder ApiError), wird dieses verwendet (z. B. "CkanClient", "UserService", "MetadataService").
* Andernfalls wird der Fehler-Klassenname genommen (z. B. "TypeError", "AxiosError").
Vorteil für den Admin: In der Log-Spalte context ist sofort ersichtlich, welches Modul oder welche Komponente den Fehler verursacht hat.
2. msg (Formatiertes Log-Message-Signal)¶
Die Nachricht msg wird dynamisch im Format [FehlerName] Fehlermeldung | at CodeOrt aufgebaut:
* Beispiel: [ServerError] Connection failed to Ckan | at CkanClient.post (/app/dist/ckan.client.js:42:11)
* Vorteil für den Admin: In Grafana Loki sieht man direkt in der Log-Zeilenvorschau die konkrete Ursache und den Aufrufort, ohne den JSON-Baum aufklappen zu müssen.
3. requestId (Correlation ID)¶
Die eindeutige Request ID der eingehenden HTTP-Anfrage (siehe Fehleranalyse & Request ID). Über diese ID kann der gesamte Anfrageverlauf serviceübergreifend im Grafana Dashboard gefiltert werden: * 🚀 Produktion (Prod): grafana.odi.schleswig-holstein.de/d/betrieb-request-id-v1/request-id-suche * 🧪 Staging (Stage): grafana.odi-stage.schleswig-holstein.de/d/betrieb-request-id-v1/request-id-suche
4. error & Verschachtelte Fehler (internalError)¶
Das error-Objekt wird von der Filter-Pipeline serialisiert. Liegt ein verschachtelter Fehler vor (z. B. ein ApiError, der einen ServerError kapselt, welcher wiederum einen ClientError mit HTTP 502 Daten enthält), wird die gesamte Fehlerkette unter internalError lückenlos im JSON dargestellt.
Verhalten bei lokaler Entwicklung (LOG_JSON=false)¶
Bei der lokalen Entwicklung im Terminal ist LOG_JSON standardmäßig deaktiviert (false). Für maximale Lesbarkeit beim Entwickeln wird dort weiterhin eine formatierte, mehrzeilige Konsolenausgabe mit Trennlinien erzeugt:
------------------------------------------------
| ERROR DETAILS |
------------------------------------------------
Timestamp: 27.07.2026, 13:05:00
Request ID: b92f180a-912c-4c6e-821f-0e1234567890
Request Path: /backend/datasets/sync
Method: POST
Error Message: Connection failed to Ckan
Environment Mode: DEVELOPMENT
Stack Trace:
------------------------------------------------
Error: Connection failed to Ckan
at CkanClient.post (/app/dist/ckan.client.js:42:11)
------------------------------------------------