Sentry-javascript: Google Cloud Functions - Sentry 客户端设置

创建于 2019-06-18  ·  35评论  ·  资料来源: getsentry/sentry-javascript

嗨团队,爱哨兵,谢谢!

我正在将带有 Typescript 的 Google Cloud Functions 用于一个新项目,并且我很想使用 Sentry 来捕获错误。

从这条评论来看,Cloud Functions 似乎不允许第 3 方连接到 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 Cloud Functions 交互。 我现在正在进行技术规划,所以不会很快进行测试。 但如果/当我这样做时,我会告诉你。 :)

现在我们只是将我们所有的云函数包装在:

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 的这一点!

超级有帮助,谢谢卡米尔。 如果我有任何问题,我将实施这些更改并在此回复您。 我相信这个问题会对在 JS serverless 上使用 Sentry 的其他人有所帮助。

惊人的! 我将关闭该问题以进行分类,但请随时联系我,如有必要,我将重新打开它! :)

@kamilogorek我在 Google Cloud Functions 上添加了面包屑,我想我看到了来自不同函数调用的面包屑。

那有意义吗? 如何将面包屑限制为仅来自一个带有 Google Cloud Functions 的 HTTP 请求?

这是有道理的,但是你能告诉我你在云函数中使用的示例代码吗? 这样就更容易理解一切。

@kamilogorek你真的很有帮助!

我们切换到使用带有 Docker / Node 的 Koa。 所以我们有这样的 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. 您可以将实际的节点reqres绑定到存储在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. 在 Koa 中nextasync 。 这会导致节点内部出现任何问题domain.run(...)

让我知道这是否有意义。 这已经非常非常有用了。 我将在本周晚些时候对其进行测试。

啊 re 3 我看到你只是从示例中复制它,所以我将忽略那部分:)。 现在测试它...

嗯,我很难让这个工作。 我认为这是由于 Koa 中混合了域回调和async函数。

此外,看起来域甚至在 Node 12 中都不起作用(我计划尽快升级以获得异步堆栈跟踪支持)。

不管怎样,我不明白这个带有域的代码是如何工作的,所以我很犹豫把它放在应用程序的关键部分。

是否有另一种方法可以将当前的“集线器”放在ctx上并以某种方式与当前请求相关联地调用addBreadcrumb ? 我不太确定 Sentry 中的集线器和消息处理是如何工作的......

我得到了这段代码(这是两个连续的中间件函数),但我对它的工作原理感到不舒服_why_...

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

我叫它一个晚上。 我没有得到令我满意的有效解决方案。

我还意识到我的大部分面包屑都来自 Sentry 自动捕获的console.log语句。 所以我需要弄清楚如何让这些面包屑在正确的范围内......

我戳了一下代码和域,听起来它解决了这个问题。 但是使用异步函数的域似乎并不可靠,而且它们很快就会消失......

是否有另一种方法可以将当前的“集线器”放在 ctx 上并以某种方式调用与当前请求相关的 addBreadcrumb?

您可以将集线器直接分配给ctx ,但是它不适用于自动捕获的面包屑,因为那些需要Sentry.getCurrentHub()才能返回集线器,因此应该将面包屑分配给。 它检测到它的方法之一是使用domain.active

但是使用异步函数的域似乎并不可靠,而且它们很快就会消失......

不幸的是,它们自2014年 12 月起就消失了,看不到明显的替代品(有异步钩子,但它们也不完美),并且在那 5 年里它们没有从节点的核心中删除。

我们现在正在玩zone.js ,因为它会有很大的帮助,但它现在仍然是一个巨大的 PoC,并且无法判断我们何时甚至是否会使用它来替换域。

@kamilogorek

我正在尝试将未处理的错误捕获到 Sentry 中,并尝试了您在https://github.com/getsentry/sentry-javascript/issues/2122#issuecomment -503440087 中提到的内容

但我猜谷歌不会让你连接到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不,不幸的是,不会有,因为谷歌本身并没有提供允许这样做的机制。 查看他们自己的记者 - https://cloud.google.com/error-reporting/docs/setup/nodejs它也使用手动调用。

嗯,有趣的是,我在 Firebase 云功能上设置了 Sentry(它在幕后使用了谷歌云功能),我刚收到一个错误报告 - 所以它似乎工作了!

这是我的 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/catch 中?

@Dinduks这就是我正在做的事情。 开销有点烦人,但能够使用哨兵是值得的,而不是 firebase 云功能日志记录(不是很好)。

@Dinduks wrap仍然存在,请参阅: https ://github.com/getsentry/sentry-javascript/blob/master/packages/browser/src/exports.ts#L40
但是,请记住,它会立即执行该函数并将返回值返回给您。 所以我不确定这是否比常规的 try/catch 更有帮助,它还允许您执行一些用户后备操作。

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);
  }),
);

我想知道是否有更好的方法。

@kamilogorek
我也在为此苦苦挣扎。

我已经成功Sentry.init()并且当我将所有代码包装在try {} catch {}语句中并手动调用Sentry.captureExceptionSentry.flush()时,我遇到了问题。
但是,如果我删除try/catch语句,我将无法得到任何报告。
性能监控也是如此,除非我在函数开头使用Sentry.startTransaction()手动创建事务,否则我什么也得不到。

这是预期的吗?
有没有办法将未处理的错误发送到 Sentry?
如果不是,这是否意味着性能选项卡将始终将故障率设置为 0% ? (因为我们捕捉到错误并手动报告它,事务正在正确关闭,因此状态正常?)

@axelvaindal我们还不支持无服务器的性能监控。 至于这个问题:

有没有办法将未处理的错误发送到 Sentry?

那么不,不是真的,因为 GCF 没有提供挂钩未处理异常/拒绝的方法,所以我们无法拦截它。 您需要包装您的处理程序(请参阅您上面的评论)以手动捕获它。

您还可以阅读我们的 AWSLambda 处理程序实施,以了解如何改进该代码段 - https://github.com/getsentry/sentry-javascript/blob/master/packages/serverless/src/awslambda.ts

此页面是否有帮助?
0 / 5 - 0 等级