Logs sind das Stiefkind vieler Systeme. Man braucht sie selten, aber dann dringend. In Kubernetes-Clustern mit dutzenden Services und flüchtigen Pods wird das Log-Management schnell zum Albtraum — es sei denn, man investiert in ein passendes Tooling.
Warum nicht Elasticsearch
Wir haben lange mit einem Elasticsearch-basierten Stack gelebt. ES ist mächtig, hat aber zwei Probleme im Alltag: Es ist teuer (RAM-hungrig, skaliert linear mit Datenvolumen), und es lädt Teams ein, komplexe Index-Strategien zu bauen, deren Wartung irgendwann keiner mehr übernimmt.
Für ein mittelgroßes Setup (unsere Größenordnung: ~50 GB Logs pro Tag bei 20 Services) war ES überdimensioniert. Jede Suche bedeutete Volltext-Indexing im Hintergrund, und die resultierenden Indexe verbrauchten dreimal so viel Platz wie die Rohdaten.
Wie Loki anders funktioniert
Loki ist grundlegend anders: Es indiziert nur Metadaten (Labels), nicht den Log-Inhalt. Das macht Loki kompakt und günstig — ein Cluster, der mit 8 GB RAM auskommt, wo ES 64 GB brauchte.
Der Tradeoff: Volltext-Suche ist langsamer. In der Praxis ist das akzeptabel, weil gute Label-Hygiene (nach Service, Namespace, Severity) die Suche meistens schon eingrenzt, bevor Volltext ins Spiel kommt.
Unser Label-Schema
Wir haben uns auf fünf Standard-Labels festgelegt, die jeder Service ausspielen MUSS:
service— Name des Service (z.B.web-core-gateway)namespace— Kubernetes-Namespaceenv— prod/staging/devlevel— debug/info/warn/errorrequest_id— nur bei Service-Logs mit Request-Kontext
request_id ist der wichtigste. Er ermöglicht Cross-Service-Korrelation: Eine Anfrage trifft das Gateway, geht weiter an den Orchestrator, dann an drei Agents. Alle Logs zu dieser Anfrage tragen dieselbe request_id und können in Grafana zu einer Timeline zusammengeführt werden.
Der "LogQL-Moment"
LogQL ist Lokis Query-Sprache. Sie orientiert sich an PromQL und fühlt sich für Prometheus-Nutzer vertraut an. Der entscheidende Moment im Team war, als wir gelernt haben, | json und | unwrap zu nutzen — JSON-Logs können in LogQL direkt geparst und gefiltert werden.
Eine typische Query bei uns:
{service="web-core-gateway"} | json | duration > 1s
Das gibt uns in Sekunden alle langsamen Requests im Gateway. Ohne Index-Vorbereitung, ohne Aggregations-Pipeline.
Grafana als Cockpit
Wir haben sieben Dashboards, die wir täglich nutzen:
- Cluster-Gesundheit: CPU/Memory/Disk pro Node
- Service-Health: Up/Down, Latenz p50/p95/p99 pro Service
- Error-Rate: 5xx-Rate pro Endpoint, Fehler-Top-10
- RAG-Pipeline: Retrieval-Latenz, LLM-Latenz, Token-Verbrauch
- Auth-Events: Login-Erfolge/Fehler, Token-Rotationen
- Content-Pipeline: Indexer-Durchsatz, Index-Aktualität
- Business-KPIs: Aktive Sessions, Conversion-Rates
Der Schlüssel: Jedes Dashboard beantwortet eine spezifische Frage. Keine Dashboards, in denen "alles mögliche" steht.
Alerting: Weniger ist mehr
Wir haben bewusst nur wenige Alerts. Die Regel: Ein Alert muss einen klaren Handlungs-Hinweis geben. Ein Alert, der nur "irgendwas ist komisch" sagt, wird innerhalb von zwei Wochen gemutet und nie wieder angeschaut.
Unsere Alert-Liste:
- Service down > 2 Minuten → Slack + PagerDuty
- 5xx-Rate > 1% über 5 Minuten → Slack
- Disk > 85% → Slack + Ticket
- Cert-Expiry in < 7 Tagen → Slack
Das war's. Alle anderen Auffälligkeiten findet man in den Dashboards.
Ausblick
Nächster Schritt ist OpenTelemetry Traces, um Service-übergreifende Latenzen besser zu verstehen. Loki + Tempo + Mimir (alles Grafana-Stack) sollen das liefern, ohne dass wir einen zweiten Observability-Provider dazubekommen.