1 (function (win) { 2 'use strict'; 3 /** 4 * Hermes is the class that allows to log messages to different appenders depending of level to show. 5 * Highly extensible. You can create your own Appenders and add it to Hermes. 6 * The idea was to create an ErrorHandler in the same way as Log4JS but being more extensible. 7 * @author Tomás Corral Casas 8 * @version 1.0 9 */ 10 /** 11 * Hermes is the private declaration of the Hermes object. 12 * Hermes is declared null by default. 13 * @private 14 * @type Hermes 15 */ 16 var Hermes = null, 17 Trace = null, 18 Tracer = null, 19 TraceMessage = null, 20 oTracer = null, 21 /** 22 * oHermes is the instance of Hermes object. 23 * oHermes is declared null by default. 24 * @private 25 * @type Hermes 26 */ 27 oHermes = null, 28 ErrorExt = null, 29 30 Appender = null, 31 ConsoleAppender = null, 32 Layout = null, 33 ConsoleLayout = null, 34 Level = null, 35 DateFormatter = null, 36 /** 37 * nTimeoutToLogMilliseconds is the default value for nTimeout in Hermes to defer the log of message 38 * If we need to log messages to one DB it's important not to launch so much Ajax calls 39 * nTimeoutToLogMilliseconds is declared 10000 by default. 40 * @private 41 * @type Number 42 */ 43 nTimeoutToLogMilliseconds = 5000; 44 /** 45 * isFunction checks if fpCallback is function or not 46 * @private 47 * @return {Boolean} 48 */ 49 function isFunction(fpCallback) { 50 return Object.prototype.toString.call(fpCallback) === '[object Function]'; 51 } 52 /** 53 * existObjectAndMethod checks if sMethod exist on oObject 54 * @private 55 * @return {Boolean} 56 */ 57 function existObjectAndMethod(oObject, sMethod) { 58 return oObject && isFunction(oObject[sMethod]); 59 } 60 /** 61 * times count the executions and add this as indentation 62 * @private 63 * @return {Number} 64 */ 65 function times(nCount) 66 { 67 return nCount < 1 ? '' : new Array(nCount + 1).join(" "); 68 } 69 /** 70 * getTypeFromMessage returns the type of error extracted from message 71 * @private 72 * @returns {String} 73 */ 74 function getTypeFromMessage(sMessage) { 75 var nIndexDoubleColon = sMessage.indexOf(":"); 76 return sMessage.substr(0, nIndexDoubleColon); 77 } 78 /** 79 * removeTypeFromMessage returns the message removing the error type 80 * @private 81 * @returns {String} 82 */ 83 function removeTypeFromMessage(sMessage) { 84 var sType = getTypeFromMessage(sMessage); 85 return sMessage.replace(sType + ":", ""); 86 } 87 /** 88 * Appender is the class that will log or clear messages 89 * @abstract 90 * @private 91 * @class Appender 92 * @constructor 93 * @param oLayout 94 * @type {Layout} 95 */ 96 Appender = function (oLayout) { 97 /** 98 * sName is the name of the appender 99 * @member Appender.prototype 100 * @type {String} 101 */ 102 this.sName = '[object Appender]'; 103 /** 104 * oLayout is the Layout instance to be used to format the error before append it 105 * @member Appender.prototype 106 * @type {Layout} 107 */ 108 this.oLayout = oLayout || null; 109 }; 110 /** 111 * setName is the method to set the name of Appender 112 * @member Appender.prototype 113 * @param sName 114 * @type {String} 115 * @return Appender instance 116 */ 117 Appender.prototype.setName = function (sName) { 118 this.sName = sName; 119 return this; 120 }; 121 /** 122 * setLayout is the method to set the layout for Appender 123 * @member Appender.prototype 124 * @param oLayout 125 * @type {Layout} 126 * @return Appender instance 127 */ 128 Appender.prototype.setLayout = function (oLayout) { 129 this.oLayout = oLayout; 130 return this; 131 }; 132 /** 133 * toString is the method to overwrite the Object.prototype.toString 134 * @member Appender.prototype 135 * @return {String} 136 */ 137 Appender.prototype.toString = function () { 138 return this.sName; 139 }; 140 /** 141 * log is an abstract method that needs to be overwritten on extended classes to make it work. 142 * @member Appender.prototype 143 * @param oError 144 * @type {ErroExt} 145 */ 146 Appender.prototype.log = function (oError) { 147 throw new Error("This method must be overwritten!"); 148 }; 149 /** 150 * clear is an abstract method that needs to be overwritten on extended classes to make it work. 151 * @member Appender.prototype 152 */ 153 Appender.prototype.clear = function () { 154 throw new Error("This method must be overwritten!"); 155 }; 156 /** 157 * Layout is the class that will format the errors before send it to the Appender class 158 * @abstract 159 * @private 160 * @class Layout 161 * @constructor 162 */ 163 Layout = function () { 164 165 }; 166 /** 167 * formatError is an abstract method that needs to be overwritten on extended classes to make it work. 168 * @member Layout.prototype 169 * @param oError 170 * @type {ErroExt} 171 */ 172 Layout.prototype.formatError = function (oError) { 173 throw new Error("This method must be overwritten!"); 174 }; 175 /** 176 * ConsoleLayout is a class to format log messages. 177 * @extends Layout 178 * @private 179 * @class ConsoleLayout 180 * @constructor 181 */ 182 ConsoleLayout = function () { 183 Layout.apply(this); 184 }; 185 ConsoleLayout.prototype = new Layout(); 186 /** 187 * formatError is a method to format the error. 188 * @member ConsoleLayout.prototype 189 * @param oError 190 * @type {ErrorExt} 191 * @return sError 192 * @type {String} 193 */ 194 ConsoleLayout.prototype.formatError = function (oError) { 195 var sError = ''; 196 if (oError instanceof ErrorExt) { 197 sError = "Error level: " + oError.oLevel.toString() + 198 ", Time: " + oError.getFormattedDate(); 199 if(oError.sCategory !== null) 200 { 201 sError += ", Category: " + oError.sCategory; 202 } 203 if(oError.sMessage !== null) 204 { 205 sError += ", Message: " + oError.sMessage 206 } 207 if(oError.sFilenameUrl !== null) 208 { 209 sError += ", FilenameUrl: " + oError.sFilenameUrl 210 } 211 if(oError.nLineNumber !== null) 212 { 213 sError += ", LineNumber: " + oError.nLineNumber 214 } 215 sError += '.'; 216 } 217 return sError; 218 }; 219 /** 220 * ConsoleAppender is a class to append log messages in the console. 221 * @extends Appender 222 * @private 223 * @class ConsoleAppender 224 * @constructor 225 */ 226 ConsoleAppender = function (oLayout) { 227 Appender.apply(this, arguments); 228 /** 229 * sName is the name of the appender 230 * @member ConsoleAppender.prototype 231 * @type {String} 232 */ 233 this.sName = '[object ConsoleAppender]'; 234 /** 235 * oLayout is the Layout instance to be used to format the error before append it 236 * @member ConsoleAppender.prototype 237 * @type {ConsoleLayout} 238 */ 239 this.oLayout = new ConsoleLayout(); 240 }; 241 ConsoleAppender.prototype = new Appender(); 242 /** 243 * log is the method that logs the message to the console. 244 * Before log it's needed to check if console exist. 245 * @member ConsoleAppender.prototype 246 * @param oError 247 * @type {ErrorExt} 248 */ 249 ConsoleAppender.prototype.log = function (oError) { 250 if (existObjectAndMethod(win.console, "log")) { 251 win.console.log(this.oLayout.formatError(oError)); 252 } 253 }; 254 /** 255 * clear is the method that clear all the logged messages in the console. 256 * Before clear it's needed to check if console exist. 257 * @member ConsoleAppender.prototype 258 */ 259 ConsoleAppender.prototype.clear = function () { 260 if (existObjectAndMethod(win.console, "clear")) { 261 win.console.clear(); 262 } 263 }; 264 /** 265 * Hermes is the class that manages Appenders, Error, Deferred calls and Levels of Error to show 266 * We can add Errors but if the Error is not of the same level the error will not be showed. 267 * If the Hermes Error Level is ALL all the logs will be added 268 * If the Hermes Error Level is OFF no log will be added 269 * The add of logs can be deferred to avoid multiple calls that block browser 270 * @private 271 * @class Hermes 272 * @constructor 273 * @param oLevel 274 * @type {Level} 275 * @param nImmediate 276 * @type {Number} 277 * @param nTimeout 278 * @type {Number} 279 */ 280 Hermes = function (oLevel, nImmediate, nTimeout) { 281 /** 282 * oAppenders is the object that will contain the Appenders to append messages 283 * @member Hermes.prototype 284 * @type {Object} 285 */ 286 this.oAppenders = {}; 287 /** 288 * aErrors is the array that will contain the Errors 289 * @member Hermes.prototype 290 * @type {Array} 291 */ 292 this.aErrors = []; 293 /** 294 * oLevel is the Level instance to manage the Error Level to show. 295 * @member Hermes.prototype 296 * @type {Level} 297 */ 298 this.oLevel = oLevel || Level.ALL; 299 /** 300 * nTimeLastSent is the number of milliseconds of the last sent 301 * now date by default 302 * @member Hermes.prototype 303 * @type {Number} 304 */ 305 this.nTimeLastSent = this.now(); 306 /** 307 * nImmediate is the config number to defer or not the append of logs 308 * @member Hermes.prototype 309 * @type {Number} 310 */ 311 this.nImmediate = nImmediate || Hermes.DEFERRED; 312 /** 313 * nTimeout is the number of milliseconds to defer the append of logs 314 * @member Hermes.prototype 315 * @type {Number} 316 */ 317 this.nTimeout = nTimeout || nTimeoutToLogMilliseconds; 318 }; 319 /** 320 * @static 321 */ 322 Hermes.DEFERRED = 0; 323 /** 324 * @static 325 */ 326 Hermes.IMMEDIATE = 1; 327 /** 328 * deferLog change the method to append logs to be deferred 329 * @member Hermes.prototype 330 * @return Hermes instance 331 */ 332 Hermes.prototype.deferLog = function () { 333 this.nImmediate = Hermes.DEFERRED; 334 return this; 335 }; 336 /** 337 * immediateLog change the method to append logs to be immediate 338 * @member Hermes.prototype 339 * @return Hermes instance 340 */ 341 Hermes.prototype.immediateLog = function () { 342 this.nImmediate = Hermes.IMMEDIATE; 343 return this; 344 }; 345 /** 346 * now returns the number of milliseconds at the moment of the execution of this method 347 * @member Hermes.prototype 348 * @return {Number} Milliseconds 349 */ 350 Hermes.prototype.now = function () { 351 return +new Date(); 352 }; 353 /** 354 * setLevel set a new Level for the Hermes 355 * Before change the level it's checked if oLevel is instance of Level to be assigned or not. 356 * @member Hermes.prototype 357 * @param oLevel 358 * @type {Level} 359 * @return Hermes instance 360 */ 361 Hermes.prototype.setLevel = function (oLevel) { 362 if (oLevel instanceof Level) { 363 this.oLevel = oLevel; 364 } 365 return this; 366 }; 367 /** 368 * addAppender add a new Appender to Hermes. When the Appender is added starts to log messages 369 * Before add a new Appender it's checked if oAppender is instance of Appender to be added or not. 370 * @member Hermes.prototype 371 * @param oAppender 372 * @type {Appender} 373 * @return Hermes instance 374 */ 375 Hermes.prototype.addAppender = function (oAppender) { 376 if (oAppender instanceof Appender) { 377 this.oAppenders[oAppender.toString()] = oAppender; 378 } 379 return this; 380 }; 381 /** 382 * removeAppender removes the Appender. When the Appender is removed stops to log messages 383 * @member Hermes.prototype 384 * @param oAppender 385 * @type {Appender} 386 * @return Hermes instance 387 */ 388 Hermes.prototype.removeAppender = function (oAppender) { 389 delete this.oAppenders[oAppender.toString()]; 390 return this; 391 }; 392 /** 393 * setAppenders add an array of Appenders to be added in one step. 394 * @member Hermes.prototype 395 * @param aAppenders 396 * @type {Array} 397 * @return Hermes instance 398 */ 399 Hermes.prototype.setAppenders = function (aAppenders) { 400 var nAppender = 0, 401 nLenAppenders = aAppenders.length, 402 oAppender = null; 403 404 for (; nAppender < nLenAppenders;) { 405 oAppender = aAppenders[nAppender]; 406 this.addAppender(oAppender); 407 nAppender = nAppender + 1; 408 } 409 try { 410 return this; 411 } finally { 412 nAppender = nLenAppenders = oAppender = null; 413 } 414 }; 415 /** 416 * isSameLevel checks if the Error Level is the same that in oError 417 * The method returns true if Level is ALL to allow log all the logs 418 * The method returns false if Level is OFF to avoid log any log 419 * @member Hermes.prototype 420 * @param oError 421 * @type {ErrorExt} 422 * @return {Boolean} 423 */ 424 Hermes.prototype.isSameLevel = function (oError) { 425 if (this.oLevel.valueOf() === Level.nALL) { 426 return true; 427 } else if (this.oLevel.valueOf() === Level.OFF) { 428 return false; 429 } else { 430 return this.oLevel.valueOf() === oError.oLevel.valueOf(); 431 } 432 }; 433 /** 434 * sendMessage executes the action in Appender for each Error. 435 * @member Hermes.prototype 436 * @param oAppender 437 * @type {Appender} 438 * @param sAction 439 * @type {String} 440 */ 441 Hermes.prototype.sendMessage = function (oAppender, sAction) { 442 if (typeof oAppender[sAction] === "undefined") { 443 return; 444 } 445 var nError = 0, 446 nLenError = this.aErrors.length, 447 oError = null; 448 449 for (; nError < nLenError;) { 450 oError = this.aErrors[nError]; 451 if (this.isSameLevel(oError)) { 452 oAppender[sAction](oError); 453 } 454 nError = nError + 1; 455 } 456 this.nTimeLastSent = this.now(); 457 }; 458 /** 459 * isImmediate checks if nImmediate is equals to Hermes.IMMEDIATE 460 * @member Hermes.prototype 461 * @return {Boolean} 462 */ 463 Hermes.prototype.isImmediate = function () { 464 return this.nImmediate === Hermes.IMMEDIATE; 465 }; 466 /** 467 * isTimeToSent checks if now is time to sent new logs to the appender 468 * @member Hermes.prototype 469 * @return {Boolean} 470 */ 471 Hermes.prototype.isTimeToSent = function () { 472 return this.now() >= this.nextTimeToSent(); 473 }; 474 /** 475 * notifyAppenders send the message log for all the Appenders if the execution is Deferred and is time to send new logs or if the execution is Immediate. 476 * after execute notifyAppenders the errors are reset to allow new Errors to log. 477 * @member Hermes.prototype 478 * @param sAction 479 * @type {String} 480 * @return Hermes instance 481 */ 482 Hermes.prototype.notifyAppenders = function (sAction) { 483 var sKey = '', 484 oAppender = null; 485 sAction = sAction.toLowerCase(); 486 487 if (this.isImmediate()) 488 { 489 for (sKey in this.oAppenders) { 490 if (this.oAppenders.hasOwnProperty(sKey)) { 491 oAppender = this.oAppenders[sKey]; 492 this.sendMessage(oAppender, sAction); 493 } 494 } 495 this.resetErrors(); 496 } else if (this.isTimeToSent()){ 497 for (sKey in this.oAppenders) { 498 if (this.oAppenders.hasOwnProperty(sKey)) { 499 oAppender = this.oAppenders[sKey]; 500 this.sendMessage(oAppender, sAction); 501 } 502 } 503 this.resetErrors(); 504 } 505 return this; 506 }; 507 /** 508 * addError check if oError is instance of ErrorExt and if it's the error is added and the notifyAppenders is called 509 * notifyAppenders is executed to defer or log the message. 510 * @member Hermes.prototype 511 * @param oError 512 * @type {ErrorExt} 513 * @return Hermes instance 514 */ 515 Hermes.prototype.addError = function (oError) { 516 if (oError instanceof ErrorExt) { 517 this.aErrors.push(oError); 518 this.notifyAppenders('log'); 519 } 520 return this; 521 }; 522 /** 523 * resetErrors clean all the erros in the aErrors Array 524 * @member Hermes.prototype 525 */ 526 Hermes.prototype.resetErrors = function () { 527 this.aErrors = []; 528 }; 529 /** 530 * nextTimeToSent returns the time to know, if the method of log is Deferred, if it's possible to log messages. 531 * @member Hermes.prototype 532 * @return {Number} 533 */ 534 Hermes.prototype.nextTimeToSent = function () { 535 return this.nTimeLastSent + this.nTimeout; 536 }; 537 /** 538 * forceLog will log messages even if the method of log is Deferred. 539 * @member Hermes.prototype 540 * @return Hermes instance 541 */ 542 Hermes.prototype.forceLog = function () { 543 this.immediateLog(); 544 this.log(); 545 this.deferLog(); 546 return this; 547 }; 548 /** 549 * log is the method that will log messages 550 * @member Hermes.prototype 551 */ 552 Hermes.prototype.log = function () { 553 this.notifyAppenders("log"); 554 }; 555 /** 556 * clear is the method that will clear messages 557 * @member Hermes.prototype 558 */ 559 Hermes.prototype.clear = function () { 560 this.notifyAppenders("clear"); 561 }; 562 /** 563 * DateFormatter is the class to format dates 564 * @private 565 * @class DateFormatter 566 * @constructor 567 * @param sFormat 568 * @type {String} 569 */ 570 DateFormatter = function (sFormat) { 571 this.sFormat = sFormat || DateFormatter.DEFAULT; 572 }; 573 /** 574 * @static 575 */ 576 DateFormatter.DEFAULT = "yyyy-MM-dd hh:mm:ss TZ"; 577 /** 578 * setDefaultFormat change the format to format the date 579 * @member DateFormatter.prototype 580 * @param sFormat 581 * @type {String} 582 * @return DateFormatter instance 583 */ 584 DateFormatter.prototype.setDefaultFormat = function (sFormat) { 585 this.sFormat = sFormat || DateFormatter.DEFAULT; 586 return this; 587 }; 588 /** 589 * formatDate returns the string with the formatted date. 590 * @member DateFormatter.prototype 591 * @param oDate 592 * @type {Date} 593 */ 594 DateFormatter.prototype.formatDate = function (oDate) { 595 var nDay = this.addZero(oDate.getDate()), 596 nMonth = this.addZero(oDate.getMonth() + 1), 597 nYearLong = oDate.getFullYear(), 598 nYearShort = String(nYearLong).substring(3, 4), 599 nYear = this.sFormat.indexOf("yyyy") > -1 ? nYearLong : nYearShort, 600 nHours = this.addZero(oDate.getHours()), 601 nMinutes = this.addZero(oDate.getMinutes()), 602 nSeconds = this.addZero(oDate.getSeconds()), 603 nTimeZone = this.getTimeZoneOffset(oDate), 604 sDate = this.sFormat.replace(/dd/g, nDay).replace(/MM/g, nMonth).replace(/y{1,4}/g, nYear); 605 sDate = sDate.replace(/hh/g, nHours).replace(/mm/g, nMinutes).replace(/ss/g, nSeconds); 606 sDate = sDate.replace(/TZ/g, nTimeZone); 607 try { 608 return sDate; 609 } finally { 610 nDay = nMonth = nYearLong = nYearShort = nYear = nHours = nMinutes = nSeconds = nTimeZone = sDate = null; 611 } 612 }; 613 /** 614 * addZero is a method that adds a Zero to all the number that are less than 10 615 * @member DateFormatter.prototype 616 * @param nNumber 617 * @type {Number} 618 * @return {String} 619 */ 620 DateFormatter.prototype.addZero = function (nNumber) { 621 return ((nNumber < 10) ? "0" : "") + nNumber; 622 }; 623 /** 624 * getTimeZoneOffset set the timezone and returns the difference 625 * @member DateFormatter.prototype 626 * @param oDate 627 * @type {Date} 628 * @return {String} 629 */ 630 DateFormatter.prototype.getTimeZoneOffset = function (oDate) { 631 var nOffset = Math.abs(oDate.getTimezoneOffset()), 632 nHour = this.addZero(Math.floor(nOffset / 60)), 633 nMinutes = this.addZero((nOffset % 60)); 634 try { 635 return oDate.getTimezoneOffset() < 0 ? "+" + nHour + ":" + nMinutes : "-" + nHour + ":" + nMinutes; 636 } finally { 637 nOffset = nHour = nMinutes = null; 638 } 639 }; 640 /** 641 * ErrorExt is the class that represents the Error 642 * @private 643 * @class ErrorExt 644 * @constructor 645 * @param oLevel 646 * @type {Level} 647 * @param sCategory 648 * @type {String} 649 * @param sMessage 650 * @type {String} 651 * @param sFilenameUrl 652 * @type {String} 653 * @param nLineNumber 654 * @type {Number} 655 * @param sDateFormat 656 * @type {String} 657 */ 658 ErrorExt = function (oLevel, sCategory, sMessage, sFilenameUrl, nLineNumber, sDateFormat) { 659 /** 660 * oLevel is the Error Level to know if it can be logged or not. 661 * @member ErrorExt.prototype 662 * @type {Level} 663 */ 664 this.oLevel = oLevel || Level.ALL; 665 /** 666 * sCategory is the error category 667 * @member ErrorExt.prototype 668 * @type {String} 669 */ 670 this.sCategory = sCategory || 'GENERAL'; 671 /** 672 * sMessage is the error message 673 * @member ErrorExt.prototype 674 * @type {String} 675 */ 676 this.sMessage = sMessage || 'Error Undefined'; 677 /** 678 * dInitialize sets the instanciation date 679 * @member ErrorExt.prototype 680 * @type {Date} 681 */ 682 this.dInitialize = new Date(); 683 /** 684 * oDateFormatter is the instance of DateFormatter after set the default format 685 * @member ErrorExt.prototype 686 * @type {DateFormatter} 687 */ 688 this.oDateFormatter = new DateFormatter().setDefaultFormat(sDateFormat); 689 /** 690 * sFilenameUrl is the url or filename to the file where the error is launched 691 * @member ErrorExt.prototype 692 * @type {String} 693 */ 694 this.sFilenameUrl = sFilenameUrl || null; 695 /** 696 * nFileNumber is the number of line where the error is launched 697 */ 698 this.nLineNumber = nLineNumber || null; 699 }; 700 /** 701 * setFilenameUrl change the sFilenameUrl 702 * @member ErrorExt.prototype 703 * @param sFilenameUrl 704 * @type {String} 705 * @return ErrorExt instance 706 */ 707 ErrorExt.prototype.setFilenameUrl = function (sFilenameUrl) { 708 this.sFilenameUrl = sFilenameUrl; 709 return this; 710 }; 711 /** 712 * setLineNumber change the nLineNumber 713 * @member ErrorExt.prototype 714 * @param nLineNumber 715 * @type {Number} 716 * @return ErrorExt instance 717 */ 718 ErrorExt.prototype.setLineNumber = function (nLineNumber) { 719 this.nLineNumber = nLineNumber; 720 return this; 721 }; 722 /** 723 * setCategory change the sCategory 724 * @member ErrorExt.prototype 725 * @param sCategory 726 * @type {String} 727 * @return ErrorExt instance 728 */ 729 ErrorExt.prototype.setCategory = function (sCategory) { 730 this.sCategory = sCategory; 731 return this; 732 }; 733 /** 734 * setLevel change the level if oLevel is instance of Level 735 * @member ErrorExt.prototype 736 * @param oLevel 737 * @type {Level} 738 * @return ErrorExt instance 739 */ 740 ErrorExt.prototype.setLevel = function (oLevel) { 741 if (oLevel instanceof Level) { 742 this.oLevel = oLevel; 743 } 744 return this; 745 }; 746 /** 747 * setMessage change the sMessage 748 * @member ErrorExt.prototype 749 * @param sMessage 750 * @type {String} 751 * @return ErrorExt instance 752 */ 753 ErrorExt.prototype.setMessage = function (sMessage) { 754 this.sMessage = sMessage; 755 return this; 756 }; 757 /** 758 * setDateFormat change the sDateFormat 759 * @member ErrorExt.prototype 760 * @param sDateFormat 761 * @type {String} 762 * @return ErrorExt instance 763 */ 764 ErrorExt.prototype.setDateFormat = function (sDateFormat) { 765 this.sDateFormat = sDateFormat; 766 return this; 767 }; 768 /** 769 * getFormattedDate returns the error Date afte format it 770 * @member ErrorExt.prototype 771 * @return {String} 772 */ 773 ErrorExt.prototype.getFormattedDate = function () { 774 return this.oDateFormatter.formatDate(this.dInitialize); 775 }; 776 /** 777 * Level is the class that sets the Error level 778 * @private 779 * @class Level 780 * @constructor 781 * @param nLevel 782 * @type {Level} 783 * @param sLevel 784 * @type {String} 785 */ 786 Level = function (nLevel, sLevel) { 787 this.nLevel = nLevel; 788 this.sLevel = sLevel; 789 }; 790 /** 791 * getLevel return the Level from the sLevel Name 792 * @param sLevel 793 * @type {String} 794 * @return oNewLevel 795 * @type Level 796 */ 797 Level.prototype.getLevel = function (sLevel) { 798 var oNewLevel = Level[sLevel]; 799 if (typeof oNewLevel !== "undefined") { 800 return oNewLevel; 801 } 802 oNewLevel = null; 803 }; 804 /** 805 * toString overwrittes the Object.prototype.toString to return the name of the Level 806 * @return sLevel 807 * @type {String} 808 */ 809 Level.prototype.toString = function () { 810 return this.sLevel; 811 }; 812 /** 813 * valueOf returns the number of Level 814 * @return nLevel 815 * @type {Number} 816 */ 817 Level.prototype.valueOf = function () { 818 return this.nLevel; 819 }; 820 /** 821 * @static 822 * @type {Number} 823 */ 824 Level.nOFF = Number.MAX_VALUE; 825 /** 826 * @static 827 * @type {Number} 828 */ 829 Level.nFATAL = 50000; 830 /** 831 * @static 832 * @type {Number} 833 */ 834 Level.nERROR = 40000; 835 /** 836 * @static 837 * @type {Number} 838 */ 839 Level.nWARNING = 30000; 840 /** 841 * @static 842 * @type {Number} 843 */ 844 Level.nINFO = 20000; 845 /** 846 * @static 847 * @type {Number} 848 */ 849 Level.nDEBUG = 10000; 850 /** 851 * @static 852 * @type {Number} 853 */ 854 Level.nTRACE = 5000; 855 /** 856 * @static 857 * @type {Number} 858 */ 859 Level.nALL = Number.MIN_VALUE; 860 /** 861 * @static 862 * @type {Level} 863 */ 864 Level.OFF = new Level(Level.nOFF, "OFF"); 865 /** 866 * @static 867 * @type {Level} 868 */ 869 Level.FATAL = new Level(Level.nFATAL, "FATAL"); 870 /** 871 * @static 872 * @type {Level} 873 */ 874 Level.ERROR = new Level(Level.nERROR, "ERROR"); 875 /** 876 * @static 877 * @type {Level} 878 */ 879 Level.WARNING = new Level(Level.nWARNING, "WARN"); 880 /** 881 * @static 882 * @type {Level} 883 */ 884 Level.INFO = new Level(Level.nINFO, "INFO"); 885 /** 886 * @static 887 * @type {Level} 888 */ 889 Level.DEBUG = new Level(Level.nDEBUG, "DEBUG"); 890 /** 891 * @static 892 * @type {Level} 893 */ 894 Level.TRACE = new Level(Level.nTRACE, "TRACE"); 895 /** 896 * @static 897 * @type {Level} 898 */ 899 Level.ALL = new Level(Level.nALL, "ALL"); 900 /** 901 * TraceMessage is the message class for automatic Trace 902 * @private 903 * @extends ErrorExt 904 * @class TraceMessage 905 * @constructor 906 * @param sMessage 907 * @type {String} 908 */ 909 TraceMessage = function(sMessage) 910 { 911 ErrorExt.apply(this, [Level.TRACE, 'Trace Message', sMessage]); 912 }; 913 TraceMessage.prototype = new ErrorExt(); 914 /** 915 * Trace is the class that manage all the info to be used in Tracer. 916 * @private 917 * @class Trace 918 * @constructor 919 * @param oConstructor 920 * @type {Function} 921 * @param sMethodName 922 * @type {String} 923 * @param oTracer 924 * @type {Tracer} 925 */ 926 Trace = function (oConstructor, sMethodName, oTracer) { 927 /** 928 * sIndentString is the number of spaces to indent functions. 929 * Missing modifycation - maybe in the next version - 930 * @member Trace.prototype 931 * @type {String} 932 */ 933 this.sIndentString = ''; 934 /** 935 * oConstructor is the oConstructor to trace 936 * @member Trace.prototype 937 * @type {Function} 938 */ 939 this.oConstructor = oConstructor; 940 /** 941 * sMethodName is the name of the oConstructor 942 * @member Trace.prototype 943 * @type {String} 944 */ 945 this.sMethodName = sMethodName; 946 /** 947 * oTracer is the reference to the Tracer 948 * @member Trace.prototype 949 * @type {Tracer} 950 */ 951 this.oTracer = oTracer; 952 }; 953 /** 954 * formatArguments returns the arguments passed to the function as a string 955 * @member Trace.prototype 956 * @param aArgs 957 * @type {Array} 958 * @return {String} 959 */ 960 Trace.prototype.formatArguments = function (aArgs) 961 { 962 return '(' + aArgs.join(", ") + ')'; 963 }; 964 /** 965 * getIndentation returns the indentation 966 * @member Trace.prototype 967 * @return {String} 968 */ 969 Trace.prototype.getIndentation = function () { 970 return this.sIndentString; 971 }; 972 /** 973 * getMethodNameIndentedAndParams return the string with method name and formatted arguments. 974 * @member Trace.prototype 975 * @return {String} 976 */ 977 Trace.prototype.getMethodNameIndentedAndParams = function(aArgs) 978 { 979 return (this.getIndentation() + this.sMethodName + this.formatArguments(aArgs)); 980 }; 981 /** 982 * getProfile return the string with method name, result and time that tooks the execution 983 * @member Trace.prototype 984 * @return {String} 985 */ 986 Trace.prototype.getProfile = function(nStart, oResult) 987 { 988 return this.getIndentation(true) + this.sMethodName + ' -> result: ' + oResult + '. (' + (+new Date() - nStart) +'ms)'; 989 }; 990 /** 991 * wrap is the method to wrap all the methods to trace 992 * @member Trace.prototype 993 */ 994 Trace.prototype.wrap = function() 995 { 996 var sKey = ''; 997 for(sKey in this.oConstructor) 998 { 999 this[sKey] = this.oConstructor[sKey]; 1000 } 1001 }; 1002 /** 1003 * Tracer is the class that start the automatic tracer for the element that you want to trace 1004 * Is possible to trace more than one object at the same time. 1005 * @private 1006 * @class Tracer 1007 * @constructor 1008 */ 1009 Tracer = function () { 1010 /** 1011 * rNativeCode is the regular expression that returns if the content is native code or not. 1012 * @member Tracer.prototype 1013 * @type {RegExp} 1014 */ 1015 this.rNativeCode = /\[native code\]/; 1016 /** 1017 * nIndentCount is the number of indents. 1018 * Missing modifycation - maybe in the next version - 1019 * @member Tracer.prototype 1020 * @type {Number} 1021 */ 1022 this.nIndentCount = -4; 1023 /** 1024 * aTracing is the array where all the trace elements will be saved to remove it if needed 1025 * @member Tracer.prototype 1026 * @type {Array} 1027 */ 1028 this.aTracing = []; 1029 }; 1030 /** 1031 * trace gets the method to trace and wraps his content 1032 * @member Tracer.prototype 1033 * @param oConstructor 1034 * @type {Function} 1035 * @param sMethodName 1036 * @type {String} 1037 * @return {mixed} Depends of the execution 1038 */ 1039 Tracer.prototype.trace = function (oConstructor, sMethodName) { 1040 var oTrace = new Trace(oConstructor, sMethodName, this); 1041 oTrace.wrap(); 1042 oHermes.addError(new TraceMessage("Tracing: " + sMethodName)); 1043 return function () { 1044 var oResult = null, 1045 nStart = +new Date(), 1046 aArgs = Array.prototype.slice.call(arguments); 1047 oHermes.addError(new TraceMessage(oTrace.getMethodNameIndentedAndParams(aArgs))); 1048 oResult = oConstructor.apply(oTrace, arguments); 1049 oHermes.addError(new TraceMessage(oTrace.getProfile(nStart, oResult))); 1050 return oResult; 1051 } 1052 }; 1053 /** 1054 * addTracing adds a new element to trace 1055 * @member Tracer.prototype 1056 * @param oTracing 1057 * @type {Trace} 1058 */ 1059 Tracer.prototype.addTracing = function(oTracing) 1060 { 1061 this.aTracing.push(oTracing); 1062 }; 1063 /** 1064 * traceAll trace all the methods in oRoot to be tracable 1065 * @member Tracer.prototype 1066 * @param oRoot 1067 * @type {Object} 1068 * @param bRecurse 1069 * @type {Boolean} 1070 */ 1071 Tracer.prototype.traceAll = function (oRoot, bRecurse) 1072 { 1073 var sKey = ''; 1074 var oThat = null; 1075 if ((oRoot === win) || !((typeof oRoot === 'object')) || (typeof oRoot === 'function')) { 1076 return; 1077 } 1078 for(sKey in oRoot) { 1079 if (oRoot[sKey] !== oRoot) 1080 { 1081 oThat = oRoot[sKey]; 1082 if (typeof oThat === 'function') { 1083 if ((this !== oRoot) && !oThat.oConstructor && !this.rNativeCode.test(oThat)) { 1084 oRoot[sKey] = this.trace(oRoot[sKey], sKey); 1085 this.addTracing({ 1086 oObj: oRoot, 1087 sMethodName: sKey 1088 }); 1089 } 1090 } 1091 bRecurse && this.traceAll(oThat, true); 1092 } 1093 } 1094 }; 1095 /** 1096 * resetTracing removes all the tracable elements 1097 * @member Tracer.prototype 1098 */ 1099 Tracer.prototype.resetTracing = function () { 1100 this.aTracking = []; 1101 }; 1102 /** 1103 * untraceAll removes all the tracable elements and restore the original object 1104 * @member Tracer.prototype 1105 */ 1106 Tracer.prototype.untraceAll = function () { 1107 var nTrace = 0; 1108 var aTracing = this.aTracing; 1109 var nLenTrace = aTracing.length; 1110 var oTrace = null; 1111 for (; nTrace < nLenTrace; nTrace++) { 1112 oTrace = aTracing[nTrace]; 1113 oTrace.oObj[oTrace.sMethodName] = oTrace.oObj[oTrace.sMethodName].oConstructor; 1114 } 1115 oHermes.addError(new TraceMessage("Tracing disabled")); 1116 this.resetTracing(); 1117 }; 1118 /* 1119 * oHermes is the instance of Hermes. 1120 */ 1121 oHermes = new Hermes(Level.ALL, Hermes.IMMEDIATE).addAppender(new ConsoleAppender()); 1122 /* 1123 * fpErrorTrap is the method that will be called when uncaught errors are launched 1124 * Returns false to avoid default behaviour. 1125 * @return {Boolean} 1126 */ 1127 win.onerror = function fpErrorTrap(oErrorMsg, sFileNameUrl, nLineNumber) { 1128 oHermes.addError(new ErrorExt(oHermes.oLevel, getTypeFromMessage(oErrorMsg), removeTypeFromMessage(oErrorMsg), sFileNameUrl, nLineNumber)); 1129 return false; 1130 }; 1131 /* 1132 * This object expose the private classes as global to use it from outside of the module. 1133 */ 1134 win.Hermes = { 1135 logger: oHermes, 1136 appender: Appender, 1137 level: Level, 1138 error: ErrorExt, 1139 layout: Layout, 1140 tracer: new Tracer() 1141 }; 1142 }(window)); 1143