开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天第 1 篇,点击查看活动详情
书接上回
距离上次写前端监控的复盘都好几个月了,赶紧补上,上一回我们编写了一些我们前端监控会用到的小轮子,这些轮子在后面的代码中经常会使用到,也是在平时的工作中经常会用到的小技巧,希望在之后的参考代码中突然看到轮子的使用时,不要想不起来它是干啥的。为我们前端系统做了一个前期的准备,接下来我们就要正式开始编写前端监控代码了,我们就从最让程序猿头疼的bug开始,前文有说到前端监控定位错误的作用,那我们就先来完成监控js错误的部分,我们监控的JS错误主要分为三个部分:JS运行时错误、未捕获的promise错误、console.error自定义错误。至于资源请求错误我们放在资源请求的监控中讲。
JS运行时错误
这个比较简单,JS产生运行时语法错误时会触发window.onerror这个回调方法,那么我们重写他就能完成监听了
参考代码
// 监听js错误
window.onerror = function(...argumentList) {
let [message, filename, row, col, error] = argumentList;
let lastEvent = getLastEvent(); // 获取到最后一个交互事件
console.log('jserror',lastEvent)
tracker.send({
type: "jsError", // js执行错误
message, // 报错信息
filename, // 文件名
position: `${row}:${col}`, // 报错的行列位置
stack: getLines(error?.stack),
selector: lastEvent ? getSelector(lastEvent.path) : "", // 代表最后一个操作的元素
});
}
数据模型:
{
'...':'...',//公共字段
"type": "jsError",
"message": "Uncaught TypeError: Cannot set property 'error' of undefined",
"filename": "http://localhost:8080/",
"position": "0:0",
"stack": "btnClick (http://localhost:8080/:20:39)^HTMLInputElement.onclick (http://localhost:8080/:14:72)",
"selector": "HTML BODY #container .content INPUT"
}
未捕获的promise错误
前端请求错误时经常会出现promise错误未捕获,如果出现了,会触发window.onunhandledrejection方法的回调。也就是说,要想监控未捕获的promise错误,我们就可以重写这个方法,当然也可以添加监听器,道理是一样的,对于错误原因的解析可能会有疑惑的小伙伴打印一下它的参数就明白了
参考代码
window.addEventListener(
"unhandledrejection",
(event) => {
let lastEvent = getLastEvent(); // 获取到最后一个交互事件
let message;
let filename;
let line = 0;
let column = 0;
let stack = "";
let reason = event.reason;
if (typeof reason === "string") {
message = reason;
} else if (typeof reason === "object") {
message = reason.message;
if (reason.stack) {
let matchResult = reason.stack.match(/at\s+(.+):(\d+):(\d+)/);//正则匹配分割数据
filename = matchResult[1];
line = matchResult[2];
column = matchResult[3];
}
stack = getLines(reason.stack);
}
tracker.send({
type: "promiseError", // promise错误
message, // 报错信息
filename, // 哪个文件报错了
position: `${line}:${column}`, // 报错的行列位置
stack,
selector: lastEvent ? getSelector(lastEvent.path) : "", // 最后一个操作的元素
});
},
true
);
}
数据模型
{
'...':'...',//公共字段
"type": "promiseError",
"message": "Cannot set properties of undefined (setting 'error')",
"filename": "http://127.0.0.1:8080/src/views/test/js.vue?t=1661750861620",
"position": "12:30",
"stack": "http://127.0.0.1:8080/src/views/test/js.vue?t=1661750861620:12:30^new Promise (<anonymous>)^Proxy.promiseErrorClick (http://127.0.0.1:8080/src/views/test/js.vue?t=1661750861620:11:7)^_createElementVNode.onClick._cache.<computed>._cache.<computed> (http://127.0.0.1:8080/src/views/test/js.vue?t=1661750861620:56:63)^callWithErrorHandling (http://127.0.0.1:8080/node_modules/.vite/deps/chunk-K52QI2KL.js?v=e1179f47:183:18)^callWithAsyncErrorHandling (http://127.0.0.1:8080/node_modules/.vite/deps/chunk-K52QI2KL.js?v=e1179f47:191:17)^HTMLInputElement.invoker (http://127.0.0.1:8080/node_modules/.vite/deps/chunk-HTBBOVTW.js?v=e1179f47:312:7)",//非必要
"selector": "html body div#app div#container div#content input#promiseErrorBtn",
}
console.error自定义错误
console.error监控就很直白,他本身就是调用console.error输出的报错,所以我们可以选择给这个方法做一个代理,当然也可以用直接重写,但是我看网上的有教程说重写他的目的并不是为了监听console.error而是因为页面刚加载时window.onerror监听不生效,所以重写console.error在它不生效的阶段取代它,但是我并没有发现它能起到这样的作用,索性我们在这了直接用来监控自定义错误。
参考代码
const oldError = console.error;
console.error = new Proxy(oldError, {
apply: function (target, thisArg, ...argumentList) {
tracker.send({
type: "customError", // 资源获取错误
message: argumentList[0], // 报错信息
});
return target.apply(thisArg, argumentList);
}
}
)
数据模型
{
'...':'...',//公共字段
"type": "custumError",
"message": "Cannot set properties of undefined (setting 'error')",//报错信息
}
总结
好,对于JS错误的监控就到这里,最后一部分的对console.error的监控写的比较粗糙,有兴趣的同学可以多加改进,下一步我们就来写对于静态资源的监控吧,敬请期待。最后再打个广告,关注公众号程序猿青空,免费领取191本计算机领域黑皮书电子书,更有集赞活动免费挑选精品课程(各个领域的都有),不定期分享各种优秀文章、学习资源、学习课程,能在未来(因为现在还没啥东西)享受更多福利。