Sentry-javascript: 节点建议:在 `unhandledRejection` 中记录错误

创建于 2019-02-19  ·  41评论  ·  资料来源: getsentry/sentry-javascript

  • [x] 查看文档: https :
  • [x] 搜索现有问题: https :
  • [x] 使用最新版本: https :

套餐+版本

  • [ ] @sentry/browser
  • [x] @sentry/node
  • [ ] raven-js
  • [ ] raven-node _(节点的乌鸦)_
  • [ ] 其他:

版本:

4.6.1

描述

开箱即用,Node 将记录未处理的承诺拒绝。 但是,在初始化 Sentry 后,这些日志将消失。 发生这种情况是因为如果存在unhandledRejection处理程序

我想建议 Sentry 在其处理程序中添加日志记录,以提供与开箱即用的体验。 至少,文档应该提到这种行为以及需要手动添加额外的处理程序来恢复日志记录。

最有用的评论

我仍然觉得uncaughtExceptionunhandledRejection的处理方式不同很奇怪。 Sentry 恢复uncaughtException日志记录——为什么它不为unhandledRejection做同样的事情? 用户不必记住使用该 Node 标志。 🤔

所有41条评论

听起来不错,但是,我不确定这是否应该在我们的debug: true标志后面。
最后,您有意识地附加错误处理程序,它将错误流重定向到 Sentry。

我也不确定。

启用 Sentry 不会对未捕获的异常禁用日志记录的效果,因此对于未处理的承诺拒绝可能也是如此。 (尽管这种行为上的差异在 Node 中比 Sentry 更像是一种不一致。)

当我启用 Sentry 时,我知道它正在报告我的错误,但我倾向于将控制台日志记录视为单独的事情,尤其是在开发中。 🤔

我们不会修改默认的日志记录行为,对于未捕获的异常,触发日志调用的是节点本身。

我认为这里最好的解决方案是在文档中做一个注释然后离开

process.on('unhandledRejection', (reason, p) => {
  console.log('Unhandled Rejection at:', p, 'reason:', reason);
});

片段以防有人想重新启用警告。

文档就足够了。 至少,哨兵没有做任何特别的事情。 这真的是 Node 的问题所在。

或未捕获的异常是节点本身触发日志调用的原因。

仔细想想,我不确定这是不是真的。

Sentry 注册一个uncaughtException处理程序,它禁用默认的节点行为(日志 + 退出)。

处理程序 ( defaultOnFatalError ) 触发自己的日志调用: https :

如果我们正在“恢复”未捕获异常的日志记录,我相信我们应该对未处理的承诺拒绝做同样的事情。

我们可以重新打开这个吗? 根据我的最后一条评论,我现在相信 Sentry 应该恢复未处理的承诺拒绝的日志记录,就像它对未捕获的异常所做的那样。

我发现这个问题是因为我开始使用 Sentry 并想知道我是否在我的代码中犯了错误,因为我的日志中只有uncaughtException而不是unhandledRejection 。 以相同的方式处理这两种情况会很有意义。

所以我会说要么记录两者要么都不记录。

我找到了一个更好的方法来处理这个问题,而不是从 Node 的核心复制代码。

λ: node --unhandled-rejections=warn app.js

通过包含在主文档页面https://github.com/getsentry/sentry-docs/pull/1099也使其显而易见

我仍然觉得uncaughtExceptionunhandledRejection的处理方式不同很奇怪。 Sentry 恢复uncaughtException日志记录——为什么它不为unhandledRejection做同样的事情? 用户不必记住使用该 Node 标志。 🤔

为什么它对 unhandledRejection 不做同样的事情

因为一个是关键的( unhandledException杀死进程),一个是信息性的(因此它是一个警告,而不是错误)。

如果我们颠倒顺序并默认发出警告,我们将打破 CLI 行为,尽管--unhandled-rejections被设置为none也会发出警告。 现在,根据官方节点文档,一切正常。 此更改_将_使其成为非标准。

一旦 Node 决定让unhandledRejection也终止一个进程(他们说现在有 4 个版本:P),我们将进行更改以符合官方规范。

如果我们颠倒顺序并默认发出警告,我们将破坏 CLI 行为

@kamilogorek ,但是当使用node运行时,它确实将unhandledRejectionconsole 。 所以我不知道官方节点文档是怎么说的,但至少这是我注意到的行为。

@freeall仅当您不附加unhandledRejection处理程序时。 如果你运行下面的代码,即使没有 SDK,它仍然不会记录,因此你必须知道你正在运行什么代码。

process.on('unhandledRejection', () => `¯\_(ツ)_/¯`);

我们在这里非常明确地声明我们这样做: https :

系统集成是默认启用的集成,它们组合到标准库或解释器本身中。 它们被记录在案,因此您可以看到它们的作用,如果它们引起问题,可以禁用它们。
OnUnhandledRejection
这种集成附加了全局未处理的拒绝处理程序。

我只是不想改变节点的行为,就是这样。 行为是:“如果有侦听器,请不要发出。如果您仍然想要警告,请使用标志。” - 这正是我们所做的。

@kamilogorek我知道你来自哪里。 但我认为 Sentry 的用户会希望 Sentry 不会改变他们程序的行为。

如果我有一个没有 Sentry 的unhandledRejection ,我会在控制台中看到它。
如果我有一个带有 Sentry 的unhandledRejection ,我在控制台中看不到它。

我个人不喜欢哨兵改变行为。

但这就是 Node.js 的设计方式¯_(ツ)_/¯
如果添加处理程序,警告就会消失。 我们的 SDK 添加了处理程序,因为它是捕获未处理错误的唯一方法,这是 SDK 的主要目的。

您当然对节点的设计方式是正确的。 当您附加处理程序时,警告消失了。
人们问的是你模仿节点的默认行为并将其记录到控制台。 改变的行为不是您对 Sentry 等工具的期望

无论如何,您似乎已经开始采取这种行为,因此继续讨论没有意义。 但感谢您抽出时间来回答:)

@freeall 也谢谢,看到双方总是很好:)

澄清一下:启用 Sentry 时, unhandledException (exit + log) 的行为被保留,但unhandledRejection (log) 的行为不是:

|处理程序|日志|退出|
|-|-|-|
| unhandledException默认|是|是|
| unhandledException哨兵|是|是|
| unhandledRejection默认|是|否|
| unhandledRejection哨兵|没有|没有|

现在,根据官方节点文档,一切正常。

这里的“如预期”是假设用户知道 Sentry 正在为unhandledRejection注册一个监听器。 这是用户不必担心的实现细节。

不过我确实明白你的意思。 Sentry 还应该尊重--unhandled-rejections ,如果标志设置为none并且 Sentry 继续记录,它就不会这样做。

@freeall评论很好地总结了这一点:

我认为 Sentry 的用户希望 Sentry 不会改变他们程序的行为。

未捕获的承诺拒绝不是“二等错误”。 它们可以而且确实会导致应用程序崩溃,就像正常错误一样。

似乎有几个用户遇到了这个问题(包括我),将来还会有更多用户遇到这个问题。

总而言之:Sentry 将控制台中的错误静音。 而且我认为很明显这是多么令人困惑,并且可能不是大多数哨兵用户的本意。 他们不会安装哨兵来从控制台消除某些错误子集。

所以推理( @kamilogorek

这就是 Node.js 的设计方式

对我来说有点神秘。 这如何决定哨兵的行为方式?

我们是否应该期望用户了解:

a) sentrys 内部工作(注册事件处理程序)

b) 节点内部工作(添加处理程序会导致警告消失)

如果他们不这样做,那么他们应该这样做吗?

当然,您说“好吧,用户应该知道代码在幕后做了什么”是正确的,但实际情况看起来有所不同。 而且你有机会在这里增加哨兵的易用性,或者不。

可以说“我们不关心这个特定问题”,但用“这不是错误,这是一个功能”的方式来描绘它似乎是不真诚的。

TLDR:恕我直言如果您想提高易用性并减少新手用户的问题,那么应该解决这个问题。

@OliverJAsh ,@freeall
你对这个问题使用了哪些解决方案?

@schmannd我已经从我的加载方式中添加了一个片段。 我同意你的看法,“这不是错误,这是一个功能”的答案似乎并不令人满意。 我担心很多程序员不会因为 Sentry 吃掉它们而捕捉到他们程序中的一些错误。 对我来说,像 Sentry 这样的工具的首要任务应该是捕捉错误,而不是创造它们。

...
if (isUsingSentry) {
  // Log to console because Sentry overwrites standard behavior. https://github.com/getsentry/sentry-javascript/issues/1909.
  // Note that it doesn't overwrite for uncaughtException.
  process.on('unhandledRejection', console.error)
}
...

当我在 React Native 中使用哨兵时,似乎我需要做一些棘手的事情来解决这个问题。 有没有人使用 React Native 并以不同的方式解决这个问题?

@OliverJAsh ,@kamilogorek
我们可以重新打开并修复此问题吗?

Sentry 绝对不应该在控制台中记录错误的方式——这不仅仅是 Node 的问题,因为控制台日志记录也在浏览器中被抑制。

尝试调试时非常烦人,而且由于您没有在 JavaScript 文档中提及这一点,因此我实际上认为在测试暂存构建时查看控制台时没有错误,因此没有意识到这一点有错误,直到将其部署到生产中。 对于错误报告服务来说,这是一种非常糟糕的用户体验,如果您想要满意的客户,您需要解决这个问题。

如前所述,承诺拒绝不是二等错误——它们可能与任何其他未处理的错误一样致命,不应以任何方式压制。

因此,一些调试揭示了控制台日志记录被抑制的原因:

image

Sentry 为window.onunhandledrejection分配一个函数,正如我们在这里看到的,该函数返回false ,从而显式抑制控制台日志记录。 所以是的,哨兵_确实_改变了默认行为 - 这并不酷。

幸运的是,它存储了对任何现有函数的引用,并在它存在时调用它。
因此,重新启用控制台日志记录的 hacky 解决方法是在初始化 Sentry 之前添加这一行:

window.onunhandledrejection = () => true;

现在,请修复此问题,这样我们就可以拥有默认行为,而无需进行这些毫无意义的黑客攻击 🙂

@schmannd请重新打开 issue

@thomas-darling 我同意浏览器的变化,它也应该为 promise 返回true ,我可以改变它。

但是,对于节点,由于一个原因,我仍然不相信。 它将代码与当前的 Node 实现联系起来。 如果我们将复制内部结构而不是依赖标志,并且承诺拒绝行为将在 v14 中改变,我们将必须检测我们所在的节点版本并采取相应的行动。
我们从侦听器返回什么并不重要,因为内部节点只检查侦听器数组并仅在根本没有侦听器时发出警告,并且无法修改此检测 - https://github.com/nodejs /node/blob/7cf6f9e964aa00772965391c23acda6d71972a9a/lib/internal/process/promises.js#L163 -L216

听起来不错,关于浏览器更改:+1:

至于 Node,如果你不修复 Sentry 的登录,你基本上只是强迫所有用户自己做 - 增加的风险是有些人会做错,有些人甚至不会意识到他们需要它,直到他们之后像我一样被生产错误咬伤。 这不是一个好的开发者体验......

@thomas-darling 你想如何修复它? 重现节点代码中的相同代码?

在我们文档的最顶部有一个非常明显的注释,为了获得默认的控制台日志记录 - https://docs.sentry.io/platforms/node/

image

我确实明白你的意思 - 必须复制节点行为将是一个潜在的维护问题,对于节点来说,这可以通过一个简单的命令行标志来解决,这确实有帮助。

但是,如果您不想复制节点行为,那么至少在未指定该标志时将警告记录到控制台,以便用户知道错误正在被抑制,以及如何避免这种情况。

这在节点的下一个版本中变得更加重要,默认情况下,未经处理的拒绝将导致进程崩溃——据我所知,当 Sentry 添加其处理程序时不会发生这种情况。
依赖 node 中这种新默认行为的用户可能会遇到令人讨厌的惊喜,如果他们稍后安装 Sentry 并且他们的进程突然继续,尽管发生了致命错误。
这种事情可能会导致数据丢失或其他灾难。

在我看来,有几个选项:

  1. 复制 Node.js 的做法
  2. 当有未处理的拒绝时,只需写信给console.error
  3. 抑制错误,使开发人员永远不会看到它

我认为选项 1 或 2 看起来都不错。 您的客户会看到错误并可以修复它。
您绝对不应该做的是选项 3,在这种情况下,您的客户不会看到错误,而 Sentry 会导致错误进入生产环境(哦,但对错误报告工具来说具有讽刺意味)。 这是当前的行为,这真的应该停止! Sentry 应该帮助我发现错误,而不是让它变得更糟。

即使您选择选项 2,至少开发人员会看到拒绝并注意到他们想要不同的行为(如崩溃),并且可以实现它。 但是在不知道甚至有拒绝的情况下,他们对此无能为力。

这应该可以完成工作。 https://github.com/getsentry/sentry-javascript/pull/2312
我没有添加添加自己的回调的方法,因为在下面编写代码会产生完全相同的效果:

```js
哨兵.init({
集成:[
新 Sentry.Integrations.OnUnhandledRejection({
模式:'无'
})
]
});

process.on('unhandledRejection', (reason) => {
// 你的回调
})

对我来说,结果是TypeError: undefined is not a constructor 。 可能是,因为我现在正在使用@sentry/react-native包。 顺便说一句,那个包有同样的问题吗?

@schermand @sentry/react-native不使用@sentry/node ,因此它没有这种集成。 为此,您只需要在我们发布哨兵/浏览器后更新一个版本,它就会正常工作(因为从处理程序返回true更改是默认的且不可配置的)。

@kamilogorek对我来说看起来不错👍

谢谢你这样做! 发布后你能在这里ping吗?

@OliverJAsh ping :)

只是为了检查我是否理解正确:如果我使用 Node 的--unhandled-rejections=strict标志,Node 会将未处理的拒绝作为异常引发,然后 Sentry 将拦截该异常并报告它? 这就是我想我看到的。

我问是因为当我尝试启用--unhandled-rejections=strict ,似乎OnUnhandledRejections集成没有效果——事件监听器从未被调用。

如果我们可以围绕此添加一些文档,那就太好了!

Docs PR 已经在进行中https://github.com/getsentry/sentry-docs/pull/1351/

@OliverJAsh此更改与 cli 标志无关。 它的行为没有受到影响。 改变的是OnUnhandledRejection集成有一个新选项,允许您使其行为类似于 cli 标志。

Sentry.init({
  integrations: [
    new Sentry.Integrations.OnUnhandledRejection({
      mode: 'none'
    })
  ]
});

(概念上)与--unhandled-rejection=none相同, warnstrict
当您使用warn (现在是默认设置)时,它会记录警告和错误本身,但该进程将保持活动状态。
当您使用strict ,它会记录、捕获事件、刷新它(等待它交付)并使用退出代码 1 终止进程。

那讲得通。 我了解集成不会改变 Node 标志的行为。 但是,我可以检查一下我是否正确理解了 Sentry 在 Node 标志方面的行为(在此集成之外)?

只是为了检查我是否理解正确:如果我使用 Node 的 --unhandled-rejections=strict 标志,Node 会将未处理的拒绝作为异常引发,然后 Sentry 将拦截该异常并报告它? 这就是我想我看到的。

@schumannd仅供参考,今天早上我们发布了@sentry/react-native 1.10.0 [编辑:哎呀,应该是1.1.0 ],它更新了它的依赖项以使用最新版本的@sentry/browser (包括返回- true -instead-of- false上面提到的修复)。

@lobsterkatie @sentry/react-native 的最新版本似乎是 1.3.7。 .

所以尝试安装 1.10.0 是行不通的。 我如何获得修复?

@schumannd @lobsterkatie意味着1.1.0 ,因为这是我们更新到5.9.0@sentry/browser 。 设置日志级别的处理程序选项在@sentry/react-native的最新版本中也应该可以正常工作。

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

相关问题

ma2gedev picture ma2gedev  ·  3评论

grigored picture grigored  ·  3评论

dimmduh picture dimmduh  ·  3评论

kamilogorek picture kamilogorek  ·  3评论

Taewa picture Taewa  ·  3评论