Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit

  • Loading branch information...
commit 0e021f560ea32b664665ac2f96be0e113018ef4c 0 parents
@chrisdickinson authored
Showing with 257 additions and 0 deletions.
  1. +98 −0 README.md
  2. +17 −0 package.json
  3. +142 −0 trace.js
98 README.md
@@ -0,0 +1,98 @@
+Trace.js
+========
+
+Providing better stack traces for V8 by giving you full-fledged objects for each frame in the trace.
+
+Examples
+========
+
+Creating a nice color trace with context, reversed so that the latest call is printed last:
+
+````javascript
+var trace = require('trace');
+
+try {
+ somethingThatThrowsAnError();
+} catch(err) {
+ var stacktrace = trace(err);
+ console.error(stacktrace.toString());
+}
+````
+
+Iterating through frames and grabbing the constituent files:
+
+````javascript
+var trace = require('trace');
+
+try {
+ somethingThatThrowsAnError();
+} catch(err) {
+ var stacktrace = trace(err);
+ for(var i = 0, len = stacktrace.frames.length; i < len; ++i) {
+ console.error(stacktrace.frames[i].filename, stacktrace.frames[i].filedata());
+ }
+}
+````
+
+API
+===
+
+trace(err)
+-----
+
+Creates and returns a `Trace` object by parsing an `Error` object.
+
+object Trace
+------------
+
+Holds the original error, the first line of the trace (the message), and the frames that make up the stack trace. Returned by `trace`.
+
+Members:
+
+*. `frames`: an `Array` of `Frame` objects.
+*. `first_line`: the first line of the original stack trace -- usually contains the error message, if any.
+*. `original_error`: the original `Error` object that the `Trace` was generated from.
+
+Trace.defaults
+--------------
+
+The default printing mode for the trace; an array of `[context_lines:int, `print_cursor:boolean`, `highlight_character_color:string`].
+Defaults to two lines of context with a cursor, with the character that caused the error appearing red.
+
+Trace#toString(reversed[, contextLines, showCursor, highlightErrorCharacterColor])
+--------------
+
+Returns the prettified stack trace as a string, using `Trace.defaults`. `reversed` defaults to `true`, meaning the most recent call is displayed last. The remaining arguments are passed to `Frame#toString` for each frame in `Trace#frames`.
+
+object Frame
+------------
+
+Contains information about a specific stack frame.
+
+Members:
+
+*. `named_location`: The name of the scope where the frame originated; e.g., `'ReadStream.emit'`.
+*. `filename`: The filename of the frame.
+*. `line`: The integer line number of the frame.
+*. `character`: The character at which the error occurred in the line of the frame.
+
+Frame#filedata()
+--------------
+
+Returns a string containing the text of the file the frame originated from. Works on both native modules as well as userland modules. Cached and synchronous.
+
+Frame#toString([contextLines, showCursor, highlightErrorCharacterColor])
+---------------
+
+Wraps the output from `Frame#get_lines()` with information about the file, line number, character, and scope if available.
+
+Frame#get_lines(context, ascii_cursor, highlight_error_start)
+---------------
+
+Returns a string containing `context` lines surrounding the error line from the file of this frame. If `ascii_cursor` is `true`, it will
+insert a `>` at the line where the error occurred, and a space before all other lines. `highlight_error_start` can be any value that [ansi-colors](https://github.com/loopj/commonjs-ansi-color) will accept, or false to avoid highlighting the character.
+
+License
+=======
+
+new BSD.
17 package.json
@@ -0,0 +1,17 @@
+{
+ "author": "Chris Dickinson <chris@neversaw.us> (http://neversaw.us/)",
+ "name": "tracejs",
+ "description": "Expand Error.stack traces into usable objects providing context and highlighting",
+ "version": "0.0.1",
+ "homepage": "http://github.com/chrisdickinson/tracejs/",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/chrisdickinson/tracejs.git"
+ },
+ "main": "trace.js",
+ "engines": {
+ "node": "~v0.4.7"
+ },
+ "dependencies": {"ansi-color":"0.2.X"},
+ "devDependencies": {}
+}
142 trace.js
@@ -0,0 +1,142 @@
+var fs = require('fs'),
+ natives = process.binding('natives'),
+ color = require('ansi-color').set;
+
+var err_re = / at ([^\s]+) \(([\w\d\._\-\/]+):(\d+):(\d+)\)/g;
+
+var Trace = function(first_line, frames, original_error) {
+ this.first_line = first_line;
+ this.frames = frames;
+ this.original_error;
+};
+
+Trace.defaults = [2, true, 'red'];
+
+Trace.prototype.toString = function(reversed) {
+ reversed === undefined && (reversed = true);
+ var args = [].slice.call(args, 1);
+ args.length === 0 && (args = Trace.defaults.slice());
+
+ var frame_data = [this.first_line, '======\n'].concat(this.frames.map(function(frame) {
+ return frame.toString.apply(frame, args);
+ }));
+
+ if(reversed)
+ frame_data = frame_data.reverse();
+
+ return frame_data.join('');
+};
+
+var Frame = function(named_location, filename, line, character) {
+ this.named_location = named_location;
+ this.filename = filename;
+ this.line = line;
+ this.character = character;
+ this._filedata = null;
+};
+
+Frame.prototype.load_file_native = function() {
+ var base_name = this.filename.replace(/\.js$/g, ''),
+ data = natives[base_name] || '';
+
+ return data;
+};
+
+Frame.prototype.load_file_path = function() {
+ try {
+ return fs.readFileSync(this.filename, 'utf8').toString();
+ } catch(err) {
+ return '';
+ }
+};
+
+Frame.prototype.filedata = function() {
+ if(this._filedata)
+ return this._filedata;
+
+ if(this.filename.indexOf('/') === -1) {
+ this._filedata = this.load_file_native();
+ } else {
+ this._filedata = this.load_file_path();
+ }
+ return this._filedata;
+};
+
+Frame.prototype.toString = function() {
+ var args = [].slice.call(arguments);
+ return 'file '+color('"'+this.filename.replace(process.cwd(), '.')+'"', 'cyan')+' line '+color(this.line, 'red+bold')+', char '+color(this.character, 'red+bold')+', in '+color(this.named_location, 'cyan')+':\n'+
+ color(this.get_lines.apply(this, args), 'yellow')+'\n';
+};
+
+Frame.prototype.get_lines = function(context, ascii_cursor, highlight_error_start) {
+ context = context || 0;
+ filedata = this.filedata().split('\n');
+
+ var start_line = this.line - context - 1,
+ end_line = this.line + context,
+ character = this.character;
+
+ var lines = filedata.slice(start_line, end_line);
+
+ if(highlight_error_start) {
+ lines = lines.map(function(line, idx) {
+ if(idx === context) {
+ line = line.split(/\b/g);
+ var start = 0;
+ line = line.map(function(word) {
+ var next = start + word.length;
+ if(character <= next && character >= start) {
+ word = color(word, highlight_error_start);
+ }
+ start = next;
+ return word;
+ }).join('');
+ }
+ return line;
+ });
+ }
+ if(ascii_cursor) {
+ lines = lines.map(function(line, idx) {
+ return (idx === context ? '>' : ' ') + line;
+ });
+ }
+
+ return lines.join('\n');
+};
+
+var trace = function(err) {
+ if(!err) {
+ err = {};
+ Error.captureStackTrace(err);
+ }
+
+ var lines = err.stack.split('\n'),
+ first = lines[0],
+ stack = lines.slice(1).join('\n');
+
+ var frames = [],
+ match;
+
+ do {
+ match = err_re(stack);
+ if(match) {
+ frames.push(
+ new Frame(match[1], match[2], parseInt(match[3], 10), parseInt(match[4], 10))
+ );
+ }
+ } while(match);
+
+ return new Trace(first, frames, err);
+};
+
+exports.trace = trace;
+exports.Frame = Frame;
+exports.Trace = Trace;
+
+exports.set_context = function(context) {
+ Trace.defaults[0] = context;
+};
+
+exports.set_add_cursor = function(tf) {
+ Trace.defaults[1] = tf;
+};
Please sign in to comment.
Something went wrong with that request. Please try again.