前端性能监控的基本介绍

蚊子前端博客
发布于 2023-03-04 10:50
我们主要介绍下前端性能监控的目的、指标,和一些基本的实现思路。

前端性能监控的基本介绍这是我之前在我们团队中做的一次分享,很多地方写的比较简单,就是抛砖引玉,大致讲解下前端性能监控的基本思路。

1. 性能监控的目的、意义

服务上线,才是万里长征的第一步,我们还要根据收集到性能收据和业务数据,进行后续的调整和优化。

web 的性能一定程度上影响了用户留存率,用户各种情况不确定,诸如网络情况,机型,浏览器版本等,或者项目本身的迭代更新。不可能每一项在本地测试过程中,都能覆盖的到。而且,随着版本的迭代,有的指标会发生变化。若没有监控,线上的一切情况对我们来说就是一个黑盒。

因此这里需要一个性能监控系统,监控和预警页面的性能,并且能在发现瓶颈的时候来进行优化。

我们可转化为三个方面来看:响应速度、页面稳定性、外部服务调用

  1. 关注性能指标,提升用户体验:老说性能优化,那哪个性能有问题,得先知道;

  2. 及早发现错误:内部测试时,受限于用户规模小、机型少等原因,无法覆盖到所有场景;通过该监控,可以及早发现错误并修正;不用等待用户的主动反馈(能主动反馈问题的用户都是忠实用户);

  3. 关注第三方系统指标:比如调用的后端接口、其他第三方接口等,可以排查出是前端问题、还是后端问题等;

2. 监控什么指标?

我们在上面分成了三部分来监控,每项细分的话,主要有:

2.1 性能指标

  1. 各种资源的加载时间,包括 html, js, css, img 等资源;

  2. 首页、首屏、整页的渲染耗时(服务端渲染和客户端渲染);

  3. 慢日志(加载在 10s 以上的资源);

比如首屏渲染耗时的计算,根据渲染方式的不同,计算方法也不一样。若采用服务端渲染的,只需要在恰当的位置打点就行。但若采用的是前端渲染,则计算方法就复杂的多。

  1. 在代码组件中上报,比如 useEffect() 中,这对代码的侵入性比较大;

  2. IntersectionObserver + MutationObserver ,监听节点的变化,判断出现在首屏中的节点,待所有的节点都不再变动,则认为首屏渲染完毕(当然,这里也要考虑到图片等资源的耗时);

2.2 各种错误

  1. 未捕获的错误,如从未定义的变量中获取某属性,未 catch 的 Promise 异常等;

  2. 开发者主动抛出的错误,如 console.error;

  3. 静态资源(如图片等)加载异常;

2.3 第三方系统的数据

  1. 请求接口的成功率(如 200、4xx、5xx 等);

  2. 接口的耗时、code 码等;

2.4 公共指标

  1. ua;

  2. 当前页的 url;

  3. 设备尺寸、分辨率、设备像素比等;

  4. 用户标识(openid、uid 等)、设备标识;

3. 怎么监控这些指标

3.1 性能指标

性能指标可以通过 performance 来获取。

html 页面的数据,如 ttfb,白屏时间等,可以通过 performance.timing 来获取。

其他的如 jscssimg 等资源,可以通过 performance.getEntries() 来获取。

performance中的各个指标-蚊子的前端博客

3.2 各种错误

被动捕获的全局错误。

COPYJAVASCRIPT

window.addEventListenter("error", (event) => { upload("error", event); }); // 未捕获的 reject 错误 window.addEventListenter("unhandledRejection", (event) => { upload("unhandledRejection", event); });

主动输出的错误:

COPYJAVASCRIPT

const originalError = console.error; console.error = (...rest) => { originalError(...rest); upload("console.error", ...rest); };

注意,使用throw抛出的错误,会被 onerror 捕获。

3.3 监控接口的数据

一般是劫持发送请求的方法,然后埋上自己的监控代码。

以 XMLHttpRequest 为例,我们要监听的是他的方法,而不是这个类。

COPYJAVASCRIPT

const originalSend = XMLHttpRequest.prototype.send; const originalOpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function (method, url, ...rest) { originalOpen.call(this, method, url, ...rest); }; XMLHttpRequest.prototype.send = function (data) { this.startTime = Date.now(); const { onloadend, ontimeout } = this; this.onloadend = function () { onloadend(); }; this.ontimeout = function () { ontimeout(); }; originSend.call(this, data); };

fetch 方法同理:

COPYJAVASCRIPT

const originalFetch = window.fetch; window.fetch = function (...rest) { const startTime = Date.now(); return originalFetch(...rest).then((response) => { upload("fetch", response); return response; }); };

4. 上报哪些数据

我们依照的原则是:

  1. 在不侵犯用户隐私的前提下,尽可能多维度的收集信息,来更好地分析和定位问题;

  2. 尽量不漏报数据、不死循环(如自己上报自己的错误、劫持方法没写好);

  3. 通用,与前端使用的框架无关;并且,减少开发者的使用负担(能主动收集的,就不麻烦开发者);

给到开发者的都是一些可选的配置,如:

  1. 用户的标识:每个应用对用户的标识是不一样的,可以让开发者自行定义用户的标识,如有的是 openid,有的是 uid,手 Q 里是 uin 等;

  2. 采样率:有的流量特别大的应用,可以设置采样率(0.01~1),避免上报的数据量过多;

  3. 是否是 spa 应用:

5. 如何上报这些数据

有三种方式,主要是前 2 种。

优点缺点
创建 img 图片方式简单,可跨域,兼容性好数据量有限制
xhr 的 post 请求数据大小没有限制需特殊处理跨域限制
navigator.sendBeacon页面卸载前上报数据避免丢失兼容性一般

其实要说 sendBeacon() 的兼容性,目前也算还可以,大部分主流浏览器都是满足的:sendbeacon 的兼容性

有些 sdk 为了避免多次高频的数据上报,也会先在本地积攒一定的数量或时间后,再统一进行上报。

6. 上报之后

数据上报之后,主要是进行 3 个过程:

  1. 数据清洗;

  2. 敏捷分析;

  3. 展示和告警;

6.1 数据清洗

对一些不规范或缺失的数据,进行补全或剔除。

数据清洗-蚊子的前端博客

6.3 展示和告警

从原始的数据中分析出页面的性能和错误率后,需要在某一个平台上进行展示和过滤,方便我们前端开发者可以定位问题。

某平台中的数据展示-蚊子的前端博客

同时,为了及早发现错误,也需要配置相应的告警机制。告警策略也分很多种,如:

  1. 环比:单位时间内数据曲线比前一个周期有激增或急降(大部分都设置这个);

  2. 同比:与昨日同时间段内的数据波动;

  3. 按照不同的字段,告警的单位也是不一样的,如 pv 数据、错误数据等一般是按照百分比;首页耗时等是按照耗时上下限;http code 有的按照具体的数量,有的按照百分比;

告警提示的样例,随便找了张图:

告警提示-蚊子的前端博客

7. 总结

在实际的实现过程中,就要复杂的很多,要考虑的因素也更多。就现在市面上的各种前端监控系统,着重点也不一样,支持的平台也不一样。

比如我曾经使用过的 RUM(之前叫 Aegis),支持的功能就比较多,更多是在前端领域。我摘抄一下RUM 官方的介绍

前端性能监控(Real User Monitoring,RUM)是一站式前端监控解决方案,专注于 Web、小程序等场景监控。前端性能监控聚焦用户页面性能(页面测速,接口测速,CDN 测速等)和质量(JS 错误,Ajax 错误等),并且联动腾讯云应用性能观测实现前后端一体化监控。用户只需要安装 SDK 到自己的项目中,通过简单配置化,即可实现对用户页面质量的全方位守护,真正做到低成本使用和无侵入监控。

但我后来了解到的 Sentry,它是更聚焦在错误日志收集和全链路追踪上,支持前端 js、Node.js、Python、Go 等各种语言环境。如每条的错误信息,除了一些全局数据(如浏览器版本等),还有收集发生该错误前的一些接口请求、console 日志输出和用户的点击行为等,方便我们可以尽可能的还原现场。同时,还对每条的 xhr 请求添加 trace 等请求头参数,便于追踪该请求的整条链路的处理。

标签:
阅读(592)

公众号:

qrcode

微信公众号:前端小茶馆

公众号:

qrcode

微信公众号:前端小茶馆