preface

After discussing how FETCH and XHR are captured, let’s take a look at how JS exception capture is done.

About addInstrumentationHandler and fill method can know in the first article.

Previous articles can be seen here:

  • Sentry -javascript parsing (一) How to fetch
  • How to capture XHR

Lead to

Common code exceptions

Let’s list a few common code exceptions

Js code exception

function demoA(params) {
	params.func();
}
demoA();
Copy the code

Dom operation exception

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

Abnormal Promise

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

Resource loading exception

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

Abnormal error event

As you can see from the code above, the errors are different in different cases. Let’s take a look at what kind of exception events are triggered by these exceptions.

Onerror and error events

The most common ones are window. onError and addEventListener(‘error’, callback). What’s the difference between them? mdn – GlobalEventHandlers.onerror

  • window.onerrorIs a global variable when there isjsRuntime trigger error,windowWill triggererrorEvent and executewindow.onerror().
  • Listening to thejsRun-time error events are more thanwindow.onerrorTrigger first, yesGlobal catch resource loading exceptionThe error.

Unhandledrejection event

When a Promise is rejected and there is no Reject handler, the unhandledrejection event is emitted. mdn – unhandledrejection

Js exception error capture

Let’s take a look at how Sentry does exception catching.

Onerror capture

Higher-order functions encapsulate onError

According to the code of addInstrumentationHandler we can accurately see through type: ‘error’ should be executed next instrumentError method, we take a look at the way the code:

// The global closure caches the current window.onerror
let _oldOnErrorHandler: OnErrorEventHandler = null;

function instrumentError() :void {
  // The global closure caches the current window.onerror
  _oldOnErrorHandler = global.onerror;
  / / reset window. Onerror
  global.onerror = function(msg, url, line, column, error) :boolean {
    // Iterate over the corresponding callback to onError
    triggerHandlers('error', {
      column,
      error,
      line,
      msg,
      url,
    });
    // If onError is already set, the call continues
    if (_oldOnErrorHandler) {
      return _oldOnErrorHandler.apply(this.arguments);
    }

    return false;
  };
}
Copy the code

Onerror corresponds to a callback

Now that we know how onError is wrapped, let’s take a look at what happens in the corresponding callback. We can in @ sentry/SRC/integrations of browser/globalhandlers ts found in the corresponding code.

 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 is used to determine whether the error datatype is the original datatype
        // Here, according to the error data type, splice into a unified data structure
        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'});// Record and report this event
        currentHub.captureEvent(event, {
          originalException: error,
        });
      },
      type: 'error'});Copy the code

The onError callback is relatively simple. In simple terms, according to the different data types of the error object captured, after specific integration, the unified data structure is output and the report ends.

Unhandledrejection capture

Higher-order functions encapsulate unhandledrejection

According to the code of addInstrumentationHandler we can accurately see through type: ‘unhandledrejection should execute next instrumentUnhandledRejection method, we take a look at the way the code:

Cache the current window. / / global closure onunhandledrejection
let _oldOnUnhandledRejectionHandler: ((e: any) = > void) | null = null;

function instrumentUnhandledRejection() :void {
  Cache the current window. / / global closure onunhandledrejection
  _oldOnUnhandledRejectionHandler = global.onunhandledrejection;
  / / reset window. Onunhandledrejection
  global.onunhandledrejection = function(e: any) :boolean {
    // Iterate through the unhandledrejection corresponding callback
    triggerHandlers('unhandledrejection', e);
    // If unhandledrejection is already set, the call will continue
    if (_oldOnUnhandledRejectionHandler) {
      return _oldOnUnhandledRejectionHandler.apply(this.arguments);
    }

    return true;
  };
}
Copy the code

Unhandledrejection corresponds to a callback

Now that we know how unHandledrejection wraps, let’s take a look at what happens in the corresponding callback. We can in @ sentry/SRC/integrations of browser/globalhandlers ts found in the corresponding code.

addInstrumentationHandler({
      callback: (e: any) = > {
        let error = e;

        try {
          // Check whether error contains the reason field
          / / refer to https://developer.mozilla.org/zh-CN/docs/Web/API/PromiseRejectionEvent for details
          if ('reason' in e) {
            error = e.reason;
          } else if ('detail' in e && 'reason' in e.detail) {
            // Custom events are compatible here to obtain the corresponding reason
            / / refer to https://developer.mozilla.org/zh-CN/docs/Web/API/CustomEvent for detailserror = 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 is used to determine whether the error datatype is the original datatype
        // Here, according to the error data type, splice into a unified data structure
        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'});// Record and report this event
        currentHub.captureEvent(event, {
          originalException: error,
        });

        return;
      },
      type: 'unhandledrejection'});Copy the code

We find that onunHandledrejection is also relatively simple. According to the different error data structures captured, the unified data structure will be output and reported after specific integration.

The difference with onError is that compatibility takes into account different scenarios:

  • To capture theerrorcontainsreasonField, thenreasonGive priority to
  • To capture theerrorIs inheritedCustomEventTo customize events, usedetail.reasonGive priority to

conclusion

The Sentry will listen for exception errors through onError and onunHandledrejection methods. Higher-order function encapsulation is also relatively simple, with sentry callback reported in advance.