Name: js-handler/node_modules/restify/node_modules/bunyan/bin/bunyan 
1:
#!/usr/bin/env node
2:
// -*- mode: js -*-
3:
//
4:
// bunyan -- filter and pretty-print JSON logs, like Bunyan logs.
5:
//
6:
// See <https://github.com/trentm/node-bunyan>.
7:
//
8:
 
9:
var VERSION = '0.21.1';
10:
 
11:
var util = require('util');
12:
var pathlib = require('path');
13:
var vm = require('vm');
14:
var http = require('http');
15:
var fs = require('fs');
16:
var warn = console.warn;
17:
var child_process = require('child_process'),
18:
    spawn = child_process.spawn,
19:
    exec = child_process.exec,
20:
    execFile = child_process.execFile;
21:
var assert = require('assert');
22:
 
23:
var nodeSpawnSupportsStdio = (
24:
    Number(process.version.split('.')[0]) >= 0 ||
25:
    Number(process.version.split('.')[1]) >= 8);
26:
 
27:
 
28:
 
29:
//---- globals and constants
30:
 
31:
// Internal debug logging via `console.warn`.
32:
var _DEBUG = false;
33:
 
34:
// Output modes.
35:
var OM_LONG = 1;
36:
var OM_JSON = 2;
37:
var OM_INSPECT = 3;
38:
var OM_SIMPLE = 4;
39:
var OM_SHORT = 5;
40:
var OM_BUNYAN = 6;
41:
var OM_FROM_NAME = {
42:
    'long': OM_LONG,
43:
    'paul': OM_LONG,  /* backward compat */
44:
    'json': OM_JSON,
45:
    'inspect': OM_INSPECT,
46:
    'simple': OM_SIMPLE,
47:
    'short': OM_SHORT,
48:
    'bunyan': OM_BUNYAN
49:
};
50:
 
51:
 
52:
// Levels
53:
var TRACE = 10;
54:
var DEBUG = 20;
55:
var INFO = 30;
56:
var WARN = 40;
57:
var ERROR = 50;
58:
var FATAL = 60;
59:
 
60:
var levelFromName = {
61:
    'trace': TRACE,
62:
    'debug': DEBUG,
63:
    'info': INFO,
64:
    'warn': WARN,
65:
    'error': ERROR,
66:
    'fatal': FATAL
67:
};
68:
var nameFromLevel = {};
69:
var upperNameFromLevel = {};
70:
var upperPaddedNameFromLevel = {};
71:
Object.keys(levelFromName).forEach(function (name) {
72:
    var lvl = levelFromName[name];
73:
    nameFromLevel[lvl] = name;
74:
    upperNameFromLevel[lvl] = name.toUpperCase();
75:
    upperPaddedNameFromLevel[lvl] = (
76:
        name.length === 4 ? ' ' : '') + name.toUpperCase();
77:
});
78:
 
79:
 
80:
// The current raw input line being processed. Used for `uncaughtException`.
81:
var currLine = null;
82:
 
83:
// Child dtrace process, if any. Used for signal-handling.
84:
var child = null;
85:
 
86:
// Whether ANSI codes are being used. Used for signal-handling.
87:
var usingAnsiCodes = false;
88:
 
89:
// Global ref to options used only by 'uncaughtException' handler.
90:
var gOptsForUncaughtException;
91:
 
92:
// Pager child process, and output stream to which to write.
93:
var pager = null;
94:
var stdout = process.stdout;
95:
 
96:
 
97:
 
98:
//---- support functions
99:
 
100:
function getVersion() {
101:
    return VERSION;
102:
}
103:
 
104:
 
105:
var format = util.format;
106:
if (!format) {
107:
    /* BEGIN JSSTYLED */
108:
    // If not node 0.6, then use its `util.format`:
109:
    // <https://github.com/joyent/node/blob/master/lib/util.js#L22>:
110:
    var inspect = util.inspect;
111:
    var formatRegExp = /%[sdj%]/g;
112:
    format = function format(f) {
113:
        if (typeof f !== 'string') {
114:
            var objects = [];
115:
            for (var i = 0; i < arguments.length; i++) {
116:
                objects.push(inspect(arguments[i]));
117:
            }
118:
            return objects.join(' ');
119:
        }
120:
 
121:
        var i = 1;
122:
        var args = arguments;
123:
        var len = args.length;
124:
        var str = String(f).replace(formatRegExp, function (x) {
125:
            if (i >= len)
126:
                return x;
127:
            switch (x) {
128:
                case '%s': return String(args[i++]);
129:
                case '%d': return Number(args[i++]);
130:
                case '%j': return JSON.stringify(args[i++]);
131:
                case '%%': return '%';
132:
                default:
133:
                    return x;
134:
            }
135:
        });
136:
        for (var x = args[i]; i < len; x = args[++i]) {
137:
            if (x === null || typeof x !== 'object') {
138:
                str += ' ' + x;
139:
            } else {
140:
                str += ' ' + inspect(x);
141:
            }
142:
        }
143:
        return str;
144:
    };
145:
    /* END JSSTYLED */
146:
}
147:
 
148:
function indent(s) {
149:
    return '    ' + s.split(/\r?\n/).join('\n    ');
150:
}
151:
 
152:
function objCopy(obj) {
153:
    if (obj === null) {
154:
        return null;
155:
    } else if (Array.isArray(obj)) {
156:
        return obj.slice();
157:
    } else {
158:
        var copy = {};
159:
        Object.keys(obj).forEach(function (k) {
160:
            copy[k] = obj[k];
161:
        });
162:
        return copy;
163:
    }
164:
}
165:
 
166:
function printHelp() {
167:
    /* BEGIN JSSTYLED */
168:
    var p = console.log;
169:
    p('Usage:');
170:
    p('  bunyan [OPTIONS] [FILE ...]');
171:
    p('  ... | bunyan [OPTIONS]');
172:
    p('  bunyan [OPTIONS] -p PID');
173:
    p('');
174:
    p('Filter and pretty-print Bunyan log file content.');
175:
    p('');
176:
    p('General options:');
177:
    p('  -h, --help    print this help info and exit');
178:
    p('  --version     print version of this command and exit');
179:
    p('');
180:
    p('Dtrace options (only on dtrace-supporting platforms):');
181:
    p('  -p PID        Process bunyan:log-* probes from the process');
182:
    p('                with the given PID. Can be used multiple times,');
183:
    p('                or specify all processes with "*", or a set of');
184:
    p('                processes whose command & args match a pattern');
185:
    p('                with "-p NAME".');
186:
    p('');
187:
    p('Filtering options:');
188:
    p('  -l, --level LEVEL');
189:
    p('                Only show messages at or above the specified level.');
190:
    p('                You can specify level *names* or numeric values.');
191:
    p('                (See "Log Levels" below.)');
192:
    p('  -c, --condition CONDITION');
193:
    p('                Run each log message through the condition and');
194:
    p('                only show those that return truish. E.g.:');
195:
    p('                    -c \'this.pid == 123\'');
196:
    p('                    -c \'this.level == DEBUG\'');
197:
    p('                    -c \'this.msg.indexOf("boom") != -1\'');
198:
    p('                "CONDITION" must be legal JS code. `this` holds');
199:
    p('                the log record. The TRACE, DEBUG, ... FATAL values');
200:
    p('                are defined to help with comparing `this.level`.');
201:
    p('  --strict      Suppress all but legal Bunyan JSON log lines. By default');
202:
    p('                non-JSON, and non-Bunyan lines are passed through.');
203:
    p('');
204:
    p('Output options:');
205:
    p('  --pager       Pipe output into `less` (or $PAGER if set), if');
206:
    p('                stdout is a TTY. This overrides $BUNYAN_NO_PAGER.');
207:
    p('                Note: Paging is only supported on node >=0.8.');
208:
    p('  --no-pager    Do not pipe output into a pager.');
209:
    p('  --color       Colorize output. Defaults to try if output');
210:
    p('                stream is a TTY.');
211:
    p('  --no-color    Force no coloring (e.g. terminal doesn\'t support it)');
212:
    p('  -o, --output MODE');
213:
    p('                Specify an output mode/format. One of');
214:
    p('                  long: (the default) pretty');
215:
    p('                  json: JSON output, 2-space indent');
216:
    p('                  json-N: JSON output, N-space indent, e.g. "json-4"');
217:
    p('                  bunyan: 0 indented JSON, bunyan\'s native format');
218:
    p('                  inspect: node.js `util.inspect` output');
219:
    p('                  short: like "long", but more concise');
220:
    p('  -j            shortcut for `-o json`');
221:
    p('');
222:
    p('Log Levels:');
223:
    p('  Either numeric values or their associated strings are valid for the');
224:
    p('  -l|--level argument.  However, -c|--condition scripts will see a numeric');
225:
    p('  "level" value, not a string.');
226:
    p('');
227:
    Object.keys(levelFromName).forEach(function (name) {
228:
        var n = name;
229:
        while (n.length < 6)
230:
            n += ' ';
231:
        p('    %s %d', n, levelFromName[name]);
232:
    });
233:
    p('');
234:
    p('Environment Variables:');
235:
    p('  BUNYAN_NO_COLOR    Set to a non-empty value to force no output ');
236:
    p('                     coloring. See "--no-color".');
237:
    p('  BUNYAN_NO_PAGER    Disable piping output to a pager. ');
238:
    p('                     See "--no-pager".');
239:
    p('');
240:
    p('See <https://github.com/trentm/node-bunyan> for more complete docs.');
241:
    p('Please report bugs to <https://github.com/trentm/node-bunyan/issues>.');
242:
    /* END JSSTYLED */
243:
}
244:
 
245:
/*
246:
 * If the user specifies multiple input sources, we want to print out records
247:
 * from all sources in a single, chronologically ordered stream.  To do this
248:
 * efficiently, we first assume that all records within each source are ordered
249:
 * already, so we need only keep track of the next record in each source and
250:
 * the time of the last record emitted.  To avoid excess memory usage, we
251:
 * pause() streams that are ahead of others.
252:
 *
253:
 * 'streams' is an object indexed by source name (file name) which specifies:
254:
 *
255:
 *    stream        Actual stream object, so that we can pause and resume it.
256:
 *
257:
 *    records       Array of log records we've read, but not yet emitted.  Each
258:
 *                  record includes 'line' (the raw line), 'rec' (the JSON
259:
 *                  record), and 'time' (the parsed time value).
260:
 *
261:
 *    done          Whether the stream has any more records to emit.
262:
 */
263:
var streams = {};
264:
 
265:
function gotRecord(file, line, rec, opts, stylize)
266:
{
267:
    var time = new Date(rec.time);
268:
 
269:
    streams[file]['records'].push({ line: line, rec: rec, time: time });
270:
    emitNextRecord(opts, stylize);
271:
}
272:
 
273:
function filterRecord(rec, opts)
274:
{
275:
    if (opts.level && rec.level < opts.level) {
276:
        return false;
277:
    }
278:
 
279:
    if (opts.conditions) {
280:
        for (var i = 0; i < opts.conditions.length; i++) {
281:
            var pass = opts.conditions[i].runInNewContext(rec);
282:
            if (!pass)
283:
                return false;
284:
        }
285:
    }
286:
 
287:
    return true;
288:
}
289:
 
290:
function emitNextRecord(opts, stylize)
291:
{
292:
    var ofile, ready, minfile, rec;
293:
 
294:
    for (;;) {
295:
        /*
296:
         * Take a first pass through the input streams to see if we have a
297:
         * record from all of them.  If not, we'll pause any streams for
298:
         * which we do already have a record (to avoid consuming excess
299:
         * memory) and then wait until we have records from the others
300:
         * before emitting the next record.
301:
         *
302:
         * As part of the same pass, we look for the earliest record
303:
         * we have not yet emitted.
304:
         */
305:
        minfile = undefined;
306:
        ready = true;
307:
        for (ofile in streams) {
308:
 
309:
            if (streams[ofile].stream === null ||
310:
                (!streams[ofile].done && streams[ofile].records.length === 0)) {
311:
                ready = false;
312:
                break;
313:
            }
314:
 
315:
            if (streams[ofile].records.length > 0 &&
316:
                (minfile === undefined ||
317:
                    streams[minfile].records[0].time >
318:
                        streams[ofile].records[0].time)) {
319:
                minfile = ofile;
320:
            }
321:
        }
322:
 
323:
        if (!ready || minfile === undefined) {
324:
            for (ofile in streams) {
325:
                if (!streams[ofile].stream || streams[ofile].done)
326:
                    continue;
327:
 
328:
                if (streams[ofile].records.length > 0) {
329:
                    if (!streams[ofile].paused) {
330:
                        streams[ofile].paused = true;
331:
                        streams[ofile].stream.pause();
332:
                    }
333:
                } else if (streams[ofile].paused) {
334:
                    streams[ofile].paused = false;
335:
                    streams[ofile].stream.resume();
336:
                }
337:
            }
338:
 
339:
            return;
340:
        }
341:
 
342:
        /*
343:
         * Emit the next record for 'minfile', and invoke ourselves again to
344:
         * make sure we emit as many records as we can right now.
345:
         */
346:
        rec = streams[minfile].records.shift();
347:
        emitRecord(rec.rec, rec.line, opts, stylize);
348:
    }
349:
}
350:
 
351:
/**
352:
 * Parse the command-line options and arguments into an object.
353:
 *
354:
 *    {
355:
 *      'args': [...]       // arguments
356:
 *      'help': true,       // true if '-h' option given
357:
 *       // etc.
358:
 *    }
359:
 *
360:
 * @return {Object} The parsed options. `.args` is the argument list.
361:
 * @throws {Error} If there is an error parsing argv.
362:
 */
363:
function parseArgv(argv) {
364:
    var parsed = {
365:
        args: [],
366:
        help: false,
367:
        color: null,
368:
        paginate: null,
369:
        outputMode: OM_LONG,
370:
        jsonIndent: 2,
371:
        level: null,
372:
        conditions: null,
373:
        strict: false,
374:
        pids: null,
375:
        pidsType: null
376:
    };
377:
 
378:
    // Turn '-iH' into '-i -H', except for argument-accepting options.
379:
    var args = argv.slice(2);  // drop ['node', 'scriptname']
380:
    var newArgs = [];
381:
    var optTakesArg = {'d': true, 'o': true, 'c': true, 'l': true, 'p': true};
382:
    for (var i = 0; i < args.length; i++) {
383:
        if (args[i].charAt(0) === '-' && args[i].charAt(1) !== '-' &&
384:
            args[i].length > 2)
385:
        {
386:
            var splitOpts = args[i].slice(1).split('');
387:
            for (var j = 0; j < splitOpts.length; j++) {
388:
                newArgs.push('-' + splitOpts[j]);
389:
                if (optTakesArg[splitOpts[j]]) {
390:
                    var optArg = splitOpts.slice(j+1).join('');
391:
                    if (optArg.length) {
392:
                        newArgs.push(optArg);
393:
                    }
394:
                    break;
395:
                }
396:
            }
397:
        } else {
398:
            newArgs.push(args[i]);
399:
        }
400:
    }
401:
    args = newArgs;
402:
 
403:
    var condDefines = [];
404:
    Object.keys(upperNameFromLevel).forEach(function (lvl) {
405:
        condDefines.push(
406:
            format('Object.prototype.%s = %s;', upperNameFromLevel[lvl], lvl));
407:
    });
408:
    condDefines = condDefines.join('\n') + '\n';
409:
 
410:
    var endOfOptions = false;
411:
    while (args.length > 0) {
412:
        var arg = args.shift();
413:
        switch (arg) {
414:
            case '--':
415:
                endOfOptions = true;
416:
                break;
417:
            case '-h': // display help and exit
418:
            case '--help':
419:
                parsed.help = true;
420:
                break;
421:
            case '--version':
422:
                parsed.version = true;
423:
                break;
424:
            case '--strict':
425:
                parsed.strict = true;
426:
                break;
427:
            case '--color':
428:
                parsed.color = true;
429:
                break;
430:
            case '--no-color':
431:
                parsed.color = false;
432:
                break;
433:
            case '--pager':
434:
                parsed.paginate = true;
435:
                break;
436:
            case '--no-pager':
437:
                parsed.paginate = false;
438:
                break;
439:
            case '-o':
440:
            case '--output':
441:
                var name = args.shift();
442:
                var idx = name.lastIndexOf('-');
443:
                if (idx !== -1) {
444:
                    var indentation = Number(name.slice(idx+1));
445:
                    if (! isNaN(indentation)) {
446:
                        parsed.jsonIndent = indentation;
447:
                        name = name.slice(0, idx);
448:
                    }
449:
                }
450:
                parsed.outputMode = OM_FROM_NAME[name];
451:
                if (parsed.outputMode === undefined) {
452:
                    throw new Error('unknown output mode: "'+name+'"');
453:
                }
454:
                break;
455:
            case '-j': // output with JSON.stringify
456:
                parsed.outputMode = OM_JSON;
457:
                break;
458:
            case '-p':
459:
                if (!parsed.pids) {
460:
                    parsed.pids = [];
461:
                }
462:
                var pidArg = args.shift();
463:
                var pid = +(pidArg);
464:
                if (!isNaN(pid) || pidArg === '*') {
465:
                    if (parsed.pidsType && parsed.pidsType !== 'num') {
466:
                        throw new Error(format('cannot mix PID name and '
467:
                            + 'number arguments: "%s"', pidArg));
468:
                    }
469:
                    parsed.pidsType = 'num';
470:
                    if (!parsed.pids) {
471:
                        parsed.pids = [];
472:
                    }
473:
                    parsed.pids.push(isNaN(pid) ? pidArg : pid);
474:
                } else {
475:
                    if (parsed.pidsType && parsed.pidsType !== 'name') {
476:
                        throw new Error(format('cannot mix PID name and '
477:
                            + 'number arguments: "%s"', pidArg));
478:
                    }
479:
                    parsed.pidsType = 'name';
480:
                    parsed.pids = pidArg;
481:
                }
482:
                break;
483:
            case '-l':
484:
            case '--level':
485:
                var levelArg = args.shift();
486:
                var level = +(levelArg);
487:
                if (isNaN(level)) {
488:
                    level = +levelFromName[levelArg.toLowerCase()];
489:
                }
490:
                if (isNaN(level)) {
491:
                    throw new Error('unknown level value: "'+levelArg+'"');
492:
                }
493:
                parsed.level = level;
494:
                break;
495:
            case '-c':
496:
            case '--condition':
497:
                var condition = args.shift();
498:
                parsed.conditions = parsed.conditions || [];
499:
                var scriptName = 'bunyan-condition-'+parsed.conditions.length;
500:
                var code = condDefines + condition;
501:
                try {
502:
                    var script = vm.createScript(code, scriptName);
503:
                } catch (compileErr) {
504:
                    throw new Error(format('illegal CONDITION code: %s\n'
505:
                        + '  CONDITION script:\n'
506:
                        + '%s\n'
507:
                        + '  Error:\n'
508:
                        + '%s',
509:
                        compileErr, indent(code), indent(compileErr.stack)));
510:
                }
511:
 
512:
                // Ensure this is a reasonably safe CONDITION.
513:
                try {
514:
                    script.runInNewContext(minValidRecord);
515:
                } catch (condErr) {
516:
                    throw new Error(format(
517:
                        /* JSSTYLED */
518:
                        'CONDITION code cannot safely filter a minimal Bunyan log record\n'
519:
                        + '  CONDITION script:\n'
520:
                        + '%s\n'
521:
                        + '  Minimal Bunyan log record:\n'
522:
                        + '%s\n'
523:
                        + '  Filter error:\n'
524:
                        + '%s',
525:
                        indent(code),
526:
                        indent(JSON.stringify(minValidRecord, null, 2)),
527:
                        indent(condErr.stack)
528:
                        ));
529:
                }
530:
 
531:
                parsed.conditions.push(script);
532:
                break;
533:
            default: // arguments
534:
                if (!endOfOptions && arg.length > 0 && arg[0] === '-') {
535:
                    throw new Error('unknown option "'+arg+'"');
536:
                }
537:
                parsed.args.push(arg);
538:
                break;
539:
        }
540:
    }
541:
    //TODO: '--' handling and error on a first arg that looks like an option.
542:
 
543:
    return parsed;
544:
}
545:
 
546:
 
547:
function isInteger(s) {
548:
    return (s.search(/^-?[0-9]+$/) == 0);
549:
}
550:
 
551:
 
552:
// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
553:
// Suggested colors (some are unreadable in common cases):
554:
// - Good: cyan, yellow (limited use), grey, bold, green, magenta, red
555:
// - Bad: blue (not visible on cmd.exe)
556:
var colors = {
557:
    'bold' : [1, 22],
558:
    'italic' : [3, 23],
559:
    'underline' : [4, 24],
560:
    'inverse' : [7, 27],
561:
    'white' : [37, 39],
562:
    'grey' : [90, 39],
563:
    'black' : [30, 39],
564:
    'blue' : [34, 39],
565:
    'cyan' : [36, 39],
566:
    'green' : [32, 39],
567:
    'magenta' : [35, 39],
568:
    'red' : [31, 39],
569:
    'yellow' : [33, 39]
570:
};
571:
 
572:
function stylizeWithColor(str, color) {
573:
    if (!str)
574:
        return '';
575:
    var codes = colors[color];
576:
    if (codes) {
577:
        return '\033[' + codes[0] + 'm' + str +
578:
                     '\033[' + codes[1] + 'm';
579:
    } else {
580:
        return str;
581:
    }
582:
}
583:
 
584:
function stylizeWithoutColor(str, color) {
585:
    return str;
586:
}
587:
 
588:
 
589:
/**
590:
 * Is this a valid Bunyan log record.
591:
 */
592:
function isValidRecord(rec) {
593:
    if (rec.v == null ||
594:
            rec.level == null ||
595:
            rec.name == null ||
596:
            rec.hostname == null ||
597:
            rec.pid == null ||
598:
            rec.time == null ||
599:
            rec.msg == null) {
600:
        // Not valid Bunyan log.
601:
        return false;
602:
    } else {
603:
        return true;
604:
    }
605:
}
606:
var minValidRecord = {
607:
    v: 0,   //TODO: get this from bunyan.LOG_VERSION
608:
    level: INFO,
609:
    name: 'name',
610:
    hostname: 'hostname',
611:
    pid: 123,
612:
    time: Date.now(),
613:
    msg: 'msg'
614:
};
615:
 
616:
 
617:
/**
618:
 * Parses the given log line and either emits it right away (for invalid
619:
 * records) or enqueues it for emitting later when it's the next line to show.
620:
 */
621:
function handleLogLine(file, line, opts, stylize) {
622:
    currLine = line; // intentionally global
623:
 
624:
    // Emit non-JSON lines immediately.
625:
    var rec;
626:
    if (!line) {
627:
        if (!opts.strict) emit(line + '\n');
628:
        return;
629:
    } else if (line[0] !== '{') {
630:
        if (!opts.strict) emit(line + '\n');  // not JSON
631:
        return;
632:
    } else {
633:
        try {
634:
            rec = JSON.parse(line);
635:
        } catch (e) {
636:
            if (!opts.strict) emit(line + '\n');
637:
            return;
638:
        }
639:
    }
640:
 
641:
    if (!isValidRecord(rec)) {
642:
        if (!opts.strict) emit(line + '\n');
643:
        return;
644:
    }
645:
 
646:
    if (!filterRecord(rec, opts))
647:
        return;
648:
 
649:
    if (file === null)
650:
        return emitRecord(rec, line, opts, stylize);
651:
 
652:
    return gotRecord(file, line, rec, opts, stylize);
653:
}
654:
 
655:
/**
656:
 * Print out a single result, considering input options.
657:
 */
658:
function emitRecord(rec, line, opts, stylize) {
659:
    var short = false;
660:
 
661:
    switch (opts.outputMode) {
662:
    case OM_SHORT:
663:
        short = true;
664:
        /* jsl:fall-thru */
665:
 
666:
    case OM_LONG:
667:
        //    [time] LEVEL: name[/comp]/pid on hostname (src): msg* (extras...)
668:
        //        msg*
669:
        //        --
670:
        //        long and multi-line extras
671:
        //        ...
672:
        // If 'msg' is single-line, then it goes in the top line.
673:
        // If 'req', show the request.
674:
        // If 'res', show the response.
675:
        // If 'err' and 'err.stack' then show that.
676:
        if (!isValidRecord(rec)) {
677:
            return emit(line + '\n');
678:
        }
679:
 
680:
        delete rec.v;
681:
 
682:
        /*
683:
         * We assume the Date is formatted according to ISO8601, in which
684:
         * case we can safely chop off the date information.
685:
         */
686:
        if (short && rec.time[10] == 'T') {
687:
            var time = rec.time.substr(11);
688:
            time = stylize(time, 'XXX');
689:
        } else {
690:
            var time = stylize('[' + rec.time + ']', 'XXX');
691:
        }
692:
 
693:
        delete rec.time;
694:
 
695:
        var nameStr = rec.name;
696:
        delete rec.name;
697:
 
698:
        if (rec.component) {
699:
            nameStr += '/' + rec.component;
700:
        }
701:
        delete rec.component;
702:
 
703:
        if (!short)
704:
            nameStr += '/' + rec.pid;
705:
        delete rec.pid;
706:
 
707:
        var level = (upperPaddedNameFromLevel[rec.level] || 'LVL' + rec.level);
708:
        if (opts.color) {
709:
            var colorFromLevel = {
710:
                10: 'grey',     // TRACE
711:
                20: 'grey',     // DEBUG
712:
                30: 'cyan',     // INFO
713:
                40: 'magenta',  // WARN
714:
                50: 'red',      // ERROR
715:
                60: 'inverse',  // FATAL
716:
            };
717:
            level = stylize(level, colorFromLevel[rec.level]);
718:
        }
719:
        delete rec.level;
720:
 
721:
        var src = '';
722:
        if (rec.src && rec.src.file) {
723:
            var s = rec.src;
724:
            if (s.func) {
725:
                src = format(' (%s:%d in %s)', s.file, s.line, s.func);
726:
            } else {
727:
                src = format(' (%s:%d)', s.file, s.line);
728:
            }
729:
            src = stylize(src, 'green');
730:
        }
731:
        delete rec.src;
732:
 
733:
        var hostname = rec.hostname;
734:
        delete rec.hostname;
735:
 
736:
        var extras = [];
737:
        var details = [];
738:
 
739:
        if (rec.req_id) {
740:
            extras.push('req_id=' + rec.req_id);
741:
        }
742:
        delete rec.req_id;
743:
 
744:
        var onelineMsg;
745:
        if (rec.msg.indexOf('\n') !== -1) {
746:
            onelineMsg = '';
747:
            details.push(indent(stylize(rec.msg, 'cyan')));
748:
        } else {
749:
            onelineMsg = ' ' + stylize(rec.msg, 'cyan');
750:
        }
751:
        delete rec.msg;
752:
 
753:
        if (rec.req && typeof (rec.req) === 'object') {
754:
            var req = rec.req;
755:
            delete rec.req;
756:
            var headers = req.headers;
757:
            var s = format('%s %s HTTP/%s%s', req.method,
758:
                req.url,
759:
                req.httpVersion || '1.1',
760:
                (headers ?
761:
                    '\n' + Object.keys(headers).map(function (h) {
762:
                        return h + ': ' + headers[h];
763:
                    }).join('\n') :
764:
                    '')
765:
            );
766:
            delete req.url;
767:
            delete req.method;
768:
            delete req.httpVersion;
769:
            delete req.headers;
770:
            if (req.body) {
771:
                s += '\n\n' + (typeof (req.body) === 'object'
772:
                    ? JSON.stringify(req.body, null, 2) : req.body);
773:
                delete req.body;
774:
            }
775:
            if (req.trailers && Object.keys(req.trailers) > 0) {
776:
                s += '\n' + Object.keys(req.trailers).map(function (t) {
777:
                    return t + ': ' + req.trailers[t];
778:
                }).join('\n');
779:
            }
780:
            delete req.trailers;
781:
            details.push(indent(s));
782:
            // E.g. for extra 'foo' field on 'req', add 'req.foo' at
783:
            // top-level. This *does* have the potential to stomp on a
784:
            // literal 'req.foo' key.
785:
            Object.keys(req).forEach(function (k) {
786:
                rec['req.' + k] = req[k];
787:
            })
788:
        }
789:
 
790:
        if (rec.client_req && typeof (rec.client_req) === 'object') {
791:
            var client_req = rec.client_req;
792:
            delete rec.client_req;
793:
            var headers = client_req.headers;
794:
            var hostHeaderLine = '';
795:
            var s = '';
796:
            if (client_req.address) {
797:
                hostHeaderLine = 'Host: ' + client_req.address;
798:
                if (client_req.port)
799:
                    hostHeaderLine += ':' + client_req.port;
800:
                hostHeaderLine += '\n';
801:
            }
802:
            delete client_req.headers;
803:
            delete client_req.address;
804:
            delete client_req.port;
805:
            s += format('%s %s HTTP/%s\n%s%s', client_req.method,
806:
                client_req.url,
807:
                client_req.httpVersion || '1.1',
808:
                hostHeaderLine,
809:
                (headers ?
810:
                    Object.keys(headers).map(
811:
                        function (h) {
812:
                            return h + ': ' + headers[h];
813:
                        }).join('\n') :
814:
                    ''));
815:
            delete client_req.method;
816:
            delete client_req.url;
817:
            delete client_req.httpVersion;
818:
            if (client_req.body) {
819:
                s += '\n\n' + (typeof (client_req.body) === 'object' ?
820:
                    JSON.stringify(client_req.body, null, 2) :
821:
                    client_req.body);
822:
                delete client_req.body;
823:
            }
824:
            // E.g. for extra 'foo' field on 'client_req', add
825:
            // 'client_req.foo' at top-level. This *does* have the potential
826:
            // to stomp on a literal 'client_req.foo' key.
827:
            Object.keys(client_req).forEach(function (k) {
828:
                rec['client_req.' + k] = client_req[k];
829:
            })
830:
            details.push(indent(s));
831:
        }
832:
 
833:
        function _res(res) {
834:
            var s = '';
835:
            if (res.header) {
836:
                s += res.header.trimRight();
837:
            } else if (res.headers) {
838:
                if (res.statusCode) {
839:
                    s += format('HTTP/1.1 %s %s\n', res.statusCode,
840:
                        http.STATUS_CODES[res.statusCode]);
841:
                }
842:
                var headers = res.headers;
843:
                s += Object.keys(headers).map(
844:
                    function (h) { return h + ': ' + headers[h]; }).join('\n');
845:
            }
846:
            delete res.header;
847:
            delete res.headers;
848:
            delete res.statusCode;
849:
            if (res.body) {
850:
                s += '\n\n' + (typeof (res.body) === 'object'
851:
                    ? JSON.stringify(res.body, null, 2) : res.body);
852:
                delete res.body;
853:
            }
854:
            if (res.trailer) {
855:
                s += '\n' + res.trailer;
856:
            }
857:
            delete res.trailer;
858:
            if (s) {
859:
                details.push(indent(s));
860:
            }
861:
            // E.g. for extra 'foo' field on 'res', add 'res.foo' at
862:
            // top-level. This *does* have the potential to stomp on a
863:
            // literal 'res.foo' key.
864:
            Object.keys(res).forEach(function (k) {
865:
                rec['res.' + k] = res[k];
866:
            });
867:
        }
868:
 
869:
        if (rec.res && typeof (rec.res) === 'object') {
870:
            _res(rec.res);
871:
            delete rec.res;
872:
        }
873:
        if (rec.client_res && typeof (rec.client_res) === 'object') {
874:
            _res(rec.client_res);
875:
            delete rec.res;
876:
        }
877:
 
878:
        if (rec.err && rec.err.stack) {
879:
            details.push(indent(rec.err.stack));
880:
            delete rec.err;
881:
        }
882:
 
883:
        var leftover = Object.keys(rec);
884:
        for (var i = 0; i < leftover.length; i++) {
885:
            var key = leftover[i];
886:
            var value = rec[key];
887:
            var stringified = false;
888:
            if (typeof (value) !== 'string') {
889:
                value = JSON.stringify(value, null, 2);
890:
                stringified = true;
891:
            }
892:
            if (value.indexOf('\n') !== -1 || value.length > 50) {
893:
                details.push(indent(key + ': ' + value));
894:
            } else if (!stringified && (value.indexOf(' ') != -1 ||
895:
                value.length === 0))
896:
            {
897:
                extras.push(key + '=' + JSON.stringify(value));
898:
            } else {
899:
                extras.push(key + '=' + value);
900:
            }
901:
        }
902:
 
903:
        extras = stylize(
904:
            (extras.length ? ' (' + extras.join(', ') + ')' : ''), 'grey');
905:
        details = stylize(
906:
            (details.length ? details.join('\n    --\n') + '\n' : ''), 'grey');
907:
        if (!short)
908:
            emit(format('%s %s: %s on %s%s:%s%s\n%s',
909:
                time,
910:
                level,
911:
                nameStr,
912:
                hostname || '<no-hostname>',
913:
                src,
914:
                onelineMsg,
915:
                extras,
916:
                details));
917:
        else
918:
            emit(format('%s %s %s:%s%s\n%s',
919:
                time,
920:
                level,
921:
                nameStr,
922:
                onelineMsg,
923:
                extras,
924:
                details));
925:
        break;
926:
 
927:
    case OM_INSPECT:
928:
        emit(util.inspect(rec, false, Infinity, true) + '\n');
929:
        break;
930:
 
931:
    case OM_BUNYAN:
932:
        emit(JSON.stringify(rec, null, 0) + '\n');
933:
        break;
934:
 
935:
    case OM_JSON:
936:
        emit(JSON.stringify(rec, null, opts.jsonIndent) + '\n');
937:
        break;
938:
 
939:
    case OM_SIMPLE:
940:
        /* JSSTYLED */
941:
        // <http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/SimpleLayout.html>
942:
        if (!isValidRecord(rec)) {
943:
            return emit(line + '\n');
944:
        }
945:
        emit(format('%s - %s\n',
946:
            upperNameFromLevel[rec.level] || 'LVL' + rec.level,
947:
            rec.msg));
948:
        break;
949:
    default:
950:
        throw new Error('unknown output mode: '+opts.outputMode);
951:
    }
952:
}
953:
 
954:
 
955:
var stdoutFlushed = true;
956:
function emit(s) {
957:
    try {
958:
        stdoutFlushed = stdout.write(s);
959:
    } catch (e) {
960:
        // Handle any exceptions in stdout writing in `stdout.on('error', ...)`.
961:
    }
962:
}
963:
 
964:
 
965:
/**
966:
 * A hacked up version of 'process.exit' that will first drain stdout
967:
 * before exiting. *WARNING: This doesn't stop event processing.* IOW,
968:
 * callers have to be careful that code following this call isn't
969:
 * accidentally executed.
970:
 *
971:
 * In node v0.6 "process.stdout and process.stderr are blocking when they
972:
 * refer to regular files or TTY file descriptors." However, this hack might
973:
 * still be necessary in a shell pipeline.
974:
 */
975:
function drainStdoutAndExit(code) {
976:
    if (_DEBUG) warn('(drainStdoutAndExit(%d))', code);
977:
    stdout.on('drain', function () {
978:
        cleanupAndExit(code);
979:
    });
980:
    if (stdoutFlushed) {
981:
        cleanupAndExit(code);
982:
    }
983:
}
984:
 
985:
 
986:
/**
987:
 * Process all input from stdin.
988:
 *
989:
 * @params opts {Object} Bunyan options object.
990:
 * @param stylize {Function} Output stylize function to use.
991:
 * @param callback {Function} `function ()`
992:
 */
993:
function processStdin(opts, stylize, callback) {
994:
    var leftover = '';  // Left-over partial line from last chunk.
995:
    var stdin = process.stdin;
996:
    stdin.resume();
997:
    stdin.setEncoding('utf8');
998:
    stdin.on('data', function (chunk) {
999:
        var lines = chunk.split(/\r\n|\n/);
1000:
        var length = lines.length;
1001:
        if (length === 1) {
1002:
            leftover += lines[0];
1003:
            return;
1004:
        }
1005:
 
1006:
        if (length > 1) {
1007:
            handleLogLine(null, leftover + lines[0], opts, stylize);
1008:
        }
1009:
        leftover = lines.pop();
1010:
        length -= 1;
1011:
        for (var i = 1; i < length; i++) {
1012:
            handleLogLine(null, lines[i], opts, stylize);
1013:
        }
1014:
    });
1015:
    stdin.on('end', function () {
1016:
        if (leftover) {
1017:
            handleLogLine(null, leftover, opts, stylize);
1018:
            leftover = '';
1019:
        }
1020:
        callback();
1021:
    });
1022:
}
1023:
 
1024:
 
1025:
/**
1026:
 * Process bunyan:log-* probes from the given pid.
1027:
 *
1028:
 * @params opts {Object} Bunyan options object.
1029:
 * @param stylize {Function} Output stylize function to use.
1030:
 * @param callback {Function} `function (code)`
1031:
 */
1032:
function processPids(opts, stylize, callback) {
1033:
    var leftover = '';  // Left-over partial line from last chunk.
1034:
 
1035:
    /**
1036:
     * Get the PIDs to dtrace.
1037:
     *
1038:
     * @param cb {Function} `function (errCode, pids)`
1039:
     */
1040:
    function getPids(cb) {
1041:
        if (opts.pidsType === 'num') {
1042:
            return cb(null, opts.pids);
1043:
        }
1044:
        if (process.platform === 'sunos') {
1045:
            execFile('/bin/pgrep', ['-lf', opts.pids],
1046:
                function (pidsErr, stdout, stderr) {
1047:
                    if (pidsErr) {
1048:
                        warn('bunyan: error getting PIDs for "%s": %s\n%s\n%s',
1049:
                            opts.pids, pidsErr.message, stdout, stderr);
1050:
                        return cb(1);
1051:
                    }
1052:
                    var pids = stdout.trim().split('\n')
1053:
                        .map(function (line) {
1054:
                            return line.trim().split(/\s+/)[0]
1055:
                        })
1056:
                        .filter(function (pid) {
1057:
                            return Number(pid) !== process.pid
1058:
                        });
1059:
                    if (pids.length === 0) {
1060:
                        warn('bunyan: error: no matching PIDs found for "%s"',
1061:
                            opts.pids);
1062:
                        return cb(2);
1063:
                    }
1064:
                    cb(null, pids);
1065:
                }
1066:
            );
1067:
        } else {
1068:
            var regex = opts.pids;
1069:
            if (regex && /[a-zA-Z0-9_]/.test(regex[0])) {
1070:
                // 'foo' -> '[f]oo' trick to exclude the 'grep' PID from its
1071:
                // own search.
1072:
                regex = '[' + regex[0] + ']' + regex.slice(1);
1073:
            }
1074:
            exec(format('ps -A -o pid,command | grep \'%s\'', regex),
1075:
                function (pidsErr, stdout, stderr) {
1076:
                    if (pidsErr) {
1077:
                        warn('bunyan: error getting PIDs for "%s": %s\n%s\n%s',
1078:
                            opts.pids, pidsErr.message, stdout, stderr);
1079:
                        return cb(1);
1080:
                    }
1081:
                    var pids = stdout.trim().split('\n')
1082:
                        .map(function (line) {
1083:
                            return line.trim().split(/\s+/)[0];
1084:
                        })
1085:
                        .filter(function (pid) {
1086:
                            return Number(pid) !== process.pid;
1087:
                        });
1088:
                    if (pids.length === 0) {
1089:
                        warn('bunyan: error: no matching PIDs found for "%s"',
1090:
                            opts.pids);
1091:
                        return cb(2);
1092:
                    }
1093:
                    cb(null, pids);
1094:
                }
1095:
            );
1096:
        }
1097:
    }
1098:
 
1099:
    getPids(function (errCode, pids) {
1100:
        if (errCode) {
1101:
            return callback(errCode);
1102:
        }
1103:
 
1104:
        var probes = pids.map(function (pid) {
1105:
            return format('bunyan%s:::log-*', pid);
1106:
        }).join(',');
1107:
        var argv = ['dtrace', '-Z', '-x', 'strsize=4k', '-qn',
1108:
            format('%s{printf("%s", copyinstr(arg0))}', probes)];
1109:
        //console.log('dtrace argv: %s', argv);
1110:
        var dtrace = spawn(argv[0], argv.slice(1),
1111:
            // Share the stderr handle to have error output come
1112:
            // straight through. Only supported in v0.8+.
1113:
            {stdio: ['pipe', 'pipe', process.stderr]});
1114:
        child = dtrace; // intentionally global
1115:
 
1116:
        function finish(code) {
1117:
            if (leftover) {
1118:
                handleLogLine(null, leftover, opts, stylize);
1119:
                leftover = '';
1120:
            }
1121:
            callback(returnCode);
1122:
        }
1123:
 
1124:
        dtrace.stdout.setEncoding('utf8');
1125:
        dtrace.stdout.on('data', function (chunk) {
1126:
            var lines = chunk.split(/\r\n|\n/);
1127:
            var length = lines.length;
1128:
            if (length === 1) {
1129:
                leftover += lines[0];
1130:
                return;
1131:
            }
1132:
            if (length > 1) {
1133:
                handleLogLine(null, leftover + lines[0], opts, stylize);
1134:
            }
1135:
            leftover = lines.pop();
1136:
            length -= 1;
1137:
            for (var i = 1; i < length; i++) {
1138:
                handleLogLine(null, lines[i], opts, stylize);
1139:
            }
1140:
        });
1141:
 
1142:
        if (nodeSpawnSupportsStdio) {
1143:
            dtrace.on('exit', finish);
1144:
        } else {
1145:
            // Fallback (for < v0.8) to pipe the dtrace process' stderr to
1146:
            // this stderr. Wait for all of (1) process 'exit', (2) stderr
1147:
            // 'end', and (2) stdout 'end' before returning to ensure all
1148:
            // stderr is flushed (issue #54).
1149:
            var returnCode = null;
1150:
            var eventsRemaining = 3;
1151:
            function countdownToFinish(code) {
1152:
                returnCode = code;
1153:
                eventsRemaining--;
1154:
                if (eventsRemaining == 0) {
1155:
                    finish(returnCode);
1156:
                }
1157:
            }
1158:
            dtrace.stderr.pipe(process.stderr);
1159:
            dtrace.stderr.on('end', countdownToFinish);
1160:
            dtrace.stderr.on('end', countdownToFinish);
1161:
            dtrace.on('exit', countdownToFinish);
1162:
        }
1163:
    });
1164:
}
1165:
 
1166:
 
1167:
/**
1168:
 * Process all input from the given log file.
1169:
 *
1170:
 * @param file {String} Log file path to process.
1171:
 * @params opts {Object} Bunyan options object.
1172:
 * @param stylize {Function} Output stylize function to use.
1173:
 * @param callback {Function} `function ()`
1174:
 */
1175:
function processFile(file, opts, stylize, callback) {
1176:
    var stream = fs.createReadStream(file);
1177:
    if (/\.gz$/.test(file)) {
1178:
        stream = stream.pipe(require('zlib').createGunzip());
1179:
    }
1180:
    // Manually decode streams - lazy load here as per node/lib/fs.js
1181:
    var decoder = new (require('string_decoder').StringDecoder)('utf8');
1182:
 
1183:
    streams[file].stream = stream;
1184:
 
1185:
    stream.on('error', function (err) {
1186:
        streams[file].done = true;
1187:
        callback(err);
1188:
    });
1189:
 
1190:
    var leftover = '';  // Left-over partial line from last chunk.
1191:
    stream.on('data', function (data) {
1192:
        var chunk = decoder.write(data);
1193:
        if (!chunk.length) {
1194:
            return;
1195:
        }
1196:
        var lines = chunk.split(/\r\n|\n/);
1197:
        var length = lines.length;
1198:
        if (length === 1) {
1199:
            leftover += lines[0];
1200:
            return;
1201:
        }
1202:
 
1203:
        if (length > 1) {
1204:
            handleLogLine(file, leftover + lines[0], opts, stylize);
1205:
        }
1206:
        leftover = lines.pop();
1207:
        length -= 1;
1208:
        for (var i = 1; i < length; i++) {
1209:
            handleLogLine(file, lines[i], opts, stylize);
1210:
        }
1211:
    });
1212:
 
1213:
    stream.on('end', function () {
1214:
        streams[file].done = true;
1215:
        if (leftover) {
1216:
            handleLogLine(file, leftover, opts, stylize);
1217:
            leftover = '';
1218:
        } else {
1219:
            emitNextRecord(opts, stylize);
1220:
        }
1221:
        callback();
1222:
    });
1223:
}
1224:
 
1225:
 
1226:
/**
1227:
 * From node async module.
1228:
 */
1229:
/* BEGIN JSSTYLED */
1230:
function asyncForEach(arr, iterator, callback) {
1231:
    callback = callback || function () {};
1232:
    if (!arr.length) {
1233:
        return callback();
1234:
    }
1235:
    var completed = 0;
1236:
    arr.forEach(function (x) {
1237:
        iterator(x, function (err) {
1238:
            if (err) {
1239:
                callback(err);
1240:
                callback = function () {};
1241:
            }
1242:
            else {
1243:
                completed += 1;
1244:
                if (completed === arr.length) {
1245:
                    callback();
1246:
                }
1247:
            }
1248:
        });
1249:
    });
1250:
};
1251:
/* END JSSTYLED */
1252:
 
1253:
 
1254:
 
1255:
/**
1256:
 * Cleanup and exit properly.
1257:
 *
1258:
 * Warning: this doesn't stop processing, i.e. process exit might be delayed.
1259:
 * It is up to the caller to ensure that no subsequent bunyan processing
1260:
 * is done after calling this.
1261:
 *
1262:
 * @param code {Number} exit code.
1263:
 * @param signal {String} Optional signal name, if this was exitting because
1264:
 *    of a signal.
1265:
 */
1266:
var cleanedUp = false;
1267:
function cleanupAndExit(code, signal) {
1268:
    // Guard one call.
1269:
    if (cleanedUp) {
1270:
        return;
1271:
    }
1272:
    cleanedUp = true;
1273:
    if (_DEBUG) warn('(bunyan: cleanupAndExit)');
1274:
 
1275:
    // Clear possibly interrupted ANSI code (issue #59).
1276:
    if (usingAnsiCodes) {
1277:
        stdout.write('\033[0m');
1278:
    }
1279:
 
1280:
    // Kill possible dtrace child.
1281:
    if (child) {
1282:
        child.kill(signal);
1283:
    }
1284:
 
1285:
    if (pager) {
1286:
        // Let pager know that output is done, then wait for pager to exit.
1287:
        stdout.end();
1288:
        pager.on('exit', function (pagerCode) {
1289:
            if (_DEBUG)
1290:
                warn('(bunyan: pager exit -> process.exit(%s))',
1291:
                    pagerCode || code);
1292:
            process.exit(pagerCode || code);
1293:
        });
1294:
    } else {
1295:
        if (_DEBUG) warn('(bunyan: process.exit(%s))', code);
1296:
        process.exit(code);
1297:
    }
1298:
}
1299:
 
1300:
 
1301:
 
1302:
//---- mainline
1303:
 
1304:
process.on('SIGINT', function () { cleanupAndExit(1, 'SIGINT'); });
1305:
process.on('SIGQUIT', function () { cleanupAndExit(1, 'SIGQUIT'); });
1306:
process.on('SIGTERM', function () { cleanupAndExit(1, 'SIGTERM'); });
1307:
process.on('SIGHUP', function () { cleanupAndExit(1, 'SIGHUP'); });
1308:
 
1309:
process.on('uncaughtException', function (err) {
1310:
    function _indent(s) {
1311:
        var lines = s.split(/\r?\n/);
1312:
        for (var i = 0; i < lines.length; i++) {
1313:
            lines[i] = '*     ' + lines[i];
1314:
        }
1315:
        return lines.join('\n');
1316:
    }
1317:
 
1318:
    var title = encodeURIComponent(format(
1319:
        'Bunyan %s crashed: %s', getVersion(), String(err)));
1320:
    var e = console.error;
1321:
    e('* * *');
1322:
    e('* The Bunyan CLI crashed!');
1323:
    e('*');
1324:
    if (err.name === 'ReferenceError' && gOptsForUncaughtException.conditions) {
1325:
        e('* A "ReferenceError" is often the result of given');
1326:
        e('* `-c CONDITION` code that doesn\'t guard against undefined');
1327:
        e('* values. If that is not the problem:');
1328:
        e('*');
1329:
    }
1330:
    e('* Please report this issue and include the details below:');
1331:
    e('*');
1332:
    e('*    https://github.com/trentm/node-bunyan/issues/new?title=%s', title);
1333:
    e('*');
1334:
    e('* * *');
1335:
    e('* node version:', process.version);
1336:
    e('* bunyan version:', getVersion());
1337:
    e('* argv: %j', process.argv);
1338:
    e('* log line: %j', currLine);
1339:
    e('* stack:');
1340:
    e(_indent(err.stack));
1341:
    e('* * *');
1342:
    process.exit(1);
1343:
});
1344:
 
1345:
 
1346:
function main(argv) {
1347:
    try {
1348:
        var opts = parseArgv(argv);
1349:
    } catch (e) {
1350:
        warn('bunyan: error: %s', e.message);
1351:
        return drainStdoutAndExit(1);
1352:
    }
1353:
    gOptsForUncaughtException = opts; // intentionally global
1354:
    if (opts.help) {
1355:
        printHelp();
1356:
        return;
1357:
    }
1358:
    if (opts.version) {
1359:
        console.log('bunyan ' + getVersion());
1360:
        return;
1361:
    }
1362:
    if (opts.pid && opts.args.length > 0) {
1363:
        warn('bunyan: error: can\'t use both "-p PID" (%s) and file (%s) args',
1364:
            opts.pid, opts.args.join(' '));
1365:
        return drainStdoutAndExit(1);
1366:
    }
1367:
    if (opts.color === null) {
1368:
        if (process.env.BUNYAN_NO_COLOR &&
1369:
                process.env.BUNYAN_NO_COLOR.length > 0) {
1370:
            opts.color = false;
1371:
        } else {
1372:
            opts.color = process.stdout.isTTY;
1373:
        }
1374:
    }
1375:
    usingAnsiCodes = opts.color; // intentionally global
1376:
    var stylize = (opts.color ? stylizeWithColor : stylizeWithoutColor);
1377:
 
1378:
    // Pager.
1379:
    var nodeVer = process.versions.node.split('.').map(Number);
1380:
    var paginate = (
1381:
        process.stdout.isTTY &&
1382:
        process.stdin.isTTY &&
1383:
        !opts.pids && // Don't page if following process output.
1384:
        opts.args.length > 0 && // Don't page if no file args to process.
1385:
        process.platform !== 'win32' &&
1386:
        nodeVer >= [0, 8, 0] &&
1387:
        (opts.paginate === true ||
1388:
            (opts.paginate !== false &&
1389:
                (!process.env.BUNYAN_NO_PAGER ||
1390:
                    process.env.BUNYAN_NO_PAGER.length === 0))));
1391:
    if (paginate) {
1392:
        var pagerCmd = process.env.PAGER || 'less';
1393:
        /* JSSTYLED */
1394:
        assert.ok(pagerCmd.indexOf('"') === -1 && pagerCmd.indexOf("'") === -1,
1395:
            'cannot parse PAGER quotes yet');
1396:
        var argv = pagerCmd.split(/\s+/g);
1397:
        var env = objCopy(process.env);
1398:
        if (env.LESS === undefined) {
1399:
            // git's default is LESS=FRSX. I don't like the 'S' here because
1400:
            // lines are *typically* wide with bunyan output and scrolling
1401:
            // horizontally is a royal pain. Note a bug in Mac's `less -F`,
1402:
            // such that SIGWINCH can kill it. If that rears too much then
1403:
            // I'll remove 'F' from here.
1404:
            env.LESS = 'FRX';
1405:
        }
1406:
        if (_DEBUG) warn('(pager: argv=%j, env.LESS=%j)', argv, env.LESS);
1407:
        // `pager` and `stdout` intentionally global.
1408:
        pager = spawn(argv[0], argv.slice(1),
1409:
            // Share the stderr handle to have error output come
1410:
            // straight through. Only supported in v0.8+.
1411:
            {env: env, stdio: ['pipe', 1, 2]});
1412:
        stdout = pager.stdin;
1413:
 
1414:
        // Early termination of the pager: just stop.
1415:
        pager.on('exit', function (pagerCode) {
1416:
            if (_DEBUG) warn('(bunyan: pager exit)');
1417:
            pager = null;
1418:
            stdout.end()
1419:
            stdout = process.stdout;
1420:
            cleanupAndExit(pagerCode);
1421:
        });
1422:
    }
1423:
 
1424:
    // Stdout error handling. (Couldn't setup until `stdout` was determined.)
1425:
    stdout.on('error', function (err) {
1426:
        if (_DEBUG) warn('(stdout error event: %s)', err);
1427:
        if (err.code === 'EPIPE') {
1428:
            drainStdoutAndExit(0);
1429:
        } else if (err.toString() === 'Error: This socket is closed.') {
1430:
            // Could get this if the pager closes its stdin, but hasn't
1431:
            // exited yet.
1432:
            drainStdoutAndExit(1);
1433:
        } else {
1434:
            warn(err);
1435:
            drainStdoutAndExit(1);
1436:
        }
1437:
    });
1438:
 
1439:
    var retval = 0;
1440:
    if (opts.pids) {
1441:
        processPids(opts, stylize, function (code) {
1442:
            cleanupAndExit(code);
1443:
        });
1444:
    } else if (opts.args.length > 0) {
1445:
        var files = opts.args;
1446:
        files.forEach(function (file) {
1447:
            streams[file] = { stream: null, records: [], done: false }
1448:
        });
1449:
        asyncForEach(files,
1450:
            function (file, next) {
1451:
                processFile(file, opts, stylize, function (err) {
1452:
                    if (err) {
1453:
                        warn('bunyan: %s', err.message);
1454:
                        retval += 1;
1455:
                    }
1456:
                    next();
1457:
                });
1458:
            },
1459:
            function (err) {
1460:
                if (err) {
1461:
                    warn('bunyan: unexpected error: %s', err.stack || err);
1462:
                    return drainStdoutAndExit(1);
1463:
                }
1464:
                cleanupAndExit(retval);
1465:
            }
1466:
        );
1467:
    } else {
1468:
        processStdin(opts, stylize, function () {
1469:
            cleanupAndExit(retval);
1470:
        });
1471:
    }
1472:
}
1473:
 
1474:
if (require.main === module) {
1475:
    // HACK guard for <https://github.com/trentm/json/issues/24>.
1476:
    // We override the `process.stdout.end` guard that core node.js puts in
1477:
    // place. The real fix is that `.end()` shouldn't be called on stdout
1478:
    // in node core. Node v0.6.9 fixes that. Only guard for v0.6.0..v0.6.8.
1479:
    var nodeVer = process.versions.node.split('.').map(Number);
1480:
    if ([0, 6, 0] <= nodeVer && nodeVer <= [0, 6, 8]) {
1481:
        var stdout = process.stdout;
1482:
        stdout.end = stdout.destroy = stdout.destroySoon = function () {
1483:
            /* pass */
1484:
        };
1485:
    }
1486:
 
1487:
    main(process.argv);
1488:
}