Skip to content

Alarme

Ongrids Alarm-Subsystem ist eine einzelne Tick-Loop, die jede aktivierte Regelzeile durchläuft, das passende Backend fragt (Prom für Metriken + Trace-Spanmetrics, Loki für Logs), ob das Prädikat matcht, und Feuerungen in die incidents-Tabelle schreibt.

Es gibt keinen separaten Alertmanager, keine separate Rules-Datei. Regeln liegen in MySQL, der Evaluator pollt sie bei einem 30s-Cache-Refresh, und Benachrichtigungen fächern durch die Kanal-Registry aus.

Die 14 Regelarten

Regeln werden mit einer kind-Spalte gespeichert. Der Compiler dispatcht darauf.

Der Compiler liegt in rules.go und die Evaluatoren in evaluators_phaseA.go + evaluators_phaseB.go.

Die 8+6-Aufteilung ist HLD-004s Phase-A (Metriken) / Phase-B (Logs + Traces), gelandet 2026-05-08.

Metrik-Arten (Phase A)

ArtWas sie tutSpec-Felder
metric_rawPromQL-Ausdruck IST das Prädikat. Feuert pro zurückgegebenem Vektor-Eintrag.expr
metric_anomalyZ-Score oder MAD über ein rollendes Baseline-Fenster.metric, method, baseline_window, baseline_step, deviation, for_seconds
metric_forecastpredict_linear(metric[fit_window], predict_seconds) <op> threshold.metric, fit_window, predict_seconds, operator, threshold
metric_burn_rateGoogles SRE Multi-Window-Multi-Burn über ein SLO. ALLE Fenster müssen triggern.sli, slo, burns[].window, burns[].multiplier

Die Legacy-prom_query-Art wurde in metric_raw umbenannt. Die Legacy-metric_threshold-Form ist jetzt ein nur-UI-Eintrag, der zur Speicherzeit zu metric_raw kompiliert wird — es gibt keinen separaten Evaluator dafür.

go
// internal/manager/biz/alert/rules.go:36
type MetricRawRule struct {
    ID         uint64
    RuleKey    string
    Name       string
    Severity   string
    ScopeType  string // host / global / monitoring_pipeline
    RunbookURL string
    Labels     map[string]string
    Expr       string // canonical predicate, e.g. `up == 0`
}

Log- + Trace-Arten (Phase B)

ArtWas sie tutBackend
log_matchcount_over_time(<stream> |~ <filter> [window]) <op> threshold gegen Loki. Pro Labelset-Feuern.Loki
log_volumeGleiche Form wie log_match, Current-Window-Count vs. absoluter Schwellwert.Loki
trace_latencyhistogram_quantile(q, sum by(le)(rate(traces_spanmetrics_latency_bucket[w]))) > threshold_ms.Prom (Spanmetrics)
trace_error_rate100 * (sum rate(traces_spanmetrics_calls_total{status_code="STATUS_CODE_ERROR"}) / sum rate(...)) > pct.Prom (Spanmetrics)

Trace-Arten fragen Prometheus ab, nicht Tempo. Der Spanmetrics-Generator scrapt Tempo und schreibt traces_spanmetrics_*-Serien zurück in Prom — Prom abzufragen hält den Alarm-Evaluator auf einer Query-Engine und nutzt die gesamte Operator-Filter-/Schwellwert-Logik wieder.

Scope-Typen

Jede Regel hat scope_type ∈ {host, global, monitoring_pipeline}. Default pro Art definiert in defaultScopeForKind in rules.go.

  • host — Incident muss eine device_id tragen. Der Evaluator parst das device_id-Label aus den Prom-Ergebnislabels; validateFiring lehnt host-scoped Feuerungen ohne eine ab.
  • global — service-level Alarme (trace_, log_), die nicht an einen einzelnen Host pinnen.
  • monitoring_pipeline — Meta-Alarme über Ongrid selbst (scrape_down, prom_ingest_fail, ...).

Der Evaluator-Tick

PipelineEvaluator.evaluate läuft alle Interval (default 5 min, konfigurierbar via PipelineEvaluatorOpts.Interval).

go
func (e *PipelineEvaluator) evaluate(ctx context.Context) {
    now := e.now()
    if e.edges != nil {
        e.refreshDeviceStalenessGauge(ctx, now)
    }
    if e.prom != nil {
        e.evaluatePromQuery(ctx, now)
        e.evaluateMetricAnomaly(ctx, now)
        e.evaluateMetricForecast(ctx, now)
        e.evaluateMetricBurnRate(ctx, now)
        e.evaluateTraceLatency(ctx, now)
        e.evaluateTraceErrorRate(ctx, now)
    }
    if e.logq != nil {
        e.evaluateLogMatch(ctx, now)
        e.evaluateLogVolume(ctx, now)
    }
}

Ein nil-Backend überspringt die entsprechenden Arten stillschweigend — Loki down bricht keine Metrik-Alarme.

Dedup + Recovery

Der Evaluator trackt firingSnapshot[ruleKey] = set<dedupeKey> über Ticks hinweg. Ein im letzten Tick präsenter, in diesem Tick abwesender Schlüssel → PromQLs Vergleichsfilter ließ die Serie fallen → Prädikat geräumt → SystemResolveIncident feuert mit "prom condition cleared". So erholen sich Alarme ohne separaten „Resolve"-Evaluator.

Dedupe-Schlüssel-Form: pipeline:<rule_key>:<sorted-label-set> — Provenienz-Labels (__name__, ongrid_source) werden entfernt, sodass derselbe Alarm, der sowohl vom eingebetteten als auch vom Cloud-Collector gemeldet wird, zu einem Incident dedupliziert, nicht zu zwei (labelSetKey).

Kanal-Fan-out

Wenn ein Incident feuert, konsultiert der Notifier.MaybeNotify-Pfad den ChannelResolver:

  1. Per-Regel-Pinning — wenn rule.notify_channel_ids_json nicht leer ist, matchen nur diese Channel-IDs (und nur die aktivierten).
  2. Andernfalls wird jede aktivierte notification_channels-Zeile nach match_severity_min und match_scope_types gefiltert.
  3. Wenn nichts matcht, fällt der Resolver auf eine synthetische Kanalliste zurück, die von DefaultChannels geseedet wird, sodass Benachrichtigungen nie verschwinden.

Siehe router.go.

Inhibition

Zwei eingebaute Inhibition-Regeln (inhibit.go), die die lauten Default-Fälle abdecken:

  • edge_offline:edge_X inhibitiert jedes host:X:* — wenn eine Edge nicht erreichbar ist, wird jeder host-scoped Alarm auf ihr unterdrückt.
  • pipeline:prom_ingest_fail inhibitiert pipeline:scrape_down:* — wenn Prometheus selbst nicht ingesten kann, ist jeder „Target Down"-Alarm Rauschen.

Eine zukünftige inhibition_rules-Tabelle erweitert dies auf admin-definierte Gruppen.

Cooldown + Dämpfung

NotifyOpts.Cooldown (default 10 Minuten) begrenzt Re-Notifizierung auf demselben dedupe_key. Der Dämpfungsfilter sitzt innerhalb von Usecase.MaybeNotify, sodass der Channel-Resolver und Inhibitor bei jeder Feuerung weiterhin laufen — nur der tatsächliche Notifier.Send wird übersprungen.

Siehe auch

  • RCA — was passiert, wenn ein Incident feuert.
  • Logs — Loki + die log_match / log_volume-Evaluatoren.
  • Traces — Tempo + die trace_latency / trace_error_rate-Evaluatoren.
  • Kanäle-Übersicht — wie Slack- / Telegram- / Lark- / DingTalk- / WeCom- + Webhook-Kanäle konfiguriert werden.
  • Alarmregel-Schema — das Wire-Format der Regel-Zeile.