前端异常监控
整体流程
异常捕获
--> 数据存储
--> 数据上传
异常捕获
--> 数据上传
异常捕获
window.onerror
js
window.onerror = function(message, source, lineno, colno, error) {...}
window.onerror = function(message, source, lineno, colno, error) {...}
函数参数:
message
: 错误信息(字符串)。可用于HTML onerror=""处理程序中的eventsource
: 发生错误的脚本URL(字符串)lineno
: 发生错误的行号(数字)colno
: 发生错误的列号(数字)error
: Error对象(对象)
js
window.onerror = (message, source, lineno, clono, error) => {
// 处理异常数据
this.saveLog({
type: 'onerror',
mesage: message,
url: source,
detail: `${lineno}-${clono}: ${error}`
})
}
window.onerror = (message, source, lineno, clono, error) => {
// 处理异常数据
this.saveLog({
type: 'onerror',
mesage: message,
url: source,
detail: `${lineno}-${clono}: ${error}`
})
}
unhandledrejection
未捕获的reject会报异常,触发unhandledrejection事件,通过监听unhandledrejection事件,拿到异常数据。
js
window.addEventListener('unhandledrejection', (param) => {
this.saveLog({
type: 'unhandledrejection',
data: param.reason
})
})
window.addEventListener('unhandledrejection', (param) => {
this.saveLog({
type: 'unhandledrejection',
data: param.reason
})
})
接口错误
通过重写window.XMLHttpRequest的方法,在需要处理异常的地方记录数据。
js
function sendError(e) {
this.saveLog({
type: 'http',
message: e.target.status,
url: e.target.responseURL,
detail: e.target.response
})
}
window.XMLHttpRequest.prototype.send = function (...param) {
const oldHttpStatechange = this.onreadystatechange
this.addEventListener('error', sendError)
this.addEventListener('abort', sendError)
this.onreadystatechange = (e) => {
if (this.readyState === 4) {
if (this.status !== 200)
sendError()
}
oldHttpStatechange.call(this)
}
return oldHttpSend.call(this, ...param)
}
function sendError(e) {
this.saveLog({
type: 'http',
message: e.target.status,
url: e.target.responseURL,
detail: e.target.response
})
}
window.XMLHttpRequest.prototype.send = function (...param) {
const oldHttpStatechange = this.onreadystatechange
this.addEventListener('error', sendError)
this.addEventListener('abort', sendError)
this.onreadystatechange = (e) => {
if (this.readyState === 4) {
if (this.status !== 200)
sendError()
}
oldHttpStatechange.call(this)
}
return oldHttpSend.call(this, ...param)
}
错误日志
在代码中通过console.error打印出的错误信息,通过重写方法捕获数据。
js
const oldConsole = window.console.error
window.console.error = (info) => {
oldConsole(info)
this.saveLog({
type: 'console-error',
message: info
})
}
const oldConsole = window.console.error
window.console.error = (info) => {
oldConsole(info)
this.saveLog({
type: 'console-error',
message: info
})
}
Vue errorHandler
指定组件的渲染和观察期间未捕获错误的处理函数。这个处理函数被调用时,可获取错误信息和 Vue 实例。
js
Vue.config.errorHandler = function (err, vm, info) {
// handle error
// `info` 是 Vue 特定的错误信息,比如错误所在的生命周期钩子
// 只在 2.2.0+ 可用
}
Vue.config.errorHandler = function (err, vm, info) {
// handle error
// `info` 是 Vue 特定的错误信息,比如错误所在的生命周期钩子
// 只在 2.2.0+ 可用
}
Vue2
- 从 2.4.0起,这个钩子也会捕获 Vue 自定义事件处理函数内部的错误
- 从 2.6.0 起,这个钩子也会捕获 v-on DOM 监听器内部抛出的错误。另外,如果任何被覆盖的钩子或处理函数返回一个 Promise 链 (例如 async 函数),则来自其 Promise 链的错误也会被处理。
Vue3
- 组件渲染器
- 事件处理器
- 生命周期钩子
- setup() 函数
- 侦听器
- 自定义指令钩子
- 过渡 (Transition) 钩子
数据存储
Cookies
:信息会被带入请求头中,容量较小且影响数据包大小LocalStorage
: 容量较小SessionStorage
:容量较小,同时关闭后会被清除indexDB
:采用键值对存储数据,提供异步API对数据进行操作webSQL
:兼容性较差,标准被废除不再更新
js
insert(param) {
try {
return new Promise((resolve, reject) => {
const transaction = this.database.transaction(param.name, 'readwrite')
const store = transaction.objectStore(param.name)
const dbRequest = store.add(param.data)
dbRequest.onsuccess = () => {
console.log('%c insert success', 'color: green;')
resolve()
}
dbRequest.onerror = (e) => {
console.log('insert error', e)
reject(e)
}
})
} catch (error) {
console.log('insert error', error)
}
}
insert(param) {
try {
return new Promise((resolve, reject) => {
const transaction = this.database.transaction(param.name, 'readwrite')
const store = transaction.objectStore(param.name)
const dbRequest = store.add(param.data)
dbRequest.onsuccess = () => {
console.log('%c insert success', 'color: green;')
resolve()
}
dbRequest.onerror = (e) => {
console.log('insert error', e)
reject(e)
}
})
} catch (error) {
console.log('insert error', error)
}
}