shatter_monitor
Version:
A SDK for monitoring browser errors
144 lines (133 loc) • 5.76 kB
text/typescript
/* eslint-disable prefer-rest-params */
import { BlockEventSingleTypes } from '../types/index'
import { ERRORTYPES, ERRORNAMETYPES } from '../common/errorType'
import { eventWarp } from './types/eventWarp'
import { isError } from 'utils/typeCheck'
export const BindStaticEvent = function (w: eventWarp, options: BlockEventSingleTypes) {
/**
* 监听全局 addEventListener 事件,针对跨域脚本的 Script error 问题
*/
if (!options.blockTry) {
const originAddEventListener = EventTarget.prototype.addEventListener
const originRemoveEventListener = EventTarget.prototype.removeEventListener
const catchFuncStack = []
EventTarget.prototype.addEventListener = function (type, listener:any, options) {
const wrappedListener = function () {
try {
return listener.apply(this, arguments)
}
catch (err) {
throw err
}
}
catchFuncStack.push({
origin: listener,
wrap: wrappedListener
})
return originAddEventListener.call(this, type, wrappedListener, options)
}
EventTarget.prototype.removeEventListener = function (type, listener, options) {
let wrap
const isInclude = catchFuncStack.some((item) => {
if (item.origin === listener) {
wrap = item.wrap
return true
} else {
return false
}
})
if (isInclude) {
return originRemoveEventListener.call(this, type, wrap, options)
} else {
return originRemoveEventListener.call(this, type, listener, options)
}
}
}
/**
* 统一对 js 错误进行处理
*/
if (!options.blockError) {
const oldError = window.onerror || null
window.onerror = (msg:string, url, line, col, error) => {
w.report({
name: ERRORNAMETYPES['jsError'], msg, url, line, col, type: ERRORTYPES['JAVASCRIPT_ERROR']
})
oldError && oldError(msg, url, line, col, error)
}
}
/**
* 统一对 资源 加载错误进行处理
*/
if (!options.blockSource) {
window.addEventListener('error', event => {
if (!event) return
// 过滤js error
const target = event.target || event.srcElement
const isElementTarget = target instanceof HTMLScriptElement || target instanceof HTMLLinkElement || target instanceof HTMLImageElement
if (!isElementTarget) return false
// 上报资源地址
const url = (<HTMLImageElement>target).src || (<HTMLLinkElement>target).href
w.report({
name: ERRORNAMETYPES['sourceError'], url, type: ERRORTYPES['RESOURCE_ERROR']
})
}, true)
}
/**
* 统一对 promise 错误进行处理
*/
if (!options.blockPromise) {
window.addEventListener('unhandledrejection', event => {
if (!event.reason || !event.reason.stack || !event.reason.stack.includes('\n')) {
w.report({
name: ERRORNAMETYPES['promiseError'],
type: ERRORTYPES['PROMISE_ERROR'],
msg: (event.reason && event.reason.stack) || event.reason || ''
})
return
}
const fileMsg = event.reason.stack.split('\n')[1].split('at ')[1]
const fileArr = fileMsg.split(':')
const line = fileArr[fileArr.length - 2]
const col = fileArr[fileArr.length - 1]
const url = fileMsg && fileMsg.slice(0, -line.length -col.length - 2)
const msg = event.reason.message
w.report({
name: ERRORNAMETYPES['promiseError'], msg, url, line, col, type: ERRORTYPES['PROMISE_ERROR']
})
}, true)
}
/**
* 监听全局 console.error 函数进行处理
*/
if (!options.blockConsole) {
const originConsoleError = window.console.error
window.console.error = (func => {
return (...args) => {
args.forEach(item => {
if (isError(item)) {
let fileMsg
if (item.stack && item.stack.split('\n')[1]) {
fileMsg = item.stack && item.stack.split('\n')[1].split('at ')[1]
} else {
fileMsg = item.stack
}
const fileArr = fileMsg.split(':')
const line = fileArr[fileArr.length - 2]
const col = fileArr[fileArr.length - 1]
const url = (fileMsg.split('(')[1] && fileMsg.split('(')[1].slice(0, -line.length -col.length - 2)) || 'anonymousFunction'
w.report({
name: ERRORNAMETYPES['consoleError'], msg: item.stack, url, line, col, type: ERRORTYPES['LOG_ERROR']
})
} else { // 不是作为 Error 打印出来的 error 只发送 msg
w.report({
name: ERRORNAMETYPES['consoleError'],
msg: item,
type: ERRORTYPES['LOG_ERROR']
})
}
})
func.apply(console, args)
}
})(originConsoleError)
}
}