前端工程的异常信息收集

前言

在项目的前端工程中,我们通常会做一些异常的容错处理和异常信息收集埋点,以便快速的定位一些工程中在开发中难以预见或因为粗心而产生的一些Bug,或者为了便于优化整体项目以便给用户带来最佳的使用体验。

比如我们代码中常见的 try-catch 或者Promise中的因reject情况下编写的catch逻辑代码,这种防御式编程都是对项目的容错处理,为了避免造成程序上的功能损坏。但通常这种代码都是开发人员可预见性的错误,自然做了相应处理,除此之外,一些难以预见的错误要怎么办? 那么就有了今天的主题,前端工程的全局异常信息处理收集:通过程序报错的堆栈信息去收集异常。

全局异常信息收集方法(Vue工程)

window.onerror

window.onerror

描述

window.onerror 能捕获未被其他 try…catch 块处理的同步 JavaScript 错误。这包括代码中的语法错误、运行时错误,以及外部脚本中的错误等。

参数

1、 errorMessage: 错误信息或描述
2、 scriptURI: 发生错误的脚本 URL
3、 lineNumber: 错误发生的行号
4、 columnNumber: 错误发生的列号
5、 errorObj: 错误详情

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    window.onerror = function(errorMessage, scriptURI, lineNumber,columnNumber,errorObj) { 
console.log('window.onerror错误');
console.log("错误信息:" , errorMessage);
console.log("出错文件:" , scriptURI);
console.log("出错行号:" , lineNumber);
console.log("出错列号:" , columnNumber);
console.log("错误详情:" , errorObj);
const stackTrace = (errorObj && errorObj.stack) || '';
const functionNameMatch = stackTrace.match(/at\s+(\S+)\s+\(/);
const functionName = functionNameMatch ? functionNameMatch[1] : 'Unknown function';
console.log('函数名:', functionName);
}

var testFunction = function() {
console.log(a);
};
testFunction();

在上述代码中,我们定义了一个testFunction,并且答应了一个为定义的变量a,当我们执行上述代码时,这个错误就会被window.onerror捕获,同时,我们对捕获的参数信息做了额外处理,通过正则匹配的方式,能定位到具体报错的方法(如图)。如果此时你的工程中接入了埋点,那么可以在此将异常的信息和方法名上报给埋点系统处理。

window.addEventListener(‘error’, function (errorEvent) {} )

window.addEventListener(‘error’, function (errorEvent) {})

描述

window.addEventListener(‘error’) 事件是浏览器提供的另一个全局错误处理机制,用于捕获页面中未被捕获的错误事件。相比 window.onerror,window.addEventListener(error) 提供了更细粒度的控制,并且能够处理更多类型的错误,包括资源加载错误

参数

1、 ErrorEvent: 错误事件对象

  • message: 错误消息的描述文本
  • filename: 发生错误的文件的 URL
  • lineno: 错误发生的行号
  • colno: 错误发生的列号
  • error: 错误详情
  • target: 触发错误的 DOM 元素(仅在资源加载错误时有用)
  • currentTarget: 调用事件处理器的元素,通常与 target 相同
  • type: 事件的类型(对于错误事件始终为 ‘error’)

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    window.addEventListener('error', function (errorEvent) {
console.log('window.addEventListener报错', errorEvent);
console.log("Error occurred:", errorEvent.message);
console.log("Source:", errorEvent.filename);
console.log("Line number:", errorEvent.lineno);
console.log("Column number:", errorEvent.colno);
console.log("Error object:", errorEvent.error);
console.log("Resource URL:", errorEvent.target?.src || errorEvent.target?.href);
console.log("Type:", errorEvent.type);
});
var testFunction = function() {
console.log(a);
};
testFunction();

现在我们将window.onerror的代码做个替换,发现一样可以实现对错误信息的捕获。接下来,我们验证一下两个方法对资源加载的报错收集:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<body>
<img src="a.jpg" />
<script>
window.onerror = function(message, source, lineno, colno, error) {
console.log('window.onerror:', message, 'at', source, 'line:', lineno, 'column:', colno);
return true;
};

window.addEventListener('error', function(event) {
if (event.target !== window) {
console.log('Resource loading error:', event.target.src || event.target.href);
} else {
console.log('Global error:', event.message);
}
});
</script>
</body>

同样的js等资源的加载出现问题也会被此方法捕获,有兴趣的同学可以尝试一下。

window.addEventListener(‘unhandledrejection’, function (errorEvent) {} )

window.addEventListener(‘unhandledrejection’, function (errorEvent) {} )

描述

window.addEventListener(‘unhandledrejection’) 用于监听 JavaScript 中未被捕获的Promise拒绝(即.catch 未处理的Promise 错误)。它提供了一种捕获全局 Promise 拒绝的机制。

参数

1、event

  • reason: 拒绝的原因
  • promise: 产生拒绝的 Promise
  • type: 事件的类型(对于未处理的Promise拒绝事件始终为 ‘unhandledrejection’)

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
window.addEventListener('unhandledrejection', function(event) {
console.error('Unhandled promise rejection captured:');
console.error('Reason:', event.reason);
console.error('Promise:', event.promise);
});

// 模拟未捕获的 Promise 拒绝
function test() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('This is an unhandled promise rejection!');
}, 1000);
});
}
test();

可以看到,当Promise被reject时,unhandledrejection事件被触发,并捕获到错误信息。

Vue.config.errorHandler

Vue.config.errorHandler

描述

Vue.config.errorHandler 是 Vue.js 提供的一个全局错误处理钩子,可以用来捕获和处理组件生命周期钩子、指令、渲染函数等代码中的错误。
通常可以捕获以下错误:

组件生命周期钩子:如 created、mounted 等

模板渲染错误:当模板渲染出错时

事件处理函数:当 Vue 组件的事件处理函数抛出错误时

自定义指令:自定义指令中的错误

watch 回调:观察属性的回调函数中的错误

参数

  • err: Error 对象,表示捕获的错误
  • vm: Vue 实例,在哪个 Vue 实例上发生了错误
  • info: 字符串,Vue 特定的错误信息,包含发生错误的生命周期钩子、事件等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

//vue2
import Vue from 'vue';
Vue.config.errorHandler = function (err, vm, info) {
console.error('Error:', err);
console.error('Vue instance:', vm);
console.error('Info:', info);
// 这里可以执行其他的错误处理逻辑,如日志记录、用户提示等
};

//vue3
import { createApp } from 'vue'
app.use(pinia)

app.config.errorHandler = (err, instance, info) => {
console.error('Error captured:', err);
console.error('Vue instance:', instance);
console.error('Info:', info);
// 这里可以执行其他的错误处理逻辑,如日志记录、用户提示等
};
app.mount('#app')

我使用Vue3的项目进行测试,在set up内打印一个不存在的变量,也可以正常的被捕获到。

总结

以上,就是总结在Vue项目工程中,全局异常信息捕获的几种方法,基本可以覆盖到整个项目中容易出现错误的地方,在这几种方法中,我们可以维护对应的逻辑,增加系统日志或上报埋点,减少排查问题的时间及增加程序的健壮性。如果有不对的地方欢迎大家指正。