首页 > 解决方案 > Axios 每天报告“未定义”1000 次

问题描述

TLDR

我们undefined每天从网络请求中收到数千个响应,占 Sentry 中错误事件总数的 80-90%(每天)。我已经阅读了很多关于 Axios 的“网络错误”的帖子,但我相信我们会收到如此多的错误,因为我们的服务器/节点应用程序每天都会失败很多次。

关于我们

我们每天有 1000 名活跃用户,在上午 10 点到下午 5 点之间的任何给定时间,整个应用程序上有 10 到 20 个并发用户。

我们使用 Sentry 记录在 FE 上发出的每个请求的每个错误(我们使用 try...catch 块系统,在每个 catch 块中,都有一个 Sentry 请求来报告该错误)。

我们的问题

我们每天会收到大约 4000 个错误事件(我知道非常高),但是报告了 3200 个,或者说大约 80% 是response: [undefined]. 这使得我们无法真正找到并解决我们的用户遇到的真正错误,除非他们直接向我们报告。

我知道有很多关于 Axios 的“网络错误”的对话,因为当浏览器中出现网络错误时,Axios 无法始终如一地准确报告,但显然有些东西导致这个错误每天发生数千次用户数。

一些可能有帮助的附加信息:

我希望找到什么

我相信 Axios 没有任何问题。我想知道我们的 CORS 配置是否不正确,导致错误的比例很低,或者我们的 Axios 配置不正确,或者我们后端的某些东西导致我们的服务器/系统/任何东西崩溃了很多。

任何经验或想法将不胜感激!我们被这件事困扰着。

下面看看我们的axiosConfig.js

// This is copied to /marketing-website/utils/api/api-service
// If it needs updated for any reason, please update /marketing-website if applicable
// Packages
import axios from 'axios'
import axiosRetry from 'axios-retry'
import { v4 as uuidv4 } from 'uuid'
// Helpers
import { getCurrentJWTToken } from './cognito'
import { CError } from './consoleMixin'

const BASE_API = async () => {
  const jwtToken = await getCurrentJWTToken()
  return {
    headers: {
      authorization: jwtToken,
      indempotency: uuidv4(),
      'content-type': 'application/json',
    },
    baseURL: process.env.VUE_APP_BASE_URL
  }
}

// we don't necessarily want to hit the same base url every time, if we use other apis
// this is a place we can initialize the headers we send everywhere as well
const API = async () => {
  const api = axios.create({
    ...await BASE_API(),
  })
  const userAgent = window.navigator.userAgent
  // If user is on Edge or IE, return now
  if (userAgent.includes('Trident') || userAgent.includes('Edge')) {
    return api
  }
  // Breaks login on Edge/IE
  api.interceptors.request.use((config) => {
    config.metadata = { startTime: new Date() }
    // If a controlledClient currently exists, send their information to the backend
    const controlledClient = localStorage.getItem('controlledClient')
    if (controlledClient) {
      if (!config.params) {
        config.params = { controlledClientUser: JSON.parse(controlledClient).id }
      } else {
        const configParams = config.params
        configParams.controlledClientUser = JSON.parse(controlledClient).id
        config.params = configParams
      }
    }

    return config
  }, (error) => {
    this.CError(error)
    return Promise.reject(error)
  })
  api.interceptors.response.use(
    (response) => {
      response.config.metadata.endTime = new Date()
      response.duration = response.config.metadata.endTime - response.config.metadata.startTime
      // CLog('request and time:', response.config.url, response.duration)
      try {
        // remove any id numbers from the end since we're grouping by API call type
        const urlSplits = response.config.url.split('/');
        const last = urlSplits[urlSplits.length - 1]
        const url = isNaN(last)
          ? response.config.url
          : response.config.url.split(`/${last}`)[0]

        let role = 'UNAUTHENTICATED';
        const user = JSON.parse(localStorage.getItem('user'))
        if (user && user.employee) role = 'EMPLOYEE'
        if (user && user.client) role = 'CLIENT'
        if (window.ga) {
          window.ga('send', {
            hitType: 'timing',
            timingCategory: `API REQUEST ${role} ${response.config.method.toUpperCase()}`,
            timingVar: url,
            timingValue: response.duration,
            label: url
          })
        }
      } catch (err) {
        CError('Error with google analytics', err)
      }
      return response
    },
    (error) => {
      CError('GENERAL ERROR:', error)
      error.config.metadata.endTime = new Date()
      error.duration = error.config.metadata.endTime - error.config.metadata.startTime
      CError('AXIOS ERROR:', error.config, error.response)

      try {
        CError('Stringified:', JSON.stringify(error))
      } catch (err) {
        CError('Error with stringify error')
      }
      if (error.response && error.response.status === 401) {
        if (
          error.response.data.message.includes === 'User not found'
          || error.response.data.name === 'AuthError')
        {
          window.open('/login', '_self')
        }
      }
      return Promise.reject(error)
    }
  );
  const retryDelay = (retryNumber = 0) => {
    // eslint-disable-next-line no-restricted-properties
    const seconds = Math.pow(2, retryNumber) * 1000;
    const randomMs = 1000 * Math.random();
    return seconds + randomMs;
  };
  axiosRetry(api, {
    retries: 3,
    retryDelay,
    // retry on Network Error
    retryCondition: axiosRetry.isNetworkError,
  });
  return api
}

export default API

这是我本地的堆栈跟踪: 在此处输入图像描述

标签: node.jsajaxvue.jsaxioscors

解决方案


推荐阅读