diff --git a/doc/api/console.markdown b/doc/api/console.markdown index 25d02953605ebc..03adc1e2457f2f 100644 --- a/doc/api/console.markdown +++ b/doc/api/console.markdown @@ -109,6 +109,21 @@ Prints to stdout with newline. This function can take multiple arguments in a If formatting elements are not found in the first string then `util.inspect` is used on each argument. See [util.format()][] for more information. +### console.group([data][, ...]) + +Starts a new logging group with an optional title. All console output that occurs +after calling this method and calling `console.groupEnd` has the same +indent level. + +### console.groupCollapsed([data][, ...]) + +Same as `console.group`. + +### console.groupEnd() + +Closes the most recent logging group that was created with `console.group` or +`console.groupCollapsed`. + ### console.time(label) Starts a timer that can be used to compute the duration of an operation. Timers diff --git a/lib/console.js b/lib/console.js index a0eec4e34878be..755fd1c611eaa7 100644 --- a/lib/console.js +++ b/lib/console.js @@ -23,6 +23,10 @@ function Console(stdout, stderr) { Object.defineProperty(this, '_stderr', prop); prop.value = new Map(); Object.defineProperty(this, '_times', prop); + prop.value = 0; + Object.defineProperty(this, '_group', prop); + prop.value = ''; + Object.defineProperty(this, '_prefix', prop); // bind the prototype functions to this Console instance var keys = Object.keys(Console.prototype); @@ -33,7 +37,8 @@ function Console(stdout, stderr) { } Console.prototype.log = function() { - this._stdout.write(util.format.apply(this, arguments) + '\n'); + this._stdout.write((this._group ? util.format.apply(this, arguments).replace( + /^/gm, this._prefix) : util.format.apply(this, arguments)) + '\n'); }; @@ -41,7 +46,8 @@ Console.prototype.info = Console.prototype.log; Console.prototype.warn = function() { - this._stderr.write(util.format.apply(this, arguments) + '\n'); + this._stderr.write((this._group ? util.format.apply(this, arguments).replace( + /^/gm, this._prefix) : util.format.apply(this, arguments)) + '\n'); }; @@ -49,9 +55,31 @@ Console.prototype.error = Console.prototype.warn; Console.prototype.dir = function(object, options) { - this._stdout.write(util.inspect(object, util._extend({ - customInspect: false - }, options)) + '\n'); + var str = util.inspect(object, util._extend({customInspect: false}, options)); + this._stdout.write((this._group ? + str.replace(/^/gm, this._prefix) : str) + '\n'); +}; + + +Console.prototype.group = function() { + if (arguments.length) { + this.log.apply(this, arguments); + } + this._group++; + this._prefix = ' '.repeat(this._group); +}; + + +Console.prototype.groupCollapsed = function() { + this.group.apply(this, arguments); +}; + + +Console.prototype.groupEnd = function() { + if (this._group) { + this._group--; + this._prefix = (this._group ? ' '.repeat(this._group) : ''); + } }; diff --git a/test/parallel/test-console-group.js b/test/parallel/test-console-group.js new file mode 100644 index 00000000000000..7342a9e14a3f17 --- /dev/null +++ b/test/parallel/test-console-group.js @@ -0,0 +1,67 @@ +var common = require('../common'); +var assert = require('assert'); + +assert.ok(process.stdout.writable); +assert.ok(process.stderr.writable); +// Support legacy API +assert.equal('number', typeof process.stdout.fd); +assert.equal('number', typeof process.stderr.fd); + +assert.doesNotThrow(function () { + console.group('label'); + console.groupEnd(); +}); + +assert.doesNotThrow(function () { + console.groupCollapsed('label'); + console.groupEnd(); +}); + +// test multiple calls to console.groupEnd() +assert.doesNotThrow(function () { + console.groupEnd(); + console.groupEnd(); + console.groupEnd(); +}); + +var stdout_write = global.process.stdout.write; +var strings = []; +global.process.stdout.write = function(string) { + strings.push(string); +}; +console._stderr = process.stdout; + +// test console.group(), console.groupCollapsed() and console.groupEnd() +console.log('foo'); +console.group('bar'); +console.log('foo bar'); +console.groupEnd(); +console.groupCollapsed('group 1'); +console.log('log'); +console.warn('warn'); +console.error('error'); +console.dir({ foo: true }); +console.group('group 2'); +console.log('foo bar hop'); +console.log('line 1\nline 2\nline 3'); +console.groupEnd(); +console.log('end 2'); +console.groupEnd(); +console.log('end 1'); + +global.process.stdout.write = stdout_write; + +assert.equal('foo\n', strings.shift()); +assert.equal('bar\n', strings.shift()); +assert.equal(' foo bar\n', strings.shift()); +assert.equal("group 1\n", strings.shift()); +assert.equal(' log\n', strings.shift()); +assert.equal(' warn\n', strings.shift()); +assert.equal(' error\n', strings.shift()); +assert.equal(' { foo: true }\n', strings.shift()); +assert.equal(' group 2\n', strings.shift()); +assert.equal(' foo bar hop\n', strings.shift()); +assert.equal(' line 1\n line 2\n line 3\n', strings.shift()); +assert.equal(' end 2\n', strings.shift()); +assert.equal('end 1\n', strings.shift()); +assert.equal(strings.length, 0);