【监控】错误监控的手段

错误监控的手段

相关文章

错误监控的手段


一、什么是错误监控

错误监控是指在前端应用中收集、分析和监控 JavaScript 错误、HTTP 错误等错误信息的过程。通过错误监控,可以及时发现并解决前端应用中的错误,提高用户体验和应用稳定性。以下是对错误监控的详细介绍:

二、错误类型

  • JavaScript 错误: 包括语法错误、运行时错误和未捕获的异常。例如,代码中的语法错误、变量未定义、空指针异常等。
  • HTTP 错误: 发生在网络请求过程中的错误,包括请求超时、404 Not Found、500 Internal Server Error 等。

三、如何捕获

捕获JS语法错误、运行时错误

通过在全局范围内监听 JavaScript 的错误事件,如 window.onerror 和 window.addEventListener(‘error’),可以捕获并处理未被捕获的 JavaScript 错误。这种方法可以用于捕获语法错误、运行时错误等。

window.onerror 和 window.addEventListener(‘error’)区别

  • 捕获的错误类型

    • window.onerror 只能捕获全局的未捕获的 JavaScript 错误,包括语法错误、运行时错误和未捕获的异常。
    • window.addEventListener(‘error’, listener) 可以捕获更多类型的错误,包括资源加载错误(如图片加载失败、CSS 加载失败等)、跨域脚本错误等。
  • 事件冒泡

    • window.onerror 无法阻止错误事件冒泡到浏览器的默认错误处理机制。如果你返回 true(或者一个字符串)来表示你已经处理了错误,错误仍然会冒泡并被浏览器默认的错误处理机制处理。
    • window.addEventListener(‘error’, listener) 允许你调用 event.preventDefault() 来阻止错误事件的默认行为。这意味着你可以完全控制错误的处理方式,并且阻止浏览器默认的错误处理机制。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Page Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
// 监听全局错误事件
window.addEventListener("error", function (event) {
// 收集错误信息
const message = event.message; // 错误消息
const source = event.filename; // 出错文件路径
const lineno = event.lineno; // 出错行号
const colno = event.colno; // 出错列号
const error = event.error; // 错误对象

// 将错误信息上报到控制台
console.error("【监控】GET Error");
console.error("【监控】Message:", message);
console.error("【监控】Source:", source);
console.error("【监控】Line:", lineno);
console.error("【监控】Column:", colno);
console.error("【监控】Error Object:", error);
});

// 模拟一个出错的情况
function triggerError() {
undefinedFunction(); // 触发一个未定义的函数,抛出错误
}

// 在某个事件或者条件下调用触发错误函数
triggerError();
</script>
</head>
<body></body>
</html>

输出

捕获资源加载错误

由于路径问题、网络问题、服务器问题或资源文件本身问题等原因导致的资源加载失败情况的方法。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Page Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<script>
// 监听全局错误事件
window.addEventListener(
"error",
function (event) {
console.error("【监控】GET Error");
console.error("【监控】Type:", event.type); // 事件类型
console.error("【监控】Time:", event.timeStamp); // 时间戳

const errorSources = {
IMG: "src", // 图片路径
SCRIPT: "src", // 脚本路径
LINK: "href", // 样式表路径
};

const tagName = event.target.tagName;
if (tagName in errorSources) {
const sourceAttribute = errorSources[tagName];
const source = event.target[sourceAttribute];
console.error("【监控】Source:", source);
console.error("");
} else {
console.error("【监控】event:", event);
}
},
true
);

// 模拟一个图片出错的情况
function triggerImgError() {
const img = document.createElement("img");
img.src = "path/to/your/image.jpg"; // 不存在的路径
document.body.appendChild(img);
}
triggerImgError();

// 模拟一个脚本加载失败的情况
function triggerScriptError() {
const script = document.createElement("script");
script.src = "path/to/your/script.js"; // 不存在的路径
document.body.appendChild(script);
}
triggerScriptError();

// 模拟一个样式表加载失败的情况
function triggerStyleError() {
const link = document.createElement("link");
link.rel = "stylesheet";
link.href = "path/to/your/styles.css"; // 不存在的路径
document.head.appendChild(link);
}
triggerStyleError();
</script>
</body>
</html>

输出

捕获 Promise 错误

当一个 Promise 被拒绝(rejected)且没有被捕获时触发。unhandledrejection事件提供了一种机制来捕获在 Promise 链中没有被处理的错误,允许开发者在发生错误时采取适当的措施。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Page Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
// 监听 unhandledrejection 事件
window.addEventListener("unhandledrejection", function (event) {
// 从事件对象中获取被拒绝的 Promise 和拒绝原因
const { promise, reason } = event;

// 处理未被捕获的 Promise 错误
console.error("【监控】GET Error");
console.error("【监控】Type:", event.type); // 事件类型
console.error("【监控】Time:", event.timeStamp); // 时间戳
console.error("【监控】Promise:", event.promise); // 被拒绝的 Promise
console.error("【监控】Reason:", event.reason); // 拒绝原因
});

// 创建一个被拒绝的 Promise,但没有被捕获
const rejectedPromise = new Promise((resolve, reject) => {
reject(new Error("Unhandled Promise Error"));
});
</script>
</head>
<body></body>
</html>

输出

捕获网络请求错误

通过监控网络请求,可以捕获到资源加载失败、接口请求错误等情况。可以通过拦截XMLHttpRequest、fetch等方法来实现监控。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Page Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<script>
// 保存原始的XMLHttpRequest对象的open方法
var originalOpen = window.XMLHttpRequest.prototype.open;
// 保存原始的XMLHttpRequest对象的send方法
var originalSend = window.XMLHttpRequest.prototype.send;

// 拦截XMLHttpRequest的open方法
window.XMLHttpRequest.prototype.open = function(method, url) {
// 保存请求的URL
this.__url = url;
// 调用原始的open方法
originalOpen.apply(this, arguments);
};

// 拦截XMLHttpRequest的send方法
window.XMLHttpRequest.prototype.send = function() {
// 保存当前XMLHttpRequest对象的引用
var self = this;

// 添加error事件监听,捕获请求错误
this.addEventListener("error", function() {
console.error("【监控】 GET Error");
console.error("【监控】URL:", self.__url); // 请求的URL
console.error("【监控】Status:", self.status); // HTTP状态码
});

// 调用原始的send方法
originalSend.apply(this, arguments);
};

// 制造一个请求错误的示例
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://example.com/nonexistent", true);
xhr.send();
</script>
</body>
</html>

输出

捕获前端框架提供的错误

vue2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div>
<button @click="causeError">Click me to cause error</button>
</div>
</template>

<script>
export default {
methods: {
causeError() {
// 故意制造一个错误,例如调用一个未定义的函数
nonexistentFunction();
}
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
import Vue from 'vue'
import App from './App.vue'

Vue.config.errorHandler = function (err, vm, info) {
console.error('Vue Error:', err);
console.error('Vue Component:', vm);
console.error('Vue Error Info:', info);
};

new Vue({
render: h => h(App),
}).$mount('#app')

在这个全局配置中,我们将 Vue.config.errorHandler 设置为一个函数,该函数接收三个参数:err 表示错误对象,vm 表示出错的 Vue 实例,info 表示一个 Vue 特定的错误信息,比如在模板渲染期间出错时的错误信息。在错误处理函数中,我们可以对错误进行适当的处理,比如打印错误信息、上报错误到服务器等。

vue3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { createApp } from 'vue';
import App from './App.vue';

const app = createApp(App);

// 全局错误处理函数
const errorHandler = (error, vm, info) => {
console.error('Vue Error:', error);
console.error('Vue Component:', vm);
console.error('Vue Error Info:', info);
};

// 在全局事件监听器中注册全局错误处理函数
window.addEventListener('error', errorHandler);

app.mount('#app');

react类组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import React, { Component } from 'react';

// 创建一个错误边界组件
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}

// 捕获子组件中的错误
componentDidCatch(error, info) {
this.setState({ hasError: true });
// 这里可以将错误信息记录到日志中或进行其他处理
console.error('Error caught by error boundary:', error);
}

render() {
if (this.state.hasError) {
// 在出现错误时渲染备用UI
return <h1>Something went wrong.</h1>;
}
// 如果没有错误,正常渲染子组件
return this.props.children;
}
}

// 错误边界的使用示例
class MyComponent extends Component {
render() {
// 在这个示例中,意图是制造一个错误
// 这个错误会被ErrorBoundary捕获并处理
return (
<ErrorBoundary>
{/* 在这个子组件中制造一个错误 */}
<ChildComponent />
</ErrorBoundary>
);
}
}

// 子组件,故意制造一个错误
class ChildComponent extends Component {
render() {
// 这里制造了一个故意的错误,例如调用一个未定义的函数
return <button onClick={this.handleClick}>Click me</button>;
}
}

export default MyComponent;

react函数式组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import React, { useEffect } from 'react';

// 全局错误处理函数
const errorHandler = (error, vm, info) => {
console.error('React Error:', error);
console.error('React Component:', vm);
console.error('React Error Info:', info);
};

// 在全局事件监听器中注册全局错误处理函数
window.addEventListener('error', errorHandler);

// 函数式组件
const MyComponent = () => {
const causeError = () => {
// 故意制造一个错误,例如调用一个未定义的函数
nonexistentFunction();
};

useEffect(() => {
// 在组件卸载时移除全局错误处理函数
return () => {
window.removeEventListener('error', errorHandler);
};
}, []);

return (
<div>
<button onClick={causeError}>Click me to cause error</button>
</div>
);
};

export default MyComponent;

捕获异步错误

在 JavaScript 中,异步的错误是无法直接捕获的,因为它们在异步上下文中执行,而错误会在执行上下文中被捕获并处理。然而,你可以通过一些技巧来间接地捕获 setTimeout 内部的错误。

一种方法是封装 函数,创建一个可以捕获内部错误的函数。例如,你可以创建一个 safeSetTimeout 函数,类似于以下方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function GSetTimeout(callback, delay) {
setTimeout(() => {
try {
callback();
} catch (error) {
console.error('An error occurred in setTimeout callback:', error);
}
}, delay);
}

// 使用示例
GSetTimeout(() => {
// 可能会引发错误的代码
}, 1000);

捕获跨域错误

JavaScript无法直接捕获跨域错误。浏览器出于安全考虑,限制了跨域请求的能力以及对跨域资源的访问。跨域错误通常是由浏览器的同源策略(Same Origin Policy)所引起的。

如果你试图在JavaScript中发送一个跨域请求,浏览器会阻止该请求,并且不会触发相应的JavaScript错误事件。相反,它会在控制台中显示一条错误消息,提示请求被阻止了,但这个错误是不可捕获的。

捕获内存错误

在前端中,内存错误通常是由于内存泄漏或大量数据处理等原因导致的。由于浏览器环境的限制,我们无法直接监控内存错误。


喜欢这篇文章?打赏一下支持一下作者吧!
【监控】错误监控的手段
https://www.cccccl.com/20230101/监控/错误监控的手段/
作者
Jeffrey
发布于
2023年1月1日
许可协议