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


    原文链接: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 的区别

  • 相关阅读:
    虚拟机类加载机制详解
    简单了解Tomcat与OSGi的类加载器架构
    Java高并发编程(四)
    Java高并发编程(三)
    Java高并发编程(一)
    垃圾收集与几种常用的垃圾收集算法
    初识java内存区域
    vue2.0基础学习(1)
    git/github 生成密钥
    手机预览vue项目
  • 原文地址:https://www.cnblogs.com/yalong/p/15954026.html
Copyright © 2020-2023  润新知