vue-exception-captor
Version:
用于捕获Vue前端工程中的全局异常、Promise异常,并支持HTTP模式上报到后台存储
179 lines (163 loc) • 5.78 kB
JavaScript
/*
* @Author: CaoZhongQian
* @Date: 2023-01-27 22:11:21
* @LastEditors: CaoZhongQian
* @LastEditTime: 2023-01-31 16:35:05
* @FilePath: \vue-exception-captor\src\index.js
* @Description: 全局异常捕获插件
*/
// Vue组件内的同步异常,无法被window.onerror捕获
// Vue组件内部的定时器异常,可以被window.onerror捕获
// window.onerror不能捕获promise异常
(function() {
Number.isInteger = Number.isInteger || function(value) {
return typeof value === 'number' &&
isFinite(value) &&
Math.floor(value) === value
}
var Axios = require('axios')
var VueExceptionCaptor = {
// 产品名字
productName: 'unknown',
// 用户名称
username: 'unknown',
// 上报到后台存储的Http接口地址
reportUrl: '',
// axios http客户端
reportClient: null,
// 是否禁用上报
disabled: false,
// 是否启用控制台打印
console: false,
install: function(Vue, options) {
this.productName = options.productName || this.productName
this.username = options.username || this.username
this.reportUrl = options.reportUrl || ''
this.disabled = options.disabled || false
this.console = options.console || false
this.reportClient = Axios.create({
baseURL: this.reportUrl,
withCredentials: false,
timeout: 3000
})
window.onerror = (event, source, lineNo, columnNo, error) => {
if (this.console) {
console.log('进入了window.onerror')
console.log(error)
}
if (!this.disabled) {
this.reportException('window.onerror', error, '捕获全局异常', 'error')
}
return true
}
window.onunhandledrejection = (event) => {
if (this.console) {
console.log('进入了window.onunhandledrejection')
console.log(event)
}
if (!this.disabled) {
this.reportException('window.onunhandledrejection', event.reason, '捕获全局Promise异常', 'error')
}
}
Vue.config.errorHandler = (error, vm, info) => {
if (this.console) {
console.log('进入了Vue.config.errorHandler')
console.log(error)
}
if (!this.disabled) {
this.reportException('vue.errorHandler', error, '捕获Vue全局异常', 'error')
}
}
Vue.prototype.$exceptionCaptor = this
Vue.prototype.$reportException = this.reportException
},
/**
* @description: 上报异常
* @param {*} type vue.errorHandler、window.onunhandledrejection、window.onunhandledrejection
* @param {*} error 异常信息
* @param {*} message 说明信息
* @param {*} level 异常等级,默认为error
* @return {*}
*/
async reportException(type, error, message, level = 'error') {
try {
const exception = this.getException(type, error, message, level)
await this.report(exception)
} catch (error) {
console.log('处理上报的异常出错')
console.log(error)
}
},
/**
* @description: 生成上报的异常数据
* @param {*} type type vue.errorHandler、window.onunhandledrejection、window.onunhandledrejection
* @param {*} error 异常信息
* @param {*} message 说明信息
* @param {*} level 异常等级,默认为error
* @return {*}
*/
getException(type, error, message, level = 'error') {
const jsonError = this.serializeError(error)
return {
product: this.productName,
username: this.username,
href: window.location.href,
type: type,
level: level,
message: message || '',
error: jsonError
}
},
/**
* @description: 序列化Error对象
* @param {*} error
* @return {*}
*/
serializeError(error) {
try {
return error ? JSON.stringify(error, Object.getOwnPropertyNames(error), null) : ''
} catch (error) {
return ''
}
},
/**
* @description: 上报异常到后台服务
* @param {*} exception
* @return {*}
*/
async report(exception) {
try {
// 将异常序列化单行字符串,保证body体的json到了nginx是单行的json内容,解决filebeat解析多行文本失败的BUG
const exceptionStr = JSON.stringify(exception)
await this.reportClient({
method: 'post',
data: exceptionStr,
headers: {
'Content-Type': 'application/json'
}
})
} catch (error) {
console.log('上报全局异常出错')
console.log(error)
}
},
/**
* @description: 设置上报的用户信息,用于标识异常信息来自哪个用户
* @param {*} username
* @return {*}
*/
setExceptionUserName(username) {
this.username = username
}
}
if (typeof exports === 'object') {
module.exports = VueExceptionCaptor
} else if (typeof define === 'function' && define.amd) {
define([], function() {
return VueExceptionCaptor
})
} else if (window.Vue) {
window.VueExceptionCaptor = VueExceptionCaptor
Vue.use(VueExceptionCaptor)
}
})()