记一次全局错误捕获打版本号 + 工程化插入js脚本(上)

由于公司有各种移动端的中小项目,数量多,不集中,外加移动端天生不好查看日志检查错误,所以我们统一编写了一个捕获错误上报错误日志的脚本

前期研究参考了很多文章,罗列出了各个捕获错误api的利弊,如下图所示(直接借用的哦):

最后采用了

window.addEventListener('error') 来捕获同步错误、普通的异步错误和资源加载错误(js、css、img加载失败)

window.addEventListener('unhandledrejection') 来捕获es6+的异步任务报错

话不多说上代码

// 运用IIFE用来隔离模块,避免污染全局
(function (window) {
  // 运用Object.defineProperty 将捕获错误信息函数定义为全局的不可重写的唯一方法
  // 这样在某些可以提前预知的报错处可以直接调用这个方法而不必在手动报错给全局
  Object.defineProperty(window, 'xxxxxErrorHandle', {
    get() {
      return handleErrorMessage
    set(newVal) {
      console.error('TypeError: Cannot redefine property : window.handleError is existed !!!')
      return
  // 三种错误类型 运行时同步错误 资源加载错误 异步错误
  var ERROR_RUNTIME = 'ERROR_RUNTIME'
  var ERROR_RESOURCE = 'ERROR_RESOURCE'
  var ERROR_ASYNC = 'ERROR_ASYNC'
  var requestErrorURL = `http://baidu.com/log?from=${getWebHOST()}` // 测试环境接口
  var webVersion = null
  // 根据公司要求需要给每个项目打个版本号,运用meta标签给一个version的标识,在每次发送错误的时候都获取到版本号
  // 获取项目版本号
  function getWebVersion() {
    if (webVersion != null) return webVersion
    try {
      var version_meta = document.querySelector('meta[http-equiv="version"]')
      version_meta && (webVersion = version_meta.content)
      return webVersion
    } catch (error) {
      console.error(error)
  // 获取页面当前url
  function getWebURL() {
    return window.location.href
  // 获取页面当前host
  function getWebHOST() {
    return window.location.host
  // 监听全局同步错误和资源加载错误
  window.addEventListener('error', function (e) {
    var errorData = {}
    errorData.url = getWebURL()
    errorData.version = getWebVersion()
    errorData.from = getWebHOST()
    // 这里是判断是 运行时同步错误 还是 资源加载错误
    // 因为有一些老旧项目所以都是用老式赋值,没有采用解构赋值
    if (e.target === window) { // 如果e.target指向window则是运行时同步错误
      errorData.type = ERROR_RUNTIME
      errorData.message = e.message
      errorData.lineno = e.lineno
      errorData.colno = e.colno
      errorData.stack = e.error.stack
    } else { // 如果e.target不是指向window则是资源加载错误 而e.target就是报错的资源元素
      var tgt = e.target
      // 这里判断兼容了一下target是url指向问题,有可能是href报错也有可能是src报错
      var srcText = tgt.attributes.href ? tgt.attributes.href.value : tgt.attributes.src.value
      errorData.type = ERROR_RESOURCE
      errorData.message = 'load resource error !!!'
      errorData.outerHTML = encodeURI(tgt.outerHTML)
      errorData.src = tgt.src || tgt.href
      errorData.srcText = srcText
    changeyouErrorHandle(errorData)
  }, true)
  // 监听全局的es6+异步任务的错误
  window.addEventListener('unhandledrejection', function (e) {
    var errorData = {}
    errorData.url = getWebURL()
    errorData.version = getWebVersion()
    errorData.from = getWebHOST()
    errorData.type = ERROR_ASYNC
    errorData.message = e.reason.message
    errorData.stack = e.reason.stack
    changeyouErrorHandle(errorData)
  }, true)
  // 真正处理错误消息的函数
  // logErr是否需要打印错误
  function handleErrorMessage(err, logErr) {
    // 发送错误消息
    sendErrorByAjax(err)
    if (logErr) {
      console.error(err)
  // 这里因为服务端某些原因,采用的是ajax通信,也可以采用1px* 1px图片传输,或者SendBeacon
  // 发送错误消息方法
  function sendErrorByAjax(errData) {
    try {
      var xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
      xhr.onreadystatechange = function () {
        if (xhr.readyState == 4) {
          if (xhr.status >= 200 && xhr.status < 300) {
            // console.log(xhr.responseText)
          } else {
        } else {
      xhr.open('post', requestErrorURL, true)
      // xhr.timeout = 500
      // xhr.ontimeout=function(){
      //   console.log('ajax is timeout !!!')
      xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8')
      xhr.send(JSON.stringify(errData))
    } catch (error) {
      console.error(error)
})(window)
        web前端 @ 北京畅游
      
粉丝