-
Notifications
You must be signed in to change notification settings - Fork 211
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
Enable node.js stack locals capture #902
Conversation
3547511
to
b02bb69
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.
Overall I made comments/asked questions about the implementation and public api of Locals
Also wondering, should there be unit tests for the Locals
API?
The tests I see seem to be routed through the rollbar SDK
src/server/locals.js
Outdated
var key = params.data.description; | ||
this.currentErrors.set(key, params); | ||
|
||
if (this.currentErrors.size > 4) { |
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.
@waltjones what's the significance of the size > 4
- should this be maintained in a descriptive constant value?
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.
Constant and explanatory comment added.
src/server/locals.js
Outdated
return callback(e); | ||
} | ||
|
||
this.getLocalScopesForFrames(matchedFrames, function(err) { |
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 reason not to just pass callback
as the second arg here?
as in:
this.getLocalScopesForFrames(matchedFrames, callback);
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.
Good catch. Each of these cases have been condensed now.
src/server/locals.js
Outdated
return matchedFrames; | ||
} | ||
|
||
Locals.prototype.firstFrame = function(localIndex, stackIndex) { |
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 need for this function to be attached to the Locals.prototype
?
Seems better suited as a private local utility function
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.
Non-interface functions are all private now.
src/server/locals.js
Outdated
return !localIndex && !stackIndex; | ||
} | ||
|
||
Locals.prototype.matchedFrame = function(callFrame, stackLocation) { |
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.
same comment about this function as the above for firstFrame
- this function isn't making use of this
- does it need to be exported on the Locals.prototype
?
src/server/locals.js
Outdated
} | ||
|
||
Locals.prototype.getLocalScopesForFrames = function(matchedFrames, callback) { | ||
async.each(matchedFrames, this.getLocalScopeForFrame.bind(this), function (err) { |
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.
seems that you can pass callback
as the thrid arg to async.each
rather than having the wrapping function - is there a reason for the extra wrapping function?
seems it will be called no matter the outcome the way it's written
src/server/locals.js
Outdated
callFrameColumn === position.column; | ||
} | ||
|
||
Locals.prototype.getLocalScopesForFrames = function(matchedFrames, callback) { |
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 function need to be attached to the Locals.prototype
and exported? I don't see it used outside of the mergeLocals
body which does need to be exported (public).
As for the bind
call to this, that could be done by mergeLocals
when passing maybe? Just a thought on trying to expose less of these internal methods if they're not needed externally
src/server/locals.js
Outdated
var _this = this; | ||
for (var i = 0; i < scopes.length; i++) { | ||
var scope = scopes[i] | ||
if (scope.type === 'local') { |
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.
why not filter before the loop using a scopes.filter(scope => scope.type === 'local')
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.
Updated to use find
and expect one local scope.
src/server/locals.js
Outdated
return value; | ||
} | ||
|
||
Locals.prototype.getObjectValue = function(local) { |
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 need to be exported as attached to the Locals.prototype
?
src/server/locals.js
Outdated
} | ||
} | ||
|
||
Locals.prototype.getLocalValue = function(local) { |
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.
since the only ref to this
is for the this.getObjectValue
call, does this need to be exported on the Locals.prototype
?
var inspector = require('inspector'); | ||
var async = require('async'); | ||
|
||
function Locals(config) { |
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.
defining classes like this the old school JS way has the problem of possible function call without instantiation, i.e. missing the new
keyword by just calling Locals(…)
as a function. To prevent this from doing harm, a pattern emerged for checking how a constructor is called like this:
function Locals(config) {
if (!(this instanceof Locals)) {
return new Locals(config);
}
// rest of constructor logic
}
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.
Added.
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.
I never seem to get this right - requested changes are comments in previous "Comment" and submitting this now as a Review for the process to work
b02bb69
to
52cc8c1
Compare
Major updates are:
|
Update? |
Just waiting on approval or further feedback. |
Description of the change
Enables capture of local stack variables in Node.js. To enable, set
locals: true
in the config.This feature uses the Node Inspector API to access debugger data at runtime. The Inspector API is a wrapper for the V8 Inspector interface. While the V8 interface relies on
send()
andreceive()
functions that can't be accessed from Node, the Node Inspector API provides access to these via thepost()
method on the Inspector Session instance. Usingsession.post()
, all of the DevTools protocol can be accessed, including theDebugger
andRuntime
interfaces, which are used here.The initial version of this feature supports both caught and uncaught exceptions and promise rejections. Nested exceptions and trace chains are supported.
The DevTools protocol allows retrieving nested variables to an arbitrary depth. However, the initial version of this feature only implements the equivalent of depth = 0. A future PR may enable arbitrary depth with a config option to set the depth.
Unexpanded objects are identified by their class, in keeping with the example in the Rollbar API docs. (https://explorer.docs.rollbar.com/#tag/Item)
Example:
The feature is enabled for Node 10+. Node 8 doesn't implement the
url
property on theCallFrame
object, and cannot reliably resolve to the correct locations in the stack. (There may be a workaround, but none that I know at this time.)This PR adheres to the established style of Rollbar.js and uses
var
declarations, rather thanlet
andconst
. ES5 style classes are used. I'm not sure whethersession.post()
supports async/await. Regardless, callbacks are used throughout as shared code that cannot be made async prevents the use of async/await in this code path.Type of change
Related issues
Fixes: ch74679
Development
Code review