不是,前端异常信息你还不会收集?

819 阅读3分钟

前言

小编是一名清澈的大学生,在速通了好多家中小厂后,在自信心的驱使下,对大厂发起攻势,经历了三次大厂面试的告警平台拷打后,决定好好沉淀并记录告警平台学习日志......,如果有什么学习建议也希望各位能在评论区指导指导,大家一起进步。

正文

面试官:“收集报错信息,具体收集了哪些报错?”

错误信息:

  • 同/异步异常 window.addEventListener('error')

  • 接口异常 xhr、fetch

  • 资源异常(element.localName)

  • 其它常见异常 unhandleRejection

  • 框架异常:

    Vue: vue.config.errorHandler

    React: componentDidCatch

xhr、fetch 接口异常收集

面试官:“通过重写 xhr、fetch 拦截器获取请求信息,根据什么原理来完成,简单说一下实现的流程?”

xhr---> 找到对应的原型,并重写原型上的方法(XMLHttpRequest.prototype)

重写了两个方法: open(在请求初始化时获取请求信息)、send(发送请求时添加额外埋点,通过监听loadend事件上报请求信息)

简易版的 xhr 请求数据拦截实现如下(上报过程未实现):

image.png 实现效果如图:

image.png

fetch---> 返回的是一个 promise ,所以可以在请求发送前和 .then 方法里做拦截

简易版的拦截如下(上报过程未实现):

image.png

效果如图所示:

image.png

代码 & 资源 信息拦截

可以通过监听 window.error 事件捕获错误,对于资源加载错误,可以通过 Element.localName来获取,面试官:“对于编码错误,线上的代码都是压缩混淆的,如何知道是哪个脚本哪一行代码出错呢?”

相信不少人对于报错信息还是很熟悉的,如图:

屏幕截图 2025-05-02 184626.png

不同的浏览器报错堆栈的格式是不一样的,所以我们要定义正则表达式匹配不同的格式,将堆栈信息按行分割为数组,遍历堆栈的每一行,依次尝试匹配不同的正则表达式,匹配成功提取堆栈信息并存储,如果堆栈中包含 eval ,还要进一步解析 eval

//正则表达式
chrome =
      /^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|webpack|<anonymous>|[a-z]:|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i,
    gecko =
      /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|\[native).*?|[^@]*bundle)(?::(\d+))?(?::(\d+))?\s*$/i,
    winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i,
    //eval
    geckoEval = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i,
    chromeEval = /\((\S*)(?::(\d+))(?::(\d+))\)/,

遍历过程如下:

image.png

这样我们就可以针对代码&资源进行错误拦截,具体代码如下:

image.png

实现效果如图:

image.png

promise异常

如果没有通过try...catch...局部捕获 promise 异常,就需要通过监听 unhandledRejection 事件捕获异常

window.addEventListener('unhandledrejection', function (ev) {
        // Promise错误
        let data = {
          type: 'PROMISE_ERROR',
          message: JSON.stringify(ev.reason),
          url: window.location.href,
          name: ev.type,
          time: Date.now(),
          level: 'Low',
        };
        console.log('Promise错误 ---> ', data);
      });

实现效果如图:

image.png

Vue 框架异常

Vue 提供了app.config.errorHandler用于为应用内抛出的未捕获错误指定一个全局处理函数。它可以从下面这些来源中捕获错误:

  • 组件渲染器
  • 事件处理器
  • 生命周期钩子
  • setup() 函数
  • 侦听器
  • 自定义指令钩子
  • 过渡 (Transition) 钩子

React 框架异常

React 可以使用 componentDidCatch 这一生命周期方法用于捕获子组件树中抛出的 JavaScript 错误,具体的就不过多介绍了。

总结

在本文中,我们通过重写原生对象 XHR 的属性,根据 promise 特性对接口信息的收集,监听 error 和 unhandledRejection 事件捕获资源、编码以及其它报错,全方位地覆盖了前端应用中可能出现的报错场景,但是对于前端监控来说,页面报错仅仅只是技术层面的观测指标,并未能体现用户视角的感受,所以我们还需统计各项页面性能指标(FP、FCP、CLS等等指标)本文只介绍了如何收集报错信息,未实现的上报过程以及对错误信息统一格式化将在下一篇介绍......