Thursday, 31 January 2013

Stack Traces and Error Objects

A frequently requested feature has been that of stack traces with errors. However, because window.onerror, doesn't give access to an error object, it has not been possible for Errorception to capture stack traces. That's set to change today.

Starting today, you will be able to pass error objects to Errorception. An example is probably the best way to explain this.

try {
    var myObject = JSON.parse(jsonString); // will break if jsonString is invalid
} catch(e) {
    _errs.push(e);
    throw e;
}

When you pass such errors manually to Errorception, Errorception will now be able to record the stack trace for this error. Undoubtedly, this can be very useful for debugging.

Important

  • What you push to _errs should be a valid Error object, that is, it should be an instanceof Error.
  • It is important that you throw the error right after passing it to _errs. This is for two reasons. Firstly, you really want your program's execution to stop when you encounter an error, and throw is a great way to do so. Secondly, Errorception internally uses both the Error object and the data from window.onerror to capture error data. If you don't throw the error, Errorception will ignore the error object. Update: Throwing errors isn't required anymore, but is highly recommended.

As an additional bonus, with the exception of Firefox and Safari, you will also get the column number of your error. This is especially important since your JS is likely minified without line-breaks. This column number information is especially exciting — it's likely to guide the future features of Errorception.

Bonus: This works perfectly well with the recently launched ability to record custom information with errors. For example, in Errorception I was recently doing this (yes, Errorception uses Errorception):

try {
    var myObject = JSON.parse(jsonString);
} catch(e) {
    _errs.meta = {json: jsonString};
    _errs.push(e);
    throw e;
}

10 comments:

  1. So how does this work for callbacks?

    Does this work?

    try {
    setTimeout(function() {
    var myObject = JSON.parse(jsonString);
    }, 1000);
    } catch(e) {
    _errs.push(e);
    throw e;
    }

    ReplyDelete
    Replies
    1. Ruud, In this case, you'd have to put the try-catch inside the function, because of the way the browser's event loop and JS's call stacks work. I'll explain this in further detail in a blog post soon.

      Delete
  2. Rakesh, what do you think about a javascript pre-processor that wraps all function calls with try...catch?

    Using something like https://github.com/crcn/catchall, you just need to define the error callback once:

    catchall.onerror = function(e) {
    _errs.push(e);
    throw e;
    }


    I think it's going to be way too much work to add try...catch blocks manually.

    ReplyDelete
    Replies
    1. You are absolutely right, Nathan. This one of those cases where I wanted to get the functionality right before the usability. I'll be rolling out stuff soon to make working with this easier.

      Delete
    2. Hi Rakesh, I've been working with the airbrake javascript notifier recently, and have added some improvements for wrapping jQuery with try...catch blocks. It seems that 99% of errors will be caught by wrapping 'jQuery.fn.on', 'jQuery.fn.ready', and setting up a jQuery.ajaxPrefilter to wrap success, error and complete callbacks.

      Here's my jQuery wrapping code: https://gist.github.com/ndbroadbent/5097601 (The original code is at https://github.com/airbrake/airbrake-js/blob/master/src/notifier.js#L173)

      Maybe you might find that useful, and please let me know if you know of any further places in jQuery that should be wrapped.


      Best,
      Nathan

      Delete
    3. Woah! Thanks for taking the time to come back here with an update, Nathan. I'm working on something very similar, and will be glad to share my code. Your approach with jQuery is interesting. My approach has been slightly more low-level, but maybe more comprehensive, well according to me at least. I'm not a 100% sure yet, since I haven't been able to get jQuery's test suite to run at all, but I'll keep you updated. I don't have your email, so I'll update these comments here.

      Just checking: what license are you using for the gist? You know, just in case I decide to use your ideas.

      Delete
  3. I just found out that this works wonderfully with the Ember framework. Using `Ember.onerror` one can easily catch the error, pass it on to Errorception and enjoy more debugging information.

    ReplyDelete
    Replies
    1. Huge win! Thanks for the heads-up. Cheers!

      Delete
  4. You can actually get stack traces from within onerror in some browsers - https://gist.github.com/6727083

    ReplyDelete
    Replies
    1. Oh yes. I'm rolling something similar into Errorception itself. :)

      Delete