diff --git a/conf/full.yaml b/conf/full.yaml index 9a2e3d3cf..1af6251e8 100644 --- a/conf/full.yaml +++ b/conf/full.yaml @@ -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: diff --git a/docs/logger.md b/docs/logger.md index b14c40a48..363b19016 100644 --- a/docs/logger.md +++ b/docs/logger.md @@ -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 diff --git a/package.json b/package.json index 4163e2528..1b25e9675 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/lib/logger.js b/src/lib/logger.js index d4be8e059..ac7748f1f 100644 --- a/src/lib/logger.js +++ b/src/lib/logger.js @@ -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 @@ -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 @@ -33,55 +45,77 @@ function setup(logs) { } logs.forEach(function(target) { + let level = target.level || 35; + if (level === 'http') { + level = 35; + } + // create a stream for each log configuration - const stream = new Stream(); - stream.writable = true; + 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!'); + } - 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'; - }; + 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}) + ); - 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); + streams.push( + { + type: 'raw', + level, + stream, + } + ); + } else { + 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`); + }; + } + + streams.push({ + type: 'raw', + level, + stream: stream, }); - } 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'); } - - 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)); - }; - } else { - stream.write = (obj) => { - dest.write(jsonPrint(obj)); - }; - } - - - if (target.level === 'http') target.level = 35; - streams.push({ - type: 'raw', - level: target.level || 35, - stream: stream, - }); }); // buyan default configuration diff --git a/yarn.lock b/yarn.lock index 98c2c95d7..2a6e9cc99 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6641,7 +6641,7 @@ mute-stream@0.0.7: 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: