sentry 入门配置简介
工作中踩的坑,很深很深的坑,记录一下
1. sentry 是什么
sentry 简单来说就是一个监控代码错误的平台, 当我们把 sentry 接入到我们的项目中后, 用户使用我们的产品, 当遇到一些 bug, 就会上传到 sentry 监控平台上, 方便我们排查错误, 提升系统稳定性。
sentry 捕获错误的原理其实很简单, 有兴趣的可以去看看源码, 这里粗略的说一下, js 中有很多错误, 对应的不同的错误捕获方法, sentry 就是利用 js 提供的全局捕获错误函数捕获到错误以后上传到 sentry 平台。
sentry 支持很多平台, 官方文档在这里, 这里我们以 javascript 为例
onerror 和 error 事件
window.onerror
是一个全局变量, 当有 js 运行时触发错误, window 就会触发error
事件, 并执行window.onerror()
error
事件监听 js 运行时错误事件, 会比window.onerror
先触发, 可以 全局捕获资源加载异常 的错误
unhandledrejection 事件
当 Promise 被 reject 且没有 reject 处理器的时候,会触发 unhandledrejection 事件。
如上图所示, 错误详情页, TAGS 部分, mechanism 代表的就是捕获错误所使用的方法
2. sentry 配置
查阅官方文档, 发现有这么一段代码,
import * as Sentry from "@sentry/browser";
import { BrowserTracing } from "@sentry/tracing";
Sentry.init({
dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
// Alternatively, use `process.env.npm_package_version` for a dynamic release version
// if your build tool supports it.
release: "my-project-name@2.3.12",
integrations: [new BrowserTracing()],
// Set tracesSampleRate to 1.0 to capture 100%
// of transactions for performance monitoring.
// We recommend adjusting this value in production
tracesSampleRate: 1.0,
});
以上配置就可以做到简单的上报错误。
1. dns
当我们在 sentry 平台创建一个新项目后, 它会自动生成一个 dsn, 将生成的 dsn 复制以后, 放在配置项上。
dsn 告诉 SDK 将事件发送到哪里。 如果未提供此值,SDK 将尝试从 SENTRY_DSN 环境变量中读取它。 如果该变量也不存在,SDK 将不会发送任何事件。
2. release
部署到对应环境中的代码版本, 当你向 Sentry 提供有关你的版本的信息时,你可以:
确定在哪个版本中引入的问题和回归
预测哪个提交导致了问题以及谁可能负责
通过在提交消息中包含问题编号来解决问题
部署代码时接收电子邮件通知
该配置项还和 sourcemap 相关,必须和 @sentry/webpack-plugin webpack 插件中的配置项一致, 否则 sourcemap 对应不上,不能解析出报错代码的具体位置
3. tracesSampleRate & sampleRate
采样率, 设置为 1 时, 所有的错误都会上报, 这里我做了一个简单的 demo 去验证这个配置项, 当设置为 0.2 时,上报十个相同的错误,sentry上可能只上报两个, 十个不同的错误也是只可能上报两个, 可根据自己的需求做配置
4. integrations
集成器,这里可以对 SDK 的集成器做一些修改, 我也不太明白集成器代表什么, 我理解的就是 SDK 支持一些可配置的功能, 利用该配置可实现功能的增加、删除、修改。 在项目中接入 sentry 时,在这就踩了个坑,陷了好几天, 不过最后终于爬出来了, 一会儿介绍。😏
5. beforeSend
这是一个函数, 错误在上报前会执行此函数, 可以在此做一些错误忽略, 或者在错误对象上绑定一些信息, 比如环境、用户信息等
6. ignoreErrors
在官网中还看到了这个配置, 我们是 Electron 项目, 验证此配置项不生效(不知道它是不是跟 Electron 项目有关), 他的原本作用是忽略某些错误上报。
我们项目中用到的配置基本就这些, 详细的配置请参考官方文档
3. 上传 sourcemap
以上配置完成以后, 确实可以上报错误了,但是此时上报的错误并不能展示具体的报错位置, 因为我们上传到正式环境的代码是经过压缩的,所以要想显示压缩前的代码, 我们需要使用 sourcemap 来解析。
webpack 打包要想生成 sourcemap, 需要配置 devtools: xxx, 它支持好几种生成 sourcemap 的方式, 但要使用生产环境可以使用的, 否则容易造成代码泄漏。 这里我们使用的是 hidden-source-map
, 可以参考这里。
sourcemap 生成以后,就要考虑如何上传到 sentry 服务器上了, 这里我们采用 webpack plugin 的方式完成上传。
该 webpack plugin 是 @sentry/webpack-plugin, 使用方式也很简单
const sentryWebpackPlugin = require('@sentry/webpack-plugin');
// webpack plugin 配置项
plugins: [
new sentryWebpackPlugin({
// 配置项
release: xxx, // 指定发布版本, 与 sentry.init 中的 release 保持一直
url: 'xxx', // sentry 服务器地址
authToken: 'xxx', // 用户 token, 网上有很多教程可以拿到该 token
org: 'xxx', // 项目所属组织, Settings 中可以查到
project: 'xxx', // 项目名称
ignore: ['node_modules', 'webpack.config.js'], // 忽略上传的目录 比如 node_modules
include: './dist', // 执行上传目录
urlPrefix: '~/', // 此配置项要注意, 添加到所有文件名开头的 URL 前缀, 默认 ~/
})
]
sourcemap 上传成功以后,可在这里查看
4. 踩过的一些坑
1. sourcemap 成功上传,但是仍不能解析出原始代码。
这里有三点需要注意。
1.1 @sentry/webpack-plugin 插件配置项中的 urlPrefix, 它必须指向你打包后生成文件的目录, 假如你打包后的文件存储在子文件夹中,需用这样配置 ~/static/js
。
1.2 sentry.init 中配置的 release 和 @sentry/webpack-plugin 中配置的 release 必须一致。
1.3 如果你也是 Electron 项目, 可能也会遇到此问题。 就是修改集成器, 也就是上边说踩大坑的问题.
参考这里解决的
initForRenderer({
integrations: (integrations) => {
// 指定集成器
return [
// 重写 frame, 用于匹配 sourcemap
// https://forum.sentry.io/t/source-code-was-not-found-when-sourcemaps-available/7685
new RewriteFramesIntegration({
iteratee: (frame) => {
if (frame.filename) {
frame.filename = frame.filename
.replace(/^file\:\/\//, '')
.replace(/^address at /, '')
.replace(/^.*\/[^\.]+(\.app|CodePush|.*(?=\/))/, '');
// Add additional regex here to fix the `at` issue
const appPrefix = 'app:// + 打包后的生成的文件目录'; // eg. app:///src/static/js
// We always want to have a tripple slash
frame.filename =
frame.filename.indexOf('/') === 0
? `${appPrefix}${frame.filename}`
: `${appPrefix}/${frame.filename}`;
}
return frame;
},
}),
];
},
});
如果问题详情里多出一个选项, 如下图, 那代表原始代码被成功的解析了出来, 那么就可以愉快的定位到出错的代码位置, 然后去把这个问题 kill 掉。
2. 忽略某些错误上报
按照官网提供的配置项, 忽略错误上报直接在配置项中加入 ignoreErrors 就可以了, 但是实际并没有生效,所以最后的解决方法, 是在 beforeSend 函数里去匹配错误, 如果匹配到,则返回 null, 代表不上报。代码如下
beforeSend(event, hint) {
const error = hint.originalException;
if (error && error.message) {
// ignoreErrors 为数组, 其中的每一项为忽略错误的错误信息, 正则类型
if (ignoreErrors.some((errorItem) => error.message.match(errorItem))) {
return null;
}
}
if (event.exception && event.exception.values && event.exception.values[0]) {
// event.exception.values[0].value // error message
const errorMsg = event.exception.values[0].value || '';
if (ignoreErrors.some((errorItem) => errorMsg.match(errorItem))) {
return null;
}
}
}