(function(JLog){ var anonymousLoggerName = "[anonymous]"; var defaultLoggerName = "[default]"; var rootLoggerName = "root"; var loggers = {}; var loggerNames = []; var rootLogger = new Logger(rootLoggerName); var rootLoggerDefaultLevel = JLog.Level.DEBUG; rootLogger.setLevel(rootLoggerDefaultLevel); JLog.getRootLogger = function() { return rootLogger; }; var defaultLogger = null; JLog.getDefaultLogger = function() { if (!defaultLogger) defaultLogger = JLog.getLogger(defaultLoggerName); return defaultLogger; }; /* Class: Logger Manager of logged events. */ function Logger(name) { this.parent = null; this.children = []; var _name = name; var _appenders = []; var _currentLevel = null; var _isRoot = (_name === rootLoggerName); var _enabled = true; var _appenderCache = null; var _appenderCacheInvalidated = false; this.turnOn = function() { _enabled = true; }; this.turnOff = function() { _enabled = false; }; this.isOn = function() { return _enabled; }; this.getName = function() { return _name; } var _additive = true; /* Method: getAdditivity Get if log events are propagated higher in logger hierarchy. Returns: If events should be propagated higher. See Also: <setAdditivity> */ this.getAdditivity = function() { return _additive; }; /* Method: setAdditivity Set if log events are propagated higher in logger hierarchy. Parameters: additivity - if events should be propagated higher. See Also: <getAdditivity> */ this.setAdditivity = function(additivity) { var valueChanged = (_additive != additivity); _additive = additivity; if(valueChanged) this._invalidateAppenderCache(); }; /* Method: addChild Makes given logger a child of current logger. If child is additive, all it's events will go through this logger either. Parameters: childLogger - given logger. */ this.addChild = function(childLogger) { this.children.push(childLogger); childLogger.parent = this; childLogger._invalidateAppenderCache(); }; /* Method: addAppender Push an appender for this logger. Parameters: appender - appender to push. See Also: <removeAppender> <removeAllAppenders> */ this.addAppender = function(appender) { if(_appenders.indexOf(appender) >= 0) return; _appenders.push(appender); this._invalidateAppenderCache(); }; /* Method: removeAppender Remove appender out of this logger. Parameters: appender - appender to remove. See Also: <addAppender> <removeAllAppenders> */ this.removeAppender = function(appender) { _appenders.splice(_appenders.indexOf(appender), 1); this._invalidateAppenderCache(); }; /* Method: removeAllAppender Remove all appenders from this logger. See Also: <addAppender> <removeAppender> */ this.removeAllAppenders = function() { _appenders = []; this._invalidateAppenderCache(); }; /* Method: getEffectiveAppenders Returns all appender which will log given message. */ this.getEffectiveAppenders = function() { if (_appenderCache === null || _appenderCacheInvalidated) { // Build appender cache var parentEffectiveAppenders = (_isRoot || !this.getAdditivity()) ? [] : this.parent.getEffectiveAppenders(); _appenderCache = _.union(parentEffectiveAppenders, _appenders); _appenderCacheInvalidated = false; } return _appenderCache; }; this._invalidateAppenderCache = function() { _appenderCacheInvalidated = true; _.each(this.children, function(c) { c._invalidateAppenderCache(); }); }; /* Method: log Publish logger message across all the appenders! Prefer using specialized function instead. Parameters: level - <Level> of message event params - array of message parts. first parameter is usually a simple message. */ this.log = function(level, params) { try { if(!(this.isOn() && level.isGreaterOrEqual(this._getEffectiveLevel()))) return; params = Array.prototype.slice.call(params || []); var exception; var finalParamIndex = params.length - 1; var lastParam = params[finalParamIndex]; if (params.length > 1 && (lastParam instanceof Error)) { exception = lastParam; --finalParamIndex; } var messages = finalParamIndex >= 0 ? params.slice(0, finalParamIndex + 1) : []; var logEvent = new JLog.LoggingEvent(this, new Date(), level, messages, exception); this._callAppenders(logEvent); } catch(loggerError) { JLog.handleError(loggerError); // TODO deal with errors inside logger - user of the logger should not know about such a bugs, unless explicitly // requested this! } }; this._callAppenders = function(logEvent) { _.each(this.getEffectiveAppenders(), function(app) { app.doAppend(logEvent); }); }; /* Method: setLevel Set minimal level of log messages to populate. Parameters: level - minimal <Level> */ this.setLevel = function(level) { if(!(level instanceof JLog.Level)) throw new Error("Logger.setLevel: please use JLog.Level to set Level"); // Having a level of null on the root logger would be very bad. if(_isRoot && level === null) throw new Error("Logger.setLevel: you cannot set the level of the root logger to null"); _currentLevel = level; }; /* Method: getLevel Get minimal level of log messages to populate. */ this.getLevel = function() { return _currentLevel; }; this._getEffectiveLevel = function() { for(var logger = this; logger !== null; logger = logger.parent) { var level = logger.getLevel(); if(level !== null) return level; } }; this.toString = function() { return "Logger[" + this.getName() + "]"; }; }; Logger.prototype = { /* Method: debug Attempt to publish <Level.DEBUG> message. */ debug: function() { this.log(JLog.Level.DEBUG, arguments); }, /* Method: info Attempt to publish <Level.INFO> message. */ info: function() { this.log(JLog.Level.INFO, arguments); }, /* Method: warn Attempt to publish <Level.WARN> message. */ warn: function() { this.log(JLog.Level.WARN, arguments); }, /* Method: error Attempt to publish <Level.ERROR> message. */ error: function() { this.log(JLog.Level.ERROR, arguments); }, /* Method: fatal Attempt to publish <Level.FATAL> message. */ fatal: function() { this.log(JLog.Level.FATAL, arguments); }, /* Method: isEnabledFor Checks if logger will publish message of given <Level>. */ isEnabledFor: function(level) { return level >= this._getEffectiveLevel(); }, /* Method: isDebugEnabled Checks if logger will publish message of <Level.DEBUG>. */ isDebugEnabled: function() { return this.isEnabledFor(JLog.Level.DEBUG); }, /* Method: isInfoEnabled Checks if logger will publish message of <Level.INFO>. */ isInfoEnabled: function() { return this.isEnabledFor(JLog.Level.INFO); }, /* Method: isWarnEnabled Checks if logger will publish message of <Level.WARN>. */ isWarnEnabled: function() { return this.isEnabledFor(JLog.Level.WARN); }, /* Method: isErrorEnabled Checks if logger will publish message of <Level.ERROR>. */ isErrorEnabled: function() { return this.isEnabledFor(JLog.Level.ERROR); }, /* Method: isFatalEnabled Checks if logger will publish message of <Level.FATAL>. */ isFatalEnabled: function() { return this.isEnabledFor(JLog.Level.FATAL); } }; /* Method: getLogger Returns instance of logger for given hierarchy. Parameters: loggerName - dot separated logger name. Returns: <Logger> for given name. */ JLog.getLogger = function(loggerName) { // Use anonymous logger if loggerName is not specified or invalid if (!(typeof loggerName == "string")) loggerName = anonymousLoggerName; // Do not allow retrieval of the root logger by name if (loggerName == rootLoggerName) throw new Error("JLog.getLogger: root logger may not be obtained by name"); // Create the logger for this name if it doesn't already exist if (!loggers[loggerName]) { var logger = new Logger(loggerName); loggers[loggerName] = logger; loggerNames.push(loggerName); // Set up parent logger, if it doesn't exist var lastDotIndex = loggerName.lastIndexOf("."); var parentLogger; if(lastDotIndex > -1) { var parentLoggerName = loggerName.substring(0, lastDotIndex); parentLogger = JLog.getLogger(parentLoggerName); // Recursively sets up parents etc. } else parentLogger = rootLogger; parentLogger.addChild(logger); } return loggers[loggerName]; }; })(JLog);