前端监控sdk开发(二)js错误捕获

引言

在前端开发中,JS错误是一个常见的问题,它可能导致页面崩溃、功能异常或用户体验下降。为了及时发现和解决这些问题,我们需要在前端监控SDK中实现JS错误的捕获。

监听全局未捕获的错误

window.addEventListener('error', callback)

js 复制代码
window.addEventListener('error', function(event) {
    let lastEvent = getLastEvent()
    if (event.target && (event.target.src || event.target.href)) {
      tracker.send({
        kind: 'stability', // 稳定性(大类)
        type: 'error', // 小类(js error)
        errorType: 'resourceError', // 资源加载错误
        tagName: event.target.tagName, // 报错标签
        filename: event.target.src || event.target.href, // 报错文件
        selector: getSelector(event.target) // 最后一个操作的元素
      })
    } else {
      tracker.send({
        kind: 'stability', // 稳定性(大类)
        type: 'error', // 小类(js error)
        errorType: 'jsError', // JS执行错误
        message: event.message, // 报错信息
        filename: event.filename, // 报错文件
        position: `${event.lineno}:${event.colno}`, // 报错位置 行:列
        stack: getStackLines(event.error.stack),
        selector: lastEvent ? getSelector(lastEvent.path) : '' // 最后一个操作的元素
      })
    }
}, true)

这段代码是用来监听全局错误事件的。当页面发生错误时,会触发error事件,并执行相应的处理逻辑。

首先,代码通过window.addEventListener('error', function(event) { ... })来添加一个错误事件的监听器。

在事件处理函数中,首先通过getLastEvent()函数获取最后一个事件对象。

然后,代码判断错误类型。如果错误是由于资源加载失败引起的(例如图片加载失败),则会发送一个资源加载错误信息。这个信息包括了错误的类型、标签名、文件名和最后一个操作的元素等。

如果错误是由于JS执行引起的(例如语法错误或未定义变量等),则会发送一个JS执行错误信息。这个信息包括了错误的类型、报错信息、报错文件名、报错位置和堆栈跟踪等。

最后,根据不同类型的错误,调用tracker.send()方法发送相应的错误信息到服务器进行记录和分析。

getLastEvent.js

js 复制代码
let lastEvent
['click', 'touchstart', 'mousedown', 'keydown', 'mouseover'].forEach(eventType => {
  document.addEventListener(eventType, (event) => {
    lastEvent = event
  }, {
    capture: true,
    passive: true // 默认不阻止默认事件
  })
})
export default function () {
  return lastEvent
}

通过监听多个事件(包括clicktouchstartmousedownkeydownmouseover)来更新这个变量的值。代码通过循环遍历事件类型数组 ,对每个事件类型都调用 document.addEventListener(eventType, (event) => { ... }) 来添加事件监听器。

在事件监听器中,当事件触发时,会将当前的事件对象赋值给 lastEvent 变量。这样就可以实时更新 lastEvent 变量的值为最后一个触发的事件对象。

在添加事件监听器时,还传入了一个配置对象 { capture: true, passive: true }。其中 capture: true 表示在捕获阶段处理事件,而不是冒泡阶段;而 passive: true 表示默认不阻止默认事件的执行。

最后,代码通过导出一个函数来返回 lastEvent 变量的值。这样其他模块可以通过调用这个函数来获取最后一个触发的事件对象。

总体来说,对多个不同类型的事件进行监听,并记录最后一个触发的事件对象。这可以用于在需要获取最后一次用户操作或交互行为时使用。

getSelector.js

js 复制代码
// 定义一个函数,用于根据路径数组生成选择器字符串
function getSelectors(path) {
  // 反转路径数组,并过滤掉 window 和 document 元素
  return path.reverse().filter(element => {
    return element !== window && element !== document;
  }).map((element) => {
    // 根据元素的属性生成选择器字符串
    if (element.id) {
      return `${element.tagName.toLowerCase()}#${element.id}`;
    } else if (element.className && typeof element.className === 'string') {
      return `${element.nodeName.toLowerCase()}.${element.className}`;
    } else {
      return element.nodeName.toLowerCase();
    }
  }).join(' '); // 将选择器字符串连接起来并返回
}

// 默认导出一个函数,用于根据路径数组或目标元素生成选择器字符串
export default function (pathsOrTarget) {
  if (Array.isArray(pathsOrTarget)) { // 如果传入的是路径数组
    return getSelectors(pathsOrTarget); // 调用 getSelectors 函数生成选择器字符串并返回
  } else { // 如果传入的是目标元素
    let path = [];
    while (pathsOrTarget) { // 循环遍历目标元素的父节点,将其添加到路径数组中
      path.push(pathsOrTarget);
      pathsOrTarget = pathsOrTarget.parentNode;
    }
    return getSelectors(path); // 调用 getSelectors 函数生成选择器字符串并返回
  }
}

getSelectors 函数用于根据路径数组生成选择器字符串,而默认导出的函数则用于根据路径数组或目标元素生成选择器字符串。

getSelectors 函数首先对路径数组进行反转,并使用 filter 方法过滤掉 windowdocument 元素。然后使用 map 方法遍历剩余的元素,并根据元素的属性生成相应的选择器字符串。如果元素有 id 属性,则返回类似于 <tagName>#<id> 的格式;如果元素有 className 属性,则返回类似于 <tagName>.<className> 的格式;否则,只返回标签名。最后,使用 join 方法将选择器字符串连接起来并返回。

默认导出的函数根据传入的参数类型进行判断。如果传入的是路径数组,则直接调用 getSelectors 函数生成选择器字符串并返回。如果传入的是目标元素,则通过循环遍历目标元素的父节点,将其添加到路径数组中,然后再调用 getSelectors 函数生成选择器字符串并返回。

getStackLines方法

js 复制代码
function getStackLines(stack) {
    return stack.split('\n').slice(1).map(item => item.replace(/^\s+at\s+/g, "")).join('^')
  }

首先使用 split('\n') 方法将堆栈跟踪字符串按换行符分割成数组。 然后使用 slice(1) 方法去掉数组中的第一行,因为第一行通常是错误信息本身,不包含具体的调用栈信息。

接下来,使用 map() 方法遍历数组中的每一行,并使用正则表达式和 replace() 方法将每行开头的 "at " 替换为空字符串,以去除每行前面的 "at " 标识。

最后,使用 join('^') 方法将处理后的数组元素连接成一个字符串,并以 "^" 符号作为分隔符。 这样就得到了经过处理的堆栈跟踪信息字符串。该函数通常用于提取和格式化错误堆栈跟踪信息,以便更好地理解和调试错误。

event错误信息

处理后需上传的数据

json 复制代码
{
    "title": "前端监控",
    "url": "http://127.0.0.1:5500/projects/bug-report/packages/sdk/src/index.html",
    "timestamp": 1703152216271,
    "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
    "kind": "stability",
    "type": "error",
    "errorType": "jsError",
    "message": "Uncaught TypeError: Cannot set properties of undefined (setting 'error')",
    "filename": "http://127.0.0.1:5500/projects/bug-report/packages/sdk/src/index.html",
    "position": "47:28",
    "stack": "errorClick (http://127.0.0.1:5500/projects/bug-report/packages/sdk/src/index.html:47:28)^HTMLInputElement.onclick (http://127.0.0.1:5500/projects/bug-report/packages/sdk/src/index.html:19:67)",
    "selector": ""
}

总结

通过addEventListener方法监听全局的error事件,当页面中加载资源(如图片、脚本等)失败时,会触发该事件,并将相关信息传递给回调函数。我们可以通过监听error事件来捕获资源加载失败的错误。

相关推荐
轻口味1 小时前
【每日学点鸿蒙知识】AVCodec、SmartPerf工具、web组件加载、监听键盘的显示隐藏、Asset Store Kit
前端·华为·harmonyos
alikami1 小时前
【若依】用 post 请求传 json 格式的数据下载文件
前端·javascript·json
吃杠碰小鸡1 小时前
lodash常用函数
前端·javascript
emoji1111112 小时前
前端对页面数据进行缓存
开发语言·前端·javascript
泰伦闲鱼2 小时前
nestjs:GET REQUEST 缓存问题
服务器·前端·缓存·node.js·nestjs
m0_748250032 小时前
Web 第一次作业 初探html 使用VSCode工具开发
前端·html
一个处女座的程序猿O(∩_∩)O2 小时前
vue3 如何使用 mounted
前端·javascript·vue.js
m0_748235952 小时前
web复习(三)
前端
AiFlutter2 小时前
Flutter-底部分享弹窗(showModalBottomSheet)
java·前端·flutter
麦兜*2 小时前
轮播图带详情插件、uniApp插件
前端·javascript·uni-app·vue