前端监控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事件来捕获资源加载失败的错误。

相关推荐
gqkmiss24 分钟前
Chrome 浏览器 131 版本开发者工具(DevTools)更新内容
前端·chrome·浏览器·chrome devtools
Summer不秃29 分钟前
Flutter之使用mqtt进行连接和信息传输的使用案例
前端·flutter
旭日猎鹰34 分钟前
Flutter踩坑记录(二)-- GestureDetector+Expanded点击无效果
前端·javascript·flutter
Viktor_Ye40 分钟前
高效集成易快报与金蝶应付单的方案
java·前端·数据库
hummhumm42 分钟前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
乐闻x1 小时前
Vue.js 性能优化指南:掌握 keep-alive 的使用技巧
前端·vue.js·性能优化
一条晒干的咸魚1 小时前
【Web前端】创建我的第一个 Web 表单
服务器·前端·javascript·json·对象·表单
Amd7941 小时前
Nuxt.js 应用中的 webpack:compiled 事件钩子
前端·webpack·开发·编译·nuxt.js·事件·钩子
生椰拿铁You2 小时前
09 —— Webpack搭建开发环境
前端·webpack·node.js
狸克先生2 小时前
如何用AI写小说(二):Gradio 超简单的网页前端交互
前端·人工智能·chatgpt·交互