25

Today I find the need to track and retrieve a Javascript error stacktrace to solve them.

Today we were able to capture all rest calls, the idea is that once you get an error, automatically posts the stacktrace of that error plus the responses of the rest saved services so we can detect, reproduce, and solve the problems in almost an identical environment/situation.

As a requirement we were asked to make a module that can be included without being intrusive, for example: Include the module that contains the hook logic in one JS, would be not invasive, include several lines of code in various JS files would be invasive.

The goal is to make a tool that can be included in a system already developed and track error events (like console).

I've read about this trackers logic:

  • errorception.com/
  • trackjs.com/
  • atatus.com/
  • airbrake.io/
  • jslogger.com/
  • getsentry.com/
  • muscula.com/
  • debuggify.net/
  • raygun.io/home

We need to do something like that, track the error and send it to our server.

As "Dagg Nabbit" says... "It's difficult to get a stack trace from errors that happen "in the wild" right now"...

So, we got a lot of paid products, but how did they really works?

In Airbrake they use stacktrace and window.onerror:

window.onerror = function(message, file, line) {
  setTimeout(function() {
    Hoptoad.notify({
      message : message,
      stack   : '()@' + file + ':' + line
    });
  }, 100);
  return true;
};

But i cant figure out when the stacktrace really used.

At some point, stacktrace, raven.js and other trackers need try / catch.

  1. what happens if we found a way to make a global wrapper?
  2. Can we just call stacktrace and wait for the catch?

How can I send a stack trace to my server when an unexpected error occurs on the client? Any advice or good practices?

Todd H. Gardner
  • 630
  • 4
  • 18
Da3
  • 261
  • 3
  • 7
  • I know of https://appenlight.com/ which has a free account with some limitations. But for open source project it's free. I kept the question closed as it's for searching a product but felt I could still provide some alternative until it gets deleted probably – Loïc Faure-Lacroix Dec 28 '13 at 15:35
  • Possible duplicate of http://stackoverflow.com/q/5328154/139010 – Matt Ball Dec 28 '13 at 15:44
  • @LoïcFaure-Lacroix, it's not searching for a product anymore, I changed it. Does the product you mentioned actually send a *stack trace*, or just the error message and place it occurred? – Dagg Nabbit Dec 28 '13 at 15:45
  • @MattBall that question doesn't ask about getting a *stack trace*, only getting the errors, and it's not clear whether the product mentioned in the accepted answer will send one or not. – Dagg Nabbit Dec 28 '13 at 15:47
  • @DaggNabbit it's clear if you've used the product, or if you read [the documentation](http://raven-js.readthedocs.org/en/latest/usage/index.html). – Matt Ball Dec 28 '13 at 15:49
  • @MattBall I stopped reading after "try it’s best" [sic]. Regardless, this is a different question. – Dagg Nabbit Dec 28 '13 at 15:51
  • @DaggNabbit https://appenlight.com/page/javascript/introduction it does but you have to catch the error in someway, it doesn't do magically everything. Also appenlight will become much more than just error tracking, it could be used to check for slow code etc and overall monitoring things. – Loïc Faure-Lacroix Dec 28 '13 at 16:00
  • @DaggNabbit the beauty of open source: https://github.com/getsentry/raven-js/pull/171 – Matt Ball Dec 28 '13 at 16:18
  • @LoïcFaure-Lacroix If I understand correctly, appenlight is based on tracekit. The implementation of tracekit says "In order to get stack traces, you need to wrap your code in a try / catch block like above. Otherwise the bug hits window.onerror handler and will only contain the error message, line number, and column number . " I can't write those wrappers... – Da3 Dec 28 '13 at 16:21
  • @Da3 looks like the Hoptoad code is just grabbing the message, file, and line number and calling that a stack trace. A one-level deep stack trace isn't much of a stack trace though ;) – Dagg Nabbit Dec 28 '13 at 16:23
  • @MattBall Raven: "context/wrap - Raven.context allows you to wrap any function to be immediately executed. Behind the scenes, Raven is just wrapping your code in a try...catch block." is there any way to make an overall wrapper? – Da3 Dec 28 '13 at 16:31
  • 1
    @Da3 you could create a script loader function and use that instead of loading scripts in the usual way. It could grab the scripts with XHR, add `try ... catch` around them, and evaluate them or stick them in script tags. It would create a whole host of other problems, like running into the same-origin policy and making the file names in the stack trace useless. Another option could be setting up a server-side script loader proxy that does the same thing; this could probably work out pretty well. – Dagg Nabbit Dec 28 '13 at 16:41
  • @DaggNabbit yes, you're right but, if we make a global try/catch per file loaded? in that case the file name in the stack trace wouldn't be useless? The server-side script loader proxy might be a new point of view, but i can't figure out how to implement it. what pieces are involved? how the architecture would be? i can't see it u_u – Da3 Dec 28 '13 at 17:01
  • 1
    @Da3 set something up on your server to handle URLs like `{domain}/jsproxy/{scheme}/{url}`, for example `yourserver.com/jsproxy/http/code.jquery.com/jquery.js`. You could just grab the file and serve it up like this [PHP example](http://benalman.com/projects/php-simple-proxy/), or you could cache things on the disk or in memory and check remote resources for changes instead of fetching them every time. – Dagg Nabbit Dec 28 '13 at 22:10
  • Just wanted to point out that http://www.debuggify.net/ is no JS debugging site anymore, raygun.io/home should just be raygun.io and http://jslogger.com/ returns a 503 error – Simon Ferndriger Aug 07 '22 at 06:14

4 Answers4

11

It's difficult to get a stack trace from errors that happen "in the wild" right now, because the Error object isn't available to window.onerror.

window.onerror = function(message, file, line) { }

There is also a new error event, but this event doesn't expose the Error object (yet).

window.addEventListener('error', function(errorEvent) { })

Soon, window.onerror will get a fifth parameter containing the Error object, and you can probably use stacktrace.js to grab a stack trace during window.onerror.

<script src="stacktrace.js"></script>
<script>
window.onerror = function(message, file, line, column, error) {
    try {
        var trace = printStackTrace({e: error}).join('\n');
        var url = 'http://yourserver.com/?jserror=' + encodeURIComponent(trace);
        var p = new printStackTrace.implementation();
        var xhr = p.createXMLHTTPObject();

        xhr.open('GET', url, true);
        xhr.send(null);
    } catch (e) { }
}
</script>

At some point the Error API will probably be standardized, but for now, each implementation is different, so it's probably smart to use something like stacktracejs to grab the stack trace, since doing so requires a separate code path for each browser.

Community
  • 1
  • 1
Dagg Nabbit
  • 75,346
  • 19
  • 113
  • 141
  • The window.onerror error object is only available on Chrome. AFAIK no other browsers have committed to it. – Todd H. Gardner Jan 06 '14 at 16:02
  • @ToddGardner good point. I'm expecting that they'll follow suit eventually given how useless `onerror` is now, but you never know. – Dagg Nabbit Jan 06 '14 at 17:31
  • I'm sure Mozilla will, probably IE12 or something too. But unfortunately we'll have to deal with old browsers that don't do it for some time yet :( – Todd H. Gardner Jan 07 '14 at 18:48
  • 1
    Update for those referencing this - `window.onerror` now receives an Error in the latest versions of most or all major browsers. – Eric Wendelin Nov 23 '15 at 06:37
8

I'm the cofounder of TrackJS, mentioned above. You are correct, sometimes getting the stack traces requires a little bit of work. At some level, async functions have to be wrapped in a try/catch block--but we do this automatically!

In TrackJS 2.0+, any function you pass into a callback (addEventListener, setTimeout, etc) will be automatically wrapped in a try/catch. We've found that we can catch nearly everything with this.

For the few things that we might now, you can always try/catch it yourself. We provide some helpful wrappers to help, for example:

function foo() {
  // does stuff that might blow up
}

trackJs.watch(foo);
Todd H. Gardner
  • 630
  • 4
  • 18
  • 1
    "In TrackJS 2.0+, any function you pass into a callback (addEventListener, setTimeout, etc) will be automatically wrapped in a try/catch [..]" What performance implications does this have? – Gajus Apr 18 '17 at 18:55
  • 1
    @Gajus Only the overhead of wrapping a function, such as: function foo() {...} function bar() { return foo(); } The incremental cost is that of calling `bar()` instead of `foo()` – Todd H. Gardner Apr 25 '17 at 19:41
4

In latest browsers, there is a 5th parameter for error object in window.onerror. In addEventListener, you can get error object by event.error

// Only Chrome & Opera pass the error object.
window.onerror = function (message, file, line, col, error) {
    console.log(message, "from", error.stack);
    // You can send data to your server
    // sendData(data);
};
// Only Chrome & Opera have an error attribute on the event.
window.addEventListener("error", function (event) {
    console.log(e.error.message, "from", event.error.stack);
    // You can send data to your server
    // sendData(data);
})

You can send data using image tag as follows

function sendData(data) {
    var img = newImage(),
        src = http://yourserver.com/jserror + '&data=' + encodeURIComponent(JSON.stringify(data));

    img.crossOrigin = 'anonymous';
    img.onload = function success() {
        console.log('success', data);
    };
    img.onerror = img.onabort = function failure() {
        console.error('failure', data);
    };
    img.src = src;
}

If you are looking for opensource, then you can checkout TraceKit. TraceKit squeezes out as much useful information as possible and normalizes it. You can register a subscriber for error reports:

TraceKit.report.subscribe(function yourLogger(errorReport) {
    // sendData(data);
});

However you have to do backend to collect the data and front-end to visualize the data.

Disclaimer: I am a web developer at https://www.atatus.com/ where you can track all your JavaScript errors and filter errors across various dimensions such as browsers, users, urls, tags etc.

Fizer Khan
  • 88,237
  • 28
  • 143
  • 153
0

@Da3 You asked about appenlight and stacktraces. Yes it can gather full stacktraces as long as you wrap the exception in try/catch block. Otherwise it will try reading the info from window.onerror which is very limited. This is a browser limitation (which may be fixed in future).

Ergo
  • 1,205
  • 9
  • 16