xys-monitor
Version:
A project named xys-monitor
120 lines (101 loc) • 5.5 kB
JavaScript
import { hijack, getFullUrl, isCrossDomain, getCgidata } from './util'
import { objectAssign } from './object'
export default function (reportSys = {}) {
reportSys = reportSys || {}
let errTypeMap = reportSys.errTypeMap
let _commonpostdata = {
data: {
}
}
hijack(XMLHttpRequest.prototype, 'open', function (...args) {
//异常返回 实例对象中不一定有url 所以在open的时候 我们自己写上一个
this.openargs = args
this._responseUrl = args[1]
this._isCrossDomain = isCrossDomain(getFullUrl(args[1]), location.href)
this._requestStartTime = (new Date()).getTime()
})
hijack(XMLHttpRequest.prototype, 'send', function (...args) {
//忽略自身上报cgii ajax请求报错
if (this._ignore) {
return
}
let loadEvent = 'onreadystatechange'
if (this.openargs.length > 0) {
_commonpostdata.data.cgidata = getCgidata(this.openargs[0], this.openargs[1], ...args)
}
this._isSend = true
// if (typeof window !== 'undefined' && window.XDomainRequest) {
// loadEvent = 'onload';
// }
//使用微任务劫持 为了兼容低版本jq
/* eslint-disable-next-line no-new */
new Promise((resolve, reject) => {
resolve()
}).then(() => {
//为了兼容3.x以上的jq
if (this.onload) {
loadEvent = 'onload'
}
hijack(this, loadEvent, function (...args) {
//todo 判断超时、404等异常
let target = args[0].target || args[0].srcElement
if (!target || target.readyState !== 4) {
return
}
let wasteTime = (new Date()).getTime() - target._requestStartTime
if (!('' + target.status).match(/^[1|2|3]/) && target.status !== 0) {
reportSys.report.reportError(objectAssign(_commonpostdata, {
errorMsg: target._responseUrl + ' ' + target.statusText,
errorType: errTypeMap['request_fail_error']
}), this)
} else if (target.status === 0 && !(target.responseURL && target.responseURL.indexOf('file:') === 0)) {
//该情况为跨域
// The request errored out and we didn't get a response, this will be
// handled by onerror instead
// With one exception: request that using file: protocol, most browsers
// will return status as 0 even though it's a successful request
if (this._isCrossDomain) {
reportSys.report.reportError(objectAssign(_commonpostdata, {
errorMsg: 'Origin ' + target._responseUrl + ' is therefore not allowed access.',
errorType: errTypeMap['cross_domain_error']
}), this)
}
} else if (reportSys.options.xhr && reportSys.options.xhr.slowtime && (wasteTime >= reportSys.options.xhr.slowtime)) {
//慢速cgi上报
reportSys.report.reportError(objectAssign(_commonpostdata, {
errorMsg: target._responseUrl + ' is slow than ' + reportSys.options.xhr.slowtime + 'ms',
errorType: errTypeMap['low_speed_error']
}), this)
} else if (target.status === 200 && typeof target.response === 'string' && (target.response.indexOf('<title>错误</title>') !== -1 || (target.response.match(/^\{.*\}$/) && JSON.parse(target.response).info))) {
//增加页面重定向 / 转发到error.html的错误监听
reportSys.report.reportError(objectAssign(_commonpostdata, {
errorMsg: target._responseUrl + ' is error',
errorType: errTypeMap['request_fail_error']
}), this)
}
})
//jq自己利用settimeout实现了一个timeout abort机制 所以我们劫持xhr omtimeout是无效的
//目前已知的是jq不仅仅timeout的时候触发abort 如果检测到beforeSend返回的是false 也会abort
//所以我们无法从xhr对象中获取是否有设置超时
hijack(this, 'abort', function (...args) {
if (this._isSend) {
//该情况为jq中的超时
reportSys.report.reportError(objectAssign(_commonpostdata, {
errorMsg: 'Origin ' + this._responseUrl + ' is abort.',
errorType: errTypeMap['timeout_error']
}), this)
}
})
}).catch(err => {
console.error('err', err)
})
hijack(this, 'ontimeout', function (...args) {
//获取_responseUrl并且上报
let target = args[0].target || args[0].srcElement
reportSys.report.reportError(objectAssign(_commonpostdata, {
errorMsg: target._responseUrl + ' is timeout ( responseTime > ' + target.timeout + 'ms )',
errorType: errTypeMap['timeout_error']
}), this)
})
})
}