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