Files
gala-ki-spielwiese/agent-backend/docs/Logging.md

127 lines
4.4 KiB
Markdown
Raw Normal View History

# Logging — Dateieingang Service
## Stack
### Entwicklung (Dev Services)
Quarkus startet den gesamten Observability-Stack automatisch über Dev Services.
**Maven-Profil** (`pom.xml`):
```xml
<profile>
<id>grafana</id>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-observability-devservices-lgtm</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</profile>
```
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
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-opentelemetry</artifactId>
</dependency>
```
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 `<filename>` downloaded (`X` bytes)" |
| `zip-extract` | INFO | "ZIP `<filename>` extracted: `N` files, `M` directories" |
| `zip-extract` | ERROR | "ZIP `<filename>` 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: `<file>``<file>.processed`" |
| `sftp-rename` | INFO | "SFTP rename: `<file>``<file>.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: `<path>`" |
| 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