Das Problem
Alert-Regeln in Loki sind Metrikabfragen. Ein Alert meldet, dass count_over_time({app="api"} |= "ERROR" [5m]) über null lag, aber nicht, welche Zeilen gepasst haben. Wer Bereitschaft hat, öffnet Grafana und baut die Abfrage von Hand nach. grafana/loki#5844 "Add log message in alert" hat 113 Reaktionen und ist seit April 2022 offen. Der Vorschlag dort lautet, die Log-Abfrage aus der Metrikabfrage abzuleiten. Der Produktmanager von Loki hat das Issue im April 2025 bestätigt, der letzte Kommentar vom März 2026 fragt nach dem Stand. Auf Stack Overflow hat How to send alert for every error in my logs using Promtail / Loki - AlertManager? etwa 15.000 Aufrufe.
Die Umgehung im Thread ist, die Meldung per | pattern in ein Label zu ziehen und danach zu gruppieren. Das geht nur für kurze Meldungen mit wenigen verschiedenen Werten und liefert eine Zeile pro Alert. Grafana-verwaltete Alerts auf einer Loki-Datenquelle haben dieselbe Lücke.
Wie es arbeitet
loki-alert-lines ist ein Go-Programm ohne Abhängigkeiten außerhalb der Standardbibliothek. Es nimmt Alerts unter /api/v1/alerts und /api/v2/alerts an, sodass alertmanager_url des Rulers darauf zeigen kann, und leitet sie an einen oder mehrere Alertmanager weiter.
Für jeden feuernden Alert liest es den Regelausdruck aus der Generator-URL. Es sucht den Log-Bereichsausdruck in der Metrikabfrage (Stream-Selektor, Pipeline und Zeitbereich, mit Rücksicht auf Strings und verschachtelte Klammern), entfernt | unwrap-Stufen und hängt für jedes Label der Alert-Instanz einen Label-Filter an. Ausgenommen sind alertname und die statischen Labels der Regel, die es aus der Ruler-API liest. Diese Log-Abfrage läuft über das Fenster von startsAt minus Zeitbereich bis jetzt, die neuesten Zeilen kommen in die Annotation log_lines. Eine Vorlage kann dann {{ .Annotations.log_lines }} in Slack, Mail oder einem anderen Empfänger zeigen. Passt die Ableitung nicht, ersetzt eine Annotation logs_query an der Regel die abgeleitete Abfrage.
Bei Grafana-verwalteten Regeln enthält die Generator-URL die UID der Regel. Der Proxy liest die Regel über die Provisioning-API von Grafana, nimmt die Abfrage auf der Loki-Datenquelle und schickt die Log-Abfrage über den Datenquellen-Proxy von Grafana. Direkter Zugriff auf Loki ist dafür nicht nötig.
Beispiel
Aus dem End-to-End-Test gegen Loki 3.7.9 und Alertmanager v0.35.0. Die Regel sum by (msg) (count_over_time({job="json"} | json | level="error" [1m])) > 2 feuerte für msg="disk full", und der Alert kam so im Alertmanager an:
log_lines_query: {job="json"} | json | level="error" | msg="disk full"
log_lines:
2026-11-13T05:17:58.9541812Z {"level":"error","msg":"disk full","n":1}
2026-11-13T05:17:59.9541812Z {"level":"error","msg":"disk full","n":2}
2026-11-13T05:18:00.9541812Z {"level":"error","msg":"disk full","n":3}
Die Zeile mit msg="other" und die mit level="info" fehlen darin.
Tests
Unit-Tests decken die LogQL-Ableitung ab (unwrap, offset, Klammern in Strings, ip()-Filter, Grafanas Zeitbereich $__auto), dazu die Label-Filter, das Weiterleiten mit Tenant- und Auth-Headern und den Grafana-Pfad gegen ein nachgebautes Grafana. Der End-to-End-Test startet Loki mit auth_enabled: true und Ruler, einen Alertmanager mit Webhook-Empfänger und im CI zusätzlich Grafana mit provisionierter Loki-Datenquelle, Alert-Regel und Alertmanager-Kontaktpunkt. Er schreibt Logzeilen, wartet auf die Alerts und prüft die Annotationen im Alertmanager, die Webhook-Benachrichtigung und die Metriken des Proxys. Das CI testet gegen Loki 2.9.17, 3.4.6, 3.7.9 und latest, Alertmanager v0.27.0, v0.28.1, v0.35.0 und latest sowie Grafana 12.4.15, 13.3.1 und latest.
Die echten Server zeigten zwei Dinge, die die Unit-Tests nicht gezeigt hatten. Loki 2.9.17 und 3.7.9 schreiben keine Generator-URL im Prometheus-Format /graph?g0.expr=, sondern einen Grafana-Explore-Link mit dem Ausdruck in einem JSON-Parameter. Die erste Version las ihn nicht und fiel unbemerkt auf die Ruler-API zurück; eine Metrik für die Herkunft jeder Abfrage macht das jetzt sichtbar. Außerdem schickte Grafana feuernde Alerts mit endsAt auf dem Nullzeitpunkt (Jahr 1), die der Proxy für aufgelöst hielt.
Grenzen
Die LogQL-Verarbeitung ist kein vollständiger Parser. Bei zwei Log-Abfragen in einer Regel, etwa einem Verhältnis, nimmt sie die erste. Labels, die erst nach der Log-Abfrage entstehen, zum Beispiel durch label_replace, werden zu Filtern, auf die nichts passt; solche Regeln brauchen logs_query. Alertmanager schickt keine neue Benachrichtigung, nur weil sich die Zeilen geändert haben. Nicht getestet: Mimir oder Cortex als Alertmanager, hochverfügbare Ruler gegen einen echten Cluster sowie echte Slack- oder Mail-Empfänger.
Der Code liegt unter github.com/Jwrede/loki-alert-lines.