这篇文章也发在我的博客,欢迎围观?
前端错误监控的必要性
前端监控是相对于后端监控而言的,以往的后端监控只能记录接口被请求之后所发生的错误,但是现在随着用户复杂度不断提高,很多错误是在后端接口被请求以前就发生的JS错误。对于这种错误,传统的后端错误监控是无能为力的,因此前端监控应运而生。
为什么选择Sentry
最近想在公司的项目上部署前端监控,以主动捕捉用户端的错误(满足我们自己的控制欲?),不再等待用户的反馈(事实上用户的反馈少之又少?)。本来考虑自己写一个,奈何水平有限,加之看到已经有不少成熟的方案,所以...你们懂得!目前较为成熟的前端监控方案,具体参考知乎帖子 如何做前端异常监控 ,大致上可供选择的方案如下:
- Sentry
- Fundebug
- Bugsnag
- BetterJS
因为是想部署到公司自己的服务器上,上述貌似只有Sentry和BetterJS有提供自行部署的方案。然后考虑到目前前端项目都是压缩打包后再发布的,能够结合sourcemaps对错误进行定位是非常有必要的,因此从两者中选择了Sentry。(上述方案中,Fundebug也是支持sourcemaps的)
如何使用
作为试用,我选择使用Sentry提供的服务,注册账号之后,选择使用免费版的服务,就可以开始在前端对我们的项目加入前端错误监控了。
初步引入错误监控模块
首先,当然是要看Sentry提供的docs了,创建项目的步骤可以说是介绍得非常清楚了。
- Create a new project
- Choose a language or framework
- Then follow the docs
OK,创建一个新项目之后,就应该到我的前端项目中引入监控的模块了。此处,因为我的项目是使用Vue的SPA项目,所以使用的是Sentry官方推荐的Raven.js。下载该模块之后,按照官方指引在项目中引入文件,其中key和project在你注册账号之后官方都会自动帮你生成的,非常贴心。
// main.js
import Raven from 'raven-js'
import RavenVue from 'raven-js/plugins/vue'
Raven
.config('https://<key>@sentry.io/<project>')
.addPlugin(RavenVue, Vue)
.install()
复制代码
这时候,你只要在你项目中的一个不起眼的角落埋下一个错误,然后去触发一下,就能看到调用了raven.js发送错误,并且在你的sentry项目中也能看到相关报错了。
但是这样的报错信息其实意义不大,除了能够看到具体的error原因外,错误位置根本无从定位。如果错误信息没有具体到我们足以马上定位出错误位置,那么我们只能一遍遍地对代码进行排查,耗时耗力。这时候就要使用sourcemaps对错误进行定位了,所幸Sentry对此的支持也是非常好的。
引入sourcemaps对错误进行定位
首先,我们来看官方文档对引入sourcemaps的说明。Sentry允许我们通过三种方式使用sourcemaps,releases API, sentry-cli, sentry-webpack-plugin。考虑到项目构建过程中就一并提交sourcemaps,我个人仅仅实践了最后一种。
// 来自官方的步骤
1. Start by creating a new authentication token under **[Account] > API**.
2. Ensure you have project:write selected under scopes.
3. Install @sentry/webpack-plugin using npm
4. Create .sentryclirc file with necessary config (see Sentry Webpack Plugin docs below)
5. Update your webpack.config.json
复制代码
下面我们来实践一下:
前两步,非常简单,略过略过。
对于第三步, 我个人发现仅仅install @sentry/webpack-plugin似乎还是不够的,还需要install @sentry/cli,不知道是我个人问题,敬请指出。
第四步,可能是我对这种配置格式不太熟悉,折腾最久就是这一步了。给出官方配置说明,下面是我的基础配置(其实就是将如defaults.url,auth.token等拆分,写入配置文件即可),在项目根目录下:
// .sentryclirc
[defaults]
url = https://sentry.io/
<!-- 此处可以从你的sentry.io的全路径看出,如:https://sentry.io/此处是你的org/此处是你的project/ -->
org =
project =
[auth]
token = 此处就是你第一步第二步创建的token
复制代码
第五步,更新我的webpack配置,配置我的监控模块
此处参考了一位兄弟的文章,他用了另外一个Plugin实现,请围观。
此处声明,这位大兄弟文中的方法都很好,但是对于需要截取7位数的参数作为版本号的问题,我个人实践是截取后Raven.js无法传递正确版本号了,反而不截取能够传递正确版本号!通过查看Raven.js的post,发现release非常长,不止7位。
下面这个步骤的urlPrefix属性特别关键,这个是给上传的文件添加前缀的,因为sentry除了根据版本号,还要匹配特定版本号中的路径。这个属性的默认值是~/,而~是仅仅是去掉了网址前面的协议和域名,即拼接起来的map资源路径是http://host/static/js/app.js.map。但是如果像我的站点,多了一级路径,那么就需要配置这个属性了,否则上传到Sentry的maps就无法访问到了。
// webpack.prod.conf.js
const SentryPlugin = require('@sentry/webpack-plugin');
plugins: [
new SentryPlugin({
include: './dist',
release: process.env.RELEASE_VERSION,
configFile: 'sentry.properties',
urlPrefix: '~/TechPage/', // 如果不需要也可以不传这个参
}),
]
复制代码
就是下面这个步骤,不应该截取
// prod.env.js
let gitSha = require('child_process').execSync('git rev-parse HEAD').toString().trim()
// process.env.RELEASE_VERSION = gitSha.substr(2, 9) 不应截取
process.env.RELEASE_VERSION = gitSha
module.exports = {
RELEASE_VERSION: `"${gitSha}"`,
NODE_ENV: '"production"'
}
复制代码
// main.js
import Raven from 'raven-js'
import RavenVue from 'raven-js/plugins/vue'
Raven
.config('https://<key>@sentry.io/<project>', {
release: process.env.RELEASE_VERSION,
debug: true
})
.addPlugin(RavenVue, Vue)
.install()
复制代码
由于sentry是通过release版本匹配一致的方式,使用特定版本的sourcemaps去定位错误的,因此我们需要在项目构建的时候生成一个特定的版本号,将这个版本号赋值给Raven.js以及SentryPlugin,这样就能形成对应关系。当然你也可以通过另外的方式生成相应的release版本号,这都是可以的,只要是能够唯一对应就可以了。即使你没有形成对应关系,但只要你发布了source maps,Sentry默认是会根据js文件中的sourceMappingURL去找到map文件,然后定位错误的。
Raven.js的配置官方文档也是有详细说明的,详情点击。此处我仅仅配置了release以及debug,设置debug为true的话,Raven.js会将一些debug信息输出到控制台。
至此,你触发项目中的错误,就能获得下面的效果,错误信息也成功定位了,非常的清楚。
使用webpack-sentry-plugin上传sourcemaps,并且在上传后删除,不让终端用户接触maps文件
此处是参照上面提到那位兄弟的做法了,不过他提到了要在上传sourcemaps到sentry之后删除,不让终端用户接触,这是对的 。但是我这边删除之后,用户端发生错误之后,没有办法把定位后的错误信息返回了,返回的错误信息都是编译打包之后的代码的,可读性一般。如果发现我哪里配置错误,敬请指出。
经过踩坑,正是由于没有正确配置urlPrefix(@sentry/webpack-plugin中的属性,webpack-sentry-plugin中是配置filenameTransform方法),所以当我不发布source maps时,无法定位错误。
const SentryPlugin = require('webpack-sentry-plugin');
plugins: [
new SentryPlugin(Object.assign({
release: process.env.RELEASE_VERSION,
deleteAfterCompile: true,
suppressErrors: true,
/** filenameTransform: function (filename) {
var pub = config.build.assetsPublicPath
if (/^\/\//.test(pub)) pub = 'http:' + pub
var urlObj = require('url').parse(pub)
return '~' + urlObj.pathname.replace(/\/+$/, '') + '/' + filename
} */
filenameTransform: function (filename) {
return '~/TechPage/' + filename
},
}, require('../sentry.conf.js'))),
]
复制代码
// sentry.conf.js(位于根目录)
module.exports = {
// 此处可以从你的sentry.io的全路径看出,如:https://sentry.io/此处是你的org/此处是你的project
organisation: '',
project: '',
apiKey: ''
}
复制代码
这种配置,我得到的报错信息是这样的,如下图:
经过配置,能够在不发布source maps的情况下,得到完整的错误定位!
最后的最后
如果想将Sentry部署到自己的服务器,Sentry官方也有详细的文档,各位请移步。
各位看官如果发现文中我的错处,请一定不要嫌麻烦,请开展花式吐槽!谢谢!
也欢迎上我的Demo网站点击创建时间一栏触发错误看看Sentry的效果,谢谢!
此处感谢 @时间是海 的提醒,参考了sentry社区里的问题才注意到urlPrefix这个问题。附上社区的两篇帖子: