/** * Created by vitali on 21/06/14. */ var winston = require('winston'); var _ = require("lodash"); require('winston-mongodb'); require('winston-nodemail'); var Levels = {silent : -Infinity, error: 0, warn: 1, info: 2, verbose: 3, debug: 4, silly: 5}; var MEGABYTE = 1048576; var Transports = { Console : function (spec) { return new (winston.transports.Console)(_.extend({ level : "silly", timestamp : true, datePattern : 'YYYY-MM-DD HH:mm:ss.SS', prettyPrint : true, colorize : false, showLevel : false, handleExceptions : false }, spec)); }, File : function (spec) { return new (winston.transports.File)(_.extend({ level : "silly", timestamp: true, filename: 'logs/default.log', json: false, maxsize: (MEGABYTE * 10), maxFiles: 30, tailable: true, colorize: true, showLevel: false, prettyPrint: false, zippedArchive: true, handleExceptions: true }, spec)); }, MongoDB : function (spec) { return new (winston.transports.MongoDB)(_.extend({ level : "info", db: 'mongodb://localhost:27017/cooldb-logs', collection: 'log-error', storeHost: true, capped: true, cappedSize: (MEGABYTE * 50) }, spec)); }, Mail : function (spec) { return new (winston.transports.Mail)(_.extend({ level: 'error', to: 'vitali.pe@gmail.com', from: 'dev@coolautomation.com', username: 'dev@coolautomation.com', host: 'smtp.coolautomation.com', port: '465', ssl: true, subject: "[CoolRemote Logger] [{{level}}] [{{msg}}]", html: true, unique: true, silent: true }, spec)); } }; // config singleton var config = { _config : null, _root : "app", _sailsRoot : "sails", generatePrefixString : function(prefix) { if (_(prefix).isEmpty()) prefix = [config._root]; var path = _(prefix) .map(config.get) .where({ignorePrefix : false}) .pluck("name") .map(function (m) { return "[" + m +"]"}) .value().join(""); return (path.length > 0) ? path + ": " : ""; }, findConfig : function (prefix, key) { if (_(prefix).isEmpty()) return config.get(config._root)[key]; prefix = _.clone(prefix); return (config.get(prefix.pop())[key] || config.findConfig(prefix, key)); }, get : function (name) { if (config._config === null) throw new Error("Dude why the fuck are you trying to use the logger before it was initialized!?"); return (_.has(config._config, name)) ? config._config[name] : { name : name, ignorePrefix : false}; }, setup : function (settings , root, sailsRoot) { var cfg = {}; var result = function (val) {return _.isFunction(val)? val() : val}; config._root = (root || config._root); config._sailsRoot = (sailsRoot || config._sailsRoot); _.each(settings, function (val, name) { var setting ={ name : name, ignorePrefix : (val.ignorePrefix === true), level : val.level }; // create new engine ? if (!_(val.transports).isEmpty()) setting.engine = new (winston.Logger)({ exitOnError: false, transports : val.transports.map(result) }); // default engine & level for root if (name === config._root) _.defaults(setting, { engine : new (winston.Logger)({exitOnError: false, transports : [Transports.Console()]}), level : "info" }); if (setting.level && _(Levels[setting.level]).isUndefined()) throw new Error("Unknown logging level: " + setting.level); cfg[name] = setting; }); if (!_(cfg).has(config._root)) throw new Error("Missing logger root config!"); if (!_(cfg).has(config._sailsRoot)) throw new Error("Missing logger sails config!"); if (!cfg[config._sailsRoot].engine) throw new Error("Missing transports in sails config!"); if (!cfg[config._sailsRoot].level) throw new Error("Missing logging level in sails config!"); config._config = cfg; return config; }, generateSailsLoggerConfig : function () { var sailsConfig = config.get(config._sailsRoot); return { colors: false, json: false, custom: createLogger([config._sailsRoot]), level: sailsConfig.level } } }; var createLogger = function (prefix) { prefix = (prefix || []); var _factory = function(name) { return createLogger((prefix || []).concat(name))}; var _handle = function(target) { var message = _.toArray(arguments); var engine = config.findConfig(prefix, "engine"); var level = config.findConfig(prefix, "level"); if (Levels[target] > Levels[level]) return; message[0] = config.generatePrefixString(prefix); return engine[target].apply(engine, message); }; // add public methods ["silly", "verbose", "debug", "info", "warn", "error"] .forEach(function (name) { _factory[name] = _handle.bind(null, name)}); // log works like we want, except for the shorthand version :( _factory.log = function (target) { if (_(Levels).has(target)) return _handle.apply(null, arguments); var args = _.toArray(arguments); args.unshift("info"); return _handle.apply(null, args); }; return _factory; }; module.exports = { logger : createLogger(), Transports : Transports, setupAndGenerateSailsConfig : function (cfg) { return config.setup(cfg).generateSailsLoggerConfig()} }; /** * logger * * Example usage: * * var config = { * "parent" : { * ignorePrefix : true, * transports : [Transports.Console, Transports.File] * }, * "child" : { level : "silent"} * } * * // call setup before you can use the logger * logging.setup(config, {root : "parent"}) * * topLevel = logging.logger; * child = topLevel("child"); * grandchild = child("grand"); // if no config is passed, it will use parent's transports * * topLevel.info("hey!"); // "hey!" - if ignorePrefix is false -> "[app]: hey!" * child.info("hey!"); // nothing... because level is silent in config... * grandchild.info("hey!"); // "[child][grand]: hey!" * */