Skip to content
This repository has been archived by the owner on Aug 30, 2021. It is now read-only.

Commit

Permalink
feat(logs): replacing unmaintained and vulnerable file-stream-rotator…
Browse files Browse the repository at this point in the history
… 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
  • Loading branch information
lirantal committed May 21, 2016
1 parent c61640b commit c8cbcd3
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 234 deletions.
19 changes: 6 additions & 13 deletions config/env/development.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
19 changes: 6 additions & 13 deletions config/env/production.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
23 changes: 8 additions & 15 deletions config/env/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
7 changes: 5 additions & 2 deletions config/lib/express.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var config = require('../config'),
flash = require('connect-flash'),
consolidate = require('consolidate'),
path = require('path'),
_ = require('lodash'),
lusca = require('lusca');

/**
Expand Down Expand Up @@ -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') {
Expand Down
189 changes: 113 additions & 76 deletions config/lib/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) : {};

// 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();
}
}
}
logger.setupFileLogger({});

return options;
}
module.exports = logger;
Loading

0 comments on commit c8cbcd3

Please sign in to comment.