Capture Javascript Async Stack Traces
Tags: javascript, errors, hack
Published: 2019-06-01 17:59:14

---
title: Capture Javascript Async Stack Traces
published: true
description: 
tags: javascript, errors, hack
---

Javascript stacktrace do not include async calls, that sucks.

Example:
```js
var sayHi = () => {
  setTimeout(() => {
    console.log('Hi there');
    thisLineBreaks();
  }, 1000);
};

sayHi();
```

Output:
```
$ node try_async.js
Hi there
/my_code/try_async.js:4
    thisLineBreaks();
    ^

ReferenceError: thisLineBreaks is not defined
    at Timeout.setTimeout [as _onTimeout] (/my_code/try_async.js:4:5)
    at listOnTimeout (timers.js:324:15)
    at processTimers (timers.js:268:5)
```

We can only see error details inside `sayHi` function and we don't know where it was called.

### Solution

To solve this, we can capture stack trace before async part and append it to actual error.


```js
var sayHi = () => {
  var startStack = new Error().stack;
  setTimeout(() => {
    try {
      console.log('Hi there');
      thisLineBreaks();
    } catch (error) {
      error.stack = error.stack + "\n" +
        startStack.substring(startStack.indexOf("\n") + 1);
      throw error;
    }
  }, 1000);
};

sayHi();
```

And now we getting it!

```
node try_async.js
Hi there
/my_code/try_async.js:9
      throw error;
      ^

ReferenceError: thisLineBreaks is not defined
    at Timeout._onTimeout (/my_code/try_async.js:6:7)
    at listOnTimeout (internal/timers.js:531:17)
    at processTimers (internal/timers.js:475:7)
    at sayHi (/my_code/try_async.js:2:20)
    at Object.<anonymous> (/my_code/try_async.js:14:1)           <<<< HERE
    at Module._compile (internal/modules/cjs/loader.js:774:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:785:10)
    at Module.load (internal/modules/cjs/loader.js:641:32)
    at Function.Module._load (internal/modules/cjs/loader.js:556:12)
    at Function.Module.runMain (internal/modules/cjs/loader.js:837:10)
    at internal/main/run_main_module.js:17:11
```

It show where my function was called &mdash; `/my_code/try_async.js:14`

### Promises

Now days most of libraries uses promises and we can easily apply it for every function that returns promise object:

```js
var trackPromise = async (func) => {
  var startStack = new Error().stack;
  try {
    return await func();
  } catch (error) {
    error.stack = error.stack + "\n" +
      startStack.substring(startStack.indexOf("\n") + 1);
    throw error;
  }
}

// will make complete stack traces
var rows = trackPromise(() => {
  return db.query('select * from something');
});
```

Or modify async functions:
```js
// patch method in object to keep stack traces
var addStackTracker = (proto, methodName) => {
  var originalMethod = proto[methodName];

  proto[methodName] = function(...args) {
    var startStack = new Error().stack;
    return new Promise((resolve, reject) => {
      originalMethod.apply(this, args).then(res => { resolve(res); })
        .catch(error => {
          error.stack = error.stack + "\n" + startStack.substring(startStack.indexOf("\n") + 1);
          reject(error);
        });
    });
  }
}

addStackTracker(DatabaseClient.prototype, 'query')

var db = new DatabaseClient()
db.query('...') // will have full stack
```

now my dev life became easier.

*Note: if your function call multiple async functions inside, than this approach will keep only stack before it enter function and stack for last async call*