Звонки по алертам Sentry: только критичные ошибки

У Sentry нет действия «позвонить». Направьте алерты через вебхук, чтобы телефон звонил только на ошибки, которые ломают продакшен: настройка, payload, фильтры.

Обновлено

Содержание

Sentry не может вам позвонить. Он умеет отправить письмо, написать в Slack или передать алерт в PagerDuty — но встроенного голосового действия у него нет. Способ получить его, не покупая платформу управления инцидентами: отправить алерт Sentry в вебхук, который звонит. Создайте внутреннюю интеграцию, направьте её на звонковый канал Echobell и поставьте перед ним фильтр, чтобы проходили только ошибки, которые действительно ломают продакшен.

В этом руководстве — весь путь: интеграция, правило алерта, реальный payload от Sentry, шаблоны, которые его читают, и две ловушки, из-за которых люди бросают эту затею.

Почему Sentry сам не может позвонить

Действия issue-алертов Sentry — это уведомления (почта, Slack, Discord, Microsoft Teams), создание задач (Jira, GitHub, Azure DevOps) и передача в пейджинговый продукт (PagerDuty, Opsgenie). Каждое заканчивается либо экраном, на который надо смотреть, либо платным местом на чужой платформе.

В 14:00 это нормально. В 03:00 сообщение в Slack неотличимо от тишины, а пуш проигрывает режиму «Не беспокоить». Для того небольшого набора ошибок, где два часа задержки стоят реальных денег — чекаут отдаёт 500, авторизация отклоняет всех, воркер молча теряет задачи — нужно устройство, которое звонит.

Вебхук — это стык. Sentry умеет вызывать произвольный HTTPS-эндпоинт как действие правила алерта; Echobell превращает этот HTTP-запрос в оповещение-звонок, которое пробивает режим фокусирования iOS.

Что понадобится

  • Организация в Sentry, где вы можете открыть Settings → Developer Settings (owner или manager)
  • Установленный Echobell (App Store / Google Play)
  • Пять минут

Ничего с вашей стороны не должно быть доступно из интернета. Исходящий запрос делает Sentry; вы только принимаете.

Шаг 1 — Создайте канал, который звонит

В Echobell создайте канал и выберите тип уведомления Звонок. В этом весь смысл: звонковый канал ведёт себя как входящий вызов, а не как пуш, поэтому проходит через режим фокусирования и «Не беспокоить».

Задайте шаблоны, понятные в три часа ночи. Payload Sentry глубоко вложен, поэтому пути к переменным длиннее обычного:

Заголовок: {{data.event.level}}: {{data.event.metadata.type}}
Текст: {{data.event.title}} — {{data.event.culprit}}

В расширенных настройках задайте шаблон ссылки, чтобы нажатие на уведомление открывало issue:

{{data.event.web_url}}

Скопируйте URL вебхука из карточки канала. Он выглядит так:

https://hook.echobell.one/t/<channel-token>

Шаг 2 — Создайте внутреннюю интеграцию Sentry

Sentry отдаёт вебхук как действие правила алерта только через интеграцию, поэтому её придётся создать. Это форма, а не сервис — писать код не нужно.

  1. Откройте Settings → Developer Settings → Custom Integrations
  2. Create New Integration → Internal Integration
  3. Name: Echobell (это имя вы потом выберете в правиле)
  4. Webhook URL: URL канала из шага 1
  5. Включите переключатель Alert Rule Action
  6. Permissions: достаточно Issue & Event → Read
  7. В разделе Webhooks оставьте все галочки снятыми — см. ловушку ниже
  8. Сохраните

Внутренняя интеграция доступна только вашей организации и устанавливается сама. Токен, который она создаёт, для этой схемы не нужен.

Шаг 3 — Добавьте интеграцию как действие правила

Откройте Alerts → Create Alert → Issue Alert или отредактируйте существующее правило.

В блоке Then perform these actions добавьте Send a notification via an integration и выберите Echobell.

Поставьте Action interval — ограничитель «если этот алерт сработал больше одного раза» — минимум на 30 minutes. По умолчанию отправка идёт при каждом срабатывании, и ошибка, летящая 400 раз в минуту, будет набирать ваш номер, пока вы всё не выключите.

Сохраните и запустите тест правила, чтобы увидеть реальный payload до того, как начнёте на это полагаться.

Шаг 4 — Разберитесь, что приходит на самом деле

Здесь ломается большинство схем, потому что форма payload не такая, как кажется. Sentry всё оборачивает:

{
  "action": "triggered",
  "actor": { "id": "sentry", "name": "Sentry", "type": "application" },
  "data": {
    "event": {
      "event_id": "e4874d664c3540c1a32eab185f12c5ab",
      "level": "error",
      "title": "ReferenceError: heck is not defined",
      "culprit": "?(<anonymous>)",
      "platform": "javascript",
      "project": 1,
      "release": null,
      "metadata": { "type": "ReferenceError", "value": "heck is not defined" },
      "tags": [["level", "error"], ["browser", "Chrome 75.0.3770"]],
      "issue_id": "1117540176",
      "issue_url": "https://sentry.io/api/0/issues/1117540176/",
      "web_url": "https://sentry.io/organizations/test-org/issues/1117540176/events/e4874.../"
    },
    "triggered_rule": "Very Important Alert!"
  },
  "installation": { "uuid": "a8e5d2..." }
}

Четыре вещи, которые стоит знать до написания первого шаблона:

  • Всё полезное лежит под data.event. {{title}} не выведет ничего; {{data.event.title}} выведет ошибку.
  • data.event.project — это числовой ID, а не slug. Если нужно читаемое имя проекта в уведомлении, впишите его текстом в шаблон заголовка и заведите по каналу на проект.
  • Поля environment нет. Окружение приходит внутри data.event.tags парой ["environment", "production"], и её позиция в массиве не фиксирована — не обращайтесь по индексу. Фильтруйте окружение в правиле Sentry (шаг 5).
  • data.triggered_rule — имя правила. Полезно в теле, когда один канал обслуживает несколько правил.

Заголовок Sentry-Hook-Resource для issue-алертов равен event_alert. Его можно потребовать в условии канала, чтобы канал не мог зазвонить ни от чего другого:

header["sentry-hook-resource"] == "event_alert"

Шаг 5 — Сузьте до того, что заслуживает звонка

Звонковый канал, который звонит на каждый новый issue, хуже, чем его отсутствие: за неделю вы его заглушите, и тогда он не позвонит на тот единственный, который был важен. Фильтруйте в двух местах.

В Sentry — через conditions и filters правила:

ЦельНастройка правила
Только продакшенЗадайте правилу Environment = production
Только настоящие поломкиФильтр: The event's level equals fatal (или error)
Не на разовые всплескиУсловие: The issue is seen more than 25 times in 1 hour
Только критичный путьФильтр: The event's tags match transaction contains /checkout
Только регрессииУсловие: A resolved issue changes state from resolved to unresolved

В Echobell условие канала работает как подстраховка для того, что Sentry выразить не умеет, или для правок, которые сегодня не выкатить:

data.event.level == "fatal" || data.event.level == "error"

Фильтровать по уровню обычно лучше в правиле Sentry — там же живёт троттлинг. В Echobell лучше, когда из одного правила Sentry нужно получить две степени срочности.

Шаг 6 — Дайте предупреждениям тихую дверь

Смысл уровней в том, чтобы звонок сохранял значение. Создайте второй канал Echobell с типом Срочное (Time Sensitive), добавьте второе правило Sentry с более низким порогом и направьте его на вторую внутреннюю интеграцию (в одной интеграции один URL вебхука, поэтому второму каналу нужна вторая интеграция).

Схема, которая переживает настоящую рабочую неделю, выглядит примерно так:

Правило SentryУровень / порогКанал EchobellПоведение
prod-fatalfatal, продакшенЗвонокЗвонит сквозь режим фокусирования
prod-error-spikeerror, более 100 за часСрочноеПопадает на экран блокировки, без звонка
new-issue-digestлюбой новый issueОбычноеОбычный пуш, прочитать когда удобно

Звонить только вне рабочего времени

Днём вы и так смотрите в Sentry. Echobell даёт условиям системные переменные времени в UTC, поэтому один канал может вести себя по-разному в зависимости от часа — без второго правила Sentry:

data.event.level == "fatal" && (hour >= 17 || hour < 9)

Добавьте проверку дня недели, если выходные у вас действительно выходные:

data.event.level == "fatal" && (hour >= 17 || hour < 9 || dayOfWeek == 0 || dayOfWeek == 6)

Всё это UTC, так что пересчитайте из своего часового пояса, прежде чем фиксировать цифры. Полный список переменных — в справочнике по условиям.

Две ловушки

Ловушка 1: отметить галочки Webhooks. У внутренней интеграции два независимых пути вебхука. Переключатель Alert Rule Action делает интеграцию доступной для выбора в правилах алертов — он вам и нужен. А галочки Webhooks (issue, error, comment) подписывают вас на каждое событие этого ресурса: любое создание, решение, назначение, архивирование или игнорирование issue по всей организации. Отметьте issue — и телефон будет звонить, когда коллега что-то закроет. Оставьте все снятыми — и вебхук будут запускать только ваши правила.

Ловушка 2: использовать старый плагин Webhooks. Старый плагин уровня проекта Legacy Integrations → WebHooks всё ещё есть и работает, и выглядит короткой дорогой: URL можно вставить без создания интеграции. Но его payload другой формы, более плоский, запросы не подписываются, и сам Sentry уводит новые настройки в сторону. Если вы его возьмёте, вашим шаблонам понадобятся другие пути к переменным. Используйте внутреннюю интеграцию.

Размер payload, шторма и обрезка

Три ограничения, о которых стоит знать заранее:

  • Тело 1 MiB. Echobell отклоняет тела триггера больше 1 MiB с HTTP 413. Payload Sentry несёт полный стектрейс и контекст запроса, обычно это десятки килобайт — но событие с большим телом запроса может подойти к границе. На стороне Sentry нет ручки вроде max_alerts, поэтому средство защиты — вычищать большие тела запросов в beforeSend вашего SDK, что и так стоит делать из соображений приватности.
  • 120 запросов в минуту на токен. Дальше триггер отвечает 429 с RATE_LIMIT_EXCEEDED и Retry-After. Под лимитом вас держит именно action interval в Sentry; 30 minutes более чем достаточно.
  • 1500 байт тела уведомления. Более длинный результат обрезается до попадания на устройство. data.event.title плюс culprit умещаются свободно; вываливать data.event.exception — нет, да и на экране блокировки это нечитаемо. Детали оставьте за шаблоном ссылки.

Совместная работа

На канал Echobell могут подписаться несколько человек, и каждый выбирает свой тип уведомления. Одно и то же правило Sentry может звонить дежурному инженеру и приходить обычным пушем всем остальным — без оплаты за место и без настройки ротации.

Но это не политика эскалации. Здесь нет «если за пять минут никто не подтвердил, звони следующему». Если нужно это, нужна настоящая платформа дежурств; Echobell закрывает слой доставки под ней.

Чего эта схема не даёт

Скажем прямо:

  • Нет подтверждения. Ответ на звонок ничего не сообщает Sentry и не останавливает телефоны других подписчиков.
  • Нет ротации и эскалации. Получают все подписчики либо никто.
  • Нет дедупликации сверх той, что делает Sentry. Группировка и троттлинг живут в правиле; Echobell доставляет то, что пришло.
  • Нет двусторонней синхронизации. Закрытие issue в Sentry ничего не убирает с вашего телефона.

Если это неприемлемо — инструмент не тот. Если же вам нужно «разбуди меня, когда сломается чекаут», это, пожалуй, самый дешёвый надёжный способ.

Разбор проблем

Правило срабатывает, но ничего не приходит. Проверьте, что в интеграции включён Alert Rule Action. Если он выключен, интеграции вообще не будет в списке действий — а правило, сохранённое до включения, сохранит устаревшее действие.

Уведомление приходит пустым. Ваш шаблон читает ключи верхнего уровня. Sentry вкладывает всё под data.event.

Звонит на то, чего вы не ждали. Проверьте галочки Webhooks в интеграции (ловушка 1), а затем — не осталось ли у правила окружение «All Environments».

Звонит снова и снова по одной ошибке. Поднимите action interval в правиле Sentry. Повтор в Echobell — это другое: Повторять неудавшийся звонок в настройках приложения перезванивает, если вы пропустили вызов.

Не приходит вообще ничего, даже тест. Сначала дёрните канал через curl, чтобы исключить сторону Echobell:

curl -X POST https://hook.echobell.one/t/<channel-token> \
  -H 'Content-Type: application/json' \
  -d '{"data":{"event":{"level":"fatal","title":"Test error","culprit":"manual test","metadata":{"type":"TestError"}}}}'

Если это звонит, а Sentry нет — проблема в интеграции, а не в канале.

Частые вопросы

Умеет ли Sentry звонить сам?

Нет. Действия issue-алертов Sentry — это уведомления, создание задач и интеграции с пейджинговыми продуктами. Голосовой звонок требует внешнего сервиса: либо платформы вроде PagerDuty, либо приёмника вебхуков, который звонит, — например Echobell.

Пробьёт ли алерт Sentry режим «Не беспокоить»?

Только если придёт как оповещение-звонок. Канал Echobell типа «звонок» ведёт себя как входящий вызов, а такие iOS пропускает через режим фокусирования и «Не беспокоить». Обычный пуш любого приложения — нет.

Нужен ли платный тариф Sentry для вебхуков?

Внутренние интеграции и действия правил доступны начиная с тарифа Developer. Сам вебхук ничего дополнительно не стоит.

Почему переменная шаблона пустая?

Почти всегда потому, что путь слишком короткий. Payload алерта вкладывает событие под data.event, поэтому нужно {{data.event.title}}, а не {{title}}. Дёрните канал один раз и посмотрите записанное тело запроса в приложении — увидите точную форму.

Как алертить только по одному окружению?

Заполните поле Environment в правиле алерта Sentry. Не пытайтесь прочитать его из data.event.tags — это массив пар [ключ, значение] без гарантированного порядка.

Могут ли позвонить двум людям по одной ошибке?

Да. Поделитесь каналом, и пусть каждый подпишется с нужным ему типом уведомления. Подтверждения нет, поэтому звонок получат все, кто выбрал «звонок».

Где фильтровать — в Sentry или в Echobell?

В Sentry, когда можно: там же живут троттлинг и привязка к окружению. В Echobell — когда из одного правила нужны две степени срочности, когда нужно окно по времени или когда правило сегодня не изменить.

Итог

Схема состоит из четырёх вещей: звонковый канал, внутренняя интеграция с включённым Alert Rule Action и снятыми галочками Webhooks, правило алерта, достаточно узкое, чтобы заслужить звонок, и шаблоны, читающие data.event. Всё остальное на этой странице — о том, как удержать её достаточно узкой, чтобы через месяц звонок всё ещё что-то значил.

Скачайте Echobell для iPhone или возьмите в Google Play, а затем отправьте curl выше — до того, как доверите этому пути что-то по-настоящему важное.

Похожие статьи