Skip to content

Alertes

Le sous-système d'alerte d'Ongrid est une boucle à tick unique qui parcourt chaque ligne de règle activée, demande au backend approprié (Prom pour métriques + trace spanmetrics, Loki pour les logs) si le prédicat matche, et enregistre les firings dans la table incidents.

Il n'y a pas d'Alertmanager séparé, pas de fichier de règles séparé. Les règles vivent dans MySQL, l'evaluator les sonde sur un rafraîchissement de cache de 30s, et les notifications partent en fan-out via le registre de canaux.

Les 14 types de règles

Les règles sont stockées avec une colonne kind. Le compilateur dispatche en fonction.

Le compilateur est dans rules.go et les evaluators dans evaluators_phaseA.go + evaluators_phaseB.go.

Le découpage 8+6 est Phase-A (métriques) / Phase-B (logs + traces) de HLD-004, atterri le 2026-05-08.

Types métriques (Phase A)

KindCe que ça faitChamps spec
metric_rawL'expression PromQL EST le prédicat. Part par entrée de vecteur renvoyée.expr
metric_anomalyZ-score ou MAD sur une fenêtre de baseline glissante.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_rateMulti-window multi-burn Google SRE sur un SLO. TOUTES les fenêtres doivent déclencher.sli, slo, burns[].window, burns[].multiplier

L'ancien type prom_query a été renommé metric_raw. L'ancienne forme metric_threshold est maintenant une entrée UI uniquement qui compile en metric_raw au moment de la sauvegarde — il n'y a pas d'evaluator séparé pour elle.

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`
}

Types log + trace (Phase B)

KindCe que ça faitBackend
log_matchcount_over_time(<stream> |~ <filter> [window]) <op> threshold contre Loki. Part par label-set.Loki
log_volumeMême forme que log_match, count fenêtre courante vs seuil absolu.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)

Les types trace interrogent Prometheus, pas Tempo. Le générateur spanmetrics scrape Tempo et réécrit les séries traces_spanmetrics_* dans Prom — interroger Prom garde l'evaluator d'alertes sur un seul moteur de requête et réutilise toute la logique de filtrage d'opérateur / seuil.

Types de scope

Chaque règle a scope_type ∈ {host, global, monitoring_pipeline}. Défaut par type défini dans defaultScopeForKind dans rules.go.

  • host — l'incident doit porter un device_id. L'evaluator parse le label device_id depuis les labels de résultat Prom ; validateFiring rejette les firings host-scoped sans.
  • global — alertes au niveau service (trace_, log_) qui ne s'épinglent pas à un seul host.
  • monitoring_pipeline — méta-alertes sur Ongrid lui-même (scrape_down, prom_ingest_fail, ...).

Le tick de l'evaluator

PipelineEvaluator.evaluate tourne tous les Interval (défaut 5 min, configurable 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)
    }
}

Un backend nil saute silencieusement les types correspondants — Loki down ne casse pas les alertes métriques.

Dédoublonnage + récupération

L'evaluator garde firingSnapshot[ruleKey] = set<dedupeKey> entre les ticks. Une clé présente au tick précédent mais absente à ce tick → le filtre de comparaison PromQL a fait tomber la série → prédicat dégagé → SystemResolveIncident part avec "prom condition cleared". C'est ainsi que les alarmes récupèrent sans un evaluator « resolve » séparé.

Forme de la clé de dédoublonnage : pipeline:<rule_key>:<sorted-label-set> — les labels de provenance (__name__, ongrid_source) sont retirés pour que la même alarme rapportée à la fois par le collecteur embarqué et par le collecteur cloud se déduplique vers un seul incident, pas deux (labelSetKey).

Fan-out vers les canaux

Quand un incident part, le chemin Notifier.MaybeNotify consulte le ChannelResolver :

  1. Épinglage par règle — si rule.notify_channel_ids_json est non vide, seuls ces ids de canal matchent (et seuls ceux activés).
  2. Sinon, chaque ligne notification_channels activée est filtrée par match_severity_min et match_scope_types.
  3. Si rien ne matche, le résolveur retombe sur une liste de canaux synthétique seedée depuis DefaultChannels pour que les notifications ne disparaissent jamais.

Voir router.go.

Inhibition

Deux règles d'inhibition intégrées (inhibit.go), couvrant les cas par défaut bruyants :

  • edge_offline:edge_X inhibe tout host:X:* — quand un edge est injoignable, chaque alarme host-scoped dessus est supprimée.
  • pipeline:prom_ingest_fail inhibe pipeline:scrape_down:* — quand Prometheus lui-même ne peut pas ingérer, chaque alarme « cible down » est du bruit.

Une future table inhibition_rules étend ceci à des groupes définis par admin.

Cooldown + dampening

NotifyOpts.Cooldown (défaut 10 minutes) borne la re-notification sur la même dedupe_key. Le filtre de dampening est dans Usecase.MaybeNotify pour que le résolveur de canal et l'inhibitor tournent toujours sur chaque firing — seul le Notifier.Send réel est sauté.

Voir aussi

  • RCA — ce qui se passe quand un incident part.
  • Logs — Loki + les evaluators log_match / log_volume.
  • Traces — Tempo + les evaluators trace_latency / trace_error_rate.
  • Aperçu des canaux — comment les canaux Slack / Telegram / Lark / DingTalk / WeCom + webhook sont configurés.
  • Schéma de règle d'alerte — le format wire de la ligne de règle.