Sentry-javascript: Облачные функции Google — настройка клиента Sentry

Созданный на 18 июн. 2019  ·  35Комментарии  ·  Источник: getsentry/sentry-javascript

Привет команда, люблю Sentry, спасибо!

Я использую Google Cloud Functions с Typescript для нового проекта, и я хотел бы использовать Sentry для фиксации ошибок.

Из этого комментария видно , что Cloud Functions не позволяет третьим сторонам подключаться к глобальному обработчику ошибок Node.

Это все еще точно? Могу ли я заставить библиотеку Sentry JS работать с Google Cloud Functions?

Связано: https://forum.sentry.io/t/google-cloud-functions-client-setup/1970

Все 35 Комментарий

Эй, спасибо! :)

Это все еще точно? Могу ли я заставить библиотеку Sentry JS работать с Google Cloud Functions?

Согласно https://github.com/googleapis/nodejs-error-reporting#catch -and-reporting-application-wide-uncaught-errors и https://github.com/googleapis/nodejs-error-reporting#unhandled - Отказы это возможно сделать сейчас, однако я еще не проверял это сам.

Я могу сделать это за несколько дней, но вы можете попробовать, если у вас есть свободное время :)

Спасибо @kamilogorek. Интересно!

Я не уверен, как это взаимодействует с облачными функциями Google. Сейчас я занимаюсь техническим планированием, так что не скоро приступлю к тестированию. Но я дам вам знать, если / когда я это сделаю. :)

Прямо сейчас мы просто оборачиваем все наши облачные функции в:

try () {
...
} catch (e) {
  Sentry.captureException(e);
}

Но мы не видим исходный код или другой контекст в Sentry.

Есть ли лучший способ сделать эту интеграцию?

@vpontis только что проверил это сам, и разрешение контекста, похоже, работает нормально. Какова ваша функция/конфигурация?

image

Привет @kamilogorek спасибо за помощь!

Я воспроизвел то, что вы получаете с Hello World, так что это здорово.

Но контекстных переменных нет. Как будто вы не видите значения переменных в области видимости? Или это только функция Python?

image


Кроме того, похоже, что более сложные ошибки, которые я получаю, возникают, когда я делаю записи/чтения в базу данных, и в потоке/событии есть какая-то ошибка.

Например, на скриншоте ниже я даже не могу сказать, из какой функции она была вызвана.

Какова ваша рекомендация? Есть ли способ получить более длинную трассировку StackTrace, включающую вызов исходного обработчика функции? Или мне просто нужно добавить больше панировочных сухарей?

image


Кроме того, есть идеи, что происходит с этими ошибками «Исходный код не найден»?

image

Но контекстных переменных нет. Как будто вы не видите значения переменных в области видимости? Или это только функция Python?

Это только функция Python. К сожалению, JS не предоставляет механизма для реализации этой функции.

Например, на скриншоте ниже я даже не могу сказать, из какой функции она была вызвана. Какова ваша рекомендация? Есть ли способ получить более длинную трассировку StackTrace, включающую вызов исходного обработчика функции? Или мне просто нужно добавить больше панировочных сухарей?

Невозможно сказать, что вызвало эту конкретную функцию, поскольку она была вызвана сокетом. И, как вы упомянули, лучший способ отследить это — использовать «хлебные крошки» при выполнении запросов к БД, прокси-вызовов или запросов внешних служб.

А поскольку бессерверные экземпляры долговечны, вы можете вызвать:

Sentry.configureScope(scope => scope.clear())
// or
Sentry.configureScope(scope => scope.clearBreadcrumbs())

чтобы получить чистый лист.

Кроме того, есть идеи, что происходит с этими ошибками «Исходный код не найден»?

Отключите Enable JavaScript source fetching в настройках ваших проектов, например. https://sentry.io/settings/kamil-ogorek/projects/testing-project/
Это приложение для бессерверных узлов, поэтому в этом нет смысла.

Это только функция Python. К сожалению, JS не предоставляет механизма для реализации этой функции.

Проклятие. Мне понравилось это в Python!

Супер полезно, спасибо Камиль. Я внесу эти изменения и свяжусь с вами здесь, если у меня возникнут дополнительные вопросы. Я уверен, что эта проблема будет полезна другим людям, использующим Sentry на JS без сервера.

Потрясающий! Я закрою проблему ради сортировки, но не стесняйтесь пинговать меня в любое время, и я открою ее снова, если это необходимо! :)

@kamilogorek Я добавил хлебные крошки в Google Cloud Functions и думаю, что вижу хлебные крошки из разных вызовов функций.

Имеет ли это смысл? Как я могу ограничить хлебные крошки только из одного HTTP-запроса с облачными функциями Google?

Это имеет смысл, однако можете ли вы показать мне пример кода, который вы используете в своих облачных функциях? Так будет проще все понять.

@kamilogorek ты очень помог!

Мы перешли на использование Koa с Docker/Node. Итак, у нас есть интеграция с Koa, настроенная следующим образом: https://docs.sentry.io/platforms/node/koa/

Он работает очень хорошо, за исключением хлебных крошек, мы видим хлебные крошки для всех запросов на сервере, а не только хлебные крошки, привязанные к текущему запросу.

Есть ли способ исправить это?

Есть ли способ исправить это?

Есть, но мне нужно протестировать его, прежде чем предоставлять код здесь :)
Я постараюсь вернуться к вам в течение ~ 1-2 дней.

@kamilogorek ты настоящая легенда мой друг

@vpontis , так что единственное, что вам действительно нужно сделать, это создать экземпляр домена в одном из ваших промежуточных программ. Как только он появится, SDK обнаружит его и использует для разделения контекстов. Вы также можете переместить туда parseRequest . (пример на основе https://github.com/koajs/examples/blob/master/errors/app.js)

const Sentry = require("@sentry/node");
const Koa = require("koa");
const app = (module.exports = new Koa());
const domain = require("domain");

Sentry.init({
  // ...
});

app.use(async function(ctx, next) {
  const local = domain.create();
  local.add(ctx);
  local.on("error", next);
  local.run(() => {
    Sentry.configureScope(scope => {
      scope.addEventProcessor(event => Sentry.Handlers.parseRequest(event, ctx.request));
    });
    next();
  });
});

app.use(async function(ctx, next) {
  try {
    await next();
  } catch (err) {
    // some errors will have .status
    // however this is not a guarantee
    ctx.status = err.status || 500;
    ctx.type = "html";
    ctx.body = "<p>Something <em>exploded</em>, please contact Maru.</p>";

    // since we handled this manually we'll
    // want to delegate to the regular app
    // level error handling as well so that
    // centralized still functions correctly.
    ctx.app.emit("error", err, ctx);
  }
});

// response

app.use(async function() {
  throw new Error("boom boom");
});

// error handler

app.on("error", function(err) {
  Sentry.captureException(err);
});

if (!module.parent) app.listen(3000);

parseRequest принимает некоторые параметры, которые вы можете использовать, чтобы выбрать, какие данные запроса вы хотите извлечь – https://github.com/getsentry/sentry-javascript/blob/f71c17426c7053d46fe3e2e35e77c564749d0eb7/packages/node/src/handlers .ts#L177

Спасибо @kamilogorek!

Некоторые мысли:

  1. Вы можете привязать фактический узел req и res к домену, который хранится в ctx.req и `ctx.res.

  2. Вы можете перейти в узел с req на parseRequest

  3. Почему вы вызываете ctx.app.emit("error", err, ctx); после ручного обнаружения ошибки?

    // since we handled this manually we'll
    // want to delegate to the regular app
    // level error handling as well so that
    // centralized still functions correctly.
    ctx.app.emit("error", err, ctx);

Разве это не тот случай, когда вы хотите просто вернуть ответ и не передавать ошибку централизованному обработчику ошибок?

  1. В Коа next равно async . Вызовет ли это какие-либо проблемы внутри узла domain.run(...)

Дайте мне знать, если это имеет смысл. Это уже будет супер, супер полезно. Я протестирую его позже на этой неделе.

Ах, что касается 3, я вижу, вы просто копируете это из примера, поэтому я проигнорирую эту часть :). Тестирую сейчас...

Хм, у меня проблемы с тем, чтобы заставить это работать. Я думаю, это из-за смешивания обратных вызовов домена и функций async в Koa.

Также похоже, что домены даже не работают в Node 12 (я планирую вскоре обновиться, чтобы получить поддержку асинхронной трассировки стека).

В любом случае, я не понимаю, как работает этот код с доменом, поэтому я не решаюсь поместить его в важную часть приложения.

Есть ли другой способ поместить текущий «концентратор» в ctx и вызвать addBreadcrumb , как-то связанный с текущим запросом? Я не совсем понимаю, как в Sentry работают концентраторы и обработка сообщений...

У меня работает этот код (это две последовательные промежуточные функции), но я не чувствую себя комфортно с тем, _почему_ он работает...

export const setSentryDomain = async (ctx, next) => {
  await new Promise(async (resolve, reject) => {
    const local = domain.create();

    local.add(ctx.req);
    local.add(ctx.res);

    local.run(async () => {
      Sentry.configureScope((scope) => {
        scope.addEventProcessor((event) => Sentry.Handlers.parseRequest(event, ctx.req));
      });

      await next();
      resolve();
    });
  });
};

export const catchErrors = async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    console.log('got error');
    ctx.app.emit('error', err, ctx);
  }
};

Я называю это ночью. Я не получил рабочего решения, которым я был доволен.

Я также понял, что большинство моих «хлебных крошек» — это операторы console.log , которые автоматически перехватываются Sentry. Так что мне нужно выяснить, как заставить эти хлебные крошки быть на правильном прицеле...

Я поковырялся в коде, и кажется, что домены решают эту проблему. Но домены не кажутся надежными с асинхронными функциями, и они скоро исчезнут...

Есть ли другой способ поместить текущий «концентратор» на ctx и вызвать addBreadcrumb, как-то связанный с текущим запросом?

Вы можете назначить концентратор непосредственно ctx , однако это не будет работать для автоматически захваченных цепочек навигации, поскольку для возврата концентратора требуется Sentry.getCurrentHub() , которому должны быть назначены навигационные крошки. И один из способов его обнаружения — использование domain.active .

Но домены не кажутся надежными с асинхронными функциями, и они скоро исчезнут...

К сожалению, они уходят с декабря 2014 года, четкой замены (асинхронные хуки есть, но они тоже не идеальны) не видно, а также не удалялись из ядра ноды за эти 5 лет.

Сейчас мы играем с zone.js , так как это очень помогло бы, но пока это все еще огромный PoC, и мы не можем сказать, когда и даже будем ли мы когда-либо использовать его для замены доменов.

Привет @kamilogorek ,

Я пытаюсь зафиксировать необработанные ошибки в Sentry и попробовал то, что вы упомянули в https://github.com/getsentry/sentry-javascript/issues/2122#issuecomment -503440087.

Но я думаю, что Google не позволяет вам подключиться к process.on('uncaughtException') , я не смог зарегистрировать какие-либо ошибки в Sentry, используя этот подход.

Есть ли какой-либо другой способ, который вы бы порекомендовали попробовать, обертывание каждого тела функции в блоке try catch не кажется идеальным способом.

Мы предоставляем метод wrap , но я не думаю, что он намного лучше, чем try/catch tbh.

exports.helloBackground = (data, context) => {
  return `Hello ${data.name || 'World'}!`;
};

// becomes

exports.helloBackground = (data, context) => {
  return Sentry.wrap(() => {
    return `Hello ${data.name || 'World'}!`;
  })
};

Мне кажется, что эту проблему, возможно, стоит оставить открытой как запрос функции для более простой поддержки облачных функций firebase/google.

Я был невероятно впечатлен простотой настройки на стороне клиента и разочарован, когда понял, что настройка на стороне сервера будет намного сложнее. Есть ли план по улучшению GCP (особенно функций)?

@goleary Не могли бы вы открыть новый выпуск с точным описанием того, что вы хотели бы видеть?
Эта ветка уже довольно большая и за ней трудно следить.

Спасибо

Неужели нет более простого способа настроить это? В идеале иметь возможность установить его один раз глобально, без необходимости устанавливать его для каждой функции?

@marcospgp нет, и, к сожалению, не будет, поскольку сам Google не предоставляет механизма, который позволил бы это сделать. См. их собственный репортер - https://cloud.google.com/error-reporting/docs/setup/nodejs , он также использует ручные вызовы.

Хм, интересно, я настроил Sentry на облачных функциях Firebase (которые за кулисами используют облачные функции Google) и только что получил отчет об ошибке - так что, похоже, он работает!

Вот мой index.js, где находится весь код Sentry:

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..."
  );
}

admin.initializeApp();

const { function1 } = require("./cloud-functions/function1");
const { function2 } = require("./cloud-functions/function2");

module.exports = {
  function1,
  function2
};

@marcospgp мог ли вышеупомянутый index.js отправлять неперехваченные исключения в ./cloud-functions/function1 и ./cloud-functions/function2 в Sentry, или вам приходилось активно входить в Sentry в этих файлах?

Я только что попробовал решение @marcospgp , но, похоже, оно не регистрирует неперехваченные исключения, он должен регистрировать их вручную (используя sentry.captureException() ?)

То же, что и @goleary здесь, ничего не регистрируется.

Также кажется, что .wrap() больше не доступен.

Означает ли это, что мы должны вручную обернуть весь код внутрь try/catches?

@Dinduks это то, что я делаю. Накладные расходы немного раздражают, но возможность использовать sentry стоит усилий по ведению журнала облачных функций firebase (не очень).

@Dinduks wrap все еще там, см.: https://github.com/getsentry/sentry-javascript/blob/master/packages/browser/src/exports.ts#L40
Однако имейте в виду, что он немедленно выполняет функцию и возвращает вам возвращаемое значение. Поэтому я не уверен, что это более полезно, чем обычная попытка/поймать, которая также позволяет вам выполнять некоторые резервные действия пользователя.

const myHandler = (req, res) => Sentry.wrap(() => {
  someFunctionThatCanBreak(req);
  return res.send(200);
});

Я не думаю, что это хорошая идея, но я создал оболочку, которая выглядит так.

import * as Sentry from "@sentry/node";

export const sentryWrapper = (f) => {
  return async function () {
    try {
      // eslint-disable-next-line prefer-rest-params
      return await f.apply(this, arguments);
    } catch (e) {
      Sentry.captureException(e);
      await Sentry.flush(2000);
      throw new Error(e);
    }
  };
};

Он будет использоваться следующим образом.

export const getChannelVideoTest = functions.https.onRequest(
  sentryWrapper(async (req, res) => {
    someFunctionThatCanBreak(req);
    return res.send(200);
  }),
);

Я хотел бы знать, есть ли лучший способ.

@камилогорек
Я тоже с этим борюсь.

Я успешно Sentry.init() и у меня есть проблемы, когда я оборачиваю весь свой код в оператор try {} catch {} и вручную вызываю Sentry.captureException и Sentry.flush() .
Однако я не могу ничего получить, если удалю оператор try/catch .
То же самое касается мониторинга производительности, когда я ничего не получаю, пока вручную не создам транзакцию с Sentry.startTransaction() в начале функции.

Ожидается ли это?
Есть ли способ отправить необработанные ошибки в Sentry?
Если нет, значит ли это, что на вкладке «Производительность» частота отказов всегда будет установлена ​​на 0%? (поскольку мы ловим ошибку и вручную сообщаем о ней, транзакция закрывается должным образом, поэтому имеет статус ok?)

@axelvaindal , мы пока не поддерживаем мониторинг производительности для бессерверных приложений. Что касается этого вопроса:

Есть ли способ отправить необработанные ошибки в Sentry?

Тогда нет, не совсем так, поскольку GCF не предоставляет возможности подключения к необработанным исключениям/отклонениям, поэтому мы не можем их перехватить. Вам нужно обернуть свои обработчики (см. комментарий выше вашего), чтобы вручную поймать его.

Вы также можете прочитать нашу реализацию обработчика AWSLambda, чтобы получить некоторые идеи, как улучшить этот фрагмент — https://github.com/getsentry/sentry-javascript/blob/master/packages/serverless/src/awslambda.ts .

Была ли эта страница полезной?
0 / 5 - 0 рейтинги