UNPKG

@sdc-monitor/demo

Version:

<div align="center"> <a href="#" target="_blank"> <img src="https://i.loli.net/2021/07/28/EvPwd4NjVH3tBfO.jpg" alt="mito-logo" height="90"> </a> <p>A Lightweight SDK For Monitor Web</p>

190 lines (183 loc) 6.65 kB
import { WxBreadcrumbTypes, ErrorTypes, globalVar, HttpTypes, HTTP_CODE, MethodTypes, WxEventTypes, BREADCRUMBCATEGORYS, WxBaseEventTypes } from '@sdc-monitor/shared' import { fromHttpStatus, getCurrentRoute, getRealPath, getTimestamp, Severity, SpanStatus, variableTypeDetection } from '@sdc-monitor/utils' import { BasePluginType, HttpCollectedType, HttpTransformedType } from '@sdc-monitor/types' import { WxClient } from '../wxClient' import { addBreadcrumbInWx } from '../utils' enum WxXhrTypes { request = 'request', downloadFile = 'downloadFile', uploadFile = 'uploadFile' } const wxRequestPlugin: BasePluginType<WxEventTypes, WxClient> = { name: WxBaseEventTypes.REQUEST, monitor(notify) { monitorWxXhr.call(this, notify) }, transform(collectedData: HttpCollectedType) { return httpTransform(collectedData) }, consumer(transformedData: HttpTransformedType) { httpTransformedDataConsumer.call(this, transformedData) } } function monitorWxXhr(this: WxClient, notify: (eventName: WxEventTypes, data: any) => void) { const hookMethods = Object.keys(WxXhrTypes) // eslint-disable-next-line @typescript-eslint/no-this-alias const that = this const { options: wxOptions } = this hookMethods.forEach((hook) => { const originRequest = wx[hook] Object.defineProperty(wx, hook, { writable: true, enumerable: true, configurable: true, value: function (...args: any[]) { // 拿到入参 const options: WechatMiniprogram.RequestOption | WechatMiniprogram.DownloadFileOption | WechatMiniprogram.UploadFileOption = args[0] const { url } = options let method: string, reqData = undefined if ((options as WechatMiniprogram.RequestOption).method) { method = (options as WechatMiniprogram.RequestOption).method } else if (hook === WxXhrTypes.downloadFile) { method = MethodTypes.Get } else { method = MethodTypes.Post } let header = options.header !header && (header = {}) if ((method === MethodTypes.Post && that.transport.isSelfDsn(url)) || wxOptions.isFilterHttpUrl(url)) { return originRequest.call(this, options) } switch (hook) { case WxXhrTypes.request: reqData = (options as WechatMiniprogram.RequestOption).data break case WxXhrTypes.downloadFile: reqData = { filePath: (options as WechatMiniprogram.DownloadFileOption).filePath } break default: // uploadFile reqData = { filePath: (options as WechatMiniprogram.UploadFileOption).filePath, name: (options as WechatMiniprogram.UploadFileOption).name } } // 收集小程序的请求信息 const httpCollect: HttpCollectedType = { request: { httpType: HttpTypes.XHR, url, method, data: reqData }, response: {}, time: getTimestamp() } wxOptions.setTraceId(url, (headerFieldName, traceId) => { httpCollect.request.traceId = traceId header[headerFieldName] = traceId }) function setRequestHeader(key: string, value: string) { header[key] = value } wxOptions.beforeAppAjaxSend && wxOptions.beforeAppAjaxSend({ method, url }, { setRequestHeader }) // 成功回调 const successHandler: | WechatMiniprogram.RequestSuccessCallback | WechatMiniprogram.DownloadFileSuccessCallback | WechatMiniprogram.UploadFileFailCallback = function (res) { const endTime = getTimestamp() httpCollect.response.data = (variableTypeDetection.isString(res.data) || variableTypeDetection.isObject(res.data)) && res.data httpCollect.elapsedTime = endTime - httpCollect.time httpCollect.response.status = res.statusCode httpCollect.errMsg = res.errMsg notify(WxBaseEventTypes.REQUEST, httpCollect) if (variableTypeDetection.isFunction(options.success)) { return options.success(res) } } const _fail = options.fail // 失败回调 const failHandler: | WechatMiniprogram.RequestFailCallback | WechatMiniprogram.DownloadFileFailCallback | WechatMiniprogram.UploadFileFailCallback = function (err) { // 系统和网络层面的失败 const endTime = getTimestamp() httpCollect.elapsedTime = endTime - httpCollect.time httpCollect.errMsg = err.errMsg httpCollect.response.status = 0 notify(WxBaseEventTypes.REQUEST, httpCollect) if (variableTypeDetection.isFunction(_fail)) { return _fail(err) } } const actOptions = { ...options, success: successHandler, fail: failHandler } // return 原始函数 return originRequest.call(this, actOptions) } }) }) } export function httpTransform(httpCollectedData: HttpCollectedType): HttpTransformedType { let message = '' const { request: { httpType, method, url }, response: { status }, elapsedTime } = httpCollectedData const name = `${httpType}--${method}` if (status === 0) { message = elapsedTime <= globalVar.crossOriginThreshold ? 'http请求失败,失败原因:跨域限制或域名不存在' : 'http请求失败,失败原因:超时' } else { message = fromHttpStatus(status) } message = message === SpanStatus.Ok ? message : `${message} ${getRealPath(url)}` return { ...httpCollectedData, type: ErrorTypes.HTTP, url: getCurrentRoute(), level: Severity.Low, message, name } } export function httpTransformedDataConsumer(this: WxClient, transformedData: HttpTransformedType) { const type = WxBreadcrumbTypes.XHR // time 是为了保持顺序,紧跟在点击事件后面 const { response: { status }, time } = transformedData const isError = status === 0 || status === HTTP_CODE.BAD_REQUEST || status > HTTP_CODE.UNAUTHORIZED addBreadcrumbInWx.call(this, transformedData, type, Severity.Info, { time }) if (isError) { const breadcrumbStack = this.breadcrumb.push({ type, category: BREADCRUMBCATEGORYS.EXCEPTION, data: { ...transformedData }, level: Severity.Error, time }) this.transport.send(transformedData, breadcrumbStack) } } export default wxRequestPlugin