Sentry-javascript: [@sentry/node] AWS Lambda 和其他无服务器解决方案支持

创建于 2018-07-28  ·  77评论  ·  资料来源: getsentry/sentry-javascript

  • @sentry/node 版本 4.0.0-beta.11
  • 我正在使用托管的哨兵

当前的行为是什么?

我正在使用 @sentry/node 来捕获 AWS lambda 函数的异常。

    .catch(err => {
      Sentry.captureException(err)
      context.fail()
    })

但是,当调用context.fail()并且异常不会在 Sentry 中结束时,它会终止该进程。

我可以做一个解决方法,例如:

    .catch(err => {
      Sentry.captureException(err)
      setTimeout(() => context.fail(), 1000)
    })

预期的行为是什么?

如果我能做类似的事情会很好:

    .catch(err => {
      Sentry.captureException(err, () => context.fail())
    })

或者全局处理回调的东西。

最有用的评论

@LinusU我们很可能会为这种情况创建一个特定的无服务器包。 我们只需要找点时间,因为现在是年底,现在事情变得越来越拥挤。 会及时向大家发布!

所有77条评论

这可能有助于我猜https://blog.sentry.io/2018/06/20/how-droplr-uses-sentry-to-debug-serverless (它使用的是旧的 raven 版本,它有一个回调,但我主要指向callbackWaitsForEmptyEventLoop标志。

目前没有正式的方法,因为我们仍在测试版中进行尝试,但可以使用以下代码:

import { init, getDefaultHub } from '@sentry/node';

init({
  dsn: 'https://my-dsn.com/1337'
});

exports.myHandler = async function(event, context) {
  // your code

  await getDefaultHub().getClient().captureException(error, getDefaultHub().getScope());
  context.fail();
}

@kamilogorek谢谢你的指点。 我会试一试并回放所学。

@kamilogorek你的建议有效。 我期待更正式的方式。

@vietbui
4.0.0-rc.1中,我们在客户端引入了一个名为close的函数,您可以这样称呼它:

import { getCurrentHub } from '@sentry/node';

getCurrentHub().getClient().close(2000).then(result => {
      if (!result) {
        console.log('We reached the timeout for emptying the request buffer, still exiting now!');
      }
      global.process.exit(1);
})

close将一直等待,直到所有请求都发送完毕,它总是会解决(结果 = 达到错误超时),直到达到超时(在本例中为 2000 毫秒)。
这是我们的官方 API。
虽然 prev 方法仍然有效,但close方法适用于所有情况。

@HazAT不错。 感谢所有的辛勤工作。

在 4.0.3 中,我在 lambda 函数中这样称呼它:

try {
  ...
} catch (err) {
  await getCurrentHub().getClient().captureException(err, getCurrentHub().getScope())
  throw err
}

getDefaultHub() 不再可用。

@vietbui现在称为getCurrentHub ,因为我们必须将我们的 API 与其他语言的 SDK 统一起来。

@kamilogorek感谢您的澄清。 getCurrentHub方法存在问题,因为不知何故我设置的范围并没有出现在 Sentry 中。

最后,我采用了@HazAT建议的不同方法来捕获我的 lambda 函数中的异常:

try {
  ...
} catch (err) {
  Sentry.captureException(err)
  await new Promise(resolve => Sentry.getCurrentHub().getClient().close(2000).then(resolve))
  throw err
}

而且效果很好。

这是等待/强制哨兵发送事件的推荐方式吗?

@albinekb是的 – https://docs.sentry.io/learn/draining/?platform=browser

由于某种原因,此解决方案对我不起作用。 它仅在有冷启动时间时在生产中第一次工作,之后就不再工作。 这是示例代码

'use strict'

const Sentry =  require('@sentry/node')
Sentry.init({
  dsn: 'xxx',
  environment: process.env.STAGE
});

module.exports.createPlaylist = async (event, context, callback) => {
  context.callbackWaitsForEmptyEventLoop = false
  if(!event.body) {
    Sentry.captureException(error)
    await new Promise(resolve => Sentry.getCurrentHub().getClient().close(2000).then(resolve))
    return {
      statusCode: 500,
      headers: { 'Content-Type': 'text/plain' },
      body: 'Missing body parameters'
    }
  }
  return {
    statusCode: 200,
  }
};

@Andriy-Kulak 这在文档中也有说明:

After shutdown the client cannot be used any more so make sure to only do that right before you shut down the application.

所以我不知道我们如何在 lambda 中处理这个问题,因为我们不知道应用程序何时会被杀死。 最好的办法是像使用旧 API 一样为每个请求耗尽哨兵?

@HazaT我们可以重新打开它吗? 我认为有一种方法可以在 Lambda 上使用它很重要,它正在成为越来越常见的部署目标。

这目前阻止我升级到最新版本...

就个人而言,我更希望能够在报告错误时获得 Promise/callback。 有一种方法来排空队列而不实际关闭它将是下一个最好的事情......

captureException中删除回调的理由是什么?

@albinekb如果我删除以下行,它根本不起作用

await new Promise(resolve => Sentry.getCurrentHub().getClient().close(2000).then(resolve))

@LinusU您使用的是什么解决方案和哨兵或乌鸦解决方案?

对我来说,基本上以下适用于sentry/node @4.3.0 ,但我必须让 lambda 手动等待一段时间(在这种情况下我放了 2 秒),让哨兵去做它需要做的事情。 我不确定为什么它需要在那里,因为我们正在等待哨兵完成captureException请求。 如果之后我没有等待期,那么哨兵似乎没有发送错误。

'use strict'

const Sentry =  require('@sentry/node')
Sentry.init({
  dsn: 'xxx',
  environment: process.env.STAGE
});

module.exports.createPlaylist = async (event, context, callback) => {
  context.callbackWaitsForEmptyEventLoop = false
  if(!event.body) {
    const error = new Error('Missing body parameters in createPlaylist')
    await Sentry.captureException(error)
    await new Promise(resolve => {setTimeout(resolve, 2000)})
    return {
      statusCode: 500,
      headers: { 'Content-Type': 'text/plain' },
      body: 'Missing body parameters'
    }
  }
  return {
    statusCode: 200,
  }
};

我们也在 Lambda 上对此感到恼火。 我们从新的库开始,完全被装箱了,考虑回到 Raven。 我们现在正在编写测试以尝试关闭集线器,然后重新初始化,如果它有水,这将是一个可行的解决方法。 但仍然很笨拙/可能会在负载下引起问题。

就个人而言,我更喜欢某种返回承诺的flush() - 很难找到缺点。 认为它会发生吗?

您使用的是什么解决方案和哨兵或乌鸦解决方案?

我正在使用以下快速错误处理程序:

app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
  let status = (err.status || err.statusCode || 500) as number

  if (process.env.NODE_ENV === 'test') {
    return next(err)
  }

  if (status < 400 || status >= 500) {
    Raven.captureException(err, () => next(err))
  } else {
    next(err)
  }
})

然后我使用将 Express 应用程序部署到 Lambda

编辑:这是 Raven "raven": "^2.6.3",

梦想的 API 应该是这样的 😍

Sentry.captureException(err: Error): Promise<void>

@LinusU https://github.com/getsentry/sentry-javascript/blob/master/packages/core/src/baseclient.ts#L145 -L152 🙂

您必须直接使用客户端实例来获取它。 这样做的原因是决定主要场景是“一劳永逸”的行为类型,因此它不是异步方法。 然而,在内部,我们确实有我们自己使用的异步 API。

似乎我真正想要的更像是:

const backend = client.getBackend()
const event = await backend.eventFromException(error)
await client.processEvent(event, finalEvent => backend.sendEvent(finalEvent))

为了跳过所有的排队和缓冲......

我知道该设计是为“即发即忘”而量身定制的,并且可以在长时间运行的服务器中运行,并且它可能非常擅长这一点,因为它会进行大量缓冲等。问题是这恰恰相反您想要的 Lambda、App Engine 和其他“无服务器”架构,这些架构变得越来越普遍。

是否有可能有一种特殊的方法尽可能快地发送事件,并返回一个我们可以await Promise 这对于无服务器场景来说是完美的!

class Sentry {
  // ...

  async unbufferedCaptureException(err: Error): Promise<void> {
    const backend = this.client.getBackend()
    const event = await backend.eventFromException(error)
    await this.client.processEvent(event, finalEvent => backend.sendEvent(finalEvent))
  }

  // ...
}

@LinusU我们很可能会为这种情况创建一个特定的无服务器包。 我们只需要找点时间,因为现在是年底,现在事情变得越来越拥挤。 会及时向大家发布!

我们很可能会为此场景创建一个特定的无服务器包

这将是惊人的! 😍

@mtford90

我什么时候会使用这个更好的解决方案? 据我所知,不可能知道 lambda 何时会关闭——而且等待任意时间关闭以允许哨兵做它的事情似乎很愚蠢——尤其是在昂贵的高内存/cpu lambda 函数上。

(谈论排水)

它应该用作关闭服务器进程之前的最后一件事。 drain方法中的超时是我们在关闭进程之前等待的最长时间,这并不意味着我们总是会用完那个时间。 如果服务器完全响应,它将立即发送所有剩余的事件。

本身没有办法知道这一点,但是有一种方法可以告诉 lambda 何时应该使用处理程序的回调参数关闭它。

另外@LinusU ,我重新阅读了您之前的评论,特别是这部分:

是否有可能有一个特殊的方法尽可能快地发送事件,并返回一个我们可以等待的 Promise? 这对于无服务器场景来说是完美的!

这就是我们实现缓冲区的方式。 客户端上的每个captureX调用都会将其添加到缓冲区中,这是正确的,它不会以任何方式排队,它会立即执行,并且仅使用此模式以便我们可以在一切正常的情况下获取信息成功发送到哨兵。

https://github.com/getsentry/sentry-javascript/blob/0f0dc37a4276aa2b832da451307bc4cd5413b34d/packages/core/src/requestbuffer.ts#L12 -L18

这意味着如果您在 AWS Lambda 中执行此类操作(假设您要使用默认客户端,这是最简单的情况):

import * as Sentry from '@sentry/browser';

Sentry.init({ dsn: '__YOUR_DSN__' });

exports.handler = (event, context, callback) => {
    try {
      // do something
    catch (err) {
      Sentry.getCurrentHub()
        .getClient()
        .captureException(err)
        .then((status) => {
          // request status
          callback(null, 'Hello from Lambda');
        })
    }
};

您可以确定它是立即发送的,并且没有时间/处理开销。

@kamilogorek
这是否意味着这样的事情应该在 async/await 处理程序中工作(你不使用回调)?

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

Sentry.init({ dsn: '__YOUR_DSN__' });

exports.handler = async (event, context) => {
    try {
      // do something

      return 'Hello from Lambda';
    catch (err) {
      await Sentry.getCurrentHub().getClient().captureException(err);
      return 'Hello from Lambda with error';
    }
};

完全@jviolas ! :)

似乎以下更改对我有用☺️

-import Raven = require('raven')
+import * as Sentry from '@sentry/node'

 // ...

-Raven.config(config.SENTRY_DSN)
+Sentry.init({ dsn: config.SENTRY_DSN })

 // ...

 app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
   let status = (err.status || err.statusCode || 500) as number

   if (process.env.NODE_ENV === 'test') {
     return next(err)
   }

   if (status < 400 || status >= 500) {
-    Raven.captureException(err, () => next(err))
+    Sentry.getCurrentHub().getClient().captureException(err).then(() => next(err))
   } else {
     next(err)
   }
 })

老实说,每一行都变得有点丑😆,但我想它在引擎盖下会更好......

@kamilogorek我在您网站上的文档中找不到getCurrentHub() ,这个 API 是否保证不会在没有重大 semver 碰撞的情况下中断? ❤️

@kamilogorek我在您网站上的文档中找不到 getCurrentHub(),这个 API 是否保证不会在没有重大 semver 碰撞的情况下中断? ❤️

是的,这是有保证的。 这是此处描述的@sentry/hub包的一部分 - https://docs.sentry.io/enriching-error-data/scopes/?platform=browser

我们在这个线程中讨论了有点“高级用途”,我们还没有到记录它们的地步。 我们最终会这样做:)

显然,我们在这里缺少的是此类高级用例中的一些文档和良好实践。 当它被记录下来或者甚至博客文章可以成为一个好的开始时,它会非常好。
否则,新的SDK使用起来真的很简单,统一性也很好。

@kamilogorek
这是否意味着这样的事情应该在 async/await 处理程序中工作(你不使用回调)?

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

Sentry.init({ dsn: '__YOUR_DSN__' });

exports.handler = async (event, context) => {
    try {
      // do something

      return 'Hello from Lambda';
    catch (err) {
      await Sentry.getCurrentHub().getClient().captureException(err);
      return 'Hello from Lambda with error';
    }
};

执行上述建议确实有效,但我无法添加额外的上下文。 例如,如果我这样做:

Sentry.configureScope(scope => {
   scope.setExtra('someExtraInformation', information);
});
await Sentry.getCurrentHub().getClient().captureException(err);

我实际上不会在 Sentry 中看到“someExtraInformation”。

有人确实在该线程的顶部提出了一种替代方法,并且可行,但似乎很hacky(强制超时)。

Sentry.configureScope(scope => {
  scope.setExtra('someExtraInformation', information);
});
Sentry.captureException(error);
await new Promise(resolve => Sentry.getCurrentHub().getClient().close(2000).then(resolve));

@kamilogorek @jviolas

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

Sentry.init({ dsn: '__YOUR_DSN__' });

exports.handler = async (event, context) => {
   try {
     // do something

     return 'Hello from Lambda';
   catch (err) {
     await Sentry.getCurrentHub().getClient().captureException(err);
     return 'Hello from Lambda with error';
   }
};

这也可以应用于 _uncaught exceptions_ 吗? 似乎修改Sentry.Integrations.OnUncaughtException集成是这样做的官方方式,但现在文档很差。

为此+1。 至少有一些正式记录的东西会很好。 Serverless 在 2019 年发展迅速,我真的很想看到 Sentry 的官方支持。 我在这里读到并且非常喜欢的想法之一是使用Sentry.flush()之类的东西来发送所有排队的事件。

@rdsedmundo你能详细说明为什么这种方法不适合你吗?

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

Sentry.getCurrentHub().getClient().close(2000).then(result => {
      if (!result) {
        console.log('We reached the timeout for emptying the request buffer, still exiting now!');
      }
      global.process.exit(1);
})

这是我们的官方方法,基本上是Sentry.flush()
参考: https ://docs.sentry.io/error-reporting/configuration/draining/?platform=javascript

@HazAT当您考虑 AWS Lambda 容器重用时,问题就来了。 在 TL;DR 术语中,这意味着如果在很短的时间窗口内完成了一个请求,那么它可以为一个新品牌提供服务。 如果我关闭与您提供的此代码段的连接,并且容器被重用,我需要设法为新请求创建一个新的集线器。 我可以很容易地看到这变得棘手。 这就是为什么简单的await Sentry.flush()会是更好的解决方案的原因:

import Sentry from './sentry'; // this calls Sentry.init under the hood

export const handler = async (event, context) => {
  try {
    ...
  } catch (error) {
    Sentry.captureException(error);
    await Sentry.flush(); // could even be called on the finally block

    return formatError(error);
  }
}

@rdsedmundo我不确定我是否可能误解了某些东西,但如果你这样做了

import Sentry from './sentry'; // this calls Sentry.init under the hood

export const handler = async (event, context) => {
  try {
    ...
  } catch (error) {
    Sentry.captureException(error);
    await Sentry.getCurrentHub().getClient().close(2000);

    return formatError(error);
  }
}

await Sentry.flush完全相同,只是您定义了超时。

如果队列中还有东西,则承诺在 2000 毫秒后肯定会用false解决。
否则close将解析为true如果队列在达到超时之前已经排空。

还是在所有承诺都解决之前容器会被重用? (我无法想象)

@HazAT不是close(...)会阻止客户端再次使用的问题吗? Lambda 重用相同的 Node 进程,所以调用会是这样的,我猜这在第一次调用close后会停止工作?

  • Sentry.init()
  • Sentry.captureException()
  • Sentry.getCurrentHub().getClient().close()
  • Sentry.captureException()
  • Sentry.getCurrentHub().getClient().close()
  • Sentry.captureException()
  • Sentry.getCurrentHub().getClient().close()
  • Sentry.captureException()
  • Sentry.getCurrentHub().getClient().close()
  • ...

不, close不会释放客户端,它只是用于排空传输队列。
我同意close在这种情况下的名称可能会产生误导,但至少在 JS/Node 中close不会对客户端做任何事情,之后仍然可以使用它是完全可以的。

编辑:如果这实际上是“问题”,我将更新文档以明确这一点。

凉爽的。 但是文档是错误的:

After shutdown the client cannot be used any more so make sure to only do that right before you shut down the application.

好的,我们只是在团队内部讨论了这个问题。
你们是对的,虽然 JavaScript 现在的行为不像我们记录的那样 🙈 我们将引入一个flush函数,它将完全符合您的期望。

因此,现在您可以毫无问题地使用close (不确定我们是否要更改它以在将来处置/禁用客户端)。
但是会有一个flush函数用于_just_ flush队列。

一旦功能登陆,我将更新此问题。

由于我对所有这些评论都有点迷失,这就是 Express 错误处理程序(模仿这个 repo 中的错误处理程序)应该是什么样子的吗?

function getStatusCodeFromResponse(error) {
    const statusCode = error.status || error.statusCode || error.status_code || (error.output && error.output.statusCode);
    return statusCode ? parseInt(statusCode, 10) : 500;
}

app.use(async (err, req, res, next) => {
    const status = getStatusCodeFromResponse(err);

    if (status >= 500) {
        Sentry.captureException(err)

        await Sentry.getCurrentHub().getClient().close(2000)
    }

    next(err)
})

看起来它正在工作,并且不会像@rreynier的代码那样丢失额外的数据。

我个人觉得

await Sentry.getCurrentHub().getClient().captureException(err)

比:

Sentry.captureException(err)
await Sentry.getCurrentHub().getClient().close(2000)

close真的读起来就像它会关闭客户端......

完整示例:

import * as Sentry from '@sentry/node'

// ...

Sentry.init({ dsn: config.SENTRY_DSN })

// ...

app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
  let status = (err.status || err.statusCode || 500) as number

  if (process.env.NODE_ENV === 'test') {
    return next(err)
  }

  if (status < 400 || status >= 500) {
    Sentry.getCurrentHub().getClient().captureException(err).then(() => next(err))
  } else {
    next(err)
  }
})

@LinusU我试过了,出于某种原因,它不会随堆栈跟踪一起发送额外的数据。 它基本上只发送堆栈跟踪。 没有关于用户、操作系统或任何东西的信息。

啊哈,一点都不好😞

当我们等待flush时,作为比上述两个选项更可靠的解决方法,您可以报告并等待结果,_and_ 包括范围,使用以下代码段:

const scope = Sentry.getCurrentHub().getScope();
await Sentry.getCurrentHub().getClient().captureException(error, scope);

我正在使用它,它似乎对我来说很可靠,报告的错误包括我所期望的一切。

我实际上将所有这些与 Netlify 函数一起使用,但理论与 Lambda 等相同。如果有人感兴趣,我已经写了一篇文章,详细介绍了如何使其工作: https://httptoolkit。技术/博客/netlify-function-error-reporting-with-sentry/

我目前在我所有的 lambdas 中都使用这个助手

@pimterry这不是与@LinusU建议的基本相同的解决方案吗? 我已经尝试过了,它也不会发送额外的数据。

到目前为止,这种方法对我来说已经奏效了@ondrowan

@ondrowan它是相同的,但手动抓取并包括当前范围。 我认为这应该足以让你工作异常。 在以前的版本中,我得到了未标记的事件,现在有了这个更改,我的异常出现了所有额外的正常细节。

@vietbui @albinekb @Andriy-Kulak @LinusU @dwelch2344 @jviolas @rreynier @guillaumekh @rdsedmundo @ondrowan @pimterry @zeusdeux不确定谁仍然对这个用例感兴趣,如果我不应该给你打电话,请原谅。

4.6.0开始,不再有client/hub舞蹈。 您可以调用任何我们的captureX方法,然后在所有内容发送到服务器后使用Sentry.flush()等待响应。 应保留所有范围/额外数据,无需任何开发交互。

这是一个成功/超时请求的示例。

image

希望能帮助到你! :)

好的!

是否仍计划制作一个仅用于从 Lambda 和其他无服务器解决方案中捕获异常的最小包? 我认为这仍然是一个非常好的补充❤️

@LinusU希望是的,但我们现在被其他语言的 SDK 淹没了😅

感谢所有人提供所有可能的解决方案,tl;博士感谢所有来到这里的人

使用: await Sentry.flush()发送所有待处理的请求,这已在4.6.x中引入。

关闭这个,如果缺少任何东西,请随时打开一个新问题(但这个线程已经超长了)。

干杯👍🎉

@kamilogorek嘿! 快速供您参考,我在我的应用程序中使用Sentry.flush代替旧的解决方法,并且没有报告任何错误。 我目前正在从更新的flush方法恢复到旧的解决方法

@zeusdeux有什么方法可以为此提供一些调试信息/重现案例?
您正在覆盖将事件添加到缓冲区的captureException方法,然后您应该在flush返回值上使用 $#$ await $#$。 您是否尝试过以“常规方式”使用它?

@kamilogorek我希望我有调试信息,但日志中没有任何内容。 我总是在被覆盖的captureException await #$ 。 按常规方式,您的意思是不覆盖captureException吗?

确切地说, @zeusdeux ,只需调用我们的原生Sentry.captureException(error)而不进行任何覆盖。

所以你的助手将是:

import * as Sentry from '@sentry/node'

export function init({ host, method, lambda, deployment }) {
  const environment = host === process.env.PRODUCTION_URL ? 'production' : host

  Sentry.init({
    dsn: process.env.SENTRY_DSN,
    environment,
    beforeSend(event, hint) {
      if (hint && hint.originalException) {
        // eslint-disable-next-line
        console.log('Error:', hint.originalException);
      }
      return event;
    }
  })

  Sentry.configureScope(scope => {
    scope.setTag('deployment', deployment)
    scope.setTag('lambda', lambda)
    scope.setTag('method', method)
  })
}

在你称之为的代码中:

import * as Sentry from '@sentry/node'

try {
  // ...
} catch (err) {
  Sentry.captureException(err);
  await Sentry.flush(2000);
  return respondWithError('Something went wrong', 500);
}

@kamilogorek我会试一试并报告。 另外,感谢关​​于beforeSend的提示 ^_^

await Sentry.flush(2000);

也〜不〜为我工作。

@tanduong你能提供复制案例吗? 只是说它不起作用并没有太大帮助😅

@kamilogorek实际上,我刚刚发现

await Sentry.getCurrentHub().getClient().close(2000)

对我也不起作用,因为我的 lambda 函数附加到 VPC。

我确认

await Sentry.flush(2000);

实际上正在工作。

顺便说一句,那么您将如何处理 VPC 中的 lambda? 连接到 NAT 网关? 我只想要 Sentry 而不是公共互联网。

@tanduong Sentry 在公共互联网上,所以是的,如果您的 lambda 在您的 VPC 中运行,您需要有 NAT 网关。 否则,您将不得不探索托管 Sentry 选项。

flush(2000)实际上在做什么? 我的这段代码工作得很好,但现在我有几个captureMessage调用同时发生,每次都超时!

通过网络刷新内部消息队列

好的,这完全有道理。 我认为我的问题是当没有其他东西可以刷新时,这个承诺永远不会返回? 每当我同时运行包装好的captureException fn 时,它都会使我的处理程序超时。

export const captureMessage = async (
  message: string,
  extras?: any,
): Promise<boolean> =>
  new Promise((resolve) => {
    Sentry.withScope(async (scope) => {
      if (typeof extras !== 'undefined') {
        scope.setExtras(extras)
      }
      Sentry.captureMessage(message)
      await Sentry.flush(2000)
      resolve(true)
    })
  })

在第一次 captureMessage 调用之后, await Sentry.flush()并没有真正完成。

我有我认为与@enapupe 类似的问题。 如果您并行调用await client.flush(2000); ,则只会解决第一个承诺。 这可能发生在 AWS lambda 上下文中,其中客户端在对处理程序的多个并发调用中被重用。

我正在使用这样的代码:

 let client = Sentry.getCurrentHub().getClient();
  if (client) {
    // flush the sentry client if it has any events to send
    log('begin flushing sentry client');
    try {
      await client.flush(2000);
    } catch (err) {
      console.error('sentry client flush error:', err);
    }
    log('end flushing sentry client');
  }

但是当我快速连续两次调用我的 lambda 函数时,我得到:

  app begin flushing sentry client +2ms
  app begin flushing sentry client +0ms
  app end flushing sentry client +2ms

你可以看到第二个承诺永远不会被解决。

@esetnik我对此提出了一个问题: https ://github.com/getsentry/sentry-javascript/issues/2131
我当前的解决方法是始终解决(基于超时)的包装器刷新 fn:

const resolveAfter = (ms: number) =>
  new Promise((resolve) => setTimeout(resolve, ms))

const flush = (timeout: number) =>
  Promise.race([resolveAfter(timeout), Sentry.flush(timeout)])

@enapupe我在 #2131 中添加了关于您的解决方法的注释。 我相信它会导致并发刷新的性能回归。

万一有人有任何问题。
这很好用

@SarasArya @HazaT
首先...感谢您分享您的解决方案! :)
我猜想它应该在 captureException 之前调用 configureScope 方法的一个回调,但它没有在同一个“线程”中完成。
这不会导致出现竞争条件吗?

@cibergarri我不这么认为,对我来说看起来是同步的,如果你在那里有一个异步方法,那么就会有竞争条件。
考虑就像数组的 .map 一样,这里发生了同样的事情。 以防万一您遇到问题。 我希望这会有所帮助。

是的,这样做完全没问题

更新:Sentry 现在支持 Node/Lambda 环境的自动错误捕获: https ://docs.sentry.io/platforms/node/guides/aws-lambda/

我正在像这样使用@sentry/serverless:

const Sentry = require("@sentry/serverless");
Sentry.AWSLambda.init({
  dsn: process.env.SENTRY_DSN,
  tracesSampleRate: 1.0,
  environment: appEnv
});

exports.main = Sentry.AWSLambda.wrapHandler(async (event, context) => {
     try{
           //my code
     }catch(error){
          Sentry.captureException(error);
          await Sentry.flush(3000);
     }

});

它不适用于 lambda。
在我的测试环境中它正在工作,但在有很多并发执行并且容器被重用的产品中,它只记录了总量的 10%。

有什么建议吗?

@armando25723

请告诉您如何衡量它丢失了异常事件? 您是否有关于如何抛出此类丢失异常的代码示例? 需要更多上下文。

const Sentry = require("@sentry/serverless"); // "version": "5.27.3"
Sentry.AWSLambda.init({
  dsn: process.env.SENTRY_DSN,
  tracesSampleRate: 1.0,
  environment: appEnv
});
exports.main = Sentry.AWSLambda.wrapHandler(async (event, context) => {
     try{
           throw new Error('Test Error');
     }catch(error){
          Sentry.captureException(error);
          await Sentry.flush(3000);
     }
});

怎么了?
如果函数被多次调用,调用之间的间隔很短,则事件只记录一次。
如果调用之间的时间间隔较大,则记录所有事件。

我认为问题出在调用是在重用的容器上时。

我也试过
await Sentry.captureException(error);
和:
await Sentry.flush();
并且没有冲洗
相同的结果

@marshall-lee 你有什么推荐的? 我应该创建一个问题,我被困在这里。

@armando25723看起来服务器在发送这些事件时响应 429(异常太多)。 如果出现超过配额/速率限制的情况,我们会抛出它。 您知道您是按顺序发送错误还是超出配额? 如果您认为这些是真正的错误事件被丢弃,并且您低于免费层的 5k 限制,我们可以进一步调试。

@ajjindal所有其他项目都可以与哨兵一起正常工作。 组织 slug 是“alegra”,项目名称是 #mail-micros 下的 mail-dispatch-serverless。 我们一直在使用哨兵很长一段时间,但第一次使用无服务器。 这不是免费套餐,我无法确切告诉您我们使用的是哪种套餐,但它是付费套餐。
如果您能帮助我进一步调试,那就太好了。
谢谢你的回复 : )

PD:是团队计划

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