跳到主要内容

异常监控平台如何设计?

基本点
  1. 错误采集
  2. 上报
  3. 数据汇总&清洗
  4. 数据统计
  5. 可视化
  6. 告警

1.错误采集

常见的 JavaScript 错误

  1. 语法错误 SyntaxError

一般来说在开发构建阶段就会被编译器捕获,如果生产需要捕获 SyntaxError 的话,需要使用分开的脚本去捕获

<script>
// 要捕获语法错误,需要使用分开的 <script>
window.onerror = function(message, source, lineno, colno, error) {
console.log('捕获到异常:',{message, source, lineno, colno, error});
}
</script>
<script>
const notdefined,
</script>

监控代码和业务代码不能放在同一个 <script> 标签中,否则此时 onerror 还没生效:Can syntax errors be caught in JavaScript?

  1. 类型错误 TypeError
  2. 引用错误 ReferenceError
  3. 范围错误 RangeError

这些错误可以通过 window.onerror 捕获:

window.onerror = function (message, source, lineno, colno, error) {
console.log('捕获到异常:', { message, source, lineno, colno, error })
}
// TypeError 可以捕获 ✅
const notdefined = 1
console.log(notdefined.a.b)

// ReferenceError 可以捕获 ✅
console.log(notdefined)

// RangeError 可以捕获 ✅
console.log(new Array(-1))

资源错误

资源错误一般指图片、script、css 加载错误,这些错误不能通过 window.onerror 捕获,需要使用 window.addEventListener 捕获:

<script>
// 资源错误,不能捕获 ❌
window.onerror = function (message, source, lineno, colno, error) {
console.log('捕获到异常:', { message, source, lineno, colno, error })
return true
}

// 图片、script、css加载错误,都能被捕获 ✅
window.addEventListener(
'error',
(error) => {
console.log('捕获到异常:', error)
},
true
)
</script>
<img src="https://yun.tuia.cn/image/kkk.png" />
<script src="https://yun.tuia.cn/foundnull.js"></script>
<link href="https://yun.tuia.cn/foundnull.css" rel="stylesheet" />

为什么 window.onerror 不能捕获资源错误,而 window.addEventListener 可以捕获资源错误? 🚧 WIP

未捕获的 Promise 错误

<script>
// 全局统一处理Promise
window.addEventListener("unhandledrejection", function(e){
console.log('捕获到异常:', e);
});
fetch('https://wukaipeng.com/test')
</script>

React 错误

主要使用 componentDidCatch 方法捕获 React 错误:

import * as React from 'react';

class ErrorListening extends React.Component {
constructor(props) {
super(props);
}
componentDidCatch(error, info) {
console.log("error", error)
}
}

在项目中使用:

import React from "react"; 

<ErrorListening>
<App />
</ErrorListening>

Vue 错误

Vue 使用 app.config.errorHandler 配置全局错误处理函数:

app.config.errorHandler = (err, instance, info) => {
// handle error, e.g. report to a service
}

2. 上报

使用 1×11\times1px 的透明 GIF 图片上报错误:

  1. 为什么不使用 GET/POST/HEAD 上报错误?因为存在跨域问题,一般上报接口和当前业务都是不同域
  2. 为什么不使用 JS/CSS/TTF 其他资源上报呢?因为其他资源需要创建元素节点并插入到 DOM 中才会发送请求,而且 JS/CSS 会阻塞渲染主线程。图片只需要 new Image() 即可发送请求,也不会阻塞渲染主线程。
  3. 为什么不使用 PNG/JPEG/BMP 文件格式呢?首先 1×11\times1px 的图片体积是最小的合法图片,只需要透明色,不需要颜色,所以排除 JPEG,其次 GIF 比 PNG/BMP 更小。

Reference

一篇讲透自研的前端错误监控