-
Notifications
You must be signed in to change notification settings - Fork 29.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Missing stack traces from async functions after the first await #11865
Comments
Cc @nodejs/diagnostics |
That's because Error.stack only reflects the actual stack. The inspector provides async stack traces, but Error.stack will never include the async parts due to the overhead of collecting it. |
Wouldn't it be nice if there was a way to enable async stack traces via a flag or something? Or when The overhead is huge, but there are cases when developers will want Error.stack to generate async errors for them. |
You could run node with --inspect and enable async stack trace in DevTools. |
I'm thinking something more command-liney would be better. Personally, I only use Chrome DevTools when I really want to figure out precisely what's happening. But most of the time, developers just want informative stack traces in their terminal. |
Would |
Arrr, meehaps |
Would it be possible to add a V8 option that enables async error stacks for "normal" error.stack properties? Or would that be too risky? |
We have promisehooks in V8 that will help you build these async stack traces. Node can use this API to expose an async stack trace. |
The @nodejs/diagnostics plan for solving this is:
This problem is unlikely to be solved by node core directly, we will just give the tools to module authors such that they can solve it. For example I have written https://github.com/AndreasMadsen/trace, which uses an undocumented version of |
Just so I understand, what's the reasoning behind solving this in userland instead of node core? |
And I guess the question I have is what changed? This problem didn't seem to exist with the equivalent generator code. I understand there are technical differences between generators and async/await but the fundamental idea that the VM pauses execution and then resumes seems the same and yet when we did that with generators, the full stack trace was available. |
Generators are resumed from user code. Async functions are resumed from the micro task queue. |
Because it can be solved in userland, once the building blocks are in place. Node.js has always aimed at keeping the core minimal and instead embrace external modules, only things that are really difficult to implement in userland end up in node core. I would say that
Correct. There is a second concern involving promises. Previously |
This issue has been inactive for sufficiently long that it seems like perhaps it should be closed. Feel free to re-open (or leave a comment requesting that it be re-opened) if you disagree. I'm just tidying up and not acting on a super-strong opinion or anything like that. |
I would like to argue that this issue be re-opened. If for no other reason than it can be a place to report on any kind of progress that's made on adding better support for stack traces in Node. I think there is a legitimate gap in functionality here and until that gap can be closed, I would argue this issue should stay open. |
I've reopened it. Also, I've changed it from "question" to "feature request" |
Thanks! And to put a point on the real problem, imagine my database layer throws an error like the following:
How is this information useful to me especially if it occurred in production where I don't know what caused it? There is not a single line of code in that stack from my code and the error from the library I'm using doesn't even tell me what's really wrong. I would have absolutely no hope of figuring out what the cause of this was without reviewing every single database statement in my code. Am I in the minority here that thinks this is major issue in trying to deal with problems? |
And I guess I would like to challenge this as well. It seems to me that as something as fundamental as getting a full stack out of errors from my code is exactly what I would expect from my runtime platform. I'm not sure the argument that there are tradeoffs works in this particular case. I would argue that almost any reasonable production environment is going to want a feature like this and implemented in a way that doesn't have major downsides (like a huge performance hit). If such a thing can be done, almost everyone is going to want it and if it can't be done, almost nobody is going to want it. I don't see a lot of room for options here so giving me choices doesn't make me feel any better. |
@TazmanianD +1. I've been keeping myself sane by using |
You are not a minority. There is a secret solution called coroutines. Other languages like golang have them and they can also be enabled in node.js ( Benefits: 1) you can eliminate 99% of your async/await syntax overhead, 2) perfect stack traces (without performance overhead - actually the opposite) |
From their own documentation:
Which I would consider unacceptable for production use. The irony here is that the major argument in favor of the asynchronous model of Node is precisely for performance reasons and if the first thing you do is something to kill performance precisely as a consequence of that model, you gotta ask what the point in using Node is :-).
Oh man, you're my hero! It sounds like fibers is exactly what I've been wanting and I can see how they solve a bunch of different problems. I just played with it a bit but this may be a "user space" solution to my original problem that I'm actually happy with. I did give your There does seem to be a bunch of libraries built on top of fibers. Care to make an argument for yours over the others? You might want to consider having your library added to their list: https://github.com/laverdet/node-fibers/wiki. |
Fibers' overhead depends on your code patterns. If you are writing low level code which is completely dominated by callbacks (each callback doing little work, with shallow stacks) I would not recommend fibers because of the overhead of switching into a fiber in every callback. On the other hand, as soon as you start to have a somewhat thick layer of logic on top of async calls, then coroutines are a winner. An async function calling another async function will not involve any intermediate promise creation / promise resolution overhead; it will just be a direct, normal function call (this is why you get perfect stack traces for free). So the deeper your stacks, the more you gain. And this is no toy. Some frameworks (Meteor) have been built with fibers. The project I am involved with has been using it for 5+ years. We used it hidden under a preprocessor (streamline.js) initially, which gave us the option to swap fibers for pure callbacks or generators (in case something went wrong with fibers). But the newer parts of the project are now written with f-promise. One important gotcha: fibers does not run in browsers, as it requires a binary add-on. I hope that coroutines will make it into JavaScript some day but there has been very strong resistance so far (the single-frame continuation rule). The main selling point of my little library: the simplicity of its API (2 calls) and the ease of interop with promises (and callbacks too - just noticed this was not documented so I added it to the README). I also designed it to be a natural upgrade for the streamline.js community (CLS feature is compatible). But just google around and you'll find other libraries. |
V8 now offers an I'm not sure what if any Node's support this new V8 capability. |
Current release (Node 11) just updated to v8 7.0; looks like the async stack traces are coming in 7.1 if I'm reading this right, so hopefully it'll be coming soon. |
This V8 feature is still experimental and depends on a spec change to a particular detail of async/await. It also isn't able to reconstruct the full stack trace in some cases. But it's better than nothing. I hope you can use it by Node 12, but you probably can already check it out with Node Canary. |
Just confirmed that the node version at https://nodejs.org/download/v8-canary/v12.0.0-v8-canary20181217e2f11a7b92/ supports the |
@eliranamar |
Detailed information about the implementation: https://docs.google.com/document/d/13Sy_kBIJGP0XT34V1CV3nkWya4TwYx9L3Yv45LdGB6Q/edit |
Bug: v8:7522, v8:8673 Change-Id: Iee2d6fda9291fbdd346d25d2c535874dba13fdc9 Ref: nodejs/node#11865 Design-Document: http://bit.ly/v8-zero-cost-async-stack-traces Reviewed-on: https://chromium-review.googlesource.com/c/1396425 Commit-Queue: Benedikt Meurer <bmeurer@chromium.org> Reviewed-by: Yang Guo <yangguo@chromium.org> Cr-Commit-Position: refs/heads/master@{#58765}
Is |
@gajus no. Those are tracked by default. |
Are async stack traces supposed to work inside catch blocks? Or does it depend on the intervening code to have an async chain? They're missing for me with code like this in Node 12.16.2 at least... (connection.query involves event emitters under the hood) I had hoped that the async query(sql, parameters) {
try {
await new Promise((resolve, reject) => connection.query(sql, (error, result) => error ? reject(error) : resolve(result)));
} catch (err) {
// None of these include the async stack traces...
console.error(err.stack)
console.error(new Error('Test').stack)
throw new Error('Test')
}
// but if we get here than this error does get the async stack trace
throw new Error('Test')
} |
@TazmanianDI Edward j |
And here comes node
back to console.log binary searching the codebase ;c |
Version: v7.7.3
Platform: Windows 7x64
The purpose of this issue is really a more broad request for better stack traces with async/await but I figured I would start with a very specific case. If we need to broaden the description, I'm good with that. I've seen a number of lengthy discussion on this subject in various places but I don't see any actual issues for it so I thought I'd start one and hopefully it's not just a duplicate of something I missed.
I'm filing the specific issue here because it seems the async/await functionality just added provides less useful error handling than we could get with generators.
Outputs:
That stack is missing everything that called
functionOne
(functionTwo
specifically).The generator equivalent of this:
Outputs:
Here you can see both
functionOne
andfunctionTwo
in the stack.If the error is thrown before any
await
in the code, then you actually get a complete stack trace even if the function was called in a whole chain of awaits and regardless if those awaits were first or not:Outputs:
The real driving force behind this was that I finally found the recipe to get complete stack traces, even when dealing with existing code using promises. With a try...catch in the generator, we can use
VError
to weld together the errors thrown by promises with the stack of the code calling the generator. This does not seem to work with async functions.Here's a more complete example using generators that I really wish would continue to work with async functions:
Outputs (with some non-relevant stuff removed):
The async equivalent outputs this:
As you can see, this has the same problem as at the top in that the rethrown error doesn't have a complete stack.
The text was updated successfully, but these errors were encountered: