前言
为了更好地进行数据统计,更加深入地获取应用运行的状态,监控线上异常。
我们想做一个针对 http 数据请求的埋点监听。通过监听应用的中的 http 数据请求,和往常业务数据进行对比,从而实现对线上业务的异常监控。
目标
为了实现这个线上监控的目的,我们要先制定需要采集的数据。
目前主流采集的数据有两个思考方向,一是从响应速度,二是响应质量。响应速度方面,我们经常采集的数据有,http 请求时间、结束时间、持续时间;响应质量这块,我们常采集的数据有 http 状态码、请求的接口、当前请求发出的页面。
方案设计
无论你是用当前流行的SWR
或者axios
请求库,亦或是用例如next.js
、umi.js
自带的请求库。其底层异步请求都是xhr、fetch支撑的。
所以我们仅需要从xhr
和fetch
入手就能实现的我们的目的。
xhr
全称是XMLHttpRequest
,它利用这个对象进行 ajax 请求。一个XMLHttpRequest
对象在发送请求时,必定要经过创建请求实例、发送请求这两个阶段。而这两个阶段又分别对应它实例里的open
和send
方法,在这里我们可以选择其中一个阶段进行我们的埋点操作。这个例子中我选择在open
阶段进行计时。
到这一步,思路就很清晰了。我们可以直接在XMLHttpRequest
对象的原型上重写open
方法,来实现我们的目的。
fetch
是基于Promise
的异步网络请求,它的底层并不使用XMLHttpRequest
对象,所以对它我们需要额外进行处理。
根据上面封装xhr
的思路,我们可以对fetch
进行重写,把它重新包装成一个Promise
来达到我们的目的。
具体代码如下:
let isCalled = false;
const sendData = data => data;
const httpMonitor = () => {
if (isCalled) return;
isCalled = true;
// 处理XHR请求
if (XMLHttpRequest?.prototype?.open) {
const originOpen = XMLHttpRequest.prototype.open;
const start = new Date().getTime();
XMLHttpRequest.prototype.open = function (...args) {
this.addEventListener('loadend', () => {
const end = new Date().getTime();
const data = {
start,
end,
duration: end - start,
httpStatus: this.status,
reqUrl: args[1],
reqPage: window?.location?.href
};
sendData(data);
});
this.addEventListener('timeout', () => console.log('xhr timeout'));
originOpen.apply(this, args);
};
}
// 处理fetch请求
if (window?.fetch) {
const originFetch = window.fetch;
window.fetch = function (...args) {
const start = new Date().getTime();
let end = start;
return new Promise((resolve, reject) => {
originFetch.apply(this, args).then(
response => {
end = new Date().getTime();
resolve(response);
},
error => {
end = new Date().getTime();
reject(error);
}
);
}).then(
res => {
const { code = 200, url = '' } = res || {};
const data = {
start,
end,
duration: end - start,
httpStatus: code,
reqUrl: url,
reqPage: window?.location?.href
};
sendData(data);
return res;
},
err => err
);
};
}
};
httpMonitor();
export { httpMonitor };