Skip to main content

One post tagged with "监控"

View All Tags

· 28 min read
刘述新

Docusaurus Plushie

Hello 大家好~~,今天给大家带来的分享是关于如何从零实现一个前端监控 SDK,(前段时间参与了性能监控平台的共建,有一些自己的心得,故此分享出来,共同进步)

接下来我将通过以下几个步骤带大家完成前端监控 SDK

一、 为什么要做前端监控,都需要监控什么?

场景:

  1. 客户手机端白屏了,系统无法使用,直到领导找到你,你紧张的汗都下来了,焦急的查着代码,但没有头绪
  2. 点击某个按钮在手机端的执行逻辑存在兼容性,而你缺不知道,导致部分客户流失
  3. 接口请求耗时较多,用户体验差,我们却全然不知

都需要做哪些监控

  • ✏️ 前端错误
  • 🔧 页面性能
  • ✏️ API 性能

知道了为什么要做监控,以及要监控 什么,那么我们接下来看要要如何实现,各类监控呢

二、 如何实现各类监控?

1、前端错误监控

Preview

如上图所示,前端错误捕获方式,主要分为以上四大类,

第一种是我们经常会遇到过的 JS 运行时错误, 一般我们采用 window.onerror 方法就可以对其进行捕获,该方法会返回 5 个参数,分别是,
  • message:错误信息(字符串)。可用于 HTML onerror=""处理程序中的 event。
  • source:发生错误的脚本 URL(字符串)
  • lineno:发生错误的行号(数字)
  • colno:发生错误的列号(数字)
  • error:Error 对象(对象)

另种运行时错误是用户利用 console.error 打印出来的我们也要进行拦截捕获。

第二种 是页面中资源(诸如图片img,脚本script)加载错误,找不到资源啦,资源加载超时啦,可以通过 window.addEventListener('error', function(event) ) 进行捕获,返回的event 中,包含

  • localName 资源名称
  • type 资源类型
  • src 资源源地址

第三种是当Promise 被 reject 且没有 reject 处理器的时候,会触发 unhandledrejection 事件。可以通过 window.addEventListener('unhandledrejection', function(event) ) 进行捕获,返回的event 中,包含

  • reason reject 的原因

第四种是异步请求的错误,浏览器中发送ajax 请求共有两种方式分别是 XMLHttpRequest 和 Fetch, 所以相应的也分两种捕获类型。

  • 对于 XHR 我们可以通过监听它的 onerror 事件来捕获异常错误,通过监听 onabort 事件来监听中断请求异常,如果请求顺利返回,我们通过判断返回的状态码 status 是否是小于 200 或者大于等于 300 来判断这次请求是否失败。
  • 对于 Fetch 来说,我们可以通过重写 catch 方法来获得发生的错误,或者判断返回的 response.ok 是否为 false 来进行错误上报

2、页面性能监控

Preview
如上图所示,当页面load 之后我们通过调用window 上的performance.timing 对象上的相关属性进行计算,就可以获得我们想要的相关时间

3、API 性能监控

Preview
如上图所示,我们先来看

第一种情况,通过重写XMLHttpRequest对象的实现逻辑来达到监听,我们首先通过重写open 属性来进行注册请求基本信息,然后重写,onreadystatechange,onload,和onloaded 来监听请求成功返回, 接下来看

第二种情况Fetch,通过监听then 方法返回的response ,来获取status 和 获取响应大小

上报时机的思考

思考题

1、我们每次上报都需要发送请求吗?

2、如果需要进行合并,如何进行上报合并呢?

优化前:

common-report

解决方案

1、上报场景分为两类,初始化页面时,和后续ajax 操作

2、初始化页面:对ajax 请求,错误信息,分别进行缓存,在一定时间内,直到所有ajax 请求 都完成以及页面load,才进行整体上报

3、后续ajax 操作:对ajax 请求,错误信息,分别进行缓存,在一定时间内,直到所有ajax 请求 都完成,才进行整体上报

优化后:

common-report

三、 数据的上报和收集

首先上报方式分为两大类

  • SDK 监听特定事件,进行上报
  • 用户通过自己的技术手段(例如 try,catch,React ErrorBoundary ),通过调用 athena.addError(ErrorOptions); 来上报

上报类型分为三大类

  1. PAGE = 1 //页面级性能上报

  2. AJAX = 2 //页面 API 性能上报

  3. ERROR = 3 //页面内错误信息上报

info

接下来我们就针对具体场景进行分析

场景一:页面级性能上报

通过监听 load事件(标识页面资源已经加载完成)来进行上报, 以及将相关静态资源进行上报

🍎 1、 performance.timing

🥔 2、 performance.getEntriesByType('resource')

场景二:页面错误上报

通过监听一系列全局事件

🍎 1、 利用 window.addEventListener('error', listenErrorEvent, true); 捕获 img,script,css,jsonp 相关错误

🍌 2、 重写 window.onerror 用于捕获运行时错误

🍇 3、 当 Promise 被 reject 且没有 reject 处理器的时候,会触发 unhandledrejection 事件。window.addEventListener('unhandledrejection', unhandledrejectionEvent);

🍉 4、 重写console.error 方法调用

场景三:XMLHttpRequest 性能和错误上报

通过重写XMLHttpRequest 对象

🍎 1、 利用 window.realXhr = XMLHttpRequest; 缓存原始XMLHttpRequest对象

🍌 2、 重写 XMLHttpRequest 对象创建过程,

🍇 3、 重写方法 overwriteMethod ,重写属性 overwriteAttributes

XHR 相关事件

Preview

XHR 重写流程图

Preview

Hooks 示意图

Preview

场景四:Fetch 性能和错误上报

通过重写fetch 对象

🍎 1、 利用 window.realFetch = window.fetch; 缓存原始Fetch对象

🍌 2、 重写 fetch函数调用过程,

🍇 3、 重写fetch的then方法 ,处理成功上报 ,重写fetch 的 catch方法处理失败错误上报

Preview

完整上报协议

tip

对于前端错误和性能监控来说,保证数据的准确性和完整性是非常重要的,这非常依托上报数据的稳定性和明确性,所以让我们一起来看一下这份上报协议

1、ReportData
上报参数描述类型示例备注
appId应用唯一标识stringax123dd32233
domain上报的地址stringhttp://www.athena.com/test
url当前页面地址 urlstringhttp://www.teest.com/test
markUser用户唯一标识string102346
markUv统计 UVstring102346
preUrl该页面来自的域名stringhttp://www.athena.com
performance性能相关数据Perform
type上报类型number1PAGE = 1 页面级性能上报; AJAX = 2 页面 API 性能上报;ERROR = 3 页面内错误信息上报;
time上报时间戳number1638237891804
screenwidth屏幕宽度number1559
screenheight屏幕高度number327
isFristIn是否第一次进入页面booleanfalse
resourceList资源相关列表Array<ResourceList>
errorList错误列表Array<ErrorList>
2、ErrorList
上报参数描述类型示例备注
n错误类型stringresourcejs, resource,ajax // ajax 请求错误上报
t当前时间戳number1638240521575
msg错误信息string[React Intl] Missing message: \"menu.manage-group\" for locale: \"en-US\", using default message as fallback.//
method请求方法stringGET
data附加信息ErrorData
{resourceUrl: "http://localhost:8000/home/15/group"} 
3、ErrorData
上报参数描述类型示例备注
target资源名称stringscriptscript, link
type类型stringerror
resourceUrl报错资源地址string"http://localhost:4005/face/app.css"
line报错触发所在行number200
col报错触发所在列number12
stack错误栈信息string
text响应信息stringOK
status响应状态码number200
4、Perform
上报参数描述类型示例备注
dnstDNS 解析时间number
tcptTCP 建立时间number
wit白屏时间number
domtdom 渲染完成时间number
lodt页面 onload 时间number
radt页面准备时间number
rdit页面重定向时间number
uodtunload 时间number
reqtrequest 请求耗时number
andt页面解析 dom 耗时number
5、ResourceList
上报参数描述类型示例备注
name资源名称stringhttp://localhost:8000/@@/devScripts.js
method请求方法methodGET
type资源类型stringscriptscript,img,xmlhttprequest, fetchrequest
duration加载时间string55.90
decodedBodySize资源大小number79
nextHopProtocol协议名称string
options其他项ResourceOptions{ }
6、ResourceOptions
上报参数描述类型示例备注
resourceUrl资源名称stringhttp://localhost:8000/@@/devScripts.js
text响应状态文本stringOK
status响应码number200200,400...

四、 SDK 整体架构介绍

基于以上的前提,故此产生了一个完整的 SDK 架构如下

Preview

五、 共享-输出 npm & 官网建设

输出 npm

info

那么我们如何把我们的成果与别人共享呢,让别人能够更快捷的接入呢? 故此我们封装成一个 npm 包来供大家使用

  1. 构建工具选择 TSDX:https://github.com/jaredpalmer/tsdx
  2. 构建工具支持 Rollup 进行库打包,可以输出三种模块类型文件,cjs,esm,umd

官网建设

info

建立官网有助于产品的宣传与接入,如何选择更高效的创建官网的工具,就很重要,故此,我调研了市面上存在的各类静态网站生成器方案如下

方案简介优点缺点地址GitHub Star
VuePressVuePress 是一款由 vue 驱动的,markdown 为项目结构中心的高性能静态网站生成器,是 vue 生态的组成部分,对集成 vue 项目的 API 文档比较契合支持 Vue 定制化对 React 不友好https://github.com/vuejs/vuepress19.8k
Docusaurus 2Docusaurus 是 Facebook 开源的基于 React 的静态网站生成器支持 React 定制化Vue 不友好https://github.com/facebook/docusaurus29.5k
GitBookGitBook 是一款可多人协作的文档平台,项目结构清晰,适合编写 API 接口文档以及内部知识库,简单快速UI 层面的自定义较困难https://github.com/GitbookIO/gitbook24.3k
GatsbyGatsby Gatsby 不仅仅是一个静态网站生成器,它更是一个渐进式 Web 应用生成器灵活,支持 graphQL,对 React 友好太灵活,不易上手https://github.com/gatsbyjs/gatsby52.1k

基于灵活性,技术栈,上手难度,所以我们选择了 Docusaurus 作为我们官网的建站工具,也就是这篇博客所在的页面