Развернули мы Sentry self-hosted на своем сервере и кажется что все работает хорошо, но как мониторить что Sentry действительно доступен?
Сходу на ум приходит идея использовать какую-то пинговалку вида:
curl -v http://sentry.local:9000/
Однако, на практике мы столкнулись с ситуациями когда веб-интерфейс Sentry работает, но события об ошибках не принимаются.
Причины могут быть разные и все они связаны с работой внутренних сервисов Sentry. Мониторить каждую из причин недоставки мы не собираемся, потому что причин может быть множество, нам нужен общий мониторинг для понимания ситуации: принимает Sentry события или нет.
На Хабре я кратко затрагивал этот момент, и даже был сделан репозиторий с примерами, а сейчас рассмотрим подробнее. Мы не будем делать реализацию в конкретной системе мониторинга, но попробуем руками выполнить действия выдающие результат, а дальше эти действия каждый сам адаптирует под свою систему мониторинга.
Доступность веб-интерфейса или API не может быть гарантом способности сервера Sentry принимать события.
Есть 2 вопроса:
web-api
Sentry отвечает 200
, а на самом деле событие внутри не записывается?Оба вопроса звучат сомнительно, особенно последний, однако это возможные ситуации на практике, с которыми мы столкнулись в ходе эксплуатации Sentry self-hosted
.
Потеря в сети маршрутизаторов возможна, например если событие доставляется в Sentry не напрямую через HTTP, а через сеть доставки, например как это сделали мы через td-agent:
В итоге делаем вывод:
Сервер Sentry или маршрутизатор может принять событие, но необязательно запишет его в свое хранилище. Это редкий сценарий, но он возможен.
Однако, доступность API
может быть первым этапом в многоуровневом мониторинге доступности, а на следующем шаге нам нужно убедиться что событие действительно появилось в Sentry.
В итоге, доступность Sentry можно описать так:
2XX
код ответа сервераТеперь когда мы определились с необходимыми шагами определения доступности Sentry примемся за их реализацию!
Для начала разберемся какими способами события поступают в Sentry. Для чего нам это нужно? Для того чтобы понимать механику работы.
Примеры напишем на bash
и заранее договоримся что во всех скриптах у нас есть такие переменные, которые нужно инициализировать:
# идентификатор проекта
PROJECT_ID=0
# хост Sentry
HOST=''
# ключ из DSN
SENTRY_KEY=''
Story Endpoint - устаревшая версия, но еще поддерживается, по этой схеме работает Sentry PHP (как минимум до 8-ой версии PHP
). Пример:
curl -v \
-d '{"exception":{"values":[{"type":"Test issue","value":"store"}]}}' \
-H 'Content-type: application/json' \
-H "X-Sentry-Auth: Sentry sentry_version=7, sentry_key=$SENTRY_KEY" \
"https://$HOST/api/$PROJECT_ID/store/"
Как видно это классический POST
запрос с json
в теле. Снаружи эта issue
выглядеть будет так (все лаконично, говорит только о типе, который мы указали явно):
Envelope Endpoint - замена предыдущему story endpoint
, по этой схеме уже давно работает Sentry JavaScript, здесь у нас уже вычурный многослойный json
разделенный новыми строками:
# подготовка тела запроса
echo '{"sdk":{"name":"sentry.javascript.vue","version":"7.54.0"},"trace":{"environment":"monitoring"}}' > body.json
echo '{"type":"event"}' >> body.json
echo '{"exception":{"values":[{"type":"Test issue","value":"envelope"}]}}' >> body.json
curl -v \
-H 'Content-type: text/plain' \
--data-binary @body.json \
"https://$HOST/api/$PROJECT_ID/envelope/?sentry_key=$SENTRY_KEY&sentry_version=7"
В Sentry эта issue
будет выглядеть так:
Tunnel - это кастомный endpoint
чтобы обойти блокировщиков рекламы для JavaScript. На Хабре я уже писал об этом подробнее.
По сути это то же самое что и envelope endpoint
, только вместо прямой отправки в Sentry используется промежуточная отправка на другой endpoint
(произвольный), который вам нужно самостоятельно реализовать.
Код отправки такой же, но в теле мы явно указываем что это tunnel
и для нашей реализации в первом json
отправляем dsn
:
# подготовка тела запроса
# авторизация в теле прямо из клиента, нам она не нужна, но для правдоподобности оставим
echo '{"sdk":{"name":"sentry.javascript.vue","version":"7.54.0"},"trace":{"environment":"monitoring"},"dsn":"https://'$SENTRY_KEY'@'$HOST'/'$PROJECT_ID'"}' > body.json
echo '{"type":"event"}' >> body.json
echo '{"exception":{"values":[{"type":"Test issue","value":"envelope tunnel"}]}}' >> body.json
# мы отправляем туда же, куда обычно шлем запросы, потому что под капотом свой обработчик
# он преобразует запрос и перенаправит его на сервер Sentry
curl -v \
-H 'Content-type: text/plain' \
--data-binary @body.json \
"https://$HOST/api/$PROJECT_ID/envelope/$SENTRY_KEY/"
Это только пример, в своей реализации вы можете сделать что угодно.
В веб-интерфейсе Sentry выглядит это так:
Для событий мониторинга мы сделали отдельный проект в Sentry и на данный момент он у нас выглядит так:
Как видно здесь всего 3 issue
для каждого из способов. После первой успешной отправки запроса сформируется issue
- группа похожих ошибок. Все последующие запросы с теми же данными будут попадать в эту же issue
.
На скрине выше видно, что для frontend
(через envelope
и tunnel
) график не равномерный, это говорит о проблемах с доставкой событий до Sentry (Sentry находится за VPN
, а мы принимаем ошибки с веб-страниц из интернета через Gateway
, вот с этим Gateway
были проблемы на скринах).
А по <unlabeled event>
(это store endpoint
для ошибок из PHP) ровный график, здесь с доставкой все хорошо. Вообще здесь практически всегда все хорошо, потому что все наши приложения и Sentry работают в одной VPN
сети.
Мы настроили мониторинг в Zabbix и вот как у нас выглядит один из графиков:
В начале графика видна неравномерность, что говорит о проблемах, которые мы уже устранили. Справа ровный график, это нормальная работа доставки тестовых событий.
Теперь мы понимаем механику отправки событий в Sentry, и в любой момент времени можем протестировать Sentry на факт приема событий. Но помним что отправка запроса и 200-ый ответ это не гарантия приема события.
На этот случай нам нужно настроить проверку приема запроса со стороны Sentry для каждого issue
, которое мы создали на предыдущем этапе. Делать это мы будем при помощи API Sentry:
ISSUE_ID=0
SENTRY_TOKEN=""
HOST=''
lastSeen=$(curl -s -H "Authorization: Bearer $SENTRY_TOKEN" "https://$HOST/api/0/issues/$ISSUE_ID/" | grep -o -m 1 '"lastSeen":"[^"]*' | grep -o -m 1 '[^"]*$')
timeDiff=$(( $(date -d "$lastSeen" +"%s") - $(date +%s) ))
timeDiff=${timeDiff#-}
echo $timeDiff
В итоге мы имеем время последнего события в определенной issue
, проще говоря когда последний раз была отправка запроса по этому issue.
Теперь встает вопрос: как настроить проверку? Мы настроили проверку каждые 15 минут, то есть:
issue
отправляются каждые 10 минутТаким образом мы заложили небольшой запас времени чтобы не создавались ложноположительные триггеры от:
Спустя полтора года использования Sentry self-hosted
показанная схема мониторинга доставляемости и принимаемости ошибок всегда работала исправно, в любой момент времени мы знали: может ли Sentry принимать ошибки из наших приложений или нет. Это позволяло не упускать критические проблемы и своевременно их обнаруживать.
Если появляется триггер значит есть проблема, ложноположительные срабатывания маловероятны.