错误监控
现在能捕捉的错误有三种。
- 资源加载错误,通过
addEventListener('error', callback, true)
在捕获阶段捕捉资源加载失败错误。 - js 执行错误,通过
window.onerror
捕捉 js 错误。 - promise 错误,通过
addEventListener('unhandledrejection', callback)
捕捉 promise 错误,但是没有发生错误的行数,列数等信息,只能手动抛出相关错误信息。
我们可以建一个错误数组变量 errors
在错误发生时,将错误的相关信息添加到数组,然后在某个阶段统一上报,具体如何操作请看代码
// 捕获资源加载失败错误 js css img... addEventListener('error', e => { const target = e.target if (target != window) { monitor.errors.push({ type: target.localName, url: target.src || target.href, msg: (target.src || target.href) + ' is load error', // 错误发生的时间 time: new Date().getTime(), }) } }, true) // 监听 js 错误 window.onerror = function(msg, url, row, col, error) { monitor.errors.push({ type: 'javascript', row: row, col: col, msg: error && error.stack? error.stack : msg, url: url, // 错误发生的时间 time: new Date().getTime(), }) } // 监听 promise 错误 缺点是获取不到行数数据 addEventListener('unhandledrejection', e => { monitor.errors.push({ type: 'promise', msg: (e.reason && e.reason.msg) || e.reason || '', // 错误发生的时间 time: new Date().getTime(), }) })
小结
通过错误收集,可以了解到网站错误发生的类型及数量,从而可以做相应的调整,以减少错误发生。
完整代码和 DEMO 请看我另一篇文章前端性能和错误监控的末尾,大家可以复制代码(HTML文件)在本地测试一下。
数据上报
性能数据上报
性能数据可以在页面加载完之后上报,尽量不要对页面性能造成影响。
window.onload = () => { // 在浏览器空闲时间获取性能及资源信息 // https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestIdleCallback if (window.requestIdleCallback) { window.requestIdleCallback(() => { monitor.performance = getPerformance() monitor.resources = getResources() }) } else { setTimeout(() => { monitor.performance = getPerformance() monitor.resources = getResources() }, 0) } }
当然,你也可以设一个定时器,循环上报。不过每次上报最好做一下对比去重再上报,避免同样的数据重复上报。
错误数据上报
我在DEMO里提供的代码,是用一个 errors
数组收集所有的错误,再在某一阶段统一上报(延时上报)。
其实,也可以改成在错误发生时上报(即时上报)。这样可以避免在收集完错误延时上报还没触发,用户却已经关掉网页导致错误数据丢失的问题。
// 监听 js 错误 window.onerror = function(msg, url, row, col, error) { const data = { type: 'javascript', row: row, col: col, msg: error && error.stack? error.stack : msg, url: url, // 错误发生的时间 time: new Date().getTime(), } // 即时上报 axios.post({ url: 'xxx', data, }) }
SPA
window.performance
API 是有缺点的,在 SPA 切换路由时,window.performance.timing
的数据不会更新。
所以我们需要另想办法来统计切换路由到加载完成的时间。
拿 Vue 举例,一个可行的办法就是切换路由时,在路由的全局前置守卫 beforeEach
里获取开始时间,在组件的 mounted
钩子里执行 vm.$nextTick
函数来获取组件的渲染完毕时间。
router.beforeEach((to, from, next) => { store.commit('setPageLoadedStartTime', new Date()) })
mounted() { this.$nextTick(() => { this.$store.commit('setPageLoadedTime', new Date() - this.$store.state.pageLoadedStartTime) }) }
除了性能和错误监控,其实我们还可以做得更多。
用户信息收集
navigator
使用 window.navigator
可以收集到用户的设备信息,操作系统,浏览器信息...
UV(Unique visitor)
是指通过互联网访问、浏览这个网页的自然人。访问您网站的一台电脑客户端为一个访客。00:00-24:00内相同的客户端只被计算一次。一天内同个访客多次访问仅计算一个UV。
在用户访问网站时,可以生成一个随机字符串+时间日期,保存在本地。在网页发生请求时(如果超过当天24小时,则重新生成),把这些参数传到后端,后端利用这些信息生成 UV 统计报告。
PV(Page View)
即页面浏览量或点击量,用户每1次对网站中的每个网页访问均被记录1个PV。用户对同一页面的多次访问,访问量累计,用以衡量网站用户访问的网页数量。
页面停留时间
传统网站
用户在进入 A 页面时,通过后台请求把用户进入页面的时间捎上。过了 10 分钟,用户进入 B 页面,这时后台可以通过接口捎带的参数可以判断出用户在 A 页面停留了 10 分钟。
SPA
可以利用 router 来获取用户停留时间,拿 Vue 举例,通过 router.beforeEach
destroyed
这两个钩子函数来获取用户停留该路由组件的时间。
浏览深度
通过 document.documentElement.scrollTop
属性以及屏幕高度,可以判断用户是否浏览完网站内容。
页面跳转来源
通过 document.referrer
属性,可以知道用户是从哪个网站跳转而来。
小结
通过分析用户数据,我们可以了解到用户的浏览习惯、爱好等等信息,想想真是恐怖,毫无隐私可言。
前端监控部署教程
前面说的都是监控原理,但要实现还是得自己动手写代码。为了避免麻烦,我们可以用现有的工具 sentry 去做这件事。
sentry 是一个用 python 写的性能和错误监控工具,你可以使用 sentry 提供的服务(免费功能少),也可以自己部署服务。现在来看一下如何使用 sentry 提供的服务实现监控。
注册账号
打开 https://sentry.io/signup/
网站,进行注册。
选择项目,我选的 Vue。
安装 sentry 依赖
选完项目,下面会有具体的 sentry 依赖安装指南。
根据提示,在你的 Vue 项目执行这段代码 npm install --save @sentry/browser @sentry/integrations @sentry/tracing
,安装 sentry 所需的依赖。
再将下面的代码拷到你的 main.js
,放在 new Vue()
之前。
import * as Sentry from "@sentry/browser"; import { Vue as VueIntegration } from "@sentry/integrations"; import { Integrations } from "@sentry/tracing"; Sentry.init({ dsn: "xxxxx", // 这里是你的 dsn 地址,注册完就有 integrations: [ new VueIntegration({ Vue, tracing: true, }), new Integrations.BrowserTracing(), ], // We recommend adjusting this value in production, or using tracesSampler // for finer control tracesSampleRate: 1.0, });
然后点击第一步中的 skip this onboarding
,进入控制台页面。
如果忘了自己的 DSN,请点击左边的菜单栏选择 Settings
-> Projects
-> 点击自己的项目 -> Client Keys(DSN)
。
创建第一个错误
在你的 Vue 项目执行一个打印语句 console.log(b)
。
这时点开 sentry 主页的 issues 一项,可以发现有一个报错信息 b is not defined
:
这个报错信息包含了错误的具体信息,还有你的 IP、浏览器信息等等。
但奇怪的是,我们的浏览器控制台并没有输出报错信息。
这是因为被 sentry 屏蔽了,所以我们需要加上一个选项 logErrors: true
。
然后再查看页面,发现控制台也有报错信息了:
上传 sourcemap
一般打包后的代码都是经过压缩的,如果没有 sourcemap,即使有报错信息,你也很难根据提示找到对应的源码在哪。
下面来看一下如何上传 sourcemap。
首先创建 auth token。
这个生成的 token 一会要用到。
安装 sentry-cli
和 @sentry/webpack-plugin
:
npm install sentry-cli-binary -g
npm install --save-dev @sentry/webpack-plugin
安装完上面两个插件后,在项目根目录创建一个 .sentryclirc
文件(不要忘了在 .gitignore
把这个文件添加上,以免暴露 token),内容如下:
[auth]
token=xxx
[defaults]
url=https://sentry.io/ org=woai3c project=woai3c
把 xxx 替换成刚才生成的 token。
org
是你的组织名称。
project
是你的项目名称,根据下面的提示可以找到。
在项目下新建 vue.config.js
文件,把下面的内容填进去:
const SentryWebpackPlugin = require('@sentry/webpack-plugin') const config = { configureWebpack: { plugins: [ new SentryWebpackPlugin({ include: './dist', // 打包后的目录 ignore: ['node_modules', 'vue.config.js', 'babel.config.js'], }), ], }, } // 只在生产环境下上传 sourcemap module.exports = process.env.NODE_ENV == 'production'? config : {}
填完以后,执行 npm run build
,就可以看到 sourcemap
的上传结果了。
我们再来看一下没上传 sourcemap 和上传之后的报错信息对比。
未上传 sourcemap
已上传 sourcemap
可以看到,上传 sourcemap 后的报错信息更加准确。
切换中文环境和时区
选完刷新即可。
性能监控
打开 performance 选项,就能看到你每个项目的运行情况。具体的参数解释请看文档 Performance Monitoring。