All files / lib LoggingDecorator.js

28.7% Statements 31/108
16.67% Branches 8/48
19.23% Functions 5/26
29.7% Lines 30/101
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263      1x   1x                   1x             1x 1x 1x                                                             1x 1x 1x   1x 1x 1x 1x   1x         1x 1x 1x 1x     1x   1x             1x                                                                                                                                                                                             9x 9x   1x   1x 1x                                                                                                                                                 1x 1x 1x 1x  
'use strict';  // This clobbers what I am trying to do:  automate obtaining the context of the error.  :(
 
let logConfig,
    debugEnabled = true,
    layoutCommands,                                     // Supported symbols and handlers
    LOG_LEVELS = Object.freeze({                        // Supported log levels
        FATAL:      1,
        ERROR:      2,
        WARN:       3,
        INFO:       4,
        DEBUG:      5,
        LOG:        6,
        TRACE:      7,
        ASSERT:     8
    }),
    LOG_STYLES = Object.freeze({                        // Supported log colors
        FATAL: "background:yellow:bold; color:red",
        ERROR: "color:red",
        WARN: "color:yellow",
        INFO: "color:green",
        DEBUG: "color:blue"
    }),
    _ = require("underscore"),
    defaultDateFormat = 'yyyy/MM/dd HH:mm:ss,SSS',
    dateFormat = require("dateformat");
 
 
//=======================================================================Functions
function justify(s, len){
    let str = [];
    len = parseInt(len);
    while(len-- > 0){
        str.push(" ");
    }
    str.push(s);
    return str.join("");
}
 
 
 
function LogEvent(context, caller, message) {
    this.name = caller.name;
    //this.location = caller.caller ? caller.caller.name : context.callee.caller.name;
    this.location = '';  // Damn you, strict mode!
    this.datetime = new Date();
    this.message = message;
    this.data = null;
    if (typeof message === 'string') {
        this.message = "";
        this.data = message;
    }
 
}
 
function findConfig(name){
    Iif (logConfig[name]) return logConfig[name];
    let packages = name.split(".") || [],
        path = packages.concat([]),
        newName;
    for (let i = 0; i < packages.length; i++){
        path.pop();
        newName = logConfig[path.join(".")];
        Iif (newName) return newName;
    }
    return null;
}
 
 
function getLogLevel(logger) {
    Iif (!debugEnabled) return false;
    Eif (!logger.logLevel) {
        let level = -1, prop = findConfig(logger.subject);
        Iif (prop) {
            level = LOG_LEVELS[prop.toUpperCase()] || -1;
        }
        logger.logLevel = level;
    }
    return logger.logLevel;
}
 
/*
 Supported Layout symbols and their handlers.  Theoretically, we could let developers add handlers for symbols,
 or override existing ones, but that is not supported yet.  Supporting it, however, would be easy now.
 */
layoutCommands = {
    JUSTIFY: {
        value: /%-[0-9]+/g,
        execute: function (format, logger) {
            let num;
            (format.match(this.value) || []).forEach(function (match) {
                num = match.replace("%-", "");
                format = format.replace("%-" + num, justify("", num));
            });
            return format;
        }
    },
    CLASSNAME: {
        value: /%M/g,
        execute: function (format, logger) {
            return format.replace(this.value, logger.subject);
        }
    },
    MESSAGE: {
        value: /%m/g,
        execute: function (format, logger) {
            return format.replace(this.value, logger.logEvent.message);
        }
    },
    LOCATION: {
        value: /%l/g,
        execute: function (format, logger) {
            return format.replace(this.value, logger.logEvent.location);
        }
    },
    EVENT: {
        value: /%p/g,
        execute: function (format, logger) {
            return format.replace(this.value, logger.logEvent.name);
        }
    },
    DATETIME: {
        value: /(%d\{[0-9A-Za-z/\-,\.:\s]+\})|%d/g,
        execute: function (format, logger) {
            let datePattern = format.match(this.value), v = this.value;
            if (datePattern) {
                datePattern.forEach(function (df) {
                    df = df.replace(/%d|\{|\}/g, "");
                    format = format.replace(v, dateFormat(logger.logEvent.datetime, (df || defaultDateFormat), true));
                });
            }
            return format;
        }
    },
    NEWLINE: {
        value: /%n/g,
        execute: function (format, logger) {
            return format.replace(this.value, "\n");
        }
    },
    FILENAME: {
        value: /%F/g,
        execute: function (format, logger) {
            let hits = format.match(this.value);
            if (!hits) return format;
            return format.replace(this.value, logger.fileName);
        }
    }
};
 
 
/*
 Replaces the original un-formatted log message with the formatted message.
 */
function fixArguments(that, args) {
    const logEvent = that.logger.logEvent;
    args = [...args];
    //args = Array.prototype.slice.call(args);
    //args.shift(); // Removing the original un-formatted log message from the argument list.
 
    // Prepending the newly formatted log message to the argument list.
    if (logEvent.data) {
        args = [that.format(), logEvent.data].concat(args);
    } else {
        args = [that.format()].concat(args);
    }
 
    return args;
}
 
 
/**
 * Decorates any logger, enhancing its functionality.
 *
 * @param logger
 * @param options
 * @constructor
 * @implements {Logger}
 */
function LoggingDecorator(logger, options) {
    options = options || {};
    this.logger = _.extend({}, logger, options);
}
LoggingDecorator.prototype = {
    log: function LOG(msg, ...varargs) {
        const logger = this.logger;
        Iif (getLogLevel(logger) >= LOG_LEVELS.LOG) {
            logger.logEvent = new LogEvent(varargs, LOG, msg);
            logger.log(...fixArguments(this, varargs));
        }
    },
    info: function INFO(msg, ...varargs) {
        const logger = this.logger;
        if (getLogLevel(logger) >= LOG_LEVELS.INFO) {
            logger.logEvent = new LogEvent(varargs, INFO, msg);
            logger.info(...fixArguments(this, varargs));
        }
    },
    error: function ERROR(msg, ...varargs) {
        const logger = this.logger;
        if (getLogLevel(logger) >= LOG_LEVELS.ERROR) {
            logger.logEvent = new LogEvent(varargs, ERROR, msg);
            logger.error(...fixArguments(this, varargs));
        }
    },
    debug: function DEBUG(msg, ...varargs) {
        const logger = this.logger;
        if (getLogLevel(logger) >= LOG_LEVELS.DEBUG) {
            logger.logEvent = new LogEvent(varargs, DEBUG, msg);
            logger.debug(...fixArguments(this, varargs));
        }
    },
    warn: function WARN(msg, ...varargs) {
        const logger = this.logger;
        if (getLogLevel(logger) >= LOG_LEVELS.WARN) {
            logger.logEvent = new LogEvent(varargs, WARN, msg);
            logger.warn(...fixArguments(this, varargs));
        }
    },
    /*
     Trace is not required by ILogger interface.
     */
    trace: function TRACE(msg, ...varargs) {
        const logger = this.logger;
        if (getLogLevel(logger) >= LOG_LEVELS.TRACE && typeof logger.trace === 'function') {
            logger.logEvent = new LogEvent(varargs, TRACE, msg);
            logger.trace(...fixArguments(this, varargs));
        }
    },
    /*
     Assert is not required by ILogger interface.
     */
    assert: function ASSERT(msg, ...varargs) {
        const logger = this.logger;
        if (getLogLevel(logger) >= LOG_LEVELS.ASSERT && typeof logger.assert === 'function') {
            logger.logEvent = new LogEvent(varargs, ASSERT, msg);
            logger.assert(this.format());
        }
    },
    /**
     *
     * @return {String}
     */
    format: function() {
        let format = logConfig.pattern;
        const logger = this.logger;
 
        // Replacing each symbol found in the pattern with the corresponding log event values
        format = layoutCommands.JUSTIFY.execute(format, this.logger);
        /* JUSTIFY must come first, but the order
         of iteration below is not guaranteed,
         so I call it first. */
        _.each(layoutCommands, function (cmd, key) {
            if (key !== "JUSTIFY") format = cmd.execute(format, logger);
        });
        return format;
    }
};
 
module.exports = function(config){
    logConfig = config;
    debugEnabled = true;
    return LoggingDecorator;
};