simple-node-logger
Version:
A node console and file logger suitable for small, medium and large production projects.
327 lines (268 loc) • 9.1 kB
JavaScript
/**
* @class SimpleLogger
*
* @author: darryl.west@raincitysoftware.com
* @created: 2014-07-06
*/
const dash = require('lodash');
const Logger = require('./Logger');
const ConsoleAppender = require('./ConsoleAppender');
const FileAppender = require('./FileAppender');
const RollingFileAppender = require('./RollingFileAppender');
const SimpleLogger = function(opts) {
'use strict';
const options = Object.assign({}, opts);
const manager = this;
const domain = options.domain;
const appenders = options.appenders || [];
const loggers = options.loggers || [];
let dfltLevel = options.level || Logger.DEFAULT_LEVEL;
let loggerConfigFile = options.loggerConfigFile;
let refresh = options.refresh;
let fs = options.fs || require('fs');
let createInterval = options.createInterval || setInterval;
let minRefresh = options.minRefresh || 10 * 1000;
let errorEventName = options.errorEventName;
/**
* create a logger with optional category and level
*
* @param category
* @param level
* @returns Logger
*/
this.createLogger = function(category, level) {
const opts = Object.prototype.toString.call(category) === '[object String]' ? options : dash.merge({}, options, category);
opts.category = dash.isString(category) ? category : opts.category;
opts.level = level ? level : opts.level || dfltLevel;
opts.appenders = appenders;
if (errorEventName) {
opts.errorEventName = errorEventName;
}
const logger = new Logger(opts);
loggers.push(logger);
return logger;
};
/**
* create the console appender and add it to the appenders list
*
* @param opts - appender settings
* @returns ConsoleAppender -
*/
this.createConsoleAppender = function(opts) {
return manager.addAppender(new ConsoleAppender(Object.assign({}, opts)));
};
/**
* create a file appender and add it to the appenders list
*
* @param opts
* @returns a FileAppender object
*/
this.createFileAppender = function(opts) {
if (!opts) {
throw new Error('file appender must be created with log file path set in options');
}
return manager.addAppender(new FileAppender(opts));
};
/**
* create a rolling file appender and add it to the appender list
*
* @param opts
* @returns the appender
*/
this.createRollingFileAppender = function(opts) {
return manager.addAppender(new RollingFileAppender(opts));
};
/**
* add the appender to list
*
* @param appender
* @returns the new appender
*/
this.addAppender = function(appender) {
appenders.push(appender);
return appender;
};
this.getAppenders = function() {
return appenders;
};
this.getLoggers = function() {
return loggers;
};
/**
* start the refresh thread; minimum cycle time = 10 seconds...
*/
this.startRefreshThread = function() {
// TODO replace with watcher thread
if (fs.existsSync(loggerConfigFile) && dash.isNumber(refresh)) {
const t = Math.max(minRefresh, refresh);
createInterval(manager.readConfig, t);
}
};
/**
* set the level of all loggers to the specified level
*
* @param level - one of the know levels
*/
this.setAllLoggerLevels = function(level) {
loggers.forEach(function(logger) {
logger.setLevel(level);
});
};
/**
* read and parse the config file; change settings if required
*/
this.readConfig = function(completeCallback) {
// TODO refactor into configuration delegate to read stats and then process file only if stats change
const callback = (err, buf) => {
if (err) {
/*eslint no-console: "off"*/
console.log(err);
} else {
const conf = JSON.parse(buf.toString());
if (conf.appenders && conf.appenders.length > 0) {
// find each appender and set the level
conf.appenders.forEach(function(app) {
const level = app.level;
const appender = dash.find(appenders, (item) => {
if (item.getTypeName() === app.typeName && app.level) {
return item;
}
});
if (appender && typeof appender.setLevel === 'function') {
appender.setLevel(level);
}
});
}
if (conf.loggers && conf.loggers.length > 0) {
conf.loggers.forEach(item => {
if (item.category === 'all') {
manager.setAllLoggerLevels(item.level);
}
});
}
}
if (completeCallback) {
return completeCallback(err);
}
};
fs.readFile(loggerConfigFile, callback);
};
this.__protected = function() {
return {
domain: domain,
dfltLevel: dfltLevel,
refresh: refresh,
loggerConfigFile: loggerConfigFile
};
};
};
module.exports = SimpleLogger;
/**
* static convenience method to create a simple console logger; see options for details
*
* @param options - optional, if present then it could be 1) a string or 2) and object. if it's a string it's assumed
* to be the logFilePath; if it's a string or an object with logFilePath property, then a file appender is created.
*
* Valid options:
* - logFilePath : a path to the file appender
* - domain : the logger domain, e.g., machine or site id
* - dfltLevel : the default log level (overrides info level)
* - timestampFormat : the format used for log entries (see moment date formats for all possibilities)
*
* @returns Logger
*/
SimpleLogger.createSimpleLogger = function(options) {
'use strict';
let opts;
// if options is a string then it must be the
if (typeof options === 'string') {
opts = {
logFilePath: options
};
} else {
opts = Object.assign({}, options);
}
const manager = new SimpleLogger(opts);
// pass options in to change date formats, etc
manager.createConsoleAppender(opts);
if (opts.logFilePath) {
manager.createFileAppender(opts);
}
return manager.createLogger();
};
/**
* static convenience method to create a file logger (no console logging);
*
* @param options - if string then it's the logFilePath, else options with the logFilePath
* @returns Logger
*/
SimpleLogger.createSimpleFileLogger = function(options) {
'use strict';
if (!options) {
throw new Error('must create file logger with a logFilePath');
}
let opts;
// if options is a string then it must be the
if (typeof options === 'string') {
opts = {
logFilePath: options
};
} else {
opts = Object.assign({}, options);
}
const manager = new SimpleLogger(opts);
manager.createFileAppender(opts);
return manager.createLogger();
};
/**
* create a rolling file logger by passing options to SimpleLogger and Logger. this enables setting
* of domain, category, etc.
*
* @param options
* @returns rolling logger
*/
SimpleLogger.createRollingFileLogger = function(options) {
'use strict';
if (!options) {
throw new Error('createRollingFileLogger requires configuration options for this constructor');
}
let opts;
// read a dynamic config file if available
if (typeof options.readLoggerConfig === 'function') {
opts = options.readLoggerConfig();
opts.readLoggerConfig = options.readLoggerConfig;
} else {
opts = options;
}
const manager = new SimpleLogger(opts);
manager.createRollingFileAppender(opts);
if (opts.refresh && opts.loggerConfigFile) {
process.nextTick(manager.startRefreshThread);
}
return manager.createLogger(opts);
};
/**
* create a log manager
*
* @param options - file or rolling file specs;
*/
SimpleLogger.createLogManager = function(options) {
'use strict';
let opts;
// read a dynamic config file if available
if (options && typeof options.readLoggerConfig === 'function') {
opts = options.readLoggerConfig();
opts.readLoggerConfig = options.readLoggerConfig;
} else {
opts = Object.assign({}, options);
}
const manager = new SimpleLogger(opts);
if (opts.logDirectory && opts.fileNamePattern) {
manager.createRollingFileAppender(opts);
}
// create at least one appender
if (manager.getAppenders().length === 0) {
manager.createConsoleAppender(opts);
}
return manager;
};