colored bunyan CLI output, and --color option

This commit is contained in:
Trent Mick 2012-02-22 21:03:03 -08:00
parent 0f02707429
commit 92a2e9d005
2 changed files with 74 additions and 17 deletions

View file

@ -2,6 +2,9 @@
## bunyan 0.6.5 (not yet released) ## bunyan 0.6.5 (not yet released)
- ANSI coloring output from `bunyan` CLI tool (for the default output mode/style).
Also add the '--color' option to force coloring if the output stream is not
a TTY, e.g. `cat my.log | bunyan --color | less -R`.
- Add 'level' field to log record before custom fields for that record. This just - Add 'level' field to log record before custom fields for that record. This just
means that the raw record JSON will show the 'level' field earlier, which is a bit means that the raw record JSON will show the 'level' field earlier, which is a bit
nicer for raw reading. nicer for raw reading.

View file

@ -48,10 +48,13 @@ var levelFromName = {
}; };
var nameFromLevel = {}; var nameFromLevel = {};
var upperNameFromLevel = {}; var upperNameFromLevel = {};
var upperPaddedNameFromLevel = {};
Object.keys(levelFromName).forEach(function (name) { Object.keys(levelFromName).forEach(function (name) {
var lvl = levelFromName[name]; var lvl = levelFromName[name];
nameFromLevel[lvl] = name; nameFromLevel[lvl] = name;
upperNameFromLevel[lvl] = name.toUpperCase(); upperNameFromLevel[lvl] = name.toUpperCase();
upperPaddedNameFromLevel[lvl] = (
name.length === 4 ? ' ' : '') + name.toUpperCase();
}); });
@ -132,6 +135,8 @@ function printHelp() {
util.puts(" -h, --help print this help info and exit"); util.puts(" -h, --help print this help info and exit");
util.puts(" --version print version of this command and exit"); util.puts(" --version print version of this command and exit");
util.puts(""); util.puts("");
util.puts(" --color Colorize output. Defaults to try if output");
util.puts(" stream is a TTY.");
util.puts(" -o, --output MODE"); util.puts(" -o, --output MODE");
util.puts(" Specify an output mode/format. One of"); util.puts(" Specify an output mode/format. One of");
util.puts(" paul: (the default) pretty") util.puts(" paul: (the default) pretty")
@ -160,7 +165,7 @@ function parseArgv(argv) {
var parsed = { var parsed = {
args: [], args: [],
help: false, help: false,
quiet: false, color: process.stdout.isTTY,
outputMode: OM_PAUL, outputMode: OM_PAUL,
jsonIndent: 2 jsonIndent: 2
}; };
@ -202,9 +207,8 @@ function parseArgv(argv) {
case "--version": case "--version":
parsed.version = true; parsed.version = true;
break; break;
case "-q": case "--color":
case "--quiet": parsed.color = true;
parsed.quiet = true;
break; break;
case "-o": case "-o":
case "--output": case "--output":
@ -244,10 +248,45 @@ function isInteger(s) {
} }
// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
// Suggested colors (some are unreadable in common cases):
// - Good: cyan, yellow (limited use), grey, bold, green, magenta, red
// - Bad: blue (not visible on cmd.exe)
var colors = {
'bold' : [1, 22],
'italic' : [3, 23],
'underline' : [4, 24],
'inverse' : [7, 27],
'white' : [37, 39],
'grey' : [90, 39],
'black' : [30, 39],
'blue' : [34, 39],
'cyan' : [36, 39],
'green' : [32, 39],
'magenta' : [35, 39],
'red' : [31, 39],
'yellow' : [33, 39]
};
function stylizeWithColor(str, color) {
var codes = colors[color];
if (codes) {
return '\033[' + codes[0] + 'm' + str +
'\033[' + codes[1] + 'm';
} else {
return str;
}
}
function stylizeWithoutColor(str, color) {
return str;
}
/** /**
* Print out a single result, considering input options. * Print out a single result, considering input options.
*/ */
function handleLogLine(line, opts) { function handleLogLine(line, opts, stylize) {
// Handle non-JSON lines. // Handle non-JSON lines.
var rec; var rec;
if (!line) { if (!line) {
@ -275,7 +314,7 @@ function handleLogLine(line, opts) {
// If 'err' and 'err.stack' then show that. // If 'err' and 'err.stack' then show that.
delete rec.v; delete rec.v;
var time = rec.time; var time = stylize('[' + rec.time + ']', 'XXX');
delete rec.time; delete rec.time;
var nameStr = rec.name; var nameStr = rec.name;
@ -289,7 +328,18 @@ function handleLogLine(line, opts) {
nameStr += '/' + rec.pid; nameStr += '/' + rec.pid;
delete rec.pid; delete rec.pid;
var level = (upperNameFromLevel[rec.level] || "<level " + rec.level + ">"); var level = (upperPaddedNameFromLevel[rec.level] || "LVL" + rec.level);
if (opts.color) {
var colorFromLevel = {
10: 'grey', // TRACE
20: 'grey', // DEBUG
30: 'cyan', // INFO
40: 'magenta', // WARN
50: 'red', // ERROR
60: 'inverse', // FATAL
}
level = stylize(level, colorFromLevel[rec.level])
}
delete rec.level; delete rec.level;
var src = ""; var src = "";
@ -319,10 +369,12 @@ function handleLogLine(line, opts) {
} }
delete rec.latency; delete rec.latency;
onelineMsg = ' ' + rec.msg; var onelineMsg;
if (rec.msg.indexOf('\n') !== -1) { if (rec.msg.indexOf('\n') !== -1) {
onelineMsg = ''; onelineMsg = '';
details.push(indent(rec.msg)); details.push(indent(stylize(rec.msg, 'cyan')));
} else {
onelineMsg = ' ' + stylize(rec.msg, 'cyan');
} }
delete rec.msg; delete rec.msg;
@ -396,9 +448,11 @@ function handleLogLine(line, opts) {
} }
} }
extras = (extras.length ? ' (' + extras.join(', ') + ')' : ''); extras = stylize(
details = (details.length ? details.join('\n --\n') + '\n' : ''); (extras.length ? ' (' + extras.join(', ') + ')' : ''), 'grey');
emit(format("[%s] %s: %s on %s%s:%s%s\n%s", details = stylize(
(details.length ? details.join('\n --\n') + '\n' : ''), 'grey');
emit(format("%s %s: %s on %s%s:%s%s\n%s",
time, time,
level, level,
nameStr, nameStr,
@ -419,8 +473,7 @@ function handleLogLine(line, opts) {
case OM_SIMPLE: case OM_SIMPLE:
// <http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/SimpleLayout.html> // <http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/SimpleLayout.html>
emit(format("%s - %s", emit(format("%s - %s", upperNameFromLevel[rec.level] || "LVL" + rec.level,
upperNameFromLevel[rec.level] || "???",
rec.msg)); rec.msg));
break; break;
default: default:
@ -490,6 +543,7 @@ function main(argv) {
util.puts("bunyan " + getVersion()); util.puts("bunyan " + getVersion());
return; return;
} }
var stylize = (opts.color ? stylizeWithColor : stylizeWithoutColor);
var leftover = ""; // Left-over partial line from last chunk. var leftover = ""; // Left-over partial line from last chunk.
var stdin = process.openStdin(); var stdin = process.openStdin();
@ -503,18 +557,18 @@ function main(argv) {
} }
if (length > 1) { if (length > 1) {
handleLogLine(leftover + lines[0], opts); handleLogLine(leftover + lines[0], opts, stylize);
} }
leftover = lines.pop(); leftover = lines.pop();
length -= 1; length -= 1;
for (var i=1; i < length; i++) { for (var i=1; i < length; i++) {
handleLogLine(lines[i], opts); handleLogLine(lines[i], opts, stylize);
} }
}); });
stdin.on('end', function () { stdin.on('end', function () {
if (leftover) { if (leftover) {
handleLogLine(leftover, opts); handleLogLine(leftover, opts, stylize);
leftover = ''; leftover = '';
} }
}); });