• 【前端】前端监控⊆埋点


    前端监控分为三个方面

    1. 异常监控(监控前端页面的报错)
    2. 性能监控(监控页面的性能)
    3. 用户行为监控(监控用户的行为,计算PV、UV、在线时间等、数据监控即我们常说的埋点

    前端监控流程

    1. 前端埋点
    2. 数据上报
    3. 加工汇总
    4. 可视化展示
    5. 监控报警

    异常监控

    • JS 代码运行错误、语法错误等;
    • AJAX 请求错误;
    • 静态资源加载错误;
    • Promise 异步函数错误;

    错误信息监控简单来说就是要搜集报错信息的发生的位置,以及报错的类型,进行上报,便于我们能够更好的掌握错误信息,从而能够对症下药。按照 5W1H 的法则来说,我们需要关注以下的几项信息:

    • What ,发生了什么错误:语法错误、类型错误、数据错误、逻辑错误等;
    • When ,什么时间发生的错误,可带上时间戳进行上报;
    • Who ,哪个用户或者哪一类用户发生了错误,包括用户 ID 、设备信息、IP 信息等;
    • Where ,哪个项目、哪些页面发生错误,可以上报页面的 URL 以及代码报错行数等信息;
    • Why ,为什么会发生错误,也就是用户在什么样的场景下发生的错误,便于问题复现;
    • How ,根据以上的信息如何进行问题的定位,然后怎么处理并解决问题;

    常见的错误捕获方法主要是 try / catch 、window.onerror 和window.addEventListener 等。

    try / catch

    这是我们在代码调试的过程中最常用的一个方式,但它只能捕获代码常规的运行错误,语法错误和异步错误并能捕获到。

    // 常规运行时错误,可以捕获 ✅
    try {
        console.log(notdefined);
    } catch(e) {
        console.log('捕获到异常:', 'ReferenceError');
     
    }
     
    // 语法错误,不能捕获 ❌
    try {
        const notdefined,
    } catch(e) {
        console.log('捕获不到异常:', 'Uncaught SyntaxError');
    }
     
    // 异步错误,不能捕获 ❌
    try {
        setTimeout(() => {
            console.log(notdefined);
        }, 0)
    } catch(e) {
        console.log('捕获不到异常:', 'Uncaught ReferenceError');
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    window.onerror
    当 JS 运行时错误发生时,window 会触发一个 ErrorEvent 接口的 error 事件,并执行 window.onerror() 。

    加载一个全局的 error 事件处理函数可用于自动收集错误报告。

    最后需要补充的是:window.onerror 函数只有在返回 true 的时候,异常才不会向上抛出,否则即使是知道异常的发生,控制台还是会显示 Uncaught Error 。

     
    ```javascript
    /**
     
    * @param { string } message 错误信息
    * @param { string } source 发生错误的脚本URL
    * @param { number } lineno 发生错误的行号
    * @param { number } colno 发生错误的列号
    * @param { object } error Error对象
     
    */
     
    window.onerror = function(message, source, lineno, colno, error) {
        console.log('捕获到的错误信息是:', message, source, lineno, colno, error )
    }
     
    // 常规运行时错误,可以捕获 ✅
    window.onerror = function(message, source, lineno, colno, error) {
        console.log('捕获到异常:',{message, source, lineno, colno, error});
    }
    console.log(notdefined);
    // message: "Uncaught ReferenceError: notdefined is not defined"
    // source: "file:///C:/Users/qinzq42866/Desktop/error.html"
    // lineno: 14
    // colno: 19
    // error: ReferenceError: notdefined is not defined at file
    // 语法错误,不能捕获 ❌
    window.onerror = function(message, source, lineno, colno, error) {
        console.log('未捕获到异常:',{message, source, lineno, colno, error});
    }
    const notdefined,
    // Uncaught SyntaxError: Missing initializer in const declaration
     
    // 异步错误,可以捕获 ✅
    window.onerror = function(message, source, lineno, colno, error) {
        console.log('捕获到异常:',{message, source, lineno, colno, error});
    }
    setTimeout(() => {
        console.log(notdefined);
    }, 0)
     
    // message: "Uncaught ReferenceError: notdefined is not defined"
     
    // source: "file:///C:/Users/qinzq42866/Desktop/error.html"
     
    // lineno: 15
     
    // colno: 21
     
    // error: ReferenceError: notdefined is not defined at file
     
    // 资源错误,不能捕获 ❌
    <script>
    window.onerror = function(message, source, lineno, colno, error) {
        console.log('捕获到异常:',{message, source, lineno, colno, error});
    }
     
    </script>
     
    // GET https://yun.tuia.cn/image/kkk.png 404 (Not Found)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    window.addEventListener
    当一项静态资源加载失败时,加载资源的元素会触发一个 Event 接口的 Error 事件,这些 Error 事件不会向上冒泡到 window ,但能被捕获。而 window.onerror 不能检测捕获。

    // 图片、script、css加载错误,都能被捕获 ✅
     
    <script>
     
      window.addEventListener('error', (error) => {
     
         console.log('捕获到异常:', error);
     
      }, true)
     
    </script>
     
     
    // fetch错误,不能捕获 ❌
     
     
    <script>
     
      window.addEventListener('error', (error) => {
     
        console.log('未捕获到异常:', error);
     
      }, true)
     
    </script
     
     
    <script>
     
      fetch('https://tuia.cn/test')
     
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    由于网络请求异常不会发生事件冒泡,因此必须在事件捕获的阶段将其捕捉到才行,这种方式虽然能够捕捉到网络请求的异常,但是却无法判断 HTTP 的状态,因此仍然需要配合服务端的日志进行配合分析。

    需要注意的是:不同浏览器下返回的 Error 对象是不一样的,需要做兼容处理。

    Promise 错误

    没有写 catch 的 Promise 中抛出的错误是无法被 onerror 或 try / catch 捕获到的,这也是为什么我们一定要在 Promise 后面加上 catch 去捕获和处理异常。

    为了防止有漏掉的 Promise 异常信息,建议在全局增加一个对 unhandledrejection 的监听,用来全局监听 Uncaught Promise Error 。

    说明:当 Promise 被 reject 且没有 reject 处理器的时候,会触发 unhandledrejection 事件;这可能发生在 window 下,但也可能发生在 Worker 中。 这对于调试回退错误处理非常有用。

    window.addEventListener("unhandledrejection", event => {
     
        console.warn('UNHANDLED PROMISE REJECTION:', ${event.reason});
     
    });
     
    window.onunhandledrejection = event => {
     
        console.warn('UNHANDLED PROMISE REJECTION:', ${event.reason});
     
    };
     
    window.addEventListener("unhandledrejection", function(e){
     
        e.preventDefault()
     
        console.log('捕获到异常:', e);
     
    });
     
    Promise.reject('promise error');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    说明:如果去掉控制台的异常显示,需要加上 event.preventDefault() ;

    Vue 错误

    由于 Vue 会捕获到所有 Vue 单文件组件或者 Vue.extend 继承的代码,所以在 Vue 里面出现的错误并不会直接抛给 window.onerror ,而是会被 Vue 自身的 Vue.config.errorHandler 捕获。

    Vue.config.errorHandler = (err, vm, info) => {
     
    console.error('通过vue errorHandler捕获的错误');
     
    console.error(err);
     
    console.error(vm);
     
    console.error(info);
     
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    React 错误

    React 16 提供了一个内置函数 componentDidCatch ,使用它可以轻松的捕获到 React 组件内部抛出的错误信息。

     
    class ErrorBoundary extends React.Component {
      constructor(props) {
        super(props);
        this.state = { hasError: false };
      }
     
      componentDidCatch(error, errorInfo) {
        console.log('捕获到错误:', error, errorInfo);
      }
     
      render() {
        if (this.state.hasError) {
          return `Something went wrong.`;
        }
     
        return this.props.children; 
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    性能监控

    • 不同用户和不同设备下的首屏加载时间,包括白屏时间;
    • HTTP 接口的响应时间;
    • 静态资源、包括图片的下载时间;

    根据W3C性能小组引入的新的API(目前IE9以上的浏览器)–window.performance,实现前端性能监控
    在这里插入图片描述

    (function () {
     
        handleAddListener('load', getTiming)
     
        function handleAddListener(type, fn) {
            if (window.addEventListener) {
                window.addEventListener(type, fn)
            } else {
                window.attachEvent('on' + type, fn)
            }
        }
     
        function getTiming() {
            try {
                var time = performance.timing;
                var timingObj = {};
     
                var loadTime = (time.loadEventEnd - time.loadEventStart);
     
                if (loadTime < 0) {
                    setTimeout(function () {
                        getTiming();
                    }, 200);
                    return;
                }
                // 阶段耗时
                timingObj['DNS解析耗时'] = (time.domainLookupEnd - time.domainLookupStart);
                timingObj['TCP连接耗时'] = (time.connectEnd - time.connectStart);
                timingObj['SSL安全连接耗时'] = (time.connectEnd - time.secureConnectionStart);//针对https
                timingObj['网络请求耗时'] = (time.responseStart - time.requestStart);
                timingObj['数据传输耗时'] = (time.responseEnd - time.responseStart);
                timingObj['DOM解析耗时'] = (time.domInteractive - time.responseEnd);
                timingObj['资源加载耗时, 表示页面中的同步加载资源'] = (time.loadEventStart - time.domContentLoadedEventEnd);
                timingObj['前端onload执行时间'] = (time.loadEventEnd - time.loadEventStart);
     
                //性能指标(上报字段名)
                timingObj["首次渲染"] = time.responseEnd - time.fetchStart
                // timingObj["首屏时间"] =  first meaningful paint
                timingObj["首次可交互"] = time.domInteractive - time.fetchStart
                timingObj["DOMReady"] = time.domContentLoadedEventEnd - time.fetchStart
                timingObj["页面完全加载"] = time.loadEventStart - time.fetchStart
                timingObj["首包时间"] = time.responseStart - time.domainLookupStart
     
     
                for (item in timingObj) {
                    console.log(item + ":" + timingObj[item] + '毫秒(ms)');
                }
     
                console.log(performance.timing);
                console.log(performance);
     
            } catch (e) {
                console.log(timingObj)
                console.log(performance.timing);
            }
        }
    })();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    用户行为监控

    • PV / UV:PV 即 Page View ,也就是页面的浏览数量,没打开页面一次就会统计一次;UV 即 User View
      ,也就是不同用户访问的次数,在 PV 的基础上根据 User 信息的不同做了去重操作;
    • 用户在每个页面停留的时间信息。即从用户打开该页面到用户离开该页面的时间差,用于表示该页面对用户的留存程度;
    • 用户的来处。即从什么入口或什么渠道来到了当前页面,通常会在 URL 中添加查询参数来做区分统计;
    • 用户的页面操作行为。即用户在该页面点击了哪些按钮,或者从什么链接去到了某些页面等等,来分析用户的去向。
    import tracker from "../util/tracker";
    export function pv() {
      tracker.send({
        kind: "business",
        type: "pv",
        startTime: performance.now(),
        pageURL: getPageURL(),
        referrer: document.referrer,
        uuid: getUUID(),
      });
      let startTime = Date.now();
      window.addEventListener(
        "beforeunload",
        () => {
          let stayTime = Date.now() - startTime;
          tracker.send({
            kind: "business",
            type: "stayTime",
            stayTime,
            pageURL: getPageURL(),
            uuid: getUUID(),
          });
        },
        false
      );
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    常见的埋点方案

    代码埋点
    嵌入代码的形式
    优点:精确(任意时刻,数据量全面)
    缺点:代码工作量点

    可视化埋点
    通过可视化交互的手段,代替代码埋点
    将业务代码和埋点代码分离,提供一个可视化交互的页面,输入为业务代码,通过这个系统,可以在业务代码中自定义的增加埋点事件等等,最后输出的代码耦合了业务代码和埋点代码
    用系统来代替手工插入埋点代码

    无痕埋点
    前端的任意一个事件被绑定一个标识,所有的事件都被记录下来
    通过定期上传记录文件,配合文件解析,解析出来我们想要的数据,并生成可视化报告供专业人员分析
    无痕埋点的优点是采集全量数据,不会出现漏埋和误埋等现象
    缺点是给数据传输和服务器增加压力,也无法灵活定制数据结构

    来源

    前端监控指的是什么?
    前端 监控

  • 相关阅读:
    几类步进电机的原理
    基于若依ruoyi-nbcio支持flowable流程增加自定义业务表单(三)
    绿竹生物获上市“大路条”:融资不搞研发去理财,孔健下什么棋?
    RabbitMQ常见问题
    代码随想录算法训练营第四十四天 | 416. 分割等和子集
    哈夫曼树的定义、原理及哈夫曼编码
    卡尔曼滤波与融合算法
    【ES】elasticsearch8.3.3
    Ubuntu20.04安装c++版本的OpenCV
    【机器学习】线性分类【下】经典线性分类算法
  • 原文地址:https://blog.csdn.net/weixin_44231544/article/details/134156201