1 var define = require("../define.js"),
  2     base = require("../base"),
  3     Level = require("./level"),
  4     appenders = require("./appenders"),
  5     configurators = require("./config");
  6 var rootTree;
  7 var LoggerTree = define.define(null, {
  8     instance : {
  9 
 10         constructor : function(root) {
 11             this.__root = root;
 12             this.__name = root.name;
 13             this.__level = root.level;
 14             this.__parent = root._parent;
 15             this.__map = {};
 16         },
 17 
 18         __getSubLoggers : function() {
 19             var map = this.__map, ret = [], n;
 20             for (var i in map) {
 21                 n = map[i];
 22                 if (n) {
 23                     ret = ret.concat(n.tree.getCurrentLoggers());
 24                 }
 25             }
 26             return ret;
 27         },
 28 
 29         __getLoggers : function() {
 30             return [this.__root].concat(this.__getSubLoggers())
 31         },
 32 
 33         getCurrentLoggers : function() {
 34             return this.__getLoggers();
 35         },
 36 
 37         getSubLoggers : function() {
 38             return this.__getSubLoggers();
 39         },
 40 
 41         getLogger : function(name) {
 42             var ret;
 43             if (name) {
 44                 var parts = name.split(".");
 45                 if (parts.length) {
 46                     var category = parts.shift();
 47                     var lNode = this.__map[category];
 48                     if (!lNode) {
 49                         lNode = this.__map[category] = new Logger(category, this);
 50                         lNode.addAppenders(this.__root.appenders);
 51                     }
 52                     ret = lNode;
 53                     if (parts.length) {
 54                         //keep searching
 55                         name = parts.join(".");
 56                         ret = lNode.tree.getLogger(name);
 57                     }
 58                 }
 59             } else {
 60                 ret = this.__root;
 61             }
 62             return ret;
 63         },
 64 
 65         getRootLogger : function() {
 66             return this.__root;
 67         },
 68 
 69         isDisabled : function(level) {
 70         },
 71 
 72         resetConfiguration : function() {
 73         },
 74 
 75         /**
 76          * level = string|Level
 77          */
 78 
 79         addAppender : function(appender) {
 80             var map = this.__map;
 81             for (var i in map) {
 82                 map[i].addAppender(appender);
 83             }
 84         },
 85 
 86         removeAppender : function(name) {
 87             var map = this.__map;
 88             for (var i in map) {
 89                 map[i].removeAppender(name);
 90             }
 91         },
 92 
 93         setters : {
 94             level : function(level) {
 95                 this.__level = level;
 96                 if (level && level instanceof Level) {
 97                     var map = this.__map;
 98                     for (var i in map) {
 99                         map[i].level = level;
100                     }
101 
102                 }
103             }
104 
105         },
106 
107         getters : {
108             categories : function() {
109                 return this.getCurrentLoggers().map(function(l) {
110                     return l.fullName;
111                 });
112             },
113 
114             name : function() {
115                 var ret = this.__name;
116                 if (this.__parent) {
117                     var pName = this.__parent.name;
118                     if (pName) {
119                         ret = pName + "." + ret;
120                     }
121                 }
122                 return ret;
123             },
124 
125             level : function() {
126                 return this.__level;
127             },
128 
129             additive : function() {
130                 return this.__root.additive;
131             }
132         }
133     }
134 });
135 
136 var comb = exports;
137 /**@namespace logging package*/
138 comb.logging = base.merge({
139     Level : Level
140 }, configurators);
141 /**@namespace appenders for logging*/
142 comb.logging.appenders = appenders;
143 
144 var logging = comb.logging;
145 
146 /**
147  * @class This class is the entry point for all logging actions in comb.
148  * <p><b>Logger should be retrieved by calling Logger.getLogger() NOT through the new keyword</b><p>
149  * <p>
150  *     All loggers in comb follow a heirarchy of inheritance based on a dot notation.
151  *     <pre class="code">
152  *                          rootLogger - ""
153  *                                  /         \
154  *                               "my"      "myOther"
155  *                               /               \
156  *                         "my.logger"       "myOther.logger"
157  *                            /                     \
158  *                       "my.logger.Log"        "myOther.logger.Log"
159  *
160  *     </pre>
161  *     In the above Tree the rootLogger is the base for all logger. my and myOther inherit from rootLogger
162  *     my.logger inherits from my, and myOther.logger inherits from myOther. The logs do not have to be retrieved in
163  *     order. If I set rootLogger to ERROR level and added a console appender to it the appender and level will be
164  *     added to all logs. However if I set my to INFO level and add a fileAppender to it the level and appender will
165  *     only be added to logs in "my" subtree. If you set my.logger to not be additive then levels, and appenders will not
166  *     propogate down to the rest of the tree.
167  *
168  * </p>
169  *
170  * <p>For information on levels see {@link comb.logging.Level}.</p>
171  * <p>For information on appenders see
172  *          <ul>
173  *              <li>{@link comb.logging.appenders.Appender}</li>
174  *              <li>{@link comb.logging.appenders.ConsoleAppender}</li>
175  *              <li>{@link comb.logging.appenders.FileAppender}</li>
176  *              <li>{@link comb.logging.appenders.JSONAppender}</li>
177  *              <li>{@link comb.logging.appenders.RollingFileAppender}</li>
178  *          </ul>
179  * </p>
180  * <p>For information on configurators see {@link comb.logging.BasicConfigurator} or {@link comb.logging.PropertyConfigurator}.</p>
181  *
182  * @example
183  *
184  * var logging = comb.logging,
185  *     Logger = logging.Logger,
186  *     appenders = logging.appenders,
187  *     Level = logging.Level;
188  *
189  * //configure you logging environement
190  *
191  * var bc = logging.BasicConfigurator();
192  * //add console appender to all loggers
193  * bc.configure();
194  * //add a file appender to all loggers
195  * bc.configure(new appenders.FileAppender({file : "/var/log/myLog.log"}));
196  *
197  * //Retreiving a logger.
198  * var combLogger = Logger.getLogger("comb");
199  * var combCollectionLogger = Logger.getLogger("comb.collections");
200  * var treeLogger = Logger.getLogger("comb.collections.Tree");
201  *
202  * //set my treeLogger to DEBUG Level
203  * treeLogger.level = Level.DEBUG;
204  * //add a JSON appender to tree logger just for fun!
205  * treeLogger.addAppender(new appenders.JSONAppender({file : "/var/log/myTreeLogger.json"}));
206  *
207  * //NOW USE THEM
208  *
209  *
210  * @name Logger
211  * @memberOf comb.logging
212  *
213  * @property {Array<comb.logging.Logger>} subLoggers all loggers this logger is the parent of.
214  * @property {comb.logging.Level} level the level of this Logger
215  * @property {Boolean} additive set to false to prevent changes to this logger from propogating down.
216  * @property {Boolean} isDebug true if this Loggers level is DEBUG
217  * @property {Boolean} isTrace true if this Loggers level is TRACE
218  * @property {Boolean} isInfo true if this Loggers level is INFO
219  * @property {Boolean} isWarn true if this Loggers level is WARN
220  * @property {Boolean} isError true if this Loggers level is ERROR
221  * @property {Boolean} isFatal true if this Loggers level is FATAL
222  * @property {Boolean} isOff true if this Loggers level is OFF
223  * @property {String} name the name of this logger this <b>does not</b> include the dot notated name
224  * @property {String} fullName the full path name of this Logger.
225  * @property {comb.logging.appenders.Appender} appenders list of appenders this logger currently contains.
226  */
227 var Logger = (logging.Logger = define.define(null, {
228 
229     instance: {
230         /**@lends comb.logging.Logger.prototype*/
231 
232         constructor : function(name, parent) {
233             this.__additive = true;
234             this.__name = name;
235             this._parent = parent;
236             this._tree = new LoggerTree(this);
237             this.fullName = this._tree.name;
238             if (!parent || !parent.additive) {
239                 this.level = Level.ALL;
240             } else {
241                 this.level = parent.level;
242             }
243             this.__appenders = {};
244         },
245 
246         /**
247          * Log an info level message
248          *
249          * @param {String} message the message to log.
250          */
251         info : function(message) {
252             this.log(Level.INFO, message);
253         },
254 
255         /**
256          * Log an debug level message
257          *
258          * @param {String} message the message to log.
259          */
260         debug : function(message) {
261             this.log(Level.DEBUG, message);
262         },
263 
264         /**
265          * Log an error level message
266          *
267          * @param {String} message the message to log.
268          */
269         error : function(message) {
270             this.log(Level.ERROR, message);
271         },
272 
273         /**
274          * Log an warn level message
275          *
276          * @param {String} message the message to log.
277          */
278         warn : function(message) {
279             this.log(Level.WARN, message);
280         },
281 
282         /**
283          * Log an trace level message
284          *
285          * @param {String} message the message to log.
286          */
287         trace : function(message) {
288             this.log(Level.TRACE, message);
289         },
290 
291         /**
292          * Log an fatal level message
293          *
294          * @param {String} message the message to log.
295          */
296         fatal : function(message) {
297             this.log(Level.FATAL, message);
298         },
299 
300         /**
301          * Log a message
302          *
303          * @param {comb.logging.Level} level the level the message is
304          * @param {String} message the message to log.
305          */
306         log : function(level, message) {
307             if (level.isGreaterOrEqualToo(this.level)) {
308                 if (Level.TRACE.equals(level)) {
309                     var err = new Error;
310                     err.name = "Trace";
311                     err.message = message || '';
312                     Error.captureStackTrace(err, arguments.callee);
313                     message = err.stack;
314                 } else if (Level.ERROR.equals(level) && base.isInstanceOf(message, Error)) {
315                     message = message.stack;
316                 }
317                 var type = level.name.toLowerCase(), appenders = this.__appenders;
318                 var event = {
319                     level : level,
320                     levelName : level.name,
321                     message : message,
322                     timeStamp : new Date(),
323                     name : this.fullName
324                 };
325                 for (var i in appenders) {
326                     appenders[i].append(event);
327                 }
328             }
329         },
330 
331         /**
332          * Add an appender to this logger. If this is additive then the appender is added to all subloggers.
333          * @param {comb.logging.Appender} appender the appender to add.
334          */
335         addAppender : function(appender) {
336             if (!base.isUndefinedOrNull(appender)) {
337                 var name = appender.name;
338                 if (!(name in this.__appenders)) {
339                     this.__appenders[name] = appender;
340                     appender.level = this.level;
341                     this._tree.addAppender(appender);
342                 }
343             }
344         },
345 
346         /**
347          * Short cut to add a list of appenders to this Logger
348          * @param {Array<comb.logging.Appender>} appenders
349          */
350         addAppenders : function(appenders) {
351             appenders.forEach(base.hitch(this, "addAppender"));
352         },
353 
354         /**
355          * Removes and appender from this logger.
356          * @param {String} name the name of the appender
357          */
358         removeAppender : function(name) {
359             if (name in this.__appenders) {
360                 delete this.__appenders[name];
361                 this._tree.removeAppender(name);
362             }
363         },
364 
365         /**
366          * Removes a list of appenders from this logger.
367          *
368          * @param {Array<String>} appenders a list of names of appenders to remove
369          */
370         removeAppenders : function(appenders) {
371             appenders.forEach(this.removeAppender, this);
372         },
373 
374         /**
375          * Removes all appenders from this logger and sub loggers if this Logger is additive.
376          */
377         removeAllAppenders : function() {
378             for (var i in this.__appenders) {
379                 this.removeAppender(i);
380             }
381         },
382 
383         /**
384          * Determines if an appender is attached.
385          *
386          * @param {String} name the name of the appender.
387          */
388         isAppenderAttached : function(name) {
389             return (name in this.__appenders);
390         },
391 
392         /**
393          * Gets an appender from this logger
394          *
395          * @param {String} name the name of the appender.
396          *
397          * @return {comb.logging.Appender|undefined} returns the appender with the specified name or
398          *                                          undefined if it is not found.
399          */
400         getAppender : function(name) {
401             var ret;
402             if (name in this.__appenders) {
403                 ret = this.__appenders[name];
404             }
405             return ret;
406         },
407 
408         /**
409          * @ignore
410          * */
411         setters : {
412 
413             level : function(level) {
414                 level = Level.toLevel(level);
415                 if (this.__additive) {
416                     this.__level = level;
417                     var appenders = this.__appenders;
418                     for (var i in appenders) {
419                         appenders[i].level = level;
420                     }
421                     this._tree.level = level;
422                 } else {
423                     this.__level = level;
424                 }
425             },
426 
427             additive : function(additive) {
428                 this.__additive = additive;
429             }
430         },
431 
432         /**@ignore*/
433         getters : {
434             /**@ignore*/
435 
436             /**@ignore*/
437             subLoggers : function() {
438                 return this._tree.getSubLoggers();
439             },
440 
441             /**@ignore*/
442             level : function() {
443                 return this.__level;
444             },
445 
446             /**@ignore*/
447             additive : function() {
448                 return this.__additive;
449             },
450 
451             isAll : function() {
452                 return Level.ALL.isGreaterOrEqualToo(this.level);
453             },
454 
455             /**@ignore*/
456             isDebug : function() {
457                 return Level.DEBUG.isGreaterOrEqualToo(this.level);
458             },
459 
460             /**@ignore*/
461             isTrace : function() {
462                 return Level.TRACE.isGreaterOrEqualToo(this.level);
463             },
464 
465             /**@ignore*/
466             isInfo : function() {
467                 return Level.INFO.isGreaterOrEqualToo(this.level);
468             },
469 
470             /**@ignore*/
471             isWarn : function() {
472                 return Level.WARN.isGreaterOrEqualToo(this.level);
473             },
474 
475             /**@ignore*/
476             isError : function() {
477                 return Level.ERROR.isGreaterOrEqualToo(this.level);
478             },
479 
480             /**@ignore*/
481             isFatal : function() {
482                 return Level.FATAL.isGreaterOrEqualToo(this.level);
483             },
484 
485             /**@ignore*/
486             isOff : function() {
487                 return Level.OFF.equals(this.level);
488             },
489 
490             /**@ignore*/
491             name : function() {
492                 return this.__name;
493             },
494 
495             /**@ignore*/
496             tree : function() {
497                 return this._tree;
498             },
499 
500             /**@ignore*/
501             appenders : function() {
502                 var ret = [];
503                 for (var i in this.__appenders) {
504                     ret.push(this.__appenders[i]);
505                 }
506                 return ret;
507             },
508 
509             categories: function() {
510                 return this._tree.categories;
511             }
512         }
513     },
514 
515     static : {
516         /**@lends comb.logging.Logger*/
517 
518         /**
519          * Return the root of all loggers
520          */
521         getRootLogger : function() {
522             return rootTree.getRootLogger();
523         },
524 
525         /**
526          * Retrieves/Creates a logger based on the name passed in
527          *
528          * @param {String} name the name of the logger
529          */
530         getLogger : function(name) {
531             return rootTree.getLogger(name);
532         }
533     }
534 }));
535 
536 var rootLogger = new Logger("");
537 rootTree = rootLogger._tree;
538 
539 
540