注:

  • 2020年10月22日
    • 补充了技术细节,合并类似重复文章
    • 补充相关话题索引。

1 基本概念

本节介绍什么是前端监控,监控什么,监控原理。

什么是前端监控

提起前端监控我们在说什么?一般是指三个方面:

类型 场景 技术实现
前端错误 用户浏览器报错 捕获错误上报
用户行为 用户行为轨迹 埋点上报
页面性能指标 页面渲染情况 关键节点上报

本文主要介绍 前端错误监控

前端有什么错误

前端项目运行时候会发生bug,大致分为几类:

  • 前端逻辑错误,比如类型错误、引用错误
  • 接口请求异步 async/await 错误
  • 资源加载失败

捕获的方式也不尽相同。

如何捕获错误

这里我配合做了一个Git仓库辅助说明,很多技术细节代码中提及。

仓库地址:web-error-demo

js运行出现的错误

我们可以通过 window.onerror 来拦截大部分错误

资源加载的错误

因为不会冒泡,可以通过 window.addEventListener('error') 来捕获

跨域代码报错会提示 script error ,需要给 script 标签添加 crossorigin 属性

异步代码

  • 统一在 try/catch promise.catch 中捕获
  • 异步错误通过 unhandledrejection 来统一收底

网络请求

考虑重写 xhr.prototypefetch ,项目中可以通过 axios 拦截器来统一处理,这意味着我们需要提前做好判断,不在业务逻辑中处理错误。

Vue框架

如果是Vue项目,可以设置全局的选项: errorHandler 来处理组件渲染和运行期间未捕获的错误。

  1. Vue.config.errorHandler = function (err, vm, info) {
  2. // handle error
  3. // `info` 是 Vue 特定的错误信息,比如错误所在的生命周期钩子
  4. // 只在 2.2.0+ 可用
  5. }

此处可索引我的读书笔记: 书籍《从零开始搭建前端监控平台》

错误处理

经过排查发现,Chrome和Firefox对error的包装不同,这里涉及到了大量的兼容代码。

err:

  • line colum 和 columnNumber
  • col line 和 lineNumber
  • err.name
  • stack 字符串截取

通过前端构建会压缩混淆代码,这里需要用到 source-map 插件来做映射,通过输入错误信息回溯到源码位置。

2 处理上报

我们捕获了来自用户的错误,应当上报给远程,统一入库。

先从简单的概念说起。

上报错误信息一般使用三种:

技术选型 场景 备注
get 简单信息量小,简单
post 长度无限制,可复杂数据
head 只上报无返回 充分利用http特性
new Image() 简单,有效 无视跨域
sendBacon 关闭、跳转页面,侵入低 新特性,特点明显

其中 head 是HTTP请求方法中不太常用的一种,特点是没有返回体。恰巧我们也不在乎返回结果。

其中 sendBeacon 值得一说,很有特点。MDN文档在此

有一些技术被用来保证数据的发送。其中一种是通过在卸载事件处理器中创建一个图片元素并设置它的 src 属性的方法来延迟卸载以保证数据的发送。因为绝大多数用户代理会延迟卸载以保证图片的载入,所以数据可以在卸载事件中发送。另一种技术是通过创建一个几秒钟的 no-op 循环来延迟卸载并向服务器发送数据。— MDN

用法很简单,在任意时间发起请求:navigator.sendBeacon(url, data);

特点:异步非阻塞,不竞争网络资源+ 可靠

信标请求可以有效地合并,以优化移动设备上的能量使用。

主要解决 beforenload unload 时候的异步请求会被忽略的问题。

异步、可靠。当然了 ie不兼容。

web.dev 是这样写的:

  1. import {getCLS, getFID, getLCP} from 'web-vitals';
  2. function sendToAnalytics({name, value, id}) {
  3. const body = JSON.stringify({name, value, id});
  4. // Use `navigator.sendBeacon()` if available, falling back to `fetch()`.
  5. (navigator.sendBeacon && navigator.sendBeacon('/analytics', body)) ||
  6. fetch('/analytics', {body, method: 'POST', keepalive: true});
  7. }
  8. getCLS(sendToAnalytics);
  9. getFID(sendToAnalytics);
  10. getLCP(sendToAnalytics);
  1. window.addEventListener('unload', logData, false);
  2. function logData() {
  3. navigator.sendBeacon("/log", analyticsData);
  4. }

beforeunload和unload 事件都不可靠(尤其是在移动设备上),并且不建议使用 它们(因为它们可以阻止页面有资格使用Back-Forward Cache)。

对于跟踪页面整个生命周期的指标,最好在visibilitychange事件的可见性状态变为时发送事件期间指标的当前值hidden。这是因为,一旦页面的可见性状态更改为,就hidden无法保证该页面上的任何脚本都能再次运行。在移动操作系统上尤其如此,在移动操作系统中,浏览器应用程序本身可以关闭,而无需触发任何页面回调。

3 前端监控后端选型

  • 自研。上报日志可选用 阿里云提供的日志服务,少量足够了。
  • 阿里云提供 应用实时监控服务ARMS ,可以用来监控前端,具体看文档 介绍
  • 无敌的Sentry,开源、知名、私有部署。这里放一个我总结的文章 Sentry