Подружим Sentry и Mattermost быстро и просто через адаптер
- среда, 20 сентября 2023 г. в 00:00:21
Всем привет, если у вас появилась идея связать эти два инструмента, то хочу вас огорчить, прямой интеграции у них пока нет...
4 простеньких шага для решения этой проблемы:
Создаем webhook в Mattermost
Создаем Custom Integration и Alert в Sentry
Пишем небольшой сервис, который будет принимать сообщение(event) из Sentry
Приводим event в нужный формат и отправляем в Mattermost с помощью webhook
Весь код сервиса лежит в этом репозитории на гитхибе.
Когда я столкнулся с проблемой, я начал искать готовые решения или хотя бы туториалы по интеграции этих инструментов, но не нашел ничего и решил это исправить, написав гайд и возможно, сэкономив кому-то пару часов, а может и дней жизни. Код сервиса будет максимально простой, главная идея статьи - показать один из способов решения проблемы и направить в какую сторону копать, надеюсь кому-то пригодится:)
Как я уже написал ранее, на сегодняшний день прямой интеграции Sentry и Mattermost нет.
Если в Sentry зайти в settings -> integrations вы увидите очень много разных плагинов, но нужного нам нет.
Интеграция напрямую через webhook так же не сработает, так как webhook Mattermost ждет один формат, а Sentry шлет совсем другой (вы получите 404 ошибку при отправке напрямую).
Например если вы захотите получать уведомления из Gitlab в Mattermost, вам достаточно в Gitlab зайти в settings -> integrations -> добавить интеграцию Mattermost notification - done! Но с Sentry так не сработает:(
Ссылка на документацию: https://developers.mattermost.com/integrate/webhooks/incoming/
Заходим Интеграции -> Входяшие вебхуки -> Создать входящий вебхук
После создания, запоминаем URL_WEBHOOK
Если у вас нет кнопки "Интеграции" - попросите админа сервера:
Зайти в админ панель Mattermost → Найти раздел Integrations → Enable Incoming Webhooks
Заходим Settings → Custom Integrations → Create new integration
Name - Имя интеграции
Webhook URL - домен + роут где будет лежать наш адаптер
Alert Rule Action - true
В разделе Webhooks ставим галочки на нужные events
Остальное можно оставить по дефолту → Создаем интеграцию
Заходим в Alert → Create new alert → Issues → Set Conditions
Далее в открывшемся окне в пункте Set Conditions вы можете задать правила отправки уведомлений, так называемые тригеры по условиям.
Главное в окне THEN выбрать раннее созданную интеграцию по имени, которые вы задали
Остальное можно оставить по дефолту, подробнее почитать тут
Приложение напишем на Nodejs, ссылка на репо
Ниже прикрепляю 2 основных файла, с комментариями:
Точка входа в приложение - app.js
const express = require("express");
const bodyParser = require("body-parser");
const axios = require("axios");
const logger = require("./utils/logger");
const getTextForMattermost = require("./getTextForMattermost");
require("dotenv").config();
const app = express();
const PORT = process.env.PORT || 4045;
// Сюда вставить вебхук из Mattermos
const URL_WEBHOOK = process.env.URL_WEBHOOK;
app.use(bodyParser.json());
// Endpoint на который Sentry будет кидать event
app.post("/webhook", ({ headers, body }) => {
try {
// Отправляем отформатированный формат eventа в Mattermost
axios.post(URL_WEBHOOK, { text: getTextForMattermost({ headers, body }) });
} catch (err) {
logger.error("Ошибка при отправке в Mattermost", error);
}
});
app
.listen(PORT, () => {
logger.info(`Сервер успешно запущен на порту ${PORT}`);
})
.on("error", (error) => logger.error("Ошибка при запуске сервера", error));
Файл форматирования - getTextForMattermost.js
const findDeepValue = require("./utils/findDeepValue");
const logger = require("./utils/logger");
/** Замкнули обьект с данными, для удобного получения нужных полей из этого обьекта на любом уровне вложенности; */
const getDataFromRequest = (reqBody) => {
return (fieldName) => {
return findDeepValue(reqBody, fieldName);
};
};
/** Sentry кидает обьект ошибки, fieldName это нужны поля из этого обьекта; */
const configFields = [
{ fieldName: "environment", labelName: "Окружение" },
{ fieldName: "title", labelName: "Название" },
{ fieldName: "action", labelName: "Действие" },
{ fieldName: "web_url", labelName: "Сслыка на Sentry" },
{ fieldName: "status", labelName: "Статус" },
{ fieldName: "message", labelName: "Описание(message)" },
];
/** Формируем сообщение для Mattermost */
module.exports = getTextForMattermost = ({ headers, body }) => {
try {
logger.info("Из Sentry пришло событие", { headers, body });
/** Docs Sentry: https://docs.sentry.io/product/integrations/integration-platform/webhooks/?original_referrer=https%3A%2F%2Fwww.google.com%2F#sentry-hook-resource */
const resource = headers["Sentry-Hook-Resource"]; // installation | event_alert | issue | metric_alert | error | comment
const getFieldFromData = getDataFromRequest(body);
let message = "";
configFields.forEach((item) => {
const value = getFieldFromData(item.fieldName);
if (value) {
message += `###### ${item.labelName}: ${value}\n`;
}
});
if (resource) {
message += `###### Название ресурса: ${resource}`;
}
return `#### Ошибка в анкете, подробности ниже:\n ${message}`;
} catch (error) {
logger.error("Ошибка в методе getTextForMattermost", error);
return (
`#### Ошибка в адапторе:\nЧто-то сломалось в методе getTextForMattermost, проверьте логи на сервере адаптера ` +
error?.message
);
}
};
Остается задеплоить ваш сервис на хостинг и запустить (я использую pm2 для управления процессами ноды), не забудьте правильно указать Webhook URL при создании интеграции с правильным доменом, на котором будет лежать ваш адаптер.
Чтобы проверить работы сервисы, вы можете на шаге создания Alert в Sentry, выбрав вашу Интеграцию, отправить “Send Test Notifications” → Должно прийти сообщение в Mattremost
Ресурсы:
Вот и все! Спасибо за внимание:)