Оповещения

Раздел «Оповещения» связывает правила с каналами доставки, чтобы команда узнавала о новых проблемах, регрессиях и всплесках без постоянного мониторинга дашбордов. Открывается по значку колокольчика в левой рельсе или напрямую по /projects/{id}/alerts.

Это страница про алерты по проблемам (issues). Пороговые алерты по числовым метрикам настраиваются отдельно — см. Оповещения по метрикам; уведомления по ним уходят в те же каналы, что описаны здесь.

Кому что доступно

Три правила по проблемам (новый issue, регрессия, всплеск) — операционная настройка: смотреть и менять их может любой оператор проекта — owner/admin организации либо обычный участник команды, привязанной к проекту, см. Команды и роли.

Каналы доставки — другое дело: их получатель и секрет — это учётные данные и персональные данные, а не операционная настройка, поэтому создавать, изменять, удалять канал и нажимать «Тест» может только owner/admin. Оператор проекта, не являющийся owner/admin, всё равно видит таблицу каналов — чтобы отличить каналы друг от друга при настройке правила, — но получатель у каждого канала замаскирован (например, t***@example.com, https://example.com/… или последние две цифры chat_id в Telegram), а секрет в его браузер вообще не попадает. К каждой маске добавлен короткий суффикс вида ·a1b2 — необратимый отпечаток полного значения: если у проекта два webhook-канала на одном хосте (Slack/Discord — секрет обычно в пути, а не в хосте), маска хоста у них совпадёт, а суффикс — нет, и их можно отличить друг от друга, не видя самого значения. Лог доставок маскирует получателя для той же аудитории тем же способом.

Каналы доставки

Канал — это конкретный адрес/получатель, куда шлётся уведомление. Один канал переиспользуется во всех правилах проекта (и в правилах по метрикам).

ТипПолучатель (поле «Получатель»)Секрет (поле «Секрет»)
EmailEmail-адресНе нужен
WebhookURL (http:// или https://)Необязательный — если задан, тело запроса подписывается HMAC-SHA256 в заголовке X-Gotcha-Signature: sha256=<hex>
Telegramchat_id получателя/группыОбязателен — токен бота (123456789:AA...)

Как добавить канал

  1. На странице «Оповещения» нажмите «+» («Добавить канал») — откроется модальное окно.
  2. Выберите Тип: Email, Webhook или Telegram.
    • Email недоступен для выбора (пункт задизаблен, подпись «Email (SMTP не настроен)»), пока оператор инстанса не настроит SMTP — см. Конфигурацию. Это переключатель уровня всего инстанса (переменная окружения процесса), не проектная и не организационная настройка.
  3. Заполните Получатель:
    • Email — просто адрес, например team@example.com;
    • Webhook — полный URL эндпойнта, который примет POST с JSON-телом, например https://example.com/hooks/gotcha;
    • Telegram — chat_id (число, для групп обычно отрицательное) — узнать его можно, например, у @userinfobot, добавив вашего бота в нужный чат.
  4. Заполните Секрет, если применимо:
    • Webhook — произвольная строка, которой вы будете проверять подпись X-Gotcha-Signature на своей стороне;
    • Telegram — токен бота, выданный @BotFather (123456789:AA...).
  5. Отметьте «Включён» (по умолчанию включено) и нажмите «Добавить канал».

Проверка на стороне сервера: email должен быть синтаксически валидным адресом, webhook — валидным http/https URL с хостом, Telegram — chat_id целым числом (у групп и супергрупп отрицательным) и непустой секрет. Неверные данные — ответ 422 с сообщением об ошибке, канал не создаётся.

Для вебхука на приватный/локальный адрес (например, http://localhost:...) по умолчанию действует SSRF-защита — такие адреса отклоняются на отправке, если оператор явно не разрешил приватные адреса на уровне инстанса (single-tenant-инсталляции).

Изменение канала — кнопка «Изменить» в строке таблицы. Меняются получатель, секрет и включённость: выключенный канал можно включить обратно, а опечатку в адресе — исправить, не теряя историю доставок. Поле секрета оставьте пустым, чтобы сохранить текущий, — оно вводится вслепую и обратно в форму не возвращается. Тип канала не меняется: у email, webhook и Telegram разный смысл адреса и секрета, поэтому «сменить тип» — это отдельный канал.

Удаление канала — кнопка «Удалить» в строке таблицы каналов; спрашивает подтверждение и после него действует сразу.

Правила по проблемам

Три вида правил, они всегда присутствуют на форме (просто «выключены», если не настраивались):

ПравилоКогда срабатываетДоп. поля
Новый issueПоявилась новая проблема (новый фингерпринт)
РегрессияРешённая проблема снова открылась (то же самое событие произошло снова)
Всплеск (spike)Число событий одной проблемы за окно достигло порогаПорог событий, окно (минут)

Для каждого правила: чекбокс «Включено», «Не чаще одного раза в (минут)» — минимальный интервал между повторными уведомлениями по одной и той же проблеме и правилу (защита от заваливания дублями; 0 — без троттлинга). У «Всплеска» дополнительно — «Порог событий» (например, 10) и «Окно (минут)» (например, 5): правило срабатывает, если проблема набрала N событий за последние M минут.

Все три правила сохраняются одной формой — кнопка «Сохранить правила» внизу секции «Правила» отправляет состояние всех трёх карточек разом.

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

Как каналы привязываются к правилам

В Gotcha нет отдельного шага «привязать канал к правилу»: включённое правило автоматически уведомляет все включённые каналы проекта. Если нужно, чтобы разные правила уходили в разные каналы, единственный способ на сегодня — включать/выключать нужные каналы. То же самое верно и для алертов по метрикам (Оповещения по метрикам) — они используют тот же список каналов проекта.

Если включённый канал — email, а SMTP на инстансе не настроен, доставка по этому каналу пропускается (с предупреждением в логе сервера), остальные каналы это не блокирует.

Лог доставок

Отдельная страница /projects/{id}/alerts/deliveries (в группе «Доставка» раздела — «Лог доставок») показывает уведомления, которые не удалось доставить: тип канала, получатель, число попыток и текст последней ошибки (например, SMTP-отказ или неуспешный HTTP-статус от вебхука), время. Полезно, когда webhook отвечает не 2xx, у Telegram-бота истёк токен или у почтового сервера временные проблемы — здесь видно причину без похода в серверные логи.

Пока неудачных доставок нет, страница показывает пустое состояние «Неудачных доставок нет».

Telegram: события не доходят до бота

Симптом: доставка в Telegram стабильно падает — в «Логе доставок» таймаут или сетевая ошибка, — а webhook и почта работают. Причина почти всегда лежит на пути к api.telegram.org, а не в боте и не в токене.

Шаг 1 — как инстанс резолвит имя.

docker compose exec gotcha getent hosts api.telegram.org

Если имя резолвится в IPv6-адрес, а глобального IPv6 у сервера нет (частый случай на VPS), соединение будет таймаутиться при полностью доступном IPv4.

Шаг 2 — доходит ли трафик до самого адреса. С сервера:

curl -sS -m 5 -o /dev/null -w '%{http_code}\n' https://api.telegram.org/

Молчание до истечения таймаута означает, что до Bot API не доходит трафик: фильтрация на стороне оператора связи, закрытый исход из периметра, отсутствующий маршрут. Опознаётся по тому, что одни адреса за этим именем отвечают, а другие нет, причём набор зависит от площадки и направления — то есть Telegram здоров, а недоступен он именно отсюда.

Что с этим делать — три средства, от самого устойчивого к самому временному.

  1. Свой адрес Bot API. GOTCHA_TELEGRAM_API_BASE направляет отправку на собственный telegram-bot-api или на обычный реверс-прокси в сети, откуда Telegram доступен. Единственный вариант, который не зависит от того, какие адреса Telegram проходят сегодня.

  2. Исходящий прокси. Стандартные HTTPS_PROXY/HTTP_PROXY/NO_PROXY в окружении контейнера действуют на доставку в Telegram. На webhook-каналы и проверки аптайма они не распространяются: те намеренно ходят к цели напрямую, иначе SSRF-фильтр перестал бы решать по фактическому адресу соединения.

  3. Пиннинг имени на живой IP — быстрая мера на время расследования. В docker-compose.override.yml:

    services:
      gotcha:
        extra_hosts:
          - "api.telegram.org:149.154.167.220"
    

    После docker compose up -d gotcha доставка восстанавливается. Это именно затычка: адрес держится ровно до следующей смены адресов Telegram или настроек фильтрации, и когда он отвалится, симптом вернётся без единого изменения с вашей стороны. Пин стоит снимать, как только заработал вариант 1 или 2.

Таймаут на TLS при живом соединении

Если соединение явно устанавливается, а падает первый крупный обмен — в ошибке почты видно, что приветствие 220 получено, а споткнулось на STARTTLS, — значит мелкие пакеты ходят, а крупные пропадают. Такое даёт фильтрация трафика и MTU сети контейнеров. Отличить просто: проверьте изнутри контейнера, а не с хоста.

docker compose exec gotcha wget -q -O /dev/null https://api.github.com/ && echo ok || echo fail

Если и здесь виснет — берите GOTCHA_COMPOSE_NET_MTU в разделе Конфигурация: там разобрано, почему с хоста при этом всё работает, почему до одних адресатов доходит, а до других нет, и почему поломка может исчезать на десять минут и возвращаться.

Приватность: что видят внешние каналы

Детали события — заголовок проблемы, culprit, уровень, тело уведомления, значения метрики — уходят только доверенным получателям. Остальным отправляется обезличенное уведомление: маршрутные поля (id проекта, проблемы, правила, счётчики, вид алерта) и ссылка на карточку в Gotcha.

Решение принимается по получателю, а не по типу канала:

ПолучательКогда считается доверенным
Emailдомен адреса — хост вашего инстанса (или его поддомен), либо перечислен в GOTCHA_TRUSTED_RECIPIENTS
Webhookхост URL — то же самое, а также любой адрес во внутренней сети (localhost, приватные диапазоны, зоны .local, .internal, .lan, .home.arpa)
Telegramпо адресу — никогда: получатель задан chat_id, домена у него нет
Любой каналотметка «Получатель внутри моего контура» в форме канала

Отметка на канале — для случаев, когда адрес ничего не доказывает: чат в Telegram, принадлежащий вам самим, опознать по chat_id невозможно. Она ставится вручную, по одному каналу, снята по умолчанию, и такие каналы помечены в таблице бейджем «С деталями». Подробнее — в разделе Приватность.

Ящик на публичном почтовом сервисе (@gmail.com, @yandex.ru) — такая же чужая инфраструктура, как Telegram: детали туда не уходят. Вебхук на ваш собственный сервер во внутренней сети, наоборот, получает их всегда.

Если почта вашей организации живёт на домене, отличном от хоста инстанса, укажите его:

GOTCHA_TRUSTED_RECIPIENTS=corp.example
GOTCHA_EXTERNAL_CHANNEL_DETAILS_ENABLED=true

снимает ограничение целиком — детали пойдут любому получателю, включая Telegram. Включайте, только если у вас есть законные основания для трансграничной передачи. Обе настройки — на уровне инстанса; подробности и обоснование см. в разделе Приватность и 152-ФЗ.

Формат тела вебхука

Каждое уведомление — POST-запрос с телом JSON (Content-Type: application/json). Если у канала задан секрет, тело подписывается: заголовок X-Gotcha-Signature: sha256=<hex> несёт HMAC-SHA256 тела запроса по секрету канала, в шестнадцатеричной кодировке, с префиксом sha256=. Чтобы проверить подпись на своей стороне: посчитать HMAC-SHA256(секрет, сырое тело запроса) и сравнить с частью после sha256= — побайтовым сравнением с постоянным временем, а не строковым ==. Если секрет не задан, заголовка нет вовсе.

Состав полей зависит от того, доверенный ли получатель канала — см. «Приватность: что видят внешние каналы» выше: при GOTCHA_EXTERNAL_CHANNEL_DETAILS_ENABLED=false и недоверенном получателе часть полей вырезается, а subject/body заменяются обезличенным текстом. Порядок ключей JSON-объекта частью контракта не является.

Ниже — тело алерта по проблеме (issue): kind равен new_issue, regression или spike.

ПолеТипС деталямиБез деталей
kindстрока
project_idчисло
project_nameстрока
urlстрока
subjectстрока✓ (тема с деталями)✓ (обезличенная тема)
bodyстрока✓ (тело с деталями)✓ (обезличенное тело)
issue_idчисло
times_seenчисло
titleстрока
culpritстрока
levelстрока

Пример тела с деталями:

{
  "body": "Project: Storefront\n\nTypeError: cannot read properties of undefined\n\nCulprit: checkoutHandler\nLevel: error\nSeen: 3 times\n\nhttps://gotcha.example/issues/42",
  "culprit": "checkoutHandler",
  "issue_id": 42,
  "kind": "new_issue",
  "level": "error",
  "project_id": 7,
  "project_name": "Storefront",
  "subject": "[Gotcha] New issue: TypeError: cannot read properties of undefined · Storefront",
  "times_seen": 3,
  "title": "TypeError: cannot read properties of undefined",
  "url": "https://gotcha.example/issues/42"
}

Пример тела без деталей (GOTCHA_EXTERNAL_CHANNEL_DETAILS_ENABLED=false, недоверенный получатель):

{
  "body": "Project: Storefront\n\nNew issue\n\nhttps://gotcha.example/issues/42",
  "issue_id": 42,
  "kind": "new_issue",
  "project_id": 7,
  "project_name": "Storefront",
  "subject": "[Gotcha] New issue · Storefront",
  "times_seen": 3,
  "url": "https://gotcha.example/issues/42"
}

Правило совместимости: добавление нового поля в тело — не ломающее изменение (интеграция обязана игнорировать незнакомые поля); удаление поля, переименование или смена типа — ломающее изменение.

Остальные виды событий шлют тот же маршрутный минимум (kind, project_id, url, subject, body, а при недоверенном получателе — тот же обезличенный путь) со своим kind и собственными дополнительными полями:

  • Сводка подавленных уведомлений (kind = suppressed_digest) — count: сколько уведомлений подавлено с прошлой сводки.
  • Регрессия производительности (kind = n_plus_one / slow_db_query / http_flood) — perf_issue_id, title, culprit, count, regression (булево: true — регрессия закрылась, false — новая находка).
  • Тестовое уведомление (кнопка «Тест» у канала) — kind = channel_test, дополнительных полей нет.

Смотрите также

  • Оповещения по метрикам — пороговые правила по числовым метрикам, тот же набор каналов.
  • Проблемы — что такое issue, регрессия, статусы.
  • Конфигурация — переменные SMTP и GOTCHA_EXTERNAL_CHANNEL_DETAILS_ENABLED.
  • Команды и роли — кто такой оператор проекта и полная таблица того, что доступно оператору, а что — только owner/admin.