Dokumentation aktualisiert auf quarkus und besser strukturiert
This commit is contained in:
126
agent-backend/docs/Logging.md
Normal file
126
agent-backend/docs/Logging.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user