前端监控流程

  1. 埋点:前端在项目中实现数据的采集。
  2. 上报:发送http请求,将数据上报到数据中心。
  3. 处理:数据中心对请求进行清洗(去除脏数据)、加工(将数据结构化)和存储(存储可能有有效期)。
  4. 消费:数据中心应该支持数据的查看(实时性不一而足)和报警的功能。

采集哪些数据

  1. 性能
    1. FP
    2. FCP
    3. FMP
    4. load
    5. DCL
    6. LCP
    7. TTI
    8. FID
    9. CLS
  2. 环境
    1. 时间
    2. 设备信息(设备id等)
    3. 操作系统(mac/win/android/ios/ipad)
    4. 浏览器(Chrome/Safari/FF/IE……)
    5. ip
    6. ……
  3. 异常
    • js报错
    • 静态资源请求异常
    • promise未捕获
    • 请求异常
    • 卡顿
    • 崩溃
  4. 业务数据
    1. pv
    2. uv
    3. 停留时间

如何采集

性能

性能指标及采集方法请参考页面性能指标

环境

浏览器、操作系统及版本号、可以通过navigator.useAgent分析出来;ip通过请求后端接口,由后端返回;时间可以通过浏览器的Date API获取。

异常

1. js报错

当页面加载自不同域的脚本(例如页面的 JS 托管在 CDN)中发生语法错误时,浏览器基于安全机制考虑,不会给出语法错误的细节,而是简单的 Script error.。因此,如果你希望自己页面的详细报错信息被监控 SDK 捕获你需要为页面中的脚本 script 添加 crossorigin="anonymous"属性,且脚本所在的服务设置 CORS 响应头Access-Control-Allow-Origin: *

由于发布到生产环境的代码是压缩过的,所以为了能够根据上报的错误信息定位到源代码的出错位置,需要把sourceMap上传到监控平台(如sentry)。

js报错通过window.onerror回调来监听,这个回调可以监听到没有被try catch捕获的错误。

  1. window.addEventListener('error', function (event) {});

2. 静态资源请求异常

可以通过在静态资源的标签(scriptlink)上添加error属性监听加载失败的事件。这样做代码侵入性较强。

通常使用监听window.onerror事件来监控资源请求错误事件。

  1. window.addEventListener('error', function (event) {
  2. if (event.target && (event.target.src || event.target.href)) {
  3. console.log('load error', event.target.src ? event.target.src : event.target.href);
  4. }
  5. });

3. promise未捕获

"unhandledrejection"事件可以用来监听未捕获的promise的reject。

  1. window.addEventListener('unhandledrejection', function (event) {
  2. console.log('promise reject', event.reason);
  3. });

静态资源加载明细,可以使用PerformanceResourceTiming API来统计。

4. 请求异常

请求异常通常是通过重写XMLHttpRequest来实现的。

  • 重写xhr的open、send方法
  • 监听load、error、abort事件

重写fetch也是一样的思路。

重写之后请求的其他信息也可以被采集到。

5. 卡顿

通常认为60帧/1s是人肉眼能接受的流畅度最低值,低于这个值,就会感觉到卡顿。

Chrome DevTool 中有一栏 Rendering 中包含 FPS 指标,但目前浏览器标准中暂时没有提供相应 API ,只能手动实现。实现方法是通过requestAnimationFrame方法,requestAnimationFrame会在下一次绘制之前被调用,可以认为此次调用和下次调用是两帧,通过这种方法就可以统计出渲染帧率。

一般认为持续一段时间帧率低于一个阈值就发生了卡顿。

由于卡顿一般是长任务造成的,因此也可以通过监听长任务来监控卡顿情况。长任务的监控参考页面性能指标

6. 崩溃

页面崩溃时候js无法执行,所以页面崩溃的监听要在web worker中进行。具体的实现方式是主页面定期向web worker发送心跳(2s ~ 10s),如果长时间未收到心跳(如3个心跳周期),则认为页面崩溃,web worker就需要上报崩溃事件了。

业务数据

1. PV UV

PV(page view) 是页面浏览量,UV(Unique visitor)用户访问量。PV 只要访问一次页面就算一次,UV 同一天内多次访问只算一次。

对于前端来说,只要每次进入页面上报一次 PV 就行,UV 的统计放在服务端来做,主要是分析上报的数据来统计得出 UV。

2. 停留时间

在进入页面时候记录一个时间,页面离开时候计算当前时间与进入页面的时间差,然后上报。

  1. funtion init() {
  2. // reportPV();
  3. const startTime = Date.now();
  4. window.addEventListener(
  5. "beforeunload",
  6. () => {
  7. let stayTime = Date.now() - startTime;
  8. // reportStayTime();
  9. },
  10. false
  11. );
  12. }

如何上报

上报方法

  1. 动态创建一个img标签将数据通过url拼接的方式传递,对于这个资源的请求,通常服务端会返回204(No Content)以保证最小的数据量,服务端会通过汇总请求日志来统计上报数据。
  2. 优先使用 Navigator.sendBeacon,这个 API 是为了解决网页卸载时候的上报丢失问题,它通过 HTTP POST 将数据异步传输到服务器且不会影响页面卸载。
  3. 使用同步 XHR 进行上报以延迟页面卸载,不过现在很多浏览器禁止了该行为。

优化

当上报数据量很大时候,需要做一些策略上的优化

  1. 批量上传,将多次上报合成一条发送请求。
  2. 设置采样率,有些上报,如业务中某个循环逻辑,可以不用每次触发时候都上报,可以设置每10次上报1次。

缓存

如果用户电脑上某段时间网络不好,数据无法上报,可以先将未成功发送的上报数据缓存到本地(localStorage),等网络通畅时候再发送,当然还要通过一些策略来避免缓存过多和发送缓存数据时候一次性发送大量请求或者请求中发送大量数据。

参考文章

深入浅出前端监控
前端监控体系搭建