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)
| Kind | Ce que ça fait | Champs spec |
|---|---|---|
metric_raw | L'expression PromQL EST le prédicat. Part par entrée de vecteur renvoyée. | expr |
metric_anomaly | Z-score ou MAD sur une fenêtre de baseline glissante. | metric, method, baseline_window, baseline_step, deviation, for_seconds |
metric_forecast | predict_linear(metric[fit_window], predict_seconds) <op> threshold. | metric, fit_window, predict_seconds, operator, threshold |
metric_burn_rate | Multi-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.
// 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)
| Kind | Ce que ça fait | Backend |
|---|---|---|
log_match | count_over_time(<stream> |~ <filter> [window]) <op> threshold contre Loki. Part par label-set. | Loki |
log_volume | Même forme que log_match, count fenêtre courante vs seuil absolu. | Loki |
trace_latency | histogram_quantile(q, sum by(le)(rate(traces_spanmetrics_latency_bucket[w]))) > threshold_ms. | Prom (spanmetrics) |
trace_error_rate | 100 * (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 undevice_id. L'evaluator parse le labeldevice_iddepuis les labels de résultat Prom ;validateFiringrejette 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).
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 :
- Épinglage par règle — si
rule.notify_channel_ids_jsonest non vide, seuls ces ids de canal matchent (et seuls ceux activés). - Sinon, chaque ligne
notification_channelsactivée est filtrée parmatch_severity_minetmatch_scope_types. - Si rien ne matche, le résolveur retombe sur une liste de canaux synthétique seedée depuis
DefaultChannelspour 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_Xinhibe touthost:X:*— quand un edge est injoignable, chaque alarme host-scoped dessus est supprimée.pipeline:prom_ingest_failinhibepipeline: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.