/** * LOGGING CONFIG * * Usage: Logger.debug("I'm a debug message!"); Logger.info("OMG! Check this window out!", window); Logger.warn("Purple Alert! Purple Alert!"); Logger.error("HOLY SHI... no carrier."); // Only log WARN and ERROR messages. Logger.setLevel(Logger.WARN); Logger.debug("Donut machine is out of pink ones"); // Not a peep. Logger.warn("Asteroid detected!"); // yes show // Retrieve a named logger and store it for use. var myLogger = Logger.get('ModuleA'); myLogger.info("FizzWozz starting up"); // This logger instance can be configured independent of // all others (including the global one). myLogger.setLevel(Logger.WARN); // As it's the same instance being returned each time, you // don't have to store a reference: Logger.get('ModuleA').warn('FizzWozz combombulated!"); */ /*! * js-logger - http://github.com/jonnyreeves/js-logger * Jonny Reeves, http://jonnyreeves.co.uk/ * js-logger may be freely distributed under the MIT license. */ (function (global) { "use strict"; // Top level module for the global, static logger instance. var Logger = { }; // For those that are at home that are keeping score. Logger.VERSION = "1.0.0"; // Function which handles all incoming log messages. var logHandler; // Map of ContextualLogger instances by name; used by Logger.get() to return the same named instance. var contextualLoggersByNameMap = {}; // Polyfill for ES5's Function.bind. var bind = function(scope, func) { return function() { return func.apply(scope, arguments); }; }; // Super exciting object merger-matron 9000 adding another 100 bytes to your download. var merge = function () { var args = arguments, target = args[0], key, i; for (i = 1; i < args.length; i++) { for (key in args[i]) { if (!(key in target) && args[i].hasOwnProperty(key)) { target[key] = args[i][key]; } } } return target; }; // Helper to define a logging level object; helps with optimisation. var defineLogLevel = function(value, name) { return { value: value, name: name }; }; // Predefined logging levels. Logger.DEBUG = defineLogLevel(1, 'DEBUG'); Logger.INFO = defineLogLevel(2, 'INFO'); Logger.TIME = defineLogLevel(3, 'TIME'); Logger.WARN = defineLogLevel(4, 'WARN'); Logger.ERROR = defineLogLevel(8, 'ERROR'); Logger.OFF = defineLogLevel(99, 'OFF'); // Inner class which performs the bulk of the work; ContextualLogger instances can be configured independently // of each other. var ContextualLogger = function(defaultContext) { this.context = defaultContext; this.setLevel(defaultContext.filterLevel); this.log = this.info; // Convenience alias. }; ContextualLogger.prototype = { // Changes the current logging level for the logging instance. setLevel: function (newLevel) { // Ensure the supplied Level object looks valid. if (newLevel && "value" in newLevel) { this.context.filterLevel = newLevel; } }, // Is the logger configured to output messages at the supplied level? enabledFor: function (lvl) { var filterLevel = this.context.filterLevel; return lvl.value >= filterLevel.value; }, debug: function () { this.invoke(Logger.DEBUG, arguments); }, info: function () { this.invoke(Logger.INFO, arguments); }, warn: function () { this.invoke(Logger.WARN, arguments); }, warning: function () { this.invoke(Logger.WARN, arguments); }, error: function () { this.invoke(Logger.ERROR, arguments); }, time: function (label) { if (typeof label === 'string' && label.length > 0) { this.invoke(Logger.TIME, [ label, 'start' ]); } }, timeEnd: function (label) { if (typeof label === 'string' && label.length > 0) { this.invoke(Logger.TIME, [ label, 'end' ]); } }, // Invokes the logger callback if it's not being filtered. invoke: function (level, msgArgs) { if (logHandler && this.enabledFor(level)) { logHandler(msgArgs, merge({ level: level }, this.context)); } } }; // Protected instance which all calls to the to level `Logger` module will be routed through. var globalLogger = new ContextualLogger({ filterLevel: Logger.OFF }); // Configure the global Logger instance. (function() { // Shortcut for optimisers. var L = Logger; L.enabledFor = bind(globalLogger, globalLogger.enabledFor); L.debug = bind(globalLogger, globalLogger.debug); L.time = bind(globalLogger, globalLogger.time); L.timeEnd = bind(globalLogger, globalLogger.timeEnd); L.info = bind(globalLogger, globalLogger.info); L.warn = bind(globalLogger, globalLogger.warn); L.warning = bind(globalLogger, globalLogger.warning); L.error = bind(globalLogger, globalLogger.error); // Don't forget the convenience alias! L.log = L.info; }()); // Set the global logging handler. The supplied function should expect two arguments, the first being an arguments // object with the supplied log messages and the second being a context object which contains a hash of stateful // parameters which the logging function can consume. Logger.setHandler = function (func) { logHandler = func; }; // Sets the global logging filter level which applies to *all* previously registered, and future Logger instances. // (note that named loggers (retrieved via `Logger.get`) can be configured independently if required). Logger.setLevel = function(level) { // Set the globalLogger's level. globalLogger.setLevel(level); // Apply this level to all registered contextual loggers. for (var key in contextualLoggersByNameMap) { if (contextualLoggersByNameMap.hasOwnProperty(key)) { contextualLoggersByNameMap[key].setLevel(level); } } }; // Retrieve a ContextualLogger instance. Note that named loggers automatically inherit the global logger's level, // default context and log handler. Logger.get = function (name) { // All logger instances are cached so they can be configured ahead of use. return contextualLoggersByNameMap[name] || (contextualLoggersByNameMap[name] = new ContextualLogger(merge({ name: name }, globalLogger.context))); }; // Configure and example a Default implementation which writes to the `window.console` (if present). Logger.useDefaults = function(defaultLevel) { // Check for the presence of a logger. if (typeof console === "undefined") { return; } // Map of timestamps by timer labels used to track `#time` and `#timeEnd()` invocations in environments // that don't offer a native console method. var timerStartTimeByLabelMap = {}; // Support for IE8+ (and other, slightly more sane environments) var invokeConsoleMethod = function (hdlr, messages) { Function.prototype.apply.call(hdlr, console, messages); }; Logger.setLevel(defaultLevel || Logger.DEBUG); Logger.setHandler(function(messages, context) { var hdlr = console.log; // append INFO/DEBUG etc into the messages var levelPrefix = ((context.level.name+" ").toUpperCase()).substr(0,6); messages[0] = levelPrefix + messages[0]; // Prepend the logger's name to the log message for easy identification. if (context.name) { messages[0] = "[" + context.name + "] " + messages[0]; } if (context.level === Logger.TIME) { if (messages[1] === 'start') { if (console.time) { console.time(messages[0]); } else { timerStartTimeByLabelMap[messages[0]] = new Date().getTime(); } } else { if (console.timeEnd) { console.timeEnd(messages[0]); } else { invokeConsoleMethod(hdlr, [ messages[0] + ': ' + (new Date().getTime() - timerStartTimeByLabelMap[messages[0]]) + 'ms' ]); } } } else { // Delegate through to custom warn/error loggers if present on the console. if (context.level === Logger.WARN && console.warn) { hdlr = console.warn; } else if (context.level === Logger.ERROR && console.error) { hdlr = console.error; } else if (context.level === Logger.INFO && console.info) { hdlr = console.info; } invokeConsoleMethod(hdlr, messages); } }); }; // Export to popular environments boilerplate. if (typeof define === 'function' && define.amd) { define(Logger); } else if (typeof module !== 'undefined' && module.exports) { module.exports = Logger; } else { Logger._prevLogger = global.Logger; Logger.noConflict = function () { global.Logger = Logger._prevLogger; return Logger; }; global.Logger = Logger; } }(this)); // init defaults Logger.useDefaults();