Skip to content

前端错误监控

捕获错误

1. 捕获通用错误

window.addEventListener('error') 可以捕获图片、script、css 加载错误。 new Image错误,不能捕获。

另外使用 window.onerror 捕获错误。常规错误和异步错误能捕获。语法错误 和 资源加载错误 不能捕获。

2.unhandledrejection 捕获 Promise 加载错误

js
// 全局统一处理Promise
window.addEventListener("unhandledrejection", function (e) {
  console.log("捕获到异常:", e);
});
fetch("https://tuia.cn/test");

Vue 错误

Vue 里面出现的错误,并不会直接被 window.onerror 捕获,而是会抛给 Vue.config.errorHandler。

js
/**
 * 全局捕获Vue错误,直接扔出给onerror处理
 */
Vue.config.errorHandler = function (err) {
  setTimeout(() => {
    throw err;
  });
};

React 错误

通过捕获 componentDidCatch

jsx
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // 更新 state 使下一次渲染能够显示降级后的 UI
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    // 你同样可以将错误日志上报给服务器
    logErrorToMyService(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // 你可以自定义降级后的 UI 并渲染
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children;
  }
}

class App extends React.Component {
  render() {
    return (
      <ErrorBoundary>
        <MyWidget />
      </ErrorBoundary>
    );
  }
}

上报接口

为什么使用 1 x 1 的 gif ?

  • 没有跨域问题, 不会携带当前域名 cookie
  • 不会阻塞页面加载,影响用户体验,只需 new Image 对象
  • 相比于 BMP / PNG 体积最小, 可以节约 41% / 35% 网络资源

采集聚合端

错误标识

1. 单个错误条目

生成单个错误的唯一标识,通过 date 和随机值生成一条对应的错误条目 id。

js
const errorKey = `${+new Date()}@${randomString(8)}`;

function randomString(len) {
  len = len || 32;
  let chars = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678";
  let maxPos = chars.length;
  let pwd = "";
  for (let i = 0; i < len; i++) {
    pwd += chars.charAt(Math.floor(Math.random() * maxPos));
  }
  return pwd;
}

randomString(8); // 2NwDAJjA

2. 单个错误事件 errorKey

可以统计这个错误事件的发生的次数。 通过 message、colno 与 lineno 进行相加计算阿斯克码值,可以生成错误的 errorKey。

js
const eventKey = compressString(String(e.message), String(e.colno) + String(e.lineno));

function compressString(str, key) {
  let chars = "ABCDEFGHJKMNPQRSTWXYZ";
  if (!str || !key) {
    return "null";
  }
  let n = 0,
    m = 0;
  for (let i = 0; i < str.length; i++) {
    n += str[i].charCodeAt();
  }
  for (let j = 0; j < key.length; j++) {
    m += key[j].charCodeAt();
  }
  let num = n + "" + key[key.length - 1].charCodeAt() + m + str[str.length - 1].charCodeAt();
  if (num) {
    num = num + chars[num[num.length - 1]];
  }
  return num;
}

错误过滤(SDK 配合)

域名过滤

只过滤本页面的 script error, 根据 domain 进行过滤

js
// 伪代码
if (!e.filename || !e.filename.match(/^(http|https):\/\/yun./)) return true;

重复上报

根据 errorKey 判断重复上报的条数不能超过阀值。

错误接收

每秒设置一定的阀值,减少请求量。

错误存储

使用阿里云的服务进行存储。

可视分析端(可视化平台)