-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat(node): Add LocalVariables
integration to capture local variables to stack frames
#6478
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
Conversation
Do we have an idea of the performance impact here? |
I haven't measured anything yet but I suspect it's Not That Bad™️. I've had to cache the promise(s) looking up the stack vars because I found that the event processor was called before all the inspector async calls returned. This suggests that the debug pause isn't that long. I guessed it would block until our async function completed but it doesn't. I'm wondering whether the inspector API might be a suitable alternative to hooking One caveat: I've only tested a single recent version of node! |
This is now failing the AWS Lambda serverless build. I had assumed that What's the safest way to only load this integration in node.js >= v14 while not tripping up Rollup or other bundlers? |
Can we just always import this integration but then gate the functionality by a runtime node version check? |
My concern was ending up with code containing However, will |
It shouldn't since it's running NodeJS, so the APIs should all be there, and we're not worried about non-node platforms for now. If the import exists since Node 8, we should be good to go. |
86be9bc
to
5e0174b
Compare
|
||
const testScriptPath = path.resolve(__dirname, 'no-local-variables.js'); | ||
|
||
childProcess.exec(`node ${testScriptPath}`, { encoding: 'utf8' }, (_, stdout) => { |
Check warning
Code scanning / CodeQL
Shell command built from environment values
|
||
const testScriptPath = path.resolve(__dirname, 'local-variables.js'); | ||
|
||
childProcess.exec(`node ${testScriptPath}`, { encoding: 'utf8' }, (_, stdout) => { |
Check warning
Code scanning / CodeQL
Shell command built from environment values
This is ready for a first pass! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1st pass. Would be nice to get some unit tests for the event manipulation and _handlePaused
func. Thanks for taking a look Tim!
the option is off by default correct? Did you already start looking at adding it to docs for node? |
Yes, this is disabled by default and enabled by It's in
No but I will do! |
d0180fc
to
0b7fd39
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's - excited to see this out there.
We also need a docs PR for this!
this.on('Debugger.paused', onPause); | ||
this.post('Debugger.enable'); | ||
// We only want to pause on uncaught exceptions | ||
this.post('Debugger.setPauseOnExceptions', { state: 'uncaught' }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this mean we only add stack locals for uncaught exceptions?
What do you think about making this configurable then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's probably worth adding an options object as the first constructor argument so we can also add more options in the future without a breaking change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add that now, and then merge this in? We can keep the options object empty for now.
stackParser: StackParser, | ||
{ params: { reason, data, callFrames } }: InspectorNotification<PausedExceptionEvent>, | ||
): Promise<void> { | ||
if (reason !== 'exception' && reason !== 'promiseRejection') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a list of all the reasons somewhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#event-paused
Allowed Values: ambiguous, assert, CSPViolation, debugCommand, DOM, EventListener, exception, instrumentation, OOM, other, promiseRejection, XHR
Added an empty options object. Docs PR next! |
Even though this could be pretty useful, local variables may contain sensitive information and sentry seems to be planning to add this to the list of default integrations. See getsentry/sentry-javascript#6478
Closes #6369
Takes the demo by @kamilogorek and adds:
It looks like the
error.stack
is available through the inspector errordata.description
. This is parsed and used to create a unique hash which is used later to lookupvars
for stack frames when the event processor is run.Applying
vars
to frames is best effort since the function names from the inspector don't always match those from the stack trace. The most obvious place this happens it anonymous functions. For example the name of anonymous function passed tosetTimeout
shows as an empty string from the inspector andTimeout._onTimeout
in the stack trace.What's left:
reason: 'promiseRejection'