hc-web-log-mon
Version:
基于 JS 跨平台插件,为前端项目提供【 行为、性能、异常、请求、资源、路由、曝光、录屏 】监控手段
179 lines (164 loc) • 5.52 kB
text/typescript
import { on, isValidKey, getTimestamp, parseGetParams } from '../utils'
import { handleSendError } from './err'
import { eventBus } from './eventBus'
import { EVENTTYPES, SENDID } from '../common'
import { options } from './options'
import { handleSendPerformance } from './performance'
// import { debug } from '../utils/debug'
import { isRegExp } from '../utils/is'
/**
* fetch请求拦截
*/
function interceptFetch(): void {
eventBus.addEvent({
type: EVENTTYPES.FETCH,
callback: (
reqUrl: string,
_options: Partial<Request> = {},
res: Response,
fetchStart: number
) => {
const { method = 'GET', body } = _options
const { url, status, statusText } = res
const requestMethod = String(method).toLocaleLowerCase()
if (isIgnoreHttp(url)) return
if (status === 200 || status === 304) {
if (options.value.performance.server) {
handleSendPerformance({
eventId: SENDID.SERVER,
requestUrl: url,
duration: getTimestamp() - fetchStart,
responseStatus: status,
requestMethod,
requestType: 'fetch',
params: method.toUpperCase() === 'POST' ? body : parseGetParams(url)
})
}
} else if (options.value.error.server) {
handleSendError({
eventId: SENDID.SERVER,
errMessage: statusText,
requestUrl: url,
responseStatus: status,
requestMethod,
requestType: 'fetch',
params: method.toUpperCase() === 'POST' ? body : parseGetParams(url)
})
}
}
})
}
class RequestTemplate {
requestUrl = '' // 请求地址
requestMethod = '' // 请求类型 GET POST
requestParams = {} // get请求的参数
triggerTime = -1 // 请求发生时间
constructor(config = {}) {
Object.keys(config).forEach(key => {
if (isValidKey(key, config)) {
this[key] = config[key] || null
}
})
}
}
/**
* xhr 请求拦截
*/
function interceptXHR(): void {
const _config = new RequestTemplate()
eventBus.addEvent({
type: EVENTTYPES.XHROPEN,
callback: (method, url) => {
_config.requestMethod = String(method).toLocaleLowerCase()
_config.requestUrl = url
_config.requestParams = parseGetParams(url)
}
})
eventBus.addEvent({
type: EVENTTYPES.XHRSEND,
callback: (that: XMLHttpRequest & any, body) => {
// readyState发生改变时触发,也就是请求状态改变时
// readyState 会依次变为 2,3,4 也就是会触发三次这里
on(that, EVENTTYPES.READYSTATECHANGE, function () {
const { readyState, status, responseURL, statusText, responseType } =
that
if (readyState === 4) {
const requestUrl = responseURL || _config.requestUrl
if (isIgnoreHttp(requestUrl)) return
// 请求已完成,且响应已就绪
if (status === 200 || status === 304) {
if (options.value.performance.server) {
let responseText
if (that.responseType === 'json') {
const { code, msg } = that.response
responseText = `${code},${msg}`
}
// 一次性发起多个请求的时候,这里的requestMethod及params存在缺陷
handleSendPerformance({
eventId: SENDID.SERVER,
requestUrl,
// requestMethod: _config.requestMethod,
requestType: 'xhr',
responseStatus: status,
duration: getTimestamp() - _config.triggerTime,
// params:
// _config.requestMethod === 'post'
// ? body
// : _config.requestParams,
responseText
})
}
} else if (options.value.error.server) {
const responseText =
responseType === 'text' ? that.responseText : ''
handleSendError({
eventId: SENDID.SERVER,
errMessage: statusText || responseText,
requestUrl,
requestMethod: _config.requestMethod,
requestType: 'xhr',
responseStatus: status,
params:
_config.requestMethod === 'post' ? body : _config.requestParams
})
}
}
})
_config.triggerTime = getTimestamp()
}
})
}
/**
* 判断请求地址是否为需要拦截的
* @param url 请求地址
*/
function isIgnoreHttp(url: string): boolean {
if (!options.value.ignoreRequest.length) return false
if (!url) return false
return options.value.ignoreRequest.some(item => {
if (isRegExp(item)) {
if ((item as RegExp).test(url)) {
// debug(`ignoreRequest拦截成功 - 截条件:${item} 拦截地址:${url}`)
return true
} else {
return false
}
} else {
if (url === item) {
// debug(`ignoreRequest拦截成功 - 截条件:${item} 拦截地址:${url}`)
return true
} else {
return false
}
}
})
}
/**
* 初始化http监控
*/
function initHttp(): void {
if (!options.value.performance.server && !options.value.error.server) return
interceptXHR()
interceptFetch()
}
export { initHttp }