Daniel@0: /*! Daniel@0: * js-logger - http://github.com/jonnyreeves/js-logger Daniel@0: * Jonny Reeves, http://jonnyreeves.co.uk/ Daniel@0: * js-logger may be freely distributed under the MIT license. Daniel@0: */ Daniel@0: Daniel@0: /*jshint sub:true*/ Daniel@0: /*global console:true,define:true, module:true*/ Daniel@0: (function (global) { Daniel@0: "use strict"; Daniel@0: Daniel@0: // Top level module for the global, static logger instance. Daniel@0: var Logger = { }; Daniel@0: Daniel@0: // For those that are at home that are keeping score. Daniel@0: Logger.VERSION = "0.9.14"; Daniel@0: Daniel@0: // Function which handles all incoming log messages. Daniel@0: var logHandler; Daniel@0: Daniel@0: // Map of ContextualLogger instances by name; used by Logger.get() to return the same named instance. Daniel@0: var contextualLoggersByNameMap = {}; Daniel@0: Daniel@0: // Polyfill for ES5's Function.bind. Daniel@0: var bind = function(scope, func) { Daniel@0: return function() { Daniel@0: return func.apply(scope, arguments); Daniel@0: }; Daniel@0: }; Daniel@0: Daniel@0: // Super exciting object merger-matron 9000 adding another 100 bytes to your download. Daniel@0: var merge = function () { Daniel@0: var args = arguments, target = args[0], key, i; Daniel@0: for (i = 1; i < args.length; i++) { Daniel@0: for (key in args[i]) { Daniel@0: if (!(key in target) && args[i].hasOwnProperty(key)) { Daniel@0: target[key] = args[i][key]; Daniel@0: } Daniel@0: } Daniel@0: } Daniel@0: return target; Daniel@0: }; Daniel@0: Daniel@0: // Helper to define a logging level object; helps with optimisation. Daniel@0: var defineLogLevel = function(value, name) { Daniel@0: return { value: value, name: name }; Daniel@0: }; Daniel@0: Daniel@0: // Predefined logging levels. Daniel@0: Logger.DEBUG = defineLogLevel(1, 'DEBUG'); Daniel@0: Logger.INFO = defineLogLevel(2, 'INFO'); Daniel@0: Logger.WARN = defineLogLevel(4, 'WARN'); Daniel@0: Logger.ERROR = defineLogLevel(8, 'ERROR'); Daniel@0: Logger.OFF = defineLogLevel(99, 'OFF'); Daniel@0: Daniel@0: // Inner class which performs the bulk of the work; ContextualLogger instances can be configured independently Daniel@0: // of each other. Daniel@0: var ContextualLogger = function(defaultContext) { Daniel@0: this.context = defaultContext; Daniel@0: this.setLevel(defaultContext.filterLevel); Daniel@0: this.log = this.info; // Convenience alias. Daniel@0: }; Daniel@0: Daniel@0: ContextualLogger.prototype = { Daniel@0: // Changes the current logging level for the logging instance. Daniel@0: setLevel: function (newLevel) { Daniel@0: // Ensure the supplied Level object looks valid. Daniel@0: if (newLevel && "value" in newLevel) { Daniel@0: this.context.filterLevel = newLevel; Daniel@0: } Daniel@0: }, Daniel@0: Daniel@0: // Is the logger configured to output messages at the supplied level? Daniel@0: enabledFor: function (lvl) { Daniel@0: var filterLevel = this.context.filterLevel; Daniel@0: return lvl.value >= filterLevel.value; Daniel@0: }, Daniel@0: Daniel@0: debug: function () { Daniel@0: this.invoke(Logger.DEBUG, arguments); Daniel@0: }, Daniel@0: Daniel@0: info: function () { Daniel@0: this.invoke(Logger.INFO, arguments); Daniel@0: }, Daniel@0: Daniel@0: warn: function () { Daniel@0: this.invoke(Logger.WARN, arguments); Daniel@0: }, Daniel@0: Daniel@0: error: function () { Daniel@0: this.invoke(Logger.ERROR, arguments); Daniel@0: }, Daniel@0: Daniel@0: // Invokes the logger callback if it's not being filtered. Daniel@0: invoke: function (level, msgArgs) { Daniel@0: if (logHandler && this.enabledFor(level)) { Daniel@0: logHandler(msgArgs, merge({ level: level }, this.context)); Daniel@0: } Daniel@0: } Daniel@0: }; Daniel@0: Daniel@0: // Protected instance which all calls to the to level `Logger` module will be routed through. Daniel@0: var globalLogger = new ContextualLogger({ filterLevel: Logger.OFF }); Daniel@0: Daniel@0: // Configure the global Logger instance. Daniel@0: (function() { Daniel@0: // Shortcut for optimisers. Daniel@0: var L = Logger; Daniel@0: Daniel@0: L.enabledFor = bind(globalLogger, globalLogger.enabledFor); Daniel@0: L.debug = bind(globalLogger, globalLogger.debug); Daniel@0: L.info = bind(globalLogger, globalLogger.info); Daniel@0: L.warn = bind(globalLogger, globalLogger.warn); Daniel@0: L.error = bind(globalLogger, globalLogger.error); Daniel@0: Daniel@0: // Don't forget the convenience alias! Daniel@0: L.log = L.info; Daniel@0: }()); Daniel@0: Daniel@0: // Set the global logging handler. The supplied function should expect two arguments, the first being an arguments Daniel@0: // object with the supplied log messages and the second being a context object which contains a hash of stateful Daniel@0: // parameters which the logging function can consume. Daniel@0: Logger.setHandler = function (func) { Daniel@0: logHandler = func; Daniel@0: }; Daniel@0: Daniel@0: // Sets the global logging filter level which applies to *all* previously registered, and future Logger instances. Daniel@0: // (note that named loggers (retrieved via `Logger.get`) can be configured independently if required). Daniel@0: Logger.setLevel = function(level) { Daniel@0: // Set the globalLogger's level. Daniel@0: globalLogger.setLevel(level); Daniel@0: Daniel@0: // Apply this level to all registered contextual loggers. Daniel@0: for (var key in contextualLoggersByNameMap) { Daniel@0: if (contextualLoggersByNameMap.hasOwnProperty(key)) { Daniel@0: contextualLoggersByNameMap[key].setLevel(level); Daniel@0: } Daniel@0: } Daniel@0: }; Daniel@0: Daniel@0: // Retrieve a ContextualLogger instance. Note that named loggers automatically inherit the global logger's level, Daniel@0: // default context and log handler. Daniel@0: Logger.get = function (name) { Daniel@0: // All logger instances are cached so they can be configured ahead of use. Daniel@0: return contextualLoggersByNameMap[name] || Daniel@0: (contextualLoggersByNameMap[name] = new ContextualLogger(merge({ name: name }, globalLogger.context))); Daniel@0: }; Daniel@0: Daniel@0: // Configure and example a Default implementation which writes to the `window.console` (if present). Daniel@0: Logger.useDefaults = function(defaultLevel) { Daniel@0: // Check for the presence of a logger. Daniel@0: if (typeof console === "undefined") { Daniel@0: return; Daniel@0: } Daniel@0: Daniel@0: Logger.setLevel(defaultLevel || Logger.DEBUG); Daniel@0: Logger.setHandler(function(messages, context) { Daniel@0: var hdlr = console.log; Daniel@0: Daniel@0: // Prepend the logger's name to the log message for easy identification. Daniel@0: if (context.name) { Daniel@0: messages[0] = "[" + context.name + "] " + messages[0]; Daniel@0: } Daniel@0: Daniel@0: // Delegate through to custom warn/error loggers if present on the console. Daniel@0: if (context.level === Logger.WARN && console.warn) { Daniel@0: hdlr = console.warn; Daniel@0: } else if (context.level === Logger.ERROR && console.error) { Daniel@0: hdlr = console.error; Daniel@0: } else if (context.level === Logger.INFO && console.info) { Daniel@0: hdlr = console.info; Daniel@0: } Daniel@0: Daniel@0: // Support for IE8+ (and other, slightly more sane environments) Daniel@0: Function.prototype.apply.call(hdlr, console, messages); Daniel@0: }); Daniel@0: }; Daniel@0: Daniel@0: // Export to popular environments boilerplate. Daniel@0: if (typeof define === 'function' && define.amd) { Daniel@0: define(Logger); Daniel@0: } Daniel@0: else if (typeof module !== 'undefined' && module.exports) { Daniel@0: module.exports = Logger; Daniel@0: } Daniel@0: else { Daniel@0: Logger._prevLogger = global.Logger; Daniel@0: Daniel@0: Logger.noConflict = function () { Daniel@0: global.Logger = Logger._prevLogger; Daniel@0: return Logger; Daniel@0: }; Daniel@0: Daniel@0: global.Logger = Logger; Daniel@0: } Daniel@0: }(this));