1 /*!
  2  * Wef logger
  3  * Copyright (c) 2011 Pablo Escalada
  4  * MIT Licensed
  5  */
  6 (function (wef) {
  7 
  8     var LOGLEVEL,textFormatter,registered = {},lastLogger,failSafeIndentation = false,logger,filteredLogs = 0;
  9 
 10     /**
 11      * @namespace  Log level constants
 12      */
 13     LOGLEVEL = {
 14         /**
 15          * @lends LOGLEVEL
 16          */
 17         /**
 18          * Max verbosity
 19          */
 20         all:-1,
 21         /**
 22          * Noisy
 23          */
 24         trace:1,
 25         /**
 26          * Useful for testing
 27          */
 28         debug:2,
 29         /**
 30          * Log and more important messages
 31          */
 32         log:2,
 33         /**
 34          * Info and more important messages
 35          */
 36         info:3,
 37         /**
 38          * Warn and error messages
 39          */
 40         warn:4,
 41         /**
 42          * Only error message
 43          */
 44         error:5,
 45         /**
 46          * Minimum verbosity. Zero logs
 47          */
 48         none:100
 49     }
 50 
 51     /**
 52      * Create a textFormatter
 53      *
 54      * @class Plain text logger formatter
 55      */
 56     textFormatter = function () {
 57         return this;
 58     };
 59 
 60     textFormatter.prototype = {
 61 
 62         /**
 63          * Formats logger messages
 64          * @param {Array-like}messages logger messages
 65          * @param {integer}indentationLevel indentation level
 66          * @param {string}type message type
 67          */
 68         format:function (messages, indentationLevel, type) {
 69             var tmp = [], levelMarks = "                                                                                            ", levelText, typeText;
 70             tmp = Array.prototype.slice.call(messages, tmp);
 71             if (failSafeIndentation && indentationLevel) {
 72                 levelText = levelMarks.slice(0, indentationLevel);
 73                 tmp.unshift(levelText);
 74             }
 75             if (type) {
 76                 typeText = "[" + type + "]";
 77                 tmp.unshift(typeText);
 78             }
 79             return tmp;
 80         }
 81     };
 82 
 83     /**
 84      * Creates a logger
 85      *
 86      * @param {string}[logName=default] Logger name
 87      *
 88      * @class Console logger withs vitamins.
 89      * </p>
 90      * Features:
 91      * <ul>
 92      *     <li>Named loggers</li>
 93      *     <li>Filters</li>
 94      *     <li>Failsafe logging functions</li>
 95      * </ul>
 96      */
 97     logger = function (logName) {
 98         var tmpLogger;
 99         if (!logName || logName === "") {
100             logName = "default";
101         }
102         lastLogger = logName;
103         if (registered[lastLogger]) {
104             return registered[lastLogger].logger;
105         } else {
106             tmpLogger = new logger.prototype.init(lastLogger);
107             registered[lastLogger] = {
108                 logLevel:LOGLEVEL.all,
109                 indentationLevel:0,
110                 logger:tmpLogger
111             };
112             return tmpLogger;
113         }
114     };
115 
116     logger.prototype = {
117         constructor:logger,
118         /**
119          * Log level
120          */
121         loglevel: LOGLEVEL,
122         /**
123          * Version number
124          */
125         version:"0.2.0",
126         /**
127          * Logger formatter. Currently a plain text formatter
128          */
129         formatter:new textFormatter(),
130         /**
131          * @ignore
132          */
133         init:function (logName) {
134             this.logName = logName;
135             return this;
136         },
137         /**
138          * Gets the number of filtered log messages. Only for testing purposes
139          * @returns {integer} Number of Filtered messages
140          */
141         _filteredLogs:function () {
142             return filteredLogs;
143         },
144         /**
145          * Gets the indentation level of the specified logger. Only for testing
146          * purposes
147          *
148          * @param {string}logName logger name
149          * @returns {integer} indentation level
150          */
151         _getIndentLevel:function (logName) {
152             return registered[logName].indentationLevel;
153         },
154         /**
155          * Filter current loggers by name and priority level.
156          * </p>
157          * Only log entries from matched loggers and priority > filter level are
158          * allowed. Filtered logs are lost.
159          *
160          * @param {Object|string} options filter options.
161          * </p>
162          * There are two shortcuts :
163          * <ul>
164          *     <li>"all": activates all loggers (logLevel: -1, pattern: ".*")</li>
165          *     <li>"none": deactivates all loggers (logLevel: 100, pattern: ".*")</li>
166          * </ul>
167          * @param {integer} options.logLevel Priority level
168          * @param {string} options.pattern Pattern that matches against current
169          * registered loggers. Pattern must be regExp compatible.
170          * */
171         filter:function (options) {
172             var name, regExp, logLevel;
173             if (!options) {
174                 return this;
175             }
176 
177             if (options == "none" || options == "off") {
178                 options = {logLevel:LOGLEVEL.none, pattern:".*"};
179             }
180 
181             if (options == "all" || options == "on") {
182                 options = {logLevel:LOGLEVEL.all, pattern:".*"};
183             }
184 
185             if (!options.logLevel || typeof options.logLevel != "number" || !options.pattern || typeof options.pattern != "string") {
186                 //do nothing
187                 return this;
188             }
189             regExp = new RegExp(options.pattern);
190             logLevel = options.logLevel;
191 
192             for (name in registered) {
193                 if (regExp.test(name)) {
194                     registered[name].logLevel = logLevel;
195                 } else {
196                     registered[name].logLevel = LOGLEVEL.none;
197                 }
198             }
199             filteredLogs = 0;
200             return this;
201         }
202     };
203 
204     /**
205      * Extension point
206      */
207     logger.fn = logger.prototype;
208 
209     /**
210      * @namespace Output object. Currently window.console
211      * </p>
212      * Redefining backend allows logs redirection
213      * </p>
214      */
215     logger.prototype.backend = window.console || {};
216 
217     /**
218      * FailSafe output. Currently unused.
219      * </p> window.console its the best option, alert messages are too intrusive
220      */
221     logger.prototype.backend.failSafe = function () {
222         //silent
223     };
224 
225     /**
226      * FailSafe grouping activation
227      */
228     logger.prototype.backend.failSafeGroup = function () {
229         failSafeIndentation = true;
230     };
231 
232     /**
233      * FailSafe ungrouping activation
234      */
235     logger.prototype.backend.failSafeGroupEnd = function () {
236         failSafeIndentation = true;
237     };
238 
239     /**
240      * trace backend
241      * @function
242      */
243     logger.prototype.backend.trace = window.console.trace || logger.prototype.backend.log;
244     /**
245      * log backend
246      * @function
247      */
248     logger.prototype.backend.log = window.console.log || logger.prototype.backend.failSafe;
249     /**
250      * debug backend
251      * @function
252      */
253     logger.prototype.backend.debug = window.console.debug || logger.prototype.backend.log;
254     /**
255      * info backend
256      * @function
257      */
258     logger.prototype.backend.info = window.console.info || logger.prototype.backend.log;
259     /**
260      * warn backend
261      * @function
262      */
263     logger.prototype.backend.warn = window.console.warn || logger.prototype.backend.log;
264     /**
265      * error backend
266      * @function
267      */
268     logger.prototype.backend.error = window.console.error || logger.prototype.backend.log;
269     /**
270      * group backend
271      * @function
272      */
273     logger.prototype.backend.group = window.console.group || logger.prototype.backend.failSafeGroup;
274     /**
275      * groupCollapsed backend
276      * @function
277      */
278     logger.prototype.backend.groupCollapsed = window.console.groupCollapsed || window.console.group || logger.prototype.backend.failSafeGroup;
279     /**
280      * groupEnd backend
281      * @function
282      */
283     logger.prototype.backend.groupEnd = window.console.groupEnd || logger.prototype.backend.failSafeGroupEnd;
284 
285     logger.prototype.init.prototype = logger.prototype;
286 
287     //TODO: refactor using wef.extend
288 
289     logger.prototype.init.prototype.debug =
290     /**
291      * Logs messages of logLevel=debug
292      * @param {string}message
293      * @param {string}[messages] more messages, comma separated
294      * @memberOf logger#
295      * @name debug
296      * @function
297      */
298     function (message) {
299         if (registered[lastLogger].logLevel > LOGLEVEL.debug) {
300             filteredLogs++;
301             return this;
302         }
303         //crossBrowser support
304         if (Function.prototype.bind && console && typeof console.debug == "object") {
305             var debug = Function.prototype.bind.call(console.debug, console);
306             debug.apply(console, this.formatter.format(arguments, registered[lastLogger].indentationLevel));
307             return this;
308         } else {
309             this.backend.debug.apply(this.backend, this.formatter.format(arguments, registered[lastLogger].indentationLevel));
310             return this;
311         }
312     };
313 
314     logger.prototype.init.prototype.error =
315     /**
316      * Logs messages of logLevel=error
317      * @param {string}message
318      * @param {string}[messages] more messages, comma separated
319      * @memberOf logger#
320      * @name error
321      * @function
322      */
323     function (message) {
324         if (registered[lastLogger].logLevel > LOGLEVEL.error) {
325             filteredLogs++;
326             return this;
327         }
328         if (Function.prototype.bind && console && typeof console.error == "object") {
329             var error = Function.prototype.bind.call(console.error, console);
330             error.apply(console, this.formatter.format(arguments, registered[lastLogger].indentationLevel));
331             return this;
332         } else {
333             this.backend.error.apply(this.backend, this.formatter.format(arguments, registered[lastLogger].indentationLevel));
334             return this;
335         }
336     };
337 
338     logger.prototype.init.prototype.info =
339     /**
340      * Logs messages of logLevel=info
341      * @param {string}message
342      * @param {string}[messages] more messages, comma separated
343      * @memberOf logger#
344      * @name info
345      * @function
346      */
347     function (message) {
348         if (registered[lastLogger].logLevel > LOGLEVEL.info) {
349             filteredLogs++;
350             return this;
351         }
352         if (Function.prototype.bind && console && typeof console.info == "object") {
353             var info = Function.prototype.bind.call(console.info, console);
354             info.apply(console, this.formatter.format(arguments, registered[lastLogger].indentationLevel));
355             return this;
356         } else {
357             this.backend.info.apply(this.backend, this.formatter.format(arguments, registered[lastLogger].indentationLevel));
358             return this;
359         }
360     };
361 
362     logger.prototype.init.prototype.warn =
363     /**
364      * Logs messages of logLevel=warn
365      * @param {string}message
366      * @param {string}[messages] more messages, comma separated
367      * @memberOf logger#
368      * @name warn
369      * @function
370      */
371     function (message) {
372         if (registered[lastLogger].logLevel > LOGLEVEL.warn) {
373             filteredLogs++;
374             return this;
375         }
376         if (Function.prototype.bind && console && typeof console.warn == "object") {
377             var warn = Function.prototype.bind.call(console.warn, console);
378             warn.apply(console, this.formatter.format(arguments, registered[lastLogger].indentationLevel));
379             return this;
380         } else {
381             this.backend.warn.apply(this.backend, this.formatter.format(arguments, registered[lastLogger].indentationLevel));
382             return this;
383         }
384     };
385 
386     logger.prototype.init.prototype.log =
387     /**
388      * Logs messages of logLevel=log
389      * @param {string}message
390      * @param {string}[messages] more messages, comma separated
391      * @memberOf logger#
392      * @name log
393      * @function
394      */
395     function (message) {
396         if (registered[lastLogger].logLevel > LOGLEVEL.log) {
397             filteredLogs++;
398             return this;
399         }
400         if (Function.prototype.bind && console && typeof console.log == "object") {
401             var log = Function.prototype.bind.call(console.log, console);
402             log.apply(console, this.formatter.format(arguments, registered[lastLogger].indentationLevel));
403             return this;
404         } else {
405             this.backend.log.apply(this.backend, this.formatter.format(arguments, registered[lastLogger].indentationLevel));
406             return this;
407         }
408     };
409 
410     logger.prototype.init.prototype.trace =
411     /**
412      * Logs messages of logLevel=trace
413      * @param {string}message
414      * @param {string}[messages] more messages, comma separated
415      * @memberOf logger#
416      * @name trace
417      * @function
418      */
419     function () {
420         if (registered[lastLogger].logLevel > LOGLEVEL.trace) {
421             filteredLogs++;
422             return this;
423         }
424         if (Function.prototype.bind && console && typeof console.trace == "object") {
425             var trace = Function.prototype.bind.call(console.trace, console);
426             trace.apply(console, this.formatter.format(arguments, registered[lastLogger].indentationLevel));
427             return this;
428         } else {
429             this.backend.trace.apply(this.backend, this.formatter.format(arguments, registered[lastLogger].indentationLevel));
430             return this;
431         }
432     };
433 
434     logger.prototype.init.prototype.group =
435     /**
436      * Groups next messages until there is a call to groupEnd
437      * and logs messages to logLevel=log
438      * @param {string}[messages] more messages, comma separated
439      * @memberOf logger#
440      * @name group
441      * @function
442      */
443     function (message) {
444         registered[lastLogger].indentationLevel++;
445         this.backend.groupCollapsed.call(this.backend);
446         if (registered[lastLogger].logLevel > LOGLEVEL.log) {
447             filteredLogs++;
448             return this;
449         }
450         if (message) {
451             this.log.apply(this, arguments);
452         }
453         return this;
454     };
455 
456     logger.prototype.init.prototype.groupEnd =
457     /**
458      * Ungroup previously grouped messages
459      * and logs messages to logLevel=log
460      * @param {string}[messages] messages, comma separated
461      * @memberOf logger#
462      * @name groupEnd
463      * @function
464      */
465     function (message) {
466         registered[lastLogger].indentationLevel--;
467         this.backend.groupEnd.call(this.backend);
468         if (registered[lastLogger].logLevel > LOGLEVEL.trace) {
469             filteredLogs++;
470             return this;
471         }
472         if (message) {
473             this.log.apply(this, arguments);
474         }
475         return this;
476     };
477 
478     wef.logger = logger;
479 
480 })(window.wef);