宇宙最全的js错误监控系统(上)

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情

前言

前端最怕控制台“爆红”,特别是生产环境“爆红”,这样的话就会算作线上故障,可能导致一年白干;为了避免线上报错导致用户长时间的无法使用,我们需要设计一个错误告警系统,遇到错误能够及时上报给开发者,这样我们总能在第一时间修复bug,减少损失;但是首先我们需要搞清楚js中有哪些错误类型如何捕捉它们;

js中的错误类型就像是一种属性,都有与之对应的属性克制它(也就是对应的监听的事件)

window.onerror

负责监控js运行时错误

我们故意写一段错误代码,假设后端返回了一个null:

const backendData = null
const dataSource = backendData.list
复制代码

放到控制台执行一下:报错,这就是js语法错误 image.png 那么这种错误如何捕捉呢?使用window.onerror,window.onerror能够拿到我们控制台报错的大部分信息:报错信息(msg)、行数(lineNo)、列数(columnNo),根据行列我们可以准确定位问题

window.onerror=(msg,url,lineNo,columnNo,e)=>{
    console.table({
      msg, url, lineNo, columnNo, e
    });
}
复制代码

我们再次运行上面的错误代码:错误信息先是以表格形式展示出来,然后爆红

image.png

如果非常讨厌控制台报错,可以这样做

window.onerror=(msg,url,lineNo,columnNo,e)=>{
    return true
}
复制代码

可以看到错误消失,return true能够阻止事件向上抛出 image.png

除此之外它还可以捕捉异步报错

window.onerror = (msg,url,lineNo,columnNo,e)=>{
  console.table({
    msg, url, lineNo, columnNo, e
  });
}
  setTimeout(() => {
    const obj = null
    console.log(obj.name)
  })
复制代码

image.png

然后我们把上面的同步错误代码用另一个server跑起来,然后在当前html引入,发现捕捉到的错误信息是这样的:

image.png

很伤心没有任何错误提示,只有一个Script error,对于我们来说没有任何提示作用,这里我们暂且放下不讲,等到后面的章节揭开谜底,我们继续看onerror还能捕捉哪些错误;

iframe错误

像上面一样我们把错误代码塞到一个html中然后ifame引入

image.png

居然没有捕捉到iframe的错误,只是看到控制台又恶心地爆红了;iframe的错误情况比较多,且听我细细道来;

iframe载入的脚本有以下几种情况:

  1. 载入与主页面同源的html,此时可以使用window.frames[0]拿到iframe的window,从而捕捉到错误信息
 window.frames[0].onerror = (msg,url,lineNo,columnNo,e)=>{
    console.table({
      msg, url, lineNo, columnNo, e
    });
    return true
}
复制代码
  1. 主域名相同而子域名不同,可以设置domain相同,此时我们本地无法模拟
  2. 域名完全不同,错误不受控制

unhandledrejection

Promise被reject之后没有catch就会触发该错误

该错误的捕捉很简单:监听"unhandledrejection"事件就可以了

window.addEventListener('unhandledrejection',(e)=>{
  if(isObject(e.reason)){
    console.log('未处理的unhandledrejection事件')
  } else{
    console.log(e.reason)
  }
})
复制代码

跨域脚本错误:Script error.

我们上面讲到了window.onerror无法捕捉到跨域脚本错误,怎么办呢?我们需要分多钟情况进行分析:

  1. 如果远程脚本内部运行时错误,还是报跨域脚本错误
 <script>
     window.onerror = console.log
 </script>
 <script src="http://127.0.0.1:8080/data.js" ></script>
复制代码

image.png 2. 如果远程脚本定义了一个函数,在我们当前页面执行,那么用try catch包裹之后可以捕捉到到错误信息 假设data.js暴露了一个函数load,我们在当前页面执行它

<script src="http://127.0.0.1:8080/data.js" ></script>
<script>
   try {
    load()
  } catch (error) {
    throw error
  }
</script>
复制代码

image.png 3. 如果是事件中的回调函数报错,其实是一样的处理流程,就是需要用try catch包裹这个回调函数,但是这里我们需要用到AOP的思想,去重写addEventListener方法

const originAddEventListener = EventTarget.prototype.addEventListener
EventTarget.prototype.addEventListener = (type, listener, options) => {
  const wrappedListener = (...args) => {
    try {
      return listener.apply(this, args)
    } catch (err) {
      throw err
    }
  }

  return originAddEventListener.call(this, type, wrappedListener, options)
}
复制代码

总结

window.onerror负责捕获运行时错误;iframe错误则需要取到iframe中的window再去捕获;unhandledrejction则需要我们牢记“始于promise终于catch”;而跨域脚本错误则需要用try catch去包裹执行的函数体以捕捉错误;

猜你喜欢

转载自juejin.im/post/7104974756553687048