前端监控之单页应用的监控
原文链接:https://www.cnblogs.com/yalong/p/15954026.html
背景
公司有大量单页应用的项目,有的年代久远,想把一些不用的项目下掉,或者把使用频率很低的工具进行功能重组,避免占用大量人力去维护众多项目;
但是不知道这些系统是否有人,以及有多少人在用,以及哪些页面使用频率最高;
所以需要统计项目的pv,uv
需求分析
- 由于单页应用的路由跟后端没有交互,所以后端无法统计到具体的pv,最多统计个uv,所以前端实现比较合适
- 由于系统很多,而且vue, react都有,关键版本也不一致,所以设计方案要兼容多框架,多版本
- 还是由于系统太多,所以要尽量减少对现有代码的侵入,降低接入成本
- 既然要实现统计pv,uv,那么能不能统计多一点的数据,比如首屏加载耗时,异常信息等
- 那么就搞一套适合当下的前端监控吧
监控系统数据采集功能拆解
前端监控的核心是采集相关数据,然后才上报,这里只说数据的采集
一.采集首屏加载相关数据
首屏加载性能指标如下:
- DNS 解析耗时: domainLookupEnd - domainLookupStart
- TCP 连接耗时: connectEnd - connectStart
- SSL 安全连接耗时: connectEnd - secureConnectionStart
- 网络请求耗时 (TTFB): responseStart - requestStart
- 数据传输耗时: responseEnd - responseStart
- DOM 解析耗时: domInteractive - responseEnd
- 资源加载耗时: loadEventStart - domContentLoadedEventEnd
- First Byte时间: responseStart - domainLookupStart
- 白屏时间: responseEnd - fetchStart
- 首次可交互时间: domInteractive - fetchStart
- DOM Ready 时间: domContentLoadEventEnd - fetchStart
- 页面完全加载时间: loadEventStart - fetchStart
- http 头部大小: transferSize - encodedBodySize
- 重定向次数:performance.navigation.redirectCount
- 重定向耗时: redirectEnd - redirectStart
这么多指标我们选择三个指标,代码如下:
let fn = () => {
let timing = performance.getEntriesByType('navigation')[0]
let TTI = timing.domInteractive - timing.fetchStart; // 首次可交互时间
let L = timing.loadEventStart - timing.fetchStart; // 页面完全加载时间
let FP = timing.responseEnd - timing.fetchStart; // 白屏时间
let obj = {
TTI, L, FP
}
console.log(obj)
}
window.addEventListener('load', fn)
其他指标获取方式相同
二.采集pv uv
问题分析
采集PV UV的核心是监听浏览器路由变化,这就不得不提到vue-router 跟react-router了
在每个项目中是可以专门写各自的router监听,但是成本太高,代码侵入太强,而且不同框架,不同版本写法还不一样
所以就不准备使用框架本身的router,用原生js写一套统一路由监听多好,这样就不得不先了解下vue-router 跟react-router的实现原理了
vue-router react-router实现原理
这俩的实现原理网上有很详细的说明,有需要可自行百度,这里就简单说下:
以vue-router为例,router有两种模式: hash模式 和 history模式
hash模式就是改变hash,通过 hashchange 可以监听hash改变
history模式就是正常的url地址,通过history.replaceState history.pushState 实现
MDN上说 replaceState 和 pushState 会触发 popState,但是亲测,触发不了,要想监听这俩方法,只能监听方法本身,也就是 window.addEventListener('replaceState', historyFn);
同时不管哪种模式下都可以通过 history.go/back/forward 以及浏览器的前进后退来改变浏览器的url
浏览器前进后退操作、history.go/back/forward调用、hashchange的时候触发popState
总结
要想监听浏览器的url变化,只需监听如下
window.addEventListener('hashchange', hashFn);
window.addEventListener('replaceState', historyFn);
window.addEventListener('pushState', historyFn);
window.addEventListener('popstate', historyFn);
这样就囊括了 hash变化,replaceState,pushState, 浏览器前进后退,history.go/back/forward;
但是hash改变的时候,会同时触发 hashChange 和 popState,
可以通过一个公共变量,hashFn、historyFn 每次执行都给这个变量temp赋值,在触发数据上报的时候,判断下,如果temp变量的值 跟现在的url地址是一样的,就代表已经上报了该url的数据了,阻止当前上报行为即可
代码如下:
let tempUrl = ''
let historyFn = (e) => {
let href = e.currentTarget.location.href
if (tempUrl === href) { // 已经触发过了
return false
} else { // 没有触发过,就赋值
tempUrl = href
}
console.log('history change')
}
let hashFn = (e) => {
let href = e.newURL
if (tempUrl === href) {
return false
} else {
tempUrl = href
}
console.log('hash change')
}
window.addEventListener('hashchange', hashFn);
window.addEventListener('replaceState', historyFn);
window.addEventListener('pushState', historyFn);
window.addEventListener('popstate', historyFn);
三.采集异常报错
异常数据捕获其实还是挺复杂的,因为上线以后,代码都是编译压缩后的代码,而且vue、react 框架本身也会对异常进行捕获并进行console.error
调研过sentry,这个工具挺好的,但是接入成本比较高,结合当前状态,那么就退而求其次,监听console.error不就行了吗,不要求看解压后的详细代码,就看个异常输出就行,对于啥内存泄漏,页面崩溃,这些就不需要了。
具体代码如下:
const monitorErrorInitFn = () => {
/**
* console.error 打印的错误,就是要处理上报的信息
*/
const oldErrorLog = console.error;
console.error = null // 这一步是避免重复添加
console.error = function(str) {
oldErrorLog(str)
console.log('看我,我监听了 error 输出')
}
}
monitorErrorInitFn()
这样做是比较草率的,如果需要详细的报错信息可以考虑接入sentry 或者其他成熟的工具
项目整体设计
目前为止,数据搜集已经做到了,那么如何让开发者方便,快捷的接入使用呢?
步骤如下:
1.首先监控系统有个自己的管理后台,用户在监控后台填写要接入的项目名称,项目线上域名,用户信息存放的位置,然后系统把这些数据保存在数据库中并生成唯一id
2.把的数据搜集工具封装成一个npm包,名字就叫u-spa
吧,这个npm包对外暴露一个方法,并把id作为参数穿传进来
3.u-spa
,初始化的时候,通过id向后端发送请求,获取用户填写的信息,包括系统名称,线上域名,用户信息存放位置,并把这些数据存放在 localStorage 中
4.触发数据上报的时候,就可以把需要采集的数据,并结合localStorage 中的网站信息,获取当前用户名,并限制在当前域名下,一起上报到后端,over
ps:
1.填写线上域名的原因是只处理线上的数据采集,本地、测试环境就不用采集和上报了
2.填写用户信息存放的位置(可以在 localStorage中,也可以在 cookie中),是为了明确知道当前用户是谁,如果接入的系统本身没有存储用户信息,那么在npm初始化请求的时候,后端可以生成一个唯一的 userId,并存放在redis中,设限24小时过期时间,以此 userId 作为用户信息的标识,不过这种情况下,只能统计当天的pu、uv,比如用户第一天使用了,第二天没使用,那么第三天生成的 userId 就跟之前的不一样了,这样统计n天之内的数据就不太准确了,所以还是建议上报真实的用户名或者邮箱
u-spa
u-spa 就是咱们封装的收集数据并上报的工具
代码地址: https://github.com/YalongYan/u-spa
安装如下
npm i u-spa -S
使用如下
import uSpa from 'u-spa'
uSpa(id)
就是在入口文件里添加上面两行代码就可以了,id就是在监控后台填写完网站信息,生成的id
现在只是在触发上报的是,console出了上报信息,真实使用还需要搭配上传数据接口。
特性
1.采集页面首次加载的性能指标,并上报
2.监听url改变,并上报数据,包括首页也会触发上报
3.监听console.error 并上报打印的数据
4.接入方便,只需两行代码
5.无技术壁垒,不管是vue 还是 react都可以支持
6.其实u-spa 不只是限制在单页应用上使用,多页应用也是可以用的
总结
其实前端监控,需要监控,以及可以监控的数据,还有很多
比如可以统计页面的访问时长、记录页面点击区域并生成点击热力图、统计按钮级别的点击事件等等
需要从实际需求出发, 满足业务需求才是第一位
本文只说数据的采集,其实搭配还需要有后端服务,对数据的接受,以及把这些数据通过图标等形式展示出来,这样才是一个完整的监控系统