Skip to content

Схема правила алерта

Правила алертов хранятся в таблице alert_rules и отправляются в POST /v1/alert-rules. Эта страница — wire-формат. Source of truth: internal/manager/model/alert/model.go.

Wire-форма

json
{
  "rule_key": "host_cpu_high",
  "kind": "metric_raw",
  "name": "Host CPU pegged",
  "source_type": "ongrid_builtin",
  "scope_type": "host",
  "join_mode": "all",
  "severity": "warning",
  "enabled": true,
  "conditions": [
    { "expr": "node_cpu_usage_percent > 90" }
  ],
  "labels":      { "team": "sre", "service": "host" },
  "annotations": { "summary": "CPU on {{$labels.device_id}} above 90%" },
  "runbook_url": "https://wiki.example.com/runbooks/host-cpu",
  "notify_channel_ids": [12, 17],
  "notify_window_seconds": 600,
  "notify_min_fires": 3
}

Справочник полей

Идентичность

ПолеТипОбязательноОписание
rule_keystringдаСтабильный lower_snake идентификатор, используемый в dedupe-ключах и incident.rule. Уникальный.
namestringдаDisplay name.
enabledboolнет (по умолчанию true)Отключённые правила пропускаются evaluator'ом и скрыты из «active» фильтров.

Source / scope

ПолеТипОбязательноОписание
source_typeenumдаongrid_builtin, prometheus_external. Встроенные правила несут канонические значения rule_key; внешние правила приходят из импортированного Prometheus alerting rule-файла.
scope_typeenumдаhost, global, monitoring_pipeline. Определяет, по какому измерению evaluator группирует — host производит один incident на device_id; global производит один incident system-wide; monitoring_pipeline для internal pipeline-health правил.
join_modeenumдаall (каждое условие должно совпадать), any (любое условие совпадает). Релевантно только когда conditions имеет больше одного элемента.

Kind

kind дискриминирует, как evaluator интерпретирует conditions. Каждый kind управляет другим sub-evaluator'ом.

KindСтатусЧто он делаетФорма conditions
metric_thresholdUI-only входДружественная форма. Biz-слой переписывает её в metric_raw во время сохранения; вы никогда не увидите этот kind на диске.[{ "metric": "cpu_pct", "operator": ">=", "threshold": 90, "window": "5m", "for": "2m", "aggregator": "avg" }]
metric_rawliveПроизвольный PromQL. Ticker-driven.[{ "expr": "rate(http_500[5m]) > 0.1" }]
metric_anomalyliveОтклонение от rolling baseline (z-score). Ticker-driven через PromQuerier.[{ "metric": "node_cpu_usage_percent", "window": "1h", "z_threshold": 3.0 }]
metric_forecastliveЛинейная экстраполяция (predict_linear), пересекающая статический порог в будущем окне.[{ "metric": "node_filesystem_avail_bytes", "window": "1h", "forecast_for": "24h", "below": 1073741824 }]
metric_burn_rateliveSLO error-budget multi-window multi-burn-rate (Google SRE Workbook).[{ "good": "sum(rate(http_2xx[1h]))", "total": "sum(rate(http_total[1h]))", "slo": 0.999, "long": "1h", "short": "5m" }]
log_matchlive (Phase-B)LogQL-паттерн, который при попадании срабатывает.[{ "expr": "{device_id=\"{{.device_id}}\"} |= \"panic\"" }]
log_volumelive (Phase-B)LogQL stream rate выше порога.[{ "expr": "sum(rate({app=\"foo\"}[5m])) > 100" }]
trace_latencylive (Phase-B)TraceQL p95 / p99 выше порога для сервиса.[{ "service": "payments", "percentile": 95, "above_ms": 800, "window": "5m" }]
trace_error_ratelive (Phase-B)TraceQL error span fraction выше порога.[{ "service": "payments", "above_percent": 1.0, "window": "5m" }]

Полный Go-enum живёт в model/alert/model.go:

go
const (
    RuleKindMetricThreshold = "metric_threshold" // UI-only input
    RuleKindMetricAnomaly   = "metric_anomaly"
    RuleKindMetricForecast  = "metric_forecast"
    RuleKindMetricBurnRate  = "metric_burn_rate"
    RuleKindMetricRaw       = "metric_raw"
    RuleKindLogMatch        = "log_match"
    RuleKindLogVolume       = "log_volume"
    RuleKindTraceLatency    = "trace_latency"
    RuleKindTraceErrorRate  = "trace_error_rate"
)

Legacy kinds (edge_offline, prom_query, ingest_health, edge_absence, health_ingest, event_internal) молча aliased к metric_raw при сохранении.

Conditions

conditions — массив. Форма каждого элемента зависит от kind — см. таблицу выше. join_mode решает, должны ли все элементы совпадать (all) или любой (any).

Для metric_threshold (UI-форма), каждое условие — это RuleCondition:

go
type RuleCondition struct {
    Metric     string  `json:"metric"`            // e.g. "cpu_pct"
    Operator   string  `json:"operator"`          // ">", ">=", "<", "<=", "=="
    Threshold  float64 `json:"threshold"`         // numeric trigger
    Window     string  `json:"window,omitempty"`  // e.g. "5m"
    For        string  `json:"for,omitempty"`     // sustain duration
    Aggregator string  `json:"aggregator,omitempty"` // avg / max / min
}

Biz-слой компилирует их в metric_raw expr во время сохранения, используя канонические closed-set host-метрики (node_cpu_usage_percent, node_memory_used_percent, node_filesystem_used_percent, node_load1, ...).

Severity

ЗначениеОбработка
infoзаписывается; уведомления гейтятся match_severity_min канала (большинство каналов пропускают этот floor)
warningпо умолчанию
criticalвсегда уведомляется, если не silenced

match_severity_min канала, установленный в warning, принимает warning + critical; critical принимает только critical. Пустое совпадает с любым.

Labels & annotations

Свободно-формные key/value мапы, хранящиеся как JSON.

  • labels дописываются к labels инцидента во время срабатывания и используются для группировки / дедупликации. Часто: service, team, env.
  • annotations шаблонизируются во время срабатывания, используя синтаксис Go template с incident-snapshot — например, summary: "CPU on {{$labels.device_id}} above 90%".

Runbook

runbook_url показывается дословно в детали инцидента и на chat-поверхности рядом с AI investigation отчётом. Используйте его, чтобы линковать на ваши внутренние runbook / playbooks.

Дампенинг уведомлений

ПолеТипПо умолчаниюОписание
notify_window_secondsint0Rolling-окно для дампенинга. 0 отключает.
notify_min_firesint0Минимум срабатываний внутри окна до того, как уведомление отправлено. 0 отключает.
notify_channel_idsint[]emptyЗакрепить уведомления на конкретных channel ID (subject to собственным enabled / severity / scope фильтрам каждого канала). Пусто = global router.

Правило, которое срабатывает меньше, чем notify_min_fires раз внутри trailing notify_window_seconds, пишет событие repeat_suppressed в timeline (чтобы вы могли видеть, что дампенинг вступил в силу), но не уведомляет. Оба нуля = дампенинг выкл, каждое срабатывание уведомляет subject to cooldown + silence + inhibition гейтам.

Смешанный (один нуль, один >0) отвергается на biz-слое с invalid_argument: notify_window_seconds and notify_min_fires must both be zero or both > 0.

Lifecycle инцидента

Поля выше описывают правило (определение триггера). Когда правило срабатывает, оно производит инцидент (таблица alert_incidents) с этими статусами:

text
open ─┬─> acknowledged ─> resolved
      ├─> silenced ─> resolved
      └─> resolved

Инциденты auto-resolve, когда underlying-условие очищается на один полный evaluator-цикл. State-машина задокументирована в internal/manager/biz/alert/.

Типы событий

Каждый state-переход записывает строку alert_events. Стабильные event-type строки:

Event typeКогда
firingправило срабатывает впервые (инцидент создан или переоткрыт)
repeat_suppressedсрабатывание внутри cooldown / окна дампенинга
acknowledgedпользователь кликает Ack
silencedпользователь silence'ит на N часов
resolvedусловие очищено или пользователь кликнул Resolve
reopenedresolved инцидент сработал снова до удаления
noteuser-added комментарий
notification_sentодна channel-доставка успешна
notification_failedодна channel-доставка провалилась
inhibitedподавлен инцидентом более высокой severity
ai_initial_diagnosisпервая интерпретация проактивного AI investigator'а, записанная, когда инцидент впервые срабатывает

Типы каналов

POST /v1/notification-channels принимает:

ТипЗначение channel_typeИсточник
Webhookwebhookenv или UI
Slackslackenv или UI
Larksuite / Feishufeishuenv или UI
DingTalkdingtalkenv или UI
WeCom (企业微信)wecomтолько UI
Telegramtelegramтолько UI

Legacy log channel-тип был удалён в 2026-05 — сам alert_events — это delivery-аудит.

См. также