最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • sentry-javascript解析(三)js异常错误如何捕获

    正文概述 掘金(superior)   2021-02-20   901

    前言

    之前讨论了fetchXHR是如何捕获的,本次我们来看看js异常捕获是如何做到的。

    关于addInstrumentationHandlerfill方法可以在第一篇文章中了解。

    之前的文章可以看这里:

    • sentry-javascript解析(一)fetch如何捕获
    • sentry-javascript解析(二)XHR如何捕获

    前置准备

    常见的代码异常

    我们列举几个常见的代码异常

    js代码异常

    function demoA(params) {
    	params.func();
    }
    demoA();
    

    sentry-javascript解析(三)js异常错误如何捕获

    dom操作异常

    var node = document.body.children[0];
    var nextNode = node.nextSibling;
    var div = document.createTextNode('div');
    node.insertBefore(div, nextNode);
    

    sentry-javascript解析(三)js异常错误如何捕获

    Promise异常

    new Promise((resolve, reject) => {
    	reject();
    });
    

    sentry-javascript解析(三)js异常错误如何捕获

    资源加载异常

    const img = document.createElement('img');
    img.src = 'abc.png';
    document.body.appendChild(img);
    

    sentry-javascript解析(三)js异常错误如何捕获

    异常错误事件

    通过上面的代码我们可以发现不同情况,出现的错误不同,接下来我们看一下这些异常都会触发哪些异常事件。

    onerror 和 error 事件

    我们最常见的就是window.onerroraddEventListener('error', callback),那么它们两个能捕获哪些异常呢?它们两个又有什么区别呢?mdn - GlobalEventHandlers.onerror

    • window.onerror是一个全局变量,当有js运行时触发错误,window会触发error事件,并执行window.onerror()
    • 监听js运行时错误事件,会比window.onerror先触发,可以全局捕获资源加载异常的错误。

    unhandledrejection 事件

    Promisereject且没有reject处理器的时候,会触发unhandledrejection事件。mdn - unhandledrejection

    js异常错误捕获

    接下来我们看一下sentry是如何做异常捕获的。

    onerror捕获

    高阶函数封装onerror

    按照addInstrumentationHandler的代码我们可以准确看出通过type: 'error'接下来应该执行instrumentError方法,我们来看一下这个方法的代码:

    // 全局闭包缓存当前window.onerror
    let _oldOnErrorHandler: OnErrorEventHandler = null;
    
    function instrumentError(): void {
      // 全局闭包缓存当前window.onerror
      _oldOnErrorHandler = global.onerror;
      // 重置window.onerror
      global.onerror = function(msg, url, line, column, error): boolean {
        // 遍历onerror对应的回调
        triggerHandlers('error', {
          column,
          error,
          line,
          msg,
          url,
        });
        // 如果当前已经有设置onerror则继续调用执行
        if (_oldOnErrorHandler) {
          return _oldOnErrorHandler.apply(this, arguments);
        }
    
        return false;
      };
    }
    

    onerror对应回调

    我们了解完onerror是如何封装之后,再来看看对应的回调里都做了什么。我们可以在@sentry/browsersrc/integrations/globalhandlers.ts中找到对应的代码。

     addInstrumentationHandler({
          callback: (data: { msg; url; line; column; error }) => {
            const error = data.error;
            const currentHub = getCurrentHub();
            const hasIntegration = currentHub.getIntegration(GlobalHandlers);
            const isFailedOwnDelivery = error && error.__sentry_own_request__ === true;
    
            if (!hasIntegration || shouldIgnoreOnError() || isFailedOwnDelivery) {
              return;
            }
    
            const client = currentHub.getClient();
            // isPrimitive用于判断error的数据类型是否为原始数据类型
            // 这里根据error数据类型不同,拼接成统一的数据结构
            const event = isPrimitive(error)
              ? this._eventFromIncompleteOnError(data.msg, data.url, data.line, data.column)
              : this._enhanceEventWithInitialFrame(
                  eventFromUnknownInput(error, undefined, {
                    attachStacktrace: client && client.getOptions().attachStacktrace,
                    rejection: false,
                  }),
                  data.url,
                  data.line,
                  data.column,
                );
    
            addExceptionMechanism(event, {
              handled: false,
              type: 'onerror',
            });
            // 记录上报本次事件
            currentHub.captureEvent(event, {
              originalException: error,
            });
          },
          type: 'error',
    });
    

    onerror的回调比较简单,简单来说就是根据捕获到的error对象数据类型不同,经过特定的整合最终输出统一的数据结构上报就结束了。

    unhandledrejection捕获

    高阶函数封装unhandledrejection

    按照addInstrumentationHandler的代码我们可以准确看出通过type: 'unhandledrejection'接下来应该执行instrumentUnhandledRejection方法,我们来看一下这个方法的代码:

    // 全局闭包缓存当前window.onunhandledrejection
    let _oldOnUnhandledRejectionHandler: ((e: any) => void) | null = null;
    
    function instrumentUnhandledRejection(): void {
      // 全局闭包缓存当前window.onunhandledrejection
      _oldOnUnhandledRejectionHandler = global.onunhandledrejection;
      // 重置window.onunhandledrejection
      global.onunhandledrejection = function(e: any): boolean {
        // 遍历unhandledrejection对应回调
        triggerHandlers('unhandledrejection', e);
        // 如果当前已经有设置unhandledrejection则继续调用执行
        if (_oldOnUnhandledRejectionHandler) {
          return _oldOnUnhandledRejectionHandler.apply(this, arguments);
        }
    
        return true;
      };
    }
    

    unhandledrejection对应回调

    我们了解完unhandledrejection是如何封装之后,再来看看对应的回调里都做了什么。我们可以在@sentry/browser的src/integrations/globalhandlers.ts中找到对应的代码。

    addInstrumentationHandler({
          callback: (e: any) => {
            let error = e;
    
            try {
              // 判断error是否包含reason字段
              // 详情参考https://developer.mozilla.org/zh-CN/docs/Web/API/PromiseRejectionEvent
              if ('reason' in e) {
                error = e.reason;
              } else if ('detail' in e && 'reason' in e.detail) {
                // 这里兼容自定义事件获取对应的reason
                // 详情参考https://developer.mozilla.org/zh-CN/docs/Web/API/CustomEvent
                error = e.detail.reason;
              }
            } catch (_oO) {
            }
    
            const currentHub = getCurrentHub();
            const hasIntegration = currentHub.getIntegration(GlobalHandlers);
            const isFailedOwnDelivery = error && error.__sentry_own_request__ === true;
    
            if (!hasIntegration || shouldIgnoreOnError() || isFailedOwnDelivery) {
              return true;
            }
    
            const client = currentHub.getClient();
            // isPrimitive用于判断error的数据类型是否为原始数据类型
            // 这里根据error数据类型不同,拼接成统一的数据结构
            const event = isPrimitive(error)
              ? this._eventFromRejectionWithPrimitive(error)
              : eventFromUnknownInput(error, undefined, {
                  attachStacktrace: client && client.getOptions().attachStacktrace,
                  rejection: true,
                });
    
            event.level = Severity.Error;
    
            addExceptionMechanism(event, {
              handled: false,
              type: 'onunhandledrejection',
            });
            // 记录上报本次事件
            currentHub.captureEvent(event, {
              originalException: error,
            });
    
            return;
          },
          type: 'unhandledrejection',
        });
    

    我们发现onunhandledrejection也是比较简单,根据捕获到的error数据结构不同,经过特定的整合最终输出统一的数据结构上报就结束了。

    onerror的区别是兼容考虑了不同的场景:

    • 捕获到的error包含reason字段,则以reason为主
    • 捕获到的error是继承的CustomEvent自定义事件,则以detail.reason为主

    总结

    sentry在异常错误捕获会通过onerroronunhandledrejection两个方法进行监听。高阶函数封装也相对比较简单,提前执行sentry对应的回调上报异常事件。


    起源地下载网 » sentry-javascript解析(三)js异常错误如何捕获

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元