![8b00f485b41388ace8ff7b868027c7c2.png](https://i-blog.csdnimg.cn/blog_migrate/89e6f7ff4a3a7d4d57a66eca3b06d7de.jpeg)
最近在做前端错误日志上报的需求,目前处于一个相对理想的状态,梳理一下错误处理的流程
在实际项目中,有时线上出现了问题,前端定位不了问题,只能去猜可能出现的原因,再尝试复现,效率很低
首先根据环境区分,开发环境是不需要借助其他方式去显示错误的,控制台足够了;当提交到测试环境的时候,错误日志使用vconsole直接输出,便于测试同学定位问题;当代码运行在线上环境,错误日志应当上报到公司日志系统,便于定位线上问题
//vue-cli生成项目的入口文件 main.js
import errorHandler from './libs/errorhandler'
import Vconsole from 'vconsole'
// 当处于开发环境下:正常在控制台输出日志,不做处理
// 当处于qa、stage环境下,在vconsole输出日志
// 当处于product环境下,错误日志上报到天网
// NODE_ENV是vue-cli自带的环境变量,DAOKE_ENV是运维同学配置的docker的环境变量
Vue.use(errorHandler)
if (process.env.NODE_ENV !== 'development' && process.env.DAOKE_ENV !== 'product') {
const vConsole = new Vconsole()
Vue.use(vConsole)
}
重点在于线上环境下的错误上报,分手动捕获和自动捕获
import Vue from 'vue'
import util from './util'
// report函数的目的是将错误信息上报到日志系统,利用动态创建script标签的方式,避免发生跨域
export function report(params) {
var script = document.createElement('script')
var head = document.head
head.appendChild(script)
var queryAry = []
var queryString = ''
for (var i in params) {
queryAry.push(`${i}=${encodeURIComponent(params[i])}`)
}
queryString = queryAry.join('&')
queryString += '&url=' + encodeURIComponent(window.location.href)
queryString += '&agent=' + encodeURIComponent(window.navigator.userAgent)
script.src = '/' + queryString
script.onload = function() {
head.removeChild(script)
}
}
// 只有正式环境需要上报错误日志
// 这里主要用于上传没有手动捕获的异常
if (process.env.DAOKE_ENV === 'product') {
// window error捕获的错误
window.onerror = (msg, url, line, col, error) => {
var _msg = handleErrorMsg(error)
report({
type: 'window ERROR',
msg: _msg,
level: 'fatal'
})
return true // error不会以error形式打印到控制台
}
// promise rejecttion 错误捕获
window.addEventListener('unhandledrejection', (e) => {
report({
type: 'unhandledrejection',
level: 'fatal',
msg: e.reason
})
return true
})
// vue捕获的错误不会上报到window.error
Vue.config.errorHandler = function(error, vm, msg) {
var _msg = handleErrorMsg(error)
console.log(error)
report({
type: 'Vue errorHandler',
level: 'fatal',
error,
msg: _msg
})
}
}
function handleErrorMsg(error) {
var _msg = ''
if (error instanceof Error) {
_msg = error.toString() + error.stack
} else if (util.isPainObject(error)) {
_msg = JSON.stringify(error)
} else {
_msg = error
}
return _msg
}
// 这一部分作为手动上传异常的方法,系统自动捕获的异常往往语义不够清晰,难以定位问题,
// 所以有风险的代码块应该是用try catch包裹,在catch中调用Logger.error,手动上传异常信息
window.Logger = {
info(msg) {
if (process.env.DAOKE_ENV !== 'product') {
console.log(msg)
return
}
report({
level: 'info',
type: 'user report',
msg
})
},
error(msg) {
if (process.env.DAOKE_ENV !== 'product') {
console.log(msg)
return
}
report({
level: 'error',
type: 'user report',
msg
})
}
}
如何手动捕获错误信息?
这个问题乍一看很简单,用try catch就行了啊,把运行的代码包裹在try里,在catch里处理异常。但是,这只回答了40分,还有60分在细节里。
1、什么时候用try catch
promise中不需要,它会自动捕获异常
try{
new Promise(function(resolve,reject){
a.b
})
.then(v=>{
console.log(v)
}).catch(e => {
console.log(e)
})
}catch(e){
console.log('报错啦',e)
}
2、try catch能捕获哪里的异常
2.1、无法捕获语法异常,即在js解析阶段出现的异常无法捕获,但是eslint可以 : )
try{
a.
}catch(e){
console.log("报错啦",e);
}
// SyntaxError: Unexpected token }
2.2、 无法捕获在try代码块外部线程执行的异常
try{
function d(){a.b;}
}catch(e){
console.log("报错啦",e);
}
d()
// ReferenceError: a is not defined
function d(){a.b;}
try{
d();
}catch(e){
console.log("报错啦",e);
}
// 报错啦 ReferenceError: a is not defined
2.3、这只是对第二点的补充,setTimeout本身就是一个宏任务,所以执行并不在try代码块内部
try{
setTimeout(()=>{
console.log(a.b);
}, 100)
}catch(e){
console.log('报错啦',e);
}
// ReferenceError: a is not defined
能被 try catch 捕捉到的异常,必须是在报错的时候,线程执行已经进入 try catch 代码块,且处在 try catch 里面,这个时候才能被捕捉到
自动捕获都能捕获什么错误信息
SyntaxError:语法错误
ReferenceError:引用错误,引用一个不存在的变量时发生的错误或将一个值分配给无法分配的对象
RangeError:范围错误,是当一个值超出有效范围时发生的错误。主要的有几种情况,第一是数组长度为负数,第二是Number对象的方法参数超出范围,以及函数堆栈超过最大值
TypeError:类型错误,变量或参数不是预期类型时发生的错误。比如使用new字符串、布尔值等原始类型和调用对象不存在的方法就会抛出这种错误
URIError:URL错误,主要是相关函数的参数不正确
EvalError:eval()函数执行错误,当eval()函数没有被正确执行时
自定义Error:throw new Error('出错了'),Error方法只接收1个参数,该参数会自动被转换为String格式