Skip to content

Commit 2439880

Browse files
committed
Merge pull request #5 from appscot/console_parity
Console parity
2 parents 44e87e5 + 7490224 commit 2439880

File tree

7 files changed

+297
-23
lines changed

7 files changed

+297
-23
lines changed

README.md

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ A thin wrapper for visionmedia/debug logger, adding levels and colored output.
99
[visionmedia/debug](https://github.com/visionmedia/debug) is a ubitiquous logging library with 1000+ dependants. Given how widespread it is and the convenience of namespaces it is a great logger for library modules.
1010
`debug-logger` is a convenience wrapper around `debug` that adds level based coloured output. Each instance of `debug-logger` will lazily instantiate several instances of `debug` such as `namespace:info`, `namespace:warn`, `namespace:error`, etc. All these are configurable. `debug-logger` has no dependencies besides `debug`.
1111

12+
`debug-logger` uses the same syntax as [node.js console](https://nodejs.org/api/console.html) so you can use it as drop in replacement.
13+
Check and run [examples/console.parity.js](https://github.com/appscot/debug-logger/blob/master/examples/console.parity.js) for more details.
14+
1215
At AppsCot we use `debug-logger` in [waterline-orientdb](https://github.com/appscot/waterline-orientdb).
1316

1417
## Instalation
@@ -105,6 +108,66 @@ log.warn("util.format style string: %s, number: %d and json: %j.", "foo", 13, {
105108
```
106109
![multiple arguments](https://raw.githubusercontent.com/wiki/appscot/debug-logger/arguments.png)
107110

111+
### Measure code execution time
112+
```javascript
113+
log.time('100-elements');
114+
for (var i = 0; i < 100; i++) {
115+
;
116+
}
117+
log.timeEnd('100-elements');
118+
119+
log.time('setTimeout');
120+
setTimeout(function(){
121+
var diff = log.timeEnd('setTimeout', 'debug');
122+
log.trace('log.timeEnd returns diff so it can be reused:', diff);
123+
}, 262);
124+
```
125+
![code time](https://raw.githubusercontent.com/wiki/appscot/debug-logger/time.png)
126+
127+
### Inspect object
128+
```javascript
129+
log.dir({ foo: { bar: 1 } });
130+
log.dir({ foo: { bar: 1 } }, { depth: 0 }, 'warn');
131+
```
132+
![dir inspect](https://raw.githubusercontent.com/wiki/appscot/debug-logger/dir.png)
133+
134+
### Assert condition
135+
```javascript
136+
log.assert(1 == 0);
137+
138+
// More elaborate example
139+
var log = require('..').config({
140+
levels: {
141+
fatal: {
142+
color : 5, //magenta
143+
prefix : '',
144+
namespaceSuffix : ':fatal',
145+
level : 6
146+
}
147+
}
148+
})('myapp');
149+
log.assert(1 == 0, 'testing %s %d', 'log.assert', 666, 'fatal');
150+
```
151+
![assert](https://raw.githubusercontent.com/wiki/appscot/debug-logger/assert.png)
152+
153+
### stderr vs stdout
154+
By default trace, debug, log and info output to stdout while warn and error output to stderr.
155+
This is configurable, [example](https://github.com/appscot/debug-logger/blob/master/examples/stdout_stderr.js):
156+
```javascript
157+
var log = require('debug')('myapp');
158+
log.trace("goes to stdout!");
159+
log.debug("goes to stdout!");
160+
log.log("goes to stdout!");
161+
log.info("goes to stdout!");
162+
log.warn("goes to stderr");
163+
log.error("goes to stderr");
164+
165+
// outputting only to stdout
166+
var stdout = require('debug').config({ levels: { warn: { fd: 1 }, error: { fd: 1 } } })('stdoutapp');
167+
stdout.warn("goes to stdout!");
168+
stdout.error("goes to stdout!");
169+
```
170+
108171
### Filter by log level (instead of namespace)
109172
```sh
110173
export DEBUG_LEVEL=info
@@ -132,12 +195,33 @@ Prints the data prepended by log level. If the terminal supports colors, each le
132195
```
133196
This function can take multiple arguments in a printf()-like way, if formatting elements are not found in the first string then util.inspect is used on each argument.
134197

198+
#### `log([message][, ...])`
199+
Outputs the message using the root/default `debug` instance, without the level suffix. Example:
200+
```
201+
myapp I'm a root/default debug instance output +0ms
202+
```
203+
135204
#### `log[level].logger()`
136-
Returns the default debug instance used by `level`.
205+
Returns the default `debug` instance used by `level`.
137206

138207
#### `log[level].enabled()`
139208
Boolean indicating if `level`'s logger is enabled.
140209

210+
#### `log.time(label)`
211+
Mark a time.
212+
213+
#### `log.timeEnd(label[, level])`
214+
Finish timer, record output. `level` will determine the logger used to output the result (defaults to 'log').
215+
Return duration in ms.
216+
217+
#### `log.dir(obj[, options][, level])`
218+
Uses util.inspect on obj and prints resulting string to the appropriate logger. This function bypasses any custom inspect() function on obj. An optional [options object](https://nodejs.org/api/console.html#console_console_dir_obj_options) may be passed that alters certain aspects of the formatted string.
219+
`level` will determine the logger used to output the result (defaults to 'log').
220+
221+
#### `log.assert(value[, message][, ...][, level])`
222+
Similar to [console.assert()](https://nodejs.org/api/console.html#console_console_assert_value_message).
223+
Additionally it outputs the error using the appropriate logger set by `level` (defaults to 'error').
224+
141225
### Module
142226

143227
#### `.config(obj)`

debug-logger.js

Lines changed: 91 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,25 +40,29 @@ exports.levels = {
4040
color : exports.colors.cyan,
4141
prefix : '',
4242
namespaceSuffix : ':trace',
43-
level : 0
43+
level : 0,
44+
fd : 1 // currently only 1 (stdout) is supported. stderr is debug's standard
4445
},
4546
debug : {
4647
color : exports.colors.blue,
4748
prefix : '',
4849
namespaceSuffix : ':debug',
49-
level : 1
50+
level : 1,
51+
fd : 1
5052
},
5153
log : {
5254
color : '',
5355
prefix : ' ',
5456
namespaceSuffix : ':log',
55-
level : 2
57+
level : 2,
58+
fd : 1
5659
},
5760
info : {
5861
color : exports.colors.green,
5962
prefix : ' ',
6063
namespaceSuffix : ':info',
61-
level : 3
64+
level : 3,
65+
fd : 1
6266
},
6367
warn : {
6468
color : exports.colors.yellow,
@@ -79,6 +83,50 @@ exports.styles = {
7983
};
8084

8185

86+
function time(label){
87+
this.timeLabels[label] = process.hrtime();
88+
}
89+
90+
function timeEnd(label, level){
91+
level = level || 'log';
92+
var diff = process.hrtime(this.timeLabels[label]);
93+
var diffMs = (diff[0] * 1e9 + diff[1]) / 1e6;
94+
this[level](label + ':', diffMs + 'ms');
95+
return diffMs;
96+
}
97+
98+
function dir(obj, options, level){
99+
if(!level && this[options]){
100+
level = options;
101+
options = undefined;
102+
}
103+
options = options || exports.inspectOptions;
104+
level = level || 'log';
105+
this[level](util.inspect(obj, options));
106+
}
107+
108+
function assert(expression){
109+
if (!expression) {
110+
var level = 'error';
111+
var arr = Array.prototype.slice.call(arguments, 1);
112+
if(this[arr[arr.length-1]]){
113+
level = arr[arr.length-1];
114+
arr = arr.slice(0, -1);
115+
}
116+
var assrt = require('assert');
117+
var err = new assrt.AssertionError({
118+
message: util.format.apply(this, arr),
119+
actual: false,
120+
expected: true,
121+
operator: '==',
122+
stackStartFunction: assert
123+
});
124+
this[level](err);
125+
throw err;
126+
}
127+
}
128+
129+
82130
var ensureNewlineEnabled = false;
83131
var fd = parseInt(process.env.DEBUG_FD, 10) || 2;
84132
function ensureNewline(){
@@ -210,10 +258,21 @@ function getForeColor(color){
210258
return color;
211259
}
212260

261+
function disableColors(loggerLevel, disable){
262+
if(disable){
263+
loggerLevel.color = '';
264+
loggerLevel.reset = '';
265+
loggerLevel.inspectionHighlight = '';
266+
}
267+
}
268+
213269
var debugInstances = {};
214-
function getDebugInstance(namespace, color){
270+
function getDebugInstance(namespace, color, fd){
215271
if(!debugInstances[namespace]){
216272
debugInstances[namespace] = vmDebug(namespace);
273+
if(fd === 1 && isNaN(parseInt(process.env.DEBUG_FD))){
274+
debugInstances[namespace].log = console.log.bind(console);
275+
}
217276
if(!isNaN(color)){
218277
debugInstances[namespace].color = color;
219278
}
@@ -224,29 +283,41 @@ function getDebugInstance(namespace, color){
224283

225284
function debugLogger(namespace) {
226285
var levels = exports.levels;
227-
var debugLoggers = { 'default': getDebugInstance.bind(this, namespace) };
286+
var debugLoggers = { 'default': getDebugInstance.bind(this, namespace, '') };
228287

229-
var logger = {};
288+
var logger = function(){
289+
debugLoggers['default']().apply(this, arguments);
290+
};
230291
logger.logLevel = getLogLevel(namespace);
231292

293+
logger.timeLabels = {};
294+
logger.time = time;
295+
logger.timeEnd = timeEnd;
296+
logger.dir = dir;
297+
logger.assert = assert;
298+
232299
Object.keys(levels).forEach(function(levelName) {
233300
var loggerNamespaceSuffix = levels[levelName].namespaceSuffix ? levels[levelName].namespaceSuffix : 'default';
234301
if(!debugLoggers[loggerNamespaceSuffix]){
235-
debugLoggers[loggerNamespaceSuffix] = getDebugInstance.bind(this, namespace + loggerNamespaceSuffix, levels[levelName].color);
302+
debugLoggers[loggerNamespaceSuffix] = getDebugInstance.bind(this, namespace + loggerNamespaceSuffix, levels[levelName].color, levels[levelName].fd);
236303
}
237304
var levelLogger = debugLoggers[loggerNamespaceSuffix];
238-
var color = vmDebug.useColors ? getForeColor(levels[levelName].color) : '';
239-
var reset = vmDebug.useColors ? exports.colorReset : '';
240-
var inspectionHighlight = vmDebug.useColors ? exports.styles.underline : '';
305+
306+
var initialized = false;
241307

242308
function logFn() {
243309
if (logger.logLevel > logger[levelName].level) { return; }
244310

245311
var levelLog = levelLogger();
246312
if(!levelLog.enabled) { return; }
247313

314+
if(!initialized){
315+
initialized = true;
316+
disableColors(logger[levelName], !levelLog.useColors);
317+
}
318+
248319
if (isString(arguments[0]) && hasFormattingElements(arguments[0])){
249-
arguments[0] = color + levels[levelName].prefix + reset + arguments[0];
320+
arguments[0] = logger[levelName].color + levels[levelName].prefix + logger[levelName].reset + arguments[0];
250321
return levelLog.apply(this, arguments);
251322
}
252323

@@ -263,14 +334,14 @@ function debugLogger(namespace) {
263334
param = errorStrings[i];
264335
message += i === 0 ? param[0] : ' ' + param[0];
265336
if (param.length > 1) {
266-
var highlightStack = param[1].indexOf('Stack') >= 0 ? color : '';
337+
var highlightStack = param[1].indexOf('Stack') >= 0 ? logger[levelName].color : '';
267338
inspections += '\n' +
268-
inspectionHighlight + '___' + param[1] + ' #' + n++ + '___' + reset +'\n' +
269-
highlightStack + param[2] + reset;
339+
logger[levelName].inspectionHighlight + '___' + param[1] + ' #' + n++ + '___' + logger[levelName].reset +'\n' +
340+
highlightStack + param[2] + logger[levelName].reset;
270341
}
271342
};
272343

273-
levelLog(color + levels[levelName].prefix + reset + message + inspections);
344+
levelLog(logger[levelName].color + levels[levelName].prefix + logger[levelName].reset + message + inspections);
274345
};
275346

276347
function logNewlineFn() {
@@ -280,10 +351,13 @@ function debugLogger(namespace) {
280351
logFn.apply(logFn, arguments);
281352
};
282353

283-
logger[levelName] = ensureNewlineEnabled ? logNewlineFn : logFn;
354+
logger[levelName] = ensureNewlineEnabled ? logNewlineFn : logFn;
284355
logger[levelName].level = levels[levelName].level;
285356
logger[levelName].logger = function(){ return levelLogger(); };
286357
logger[levelName].enabled = function(){ return logger.logLevel <= logger[levelName].level && levelLogger().enabled; };
358+
logger[levelName].color = getForeColor(levels[levelName].color);
359+
logger[levelName].reset = exports.colorReset;
360+
logger[levelName].inspectionHighlight = exports.styles.underline;
287361
});
288362

289363
return logger;

examples/console.parity.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// The below only shows up if environment variable DEBUG includes "myapp:*" namespace
2+
// e.g.: export DEBUG=$DEBUG,myapp:*
3+
var log = require('..')('myapp');
4+
5+
var count = 5;
6+
console.log('count: %d', count);
7+
log.log('count: %d', count);
8+
9+
br();
10+
count++;
11+
console.info('count:', count, '...');
12+
log.info('count:', count, '...');
13+
14+
br();
15+
count++;
16+
console.error('count: %d', count);
17+
log.error('count: %d', count);
18+
19+
br();
20+
console.warn('plain message');
21+
log.warn('plain message');
22+
23+
br();
24+
console.dir({ foo: { bar: 1 } }, { depth: 0 });
25+
log.dir({ foo: { bar: 1 } }, { depth: 0 });
26+
27+
br();
28+
console.time('100-elements');
29+
log.time('100-elements');
30+
for (var i = 0; i < 100; i++) {
31+
;
32+
}
33+
console.timeEnd('100-elements');
34+
log.timeEnd('100-elements');
35+
36+
br();
37+
count++;
38+
// log.trace accepts similar input as console.log but it behaves more like log4j's TRACE as it prints a finer level of events
39+
console.trace('count: %d', count);
40+
log.trace('count: %d', count);
41+
42+
br();
43+
try{
44+
console.assert(1 == 0, 'Wrong value');
45+
} catch(err){}
46+
try{
47+
log.assert(1 == 0, 'Wrong value');
48+
} catch(err){}
49+
50+
51+
52+
br();
53+
function br(){
54+
console.log();
55+
}

0 commit comments

Comments
 (0)