Перейти к содержимому

Политика уведомлений

Обещание DumbMonit в том, что уведомление стоит того, чтобы его прочитать. Движок оповещений снижает уровень шума до того, как оповещение может заговорить (подавление по зависимости, группировка, окна обслуживания); политика уведомлений снижает его после — по каждому каналу. Ничего не требует настройки: приведённые ниже значения по умолчанию работают из коробки.

evaluate (hysteresis, per-device overrides)
→ deduplicate → suppress by dependency → silence → group by device
→ flap hold → channel severity filter → channel routing filter
→ resolved on/off → cooldown → quiet hours → batch window
→ hourly cap → send (+ deep link)
↘ nobody acknowledged → escalation channel
ЭтапГде настраиваетсяПо умолчанию
Порог очистки (гистерезис)Редактор правил → Clear below / aboveнет (CPU: 85 %, диск: 88 % во встроенных правилах)
Переопределения для монитораРедактор правил → Per-device overridesнет
Обнаружение дребезгаПараметры → Уведомления и отчёты → Политика уведомлений → Дополнительные параметры4 изменения за 30 мин, удержание 30 мин
Минимальная серьёзностьКанал → Параметры доставки → Sendвсе
Сообщать, когда прошлоКанал → Параметры доставкивключено
Минимальный интервал на оповещениеКанал → Параметры доставкинет
Тихие часыКанал → Параметры доставкивыключено
Фильтр маршрутизации (теги, тип, правило)Канал → Параметры доставки → Only some alertsвыключено (канал слышит всё)
Эскалация, если никто не подтвердилПараметры → Уведомления и отчёты → Политика уведомленийвыключено
Окно группировкиПараметры → Уведомления и отчёты → Политика уведомлений60 с
Сообщений на канал в часПараметры → Уведомления и отчёты → Политика уведомлений20
Публичный URL (глубокие ссылки)Параметры → Уведомления и отчёты → Политика уведомлений, или DUMBMONIT_PUBLIC_URLпусто

Гистерезис и переопределения для монитора

Заголовок раздела «Гистерезис и переопределения для монитора»

Правило, которое срабатывает выше 90 % и сбрасывается в момент, когда значение опускается ниже 90 %, будет срабатывать и сбрасываться весь вечер при CPU, колеблющемся в районе 89–91 %. Задайте ему порог очистки: после срабатывания оповещение сбрасывается только когда значение опускается ниже него (для правил «срабатывает выше») или поднимается выше него (для правил «срабатывает ниже»). Поле API — clear_threshold у правила; сервер отклоняет порог очистки, заданный с неверной стороны от порога срабатывания.

Переопределение для монитора меняет порог или порог очистки для одного монитора, или отключает правило для него, без дублирования самого правила:

PUT /api/alerts/rules/{id}/overrides/{target_id}
{ "threshold": 97, "clear_threshold": 95 } # или { "enabled": false }
GET /api/alerts/rules/{id}/overrides
GET /api/alerts/overrides?target_id=7 # все переопределения одного монитора
DELETE /api/alerts/rules/{id}/overrides/{target_id}

Переопределение со всеми пустыми полями удаляется. Сообщения указывают порог, который был фактически применён.

Независимо от правила, оповещение, чей отпечаток сработал или сбросился 4 раза за 30 минут, считается дребезжащим: отправляется одно уведомление («дребезг: 4 изменения за 30 мин, уведомления удержаны на 30 мин»), и больше ничего об этом оповещении не отправляется до конца удержания. Решение после окончания удержания всё равно объявляется. Настройте или отключите это в Параметры → Уведомления и отчёты → Политика уведомлений → Дополнительные параметры (flap_events, flap_window_secs, flap_hold_secs; flap_events: 0 отключает функцию).

Каждый канал решает, что он слышит (policy в данных канала):

  • min_severity — info, warning (Advisory и выше) или critical (только Warning). Канал на телефон обычно хочет critical, чат — всё.
  • notify_resolved — false, чтобы слышать только о проблемах.
  • min_interval_secs — минимум столько времени между двумя сообщениями об одном и том же оповещении (повторные срабатывания и напоминания). Решения этим параметром никогда не задерживаются, а решение отправляется только тому каналу, который слышал о срабатывании.
  • quiet_hours — еженедельное окно в вашем местном времени, той же формы, что еженедельное окно обслуживания. Во время тихих часов проходят только оповещения critical; остальные ждут и приходят единой сводкой по окончании, с заголовком «Тихие часы закончились — …». Оповещение, которое сработало и сбросилось во время тихих часов, упоминается в этой сводке только как «решено во время тихих часов».
  • matcher — какие оповещения хочет канал, по тегу, типу монитора или правилу. Пусто (по умолчанию, и то, что сохраняют все существующие каналы) означает всё.
PUT /api/notify/channels/{id}
{ "name": "Phone", "kind": "ntfy", "settings": {…},
"policy": { "min_severity": "critical", "min_interval_secs": 900,
"quiet_hours": { "kind": "weekly", "days": [0,1,2,3,4],
"start_minute": 1320, "end_minute": 420,
"timezone": "Europe/Paris" } } }

Если policy не указано при обновлении, сохранённое значение остаётся; "quiet_hours": null очищает их, а "matcher": null очищает фильтр маршрутизации.

Монитора несут теги (site=cellar, role=storage) и тип (snmp, proxmox, synology, agent…). Фильтр маршрутизации канала указывает, какие оповещения он хочет:

{ "policy": { "min_severity": "warning",
"matcher": {
"include": [ { "field": "tag", "key": "site", "value": "cellar" } ],
"exclude": [ { "field": "tag", "key": "role", "value": "lab" } ] } } }

Три поля, и только точное равенство — без регулярных выражений, без скобок:

fieldСопоставляет поПример
tagТегу на странице самого монитора{"field":"tag","key":"site","value":"cellar"}
kindКоллектору, опрашивающему монитор{"field":"kind","value":"proxmox"}
ruleuid правила{"field":"rule","value":"disk_full"}

Как читаются списки:

  • условия на одном и том же поле (тот же ключ тега, или kind, или rule) — это ИЛИ: site=cellar плюс site=attic означает любое из них;
  • условия на разных полях — это И: site=cellar плюс role=storage означает оба;
  • exclude всегда выигрывает, даже против совпавшего include.

Поэтому пример выше читается — и интерфейс произносит его именно этими словами: этот канал получает advisory и выше с мониторов с тегом site=cellar, кроме мониторов с тегом role=lab.

Под редактором предпросмотр показывает, какие из ваших текущих мониторов выбирает фильтр. Это не приближение: сервер запускает тот же самый фильтр, что и движок оповещений, сведённый к той части, которая зависит только от монитора. Условия правила применяются к оповещениям, а не к мониторам, поэтому предпросмотр называет их отдельно, а не делает вид, что считает их.

POST /api/notify/match-preview
{ "matcher": { "include": [ { "field": "tag", "key": "site", "value": "cellar" } ] } }
→ { "devices": [ { "id": 3, "name": "nas", "kind": "synology",
"tags": { "site": "cellar" }, "matched": true } ],
"matched": 1, "total": 12, "rules": [], "excluded_rules": [] }

Фильтр ограничен двенадцатью условиями. После этого второй канал яснее, чем ещё одно условие.

Один дополнительный переход, и только один. Когда в политике заданы escalate_after_secs и escalate_channel, оповещение, которое никто не подтвердил в течение этой задержки, также отправляется — один раз — на этот канал:

PUT /api/notify/policy
{ "escalate_after_secs": 900, "escalate_channel": 4 }
  • Задержка отсчитывается с момента, когда первое сообщение действительно было отправлено, а не с момента срабатывания оповещения. Если тихие часы или неудачная доставка поглотили первое уведомление, эскалировать пока нечего, и переход ждёт.
  • Подтверждение оповещения останавливает его, точно как останавливает напоминания; так же действует и сброс оповещения.
  • Сообщение об эскалации идёт только на канал эскалации — каналы, которые уже слышали о срабатывании, не уведомляются повторно.
  • Оно пересекает собственный порог серьёзности канала, фильтр маршрутизации и тихие часы: вы назвали этот канал, чтобы он разбудил кого-то, поэтому он не сдерживается в тот самый момент, когда это важно.
  • В строке это отражено так: 🔴 Critical · Disk full — /data — 95 % … — still unacknowledged.

"escalate_channel": null отключает эскалацию; задержка 0 делает то же самое. Нужны оба значения, чтобы переход существовал, и сервер отказывает в канале, которого не существует, вместо того чтобы эскалировать в пустоту.

DumbMonit намеренно останавливается здесь: один переход, без ротации, без графика дежурств на неделю. Если вам нужно именно это, направьте канал эскалации на PagerDuty или Opsgenie, которые делают это по-настоящему.

GET/PUT /api/notify/policy хранит глобальные настройки:

{ "batch_window_secs": 60, "max_per_hour": 20,
"flap_events": 4, "flap_window_secs": 1800, "flap_hold_secs": 1800,
"public_url": "https://monit.example.lan",
"escalate_after_secs": 0, "escalate_channel": null }
  • Окно группировки — оповещения, сработавшие в пределах окна, уходят как одно сообщение на канал, сгруппированное по монитору: «3 оповещения на 2 мониторах» с разделом на каждое монитор; решения едут вместе («2 решено»). 0 отправляет на следующем цикле. Сводка показывает не более 12 строк, затем «…и ещё N оповещений».
  • Часовой лимит — как только канал получил max_per_hour сообщений за последний час, дальнейшие оповещения ждут и приходят единой сводкой, когда освобождается слот. 0 убирает лимит.
  • Каналы дежурных служб (PagerDuty, Opsgenie) никогда не группируются между мониторами: они открывают и закрывают по одному инциденту на монитор, поэтому получают по одному сообщению на монитор без ожидания.

Сбои доставки оставляют строки в очереди; следующий цикл повторяет попытку.

Каждое оповещение — одна строка, одинаковая в письме, ntfy, чате и переменной {{message}} произвольного вебхука:

🔴 Critical · Disk full — /data — 95 % (threshold > 90 %), for 12 min
⚠️ Warning · VM or container stopped — win11-desktop (101), for 59 s
✅ Resolved · Service down — https://example.lan/
  • За значком следует слово состояния (Critical / Warning / Info, Resolved, Flapping), поэтому серьёзность читается без цвета или эмодзи.
  • Правило, наблюдающее за несколькими рядами, называет тот, что сработал: ВМ и её id, точку монтирования, интерфейс, группу резервного копирования, URL проверки. Общедевайсные правила (недоступность) не называют ничего кроме монитора.
  • Значение и порог показываются для измеряемых правил; правило «всё или ничего» на метрике 0/1 (на батарее, гостевая ВМ остановлена) не повторяет «1 (threshold > 0)».

Каждое сообщение заканчивается ссылкой на монитор ({public_url}/targets/{id}), если публичный URL известен — public_url политики или DUMBMONIT_PUBLIC_URL. Произвольные вебхуки также получают её как переменную {{link}}, если у них не задан собственный base_url.

В сообщениях намеренно нет ссылки «заглушить на 1 ч»: чат-сервисы запрашивают каждую ссылку, которую показывают, чтобы построить предпросмотр, что запустило бы заглушение в момент публикации сообщения. Заглушайте со страницы монитора или со страницы оповещений.

Дизайн следует за Pulse там, где это подходит монитору для домашней лаборатории: пороги срабатывания/очистки, слой переопределений на ресурс, задержка между уведомлениями, окно группировки, часовой лимит, тихие часы, обнаружение дребезга с задержкой, «уведомлять при решении», и инструменты дежурных служб, не попадающие в сводки. Не позаимствовано: 24-часовой период наблюдения Pulse перед началом уведомлений (базовое правило DumbMonit уже учится молча; правила по порогу должны говорить с первого дня) и отложить/подтвердить из самого сообщения (см. выше). Маршрутизация по тегу на получателя была позаимствована, в виде описанного выше фильтра маршрутизации — выбор каналов в самом правиле оказался неверным местом для «всё из подвала идёт в эту комнату».