当我们完成一个业务系统的上线时,总是要观察线上的运行情况,查看日志发现问题并进行优化迭代。
因为测试永远无法做到100%覆盖,用户也不会总是按照我们所预期的进行操作,因此我们需要在系统异常时主动对其进行收集上报,以制定解决方案。
当生产环境中产生了一个 bug 时,如何做到迅速报警,找到问题原因,修复后又如何在线上验证?此时我们需要一个高效的错误监控系统。
前端错误监控
后端有各种强大的监控服务,给我们的应用稳定性保驾护航,然而大多数情况只能记录接口被请求之后所发生的错误。随着 Web 应用越来越强大,客户端的代码可就越来越复杂,很多不可预期错误发生在用户端,因此前端错误监控就不可或缺了。
Why Sentry
我们需要一个成熟的监控系统,Sentry 就是一个这样的工具。
Sentry – 正如其名「哨兵」,可以实时监控生产环境上的系统运行状态,一旦发生异常会第一时间把报错的路由路径、错误所在文件等详细信息以邮件形式通知我们,并且利用错误信息的堆栈跟踪快速定位到需要处理的问题。
选择 Sentry 作为前端监控系统,还因为下几点:
- 开源
- 对各种前端框架的友好支持 (Vue、React、Angular)
- 支持 SourceMap
Sentry 官方提供的免费服务有次数限制,达到一定限制后继续使用就需要收费了,但是我们可以利用 Sentry 的开源库在自己的服务器上搭建服务,官方已经提供了完善的操作文档。
准备工作
Sentry 的搭建方式主要有两种:
由于 Docker 更加方便易控,而且官方推荐的也是 Docker 部署,那下面就以 Docker 为例,在滴滴云的服务器上从零开始搭建一个 Sentry 服务,本示例中使用的服务器配置 1核CPU,2G 内存,操作系统 CentOs 7.4。
注意事项
- 服务器的内存至少 2G,否则在执行
sentry upgrade
命令时会出现问题。 - 以下所有命令在
sudo
权限下执行,否则 Docker 无法运行
安装 Docker
$ yum install docker -y
安装 Docker-Compose
$ pip install docker-compose
启动 Docker
$ systemctl start docker
开始搭建
1. 拉取 sentry-onpremise
仓库
Onpremise 是官方提供的包含了使用 Docker 部署 Sentry 所需依赖的引导程序。
$ git clone https://github.com/getsentry/onpremise.git
2. 创建服务端服务
$ cd onpremise
# 新建本地数据库和 Sentry 配置的目录
$ docker volume create --name=sentry-data && docker volume create --name=sentry-postgres
# 创建环境变量配置文件
$ cp -n .env.example .env
# 构建 Docker Services
$ docker-compose build
# 生成秘钥
$ docker-compose run --rm web config generate-secret-key
# 复制秘钥(即最后一行),编辑 .env 文件,将 SENTRY_SECRET_KEY 对应的 value 修改为刚才复制下来的秘钥
$ vim .env
# 创建数据库,生成管理员账号
$ docker-compose run --rm web upgrade
# 启动 Sentry 服务
$ docker-compose up -d
# 查看容器
$ docker-compose ps
如果执行过程中一切正常的话,在浏览器中输入http://ip:9000
就进入 Sentry 的登录页面了,使用上面创建的管理员用户名和密码登录系统。
集成客户端 SDK
经过以上步骤我们 Sentry 服务端完成了,接下来就可以在应用中集成 Sentry 客户端 SDK,用来在前端代码中实时上报错误。
登录系统后首先会进入配置页面,简单配置一下:
- Root URL: 绑定本 Sentry 服务的域名或者直接填写本服务的 IP
- Admin Email: 系统管理员的邮件地址
配置好后,进入添加新项目界面,以 Vue 应用为例,点击 New Project 创建新项目,选择 Vue。创建成功后,按照指引可以看到Sentry 推荐使用 Raven.js
及其 Vue 插件,在我们的前端项目中引入监控的模块,注意监控模块要在 Vue 之后引入。
$ npm install raven-js
// main.js
// 先引入Vue
import Vue from 'vue'
import Raven from 'raven-js'
import RavenVue from 'raven-js/plugins/vue'
Raven
.config('https://<key>@sentry.io/<project>')
.addPlugin(RavenVue, Vue)
.install()
验证
到这里整个错误监控系统算是成型了,我们可以制造点错误,来验证一下功能是否符合预期。可以直接在前端程序代码里写点 bug,触发之后可以在前端调试工具的 network 中看到上报错误的网络请求,同时在 Sentry Dashboard 会发现这个错误,查看详情可以看到这个错误的详细信息了。
// main.js
// 使用一个未声明过的变量触发错误
console.log(hello)
关联 SourceMap
目前大多数前端程序基本都会使用 Webpack 之类的工具构建,上线之后的代码都是经过压缩混淆的,所以这样的报错信息其实意义不大,错误位置根本无从定位,异常堆栈也难以理解,排查非常耗时费力,此时就要使用 SourceMap 对错误进行定位了,而 Sentry 也正对此有很好的支持。
使用 sentry-cli
查看官方文档对引入 SourceMaps 的说明,可以看到有两种方式来上传 SourceMap 文件:
- sentry-webpack-plugin: Sentry 提供的 Webpack 插件,灵活性不高
- sentry-cli: 灵活性比较高,可以针对不同项目进行单独的配置
下面以 sentry-cli 的方式为例,看一下具体流程。
1、安装 sentry-cli
$ npm install sentry-cli-binary -g
2、生成 token
在 Sentry Dashboard 页面选择 API,生成 token,注意要勾选 project:write,开启项目的写权限。
3、登录 cli
$ sentry-cli login
执行后输入上生成的 token
关于 Relese 控制
说到上传 SourceMap 就要提一下 Release。在平时开发过程中我们希望不监控开发环境下的异常,也有区分测试环境和生产环境异常的需求,此时就需要 Release 将异常进行版本控制。
- 创建 Release
$ sentry-cli releases -o 组织名称 -p 项目名称 new 版本号
组织名称、项目名称可以在 Sentry Dashboard 中查看,同时可以在 “Releases” 中查看是否创建成功。这样一来,通过生成不同的版本号,我们可以对异常进行分类。
- 本地应用 Release
回到前端项目,在 Raven config 中添加对应的 Release,并指定版本号,每次上报的异常就会被分类到该版本下。
// main.js
...
import Raven from 'raven-js';
Raven
.config(DSN, {
release: '版本号'
})
.addPlugin(RavenVue, Vue)
.install()
- 删除 Release
在删除 Release 时需要将其下的异常处理掉,并将该版本的 SourceMap 文件清空,否则会报错
$ sentry-cli releases -o 组织名称 -p 项目名称 delete 版本号
上传 SourceMap
以上概念了解之后,就可以开始上传 SourceMap 文件了。
sentry-cli releases -o 组织名称 -p 项目名称 files 版本号 upload-sourcemaps --url-prefix 线上js文件所在目录
注意:
- 通常来说
vendor.js
这种各种类库打包后生成的代码,并不是我们程序的源代码,不需要上传 -
--url-prefix
对应的是线上JS文件的目录,例如~/static/js/
,其中~/
代表网站根目录
SourceMap 上传成功后,就可以在 Sentry 上直接看到报错所在的源码了。
主动捕获错误
现在我们已经能成功监控程序中的大多数错误,但是无法捕获异步错误(比如定时器、接口请求错误)此时可以利用 raven.caputureException()
进行主动上报。
// 接口请求
axios.get('/user?ID=12345')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
window.$Raven.captureException(error);
})
// 定时器任务
setTimeout(()=>{
try {
// do something
} catch (err) {
window.$Raven.captureException(err);
}
}, 300)
邮箱设置
完成上面的所有步骤,我们的 Sentry 可以使用了,但是还有一个问题,上报错误时并没有收到邮件,作为一个自动化的异常提醒工具,没有了提醒功能,它价值就打大打折扣了。
在搭建好的 Sentry 的 Web 页面中无法配置邮箱 smtp 信息,需要修改配置文件 config.yml。
###############
# Mail Server #
###############
mail.backend: 'smtp' # Use dummy if you want to disable email entirely
mail.host: 'smpt.gmail.com' # 输入邮箱smpt服务器地址
mail.port: 25
mail.username: 'your-email-address'
mail.password: 'your-email-password'
mail.use-tls: false
然后修改 docker-compose.yml,为其中的 Web 容器添加 volumes 卷,使刚才修改的 config.yml 配置文件生效。
web:
volumes:
- ./config.yml:/etc/sentry/config.yml
....
然后重启服务
$ docker-compose up
测试一下,去触发一个错误,应该可以正常接收邮件了。
搭建过程中可能出现的错误
$ docker-compose run --rm web upgrade
在执行上面的命令过程中,可能会出现创建数据库失败、跳过了创建管理员账号步骤等错误,导致 Web页面无法使用。首先检查服务器的硬件配置,内存是否满足要求 (2G 以上),否则大概率就是数据库初始化时出错,此时可以手动初始化数据可以及创建管理员用户。
1、进入 sentry-web 的 shell
$ docker-compose run --rm web shell
2、初始化数据
$ from sentry.models import Project
$ from sentry.receivers.core import create_default_projects
$ create_default_projects([Project])
- 退出 sentry-web 的 shell,手动创建用户
$ docker-compose run --rm web createuser
刷新网页就可以使用刚刚创建好的账户登录。