function Logger(options, _childOptions, _childSimple) {
xxx('Logger start:', options)
if (!(this instanceof Logger)) {
return new Logger(options, _childOptions);
}
// Input arg validation.
var parent;
if (_childOptions !== undefined) {
parent = options;
options = _childOptions;
if (!(parent instanceof Logger)) {
throw new TypeError(
'invalid Logger creation: do not pass a second arg');
}
}
if (!options) {
throw new TypeError('options (object) is required');
}
if (!parent) {
if (!options.name) {
throw new TypeError('options.name (string) is required');
}
} else {
if (options.name) {
throw new TypeError(
'invalid options.name: child cannot set logger name');
}
}
if (options.stream && options.streams) {
throw new TypeError('cannot mix "streams" and "stream" options');
}
if (options.streams && !Array.isArray(options.streams)) {
throw new TypeError('invalid options.streams: must be an array')
}
if (options.serializers && (typeof (options.serializers) !== 'object' ||
Array.isArray(options.serializers))) {
throw new TypeError('invalid options.serializers: must be an object')
}
EventEmitter.call(this);
// Fast path for simple child creation.
if (parent && _childSimple) {
// `_isSimpleChild` is a signal to stream close handling that this child
// owns none of its streams.
this._isSimpleChild = true;
this._level = parent._level;
this.streams = parent.streams;
this.serializers = parent.serializers;
this.src = parent.src;
var fields = this.fields = {};
var parentFieldNames = Object.keys(parent.fields);
for (var i = 0; i < parentFieldNames.length; i++) {
var name = parentFieldNames[i];
fields[name] = parent.fields[name];
}
var names = Object.keys(options);
for (var i = 0; i < names.length; i++) {
var name = names[i];
fields[name] = options[name];
}
return;
}
// Start values.
var self = this;
if (parent) {
this._level = parent._level;
this.streams = [];
for (var i = 0; i < parent.streams.length; i++) {
var s = objCopy(parent.streams[i]);
s.closeOnExit = false; // Don't own parent stream.
this.streams.push(s);
}
this.serializers = objCopy(parent.serializers);
this.src = parent.src;
this.fields = objCopy(parent.fields);
if (options.level) {
this.level(options.level);
}
} else {
this._level = Number.POSITIVE_INFINITY;
this.streams = [];
this.serializers = null;
this.src = false;
this.fields = {};
}
if (!dtp && dtrace) {
dtp = dtrace.createDTraceProvider('bunyan');
for (var level in levelFromName) {
var probe;
probes[levelFromName[level]] = probe =
dtp.addProbe('log-' + level, 'char *');
// Explicitly add a reference to dtp to prevent it from being GC'd
probe.dtp = dtp;
}
dtp.enable();
}
// Handle *config* options (i.e. options that are not just plain data
// for log records).
if (options.stream) {
self.addStream({
type: 'stream',
stream: options.stream,
closeOnExit: false,
level: options.level
});
} else if (options.streams) {
options.streams.forEach(function (s) {
self.addStream(s, options.level);
});
} else if (parent && options.level) {
this.level(options.level);
} else if (!parent) {
if (runtimeEnv === 'browser') {
/*
* In the browser we'll be emitting to console.log by default.
* Any console.log worth its salt these days can nicely render ...n/a
function RingBuffer(options) {
this.limit = options && options.limit ? options.limit : 100;
this.writable = true;
this.records = [];
EventEmitter.call(this);
}...
github.com/davepacheco)
Potential uses: Live debugging if a running process could inspect those
messages. One could dump recent log messages at a finer log level than is
typically logged on
[`uncaughtException`](http://nodejs.org/docs/latest/api/all.html#all_event_uncaughtexception).
var ringbuffer = new bunyan.RingBuffer({ limit: 100 });
var log = new bunyan({
name: 'foo',
streams: [{
type: 'raw',
stream: ringbuffer,
level: 'debug'
}]
...function RotatingFileStream(options) {
this.path = options.path;
this.count = (options.count == null ? 10 : options.count);
assert.equal(typeof (this.count), 'number',
format('rotating-file stream "count" is not a number: %j (%s) in %j',
this.count, typeof (this.count), this));
assert.ok(this.count >= 0,
format('rotating-file stream "count" is not >= 0: %j in %j',
this.count, this));
// Parse `options.period`.
if (options.period) {
// <number><scope> where scope is:
// h hours (at the start of the hour)
// d days (at the start of the day, i.e. just after midnight)
// w weeks (at the start of Sunday)
// m months (on the first of the month)
// y years (at the start of Jan 1st)
// with special values 'hourly' (1h), 'daily' (1d), "weekly" (1w),
// 'monthly' (1m) and 'yearly' (1y)
var period = {
'hourly': '1h',
'daily': '1d',
'weekly': '1w',
'monthly': '1m',
'yearly': '1y'
}[options.period] || options.period;
var m = /^([1-9][0-9]*)([hdwmy]|ms)$/.exec(period);
if (!m) {
throw new Error(format('invalid period: "%s"', options.period));
}
this.periodNum = Number(m[1]);
this.periodScope = m[2];
} else {
this.periodNum = 1;
this.periodScope = 'd';
}
var lastModified = null;
try {
var fileInfo = fs.statSync(this.path);
lastModified = fileInfo.mtime.getTime();
}
catch (err) {
// file doesn't exist
}
var rotateAfterOpen = false;
if (lastModified) {
var lastRotTime = this._calcRotTime(0);
if (lastModified < lastRotTime) {
rotateAfterOpen = true;
}
}
// TODO: template support for backup files
// template: <path to which to rotate>
// default is %P.%n
// '/var/log/archive/foo.log' -> foo.log.%n
// '/var/log/archive/foo.log.%n'
// codes:
// XXX support strftime codes (per node version of those)
// or whatever module. Pick non-colliding for extra
// codes
// %P `path` base value
// %n integer number of rotated log (1,2,3,...)
// %d datetime in YYYY-MM-DD_HH-MM-SS
// XXX what should default date format be?
// prior art? Want to avoid ':' in
// filenames (illegal on Windows for one).
this.stream = fs.createWriteStream(this.path,
{flags: 'a', encoding: 'utf8'});
this.rotQueue = [];
this.rotating = false;
if (rotateAfterOpen) {
this._debug('rotateAfterOpen -> call rotate()');
this.rotate();
} else {
this._setupNextRot();
}
}n/a
function createLogger(options) {
return new Logger(options);
}...
running under [NW.js](http://nwjs.io/). Contributions by Adam Lynch, Jeremy
Ruppel, and Aleksey Timchenko.
- [pull #318] Add `reemitErrorEvents` optional boolean for streams added to a
Bunyan logger to control whether an "error" event on the stream will be
re-emitted on the `Logger` instance.
var log = bunyan.createLogger({
name: 'foo',
streams: [
{
type: 'raw',
stream: new MyCustomStream(),
reemitErrorEvents: true
}
...function resolveLevel(nameOrNum) {
var level;
var type = typeof (nameOrNum);
if (type === 'string') {
level = levelFromName[nameOrNum.toLowerCase()];
if (!level) {
throw new Error(format('unknown level name: "%s"', nameOrNum));
}
} else if (type !== 'number') {
throw new TypeError(format('cannot resolve level: invalid arg (%s):',
type, nameOrNum));
} else if (nameOrNum < 0 || Math.floor(nameOrNum) !== nameOrNum) {
throw new TypeError(format('level is not a positive integer: %s',
nameOrNum));
} else {
level = nameOrNum;
}
return level;
}...
- [issue #38] Fix the default `bunyan` CLI output of a `req.body` that is an
object instead of a string.
## 0.13.3
- Export `bunyan.resolveLevel(NAME-OR-NUM)` to resolve a level name or number
to its log level number value:
> bunyan.resolveLevel('INFO')
30
> bunyan.resolveLevel('debug')
20
...function safeCyclesSet() {
var seen = new Set();
return function (key, val) {
if (!val || typeof (val) !== 'object') {
return val;
}
if (seen.has(val)) {
return '[Circular]';
}
seen.add(val);
return val;
};
}...
See <https://github.com/trentm/node-bunyan#dtrace-support> for details. By
Bryan Cantrill.
## 0.14.6
- Export `bunyan.safeCycles()`. This may be useful for custom `type == "raw"
;`
streams that may do JSON stringification of log records themselves. Usage:
var str = JSON.stringify(rec, bunyan.safeCycles());
- [issue #49] Allow a `log.child()` to specify the level of inherited streams.
For example:
...function EventEmitter() {
EventEmitter.init.call(this);
}n/a
function RingBuffer(options) {
this.limit = options && options.limit ? options.limit : 100;
this.writable = true;
this.records = [];
EventEmitter.call(this);
}...
github.com/davepacheco)
Potential uses: Live debugging if a running process could inspect those
messages. One could dump recent log messages at a finer log level than is
typically logged on
[`uncaughtException`](http://nodejs.org/docs/latest/api/all.html#all_event_uncaughtexception).
var ringbuffer = new bunyan.RingBuffer({ limit: 100 });
var log = new bunyan({
name: 'foo',
streams: [{
type: 'raw',
stream: ringbuffer,
level: 'debug'
}]
...function EventEmitter() {
EventEmitter.init.call(this);
}n/a
destroy = function () {
this.writable = false;
this.emit('close');
}...
*/
Logger.prototype.reopenFileStreams = function () {
var self = this;
self.streams.forEach(function (s) {
if (s.type === 'file') {
if (s.stream) {
// Not sure if typically would want this, or more immediate
// `s.stream.destroy()`.
s.stream.end();
s.stream.destroySoon();
delete s.stream;
}
s.stream = fs.createWriteStream(s.path,
{flags: 'a', encoding: 'utf8'});
s.stream.on('error', function (err) {
...destroySoon = function () {
this.destroy();
}...
var self = this;
self.streams.forEach(function (s) {
if (s.type === 'file') {
if (s.stream) {
// Not sure if typically would want this, or more immediate
// `s.stream.destroy()`.
s.stream.end();
s.stream.destroySoon();
delete s.stream;
}
s.stream = fs.createWriteStream(s.path,
{flags: 'a', encoding: 'utf8'});
s.stream.on('error', function (err) {
self.emit('error', err, s);
});
...end = function () {
if (arguments.length > 0)
this.write.apply(this, Array.prototype.slice.call(arguments));
this.writable = false;
}...
- perhaps this would be a "buffered: true" option on the stream object
- then wrap the "stream" with a local class that handles the buffering
- to finish this, need the 'log.close' and `process.on('exit', ...)`
work that Trent has started.
- "canWrite" handling for full streams. Need to buffer a la log4js
- test file log with logadm rotation: does it handle that?
- test suite:
- test for a cloned logger double-`stream.end()` causing problems.
Perhaps the "closeOnExit" for existing streams should be false for
clones.
- test that a `log.clone(...)` adding a new field matching a serializer
works *and* that an existing field in the parent is not *re-serialized*.
- split out `bunyan` cli to a "bunyan" or "bunyan-reader" or "node-bunyan-reader"
as the basis for tools to consume bunyan logs. It can grow indep of node-bunyan
for generating the logs.
...write = function (record) {
if (!this.writable)
throw (new Error('RingBuffer has been ended already'));
this.records.push(record);
if (this.records.length > this.limit)
this.records.shift();
return (true);
}...
npm publish
.PHONY: docs
docs: toc
@[[ `which ronn` ]] || (echo "No 'ronn' on your PATH. Install with 'gem install ronn'" &&
; exit 2)
mkdir -p man/man1
ronn --style=toc --manual="bunyan manual" --date=$(shell git log -1 --pretty=format:%cd --date=short) --roff --html docs
/bunyan.1.ronn
python -c 'import sys; h = open("docs/bunyan.1.html").read(); h = h.replace(".mp dt.flush {float:left;width
:8ex}", ""); open("docs/bunyan.1.html", "w").write(
h)'
python -c 'import sys; h = open("docs/bunyan.1.html").read(); h = h.replace("</body>", "
;""<a href="https://github.com/trentm/node-bunyan"><img style="position: absolute; top:
0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="
;Fork me on GitHub"></a></body>"""); open("docs/bunyan.1.html", "w
x22;).write(h)'
@echo "# test with 'man ./docs/bunyan.1' and 'open ./docs/bunyan.1.html'"
# Re-generate the README.md table of contents.
toc:
./node_modules/.bin/markdown-toc -i README.md
...function RotatingFileStream(options) {
this.path = options.path;
this.count = (options.count == null ? 10 : options.count);
assert.equal(typeof (this.count), 'number',
format('rotating-file stream "count" is not a number: %j (%s) in %j',
this.count, typeof (this.count), this));
assert.ok(this.count >= 0,
format('rotating-file stream "count" is not >= 0: %j in %j',
this.count, this));
// Parse `options.period`.
if (options.period) {
// <number><scope> where scope is:
// h hours (at the start of the hour)
// d days (at the start of the day, i.e. just after midnight)
// w weeks (at the start of Sunday)
// m months (on the first of the month)
// y years (at the start of Jan 1st)
// with special values 'hourly' (1h), 'daily' (1d), "weekly" (1w),
// 'monthly' (1m) and 'yearly' (1y)
var period = {
'hourly': '1h',
'daily': '1d',
'weekly': '1w',
'monthly': '1m',
'yearly': '1y'
}[options.period] || options.period;
var m = /^([1-9][0-9]*)([hdwmy]|ms)$/.exec(period);
if (!m) {
throw new Error(format('invalid period: "%s"', options.period));
}
this.periodNum = Number(m[1]);
this.periodScope = m[2];
} else {
this.periodNum = 1;
this.periodScope = 'd';
}
var lastModified = null;
try {
var fileInfo = fs.statSync(this.path);
lastModified = fileInfo.mtime.getTime();
}
catch (err) {
// file doesn't exist
}
var rotateAfterOpen = false;
if (lastModified) {
var lastRotTime = this._calcRotTime(0);
if (lastModified < lastRotTime) {
rotateAfterOpen = true;
}
}
// TODO: template support for backup files
// template: <path to which to rotate>
// default is %P.%n
// '/var/log/archive/foo.log' -> foo.log.%n
// '/var/log/archive/foo.log.%n'
// codes:
// XXX support strftime codes (per node version of those)
// or whatever module. Pick non-colliding for extra
// codes
// %P `path` base value
// %n integer number of rotated log (1,2,3,...)
// %d datetime in YYYY-MM-DD_HH-MM-SS
// XXX what should default date format be?
// prior art? Want to avoid ':' in
// filenames (illegal on Windows for one).
this.stream = fs.createWriteStream(this.path,
{flags: 'a', encoding: 'utf8'});
this.rotQueue = [];
this.rotating = false;
if (rotateAfterOpen) {
this._debug('rotateAfterOpen -> call rotate()');
this.rotate();
} else {
this._setupNextRot();
}
}n/a
function EventEmitter() {
EventEmitter.init.call(this);
}n/a
function _calcRotTime(periodOffset) {
this._debug('_calcRotTime: %s%s', this.periodNum, this.periodScope);
var d = new Date();
this._debug(' now local: %s', d);
this._debug(' now utc: %s', d.toISOString());
var rotAt;
switch (this.periodScope) {
case 'ms':
// Hidden millisecond period for debugging.
if (this.rotAt) {
rotAt = this.rotAt + this.periodNum * periodOffset;
} else {
rotAt = Date.now() + this.periodNum * periodOffset;
}
break;
case 'h':
if (this.rotAt) {
rotAt = this.rotAt + this.periodNum * 60 * 60 * 1000 * periodOffset;
} else {
// First time: top of the next hour.
rotAt = Date.UTC(d.getUTCFullYear(), d.getUTCMonth(),
d.getUTCDate(), d.getUTCHours() + periodOffset);
}
break;
case 'd':
if (this.rotAt) {
rotAt = this.rotAt + this.periodNum * 24 * 60 * 60 * 1000
* periodOffset;
} else {
// First time: start of tomorrow (i.e. at the coming midnight) UTC.
rotAt = Date.UTC(d.getUTCFullYear(), d.getUTCMonth(),
d.getUTCDate() + periodOffset);
}
break;
case 'w':
// Currently, always on Sunday morning at 00:00:00 (UTC).
if (this.rotAt) {
rotAt = this.rotAt + this.periodNum * 7 * 24 * 60 * 60 * 1000
* periodOffset;
} else {
// First time: this coming Sunday.
var dayOffset = (7 - d.getUTCDay());
if (periodOffset < 1) {
dayOffset = -d.getUTCDay();
}
if (periodOffset > 1 || periodOffset < -1) {
dayOffset += 7 * periodOffset;
}
rotAt = Date.UTC(d.getUTCFullYear(), d.getUTCMonth(),
d.getUTCDate() + dayOffset);
}
break;
case 'm':
if (this.rotAt) {
rotAt = Date.UTC(d.getUTCFullYear(),
d.getUTCMonth() + this.periodNum * periodOffset, 1);
} else {
// First time: the start of the next month.
rotAt = Date.UTC(d.getUTCFullYear(),
d.getUTCMonth() + periodOffset, 1);
}
break;
case 'y':
if (this.rotAt) {
rotAt = Date.UTC(d.getUTCFullYear() + this.periodNum * periodOffset,
0, 1);
} else {
// First time: the start of the next year.
rotAt = Date.UTC(d.getUTCFullYear() + periodOffset, 0, 1);
}
break;
default:
assert.fail(format('invalid period scope: "%s"', this.periodScope));
}
if (this._debug()) {
this._debug(' **rotAt**: %s (utc: %s)', rotAt,
new Date(rotAt).toUTCString());
var now = Date.now();
this._debug(' now: %s (%sms == %smin == %sh to go)',
now,
rotAt - now,
(rotAt-now)/1000/60,
(rotAt-now)/1000/60/60);
}
return rotAt;
}...
lastModified = fileInfo.mtime.getTime();
}
catch (err) {
// file doesn't exist
}
var rotateAfterOpen = false;
if (lastModified) {
var lastRotTime = this._calcRotTime(0);
if (lastModified < lastRotTime) {
rotateAfterOpen = true;
}
}
// TODO: template support for backup files
// template: <path to which to rotate>
..._debug = function () {
// Set this to `true` to add debug logging.
if (false) {
if (arguments.length === 0) {
return true;
}
var args = Array.prototype.slice.call(arguments);
args[0] = '[' + (new Date().toISOString()) + ', '
+ this.path + '] ' + args[0];
console.log.apply(this, args);
} else {
return false;
}
}...
this.stream = fs.createWriteStream(this.path,
{flags: 'a', encoding: 'utf8'});
this.rotQueue = [];
this.rotating = false;
if (rotateAfterOpen) {
this._debug('rotateAfterOpen -> call rotate()');
this.rotate();
} else {
this._setupNextRot();
}
}
util.inherits(RotatingFileStream, EventEmitter);
..._setRotationTimer = function () {
var self = this;
var delay = this.rotAt - Date.now();
// Cap timeout to Node's max setTimeout, see
// <https://github.com/joyent/node/issues/8656>.
var TIMEOUT_MAX = 2147483647; // 2^31-1
if (delay > TIMEOUT_MAX) {
delay = TIMEOUT_MAX;
}
this.timeout = setTimeout(
function () {
self._debug('_setRotationTimer timeout -> call rotate()');
self.rotate();
},
delay);
if (typeof (this.timeout.unref) === 'function') {
this.timeout.unref();
}
}...
} else {
return false;
}
};
RotatingFileStream.prototype._setupNextRot = function () {
this.rotAt = this._calcRotTime(1);
this._setRotationTimer();
}
RotatingFileStream.prototype._setRotationTimer = function () {
var self = this;
var delay = this.rotAt - Date.now();
// Cap timeout to Node's max setTimeout, see
// <https://github.com/joyent/node/issues/8656>.
..._setupNextRot = function () {
this.rotAt = this._calcRotTime(1);
this._setRotationTimer();
}...
this.rotQueue = [];
this.rotating = false;
if (rotateAfterOpen) {
this._debug('rotateAfterOpen -> call rotate()');
this.rotate();
} else {
this._setupNextRot();
}
}
util.inherits(RotatingFileStream, EventEmitter);
RotatingFileStream.prototype._debug = function () {
// Set this to `true` to add debug logging.
...function destroy(s) {
this.stream.destroy();
}...
*/
Logger.prototype.reopenFileStreams = function () {
var self = this;
self.streams.forEach(function (s) {
if (s.type === 'file') {
if (s.stream) {
// Not sure if typically would want this, or more immediate
// `s.stream.destroy()`.
s.stream.end();
s.stream.destroySoon();
delete s.stream;
}
s.stream = fs.createWriteStream(s.path,
{flags: 'a', encoding: 'utf8'});
s.stream.on('error', function (err) {
...function destroySoon(s) {
this.stream.destroySoon();
}...
var self = this;
self.streams.forEach(function (s) {
if (s.type === 'file') {
if (s.stream) {
// Not sure if typically would want this, or more immediate
// `s.stream.destroy()`.
s.stream.end();
s.stream.destroySoon();
delete s.stream;
}
s.stream = fs.createWriteStream(s.path,
{flags: 'a', encoding: 'utf8'});
s.stream.on('error', function (err) {
self.emit('error', err, s);
});
...function end(s) {
this.stream.end();
}...
- perhaps this would be a "buffered: true" option on the stream object
- then wrap the "stream" with a local class that handles the buffering
- to finish this, need the 'log.close' and `process.on('exit', ...)`
work that Trent has started.
- "canWrite" handling for full streams. Need to buffer a la log4js
- test file log with logadm rotation: does it handle that?
- test suite:
- test for a cloned logger double-`stream.end()` causing problems.
Perhaps the "closeOnExit" for existing streams should be false for
clones.
- test that a `log.clone(...)` adding a new field matching a serializer
works *and* that an existing field in the parent is not *re-serialized*.
- split out `bunyan` cli to a "bunyan" or "bunyan-reader" or "node-bunyan-reader"
as the basis for tools to consume bunyan logs. It can grow indep of node-bunyan
for generating the logs.
...function rotate() {
// XXX What about shutdown?
var self = this;
// If rotation period is > ~25 days, we have to break into multiple
// setTimeout's. See <https://github.com/joyent/node/issues/8656>.
if (self.rotAt && self.rotAt > Date.now()) {
return self._setRotationTimer();
}
this._debug('rotate');
if (self.rotating) {
throw new TypeError('cannot start a rotation when already rotating');
}
self.rotating = true;
self.stream.end(); // XXX can do moves sync after this? test at high rate
function del() {
var toDel = self.path + '.' + String(n - 1);
if (n === 0) {
toDel = self.path;
}
n -= 1;
self._debug(' rm %s', toDel);
fs.unlink(toDel, function (delErr) {
//XXX handle err other than not exists
moves();
});
}
function moves() {
if (self.count === 0 || n < 0) {
return finish();
}
var before = self.path;
var after = self.path + '.' + String(n);
if (n > 0) {
before += '.' + String(n - 1);
}
n -= 1;
fs.exists(before, function (exists) {
if (!exists) {
moves();
} else {
self._debug(' mv %s %s', before, after);
mv(before, after, function (mvErr) {
if (mvErr) {
self.emit('error', mvErr);
finish(); // XXX finish here?
} else {
moves();
}
});
}
})
}
function finish() {
self._debug(' open %s', self.path);
self.stream = fs.createWriteStream(self.path,
{flags: 'a', encoding: 'utf8'});
var q = self.rotQueue, len = q.length;
for (var i = 0; i < len; i++) {
self.stream.write(q[i]);
}
self.rotQueue = [];
self.rotating = false;
self.emit('drain');
self._setupNextRot();
}
var n = this.count;
del();
}...
this.stream = fs.createWriteStream(this.path,
{flags: 'a', encoding: 'utf8'});
this.rotQueue = [];
this.rotating = false;
if (rotateAfterOpen) {
this._debug('rotateAfterOpen -> call rotate()');
this.rotate();
} else {
this._setupNextRot();
}
}
util.inherits(RotatingFileStream, EventEmitter);
...function write(s) {
if (this.rotating) {
this.rotQueue.push(s);
return false;
} else {
return this.stream.write(s);
}
}...
npm publish
.PHONY: docs
docs: toc
@[[ `which ronn` ]] || (echo "No 'ronn' on your PATH. Install with 'gem install ronn'" &&
; exit 2)
mkdir -p man/man1
ronn --style=toc --manual="bunyan manual" --date=$(shell git log -1 --pretty=format:%cd --date=short) --roff --html docs
/bunyan.1.ronn
python -c 'import sys; h = open("docs/bunyan.1.html").read(); h = h.replace(".mp dt.flush {float:left;width
:8ex}", ""); open("docs/bunyan.1.html", "w").write(
h)'
python -c 'import sys; h = open("docs/bunyan.1.html").read(); h = h.replace("</body>", "
;""<a href="https://github.com/trentm/node-bunyan"><img style="position: absolute; top:
0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="
;Fork me on GitHub"></a></body>"""); open("docs/bunyan.1.html", "w
x22;).write(h)'
@echo "# test with 'man ./docs/bunyan.1' and 'open ./docs/bunyan.1.html'"
# Re-generate the README.md table of contents.
toc:
./node_modules/.bin/markdown-toc -i README.md
...err = function (err) {
if (!err || !err.stack)
return err;
var obj = {
message: err.message,
name: err.name,
stack: getFullErrorStack(err),
code: err.code,
signal: err.signal
}
return obj;
}...
function mkRecord(log, minLevel, args) {
var excludeFields, fields, msgArgs;
if (args[0] instanceof Error) {
// `log.<level>(err, ...)`
fields = {
// Use this Logger's err serializer, if defined.
err: (log.serializers && log.serializers.err
? log.serializers.err(args[0])
: Logger.stdSerializers.err(args[0]))
};
excludeFields = {err: true};
if (args.length === 1) {
msgArgs = [fields.err.message];
} else {
msgArgs = args.slice(1);
...req = function (req) {
if (!req || !req.connection)
return req;
return {
method: req.method,
url: req.url,
headers: req.headers,
remoteAddress: req.connection.remoteAddress,
remotePort: req.connection.remotePort
};
// Trailers: Skipping for speed. If you need trailers in your app, then
// make a custom serializer.
//if (Object.keys(trailers).length > 0) {
// obj.trailers = req.trailers;
//}
}n/a
res = function (res) {
if (!res || !res.statusCode)
return res;
return {
statusCode: res.statusCode,
header: res._header
}
}n/a