Я интегрировал 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: