Sentry-javascript: Feature: Support `cause` property on errors

Created on 18 Jun 2018  路  14Comments  路  Source: getsentry/sentry-javascript

Do you want to request a feature or report a bug?

feature

What is the current behavior?

try {
  somethingThatThrows();
} catch (innerError) {
  let error = new Error('something failed!');
  error.cause = innerError;
  throw error;
}

With the current behavior only the outer/wrapping Error is shown in the stack trace.

What is the desired behavior?

It would be great if the inner Error was also displayed in the stacktrace view, roughly similar to how it is done for Java and Python.

I'm happy to help implement this with a bit of guidance :)

  • does the serverside need changes or does it already support this due to support in Java?
  • what changes to the submitted payload are necessary?

All 14 comments

Can be done with event hints in new SDK now

@kamilogorek can you explain what you mean by "event hints" and how that will work with the cause property?

Event and Breadcrumb hints are objects containing various information used to put together an event or a breadcrumb. For events, those are things like event_id, originalException, syntheticException (used internally to generate cleaner stacktrace), and any other arbitrary data that user attaches. For breadcrumbs it's all implementation dependent. For XHR requests, hint contains xhr object itself, for user interactions it contains DOM element and event name etc.

They are available in two places. beforeSend/beforeBreadcrumb and eventProcessors. Those are two ways we'll allow users to modify what we put together.

Examples based on your cause property (I use message for ease of reading, but there's nothing stopping you from modifying event stacktrace frames).

beforeSend/beforeBreadcrumb:

import { init } from '@sentry/browser';

init({
  dsn: 'https://[email protected]/123',
  beforeSend(event, hint) {
    const processedEvent = { ...event };
    const cause = hint.originalException.cause;

    if (cause) {
      processedEvent.message = cause.message;
    }

    return processedEvent;
  },
  beforeBreadcrumb(breadcrumb, hint) {
    if (breadcrumb.category === 'ui.click') {
      const target = hint.event.target;
      if (target.ariaLabel) breadcrumb.message = target.ariaLabel;
    }
    return breadcrumb;
  },
});

eventProcessor (this will be not used that often, but is great for writing custom plugins or share them across multiple projects - in form of an integration, more on this soon):

import { getCurrentHub } from '@sentry/browser';

getCurrentHub().configureScope(scope => {
  scope.addEventProcessor(async (event, hint) => {
    const processedEvent = { ...event };
    const cause = hint.originalException.cause;

    if (cause) {
      processedEvent.message = cause.message;
    }

    return processedEvent;
  });
});

hmm, if I understand correctly this would overwrite the original exception though, right? my hope was that it could display them in parallel like in Java/Python 馃

Can you show me an example of desired output?

This is an example of how it looks like in Java:

Exception in thread "main" java.lang.IllegalStateException: A book has a null property
        at com.example.myproject.Author.getBookIds(Author.java:38)
        at com.example.myproject.Bootstrap.main(Bootstrap.java:14)
Caused by: java.lang.NullPointerException
        at com.example.myproject.Book.getId(Book.java:22)
        at com.example.myproject.Author.getBookIds(Author.java:36)
        ... 1 more

try {
  somethingThatThrows();
} catch (innerError) {
  let error = new Error('something failed!');
  error.cause = innerError;
  throw error;
}

Without the cause assignment in the snippet above you lose any information on the original cause of the exception, but if you throw the innerError it might be cryptic for the end user and you can't include other information that might help resolve the issue. Ideally it would be possible to show both inner and outer exception (and possible even more nesting levels) like in the Java example above.

The platform itself doesn't support this use-case, so there's only this much we can do on the SDK side. In JS, there's only one "main" exception, and we don't have an easy way to display more.

I'd add this as an extra data in the event itself, which'd give you enough information on the UI:

beforeSend(event, hint) {
  const processedEvent = { ...event };
  const cause = hint.originalException.cause;

 if (cause) {
    processedEvent.extra = {
      ...processedEvent.extra,
      cause: {
        type: cause.type
        message: cause.message,
        stack: cause.stack
      }
    }
  }

  return processedEvent;
}

I'd add this as an extra data in the event itself, which'd give you enough information on the UI

yeah, unfortunately the cause.stack will be displayed without sourcemaps though :-/

The platform itself doesn't support this use-case

do you not support this for Java/Python either?

@Turbo87 like this? :)

screen shot 2018-09-05 at 11 15 20

exactly! 馃槏

I'm not sure, but I think the order needs to be flipped though. I'd expect to see the "Parent process error" on top of the other one 馃

It's PoC so far. I'll make it happen before the final release :)

Great, thanks a lot!!

@kamilogorek did this land yet?

Was this page helpful?
0 / 5 - 0 ratings