错误监控

1,003 阅读4分钟

一、错误异常类型

try-catch

  1. 基础语法:
  try{
      console.log(window.a.b)
  }catch{
    console.log(e)
  }
  1. 存在问题
  • 只能捕获同步错误,捕获不了异步错误,如下是捕获不了的
  try{
    setTimeout(()=>{
      console.log(window.a.b)
    },2000)
  }catch{
    console.log(e)
  }
  // Uncaught TypeError: Cannot read property 'b' of undefined
  • 捕获不了语法错误
  try{
    let name = 'hello world;
    console.log(name)
  }catch{
    console.log(e)
  }
  // SyntaxError: Unterminated string constant

window.onError获取

  1. window.onError是全局捕获方式,可以捕获同步和异步错误,捕获到的信息也比较丰富,可以得到具体的异常信息、异常文件的URL、异常的行号与列号及异常的堆栈信息。一般写在代码的最前边
throw new Error('啦啦啦=====')
window.onerror = function(message, source, lineno, colno, error) { 
  函数参数:
  *   message:错误信息(字符串)。可用于HTML onerror=""处理程序中的event。
  *   source:发生错误的脚本URL(字符串)
  *   lineno:发生错误的行号(数字)
  *   colno:发生错误的列号(数字)
  *   error:Error对象
  *   若该函数返回true,则阻止执行默认事件处理函数,如异常信息不会在console中打印。没有返回值或者返回值为false的时候,异常信息会在console中打印
  console.log('message',message)
  console.log('source', source)
  console.log('lineno',lineno)
  console.log('colno',colno)
  console.log('error', error)
 }
// message Uncaught Error: 啦啦啦=====
// source http://localhost:3000/static/js/vendors~main.chunk.js
// lineno 25431
// colno 9
// error Error: 啦啦啦=====
    at App (App.js:5)
    at renderWithHooks (react-dom.development.js:14985)
    at mountIndeterminateComponent (react-dom.development.js:17811)
    at beginWork (react-dom.development.js:19049)
    at HTMLUnknownElement.callCallback (react-dom.development.js:3945)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:3994)
    at invokeGuardedCallback (react-dom.development.js:4056)
...
  1. 缺点有两个,一是捕获不到网络异常错误,比如静态资源加载错误,二是跨域脚本无法捕获到正确的异常信息,这种情况会统一返回一个Script error,可以通过在script标签上使用crossorigin属性来规避这个问题

addEventListener('error')

  1. 监听js运行时错误事件,与onerror的功能大体类似,但是没有onerror打印信息丰富,但可以捕获网络资源加载错误
  2. window.addEventListener的第三个参数:useCapture,默认值为false(即 使用事件冒泡),为true则是事件捕获
  3. 【注意!】如果与window.onerror一起使用要注意去重,因为都可以捕获js错误
window.addEventListener('error', function(event) { ... })

当资源(如img或script)加载失败,加载资源的元素会触发一个Event接口的error事件,并执行该元素上的onerror()处理函数。这些error事件不会向上冒泡到window,但可以在捕获阶段被捕获
因此如果要全局监听资源加载错误,需要在捕获阶段捕获事件
//图片加载失败使用默认图片,依旧加载失败超过三次使用base64图片
window.addEventListener('error',function(e){
    let target = e.target, // 当前dom节点
        tagName = target.tagName,
        count = Number(target.dataset.count ) || 0, // 以失败的次数,默认为0
        max= 3; // 总失败次数,此时设定为3
    // 当前异常是由图片加载异常引起的
    if( tagName.toUpperCase() === 'IMG' ){
        if(count >= max){
            target.src = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD//AK3/ALYH+5hX6FV5N4Y/5GHwx/vyf+iJa9ZrysPhoYVShDZu/potDmwWFhhIzhT2bv6aLQ//Z';
        }else{
            target.dataset.count = count + 1;
            target.src = '//xxx/default.jpg';
        }
    }
},true)

window.addEventListener('unhandledrejection')

捕获异步错误的方式。没有catch的promise错误,没有办法被window.onerror和try-catch捕获到,这时候就需要unhandledrejection

//捕获到的错误信息在err.reason上
new Promise((resolve,reject)=>{
    setTimeout(()=>{
        reject('not found')
    })
})
window.addEventListener('unhandledrejection',function (err: any) {
    console.log(err.reason)
})
// Uncaught (in promise) not found

iFrame 异常

<iframe src="./iframe.html"></iframe>
<script>
    window.frames[0].onerror=function(message, source, lineno, colno, error) { 
        console.log('捕获到的iframe异常',message, source, lineno, colno, error)
    }
</script>
捕获到的iframe异常 Uncaught TypeError: Cannot read property 'b' of undefined http://localhost:3000/iframe.html 11 40 TypeError: Cannot read property 'b' of undefined

崩溃和卡顿

网页响应慢和奔溃的情况,卡顿即js没有办法及时执行代码,奔溃,即js不执行了。可以利用window的load和beforeLoad,以及serviceWorker开启一个线程进行监控

第三方库的异常捕获能力

  1. vue的话.vue文件里的错误onerror是捕获不到的,这时候要在全局的Vue.config.errorHandler 中捕获错误
Vue.config.errorHandler = function (err, vm, info) {
    console.log(err)
}
  1. react的ErrorBoundary 错误边界是一种 React 组件,这种组件可以捕获发生在其子组件树任何位置的 JavaScript 错误,并打印这些错误,同时展示降级 UI,而并不会渲染那些发生崩溃的子组件树。错误边界在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误。

接口请求的错误信息

接口请求的错误信息,接口错误信息的捕获一般会在axios的拦截器里边写,当接口code!==0,或者是catch捕获到错误时上报。

  • 这里着重说一下catch中捕获到的错误,有时候会出现JSON.stringify之后的错误信息是空对象的情况。这是因为error的具体错误信息有时候是不可枚举的,这时候就用上了JSON.stringfy的第二个参数
    console.log(JSON.stringify(error,Object.getOwnPropertyNames(error)))
    

二、生产环境下的错误异常处理问题

sourcemap

1.生产环境下的所有报的错误信息都显示在第一行了,这是因为在生产环境下被压缩了,而保留sourcemap就可以利用webpack打包后生成的.map文件来追踪具体的错误位置。但这种做法会把源代码暴露给用户,推荐的方式是在服务端对接收到的错误信息使用source-map解析。
2.webpack自定义插件实现sourcemap自动上传