export default function windowFetch()

in src/trace/interceptors/fetch.ts [24:160]


export default function windowFetch(options: CustomOptionsType, segments: SegmentFields[]) {
  const originFetch: any = window.fetch;
  setFetchOptions(options);

  window.fetch = async (...args: any) => {
    const startTime = new Date().getTime();
    const traceId = uuid();
    const traceSegmentId = uuid();
    let segment = {
      traceId: '',
      service: customConfig.service,
      spans: [],
      serviceInstance: customConfig.serviceVersion,
      traceSegmentId: '',
    } as SegmentFields;
    let url = {} as URL;
    // for args[0] is Request object see: https://developer.mozilla.org/zh-CN/docs/Web/API/fetch
    if (Object.prototype.toString.call(args[0]) === '[object URL]') {
      url = args[0];
    } else if (Object.prototype.toString.call(args[0]) === '[object Request]') {
      url = new URL(args[0].url);
    } else {
      if (args[0].startsWith('http://') || args[0].startsWith('https://')) {
        url = new URL(args[0]);
      } else if (args[0].startsWith('//')) {
        url = new URL(`${window.location.protocol}${args[0]}`);
      } else {
        url = new URL(window.location.href);
        url.pathname = args[0];
      }
    }

    const noTraceOrigins = customConfig.noTraceOrigins.some((rule: string | RegExp) => {
      if (typeof rule === 'string') {
        if (rule === url.origin) {
          return true;
        }
      } else if (rule instanceof RegExp) {
        if (rule.test(url.origin)) {
          return true;
        }
      }
    });
    const cURL = new URL(customConfig.collector);
    const pathname = cURL.pathname === '/' ? url.pathname : url.pathname.replace(new RegExp(`^${cURL.pathname}`), '');
    const internals = [ReportTypes.ERROR, ReportTypes.ERRORS, ReportTypes.PERF, ReportTypes.SEGMENTS] as string[];
    const isSDKInternal = internals.includes(pathname);
    const hasTrace = !noTraceOrigins || (isSDKInternal && customConfig.traceSDKInternal);

    if (hasTrace) {
      const traceIdStr = String(encode(traceId));
      const segmentId = String(encode(traceSegmentId));
      const service = String(encode(segment.service));
      const instance = String(encode(segment.serviceInstance));
      const endpoint = String(encode(customConfig.pagePath));
      const peer = String(encode(url.host));
      const index = segment.spans.length;
      const values = `${1}-${traceIdStr}-${segmentId}-${index}-${service}-${instance}-${endpoint}-${peer}`;

      if (args[0] instanceof Request) {
        args[0].headers.append('sw8', values);
      } else {
        if (!args[1]) {
          args[1] = {};
        }
        if (!args[1].headers) {
          args[1].headers = new Headers();
        }
        if (args[1].headers instanceof Headers) {
          args[1].headers.append('sw8', values);
        } else {
          args[1].headers['sw8'] = values;
        }
      }
    }

    const response = await originFetch(...args);

    try {
      if (response && (response.status === 0 || response.status >= 400)) {
        const logInfo = {
          uniqueId: uuid(),
          service: customConfig.service,
          serviceVersion: customConfig.serviceVersion,
          pagePath: customConfig.pagePath,
          category: ErrorsCategory.AJAX_ERROR,
          grade: GradeTypeEnum.ERROR,
          errorUrl: (response && response.url) || `${url.protocol}//${url.host}${url.pathname}`,
          message: `status: ${response ? response.status : 0}; statusText: ${response && response.statusText};`,
          collector: customConfig.collector,
          stack: 'Fetch: ' + response && response.statusText,
        };
        new Base().traceInfo(logInfo);
      }
      if (hasTrace) {
        const tags = [
          {
            key: 'http.method',
            value: args[0] instanceof Request ? args[0].method : args[1]?.method || 'GET',
          },
          {
            key: 'url',
            value: (response && response.url) || `${url.protocol}//${url.host}${url.pathname}`,
          },
        ];
        const endTime = new Date().getTime();
        const exitSpan: SpanFields = {
          operationName: customConfig.pagePath,
          startTime: startTime,
          endTime,
          spanId: segment.spans.length,
          spanLayer: SpanLayer,
          spanType: SpanType,
          isError: response && (response.status === 0 || response.status >= 400), // when requests failed, the status is 0
          parentSpanId: segment.spans.length - 1,
          componentId: ComponentId,
          peer: url.host,
          tags: customConfig.detailMode
            ? customConfig.customTags
              ? [...tags, ...customConfig.customTags]
              : tags
            : undefined,
        };
        segment = {
          ...segment,
          traceId: traceId,
          traceSegmentId: traceSegmentId,
        };
        segment.spans.push(exitSpan);
        segments.push(segment);
      }
    } catch (e) {
      throw e;
    }
    return response.clone();
  };
}