Permalink
Browse files

Feature: stackTrace.parse(err)

  • Loading branch information...
1 parent fdeed58 commit 314380d7abbcf62fd6fffdb31535aeb3ac3c6757 @felixge committed Jul 13, 2011
Showing with 148 additions and 0 deletions.
  1. +29 −0 Readme.md
  2. +57 −0 lib/stack-trace.js
  3. +2 −0 test/integration/test-get.js
  4. +60 −0 test/integration/test-parse.js
View
@@ -10,13 +10,29 @@ npm install stack-trace
## Usage
+The stack-trace module makes it easy for you to capture the current stack:
+
``` javascript
var stackTrace = require('stack-trace');
var trace = stackTrace.get();
require('assert').strictEqual(trace[0].getFileName(), __filename);
```
+However, sometimes you have already popped the stack you are interested in,
+and all you have left is an `Error` object. This module can help:
+
+``` javascript
+var stackTrace = require('stack-trace');
+var err = new Error('something went wrong');
+var trace = stackTrace.parse(err);
+
+require('assert').strictEqual(trace[0].getFileName(), __filename);
+```
+
+Please note that parsing the `Error#stack` property is not perfect, only
+certain properties can be retrieved with it as noted in the API docs below.
+
## API
### stackTrace.get([belowFn])
@@ -27,6 +43,19 @@ site.
When passing a function on the current stack as the `belowFn` parameter, the
returned array will only include `CallSite` objects below this function.
+### stackTrace.parse(err)
+
+Parses the `err.stack` property of an `Error` object into an array compatible
+with those returned by `stackTrace.get()`. However, only the following methods
+are implemented on the returned `CallSite` objects.
+
+* getTypeName
+* getFunctionName
+* getMethodName
+* getFileName
+* getLineNumber
+* getColumnNumber
+
### CallSite
The official v8 CallSite object API can be found [here][v8stackapi]. A quick
View
@@ -12,3 +12,60 @@ exports.get = function(belowFn) {
return v8StackTrace;
};
+
+exports.parse = function(err) {
+ var self = this;
+ var lines = err.stack.split('\n').slice(1);
+
+ return lines.map(function(line) {
+ var lineMatch = line.match(/at ([^\s]+)\s+\((.+?):(\d+):(\d+)\)/);
+ var methodMatch = lineMatch[1].match(/([^\.]+)(?:\.(.+))?/);
+ var object = methodMatch[1];
+ var method = methodMatch[2];
+
+ var functionName = lineMatch[1];
+ var methodName = null;
+ var typeName = 'Object';
+
+ if (method) {
+ typeName = object;
+ methodName = method;
+ }
+
+ if (method === '<anonymous>') {
+ methodName = null;
+ functionName = '';
+ }
+
+ var properties = {
+ fileName: lineMatch[2],
+ lineNumber: parseInt(lineMatch[3], 10),
+ functionName: functionName,
+ typeName: typeName,
+ methodName: methodName,
+ columnNumber: parseInt(lineMatch[4], 10),
+ };
+
+ return self._createParsedCallSite(properties);
+ });
+};
+
+exports._createParsedCallSite = function(properties) {
+ var methods = {};
+ for (var property in properties) {
+ var method = 'get' + property.substr(0, 1).toUpperCase() + property.substr(1);
+
+ (function(property) {
+ methods[method] = function() {
+ return properties[property];
+ }
+ })(property);
+ }
+
+ var callSite = Object.create(methods);
+ for (var property in properties) {
+ callSite[property] = properties[property];
+ }
+
+ return callSite;
+};
@@ -6,12 +6,14 @@ var stackTrace = require(common.dir.lib + '/stack-trace');
var trace = stackTrace.get();
assert.strictEqual(trace[0].getFunction(), testBasic);
+ assert.strictEqual(trace[0].getFunctionName(), 'testBasic');
assert.strictEqual(trace[0].getFileName(), __filename);
})();
(function testWrapper() {
(function testBelowFn() {
var trace = stackTrace.get(testBelowFn);
assert.strictEqual(trace[0].getFunction(), testWrapper);
+ assert.strictEqual(trace[0].getFunctionName(), 'testWrapper');
})();
})();
@@ -0,0 +1,60 @@
+var common = require('../common');
+var assert = common.assert;
+var stackTrace = require(common.dir.lib + '/stack-trace');
+
+(function testBasic() {
+ var err = new Error('something went wrong');
+ var trace = stackTrace.parse(err);
+
+ assert.strictEqual(trace[0].getFileName(), __filename);
+ assert.strictEqual(trace[0].getFunctionName(), 'testBasic');
+})();
+
+(function testWrapper() {
+ (function testBelowFn() {
+ var err = new Error('something went wrong');
+ var trace = stackTrace.parse(err);
+ assert.strictEqual(trace[0].getFunctionName(), 'testBelowFn');
+ assert.strictEqual(trace[1].getFunctionName(), 'testWrapper');
+ })();
+})();
+
+(function testSymmetry() {
+ var realTrace = stackTrace.get(); var err = new Error('something went wrong');
+ var parsedTrace = stackTrace.parse(err);
+
+ realTrace.forEach(function(real, i) {
+ var parsed = parsedTrace[i];
+
+ function compare(method, exceptions) {
+ var realValue = real[method]();
+ var parsedValue = parsed[method]();
+
+ if (exceptions && exceptions[i]) {
+ realValue = exceptions[i];
+ }
+
+ var realJson = JSON.stringify(realValue);
+ var parsedJson = JSON.stringify(parsedValue);
+
+ var message =
+ method + ': ' + realJson + ' != ' + parsedJson + ' (#' + i + ')';
+
+ assert.strictEqual(realValue, parsedValue, message);
+ }
+
+ compare('getFileName');
+ compare('getFunctionName', {
+ 3: 'Object..js',
+ 5: 'Function._load',
+ 6: 'Array.0',
+ 7: 'EventEmitter._tickCallback',
+ });
+ compare('getTypeName');
+ compare('getMethodName');
+ compare('getLineNumber');
+ compare('getColumnNumber', {
+ 0: 49
+ });
+ });
+})();

0 comments on commit 314380d

Please sign in to comment.