src/misc/bunyan.js
function emitRecord(rec, line, opts, stylize) {
    var short = false;

    switch (opts.outputMode) {
    case OM_SHORT:
        short = true;
        /* jsl:fall-thru */

    case OM_LONG:
        //    [time] LEVEL: name[/comp]/pid on hostname (src): msg* (extras...)
        //        msg*
        //        --
        //        long and multi-line extras
        //        ...
        // If 'msg' is single-line, then it goes in the top line.
        // If 'req', show the request.
        // If 'res', show the response.
        // If 'err' and 'err.stack' then show that.
        if (!isValidRecord(rec)) {
            return emit(line + '\n');
        }

        delete rec.v;

        // Time.
        var time;
        if (!short && opts.timeFormat === TIME_UTC) {
            // Fast default path: We assume the raw `rec.time` is a UTC time
            // in ISO 8601 format (per spec).
            time = '[' + rec.time + ']';
        } else if (!moment && opts.timeFormat === TIME_UTC) {
            // Don't require momentjs install, as long as not using TIME_LOCAL.
            time = rec.time.substr(11);
        } else {
            var tzFormat;
            var moTime = moment(rec.time);
            switch (opts.timeFormat) {
            case TIME_UTC:
                tzFormat = TIMEZONE_UTC_FORMATS[short ? 'short' : 'long'];
                moTime.utc();
                break;
            case TIME_LOCAL:
                tzFormat = TIMEZONE_LOCAL_FORMATS[short ? 'short' : 'long'];
                break;
            default:
                throw new Error('unexpected timeFormat: ' + opts.timeFormat);
            };
            time = moTime.format(tzFormat);
        }
        time = stylize(time, 'none');
        delete rec.time;

        var nameStr = rec.name;
        delete rec.name;

        if (rec.component) {
            nameStr += '/' + rec.component;
        }
        delete rec.component;

        if (!short)
            nameStr += '/' + rec.pid;
        delete rec.pid;

        var level = (upperPaddedNameFromLevel[rec.level] || 'LVL' + rec.level);
        if (opts.color) {
            var colorFromLevel = {
                10: 'white',    // TRACE
                20: 'yellow',   // DEBUG
                30: 'cyan',     // INFO
                40: 'magenta',  // WARN
                50: 'red',      // ERROR
                60: 'inverse',  // FATAL
            };
            level = stylize(level, colorFromLevel[rec.level]);
        }
        delete rec.level;

        var src = '';
        if (rec.src && rec.src.file) {
            var s = rec.src;
            if (s.func) {
                src = format(' (%s:%d in %s)', s.file, s.line, s.func);
            } else {
                src = format(' (%s:%d)', s.file, s.line);
            }
            src = stylize(src, 'green');
        }
        delete rec.src;

        var hostname = rec.hostname;
        delete rec.hostname;

        var extras = [];
        var details = [];

        if (rec.req_id) {
            extras.push('req_id=' + rec.req_id);
        }
        delete rec.req_id;

        var onelineMsg;
        if (rec.msg.indexOf('\n') !== -1) {
            onelineMsg = '';
            details.push(indent(stylize(rec.msg, 'cyan')));
        } else {
            onelineMsg = ' ' + stylize(rec.msg, 'cyan');
        }
        delete rec.msg;

        if (rec.req && typeof (rec.req) === 'object') {
            var req = rec.req;
            delete rec.req;
            var headers = req.headers;
            if (!headers) {
                headers = '';
            } else if (typeof (headers) === 'string') {
                headers = '\n' + headers;
            } else if (typeof (headers) === 'object') {
                headers = '\n' + Object.keys(headers).map(function (h) {
                    return h + ': ' + headers[h];
                }).join('\n');
            }
            var s = format('%s %s HTTP/%s%s', req.method,
                req.url,
                req.httpVersion || '1.1',
                headers
            );
            delete req.url;
            delete req.method;
            delete req.httpVersion;
            delete req.headers;
            if (req.body) {
                s += '\n\n' + (typeof (req.body) === 'object'
                    ? JSON.stringify(req.body, null, 2) : req.body);
                delete req.body;
            }
            if (req.trailers && Object.keys(req.trailers) > 0) {
                s += '\n' + Object.keys(req.trailers).map(function (t) {
                    return t + ': ' + req.trailers[t];
                }).join('\n');
            }
            delete req.trailers;
            details.push(indent(s));
            // E.g. for extra 'foo' field on 'req', add 'req.foo' at
            // top-level. This *does* have the potential to stomp on a
            // literal 'req.foo' key.
            Object.keys(req).forEach(function (k) {
                rec['req.' + k] = req[k];
            })
        }

        /*
         * `client_req` is the typical field name for an *HTTP client request
         * object* serialized by the restify-clients library. Render the
         * client request somewhat like `curl` debug output shows it.
         */
        if (rec.client_req && typeof (rec.client_req) === 'object') {
            var client_req = rec.client_req;
            delete rec.client_req;

            var headers = client_req.headers;
            delete client_req.headers;

            /*
             * `client_req.address`, and `client_req.port`, provide values for
             * a *likely* "Host" header that wasn't included in the serialized
             * headers. Node.js will often add this "Host" header in its
             * `http.ClientRequest`, e.g. for node v6.10.3:
             *      // JSSTYLED
             *      https://github.com/nodejs/node/blob/v6.10.3/lib/_http_client.js#L88-L105
             *
             * If `client_req.port` exists and is 80 or 443, we *assume* that
             * is the default protocol port, and elide it per the `defaultPort`
             * handling in the node.js link above.
             *
             * Note: This added Host header is a *guess*. Bunyan shouldn't be
             * doing this "favour" for users because it can be wrong and
             * misleading. Bunyan 2.x will drop adding this. See issue #504
             * for details.
             */
            var hostHeaderLine = '';
            if (!headers || !(
                    Object.hasOwnProperty.call(headers, 'host') ||
                    Object.hasOwnProperty.call(headers, 'Host') ||
                    Object.hasOwnProperty.call(headers, 'HOST')
                )
            ) {
                if (Object.hasOwnProperty.call(client_req, 'address')) {
                    hostHeaderLine = '\nHost: ' + client_req.address;
                    if (Object.hasOwnProperty.call(client_req, 'port')) {
                        // XXX
                        var port = +client_req.port;
                        if (port !== 80 && port !== 443) {
                            hostHeaderLine += ':' + client_req.port;
                        }
                        delete client_req.port;
                    }
                    delete client_req.address;
                }
            }

            var s = format('%s %s HTTP/%s%s%s', client_req.method,
                client_req.url,
                client_req.httpVersion || '1.1',
                hostHeaderLine,
                (headers ?
                    '\n' + Object.keys(headers).map(
                        function (h) {
                            return h + ': ' + headers[h];
                        }).join('\n') :
                    ''));
            delete client_req.method;
            delete client_req.url;
            delete client_req.httpVersion;

            if (client_req.body) {
                s += '\n\n' + (typeof (client_req.body) === 'object' ?
                    JSON.stringify(client_req.body, null, 2) :
                    client_req.body);
                delete client_req.body;
            }
            // E.g. for extra 'foo' field on 'client_req', add
            // 'client_req.foo' at top-level. This *does* have the potential
            // to stomp on a literal 'client_req.foo' key.
            Object.keys(client_req).forEach(function (k) {
                rec['client_req.' + k] = client_req[k];
            })
            details.push(indent(s));
        }

        function _res(res) {
            var s = '';

            /*
             * Handle `res.header` or `res.headers` as either a string or
             * an object of header key/value pairs. Prefer `res.header` if set,
             * because that's what Bunyan's own `res` serializer specifies,
             * because that's the value in Node.js's core HTTP server response
             * implementation that has all the implicit headers.
             *
             * Note: `res.header` (string) typically includes the 'HTTP/1.1 ...'
             * status line.
             */
            var headerTypes = {string: true, object: true};
            var headers;
            var headersStr = '';
            var headersHaveStatusLine = false;
            if (res.header && headerTypes[typeof (res.header)]) {
                headers = res.header;
                delete res.header;
            } else if (res.headers && headerTypes[typeof (res.headers)]) {
                headers = res.headers;
                delete res.headers;
            }
            if (headers === undefined) {
                /* pass through */
            } else if (typeof (headers) === 'string') {
                headersStr = headers.trimRight(); // Trim the CRLF.
                if (headersStr.slice(0, 5) === 'HTTP/') {
                    headersHaveStatusLine = true;
                }
            } else {
                headersStr += Object.keys(headers).map(
                    function (h) { return h + ': ' + headers[h]; }).join('\n');
            }

            /*
             * Add a 'HTTP/1.1 ...' status line if the headers didn't already
             * include it.
             */
            if (!headersHaveStatusLine && res.statusCode !== undefined) {
                s += format('HTTP/1.1 %s %s\n', res.statusCode,
                    http.STATUS_CODES[res.statusCode]);
            }
            delete res.statusCode;
            s += headersStr;

            if (res.body !== undefined) {
                var body = (typeof (res.body) === 'object'
                    ? JSON.stringify(res.body, null, 2) : res.body);
                if (body.length > 0) { s += '\n\n' + body };
                delete res.body;
            } else {
                s = s.trimRight();
            }
            if (res.trailer) {
                s += '\n' + res.trailer;
            }
            delete res.trailer;
            if (s) {
                details.push(indent(s));
            }
            // E.g. for extra 'foo' field on 'res', add 'res.foo' at
            // top-level. This *does* have the potential to stomp on a
            // literal 'res.foo' key.
            Object.keys(res).forEach(function (k) {
                rec['res.' + k] = res[k];
            });
        }

        if (rec.res && typeof (rec.res) === 'object') {
            _res(rec.res);
            delete rec.res;
        }
        if (rec.client_res && typeof (rec.client_res) === 'object') {
            _res(rec.client_res);
            delete rec.client_res;
        }

        if (rec.err && rec.err.stack) {
            var err = rec.err
            if (typeof (err.stack) !== 'string') {
                details.push(indent(err.stack.toString()));
            } else {
                details.push(indent(err.stack));
            }
            delete err.message;
            delete err.name;
            delete err.stack;
            // E.g. for extra 'foo' field on 'err', add 'err.foo' at
            // top-level. This *does* have the potential to stomp on a
            // literal 'err.foo' key.
            Object.keys(err).forEach(function (k) {
                rec['err.' + k] = err[k];
            })
            delete rec.err;
        }

        var leftover = Object.keys(rec);
        for (var i = 0; i < leftover.length; i++) {
            var key = leftover[i];
            var value = rec[key];
            var stringified = false;
            if (typeof (value) !== 'string') {
                value = JSON.stringify(value, null, 2);
                stringified = true;
            }
            if (value.indexOf('\n') !== -1 || value.length > 50) {
                details.push(indent(key + ': ' + value));
            } else if (!stringified && (value.indexOf(' ') != -1 ||
                value.length === 0))
            {
                extras.push(key + '=' + JSON.stringify(value));
            } else {
                extras.push(key + '=' + value);
            }
        }

        extras = stylize(
            (extras.length ? ' (' + extras.join(', ') + ')' : ''), 'none');
        details = stylize(
            (details.length ? details.join('\n    --\n') + '\n' : ''), 'none');
        if (!short)
            emit(format('%s %s: %s on %s%s:%s%s\n%s',
                time,
                level,
                nameStr,
                hostname || '<no-hostname>',
                src,
                onelineMsg,
                extras,
                details));
        else
            emit(format('%s %s %s:%s%s\n%s',
                time,
                level,
                nameStr,
                onelineMsg,
                extras,
                details));
        break;

    case OM_INSPECT:
        emit(util.inspect(rec, false, Infinity, true) + '\n');
        break;

    case OM_BUNYAN:
        emit(JSON.stringify(rec, null, 0) + '\n');
        break;

    case OM_JSON:
        emit(JSON.stringify(rec, null, opts.jsonIndent) + '\n');
        break;

    case OM_SIMPLE:
        /* JSSTYLED */
        // <http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/SimpleLayout.html>
        if (!isValidRecord(rec)) {
            return emit(line + '\n');
        }
        emit(format('%s - %s\n',
            upperNameFromLevel[rec.level] || 'LVL' + rec.level,
            rec.msg));
        break;
    default:
        throw new Error('unknown output mode: '+opts.outputMode);
    }
}
function emitRecord(rec, line, opts, stylize) {
    var short = false;

    switch (opts.outputMode) {
    case OM_SHORT:
        short = true;
        /* jsl:fall-thru */

    case OM_LONG:
        //    [time] LEVEL: name[/comp]/pid on hostname (src): msg* (extras...)
        //        msg*
        //        --
        //        long and multi-line extras
        //        ...
        // If 'msg' is single-line, then it goes in the top line.
        // If 'req', show the request.
        // If 'res', show the response.
        // If 'err' and 'err.stack' then show that.
        if (!isValidRecord(rec)) {
            return emit(line + '\n');
        }

        delete rec.v;

        // Time.
        var time;
        if (!short && opts.timeFormat === TIME_UTC) {
            // Fast default path: We assume the raw `rec.time` is a UTC time
            // in ISO 8601 format (per spec).
            time = '[' + rec.time + ']';
        } else if (!moment && opts.timeFormat === TIME_UTC) {
            // Don't require momentjs install, as long as not using TIME_LOCAL.
            time = rec.time.substr(11);
        } else {
            var tzFormat;
            var moTime = moment(rec.time);
            switch (opts.timeFormat) {
            case TIME_UTC:
                tzFormat = TIMEZONE_UTC_FORMATS[short ? 'short' : 'long'];
                moTime.utc();
                break;
            case TIME_LOCAL:
                tzFormat = TIMEZONE_LOCAL_FORMATS[short ? 'short' : 'long'];
                break;
            default:
                throw new Error('unexpected timeFormat: ' + opts.timeFormat);
            };
            time = moTime.format(tzFormat);
        }
        time = stylize(time, 'none');
        delete rec.time;

        var nameStr = rec.name;
        delete rec.name;

        if (rec.component) {
            nameStr += '/' + rec.component;
        }
        delete rec.component;

        if (!short)
            nameStr += '/' + rec.pid;
        delete rec.pid;

        var level = (upperPaddedNameFromLevel[rec.level] || 'LVL' + rec.level);
        if (opts.color) {
            var colorFromLevel = {
                10: 'white',    // TRACE
                20: 'yellow',   // DEBUG
                30: 'cyan',     // INFO
                40: 'magenta',  // WARN
                50: 'red',      // ERROR
                60: 'inverse',  // FATAL
            };
            level = stylize(level, colorFromLevel[rec.level]);
        }
        delete rec.level;

        var src = '';
        if (rec.src && rec.src.file) {
            var s = rec.src;
            if (s.func) {
                src = format(' (%s:%d in %s)', s.file, s.line, s.func);
            } else {
                src = format(' (%s:%d)', s.file, s.line);
            }
            src = stylize(src, 'green');
        }
        delete rec.src;

        var hostname = rec.hostname;
        delete rec.hostname;

        var extras = [];
        var details = [];

        if (rec.req_id) {
            extras.push('req_id=' + rec.req_id);
        }
        delete rec.req_id;

        var onelineMsg;
        if (rec.msg.indexOf('\n') !== -1) {
            onelineMsg = '';
            details.push(indent(stylize(rec.msg, 'cyan')));
        } else {
            onelineMsg = ' ' + stylize(rec.msg, 'cyan');
        }
        delete rec.msg;

        if (rec.req && typeof (rec.req) === 'object') {
            var req = rec.req;
            delete rec.req;
            var headers = req.headers;
            if (!headers) {
                headers = '';
            } else if (typeof (headers) === 'string') {
                headers = '\n' + headers;
            } else if (typeof (headers) === 'object') {
                headers = '\n' + Object.keys(headers).map(function (h) {
                    return h + ': ' + headers[h];
                }).join('\n');
            }
            var s = format('%s %s HTTP/%s%s', req.method,
                req.url,
                req.httpVersion || '1.1',
                headers
            );
            delete req.url;
            delete req.method;
            delete req.httpVersion;
            delete req.headers;
            if (req.body) {
                s += '\n\n' + (typeof (req.body) === 'object'
                    ? JSON.stringify(req.body, null, 2) : req.body);
                delete req.body;
            }
            if (req.trailers && Object.keys(req.trailers) > 0) {
                s += '\n' + Object.keys(req.trailers).map(function (t) {
                    return t + ': ' + req.trailers[t];
                }).join('\n');
            }
            delete req.trailers;
            details.push(indent(s));
            // E.g. for extra 'foo' field on 'req', add 'req.foo' at
            // top-level. This *does* have the potential to stomp on a
            // literal 'req.foo' key.
            Object.keys(req).forEach(function (k) {
                rec['req.' + k] = req[k];
            })
        }

        /*
         * `client_req` is the typical field name for an *HTTP client request
         * object* serialized by the restify-clients library. Render the
         * client request somewhat like `curl` debug output shows it.
         */
        if (rec.client_req && typeof (rec.client_req) === 'object') {
            var client_req = rec.client_req;
            delete rec.client_req;

            var headers = client_req.headers;
            delete client_req.headers;

            /*
             * `client_req.address`, and `client_req.port`, provide values for
             * a *likely* "Host" header that wasn't included in the serialized
             * headers. Node.js will often add this "Host" header in its
             * `http.ClientRequest`, e.g. for node v6.10.3:
             *      // JSSTYLED
             *      https://github.com/nodejs/node/blob/v6.10.3/lib/_http_client.js#L88-L105
             *
             * If `client_req.port` exists and is 80 or 443, we *assume* that
             * is the default protocol port, and elide it per the `defaultPort`
             * handling in the node.js link above.
             *
             * Note: This added Host header is a *guess*. Bunyan shouldn't be
             * doing this "favour" for users because it can be wrong and
             * misleading. Bunyan 2.x will drop adding this. See issue #504
             * for details.
             */
            var hostHeaderLine = '';
            if (!headers || !(
                    Object.hasOwnProperty.call(headers, 'host') ||
                    Object.hasOwnProperty.call(headers, 'Host') ||
                    Object.hasOwnProperty.call(headers, 'HOST')
                )
            ) {
                if (Object.hasOwnProperty.call(client_req, 'address')) {
                    hostHeaderLine = '\nHost: ' + client_req.address;
                    if (Object.hasOwnProperty.call(client_req, 'port')) {
                        // XXX
                        var port = +client_req.port;
                        if (port !== 80 && port !== 443) {
                            hostHeaderLine += ':' + client_req.port;
                        }
                        delete client_req.port;
                    }
                    delete client_req.address;
                }
            }

            var s = format('%s %s HTTP/%s%s%s', client_req.method,
                client_req.url,
                client_req.httpVersion || '1.1',
                hostHeaderLine,
                (headers ?
                    '\n' + Object.keys(headers).map(
                        function (h) {
                            return h + ': ' + headers[h];
                        }).join('\n') :
                    ''));
            delete client_req.method;
            delete client_req.url;
            delete client_req.httpVersion;

            if (client_req.body) {
                s += '\n\n' + (typeof (client_req.body) === 'object' ?
                    JSON.stringify(client_req.body, null, 2) :
                    client_req.body);
                delete client_req.body;
            }
            // E.g. for extra 'foo' field on 'client_req', add
            // 'client_req.foo' at top-level. This *does* have the potential
            // to stomp on a literal 'client_req.foo' key.
            Object.keys(client_req).forEach(function (k) {
                rec['client_req.' + k] = client_req[k];
            })
            details.push(indent(s));
        }

        function _res(res) {
            var s = '';

            /*
             * Handle `res.header` or `res.headers` as either a string or
             * an object of header key/value pairs. Prefer `res.header` if set,
             * because that's what Bunyan's own `res` serializer specifies,
             * because that's the value in Node.js's core HTTP server response
             * implementation that has all the implicit headers.
             *
             * Note: `res.header` (string) typically includes the 'HTTP/1.1 ...'
             * status line.
             */
            var headerTypes = {string: true, object: true};
            var headers;
            var headersStr = '';
            var headersHaveStatusLine = false;
            if (res.header && headerTypes[typeof (res.header)]) {
                headers = res.header;
                delete res.header;
            } else if (res.headers && headerTypes[typeof (res.headers)]) {
                headers = res.headers;
                delete res.headers;
            }
            if (headers === undefined) {
                /* pass through */
            } else if (typeof (headers) === 'string') {
                headersStr = headers.trimRight(); // Trim the CRLF.
                if (headersStr.slice(0, 5) === 'HTTP/') {
                    headersHaveStatusLine = true;
                }
            } else {
                headersStr += Object.keys(headers).map(
                    function (h) { return h + ': ' + headers[h]; }).join('\n');
            }

            /*
             * Add a 'HTTP/1.1 ...' status line if the headers didn't already
             * include it.
             */
            if (!headersHaveStatusLine && res.statusCode !== undefined) {
                s += format('HTTP/1.1 %s %s\n', res.statusCode,
                    http.STATUS_CODES[res.statusCode]);
            }
            delete res.statusCode;
            s += headersStr;

            if (res.body !== undefined) {
                var body = (typeof (res.body) === 'object'
                    ? JSON.stringify(res.body, null, 2) : res.body);
                if (body.length > 0) { s += '\n\n' + body };
                delete res.body;
            } else {
                s = s.trimRight();
            }
            if (res.trailer) {
                s += '\n' + res.trailer;
            }
            delete res.trailer;
            if (s) {
                details.push(indent(s));
            }
            // E.g. for extra 'foo' field on 'res', add 'res.foo' at
            // top-level. This *does* have the potential to stomp on a
            // literal 'res.foo' key.
            Object.keys(res).forEach(function (k) {
                rec['res.' + k] = res[k];
            });
        }

        if (rec.res && typeof (rec.res) === 'object') {
            _res(rec.res);
            delete rec.res;
        }
        if (rec.client_res && typeof (rec.client_res) === 'object') {
            _res(rec.client_res);
            delete rec.client_res;
        }

        if (rec.err && rec.err.stack) {
            var err = rec.err
            if (typeof (err.stack) !== 'string') {
                details.push(indent(err.stack.toString()));
            } else {
                details.push(indent(err.stack));
            }
            delete err.message;
            delete err.name;
            delete err.stack;
            // E.g. for extra 'foo' field on 'err', add 'err.foo' at
            // top-level. This *does* have the potential to stomp on a
            // literal 'err.foo' key.
            Object.keys(err).forEach(function (k) {
                rec['err.' + k] = err[k];
            })
            delete rec.err;
        }

        var leftover = Object.keys(rec);
        for (var i = 0; i < leftover.length; i++) {
            var key = leftover[i];
            var value = rec[key];
            var stringified = false;
            if (typeof (value) !== 'string') {
                value = JSON.stringify(value, null, 2);
                stringified = true;
            }
            if (value.indexOf('\n') !== -1 || value.length > 50) {
                details.push(indent(key + ': ' + value));
            } else if (!stringified && (value.indexOf(' ') != -1 ||
                value.length === 0))
            {
                extras.push(key + '=' + JSON.stringify(value));
            } else {
                extras.push(key + '=' + value);
            }
        }

        extras = stylize(
            (extras.length ? ' (' + extras.join(', ') + ')' : ''), 'none');
        details = stylize(
            (details.length ? details.join('\n    --\n') + '\n' : ''), 'none');
        if (!short)
            emit(format('%s %s: %s on %s%s:%s%s\n%s',
                time,
                level,
                nameStr,
                hostname || '<no-hostname>',
                src,
                onelineMsg,
                extras,
                details));
        else
            emit(format('%s %s %s:%s%s\n%s',
                time,
                level,
                nameStr,
                onelineMsg,
                extras,
                details));
        break;

    case OM_INSPECT:
        emit(util.inspect(rec, false, Infinity, true) + '\n');
        break;

    case OM_BUNYAN:
        emit(JSON.stringify(rec, null, 0) + '\n');
        break;

    case OM_JSON:
        emit(JSON.stringify(rec, null, opts.jsonIndent) + '\n');
        break;

    case OM_SIMPLE:
        /* JSSTYLED */
        // <http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/SimpleLayout.html>
        if (!isValidRecord(rec)) {
            return emit(line + '\n');
        }
        emit(format('%s - %s\n',
            upperNameFromLevel[rec.level] || 'LVL' + rec.level,
            rec.msg));
        break;
    default:
        throw new Error('unknown output mode: '+opts.outputMode);
    }
}
function emitRecord ( rec , line , opts , stylize ) { var short = false ; switch ( opts . outputMode ) { case OM_SHORT : short = true ; /* jsl:fall-thru */ case OM_LONG : //    [time] LEVEL: name[/comp]/pid on hostname (src): msg* (extras...) //        msg* //        -- //        long and multi-line extras //        ... // If 'msg' is single-line, then it goes in the top line. // If 'req', show the request. // If 'res', show the response. // If 'err' and 'err.stack' then show that. if ( ! isValidRecord ( rec ) ) { return emit ( line + '\n' ) ; } delete rec . v ; // Time. var time ; if ( ! short && opts . timeFormat === TIME_UTC ) { // Fast default path: We assume the raw `rec.time` is a UTC time // in ISO 8601 format (per spec). time = '[' + rec . time + ']' ; } else if ( ! moment && opts . timeFormat === TIME_UTC ) { // Don't require momentjs install, as long as not using TIME_LOCAL. time = rec . time . substr ( 11 ) ; } else { var tzFormat ; var moTime = moment ( rec . time ) ; switch ( opts . timeFormat ) { case TIME_UTC : tzFormat = TIMEZONE_UTC_FORMATS [ short ? 'short' : 'long' ] ; moTime . utc ( ) ; break ; case TIME_LOCAL : tzFormat = TIMEZONE_LOCAL_FORMATS [ short ? 'short' : 'long' ] ; break ; default : throw new Error ( 'unexpected timeFormat: ' + opts . timeFormat ) ; } ; time = moTime . format ( tzFormat ) ; } time = stylize ( time , 'none' ) ; delete rec . time ; var nameStr = rec . name ; delete rec . name ; if ( rec . component ) { nameStr += '/' + rec . component ; } delete rec . component ; if ( ! short ) nameStr += '/' + rec . pid ; delete rec . pid ; var level = ( upperPaddedNameFromLevel [ rec . level ] || 'LVL' + rec . level ) ; if ( opts . color ) { var colorFromLevel = { 10 : 'white' , // TRACE 20 : 'yellow' , // DEBUG 30 : 'cyan' , // INFO 40 : 'magenta' , // WARN 50 : 'red' , // ERROR 60 : 'inverse' , // FATAL } ; level = stylize ( level , colorFromLevel [ rec . level ] ) ; } delete rec . level ; var src = '' ; if ( rec . src && rec . src . file ) { var s = rec . src ; if ( s . func ) { src = format ( ' (%s:%d in %s)' , s . file , s . line , s . func ) ; } else { src = format ( ' (%s:%d)' , s . file , s . line ) ; } src = stylize ( src , 'green' ) ; } delete rec . src ; var hostname = rec . hostname ; delete rec . hostname ; var extras = [ ] ; var details = [ ] ; if ( rec . req_id ) { extras . push ( 'req_id=' + rec . req_id ) ; } delete rec . req_id ; var onelineMsg ; if ( rec . msg . indexOf ( '\n' ) !== - 1 ) { onelineMsg = '' ; details . push ( indent ( stylize ( rec . msg , 'cyan' ) ) ) ; } else { onelineMsg = ' ' + stylize ( rec . msg , 'cyan' ) ; } delete rec . msg ; if ( rec . req && typeof ( rec . req ) === 'object' ) { var req = rec . req ; delete rec . req ; var headers = req . headers ; if ( ! headers ) { headers = '' ; } else if ( typeof ( headers ) === 'string' ) { headers = '\n' + headers ; } else if ( typeof ( headers ) === 'object' ) { headers = '\n' + Object . keys ( headers ) . map ( function ( h ) { return h + ': ' + headers [ h ] ; } ) . join ( '\n' ) ; } var s = format ( '%s %s HTTP/%s%s' , req . method , req . url , req . httpVersion || '1.1' , headers ) ; delete req . url ; delete req . method ; delete req . httpVersion ; delete req . headers ; if ( req . body ) { s += '\n\n' + ( typeof ( req . body ) === 'object' ? JSON . stringify ( req . body , null , 2 ) : req . body ) ; delete req . body ; } if ( req . trailers && Object . keys ( req . trailers ) > 0 ) { s += '\n' + Object . keys ( req . trailers ) . map ( function ( t ) { return t + ': ' + req . trailers [ t ] ; } ) . join ( '\n' ) ; } delete req . trailers ; details . push ( indent ( s ) ) ; // E.g. for extra 'foo' field on 'req', add 'req.foo' at // top-level. This *does* have the potential to stomp on a // literal 'req.foo' key. Object . keys ( req ) . forEach ( function ( k ) { rec [ 'req.' + k ] = req [ k ] ; } ) } /*
         * `client_req` is the typical field name for an *HTTP client request
         * object* serialized by the restify-clients library. Render the
         * client request somewhat like `curl` debug output shows it.
         */ if ( rec . client_req && typeof ( rec . client_req ) === 'object' ) { var client_req = rec . client_req ; delete rec . client_req ; var headers = client_req . headers ; delete client_req . headers ; /*
             * `client_req.address`, and `client_req.port`, provide values for
             * a *likely* "Host" header that wasn't included in the serialized
             * headers. Node.js will often add this "Host" header in its
             * `http.ClientRequest`, e.g. for node v6.10.3:
             *      // JSSTYLED
             *      https://github.com/nodejs/node/blob/v6.10.3/lib/_http_client.js#L88-L105
             *
             * If `client_req.port` exists and is 80 or 443, we *assume* that
             * is the default protocol port, and elide it per the `defaultPort`
             * handling in the node.js link above.
             *
             * Note: This added Host header is a *guess*. Bunyan shouldn't be
             * doing this "favour" for users because it can be wrong and
             * misleading. Bunyan 2.x will drop adding this. See issue #504
             * for details.
             */ var hostHeaderLine = '' ; if ( ! headers || ! ( Object . hasOwnProperty . call ( headers , 'host' ) || Object . hasOwnProperty . call ( headers , 'Host' ) || Object . hasOwnProperty . call ( headers , 'HOST' ) ) ) { if ( Object . hasOwnProperty . call ( client_req , 'address' ) ) { hostHeaderLine = '\nHost: ' + client_req . address ; if ( Object . hasOwnProperty . call ( client_req , 'port' ) ) { // XXX var port = + client_req . port ; if ( port !== 80 && port !== 443 ) { hostHeaderLine += ':' + client_req . port ; } delete client_req . port ; } delete client_req . address ; } } var s = format ( '%s %s HTTP/%s%s%s' , client_req . method , client_req . url , client_req . httpVersion || '1.1' , hostHeaderLine , ( headers ? '\n' + Object . keys ( headers ) . map ( function ( h ) { return h + ': ' + headers [ h ] ; } ) . join ( '\n' ) : '' ) ) ; delete client_req . method ; delete client_req . url ; delete client_req . httpVersion ; if ( client_req . body ) { s += '\n\n' + ( typeof ( client_req . body ) === 'object' ? JSON . stringify ( client_req . body , null , 2 ) : client_req . body ) ; delete client_req . body ; } // E.g. for extra 'foo' field on 'client_req', add // 'client_req.foo' at top-level. This *does* have the potential // to stomp on a literal 'client_req.foo' key. Object . keys ( client_req ) . forEach ( function ( k ) { rec [ 'client_req.' + k ] = client_req [ k ] ; } ) details . push ( indent ( s ) ) ; } function _res ( res ) { var s = '' ; /*
             * Handle `res.header` or `res.headers` as either a string or
             * an object of header key/value pairs. Prefer `res.header` if set,
             * because that's what Bunyan's own `res` serializer specifies,
             * because that's the value in Node.js's core HTTP server response
             * implementation that has all the implicit headers.
             *
             * Note: `res.header` (string) typically includes the 'HTTP/1.1 ...'
             * status line.
             */ var headerTypes = { string : true , object : true } ; var headers ; var headersStr = '' ; var headersHaveStatusLine = false ; if ( res . header && headerTypes [ typeof ( res . header ) ] ) { headers = res . header ; delete res . header ; } else if ( res . headers && headerTypes [ typeof ( res . headers ) ] ) { headers = res . headers ; delete res . headers ; } if ( headers === undefined ) { /* pass through */ } else if ( typeof ( headers ) === 'string' ) { headersStr = headers . trimRight ( ) ; // Trim the CRLF. if ( headersStr . slice ( 0 , 5 ) === 'HTTP/' ) { headersHaveStatusLine = true ; } } else { headersStr += Object . keys ( headers ) . map ( function ( h ) { return h + ': ' + headers [ h ] ; } ) . join ( '\n' ) ; } /*
             * Add a 'HTTP/1.1 ...' status line if the headers didn't already
             * include it.
             */ if ( ! headersHaveStatusLine && res . statusCode !== undefined ) { s += format ( 'HTTP/1.1 %s %s\n' , res . statusCode , http . STATUS_CODES [ res . statusCode ] ) ; } delete res . statusCode ; s += headersStr ; if ( res . body !== undefined ) { var body = ( typeof ( res . body ) === 'object' ? JSON . stringify ( res . body , null , 2 ) : res . body ) ; if ( body . length > 0 ) { s += '\n\n' + body } ; delete res . body ; } else { s = s . trimRight ( ) ; } if ( res . trailer ) { s += '\n' + res . trailer ; } delete res . trailer ; if ( s ) { details . push ( indent ( s ) ) ; } // E.g. for extra 'foo' field on 'res', add 'res.foo' at // top-level. This *does* have the potential to stomp on a // literal 'res.foo' key. Object . keys ( res ) . forEach ( function ( k ) { rec [ 'res.' + k ] = res [ k ] ; } ) ; } if ( rec . res && typeof ( rec . res ) === 'object' ) { _res ( rec . res ) ; delete rec . res ; } if ( rec . client_res && typeof ( rec . client_res ) === 'object' ) { _res ( rec . client_res ) ; delete rec . client_res ; } if ( rec . err && rec . err . stack ) { var err = rec . err if ( typeof ( err . stack ) !== 'string' ) { details . push ( indent ( err . stack . toString ( ) ) ) ; } else { details . push ( indent ( err . stack ) ) ; } delete err . message ; delete err . name ; delete err . stack ; // E.g. for extra 'foo' field on 'err', add 'err.foo' at // top-level. This *does* have the potential to stomp on a // literal 'err.foo' key. Object . keys ( err ) . forEach ( function ( k ) { rec [ 'err.' + k ] = err [ k ] ; } ) delete rec . err ; } var leftover = Object . keys ( rec ) ; for ( var i = 0 ; i < leftover . length ; i ++ ) { var key = leftover [ i ] ; var value = rec [ key ] ; var stringified = false ; if ( typeof ( value ) !== 'string' ) { value = JSON . stringify ( value , null , 2 ) ; stringified = true ; } if ( value . indexOf ( '\n' ) !== - 1 || value . length > 50 ) { details . push ( indent ( key + ': ' + value ) ) ; } else if ( ! stringified && ( value . indexOf ( ' ' ) != - 1 || value . length === 0 ) ) { extras . push ( key + '=' + JSON . stringify ( value ) ) ; } else { extras . push ( key + '=' + value ) ; } } extras = stylize ( ( extras . length ? ' (' + extras . join ( ', ' ) + ')' : '' ) , 'none' ) ; details = stylize ( ( details . length ? details . join ( '\n    --\n' ) + '\n' : '' ) , 'none' ) ; if ( ! short ) emit ( format ( '%s %s: %s on %s%s:%s%s\n%s' , time , level , nameStr , hostname || '<no-hostname>' , src , onelineMsg , extras , details ) ) ; else emit ( format ( '%s %s %s:%s%s\n%s' , time , level , nameStr , onelineMsg , extras , details ) ) ; break ; case OM_INSPECT : emit ( util . inspect ( rec , false , Infinity , true ) + '\n' ) ; break ; case OM_BUNYAN : emit ( JSON . stringify ( rec , null , 0 ) + '\n' ) ; break ; case OM_JSON : emit ( JSON . stringify ( rec , null , opts . jsonIndent ) + '\n' ) ; break ; case OM_SIMPLE : /* JSSTYLED */ // <http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/SimpleLayout.html> if ( ! isValidRecord ( rec ) ) { return emit ( line + '\n' ) ; } emit ( format ( '%s - %s\n' , upperNameFromLevel [ rec . level ] || 'LVL' + rec . level , rec . msg ) ) ; break ; default : throw new Error ( 'unknown output mode: ' + opts . outputMode ) ; } }
Print out a single result, considering input options.
Print out a single result considering input options .