前端监控之单页应用的监控

原文链接:https://www.cnblogs.com/yalong/p/15954026.html

背景

公司有大量单页应用的项目,有的年代久远,想把一些不用的项目下掉,或者把使用频率很低的工具进行功能重组,避免占用大量人力去维护众多项目;
但是不知道这些系统是否有人,以及有多少人在用,以及哪些页面使用频率最高;
所以需要统计项目的pv,uv

需求分析

  1. 由于单页应用的路由跟后端没有交互,所以后端无法统计到具体的pv,最多统计个uv,所以前端实现比较合适
  2. 由于系统很多,而且vue, react都有,关键版本也不一致,所以设计方案要兼容多框架,多版本
  3. 还是由于系统太多,所以要尽量减少对现有代码的侵入,降低接入成本
  4. 既然要实现统计pv,uv,那么能不能统计多一点的数据,比如首屏加载耗时,异常信息等
  5. 那么就搞一套适合当下的前端监控吧

监控系统数据采集功能拆解

前端监控的核心是采集相关数据,然后才上报,这里只说数据的采集

一.采集首屏加载相关数据

首屏加载性能指标如下:

  1. DNS 解析耗时: domainLookupEnd - domainLookupStart
  2. TCP 连接耗时: connectEnd - connectStart
  3. SSL 安全连接耗时: connectEnd - secureConnectionStart
  4. 网络请求耗时 (TTFB): responseStart - requestStart
  5. 数据传输耗时: responseEnd - responseStart
  6. DOM 解析耗时: domInteractive - responseEnd
  7. 资源加载耗时: loadEventStart - domContentLoadedEventEnd
  8. First Byte时间: responseStart - domainLookupStart
  9. 白屏时间: responseEnd - fetchStart
  10. 首次可交互时间: domInteractive - fetchStart
  11. DOM Ready 时间: domContentLoadEventEnd - fetchStart
  12. 页面完全加载时间: loadEventStart - fetchStart
  13. http 头部大小: transferSize - encodedBodySize
  14. 重定向次数:performance.navigation.redirectCount
  15. 重定向耗时: 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 不只是限制在单页应用上使用,多页应用也是可以用的

总结

其实前端监控,需要监控,以及可以监控的数据,还有很多

比如可以统计页面的访问时长、记录页面点击区域并生成点击热力图、统计按钮级别的点击事件等等

需要从实际需求出发, 满足业务需求才是第一位

本文只说数据的采集,其实搭配还需要有后端服务,对数据的接受,以及把这些数据通过图标等形式展示出来,这样才是一个完整的监控系统

参考地址:

理解浏览器历史记录

如何做前端异常监控?

友盟的监控工具

hash 跟 history 的区别

posted @ 2022-03-02 11:04  进军的蜗牛  阅读(984)  评论(1编辑  收藏  举报