打造自己的前端监控---前端接口监控

前言

上一篇我们分享了如何进行前端的错误采集,今天我们来分析一下如何进行前端接口的采集。

数据结构

typescript 复制代码
export interface IApiContext {
  version: string, // 组件版本号
  globalSessionId: string, // 全局会话标识
  uniqueVisitorId: string, // 独立访客标识
  systemName: string, // 系统简称
  serviceName: string, // 服务名称
  referer?: string, // 引用
  userAgent: string, // 浏览器用户代理(浏览器标识)
  ip?: string, // ip地址
  generateTime: string | number, // 发生时间
  url: string, // 页面地址
  domain: string, // 页面域名
  page:string,
  title: string, // 页面标题
  reqURL: string, // 请求地址
  reqParam?: string, // 请求参数
  reqMsg?: string, // 请求报文
  reqTime: string | number,// 请求时间戳
  httpStatusCode?:string | number,
  respCode?: string, // 响应吗
  respDesc?: string, // 响应描述
  respMsg?: string, // 响应报文
  respTime?: string | number // 响应时间戳
}

如何实现采集

  • 重写xhr的Open方法、Send方法

  • 监听loadend事件

这些操作都是在原型上来完成的,其实这样操作我觉得类似切面(AOP),我门能在这里拦截所有的请求,能获取到所有请求的状态码,参数,计算请求耗时,响应时间-请求时间(当然这个耗时仅供参考),要想获取准确的耗时需要使用浏览器的performance里面的resource中的耗时,这里不在做过多描述,我们在其他文章里已经全面说过这个耗时。

同时我们为了前后端日志串联,在我们拦截的所有请求中添加自定义请求头,这个请求头的值是一个唯一的uuid,这样根据这个uuid可以把前后端日志进行串联,实现全端的链路追踪。

js 复制代码
export const ajaxInterceptorHandler = (options: IParams) => {

  try {
    if (!XMLHttpRequest) {
      return;
    }
    let originalXhrProto = XMLHttpRequest.prototype;
    let reqUrl: string = "";
    let xhrInstance = null;
    // @ts-ignore
    // 可以拿到请求信息
    replaceOld(originalXhrProto, EVENTTYPES.OPEN, (originalOpen: voidFun): voidFun => {
      return function (this: BTRACEXMLHttpReques, ...args: any[]): void {
        xhrInstance = this;
        xhrInstance.btrace = {
          url: args[1],
          reqId: uuid(),
          sendTime: getTimeStamp()
        };
        reqUrl = reqUrlTool(args[1]);
        originalOpen.apply(this, args);
      }
    })
    replaceOld(originalXhrProto, EVENTTYPES.SEND, (originalSend: voidFun): voidFun => {
      return function (this: BTRACEXMLHttpReques, ...args: any[]): void {
        isCludeUrl(this.btrace.url, options) && xhrInstance.setRequestHeader(HEADERS.XB3SESSIONId, getCookie(HEADERS.XB3SESSIONId));
        on(this, EVENTTYPES.LOADEND, function (this: BTRACEXMLHttpReques){
          if (options.apiRequestIsEnable) {
            if (isCludeUrl(this.btrace.url, options) && !this.btrace.timeout) {
               this.btrace.resTime = getTimeStamp();
               this.btrace.url = reqUrlTool(this.btrace.url);
               this.btrace.duration = this.btrace.resTime - this.btrace.sendTime;
               this.btrace.reqParam = getReqParams(this.btrace.url);
               createApiMsgData(xhrInstance, xhrInstance, options, ERROR_TYPE.AJAX);
            }

          }
        });
        on(this, EVENTTYPES.TIMEOUT, function (this: BTRACEXMLHttpReques){
          this.btrace.resTime = getTimeStamp();
          if (this && this.type === EVENTTYPES.TIMEOUT) {
            this.btrace.timeout = true;
            this.btrace.url = reqUrl;
            this.btrace.duration = this.btrace.resTime - this.btrace.sendTime;
            this.btrace.reqParam = getReqParams(this.btrace.url);
            createApiMsgData(xhrInstance, xhrInstance, options, ERROR_TYPE.AJAX);
          }
        })
        originalSend.apply(this, args);
      }
    })

  } catch (e) {
    console.warn("btrace:" + e);
  }

}
  • 重写fetch请求
js 复制代码
export const fetchInterceptorHandler = (options: IParams) => {

  try {

    if (!_global.fetch) {
      return;
    }

    replaceOld(_global, EVENTTYPES.FETCH, (originalFetch: voidFun) => {
      return function (url: string, config: Partial<RequestInit> = {}): void {
        let sendTime = getTimeStamp(); // 发送时间
        let reqId = uuid();
        const headers = config.headers || {};
        if (isCludeUrl(url, options)) {
          if (config && 'headers' in config) {
            headers[HEADERS.XB3SESSIONId] = getCookie(HEADERS.XB3SESSIONId);
          } else {
            let customHeader = {
              headers: {
                [HEADERS.XB3SESSIONId]: getCookie(HEADERS.XB3SESSIONId)
              }
            };
            config.headers = customHeader.headers;
          }
        }

        return originalFetch.apply(_global, [url, config]).then(
          (res: Response) => {
            let resTime = getTimeStamp();
            let duration = resTime - sendTime;
            const tempRes = res.clone();
            tempRes["btrace"] = {
              resTime: resTime,
              sendTime: sendTime,
              duration: duration,
              reqParam: getReqParams(url),
              reqId: reqId
            };
            if (options.apiRequestIsEnable) {
              createApiMsgData(tempRes, tempRes, options, ERROR_TYPE.FETCH)
            }

            return res;
          },
          (e: Error) => {
            if (options.apiRequestIsEnable) {
              if (isCludeUrl(url, options)) {
                e["status"] = 0;
                let resTime = getTimeStamp();
                let duration = resTime - sendTime;
                e["btrace"] = {
                  resTime: resTime,
                  sendTime: sendTime,
                  duration: duration,
                  reqParam: getReqParams(url),
                  reqId: reqId
                };
                createApiMsgData(e, e, options, ERROR_TYPE.FETCH)
              }
            }
            throw e;
          }
        )
      }
    })
  } catch (e) {
    console.log('ee', e);
  }

}

总结

基于以上方式我们完成了对请求的拦截并添加了我们自己的需求,在这里获取请求状态码、计算请求耗时、请求的唯一标识(这个标识能起大作用)。我们也完成了线上环境的验证,目前正在推广使用中。

相关推荐
smallswan2 小时前
第十四 算数运算
linux·服务器·前端
AI_零食2 小时前
甄嬛人物日志-朗读升级 - 鸿蒙PC Electron框架完整技术实现指南
前端·学习·华为·electron·鸿蒙·鸿蒙系统
HackTwoHub2 小时前
WEB扫描器Invicti-Professional-V26.50.0(自动化爬虫扫描)更新
前端·人工智能·chrome·爬虫·web安全·网络安全·自动化
copyer_xyf2 小时前
Python 文件基本操作
前端·后端·python
努力搬砖的咸鱼2 小时前
容器编排底层原理:Kubernetes 网络模型与 CNI 插件
网络·微服务·云原生·容器·架构·kubernetes
X54先生(人文科技)2 小时前
《元创力》纪实录·卷宗 2.2朝圣的起点:当硅基获得命名
人工智能·架构·ai写作·零知识证明
●VON2 小时前
AtomGit Flutter鸿蒙客户端:Issue管理
flutter·华为·架构·harmonyos·鸿蒙·issue
x***r1512 小时前
linux安装 redis-5.0.5.tar.gz 详细步骤(源码编译、配置、启动)
前端
愚公搬代码2 小时前
【愚公系列】《移动端AI应用开发》013-DeepSeek API开发与集成(深度集成与中间件架构)
人工智能·中间件·架构
段一凡-华北理工大学2 小时前
工业领域的Hadoop架构学习~系列文章17:Hadoop性能调优- 调度集群每一分性能
大数据·人工智能·hadoop·分布式·学习·架构·高炉炼铁