搭建前端异常监控系统

搭建前端异常监控系统

涉及技能

  • 收集前端错误(原生、React、Vue)
  • 编写错误上报逻辑
  • 利用Egg.js编写一个错误日志采集服务
  • 编写webpack插件自动上传sourcemap
  • 利用sourcemap还原压缩代码源码位置
  • 利用Jest进行单元测试

工作流程

  1. 收集错误
  2. 上报错误
  3. 代码上线打包将sourcemap文件上传至错误监控服务器
  4. 发生错误时监控服务器接收错误并记录到日志中
  5. 根据sourcemap和错误日志内容进行错误分析

异常收集

首先先看看如何捕获异常。

JS异常

js异常的特点是,出现不会导致JS引擎崩溃 最多只会终止当前执行的任务。比如一个页面有两个按钮,如果点击按钮发生异常页面,这个时候页面不会崩溃,只是这个按钮的功能失效,其他按钮还会有效。

   setTimeout(() => {
  console.log('1->begin')
  error
  console.log('1->end')
})
setTimeout(() => {
  console.log('2->begin')
  console.log('2->end')
})
复制代码

上面的例子我们用setTimeout分别启动了两个任务,虽然第一个任务执行了一个错误的方法。程序执行停止了。但是另外一个任务并没有收到影响。

其实如果你不打开控制台都看不到发生了错误。好像是错误是在静默中发生的。

下面我们来看看这样的错误该如何收集。

try-catch

我们首先想到的使用try-catch来收集。

   setTimeout(() => {
  try {
    console.log('1->begin')
    error
    console.log('1->end')
  } catch (e) {
    console.log('catch',e)
  }
})
复制代码

如果在函数中错误没有被捕获,错误会上抛。

   function fun1() {
  console.log('1->begin')
  error
  console.log('1->end')
}
setTimeout(() => {
  try {
    fun1()
  } catch (e) {
    console.log('catch',e)
  }
})
复制代码

控制台中打印出的分别是错误信息和错误堆栈。

读到这里大家可能会想那就在最底层做一个错误try-catch不就好了吗。但是理想很丰满,现实很骨感。我们看看下一个例子。

   function fun1() {
  console.log('1->begin')
  error
  console.log('1->end')
}

try {
  setTimeout(() => {
    fun1()

  })
} catch (e) {
  console.log('catch', e)
}
复制代码

大家注意运行结果,异常并没有被捕获。

这是因为JS的try-catch功能非常有限一遇到异步就不好用了。那总不能为了收集错误给所有的异步都加一个try-catch吧,太坑爹了。

window.onerror

window.onerror 最大的好处就是可以同步任务还是异步任务都可捕获。

   function fun1() {
  console.log('1->begin')
  error
  console.log('1->end')
}
window.onerror = (...args) => {
  console.log('onerror:',args)
}

setTimeout(() => {
  fun1()
})
复制代码
  • onerror返回值

    onerror还有一个问题大家要注意 如果返回返回true 就不会被上抛了。不然控制台中还会看到错误日志。

监听error事件

window.addEventListener('error',() => {})

其实onerror固然好但是还是有一类异常无法捕获。这就是网络异常的错误。比如下面的例子。

   
复制代码

试想一下我们如果页面上要显示的图片突然不显示了,而我们浑然不知那就是麻烦了。

addEventListener就是

   window.addEventListener('error', args => {
    console.log(
      'error event:', args
    );
    return true;
  }, 
  true // 利用捕获方式
);
复制代码

Promise异常捕获

Promise的出现主要是为了让我们解决回调地域问题。基本是我们程序开发的标配了。虽然我们提倡使用es7 async/await语法来写,但是不排除很多祖传代码还是存在Promise写法。

   new Promise((resolve, reject) => {
  abcxxx()
});
复制代码

这种情况无论是onerror还是监听错误事件都是无法捕获的

   new Promise((resolve, reject) => {
  error()
})
// 增加异常捕获
  .catch((err) => {
  console.log('promise catch:',err)
});
复制代码

除非每个Promise都添加一个catch方法。但是显然是不能这样做。

   window.addEventListener("unhandledrejection", e => {
  console.log('unhandledrejection',e)
});
复制代码

我们可以考虑将unhandledrejection事件捕获错误抛出交由错误事件统一处理就可以了

   window.addEventListener("unhandledrejection", e => {
  throw e.reason
});
复制代码

async/await异常捕获

   const asyncFunc = () => new Promise(resolve => {
  error
})
setTimeout(async() => {
  try {
    await asyncFun()
  } catch (e) {
    console.log('catch:',e)
  }
})
复制代码

实际上async/await语法本质还是Promise语法。区别就是async方法可以被上层的try/catch捕获。

如果不去捕获的话就会和Promise一样,需要用unhandledrejection事件捕获。这样的话我们只需要在全局增加unhandlerejection就好了。

小结

异常类型 同步方法 异步方法 资源加载 Promise async/await
try/catch ✔️ ✔️
onerror ✔️ ✔️
error事件监听 ✔️ ✔️ ✔️
unhandledrejection事件监听 ✔️ ✔️

实际上我们可以将unhandledrejection事件抛出的异常再次抛出就可以统一通过error事件进行处理了。

最终用代码表示如下:

   window.addEventListener("unhandledrejection", e => {
  throw e.reason
});
window.addEventListener('error', args => {
  console.log(
    'error event:', args
  );
  return true;
}, true);
复制代码

Webpack工程化

现在是前端工程化的时代,工程化导出的代码一般都是被压缩混淆后的。

比如:

   setTimeout(() => {
    xxx(1223)
}, 1000)
复制代码

出错的代码指向被压缩后的JS文件。

如果想将错误和原有的代码关联起来就需要sourcemap文件的帮忙了。

sourceMap是什么

简单说, sourceMap就是一个文件,里面储存着位置信息。

仔细点说,这个文件里保存的,是转换后代码的位置,和对应的转换前的位置。

那么如何利用sourceMap对还原异常代码发生的位置这个问题我们到异常分析这个章节再讲。

Vue

创建工程

利用vue-cli工具直接创建一个项目。

   # 安装vue-cli
npm install -g @vue/cli

# 创建一个项目
vue create vue-sample

cd vue-sample
npm i
// 启动应用
npm run serve

复制代码

为了测试的需要我们暂时关闭eslint 这里面还是建议大家全程打开eslint

在vue.config.js进行配置

   module.exports = {   
  // 关闭eslint规则
  devServer: {
    overlay: {
      warnings: true,
      errors: true
    }
  },
  lintOnSave:false
}
复制代码

我们故意在src/components/HelloWorld.vue

   
                    
                    

你可能感兴趣的:(前端,异常,监控)