Skip to content

Alertas

O subsistema de alerta do Ongrid é um loop de tick único que percorre cada linha de rule habilitada, pergunta ao backend apropriado (Prom para métricas + trace spanmetrics, Loki para logs) se o predicado bate, e registra disparos na tabela incidents.

Não há Alertmanager separado, nem arquivo de rules separado. As rules vivem no MySQL, o evaluator faz polling com refresh de cache de 30s, e as notificações fazem fan-out via o registry de canais.

As 14 categorias de rule

Rules são armazenadas com uma coluna kind. O compiler despacha em cima dela.

O compiler está em rules.go e os evaluators em evaluators_phaseA.go + evaluators_phaseB.go.

O split 8+6 é Phase-A (métricas) / Phase-B (logs + traces) do HLD-004, entregue em 2026-05-08.

Categorias de métrica (Phase A)

KindO que fazCampos do spec
metric_rawA expressão PromQL É o predicado. Dispara por entrada de vetor retornada.expr
metric_anomalyZ-score ou MAD sobre uma janela rolling de baseline.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 do SRE do Google sobre um SLO. TODAS as janelas precisam disparar.sli, slo, burns[].window, burns[].multiplier

O kind legado prom_query foi renomeado para metric_raw. A forma legada metric_threshold agora é uma entrada exclusiva da UI que compila para metric_raw na hora do save — não existe evaluator separado.

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

Categorias de log + trace (Phase B)

KindO que fazBackend
log_matchcount_over_time(<stream> |~ <filter> [window]) <op> threshold contra Loki. Dispara por label-set.Loki
log_volumeMesmo formato de log_match, contagem da janela atual vs threshold absoluto.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)

Categorias de trace consultam o Prometheus, não o Tempo. O gerador de spanmetrics raspa o Tempo e grava séries traces_spanmetrics_* de volta no Prom — consultar o Prom mantém o evaluator de alerta em uma única query engine e reaproveita toda a lógica de filtragem por operador / threshold.

Tipos de scope

Toda rule tem scope_type ∈ {host, global, monitoring_pipeline}. Padrão por kind definido em defaultScopeForKind em rules.go.

  • host — incidente deve carregar um device_id. O evaluator parseia a label device_id dos resultados do Prom; validateFiring rejeita disparos host-scoped sem ela.
  • global — alertas a nível de serviço (trace_, log_) que não fixam em um único host.
  • monitoring_pipeline — meta-alertas sobre o próprio Ongrid (scrape_down, prom_ingest_fail, ...).

O tick do evaluator

PipelineEvaluator.evaluate roda a cada Interval (padrão 5 min, configurável 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)
    }
}

Um backend nil pula silenciosamente as categorias correspondentes — Loki fora não quebra alertas de métrica.

Dedup + recovery

O evaluator rastreia firingSnapshot[ruleKey] = set<dedupeKey> através de ticks. Uma chave presente no tick anterior mas ausente neste tick → o filtro de comparação do PromQL descartou a série → predicado limpou → SystemResolveIncident dispara com "prom condition cleared". É assim que os alarmes se recuperam sem um evaluator separado de "resolve".

Formato da dedupe key: pipeline:<rule_key>:<sorted-label-set> — labels de proveniência (__name__, ongrid_source) são removidas de modo que o mesmo alarme reportado pelo collector embarcado e pelo cloud seja deduplicado em um único incidente, não dois (labelSetKey).

Fan-out por canal

Quando um incidente dispara, o caminho Notifier.MaybeNotify consulta o ChannelResolver:

  1. Pinning por rule — se rule.notify_channel_ids_json não estiver vazio, só esses channel ids casam (e somente os habilitados).
  2. Caso contrário, toda linha habilitada em notification_channels é filtrada por match_severity_min e match_scope_types.
  3. Se nada bate, o resolver cai para uma lista de canal sintética semeada de DefaultChannels para que notificações nunca desapareçam.

Veja router.go.

Inibição

Duas regras de inibição embutidas (inhibit.go), cobrindo os casos padrão barulhentos:

  • edge_offline:edge_X inibe qualquer host:X:* — quando um edge está inalcançável, todo alarme host-scoped nele é suprimido.
  • pipeline:prom_ingest_fail inibe pipeline:scrape_down:* — quando o próprio Prometheus não consegue ingerir, todo alarme "target down" é ruído.

Uma futura tabela inhibition_rules estende isso a grupos definidos pelo admin.

Cooldown + dampening

NotifyOpts.Cooldown (padrão 10 minutos) limita re-notificação na mesma dedupe_key. O filtro de dampening fica dentro de Usecase.MaybeNotify para que o resolver de canal e o inibidor ainda rodem em cada disparo — só o Notifier.Send em si é pulado.

Veja também

  • RCA — o que acontece quando um incidente dispara.
  • Logs — Loki + os evaluators log_match / log_volume.
  • Traces — Tempo + os evaluators trace_latency / trace_error_rate.
  • Visão geral dos canais — como canais Slack / Telegram / Lark / DingTalk / WeCom + webhook são configurados.
  • Schema da rule de alerta — o formato wire da linha de rule.