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.