1 /** Licensed Materials - Property of IBM, 5724-E76 and 5724-E77, (C) Copyright IBM Corp. 2011, 2012 - All Rights reserved. **/ 2 (function(){ 3 /** 4 * Support for frames 5 */ 6 var i$ = window.i$; 7 8 /** 9 * Entry point for accessing functions that provide logging and tracing capabilities. 10 * @name i$.log 11 * @namespace Contains static functions and constants related to logging capabilities 12 */ 13 var __log = i$.log = {}; 14 15 var _loggers = []; 16 var _handlers = []; 17 var _regExp = null; 18 var _EVENT_BASE_TOPIC = "i$.log."; 19 20 // helper functions 21 var _isArray = function(o){ 22 return o && !(typeof o === "string") && !(typeof o === "function" || o instanceof Function) && 23 (o instanceof Array || typeof o == "array" || o.length > 0); // this really only checks array likeness 24 }; 25 26 var _substitute = function(template, map){ 27 return template.replace(/\$\{([^\s\:\}]+)(?:\:([^\s\:\}]+))?\}/g, 28 function(match, key, format){ // format is not supported 29 var value = map[key];//lang.getObject(key, false, map); 30 return value; 31 }); // String 32 }; 33 34 /** 35 * A Logger object is used to log messages for a specific system or application component. Loggers are 36 * normally named, using a hierarchical dot-separated namespace. Logger names can be arbitrary strings, 37 * but they should normally be based on the package name or class name of the logged component, 38 * such as <tt>com.ibm.wps.component</tt> or <tt>com.ibm.wps.component.Class</tt>. 39 * <br/><br/> 40 * Logger objects may be obtained by calls to i$.getLogger factory methods. 41 * These will either create a new Logger or return a suitable existing Logger. 42 * <br/><br/> 43 * Logging messages will be forwarded to registered Handler objects, which can forward the messages to 44 * a variety of destinations, including consoles, files, OS logs, etc. 45 * <br/><br/> 46 * Each Logger keeps track of a "parent" Logger, which is its nearest existing ancestor in the 47 * Logger namespace. The default Enabler logging framework uses only one parent logger, the "root Logger" 48 * <br/><br/> 49 * On each logging call the Logger initially performs a cheap check of the request level 50 * (e.g. <tt>SEVERE</tt> or <tt>TRACE</tt>) against the effective global log level for the logger. 51 * If the request level is lower than the log level, the logging call returns immediately. 52 * <br/><br/> 53 * After passing this initial (cheap) test, the Logger will publish a the specified paramters to its 54 * output Handlers. By default, if the logger is not the root logger, it also publishes to it's parent's Handlers. 55 * <br/><br/> 56 * Most of the logger output methods take a "message" argument. 57 * This message argument may be either a raw value or a format string. During formatting, the format 58 * string will be filled in with the specified parameters. Otherwise the original message string is used. 59 * Formatting parameters: A format string "<tt>${0} ${1}</tt>" would format two parameters as strings. 60 * <br/><br/> 61 * A global JS object can be used to configure the loggers that are considered for tracing. <br /> 62 * This object is named <code>traceConfig</code> and can be accessed through the 63 * {@link com.ibm.mashups.enabler.services.ConfigService ConfigService} or initialized by <tt>ConfigService.properties</tt><br/><br/> 64 * <pre>[ 65 * "com.ibm.mashups.*",</br> 66 * "com.ibm.mm.*" 67 * ]</pre><br />Additional to the classes that should be logged the <code>isDebug</code> Flag must be set to <code>true</code>. 68 * 69 * <p><b>Please note: For faster access this object has been made available directly on i$ as i$.Logger.</b></p> 70 * 71 * @name i$.log.Logger 72 * @class Class representing a Logger 73 * 74 * @constructor 75 */ 76 i$.Logger = function(name) { 77 // make sure those two are really null 78 this.name = name?name:null; 79 }; 80 __log.Logger = i$.Logger; 81 82 i$.mash(__log, /** @lends i$.log*/{ 83 /** 84 * TRACE is a message level providing tracing information.<br/><br/> 85 * TRACE is intended for relatively detailed tracing. The exact meaning of the this levels will vary 86 * between subsystems. 87 * In general the TRACE level should be used for information that will be broadly interesting to developers 88 * who do not have a specialized interest in the specific subsystem.<br/><br/> 89 * TRACE messages might include things like minor (recoverable) failures. Issues indicating potential 90 * performance problems are also worth logging as FINE. This level is initialized to <code>500</code>. 91 * @type {int} 92 */ 93 LEVEL_TRACE: 500, 94 95 /** 96 * INFO is a message level for informational messages.<br/><br/> 97 * Typically INFO messages will be written to the console or its equivalent (like the system messages widget). 98 * So the INFO level should only be used for reasonably significant messages that will make sense 99 * to end users and system admins. This level is initialized to <code>800</code>. 100 * @type {int} 101 */ 102 LEVEL_INFO: 800, 103 104 /** 105 * WARNING is a message level indicating a potential problem.<br/><br/> 106 * In general WARNING messages should describe events that will be of interest to end users or system 107 * managers, or which indicate potential problems. This level is initialized to <code>900</code>. 108 * @type {int} 109 */ 110 LEVEL_WARNING: 900, 111 112 /** 113 * SEVERE is a message level indicating a serious failure.<br/><br/> 114 * In general SEVERE messages should describe events that are of considerable importance and which 115 * will prevent normal program execution. They should be reasonably intelligible to end users and to 116 * system administrators. This level is initialized to <code>1000</code>. 117 * @type {int} 118 */ 119 LEVEL_SEVERE: 1000 120 121 }); 122 123 var __LEVEL_TRACE = __log.LEVEL_TRACE; 124 var __LEVEL_INFO = __log.LEVEL_INFO; 125 var __LEVEL_WARNING = __log.LEVEL_WARNING; 126 var __LEVEL_SEVERE = __log.LEVEL_SEVERE; 127 128 i$.mash(__log, /** @lends i$*/ { 129 130 /** 131 * Find or create a logger for a named subsystem. If a logger has already been created with the given name 132 * it is returned. Otherwise a new logger is created. 133 * 134 * <p><b>Please note: For faster access this object has been made available directly on i$ as i$.getLogger.</b></p> 135 * 136 * @memberOf i$.log 137 * @name getLogger 138 * @function 139 * @param {String} loggerName A name for the logger. This should be a dot-separated name and should 140 * normally be based on the package name or class name of the subsystem, 141 * such as com.ibm.wps.component or com.ibm.wps.component.Class 142 * @return {i$.log.Logger} a suitable Logger 143 */ 144 getLogger: function(loggerName) { 145 // since we are here we have no parent and need to handle everything by ourself 146 if (!_loggers[loggerName]) { 147 _loggers[loggerName] = new i$.Logger(loggerName); 148 } 149 return _loggers[loggerName]; 150 }, 151 /** 152 * @private 153 */ 154 addHandler: function(handler) { 155 _handlers[handler.handlerID] = {}; 156 var handlerObject = _handlers[handler.handlerID]; 157 handlerObject.handler = handler; 158 159 // get the handlers desired log level and subscribe him with respect to priority 160 // ordering including from RTL severe, warning, info, trace 161 var logLevel = handler.getLogLevel(); 162 var _addListener = i$.addListener; 163 164 // always subscribe to severe 165 handlerObject.severeHandle = _addListener(_EVENT_BASE_TOPIC+__LEVEL_SEVERE, handler.log); 166 167 // subscribe to warning, if warning or finer 168 if (logLevel <= __LEVEL_WARNING) { 169 handlerObject.warningHandle = _addListener(_EVENT_BASE_TOPIC+__LEVEL_WARNING, handler.log); 170 } 171 // subscribe to info, if info or finer 172 if (logLevel <= __LEVEL_INFO) { 173 handlerObject.infoHandle = _addListener(_EVENT_BASE_TOPIC+__LEVEL_INFO, handler.log); 174 } 175 // subscribe to trace, if trace or finer 176 if (logLevel <= __LEVEL_TRACE) { 177 handlerObject.traceHandle = _addListener(_EVENT_BASE_TOPIC+__LEVEL_TRACE, handler.log); 178 } 179 }, 180 /** 181 * @private 182 */ 183 /* removed to save bytes 184 removeHandler: function(handler) { 185 var handlerObject; 186 var handlerID; 187 if (typeof handler === "string") { 188 if (!handler in _handlers) { 189 return; 190 } 191 handlerObject = _handlers[handler]; 192 handlerID = handler; 193 } 194 else if (typeof handler === "object") { 195 handlerObject = _handlers[handler.getHandlerID()]; 196 handlerID = handler.getHandlerID(); 197 } 198 else { 199 return; 200 } 201 if (handlerObject) { 202 if (handlerObject.severeHandle) { 203 i$.removeListener(handlerObject.severeHandle); 204 } 205 if (handlerObject.warningHandle) { 206 i$.removeListener(handlerObject.warningHandle); 207 } 208 if (handlerObject.infoHandle) { 209 i$.removeListener(handlerObject.infoHandle); 210 } 211 if (handlerObject.traceHandle) { 212 i$.removeListener(handlerObject.traceHandle); 213 } 214 if (handlerObject.handler) { 215 delete handlerObject.handler; 216 } 217 } 218 delete _handlers[handlerID]; 219 }, 220 */ 221 setTraceConfig: function(traceConfig, setAsCookie) { 222 var rootLoggerString = "RootLogger", methodString = "setTraceConfig", isString = (typeof traceConfig === "string"); 223 if (isString && traceConfig.length > 0 && traceConfig == "*") { 224 i$.Logger.prototype._log(rootLoggerString, __LEVEL_TRACE, methodString, "Invalid traceConfig specified: *"); 225 } 226 else if ((_isArray(traceConfig) || isString) && traceConfig.length > 0) { 227 var isArray = _isArray(traceConfig); 228 if (!isArray) { // check for serialized array 229 try { 230 traceConfig = i$.fromJson(traceConfig); 231 isArray = _isArray(traceConfig); 232 } 233 catch (e) {} // must not be a valid array 234 } 235 i$.Logger.prototype._log(rootLoggerString, __LEVEL_TRACE, methodString, "Changing traceConfig: " + traceConfig); 236 if (setAsCookie) i$.setCookie("wptheme.client.trace.config", encodeURI((isArray)?i$.toJson(traceConfig):traceConfig)); 237 var baseRegExp = (isArray)?traceConfig.join("|"):traceConfig; 238 if (baseRegExp.length > 0) { 239 _regExp = new RegExp(baseRegExp); 240 return; 241 } 242 } 243 else if (traceConfig===null) { 244 if (setAsCookie) { 245 i$.deleteCookie("wptheme.client.trace.config"); 246 i$.deleteCookie("com.ibm.portal.resourceaggregator.client.debug.mode"); 247 } 248 i$.Logger.prototype._log(rootLoggerString, __LEVEL_TRACE, methodString, "Disabling tracing."); 249 } 250 _regExp = null; 251 } 252 }); 253 i$.getLogger = i$.log.getLogger; 254 i$.setTraceConfig = i$.log.setTraceConfig; 255 256 i$.Logger.prototype = /** @lends i$.Logger.prototype*/{ 257 258 /** 259 * This is a convenience method that can be used to log entry to a method. A event with the message "ENTRY", log level <tt>TRACE</tt>, and the given methodName is published 260 * @param {String} methodName the name of the method . 261 * @param {Object[]} args array of arguments that's passed into the method. Those arguments will be added to the entering comment in a comma separated way. 262 * @type {void} 263 */ 264 entering: function(methodName, args) { 265 var logLevel = __LEVEL_TRACE; 266 if (this.isLoggable(logLevel)) { 267 var msg = this._getMessageString(args); 268 this._log(this.name, logLevel, methodName, "ENTRY" + (msg?" " + msg:"")); 269 } 270 }, 271 /** 272 * This is a convenience method that can be used to log returning of a method. A event with the message "RETURN", log level <tt>TRACE</tt>, and the given methodName is published. 273 * This method allows components to trace the exiting of the method. 274 * @param {String} methodName the name of the method . 275 * @param {Object[]} args array of arguments that's passed into the method. Those arguments will be added to the exiting comment in a comma separated way. 276 * @type {void} 277 */ 278 exiting: function(methodName, args) { 279 var logLevel = __LEVEL_TRACE; 280 if (this.isLoggable(logLevel)) { 281 var msg = this._getMessageString(args); 282 this._log(this.name, logLevel, methodName, "RETURN" + (msg?" " + msg:"")); 283 } 284 }, 285 /** 286 * This method allows components to log an <tt>INFO</tt> level event. 287 * @param {String} methodName the name of the method . 288 * @param {String} message the message that you want to log. 289 * @param {Object[]} args array of arguments that's passed into the method. Those arguments will be filled into the wildcards in your message. 290 * @type {void} 291 */ 292 info: function(methodName, message, args) { 293 this.log(__LEVEL_INFO, methodName, message, args); 294 }, 295 /** 296 * This method allows components to log an <tt>WARNING</tt> level event. 297 * @param {String} methodName the name of the method . 298 * @param {String} message the message that you want to log. 299 * @param {Object[]} args array of arguments that's passed into the method. Those arguments will be filled into the wildcards in your message. 300 * @type {void} 301 */ 302 warning: function(methodName, message, args) { 303 this.log(__LEVEL_WARNING, methodName, message, args); 304 }, 305 /** 306 * This method allows components to log an <tt>SEVERE</tt> level event. 307 * @param {String} methodName the name of the method . 308 * @param {String} message the message that you want to log. 309 * @param {Object[]} args array of arguments that's passed into the method. Those arguments will be filled into the wildcards in your message. 310 * @type {void} 311 */ 312 severe: function(methodName, message, args) { 313 this.log(__LEVEL_SEVERE, methodName, message, args); 314 }, 315 /** 316 * This method allows components to log an <tt>TRACE</tt> level event. 317 * @param {String} methodName the name of the method . 318 * @param {String} message the message that you want to log. 319 * @param {Object[]} args array of arguments that's passed into the method. Those arguments will be filled into the wildcards in your message. 320 * @type {void} 321 */ 322 trace: function(methodName, message, args) { 323 this.log(__LEVEL_TRACE, methodName, message, args); 324 }, 325 /** 326 * This method allows components to log a logging event for the specified {@link com.ibm.mashups.enabler.logging.LogLevel LogLevel} 327 * @param {int} logLevel the {@link com.ibm.mashups.enabler.logging.LogLevel LogLevel} to use for logging. 328 * @param {String} methodName the name of the method. 329 * @param {String} message the message that you want to log. 330 * @param {Object[]} args array of arguments that's passed into the method. Those arguments will be filled into the wildcards in your message. 331 * @type {void} 332 */ 333 log: function(logLevel, methodName, message, args) { 334 if (!this.name) { 335 this._log("i$.log.Logger", __LEVEL_WARNING, "{log|info|warning|severe|trace|entering|exiting}", 336 "You must not use the RootLogger (i$.Logger). Create your own logger for each class by using i$.getLogger."); 337 } 338 if (this.isLoggable(logLevel)) { 339 this._log(this.name, logLevel, methodName, message, args); 340 } 341 }, 342 /** 343 * Check if a message of the given level would actually be logged by this logger. 344 * @param {int} logLevel the {@link com.ibm.mashups.enabler.logging.LogLevel LogLevel} to check 345 * @type {boolean} 346 * @return <code>true</code> if the given message level is currently being logged. 347 */ 348 isLoggable: function(logLevel) { 349 // unless we want to also modify loglevels per package return true for LL > TRACE 350 if (logLevel != __LEVEL_TRACE) { 351 return true; 352 } 353 /* 354 // if not isDebug is set we don't want to log 355 if (!(ibmConfig && ibmConfig.isDebug)) { 356 return false; 357 } 358 */ 359 if (_regExp && _regExp.exec(this.name)) { 360 return true; 361 } 362 return false; 363 }, 364 /** 365 * @private 366 */ 367 _log: function(loggerName, logLevel, methodName, message, args) { 368 // for convinience wrap single objects to an object (that user doesn't need to). 369 if ((args && !_isArray(args)) || args === false) { 370 args = [args]; 371 } 372 373 var formattedMessage = args ? _substitute(message.toString(), args) : message; 374 375 // publish the logging event ... 376 i$.fireEvent(_EVENT_BASE_TOPIC + logLevel, [loggerName, logLevel, methodName, formattedMessage]); 377 }, 378 /** 379 * @private 380 */ 381 _getMessageString: function(args) { 382 var argc = 0; 383 var msg = ""; 384 if (args || args === false) { 385 msg += "[ "; 386 if (_isArray(args)) { 387 for (var i=0; i<args.length; ++i) { 388 var arg = args[i]; 389 if (msg.length > 2) { 390 msg += ", "; // make sure to add the separator for the output 391 } 392 if (typeof arg == "undefined") { 393 msg += "undefined"; 394 } 395 if (arg) { 396 msg += this._serialize(arg); 397 } 398 else if (arg === null) { 399 msg += "null"; // see if the value is null 400 } 401 else if (arg === false) { 402 msg += "false"; // see if the value is false 403 } 404 else { 405 msg += "n/a"; // otherwise there is no simple way to output the object 406 } 407 } 408 } else { // not an array but only one element 409 if (args) { 410 msg += this._serialize(args); 411 } 412 else if (args === null) { 413 msg += "null"; // see if the value is null 414 } 415 else if (args === false) { 416 msg += "false"; // see if the value is false 417 } 418 else { 419 msg += "n/a"; // otherwise there is no simple way to output the object 420 } 421 } 422 msg += " ]"; 423 } 424 return msg; 425 }, 426 427 _serialize: function(o) { 428 try { 429 if (i$.isFunction(o)) { 430 if (o.name) { 431 return o.name; 432 } else { 433 return "function"; 434 } 435 } else if (i$.isObject(o)) { 436 try { 437 return i$.toJson(o); 438 } catch (e) { 439 if (o.toString) { 440 return o.toString(); 441 } else { 442 return e; 443 } 444 } 445 } else if (o.toString) { 446 return o.toString(); 447 } 448 } 449 catch (e) { 450 // this can happen in case of access denied exceptions 451 return "Error accessing object: "+e; 452 } 453 } 454 455 }; 456 457 //add the default console handler 458 i$.log.addHandler({ 459 getHandlerID: function() { 460 return "ConsoleHandler"; 461 }, 462 getLogLevel: function() { 463 return __LEVEL_TRACE; 464 }, 465 log: function(loggerName, logLevel, method, message) { 466 if (logLevel == __LEVEL_SEVERE) { 467 console.error(loggerName + " " + method + ": " + message); 468 } 469 else if (logLevel == __LEVEL_WARNING) { 470 console.warn(loggerName + " " + method + ": " + message); 471 } 472 else if (logLevel == __LEVEL_INFO) { 473 console.info(loggerName + " " + method + ": " + message); 474 } 475 else if (logLevel == __LEVEL_TRACE) { 476 console.debug(loggerName + " " + method + ": " + message); 477 } 478 } 479 }); 480 481 if (typeof(__trace) == "undefined") { // for CBT 482 __trace = { 483 enter: function(clazz, method, args) { 484 if ((args) && (args.length==0)) args = null; 485 __log.getLogger(clazz).entering(method, args); 486 }, 487 msg: function(clazz, method, msg, args) { 488 __log.getLogger(clazz).trace(method, msg, args); 489 }, 490 evalFunction: function(js) { 491 return "function() {"+js+"}"; 492 }, 493 evalResult: function(clazz, method, msg) { 494 __log.getLogger(clazz).trace(method, msg); 495 }, 496 exit: function(clazz, method, retVal) { 497 __log.getLogger(clazz).exiting(method, retVal); 498 }, 499 assertFunction: function(condition) { 500 return "function() { if (!(" + condition + ")) { throw new Error('Assert failed');} }"; 501 } 502 }; 503 } 504 505 // initialize the logger 506 var _configLogger = function() { 507 try { 508 var traceCookie = i$.getCookie("wptheme.client.trace.config") 509 if (traceCookie!=null) { 510 i$.setTraceConfig(i$.fromJson(decodeURIComponent(traceCookie))); 511 } 512 else if (typeof ibmPortalConfig != "undefined" && ibmPortalConfig.traceConfig) { 513 i$.setTraceConfig(ibmPortalConfig.traceConfig); 514 } 515 else { 516 i$.setTraceConfig(); 517 } 518 } 519 catch (e) { 520 console.log(e); 521 } 522 }; 523 524 // we initialize twice, once directly, and then a second time once the page has been loaded 525 // in case the traceConfig wasn't set 526 _configLogger(); 527 i$.addOnLoad(function() { 528 _configLogger(); 529 530 //add the default status bar handler, if the status bar is on the page 531 if (i$.fromPath("com.ibm.widgets.StatusMessage")) { 532 i$.log.addHandler({ 533 getHandlerID: function() { 534 return "StatusBarHandler"; 535 }, 536 getLogLevel: function() { 537 return __LEVEL_INFO; 538 }, 539 log: function(loggerName, logLevel, method, message) { 540 var __statusBox = "ibmStatusBox"; 541 var __TOPIC = "/portal/status"; 542 var __fireEvent = i$.fireEvent; 543 var __StatusMessage = com.ibm.widgets.StatusMessage; 544 if (logLevel == __LEVEL_SEVERE) { 545 __fireEvent(__TOPIC, [{message: new __StatusMessage("error", loggerName + " " + method + ": " + message, ""), uid: __statusBox}]); 546 } 547 else if (logLevel == __LEVEL_WARNING) { 548 __fireEvent(__TOPIC, [{message: new __StatusMessage("warning", loggerName + " " + method + ": " + message, ""), uid: __statusBox}]); 549 } 550 else if (logLevel == __LEVEL_INFO) { 551 __fireEvent(__TOPIC, [{message: new __StatusMessage("info", loggerName + " " + method + ": " + message, ""), uid: __statusBox}]); 552 } 553 } 554 }); 555 } 556 557 }); 558 })(); 559