Vue SPA项目 + Sentry 实现前端错误监控

10,366 阅读6分钟

这篇文章也发在我的博客,欢迎围观😄

前端错误监控的必要性

前端监控是相对于后端监控而言的,以往的后端监控只能记录接口被请求之后所发生的错误,但是现在随着用户复杂度不断提高,很多错误是在后端接口被请求以前就发生的JS错误。对于这种错误,传统的后端错误监控是无能为力的,因此前端监控应运而生。

为什么选择Sentry

最近想在公司的项目上部署前端监控,以主动捕捉用户端的错误(满足我们自己的控制欲😄),不再等待用户的反馈(事实上用户的反馈少之又少😓)。本来考虑自己写一个,奈何水平有限,加之看到已经有不少成熟的方案,所以...你们懂得!目前较为成熟的前端监控方案,具体参考知乎帖子 如何做前端异常监控 ,大致上可供选择的方案如下:

  1. Sentry
  2. Fundebug
  3. Bugsnag
  4. BetterJS

因为是想部署到公司自己的服务器上,上述貌似只有Sentry和BetterJS有提供自行部署的方案。然后考虑到目前前端项目都是压缩打包后再发布的,能够结合sourcemaps对错误进行定位是非常有必要的,因此从两者中选择了Sentry。(上述方案中,Fundebug也是支持sourcemaps的)

如何使用

作为试用,我选择使用Sentry提供的服务,注册账号之后,选择使用免费版的服务,就可以开始在前端对我们的项目加入前端错误监控了。

初步引入错误监控模块

首先,当然是要看Sentry提供的docs了,创建项目的步骤可以说是介绍得非常清楚了。

  1. Create a new project
  2. Choose a language or framework
  3. 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项目中也能看到相关报错了。

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信息输出到控制台。

至此,你触发项目中的错误,就能获得下面的效果,错误信息也成功定位了,非常的清楚。

控制台信息
Sentry信息

使用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这个问题。附上社区的两篇帖子:

  1. JS source maps issue
  2. Doesn’t find uploaded source map file (with tilde ~)