Я интегрировал Sentry.io в интерфейс нескольких разрабатываемых мной веб-приложений и влюбился. Было так легко получать отчеты о неожиданных ошибках, с которыми пользователи сталкивались на лету. Настолько, что я подумал про себя, может, мне не следует ловить ЛЮБУЮ ошибку, чтобы она автоматически отображалась в консоли Sentry. Шучу, это не лучшая идея (вы можете отслеживать ошибки и записывать их в журнал для более изящной обработки).
Я был так восхитителен, что хотел того же и для своего бэкэнда. Два рассматриваемых приложения использовали облачные функции Firebase в качестве «бэкэнда».
К сожалению, настройка Sentry.io была далеко не такой простой, как с моим кодом внешнего интерфейса (React).
Достаточно взглянуть на этот выпуск почти годовой давности, в котором обсуждается использование Sentry в рамках Google Cloud Functions (базовая технология, на которой работают функции Firebase).
Внизу есть комментарий, который дает проблеск надежды и предлагает включить этот установочный фрагмент в index.ts / js корневого каталога функций:
const admin = require("firebase-admin"); const functions = require("firebase-functions"); const sentry = require("@sentry/node"); /** * Set up Sentry for error reporting */ if (functions.config().sentry && functions.config().sentry.dsn) { sentry.init({ dsn: functions.config().sentry.dsn }); } else { console.warn( "/!\\ sentry.dsn environment variable not found. Skipping setting up Sentry..." ); }
Для этого необходимо установить переменную конфигурации с помощью команды firebase cli:
firebase functions:config:set sentry.dsn=”<DSN>"
Вы можете найти свой DSN на панели управления Sentry в разделе Настройки ›PROJECT_NAME› Client Keys ›DSN.
К сожалению, это НЕ позволяет часовому перехватывать необработанные исключения, но вы можете регистрировать перехваченные исключения следующим образом:
const sentry = require("@sentry/node"); ... try { const uid = req.query.uid as string; const webhookData: WebhookData = req.body; const result = await plaidWebhookTrigger(uid, webhookData); return resp.json(result); } catch (error) { console.log('error: ', error); sentry.captureException(error); return resp.status(500).json({ error }) }
Тогда вы получите красивый отчет на панели инструментов Sentry, который покажет вам, какая именно строка кода вызвала исключение:
Это решение требует, чтобы вы обернули тело каждой из ваших функций в блок _1 _ / _ 2_, чтобы вы могли сообщать о каждой ошибке часовому перед повторным выбросом (чтобы Firebase также знала, что функция не удалась).
Функция многоразовой обертки
Вместо того, чтобы явно использовать блок try / catch каждый раз, вы также можете определить функцию-оболочку, которая позаботится об этом за вас и немного сократит шаблон для каждой функции.
Вот как это может выглядеть:
const wrapAndReport = (fn) => async (…args) => { try { return await fn(…args); } catch (e) { Sentry.captureException(e); await Sentry.flush(2000); throw e; } };
Используется так:
const myFirebaseFunction = functions.https.onRequest( wrapAndReport(async (req, resp) => { throw Error(“test error”); }) );
Опять же, поскольку вам нужно не забыть обернуть все свои функции Firebase, это решение не так оптимально, как Sentry, используемое в браузере, но оно избавляет вас от многократного повторения одного и того же кода try / catch + Sentry.
Это не идеальное решение, но я предпочитаю просматривать свои ошибки на панели инструментов Sentry, а не в загадочной консоли функций Firebase: