Skip to content

Commit

Permalink
feat: exposed rotating-file log for json logging (#948)
Browse files Browse the repository at this point in the history
This both allows for logger type 'rotating-file' and
passing of other options from the config "option" example:
{type: rotating-file, format: json, path: /path/to/log.jsonl, level: http, options: {period: 1d}}
  • Loading branch information
mlucool authored and juanpicado committed Aug 31, 2018
1 parent d0798c4 commit 5ca0ca5
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 46 deletions.
3 changes: 2 additions & 1 deletion conf/full.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ packages:
#
# parameters for file: name is filename
# {type: 'file', path: 'verdaccio.log', level: 'debug'},
#
# Rotating log stream. Options are passed directly to bunyan. See: https://github.com/trentm/node-bunyan#stream-type-rotating-file
# {type: rotating-file, format: json, path: /path/to/log.jsonl, level: http, options: {period: 1d}}
# parameters for stdout and stderr: format: json | pretty | pretty-timestamped
# {type: 'stdout', format: 'pretty', level: 'debug'},
logs:
Expand Down
3 changes: 3 additions & 0 deletions docs/logger.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ logs:
- {type: stdout, format: pretty, level: http}
# file output
- {type: file, path: verdaccio.log, level: info}
# Rotating log stream. Options are passed directly to bunyan. See: https://github.com/trentm/node-bunyan#stream-type-rotating-file
- {type: rotating-file, format: json, path: /path/to/log.jsonl, level: http, options: {period: 1d}}
```
Use `SIGUSR2` to notify the application, the log-file was rotated and it needs to reopen it.
Note: Rotating log stream is not supported in cluster mode. [See here](https://github.com/trentm/node-bunyan#stream-type-rotating-file)

### Configuration

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"mime": "2.3.1",
"minimatch": "3.0.4",
"mkdirp": "0.5.1",
"mv": "2.1.1",
"pkginfo": "0.4.1",
"request": "2.87.0",
"semver": "5.5.0",
Expand Down
122 changes: 78 additions & 44 deletions src/lib/logger.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
const cluster = require('cluster');
const Logger = require('bunyan');
const Error = require('http-errors');
const Stream = require('stream');
const chalk = require('chalk');
const Utils = require('./utils');
const pkgJSON = require('../../package.json');
const _ = require('lodash');

/**
* Match the level based on buyan severity scale
Expand All @@ -22,6 +24,16 @@ function getlvl(x) {
}
}

/**
* A RotatingFileStream that modifes the message first
*/
class VerdaccioRotatingFileStream extends Logger.RotatingFileStream { // We depend on mv so that this is there
write(obj) {
const msg = fillInMsgTemplate(obj.msg, obj, false);
super.write(JSON.stringify({...obj, msg}, Logger.safeCycles()) + '\n');
}
}

/**
* Setup the Buyan logger
* @param {*} logs list of log configuration
Expand All @@ -33,55 +45,77 @@ function setup(logs) {
}

logs.forEach(function(target) {
// create a stream for each log configuration
const stream = new Stream();
stream.writable = true;

let dest;
let destIsTTY = false;
const prettyPrint = (obj) => print(obj.level, obj.msg, obj, destIsTTY) + '\n';
const prettyTimestampedPrint = (obj) => obj.time.toISOString() + print(obj.level, obj.msg, obj, destIsTTY) + '\n';
const jsonPrint = (obj) => {
const msg = fillInMsgTemplate(obj.msg, obj, destIsTTY);
return JSON.stringify({...obj, msg}, Logger.safeCycles()) + '\n';
};

if (target.type === 'file') {
// destination stream
dest = require('fs').createWriteStream(target.path, {flags: 'a', encoding: 'utf8'});
dest.on('error', function(err) {
Logger.emit('error', err);
});
} else if (target.type === 'stdout' || target.type === 'stderr') {
dest = target.type === 'stdout' ? process.stdout : process.stderr;
destIsTTY = dest.isTTY;
} else {
throw Error('wrong target type for a log');
let level = target.level || 35;
if (level === 'http') {
level = 35;
}

if (target.format === 'pretty') {
// making fake stream for prettypritting
stream.write = (obj) => {
dest.write(prettyPrint(obj));
};
} else if (target.format === 'pretty-timestamped') {
// making fake stream for prettypritting
stream.write = (obj) => {
dest.write(prettyTimestampedPrint(obj));
};
// create a stream for each log configuration
if (target.type === 'rotating-file') {
if (target.format !== 'json') {
throw new Error('Rotating file streams only work with JSON!');
}
if (cluster.isWorker) {
// https://github.com/trentm/node-bunyan#stream-type-rotating-file
throw new Error('Cluster mode is not supported for rotating-file!');
}

const stream = new VerdaccioRotatingFileStream(
_.merge({},
// Defaults can be found here: https://github.com/trentm/node-bunyan#stream-type-rotating-file
target.options || {},
{path: target.path, level})
);

streams.push(
{
type: 'raw',
level,
stream,
}
);
} else {
stream.write = (obj) => {
dest.write(jsonPrint(obj));
};
}
const stream = new Stream();
stream.writable = true;

let destination;
let destinationIsTTY = false;
if (target.type === 'file') {
// destination stream
destination = require('fs').createWriteStream(target.path, {flags: 'a', encoding: 'utf8'});
destination.on('error', function(err) {
stream.emit('error', err);
});
} else if (target.type === 'stdout' || target.type === 'stderr') {
destination = target.type === 'stdout' ? process.stdout : process.stderr;
destinationIsTTY = destination.isTTY;
} else {
throw Error('wrong target type for a log');
}

if (target.format === 'pretty') {
// making fake stream for prettypritting
stream.write = (obj) => {
destination.write(`${print(obj.level, obj.msg, obj, destinationIsTTY)}\n`);
};
} else if (target.format === 'pretty-timestamped') {
// making fake stream for pretty pritting
stream.write = (obj) => {
destination.write(`${obj.time.toISOString()}${print(obj.level, obj.msg, obj, destinationIsTTY)}\n`);
};
} else {
stream.write = (obj) => {
const msg = fillInMsgTemplate(obj.msg, obj, destinationIsTTY);
destination.write(`${JSON.stringify({...obj, msg}, Logger.safeCycles())}\n`);
};
}

if (target.level === 'http') target.level = 35;
streams.push({
type: 'raw',
level: target.level || 35,
stream: stream,
});
streams.push({
type: 'raw',
level,
stream: stream,
});
}
});

// buyan default configuration
Expand Down
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6641,7 +6641,7 @@ [email protected]:
version "0.0.7"
resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"

mv@~2:
mv@^2.1.1, mv@~2:
version "2.1.1"
resolved "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz#ae6ce0d6f6d5e0a4f7d893798d03c1ea9559b6a2"
dependencies:
Expand Down

0 comments on commit 5ca0ca5

Please sign in to comment.