Write By CS逍遥剑仙
我的主页: www.csxiaoyao.com
GitHub: github.com/csxiaoyaojianxian
Email: [email protected]
QQ: 1724338257
传统的前端监控原理分为异常捕获和异常上报。一般使用onerror
捕获前端错误:
window.onerror = (msg, url, line, col, error) => {
console.log('onerror')
// TODO
}
但是onerror
事件无法捕获到网络异常的错误(资源加载失败、图片显示异常等),例如img
标签下图片url 404 网络请求异常的时候,onerror
无法捕获到异常,此时需要监听unhandledrejection
。
window.addEventListener('unhandledrejection', function(err) {
console.log(err)
})
捕获的异常如何上报?常用的发送形式主要有两种:
function report(error) {
var reportUrl = 'http://xxxx/report'
new Image().src = reportUrl + '?error=' + error
}
sentry是一套开源的强大的前端异常监控上报工具,官网地址:https://sentry.io,官方提供了如何搭建sentry服务,此处略过安装流程,直接使用已有的服务。
针对vue,sentry官方推荐使用raven配合sentry进行异常捕获和上报。而在vue中,vue提供了错误捕获方法vue error handler
,官方也推荐使用错误追踪服务 sentry 并通过vue error handler
选项提供了官方支持。
raven是sentry官方针对vue推荐的插件,yarn安装raven-js即可。
$ yarn add raven-js
初始化引入Vue、Raven、RavenVue即可,sentry能主动监听上报错误。
import Raven from 'raven-js'
import RavenVue from 'raven-js/plugins/vue'
const dsn = 'https://@sentry.io/'
Raven.config(dsn).addPlugin(RavenVue, Vue).install()
对于一些其他信息,如提示日志等,无法自动捕获,需要手动进行上报。
log (data = null, type = 'error', options = {}) {
// 添加面包屑
Raven.captureBreadcrumb({
message: data,
category: 'manual message'
})
// 异常上报
if (data instanceof Error) {
Raven.captureException(data, {
level: type,
logger: 'manual exception',
tags: { options: options }
})
} else {
Raven.captureException('error', {
level: type,
logger: 'manual data',
extra: {
data: data,
options: this.options,
date: new Date()
}
})
}
}
针对上述内容,封装异常上报类Report,使用单例模式,避免监控类重复实例化。
/**
* by csxiaoyao
* 2019.03.17
* [email protected]
*/
import Raven from 'raven-js'
import RavenVue from 'raven-js/plugins/vue'
class Report {
static dsn = 'https://@sentry.io/'
constructor (Vue, options = {}) {
if (process.env.NODE_ENV === 'production') { // TODO }
this.Vue = Vue
this.options = options
}
/**
* 单例模式
*/
static getInstance (Vue, options) {
if (!(this.instance instanceof this)) {
this.instance = new this(Vue, options)
this.instance.install()
this.instance.registerError()
}
return this.instance
}
/**
* init
*/
install () {
Raven.config(Report.dsn, {
release: '1.0.0',
environment: 'production'
// whitelistUrls: [/localhost/, /test\.oa\.com/]
}).addPlugin(RavenVue, this.Vue).install()
// 记录用户信息
Raven.setUserContext({
user: this.options.user || ''
})
// 设置全局tag标签
Raven.setTagsContext({ environment: this.options.env || '' })
}
/**
* 注册全局错误处理
*/
registerError () {
// 监听error
window.onerror = (msg, url, line, col, error) => {
console.log('onerror')
if (msg !== 'Script error.' && !url) {
return true
}
setTimeout(() => {
let data = {}
col = col || (window.event && window.event.errorCharacter) || 0
data.url = url
data.line = line
data.col = col
data.error = error
if (!!error && !!error.stack) {
data.msg = error.stack.toString()
}
this.log(data)
}, 0)
return true
}
// 监听promise
window.addEventListener('unhandledrejection', err => {
console.log('unhandledrejection')
setTimeout(() => {
this.log(JSON.stringify(err))
}, 0)
})
}
/**
* 主动上报
* type: 'info','warning','error'
*/
log (data = null, type = 'error', options = {}) {
// 添加面包屑
Raven.captureBreadcrumb({
message: data,
category: 'manual message'
})
// 异常上报
if (data instanceof Error) {
Raven.captureException(data, {
level: type,
logger: 'manual exception',
tags: { options: options }
})
} else {
Raven.captureException('error', {
level: type,
logger: 'manual data',
extra: {
data: data,
options: this.options,
date: new Date()
}
})
}
}
}
export default Report
main.js
中引入Report类,并绑定实例化后的sentry实例到Vue上以便全局调用。
import Report from '@/assets/Report'
let sentry = Report.getInstance(Vue, {})
Vue.prototype.$sentry = sentry // 设置全局变量
在其他的vue组件中手动上报日志。
this.$sentry.log('test')
sentry针对压缩过的js文件提供了sourceMap分析,只需要上传版本对应的sourceMap,就可以在错误日志中查看对应的源码信息。详细方法见官方文档:https://docs.sentry.io/clients/javascript/sourcemaps/(https://docs.sentry.io/clients/javascript/sourcemaps/)