首页 > 其他分享 >前端项目异常监控-全局捕获Promise错误

前端项目异常监控-全局捕获Promise错误

时间:2023-10-07 14:36:39浏览次数:36  
标签:return log stack 捕获 element let Promise 全局 event

1.核心

全局监听unhandledrejection,该事件为Promise被reject时但没有reject处理器时(没有被catch处理),则触发该事件。( async 函数内部的异步任务一旦出现错误,那么就等同于 async 函数返回的 Promise 对象被 reject。)

2.编写辅助函数

 2.1getLastEvent获取最后一个事件

let lastEvent;

['click', 'touchstart', 'mousedown', 'keydown', 'mouseover'].forEach(
  (eventType) => {
    document.addEventListener(
      eventType,
      (event) => {
        lastEvent = event
      },
      {
        capture: true, // 是在捕获阶段还是冒泡阶段执行
        passive: true // 默认不阻止默认事件
      }
    )
  }
)

export default function () {
  return lastEvent
}

 2.2getSelector获取操作元素

function getSelectors(path) {
  // 反转 + 过滤 + 映射 + 拼接
  return path
    .reverse()
    .filter((element) => {
      return element !== document && element !== window
    })
    .map((element) => {
      console.log('element', element.nodeName)
      let selector = ''
      if (element.id) {
        return `${element.nodeName.toLowerCase()}#${element.id}`
      } else if (element.className && typeof element.className === 'string') {
        return `${element.nodeName.toLowerCase()}.${element.className}`
      } else {
        selector = element.nodeName.toLowerCase()
      }
      return selector
    })
    .join(' ')
}

export default function (pathsOrTarget) {
  if (Array.isArray(pathsOrTarget)) {
    return getSelectors(pathsOrTarget)
  } else {
    let path = []
    while (pathsOrTarget) {
      path.push(pathsOrTarget)
      pathsOrTarget = pathsOrTarget.parentNode
    }
    return getSelectors(path)
  }
}

 2.3tracker报错处理器,针对捕获到的错误后进行的操作统一处理

// 主机
// let host = 'cn-guangdong-log.aliyuncs.com'
// 项目名
// let project = 'yymonitor'
// 存储名
// let logstore = 'yymonitor-store'
let userAgent = require('user-agent')

function getExtraData() {
  return {
    title: document.title,
    url: location.href,
    timestamp: Date.now(),
    userAgent: userAgent.parse(navigator.userAgent).name
  }
}

class SendTracker {
  // constructor() {
  //   // 上报的路径
  //   this.url = `http://${project}.${host}/logstores/${logstore}/track`
  //   this.xhr = new XMLHttpRequest()
  // }
  send(data = {}) {
    let extraData = getExtraData()
    let log = { ...data, ...extraData }
    // 阿里云要求值不能为数字
    for (const key in log) {
      if (typeof log[key] === 'number') {
        log[key] = `${log[key]}`
      }
    }
    console.log('log', log)
    // 接入日志系统,此处以阿里云为例
    // let body = JSON.stringify({
    //   __logs__: [log]
    // })
    // this.xhr.open('POST', this.url, true)
    // this.xhr.setRequestHeader('Content-Type', 'application/json')
    // this.xhr.setRequestHeader('x-log-apiversion', '1.0.0')
    // this.xhr.setRequestHeader('x-log-bodyrawsize', body.length)
    // this.xhr.onload = function () {
    //   // console.log(this.xhr.response);
    // }
    // this.xhr.onerror = function (error) {
    //   console.log(error)
    // }
    // this.xhr.send(body)
  }
}

export default new SendTracker()

3.编写全局监听函数,附带捕获JS资源加载异常

import getLastEvent from '../utils/getLastEvent'
import getSelector from '../utils/getSelector'
import tracker from '../utils/tracker'

export function injectJsError() {
  // 监听全局未捕获的错误
  window.addEventListener(
    'error',
    (event) => {
      console.log('error+++++++++++', event)
      let lastEvent = getLastEvent() // 获取到最后一个交互事件
      // 脚本加载错误
      if (event.target && (event.target.src || event.target.href)) {
        tracker.send({
          kind: 'stability', // 监控指标的大类,稳定性
          type: 'error', // 小类型,这是一个错误
          errorType: 'resourceError', // js执行错误
          filename: event.target.src || event.target.href, // 哪个文件报错了
          tagName: event.target.tagName,
          selector: getSelector(event.target) // 代表最后一个操作的元素
        })
      } else {
        tracker.send({
          kind: 'stability', // 监控指标的大类,稳定性
          type: 'error', // 小类型,这是一个错误
          errorType: 'jsError', // js执行错误
          message: event.message, // 报错信息
          filename: event.filename, // 哪个文件报错了
          position: `${event.lineno}:${event.colno}`, // 报错的行列位置
          stack: getLines(event.error.stack),
          selector: lastEvent ? getSelector(lastEvent.path) : '' // 代表最后一个操作的元素
        })
      }
    },
    true
  )

  window.addEventListener(
    'unhandledrejection',
    (event) => {
      console.log('unhandledrejection-------- ', event)
      let lastEvent = getLastEvent() // 获取到最后一个交互事件
      let message
      let filename
      let line = 0
      let column = 0
      let stack = ''
      let reason = event.reason
      if (typeof reason === 'string') {
        message = reason
      } else if (typeof reason === 'object') {
        message = reason.message
        if (reason.stack) {
          let matchResult = reason.stack.match(/at\s+(.+):(\d+):(\d+)/)
          filename = matchResult[1]
          line = matchResult[2]
          column = matchResult[3]
        }
        stack = getLines(reason.stack)
      }
      tracker.send({
        kind: 'stability', // 监控指标的大类,稳定性
        type: 'error', // 小类型,这是一个错误
        errorType: 'promiseError', // js执行错误
        message, // 报错信息
        filename, // 哪个文件报错了
        position: `${line}:${column}`, // 报错的行列位置
        stack,
        selector: lastEvent ? getSelector(lastEvent.path) : '' // 代表最后一个操作的元素
      })
    },
    true
  )
}

function getLines(stack) {
  return stack
    .split('\n')
    .slice(1)
    .map((item) => item.replace(/^\s+at\s+/g, ''))
    .join('^')
}

 

标签:return,log,stack,捕获,element,let,Promise,全局,event
From: https://www.cnblogs.com/LylePark/p/17746213.html

相关文章

  • Redis学习之分布式全局id生成
    介绍为什么需要分布式全局ID生成器?对于订单这种数据,数据库自增的规律性太明显,会暴露一些信息(比如根据昨日和今日的订单号差值看出销量)数据量过大时,不同表的id分别自增,容易出现id冲突分布式全局ID生成应满足的特点:唯一:整个系统每个id都是唯一的递增......
  • Vue在main.js全局引入scss文件,组件里使用scss变量报错问题
    问题描述在写组件样式的时候,普通样式都没问题,一碰到$变量就errorModulebuildfailed(from./node_modules/sass-loader/dist/cjs.js):color:$normal-active-color;^Undefinedvariable.╷22│color:$normal-active-color;│......
  • js-promise
    一基本使用<script>letfunc=functionmineReadFile(path){//注意这里必须是箭头函数。规范要这样写。returnnewPromise((resolve,reject)=>{if(path=="1"){resolve("333");......
  • vue-router.esm.js:2065 Uncaught (in promise) Error: Redirected when going from "
    原因:  vue-router路由版本更新产生的问题,导致路由跳转失败抛出该错误;真正的原因是由于返回了一个Promise对象,正常的跳转由then方法执行当正常的路由跳转,被"路由导航守卫"拦截并重新指定路由时,由于this.$router.push()返回的是Promise对象,此时then方法不能正常执......
  • 掌握全局,捕捉瞬间:Snagit2023-专业屏幕录制与截图软件
    Snagit2023是一款功能强大的屏幕录制与截图软件,为您带来全新的视觉体验和高效的屏幕操作。无论您需要记录屏幕操作、制作教程视频,还是与他人分享屏幕内容,Snagit2023都能满足您的需求。→→↓↓载Snagit2023mac版一、高清屏幕录制,流畅捕捉每一个细节Snagit2023支持高清无损的......
  • [Compose] Asynchronous Reactive Data with Promises
    Let’smakeusingtheobserversasynchronous!Thiswaywecanupdatethedataandhavemultipleobserversrunasynchronously.classAsyncData{constructor(initialData){this.data=initialData;this.subscribers=[];}//Subscribetochan......
  • flask统一异常捕获(作用:统一捕获指定的异常并返回)
    flask中可以使用装饰器errorhandler来对指定的异常、状态码等统一捕获并处理。对指定的状态码进行统一捕获@app.errorhandler(404)#参数e是异常的详细信息的对象,必须要有一个参数接收异常对象。defpagenotfound(e):#比如可以重定向到统一的404静态页面中。#不过......
  • $nextTick函数的用法以及全局事件总线
    全局事件总线的目的是可以实现任意组件之间的通信。这里需要涉及到原型链的知识。在Vue里面,我们知道运行一个完整的项目是由若干个VueComponents组件和一个AppVueComponent组件加上一个Vue的实例对象而vc组件在通过this去获取属性的值时,首先会从vc实例对象里面找,没有的话就去vc......
  • python提取论文图片波形数据:pyautogui键盘移动鼠标,跨模块全局变量使用,cv2局部放大窗口
    最近写了一个python提取论文图片波形数据的脚本,代码如下。涉及新知识点:pyautogui键盘移动鼠标,跨模块全局变量使用,cv2局部放大窗口,matplotlib图片在pyQT5lable显示,坐标变换,多线程同时使用。搜索相关关键字去对应代码区看注释就可以了。gui窗口:1#-*-coding:utf-8-*-2......
  • 【RuoYi移动端】uni-app中通过vuex的store来实现全局变量的修改和读取
    一、在store文件中新建csjVar.js文件constcsjVar={csjMess:[{aaa:"ok"},{bbb:"no"}]}exportdefaultcsjVar二、修改store文件中新建index.js文件importVuefrom'vue'importVuexfrom'vuex'importuserfrom'@/store/modules/user�......