proposal: more helpful stack traces
danprince opened this issue · comments
We've got a number of complex workflows that call out to multiple Slack APIs and we're finding it tricky to work with the stack traces that we get from this library.
Error: channel_not_found
at _res (/kumu/node_modules/slack/src/_exec.js:50:17)
at IncomingMessage.__end (/kumu/node_modules/tiny-json-http/_write.js:61:9)
at emitNone (events.js:110:20)
at IncomingMessage.emit (events.js:207:7)
at endReadableNT (_stream_readable.js:1047:12)
at _combinedTickCallback (internal/process/next_tick.js:102:11)
at process._tickDomainCallback (internal/process/next_tick.js:198:9)
The response message is a useful component, but I have no idea which of the API calls caused it.
We're using native async/await
all over our codebase and most of our asynchronous call stacks end up looking synchronous. It's only when we call out to code that uses promises or callbacks, that the stack traces disappear.
However, converting this library to async/await
would be a load of effort and it'd be a shame to no longer support Node 4.X.X.
Tentative solution incoming, based on what I've been working with locally.
I've slightly changed the implementation of exec
to grab the current stack trace when it is called. Then use it to overwrite the less useful stack traces.
module.exports = function exec(url, form, callback) {
var trace = new Error()
if (!callback) {
var pfy = promisify(_exec)
return pfy(url, form).catch(function(err) {
err.stack = trace.stack
throw err
})
}
else {
_exec(url, form, function(err, res) {
if (err) {
err.stack = trace.stack
callback(err)
}
else {
callback(null, res)
}
})
}
}
And here's the result:
Error
at Object.exec (/kumu/server/node_modules/slack/src/_exec.js:10:15)
at slackRateLimitRetry_1.slackRateLimitRetry (/kumu/server/build/models/Channel.js:103:101)
at Object.slackRateLimitRetry (/kumu/server/build/utils/slackRateLimitRetry.js:12:22)
at Channel.syncMembers (/kumu/server/build/models/Channel.js:103:55)
at Function.syncPrivateChannels (/kumu/server/build/models/Channel.js:78:31)
at <anonymous>
Looks like error.stack
is the message that the console prints when it throws, which means error.message
is lost, but that can be fixed with:
err.stack = trace.stack.replace(/^Error/, `Error: ${err.message}`)
I'm a bit conflicted as to whether this would be a good pattern for the library to follow. I don't like this kind of magic in general, because it usually ends up making stuff more confusing.
On the other hand, it's super annoying to see production errors coming in with the first form of stack trace, having no idea which call caused it (we've got ~50 API call sites across our application).
Some options:
- Keep this out of the library, we'll maintain a private fork
- Add it to the library as opt-in with an environment variable
- Add it to the library as out-out with an environment variable
- Save the original stack as a new property on the error (we'll sort it in our error middleware)
- Halfway house: provide api context in errors
Error: (conversation.members) channel_not_found
@brianleroux Let me know what you think is best and I can wrap this up, or get started on a PR.
I like this. Lets add it. Error is currently and likely for a long time unspecified so there may be future work but that'd be true if we didn't make things better. Lets go for it!
Merged in #123
fyi published in 11.0.1
with some minor updates