Logging und Tracing¶
mandari schreibt Logs auf die Standardausgabe der Container. In Produktion sind das JSON-Zeilen, die sich direkt in Loki, Elastic, OpenSearch oder CloudWatch einlesen lassen; in der Entwicklung ein lesbares Textformat. Jede Zeile trägt eine Request-Kennung, mit der sich alle Einträge einer Anfrage über Anwendung und Ingestor hinweg zusammensuchen lassen.
Umgebungsvariablen¶
| Variable | Anwendung (mandari) | Ingestor | Bedeutung |
|---|---|---|---|
LOG_FORMAT | json (Standard ohne DEBUG) / text | text (Standard) / json | Ausgabeformat |
LOG_LEVEL | INFO (Standard) | INFO (Standard) | Mindest-Level der eigenen Module; DEBUG wird in Produktion auf INFO gekappt |
OTEL_EXPORTER_OTLP_ENDPOINT | z. B. http://otel-collector:4318 | ebenso | Aktiviert das Tracing; leer = aus |
OTEL_SERVICE_NAME | mandari-web | mandari-ingestor | Dienstname in Traces und Logs |
Aufbau einer Log-Zeile¶
{"ts": "2026-09-09T09:12:44.120+00:00", "level": "INFO", "logger": "apps.work.motions",
"msg": "Antrag gespeichert", "service": "mandari-web",
"request_id": "243f21aac997439db2e38434bf1a2762",
"user_id": "879538a4-e32d-4a14-b07d-f519a8f02cae", "trace_id": "-", "span_id": "-"}
request_idkommt aus dem HeaderX-Request-ID, wenn der Reverse Proxy einen setzt (empfohlen, z. B. mit Caddyheader X-Request-ID {http.request.uuid}), sonst erzeugt die Anwendung eine. Der Wert steht auch in der Antwort, sodass Nutzer ihn bei Fehlermeldungen mitgeben können; die Session-API liefert ihn in jedem Fehlerobjekt.user_idist ausschließlich die interne UUID des angemeldeten Nutzers. Namen, E-Mail-Adressen oder Inhalte gehören nicht in Log-Zeilen.trace_id/span_idsind gesetzt, sobald Tracing aktiv ist.
Tracing mit OpenTelemetry¶
Mit gesetztem OTEL_EXPORTER_OTLP_ENDPOINT instrumentieren sich beide Dienste selbst (OTLP über HTTP):
- Anwendung: Django-Requests, Datenbank (psycopg), Redis, ausgehende HTTP-Aufrufe (requests, httpx).
- Ingestor: ausgehende HTTP-Aufrufe (httpx), Datenbank (asyncpg, SQLAlchemy).
Löst ein Admin im Betriebsmonitor einen Sync aus, übergibt die Anwendung trace_id und span_id mit dem Trigger an den Ingestor. Der Ingestor hängt seinen Sync-Span darunter – im Trace-Backend erscheint der gesamte Vorgang von der Anfrage bis zum Sync als ein Baum.
Als Empfänger eignet sich ein OpenTelemetry Collector, der an Grafana Tempo, Jaeger oder einen kommerziellen Dienst weiterleitet. Ohne Collector bleibt alles aus; es entstehen keine Verbindungen nach außen.
Empfehlungen für den Betrieb¶
- Logs über den Container-Treiber einsammeln (
docker logs, Promtail, Fluent Bit); keine Log-Dateien im Container. LOG_LEVEL=DEBUGnur kurzzeitig in der Entwicklung; in Produktion wird der Wert ohnehin aufINFObegrenzt.- Aufbewahrungsfristen der Logs im Löschkonzept berücksichtigen: Request-Kennungen und Nutzer-UUIDs sind pseudonym, aber personenbezogen.