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);