Sentry 错误监控实战原创
背景
目前前端开发正朝着多元化的方向发展,场景愈发的复杂,生产环境经常会出现一些难以复现的问题,而且用户的具体行为和操作也无法预测。生产环境下的错误,更多的是依靠用户的反馈,这往往是不实时的,而且用户能提供的信息,通常也只有文字描述,或者图片,缺少一些关键的信息,此外,生产环境下的代码都是经过混淆压缩的,拿到的报错信息栈也没有实质的帮助。
所以,在当前的环境下,前端项目更加需要一套成熟的错误监控系统,能够帮助前端开发人员实时地监控项目的运行情况。当出现问题时,及时的将错误通知给开发者,并提供完整的错误相关信息,甚至直接告诉开发者,哪行代码报错了,让开发人员能快速、准确的对错误进行跟踪处理。本文主要介绍了前端项目接入 Sentry 的流程,以及遇到的一些坑,与君共享~
Why Sentry
当前比较成熟的监控平台
- Fundebug
- Bugsnag
- Sentry
- 国内大厂自研平台
Sentry 的优势
- 开源,有免费版本
- 可以将服务部署自己的服务器,更安全
- 支持多语言,社区活跃
- 上手简单,接入成本低
- 错误追踪和状态流转方便
- 错误提醒,告警机制完善
相对于其他平台来说,我们更看重 Sentry 的私有化部署能力,完全不用担心项目中的数据及敏感信息的泄漏,同时它还支持各种开发语言以及框架,从技术角度来说,更加节省了后期的维护成本。
简介
❝
Sentry is a service that helps you monitor and fix crashes in realtime. The server is in Python, but it contains a full API for sending events from any language, in any application.
❞
Sentry 是一种帮助开发者实时监控和修复崩溃的服务。服务器基于 Python,它包含了一套完整的 API,支持通过任何应用程序的任何语言发送事件。
接入
部署
官网提供了免费版本和收费版本。因为 Sentry 是开源的,所以从安全角度出发,我们自己搭建了一个 Sentry 服务器。官方提供了两种安装方式:
Python 安装
Docker 安装(推荐)
sentry pricing
具体的安装过程不在此赘述,详情可以参考官方文档。
项目接入
创建项目
1、 访问 Sentry web,创建一个新的项目,填入项目名和团队名
选择对应的开发语言模版,这里选择 React
2、创建项目成功后,会跳转到对应模版语言的详细配置步骤页面,按照页面配置即可
初始化
1、安装依赖
npm install --save @sentry/react @sentry/tracing
npm install -D @sentry/webpack-plugin or webpack-sentry-plugin
2、初始化
import * as Sentry from '@sentry/react';
import { Integrations } from '@sentry/tracing';
Sentry.init({
dsn: 'https://xxx@sentry服务器域名/项目ID',
integrations: [new Integrations.BrowserTracing()],
environment: env,
release: require('../project.json').version,
// Set tracesSampleRate to 1.0 to capture 100%
// of transactions for performance monitoring.
// We recommend adjusting this value in production
tracesSampleRate: 0.1,
// localhost 默认不上报 可以通过Inbound Data Filters 配置一些规则
allowUrls: [/.\.xxx\.cn\/.?/],
beforeSend: (e, hint) => {
// 这里可以设置一些过滤规则,return null 将不会上报
return e;
},
});
❝
DSN 为刚刚创建项目对应的客户端密钥,在项目->设置->客户端密钥(DSN)查看
❞
3、上传 sourceMap
完成上述步骤后,项目中如果出现了报错,就会被 Sentry 拦截,然后上传到 Sentry 服务器。在对应的项目下,就能看到报错的的信息描述了。但是无法看到具体是哪一个文件,哪一行报错,因为我们上传到生产环境的代码是经过混淆压缩的,所以需要通过 sourceMap 对代码进行一个还原。
Sentry 官方提供了两种上传 sourceMap 的方式:
- 使用 Sentry-cli 命令行
- webpack 插件
由于命令行方式太过繁琐,且与前端工程化概念脱离。因此采用 webpack 插件方式进行上传 sourceMap 文件。
4、安装 webpack 插件
npm i @sentry/webpack-plugin -D
项目根目录下添加 Sentry 配置文件:.sentryclirc。
@sentry/cli 上传 sourceMap 文件时会自动检查该配置文件下的信息。
[defaults]
url=sentry服务器域名
org=组织名
project=sentry创建的项目名
[auth]
token=在sentry项目设置 -> Account -> API -> Auth Tokens 下创建的token
❝
创建的 token 需要选择 project:read 和 project:releases 权限
❞
在 webpack 等打包工具中配置插件,这里以 umi 为例
config.plugin('sentryWebpackPlugin').use(SentryWebpackPlugin, [
{
release: version,
include: './dist',
ignore: ['node_modules', './dist/third/**'],
urlPrefix: '~/micros/ta/',
configFile: '.sentryclirc',
},
]);
// 也可以使用社区维护的插件
config.plugin('sentryWebpackPlugin').use(SentryWebpackPlugin, [
{
baseSentryURL: 'xxxx',
organization: 'xx',
project: 'xxx',
apiKey: 'xxx',
release: version,
// 上传结束删除 .js.map
deleteAfterCompile: true,
// 上传相同文件 报错 但不停止打包
suppressConflictError: true,
include: /(\.js\.map|\.js)$/,
exclude:
/third\/.*\.js|font_.*.js|moment-with-locales.min.js|lodash.min.js|moment-timezone-with-data.min.js/,
// filenameTransform 就是 urlPrefix
filenameTransform: function (filename) {
return '~/' + filename;
},
},
]);
上面有几个需要注意的地方:
- devtool 必须要设置成 source-map
- urlPrefix 必须跟项目实际资源路径一致
- 上传结束后,记得删除 sourceMap, 否则就发到生产环境了
- 建议只在生产环境使用 Sentry 上报,避免无用的告警信息
❝
社区维护的插件,可以配置 deleteAfterCompile,其他的可以使用一些插件辅助,比如 CleanWebpackPlugin,或者 rimraf
❞
完成上述步骤后,再次测试错误,将看到一个完美的错误还原。
扩展
Sentry 提供了很多插件集成,除了邮件通知,还可以接入飞书、钉钉、企业微信等常用办公软件,其中用到的核心其实就是:webhook。
在项目 -> setting -> Legacy Integrations -> 选择 WebHooks -> 打开开关 -> Configure plugin, 在 Callback URLs 中写入对应的 hook, 就能完成一个消息通知。
❝
Callback URLs 可以填入多个 URL,以 \n 分割
❞
下面以飞书为例,说下配置飞书通知的步骤
1、创建飞书群聊
2、配置机器人,复制下 webhook 地址
创建飞书捷径,设置触发器,选择 Sentry,此时会生成一个 webhook 地址,复制,粘贴到 Callback URLs 中
配置飞书捷径中的 webhook, 选择应用 Sentry,选择操作,设置选项
请求方式设置为 POST
URL 输入飞书群中添加的机器人生成的 webhook
数据类型选择 application/json
数据格式可以参考如下
// 【】中是飞书提供的变量
{
"msg_type": "post",
"content": {
"post": {
"zh_cn": {
"title": "【Sentry项目】BUG提醒",
"content": [
[
{
"tag": "text",
"un_escape": true,
"text": "【Sentry事件环境】【Sentry事件标题】 "
},
{
"tag": "a",
"text": "请查看",
"href": " 【错误地址】"
}
]
]
}
}
}
}
上述配置完成,点击 Sentry Webhooks 下的 Test Plugin 按钮,飞书群将会收入一个通知
踩坑记录
urlPrefix
❝
在上传 sourceMap 文件时,不同项目打包后以及最终上传的静态资源文件路径可能有所区别。因此需要配置这个 urlPrefix 选项。该选项的意思是指项目上线后生产环境下对应的资源文件的完整路径。其中 ~ 表示网站根目录
❞
比如:
当前站点域名:https://ta.thinkingdata.cn/,~就是这个域名
静态资源路径为:https://ta.thinkingdata.cn/micros/x.{js,css,png}
而我们当前的前端项目采了微前端架构,分了三个子项目(portal,ta,va),所以每个子项目都会有自己的静态资源路径。
- portal: micros/x.js
- ta: micros/ta/x.js
- va: micros/va/x.js
所以上面三个项目,在配置 urlPrefix 时,必须是
- portal: ~/
- ta: ~/micros/ta/
- va: ~/micros/va/
否则,sourceMap 无法正常还原错误,同时需要注意的是,项目打包时,devtool 必须设置成 source-map,否则也无法正常还原错误,完美的错误还原如下图所示:
收到很多没有意义的报错
可以在当前项目的设置中,设置 Inbound Data Filters,对不想要的错误类型进行过滤
浅谈原理(仅 JS)
常见错误类型
运行时错误:代码报错
资源加载错误:js, css, 图片加载 404 等
异常捕获
- window.onerror
- window.addEventListener(‘event’)
- event = ‘error’, 捕获资源加载失败
- event = ‘unhandledrejection’, 捕获 Promise 错误
- try…catch
- Vue…config.errorHandler
- React.ErrorBoundary
Sentry 实现
上报方式
当前主流的错误上报的方式:
1、利用网络请求(xhr, fetch)上报
2、利用图片请求方式上报,比如 (new Image()).src = 'docs.sentry.io/error?error
Sentry 则采用的是第一种上报方式,并且在初始化的时候会判断当前环境是否支持 fetch,不支持的话,会有降级处理。
class BaseBackend {
constructor(options) {
// ...
this._transport = this._setupTransport();
}
_setupTransport() {
// ...
if (supportsFetch()) {
return new FetchTransport(transportOptions);
}
return new XHRTransport(transportOptions);
}
}
supportsFetch 方法其实也很简单,具体可以查看 Sentry 源码,这里就不做展开。
上报流程
- Sentry Init 初始化,读取配置的 Release 和 DSN 信息,然后将 Sentry 对象挂在到全局
- 当代码在运行过程中发生错误时,往上抛出一个 Error 对象,会执行 TraceKit 重写的 window.onerror 方法
- 如果是一个未捕获的 Promise 错误是,将执行重写的 window.onunhandledrejection
- 然后使用网络请求上报到 Sentry 服务器,这里会用到配置时使用的 DNS
总结
本文主要记述了 Sentry 的安装过程,以及遇到的一些坑,希望对这方面有需求的同学有所帮助,欢迎感兴趣的朋友一起交流。
参考文章
Sentry:https://docs.sentry.io
Sentry 源码解析:https://juejin.cn/post/6918549102301020173
我们是数数科技前端团队,目前负责游戏行业使用最多的用户行为分析系统的前端研发,同时也在积极探索前端新技术和新领域,如果你对游戏、大数据、可视化、工程化、全栈等方面有兴趣,欢迎加入我们,共创未来!
图片图片图片图片图片图片图片图片图片图片图片图片图片图片图片图片
邮箱:young@thinkingdata.cn
最后打个广告,欢迎关注我司产品体验设计团队,这里有一群有趣、敢于表达态度来传递产品温度的 TDers 等你来发现。