-
Notifications
You must be signed in to change notification settings - Fork 0
Description
为啥要监控,监控啥?
说到监控,当然是要获取点什么信息,比如获取用户行为习惯、报错数据、跟踪产品在用户端的使用情况。然后以这些数据为基础,分析出指明产品优化方向的信息,例如可以根据用户行为习惯,知道用户偏好,可以利用深度学习,适当做一些数据推荐,监控到页面的浏览量和点击量,还可以根据触发的错误信息排查bug等。
有哪些前端监控?
主要可以分成三类:数据监控、性能监控和异常监控。
数据监控
主要是监控用户的行为。主要可以监控以下几类数据:
- PV/UV:
PV(page view): 即页面的点击量或浏览量
UV(unique visitor): 独立访客,即访问某个站点和点击某条新闻的不同IP地址的数量 - 停留时间: 用户在同一个页面的停留时间
- 流量来源: 用户是通过什么入口来访问该页面
- 用户交互: 用户在相应页面中触发的行为
为啥要统计这些信息?
当然是有意义的,比如我们通过流量来源,就可以促进产品的推广。知道了用户在每一个页面的停留时间,就可以针对停留时间长的页面加入广告推送等。
性能监控
性能监控主要是监听前端的性能,用于提升用户体验。常见的性能监控项包括:
- 不同用户,不同机型和不同系统下的首屏加载时间
- 白屏时间
- http等请求的响应时间
- 静态资源整体下载时间
- 页面渲染时间
- 页面交互动画完成时间
可根据性能监控结果去分析前端性能可以优化的地方,比如兼容低版本的浏览器的动画效果,减少首屏加载时间等。可以直接使用浏览器自带的Performance API来实现这个功能。比如可直接调用performance.getEntriesByType('navigation')来获取页面中的各种详细的性能相关信息。
异常监控
前端代码在执行过程中难免发生异常,所以需要上报异常情况,避免线上故障的频繁发生。常见的有JavaScript的异常监控和样式丢失的异常监控。
大部分异常可以通过try catch方式捕获,但是比如内存泄露以及其他偶现的异常难以捕获。还有通常使用window.onerror拦截报错,但也有例外
- 对于跨域的代码运行错误会显示Script Error。需使用crossorigin添加到script标签上。
- 对于某些浏览器可能不会显示调用栈信息,这种情况可以通过 arguments.callee.caller 来做栈递归。
实现的方式(采集)
前端埋点
实现的方式主要是页面埋点,大致分为手写埋点、可视化埋点和无埋点的方式。
手动埋点
这种方式是最常用的,可自由选择要监控的数据,并将监控代码写入相应的地方。优点是比较灵活,可自主选择,精细化定义分析。缺陷就是工作量大,需要埋点的地方多的话,需要产品开发运营反复沟通,时间成本很高,效率很低。如果某次有埋点更新,或者漏埋点,都需要走发布流程,更新成本高,对系统稳定性也有伤害。
可视化埋点
这种方式是为了代替代码手动埋点,将业务代码和埋点代码分离,提供可视化交互界面。缺点是可埋点的控件有限,不能手动定制。
无埋点
无埋点是前端自动采集全部事件,上报埋点数据,然后有后端过滤出有用的信息。优点是使用方便快捷,前端只需加载一个脚本。缺点是流量和采集的数据过于庞大,服务器的性能压力变大。
Script Error
Script Error 不是一种具体的错误,而是浏览器对跨域错误出于安全机制考虑的一种处理方式。这时我们需要在script标签上添加crossorigin属性,crossorigin 生效需要服务器端和浏览器端同时支持。服务器端支持比较简单,即返回跨域脚本的服务器(一般为 CDN 服务器)正确的带上 CORS 响应头 Access-Control-Allow-Origin: * 即可,目前常见的 CDN 服务均支持这一特性。而浏览器端的crossorigin支持性不容乐观,具体参考crossorigin支持性。
另一种解决方案是通过 Patch 原生方法来尽可能的捕获到错误,这也是很多监控脚本默认提供的能力。比如说我们可以通过如下代码来 Patch 原生的 setTimeout 方法:
const prevSetTimeout = window.setTimeout;
window.setTimeout = function(callback, timeout) {
const self = this;
return prevSetTimeout(function() {
try {
callback.call(this);
} catch (e) {
// 捕获到详细的错误,在这里处理日志上报等了逻辑
// ...
throw e;
}
}, timeout);
} 同理,我们还可以 Patch 更多的原生方法,比如 Array.prototype.forEach、setInterval、requestAnimationFrame等等。诚然这种方法能帮我们尽可能捕获到更多异常,但是因为 Patch 了 JavaScript 原生的方法,总是感觉会存在很多的不确定性。
框架层面
在不少现代前端框架中,都提供了框架层的异常处理方案,比如 AngularJS 的 ErrorHandler、 Vue 的 Vue.config.errorHandler和 React 16 的 componentDidCatch,都可以使用框架的能力采集错误。