mirror of
https://github.com/taobataoma/meanTorrent.git
synced 2026-03-04 03:01:02 +01:00
feat(logs): replacing unmaintained and vulnerable file-stream-rotator package with winston log facility (#1334)
* replacing file-stream-rotator with a better logging mechanism using winston which can be extended later for other use cases and integrations * refactoring logger mechanism, accomodating for tests and environment variable configurations * only enabling morgan logger if config.log.format option was defined, and disabling the app.log file transport option for the test environment * disabling all kind of logging when in test enviroment
This commit is contained in:
19
config/env/development.js
vendored
19
config/env/development.js
vendored
@@ -16,19 +16,12 @@ module.exports = {
|
||||
// logging with Morgan - https://github.com/expressjs/morgan
|
||||
// Can specify one of 'combined', 'common', 'dev', 'short', 'tiny'
|
||||
format: 'dev',
|
||||
options: {
|
||||
// Stream defaults to process.stdout
|
||||
// Uncomment/comment to toggle the logging to a log on the file system
|
||||
// stream: {
|
||||
// directoryPath: process.cwd(),
|
||||
// fileName: 'access.log',
|
||||
// rotatingLogs: { // for more info on rotating logs - https://github.com/holidayextras/file-stream-rotator#usage
|
||||
// active: false, // activate to use rotating logs
|
||||
// fileName: 'access-%DATE%.log', // if rotating logs are active, this fileName setting will be used
|
||||
// frequency: 'daily',
|
||||
// verbose: false
|
||||
// }
|
||||
// }
|
||||
fileLogger: {
|
||||
directoryPath: process.cwd(),
|
||||
fileName: 'app.log',
|
||||
maxsize: 10485760,
|
||||
maxFiles: 2,
|
||||
json: false
|
||||
}
|
||||
},
|
||||
app: {
|
||||
|
||||
19
config/env/production.js
vendored
19
config/env/production.js
vendored
@@ -22,19 +22,12 @@ module.exports = {
|
||||
// logging with Morgan - https://github.com/expressjs/morgan
|
||||
// Can specify one of 'combined', 'common', 'dev', 'short', 'tiny'
|
||||
format: process.env.LOG_FORMAT || 'combined',
|
||||
options: {
|
||||
// Stream defaults to process.stdout
|
||||
// Uncomment/comment to toggle the logging to a log on the file system
|
||||
stream: {
|
||||
directoryPath: process.env.LOG_DIR_PATH || process.cwd(),
|
||||
fileName: process.env.LOG_FILE || 'access.log',
|
||||
rotatingLogs: { // for more info on rotating logs - https://github.com/holidayextras/file-stream-rotator#usage
|
||||
active: process.env.LOG_ROTATING_ACTIVE === 'true', // activate to use rotating logs
|
||||
fileName: process.env.LOG_ROTATING_FILE || 'access-%DATE%.log', // if rotating logs are active, this fileName setting will be used
|
||||
frequency: process.env.LOG_ROTATING_FREQUENCY || 'daily',
|
||||
verbose: process.env.LOG_ROTATING_VERBOSE === 'true'
|
||||
}
|
||||
}
|
||||
fileLogger: {
|
||||
directoryPath: process.env.LOG_DIR_PATH || process.cwd(),
|
||||
fileName: process.env.LOG_FILE || 'app.log',
|
||||
maxsize: 10485760,
|
||||
maxFiles: 2,
|
||||
json: false
|
||||
}
|
||||
},
|
||||
facebook: {
|
||||
|
||||
23
config/env/test.js
vendored
23
config/env/test.js
vendored
@@ -15,21 +15,14 @@ module.exports = {
|
||||
log: {
|
||||
// logging with Morgan - https://github.com/expressjs/morgan
|
||||
// Can specify one of 'combined', 'common', 'dev', 'short', 'tiny'
|
||||
format: process.env.LOG_FORMAT || 'combined',
|
||||
options: {
|
||||
// Stream defaults to process.stdout
|
||||
// Uncomment/comment to toggle the logging to a log on the file system
|
||||
stream: {
|
||||
directoryPath: process.cwd(),
|
||||
fileName: 'access.log',
|
||||
rotatingLogs: { // for more info on rotating logs - https://github.com/holidayextras/file-stream-rotator#usage
|
||||
active: false, // activate to use rotating logs
|
||||
fileName: 'access-%DATE%.log', // if rotating logs are active, this fileName setting will be used
|
||||
frequency: 'daily',
|
||||
verbose: false
|
||||
}
|
||||
}
|
||||
}
|
||||
// format: 'dev'
|
||||
// fileLogger: {
|
||||
// directoryPath: process.cwd(),
|
||||
// fileName: 'app.log',
|
||||
// maxsize: 10485760,
|
||||
// maxFiles: 2,
|
||||
// json: false
|
||||
// }
|
||||
},
|
||||
port: process.env.PORT || 3001,
|
||||
app: {
|
||||
|
||||
@@ -18,6 +18,7 @@ var config = require('../config'),
|
||||
flash = require('connect-flash'),
|
||||
consolidate = require('consolidate'),
|
||||
path = require('path'),
|
||||
_ = require('lodash'),
|
||||
lusca = require('lusca');
|
||||
|
||||
/**
|
||||
@@ -68,8 +69,10 @@ module.exports.initMiddleware = function (app) {
|
||||
// Initialize favicon middleware
|
||||
app.use(favicon(app.locals.favicon));
|
||||
|
||||
// Enable logger (morgan)
|
||||
app.use(morgan(logger.getFormat(), logger.getOptions()));
|
||||
// Enable logger (morgan) if enabled in the configuration file
|
||||
if (_.has(config, 'log.format')) {
|
||||
app.use(morgan(logger.getLogFormat(), logger.getMorganOptions()));
|
||||
}
|
||||
|
||||
// Environment dependent middleware
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
|
||||
@@ -3,27 +3,128 @@
|
||||
var _ = require('lodash'),
|
||||
config = require('../config'),
|
||||
chalk = require('chalk'),
|
||||
fileStreamRotator = require('file-stream-rotator'),
|
||||
fs = require('fs');
|
||||
fs = require('fs'),
|
||||
winston = require('winston');
|
||||
|
||||
// list of valid formats for the logging
|
||||
var validFormats = ['combined', 'common', 'dev', 'short', 'tiny'];
|
||||
|
||||
// build logger service
|
||||
var logger = {
|
||||
getFormat: getLogFormat, // log format to use
|
||||
getOptions: getLogOptions // log options to use
|
||||
// Instantiating the default winston application logger with the Console
|
||||
// transport
|
||||
var logger = new winston.Logger({
|
||||
transports: [
|
||||
new winston.transports.Console({
|
||||
level: 'info',
|
||||
colorize: true,
|
||||
showLevel: true,
|
||||
handleExceptions: true,
|
||||
humanReadableUnhandledException: true
|
||||
})
|
||||
],
|
||||
exitOnError: false
|
||||
});
|
||||
|
||||
// A stream object with a write function that will call the built-in winston
|
||||
// logger.info() function.
|
||||
// Useful for integrating with stream-related mechanism like Morgan's stream
|
||||
// option to log all HTTP requests to a file
|
||||
logger.stream = {
|
||||
write: function(msg) {
|
||||
logger.info(msg);
|
||||
}
|
||||
};
|
||||
|
||||
// export the logger service
|
||||
module.exports = logger;
|
||||
/**
|
||||
* Instantiate a winston's File transport for disk file logging
|
||||
*
|
||||
* @param logger a valid winston logger object
|
||||
*/
|
||||
logger.setupFileLogger = function setupFileLogger(options) {
|
||||
|
||||
var fileLoggerTransport = this.getLogOptions();
|
||||
if (!fileLoggerTransport) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// Check first if the configured path is writable and only then
|
||||
// instantiate the file logging transport
|
||||
if (fs.openSync(fileLoggerTransport.filename, 'a+')) {
|
||||
logger.add(winston.transports.File, fileLoggerTransport);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
console.log();
|
||||
console.log(chalk.red('An error has occured during the creation of the File transport logger.'));
|
||||
console.log(chalk.red(err));
|
||||
console.log();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The options to use with winston logger
|
||||
*
|
||||
* Returns a Winston object for logging with the File transport
|
||||
*/
|
||||
logger.getLogOptions = function getLogOptions(configOptions) {
|
||||
|
||||
var _config = _.clone(config, true);
|
||||
if (configOptions) {
|
||||
_config = configOptions;
|
||||
}
|
||||
|
||||
var configFileLogger = _config.log.fileLogger;
|
||||
|
||||
if (!_.has(_config, 'log.fileLogger.directoryPath') || !_.has(_config, 'log.fileLogger.fileName')) {
|
||||
console.log('unable to find logging file configuration');
|
||||
return false;
|
||||
}
|
||||
|
||||
var logPath = configFileLogger.directoryPath + '/' + configFileLogger.fileName;
|
||||
|
||||
return {
|
||||
level: 'debug',
|
||||
colorize: false,
|
||||
filename: logPath,
|
||||
timestamp: true,
|
||||
maxsize: configFileLogger.maxsize ? configFileLogger.maxsize : 10485760,
|
||||
maxFiles: configFileLogger.maxFiles ? configFileLogger.maxFiles : 2,
|
||||
json: (_.has(configFileLogger, 'json')) ? configFileLogger.json : false,
|
||||
eol: '\n',
|
||||
tailable: true,
|
||||
showLevel: true,
|
||||
handleExceptions: true,
|
||||
humanReadableUnhandledException: true
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The options to use with morgan logger
|
||||
*
|
||||
* Returns a log.options object with a writable stream based on winston
|
||||
* file logging transport (if available)
|
||||
*/
|
||||
logger.getMorganOptions = function getMorganOptions() {
|
||||
|
||||
return {
|
||||
stream: logger.stream
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The format to use with the logger
|
||||
*
|
||||
* Returns the log.format option set in the current environment configuration
|
||||
*/
|
||||
function getLogFormat () {
|
||||
logger.getLogFormat = function getLogFormat() {
|
||||
var format = config.log && config.log.format ? config.log.format.toString() : 'combined';
|
||||
|
||||
// make sure we have a valid format
|
||||
@@ -38,72 +139,8 @@ function getLogFormat () {
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The options to use with the logger
|
||||
*
|
||||
* Returns the log.options object set in the current environment configuration.
|
||||
* NOTE: Any options, requiring special handling (e.g. 'stream'), that encounter an error will be removed from the options.
|
||||
*/
|
||||
function getLogOptions () {
|
||||
var options = config.log && config.log.options ? _.clone(config.log.options, true) : {};
|
||||
logger.setupFileLogger({});
|
||||
|
||||
// check if the current environment config has the log stream option set
|
||||
if (_.has(options, 'stream')) {
|
||||
|
||||
try {
|
||||
|
||||
// check if we need to use rotating logs
|
||||
if (_.has(options, 'stream.rotatingLogs') && options.stream.rotatingLogs.active) {
|
||||
|
||||
if (options.stream.rotatingLogs.fileName.length && options.stream.directoryPath.length) {
|
||||
|
||||
// ensure the log directory exists
|
||||
if (!fs.existsSync(options.stream.directoryPath)) {
|
||||
fs.mkdirSync(options.stream.directoryPath);
|
||||
}
|
||||
|
||||
options.stream = fileStreamRotator.getStream({
|
||||
filename: options.stream.directoryPath + '/' + options.stream.rotatingLogs.fileName,
|
||||
frequency: options.stream.rotatingLogs.frequency,
|
||||
verbose: options.stream.rotatingLogs.verbose
|
||||
});
|
||||
|
||||
} else {
|
||||
// throw a new error so we can catch and handle it gracefully
|
||||
throw new Error('An invalid fileName or directoryPath was provided for the rotating logs option.');
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// create the WriteStream to use for the logs
|
||||
if (options.stream.fileName.length && options.stream.directoryPath.length) {
|
||||
|
||||
// ensure the log directory exists
|
||||
if (!fs.existsSync(options.stream.directoryPath)) {
|
||||
fs.mkdirSync(options.stream.directoryPath);
|
||||
}
|
||||
|
||||
options.stream = fs.createWriteStream(options.stream.directoryPath + '/' + config.log.options.stream.fileName, { flags: 'a' });
|
||||
} else {
|
||||
// throw a new error so we can catch and handle it gracefully
|
||||
throw new Error('An invalid fileName or directoryPath was provided for stream option.');
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
// remove the stream option
|
||||
delete options.stream;
|
||||
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
console.log();
|
||||
console.log(chalk.red('An error has occured during the creation of the WriteStream. The stream option has been omitted.'));
|
||||
console.log(chalk.red(err));
|
||||
console.log();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
module.exports = logger;
|
||||
|
||||
@@ -426,53 +426,55 @@ describe('Configuration Tests:', function () {
|
||||
});
|
||||
|
||||
it('should retrieve the log format from the logger configuration', function () {
|
||||
|
||||
config.log = {
|
||||
format: 'tiny'
|
||||
};
|
||||
|
||||
var format = logger.getFormat();
|
||||
var format = logger.getLogFormat();
|
||||
format.should.be.equal('tiny');
|
||||
});
|
||||
|
||||
it('should retrieve the log options from the logger configuration', function () {
|
||||
config.log = {
|
||||
options: {
|
||||
_test_log_option_: 'testing'
|
||||
}
|
||||
};
|
||||
it('should retrieve the log options from the logger configuration for a valid stream object', function () {
|
||||
|
||||
var options = logger.getMorganOptions();
|
||||
|
||||
options.should.be.instanceof(Object);
|
||||
options.should.have.property('stream');
|
||||
|
||||
var options = logger.getOptions();
|
||||
should.deepEqual(options, config.log.options);
|
||||
});
|
||||
|
||||
it('should verify that a writable stream was created using the logger configuration', function () {
|
||||
it('should verify that a file logger object was created using the logger configuration', function () {
|
||||
var _dir = process.cwd();
|
||||
var _filename = 'unit-test-access.log';
|
||||
|
||||
config.log = {
|
||||
options: {
|
||||
stream: {
|
||||
directoryPath: _dir,
|
||||
fileName: _filename
|
||||
}
|
||||
fileLogger: {
|
||||
directoryPath: _dir,
|
||||
fileName: _filename
|
||||
}
|
||||
};
|
||||
|
||||
var options = logger.getOptions();
|
||||
options.stream.writable.should.equal(true);
|
||||
var fileTransport = logger.getLogOptions(config);
|
||||
fileTransport.should.be.instanceof(Object);
|
||||
fileTransport.filename.should.equal(_dir + '/' + _filename);
|
||||
});
|
||||
|
||||
it('should use the default log format of "combined" when an invalid format was provided', function () {
|
||||
|
||||
var _logger = require(path.resolve('./config/lib/logger'));
|
||||
|
||||
// manually set the config log format to be invalid
|
||||
config.log = {
|
||||
format: '_some_invalid_format_'
|
||||
};
|
||||
|
||||
var format = logger.getFormat();
|
||||
var format = _logger.getLogFormat();
|
||||
format.should.be.equal('combined');
|
||||
});
|
||||
|
||||
it('should remove the stream option when an invalid filename was supplied for the log stream option', function () {
|
||||
it('should not create a file transport object if critical options are missing: filename', function () {
|
||||
|
||||
// manually set the config stream fileName option to an empty string
|
||||
config.log = {
|
||||
format: 'combined',
|
||||
@@ -484,112 +486,25 @@ describe('Configuration Tests:', function () {
|
||||
}
|
||||
};
|
||||
|
||||
var options = logger.getOptions();
|
||||
should.not.exist(options.stream);
|
||||
var fileTransport = logger.getLogOptions(config);
|
||||
fileTransport.should.be.false();
|
||||
});
|
||||
|
||||
it('should remove the stream option when an invalid directory path was supplied for the log stream option', function () {
|
||||
it('should not create a file transport object if critical options are missing: directory', function () {
|
||||
|
||||
// manually set the config stream fileName option to an empty string
|
||||
config.log = {
|
||||
format: 'combined',
|
||||
options: {
|
||||
stream: {
|
||||
directoryPath: '',
|
||||
fileName: 'test.log'
|
||||
fileName: 'app.log'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var options = logger.getOptions();
|
||||
should.not.exist(options.stream);
|
||||
});
|
||||
|
||||
it('should confirm that the log directory is created if it does not already exist', function () {
|
||||
var _dir = process.cwd() + '/temp-logs';
|
||||
var _filename = 'unit-test-access.log';
|
||||
|
||||
// manually set the config stream fileName option to an empty string
|
||||
config.log = {
|
||||
format: 'combined',
|
||||
options: {
|
||||
stream: {
|
||||
directoryPath: _dir,
|
||||
fileName: _filename
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var options = logger.getOptions();
|
||||
options.stream.writable.should.equal(true);
|
||||
});
|
||||
|
||||
it('should remove the stream option when an invalid filename was supplied for the rotating log stream option', function () {
|
||||
// enable rotating logs
|
||||
config.log = {
|
||||
format: 'combined',
|
||||
options: {
|
||||
stream: {
|
||||
directoryPath: process.cwd(),
|
||||
rotatingLogs: {
|
||||
active: true,
|
||||
fileName: '',
|
||||
frequency: 'daily',
|
||||
verbose: false
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var options = logger.getOptions();
|
||||
should.not.exist(options.stream);
|
||||
});
|
||||
|
||||
it('should confirm that the rotating log is created using the logger configuration', function () {
|
||||
var _dir = process.cwd();
|
||||
var _filename = 'unit-test-rotating-access-%DATE%.log';
|
||||
|
||||
// enable rotating logs
|
||||
config.log = {
|
||||
format: 'combined',
|
||||
options: {
|
||||
stream: {
|
||||
directoryPath: _dir,
|
||||
rotatingLogs: {
|
||||
active: true,
|
||||
fileName: _filename,
|
||||
frequency: 'daily',
|
||||
verbose: false
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var options = logger.getOptions();
|
||||
should.exist(options.stream.write);
|
||||
});
|
||||
|
||||
it('should confirm that the rotating log directory is created if it does not already exist', function () {
|
||||
var _dir = process.cwd() + '/temp-rotating-logs';
|
||||
var _filename = 'unit-test-rotating-access-%DATE%.log';
|
||||
|
||||
// enable rotating logs
|
||||
config.log = {
|
||||
format: 'combined',
|
||||
options: {
|
||||
stream: {
|
||||
directoryPath: _dir,
|
||||
rotatingLogs: {
|
||||
active: true,
|
||||
fileName: _filename,
|
||||
frequency: 'daily',
|
||||
verbose: false
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var options = logger.getOptions();
|
||||
should.exist(options.stream.write);
|
||||
var fileTransport = logger.getLogOptions(config);
|
||||
fileTransport.should.be.false();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -40,12 +40,12 @@
|
||||
"glob": "~7.0.0",
|
||||
"grunt": "0.4.5",
|
||||
"grunt-cli": "~0.1.13",
|
||||
"gulp-wiredep": "~0.0.0",
|
||||
"helmet": "~1.3.0",
|
||||
"jasmine-core": "~2.4.1",
|
||||
"lodash": "~4.6.1",
|
||||
"lusca": "~1.3.0",
|
||||
"method-override": "~2.3.5",
|
||||
"gulp-wiredep": "~0.0.0",
|
||||
"mocha": "~2.4.5",
|
||||
"mongoose": "~4.4.8",
|
||||
"morgan": "~1.7.0",
|
||||
@@ -64,7 +64,8 @@
|
||||
"serve-favicon": "~2.3.0",
|
||||
"socket.io": "~1.4.5",
|
||||
"swig": "~1.4.2",
|
||||
"validator": "~5.1.0"
|
||||
"validator": "~5.1.0",
|
||||
"winston": "^2.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"coveralls": "~2.11.6",
|
||||
|
||||
Reference in New Issue
Block a user