前端监控系统(一) | 青训营笔记

123 阅读4分钟

这是我参与「第四届青训营 」笔记创作活动的第10天

今天简单记录一下关于监控系统的一些思考,对监控系统相关要呈现的数据进行总结~
为什么要做监控系统呢?我感觉最主要的目的还是为提升用户体验提供一个佐证,那么哪些数据对提升用户体验有帮助?又该如何监控到这些数据呢?

前端数据分类

前端的数据其实有很多,从大众普遍关注的 PV、UV、广告点击量,到客户端的网络环境、登陆状态,再到浏览器、操作系统信息,最后到页面性能、JS 异常,这些数据都可以在前端收集到。总的来看,主要分为以下三大类:

  • 异常数据
  • 访问数据
  • 性能数据

异常数据

1. 静态资源异常
监控css、js、img等静态资源文件加载错误的情况。使用window.addEventListener('error',function)进行监控。

// 捕获静态资源加载失败错误 js css img
window.addEventListener('error', e => {
    const target = e.target
    if (!target) return
    const typeName = e.target.localName;
    let sourceUrl = "";
    if (typeName === "link") {
        sourceUrl = e.target.href;
    } else if (typeName === "script" || typeName === "img") {
        sourceUrl = e.target.src;
    }

    if (sourceUrl) {
        lazyReportCache({
            url: sourceUrl,
            type: 'error',
            subType: 'resource',
            startTime: e.timeStamp,
            html: target.outerHTML,
            resourceType: target.tagName,
            paths: e.path.map(item => item.tagName).filter(Boolean),
            pageURL: getPageURL(),
        })
    }
}, true)

js异常

  • 异常的提示信息:这是识别一个异常的最重要依据,如:e.src 为空或不是对象
  • JS 文件名。
  • 异常所在行。
  • 发生异常的浏览器。
  • 堆栈信息:必要的时候需要函数调用的堆栈信息,但是注意堆栈信息可能会比较大,需要截取。
// parseErrorMsg.js
const fs = require('fs');
const path = require('path');
const sourceMap = require('source-map');

export default async function parseErrorMsg(error) {
  const mapObj = JSON.parse(getMapFileContent(error.url))
  const consumer = await new sourceMap.SourceMapConsumer(mapObj)
  // 将 webpack://source-map-demo/./src/index.js 文件中的 ./ 去掉
  const sources = mapObj.sources.map(item => format(item))
  // 根据压缩后的报错信息得出未压缩前的报错行列数和源码文件
  const originalInfo = consumer.originalPositionFor({ line: error.line, column: error.column })
  // sourcesContent 中包含了各个文件的未压缩前的源码,根据文件名找出对应的源码
  const originalFileContent = mapObj.sourcesContent[sources.indexOf(originalInfo.source)]
  return {
    file: originalInfo.source,
    content: originalFileContent,
    line: originalInfo.line,
    column: originalInfo.column,
    msg: error.msg,
    error: error.error
  }
}

function format(item) {
  return item.replace(/(\.\/)*/g, '')
}

function getMapFileContent(url) {
  return fs.readFileSync(path.resolve(__dirname, `./dist/${url.split('/').pop()}.map`), 'utf-8')
}

访问数据

  • PV/UV:最基础的 PV(页面访问量)、UV(独立访问用户数据量)。
  • 页面来源:页面的 referer,可以定位页面的入口。
  • 操作系统:了解用户的 OS 情况,帮助分析用户群体的特征,特别是移动端、iOS 和 Android 的分布就更有意义了。
  • 浏览器:可以统计到各种浏览器的占比,对于是否继续兼容 IE6、新技术(HTML5、CSS3 等)的运用等调研提供参考价值。
  • 分辨率:对页面设计提供参考,特别是响应式设计。
  • 登录率:登陆用户具有更高的分析价值,引导用户登陆是非常重要的。
  • 地域分布:访问用户在地理位置上的分布,可以针对不同地域做运营、活动等。
  • 网络类型:wifi/3G/2G,为产品是否需要适配不同网络环境做决策。
  • 访问时段:掌握用户访问时间的分布,引导削峰填谷、节省带宽。
  • 停留时长:判断页面内容是否具有吸引力,对于需要长时间阅读的页面比较有意义。
  • 到达深度:和停留时长类似,例如百度百科,用户浏览时的页面到达深度直接反映词条的质量。

性能数据

FP/FCP/LCP/CLS

chrome 开发团队提出了一系列用于检测网页性能的指标:

  • FP(first-paint),从页面加载开始到第一个像素绘制到屏幕上的时间
  • FCP(first-contentful-paint),从页面加载开始到页面内容的任何部分在屏幕上完成渲染的时间
  • LCP(largest-contentful-paint),从页面加载开始到最大文本块或图像元素在屏幕上完成渲染的时间
  • CLS(layout-shift),从页面加载开始和其生命周期状态变为隐藏期间发生的所有意外布局偏移的累积分数
    统计白屏时间
    在浏览器 head 内底部加一句 JS 统计头部资源加载结束点
<!DOCTYPE HTML>
<html>
    <head>
        <meta charset="UTF-8"/>
    <script>
      var start_time = +new Date; //测试时间起点,实际统计起点为 DNS 查询
    </script>
    <!-- 3s 后这个 js 才会返回 -->
    <script src="script.php"></script>  
    <script>
      var end_time = +new Date; //时间终点
      var headtime = end_time - start_time; //头部资源加载时间    
      console.log(headtime);
    </script>
    </head> 
    <body>     
    <p>在头部资源加载完之前页面将是白屏</p>
    <p>script.php 被模拟设置 3s 后返回,head 底部内嵌 JS 等待前面 js 返回后才执行</p>
    <p>script.php 替换成一个执行长时间循环的 js 效果也一样</p>  
    </body>
</html>

参考资料

《前端监控系统设计》juejin.cn/post/704669…