前端监控
为什么要做前端监控?
- 更快发现问题和解决问题
- 做产品的决策依据
- 为业务扩展提供了更多可能性
指标数据监控
- 异常监控(稳定性):js异常 promise异常 资源加载异常 (script或者link标签资源异常)
初始化参数
稳定性监控
- 对于js错误监控,一般js运行时错误 通过监听window.onerror捕获错误
- 需要获取到的错误信息有:(从错误对象中提取信息)
-
- 当前页面url地址
-
- 用户的浏览器类型
-
- 错误类型
-
- 错误详情
-
- 错误的行列信息
-
- 错误的堆栈信息
-
- 选择器信息 (先定位到最后一个触发事件 通过target.path获取路径 有id优先取id,没有id取className没有className取tagName)
- 通过事件对象上的是否存在error对象可以判断是js错误还是一些静态资源加载异常。
- 对于promise监控,监控promise错误分为两种:
-
- 通过promise.reject异常拦截,通过监听unhandledrejection方法
-
- 通过promise对象catch方法拦截,通过监听rejectionhandled方法
- 当Promise被reject且没有reject处理器的时候,会触发unhandledrejection事件
if (listenPromiseError) {
window.addEventListener(
'unhandledrejection',
function (e) {
const errObj = {
name: e.type,
message: JSON.stringify(e.reason) || '',
stack: JSON.stringify({
info: 'promise.reject异常拦截',
}),
};
sendErrorException(errObj, sendParams);
},
true
);
window.addEventListener(
'rejectionhandled',
(e) => {
const errObj = {
name: e.type,
message: JSON.stringify(e.reason) || '',
stack: JSON.stringify({
info: 'promise对象catch数据拦截',
}),
};
sendErrorException(errObj, sendParams);
},
true
);
}
- 接口错误监控
- 接口错误监控可以对请求方法做拦截,如xhr的open和send方法
- 下面简单实现了一下对XMLHttpRequest的拦截过程
- 然后再通过addEventListener来监听对应的触发事件如load, error,abort
export function injectXHR() {
let XMLHttpRequest = window.XMLHttpRequest;
let oldOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (method, url, async, username, password) {
this.logData = {
method, url, async, username, password
}
return oldOpen.apply(this, arguments);
}
let oldSend = XMLHttpRequest.prototype.send;
let start;
XMLHttpRequest.prototype.send = function (body) {
if (this.logData) {
start = Date.now();
let handler = (type) => (event) => {
let duration = Date.now() - start;
let status = this.status;
let statusText = this.statusText;
tracker.send({
kind: 'stability',
type: 'xhr',
eventType: type,
pathname: this.logData.url,
status: status + "-" + statusText,
duration: "" + duration,
response: this.response ? JSON.stringify(this.response) : "",
params: body || ''
})
}
this.addEventListener('load', handler('load'), false);
this.addEventListener('error', handler('error'), false);
this.addEventListener('abort', handler('abort'), false);
}
oldSend.apply(this, arguments);
};
- 监控白屏
-
- 定义白屏时长
- 是指浏览器从响应用户输入网址地址,到浏览器开始显示内容的时间。
- 白屏时间 = 页面开始展示的时间点 - 开始请求的时间点
- window.performance.timing.responseStart -window.performance.timing.navigationStart
- 可以定义一个白屏时长 如果白屏时间大于这个时长就上报
-
- 使用webAPI 的 MutationObserver 属性监控根元素内的dom数量,如果没有dom则白屏
const targetNode = document.getElementById("container");
if (targetNode) {
const callback = function (mutationsList) {
console.log(mutationsList, "11111");
for (let i = 0; i < mutationsList.length; i++) {
let mutation = mutationsList[i];
if (mutation.type === "childList" && targetNode?.children?.length === 0) {
console.log("白屏");
break;
}
}
};
const config = { childList: true };
if (window.MutationObserver) {
const observer = new MutationObserver(callback);
observer.observe(targetNode, config);
}
}
const childDiv = document.createElement("div");
targetNode.appendChild(childDiv);
-
- 对页面上的9个点分横纵进行判断是否有元素,如果18次判断全部都没有检索到元素,可以断定发生白屏现象
export function blankScreen() {
const wrapperSelectors = ["body", "html", "#container", ".content"];
let emptyPoints = 0;
function isWrapper(element) {
let selector = getSelector(element);
if (wrapperSelectors.indexOf(selector) >= 0) {
emptyPoints++;
}
}
onload(function () {
let xElements, yElements;
debugger;
for (let i = 1; i <= 9; i++) {
xElements = document.elementsFromPoint(
(window.innerWidth * i) / 10,
window.innerHeight / 2
);
yElements = document.elementsFromPoint(
window.innerWidth / 2,
(window.innerHeight * i) / 10
);
isWrapper(xElements[0]);
isWrapper(yElements[0]);
}
if (emptyPoints >= 0) {
let centerElements = document.elementsFromPoint(
window.innerWidth / 2,
window.innerHeight / 2
);
tracker.send({
kind: "stability",
type: "blank",
emptyPoints: "" + emptyPoints,
screen: window.screen.width + "x" + window.screen.height,
viewPoint: window.innerWidth + "x" + window.innerHeight,
selector: getSelector(centerElements[0]),
});
}
});
}