index.ts
3.92 KB
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import { errorStore, ErrorInfo } from '/@/store/modules/error';
import { useSetting } from '/@/hooks/core/useSetting';
import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
import { App } from 'vue';
function processStackMsg(error: Error) {
if (!error.stack) {
return '';
}
let stack = error.stack
.replace(/\n/gi, '') // 去掉换行,节省传输内容大小
.replace(/\bat\b/gi, '@') // chrome中是at,ff中是@
.split('@') // 以@分割信息
.slice(0, 9) // 最大堆栈长度(Error.stackTraceLimit = 10),所以只取前10条
.map((v) => v.replace(/^\s*|\s*$/g, '')) // 去除多余空格
.join('~') // 手动添加分隔符,便于后期展示
.replace(/\?[^:]+/gi, ''); // 去除js文件链接的多余参数(?x=1之类)
const msg = error.toString();
if (stack.indexOf(msg) < 0) {
stack = msg + '@' + stack;
}
return stack;
}
function formatComponentName(vm: any) {
if (vm.$root === vm) {
return {
name: 'root',
path: 'root',
};
}
const options = vm.$options as any;
if (!options) {
return {
name: 'anonymous',
path: 'anonymous',
};
}
const name = options.name || options._componentTag;
return {
name: name,
path: options.__file,
};
}
function vueErrorHandler(err: Error, vm: any, info: string) {
const { name, path } = formatComponentName(vm);
errorStore.commitErrorInfoState({
type: ErrorTypeEnum.VUE,
name,
file: path,
message: err.message,
stack: processStackMsg(err),
detail: info,
url: window.location.href,
});
}
export function scriptErrorHandler(
event: Event | string,
source?: string,
lineno?: number,
colno?: number,
error?: Error
) {
if (event === 'Script error.' && !source) {
return false;
}
setTimeout(function () {
const errorInfo: Partial<ErrorInfo> = {};
colno = colno || (window.event && (window.event as any).errorCharacter) || 0;
errorInfo.message = event as string;
if (error && error.stack) {
errorInfo.stack = error.stack;
} else {
errorInfo.stack = '';
}
const name = source ? source.substr(source.lastIndexOf('/') + 1) : 'script';
errorStore.commitErrorInfoState({
type: ErrorTypeEnum.SCRIPT,
name: name,
file: source as string,
detail: 'lineno' + lineno,
url: window.location.href,
...(errorInfo as Pick<ErrorInfo, 'message' | 'stack'>),
});
}, 0);
return true;
}
function registerPromiseErrorHandler() {
window.addEventListener(
'unhandledrejection',
function (event: any) {
errorStore.commitErrorInfoState({
type: ErrorTypeEnum.PROMISE,
name: 'Promise Error!',
file: 'none',
detail: 'promise error!',
url: window.location.href,
stack: 'promise error!',
message: event.reason,
});
},
true
);
}
function registerResourceErrorHandler() {
// 监控资源加载错误(img,script,css,以及jsonp)
window.addEventListener(
'error',
function (e: Event) {
const target = e.target ? e.target : (e.srcElement as any);
errorStore.commitErrorInfoState({
type: ErrorTypeEnum.RESOURCE,
name: 'Resouce Error!',
file: (e.target || ({} as any)).currentSrc,
detail: JSON.stringify({
tagName: target.localName,
html: target.outerHTML,
type: e.type,
}),
url: window.location.href,
stack: 'resouce is not found',
message: (e.target || ({} as any)).localName + ' is load error',
});
},
true
);
}
export function setupErrorHandle(app: App) {
const { projectSetting } = useSetting();
const { useErrorHandle } = projectSetting;
if (!useErrorHandle) {
return;
}
// Vue异常监控;
app.config.errorHandler = vueErrorHandler;
// js错误
window.onerror = scriptErrorHandler;
// promise 异常
registerPromiseErrorHandler();
// 静态资源异常
registerResourceErrorHandler();
}