# Logging — Dateieingang Service ## Stack ### Entwicklung (Dev Services) Quarkus startet den gesamten Observability-Stack automatisch über Dev Services. **Maven-Profil** (`pom.xml`): ```xml grafana io.quarkus quarkus-observability-devservices-lgtm provided ``` Mit `./mvnw quarkus:dev -Pgrafana` startet automatisch: - **Loki** — Log-Aggregation - **Grafana** — Visualisierung / Dashboards (http://localhost:3000, admin/admin) - **Tempo** — Distributed Tracing (optional nutzbar) - **Mimir** — Metriken (optional nutzbar) **Laufzeit-Dependency** (immer aktiv): ```xml io.quarkus quarkus-opentelemetry ``` Quarkus schickt Logs via **OTLP** direkt an den LGTM-Stack — kein Promtail, kein Log-File-Scraping. ### Produktion Einen OTLP-fähigen Collector vor dem produktiven Loki/Grafana-Stack betreiben: - **Grafana Alloy** (Nachfolger von Promtail, OTLP-nativ) - **OpenTelemetry Collector** Quarkus-Konfiguration bleibt identisch — nur der OTLP-Endpoint-URL ändert sich. --- ## Log-Hierarchie (MDC-Felder) Ein ZIP-Verarbeitungslauf erzeugt Logs auf zwei Ebenen: ``` runId → ein ZIP-Verarbeitungslauf (gesetzt am Anfang jeder ZIP) step → aktueller Pipeline-Step (gesetzt zu Beginn jedes Steps) ``` `zipFilename` wird zusätzlich als menschenlesbarer Kontext geloggt, ist aber kein MDC-Feld (kein Filter-Kriterium, da runId eindeutiger ist). ### MDC-Felder im Detail | Feld | Typ | Beispielwert | Gesetzt in | Gelöscht in | |---|---|---|---|---| | `runId` | UUID | `a1b2c3d4-...` | Beginn ZIP-Verarbeitung | `finally` nach ZIP | | `step` | String | `oci-upload` | Beginn jedes Pipeline-Steps | Ende jedes Steps | `MDC.clear()` wird **immer im `finally`-Block** nach jeder ZIP aufgerufen — kein Kontext sickert in die nächste ZIP. ### Filtermöglichkeiten in Grafana (LogQL) ```logql # Alle Logs eines Verarbeitungslaufs {service="dateieingang"} | json | runId="a1b2c3d4" # Alle Logs eines Pipeline-Steps über alle Läufe {service="dateieingang"} | json | step="oci-upload" # Nur Fehler {service="dateieingang"} | json | level="ERROR" # Fehler in einem bestimmten Lauf {service="dateieingang"} | json | runId="a1b2c3d4" | level="ERROR" ``` **Loki-Labels** (wenige, statische Werte — keine hohe Kardinalität): - `service` = `dateieingang` - `level` = `INFO` / `WARN` / `ERROR` - `environment` = `dev` / `prod` Alle anderen Felder (`runId`, `step`) werden per `| json` im Query-Body gefiltert, nicht als Label. --- ## Log-Events pro Step | Step | Level | Event / Inhalt | |---|---|---| | `api-trigger` | INFO | "Processing triggered by APEX, starting async pipeline" | | `api-trigger` | WARN | "Pipeline already running, rejecting trigger (409)" | | `sftp-list` | INFO | "`N` new ZIP files found on SFTP" | | `sftp-list` | INFO | "No new ZIP files found on SFTP" | | `sftp-download` | INFO | "ZIP `` downloaded (`X` bytes)" | | `zip-extract` | INFO | "ZIP `` extracted: `N` files, `M` directories" | | `zip-extract` | ERROR | "ZIP `` is corrupt or unreadable" | | `oci-upload` | INFO | "Uploaded `N` files + marker to OCI in `X`ms" | | `oci-upload` | WARN | "OCI upload attempt `N`/3 failed (503), retrying..." | | `oci-upload` | ERROR | "OCI upload permanently failed (403) — check credentials" | | `sftp-rename` | INFO | "SFTP rename: `` → `.processed`" | | `sftp-rename` | INFO | "SFTP rename: `` → `.error`" | | `ords-notify` | INFO | "ORDS endpoint called, HTTP 200" | | `ords-notify` | WARN | "ORDS call failed (attempt `N`/3), retrying..." | | `ords-notify` | WARN | "ORDS notification failed — marker is present, APEX will pick up next run" | | `cleanup` | DEBUG | "Local files deleted: ``" | | Alle | ERROR | Fehler mit vollem MDC-Kontext, Exception-Message (nie Passwörter/Keys loggen) | --- ## Fehlerverhalten (Logging-Perspektive) - Fehler werden **immer vor** dem Rename zu `.error` geloggt — damit der Log-Eintrag auch dann vorhanden ist, wenn das Rename selbst fehlschlägt - Der `runId`-Kontext bleibt für den gesamten ZIP-Verarbeitungslauf erhalten — alle Logs einer ZIP sind in Grafana über `runId` filterbar - Retry-Verhalten: siehe `Architecture.md` — Abschnitt Fehlerbehandlung