/* * Copyright [2011] [wisemapping] * * Licensed under WiseMapping Public License, Version 1.0 (the "License"). * It is basically the Apache License, Version 2.0 (the "License") plus the * "powered by wisemapping" text requirement on every single page; * you may not use this file except in compliance with the License. * You may obtain a copy of the license at * * http://www.wisemapping.org/license * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /*jsl:option explicit*/ /** * @fileoverview log4js is a library to log in JavaScript in similar manner * than in log4j for Java. The API should be nearly the same. * * This file contains all log4js code and is the only file required for logging. * *

Example:

*
 *  var log = new Log4js.getLogger("some-category-name"); //create logger instance
 *  log.setLevel(Log4js.Level.TRACE); //set the Level
 *  log.addAppender(new ConsoleAppender(log, false)); // console that launches in new window
 
 *  // if multiple appenders are set all will log
 *  log.addAppender(new ConsoleAppender(log, true)); // console that is in-line in the page
 *  log.addAppender(new FileAppender("C:\\somefile.log")); // file appender logs to C:\\somefile.log
 * 
 *  ...
 * 
 *  //call the log
 *  log.trace("trace me" );
 * 
* * @version 0.3 * @author Stephan Strittmatter - http://jroller.com/page/stritti * @author Seth Chisamore - http://www.chisamore.com * @since 2005-05-20 * Website: http://log4js.berlios.de */ var Log4js = { /** * Current version of log4js. * @static * @final */ version: "1.0", /** * Date of logger initialized. * @static * @final */ applicationStartDate: new Date(), /** * Hashtable of loggers. * @static * @final * @private */ loggers: {}, /** * Get a logger instance. Instance is cached on categoryName level. * @param {String} categoryName name of category to log to. * @return {Logger} instance of logger for the category * @static */ getLogger: function(categoryName) { // Use default logger if categoryName is not specified or invalid if (!(typeof categoryName == "string")) { categoryName = "[default]"; } if (!Log4js.loggers[categoryName]) { // Create the logger for this name if it doesn't already exist Log4js.loggers[categoryName] = new Log4js.Logger(categoryName); } return Log4js.loggers[categoryName]; }, /** * Get the default logger instance. * @return {Logger} instance of default logger * @static */ getDefaultLogger: function() { return Log4js.getLogger("[default]"); }, /** * Atatch an observer function to an elements event browser independent. * * @param element element to attach event * @param name name of event * @param observer observer method to be called * @private */ attachEvent: function (element, name, observer) { if (element.addEventListener) { //DOM event model element.addEventListener(name, observer, false); } else if (element.attachEvent) { //M$ event model element.attachEvent('on' + name, observer); } } /** * Load a JS-script dynamically. * @param {String} src */ /* $import: function (src) { var documentScripts = document.getElementsByTagName("script"); for (index = 0; index < documentScripts.length; ++index) { var documentScript = documentScripts[index]; if (documentScript.src == src) { return false; } } var script = document.createElement('script'); script.type = 'text/javascript'; script.src = src; document.getElementsByTagName('head')[0].appendChild(script); return true; } */ }; /** * Internal object extension (OO) methods. * * @private * @ignore */ Log4js.extend = function(destination, source) { for (property in source) { destination[property] = source[property]; } return destination; } /** * Functions taken from Prototype library, * didn't want to require for just few functions. * More info at {@link http://prototype.conio.net/} * @private */ Log4js.bind = function(fn, object) { return function() { return fn.apply(object, arguments); }; }; /** * Log4js.Level Enumeration. Do not use directly. Use static objects instead. * @constructor * @param {Number} level number of level * @param {String} levelString String representation of level * @private */ Log4js.Level = function(level, levelStr) { this.level = level; this.levelStr = levelStr; }; Log4js.Level.prototype = { /** * converts given String to corresponding Level * @param {String} sArg String value of Level * @param {Log4js.Level} defaultLevel default Level, if no String representation * @return Level object * @type Log4js.Level */ toLevel: function(sArg, defaultLevel) { if(sArg === null) { return defaultLevel; } if(typeof sArg == "string") { var s = sArg.toUpperCase(); if(s == "ALL") {return Log4js.Level.ALL;} if(s == "DEBUG") {return Log4js.Level.DEBUG;} if(s == "INFO") {return Log4js.Level.INFO;} if(s == "WARN") {return Log4js.Level.WARN;} if(s == "ERROR") {return Log4js.Level.ERROR;} if(s == "FATAL") {return Log4js.Level.FATAL;} if(s == "OFF") {return Log4js.Level.OFF;} if(s == "TRACE") {return Log4js.Level.TRACE;} return defaultLevel; } else if(typeof sArg == "number") { switch(sArg) { case ALL_INT: return Log4js.Level.ALL; case DEBUG_INT: return Log4js.Level.DEBUG; case INFO_INT: return Log4js.Level.INFO; case WARN_INT: return Log4js.Level.WARN; case ERROR_INT: return Log4js.Level.ERROR; case FATAL_INT: return Log4js.Level.FATAL; case OFF_INT: return Log4js.Level.OFF; case TRACE_INT: return Log4js.Level.TRACE; default: return defaultLevel; } } else { return defaultLevel; } }, /** * @return converted Level to String * @type String */ toString: function() { return this.levelStr; }, /** * @return internal Number value of Level * @type Number */ valueOf: function() { return this.level; } }; // Static variables /** * @private */ Log4js.Level.OFF_INT = Number.MAX_VALUE; /** * @private */ Log4js.Level.FATAL_INT = 50000; /** * @private */ Log4js.Level.ERROR_INT = 40000; /** * @private */ Log4js.Level.WARN_INT = 30000; /** * @private */ Log4js.Level.INFO_INT = 20000; /** * @private */ Log4js.Level.DEBUG_INT = 10000; /** * @private */ Log4js.Level.TRACE_INT = 5000; /** * @private */ Log4js.Level.ALL_INT = Number.MIN_VALUE; /** * Logging Level OFF - all disabled * @type Log4js.Level * @static */ Log4js.Level.OFF = new Log4js.Level(Log4js.Level.OFF_INT, "OFF"); /** * Logging Level Fatal * @type Log4js.Level * @static */ Log4js.Level.FATAL = new Log4js.Level(Log4js.Level.FATAL_INT, "FATAL"); /** * Logging Level Error * @type Log4js.Level * @static */ Log4js.Level.ERROR = new Log4js.Level(Log4js.Level.ERROR_INT, "ERROR"); /** * Logging Level Warn * @type Log4js.Level * @static */ Log4js.Level.WARN = new Log4js.Level(Log4js.Level.WARN_INT, "WARN"); /** * Logging Level Info * @type Log4js.Level * @static */ Log4js.Level.INFO = new Log4js.Level(Log4js.Level.INFO_INT, "INFO"); /** * Logging Level Debug * @type Log4js.Level * @static */ Log4js.Level.DEBUG = new Log4js.Level(Log4js.Level.DEBUG_INT, "DEBUG"); /** * Logging Level Trace * @type Log4js.Level * @static */ Log4js.Level.TRACE = new Log4js.Level(Log4js.Level.TRACE_INT, "TRACE"); /** * Logging Level All - All traces are enabled * @type Log4js.Level * @static */ Log4js.Level.ALL = new Log4js.Level(Log4js.Level.ALL_INT, "ALL"); /** * Log4js CustomEvent * @constructor * @author Corey Johnson - original code in Lumberjack (http://gleepglop.com/javascripts/logger/) * @author Seth Chisamore - adapted for Log4js * @private */ Log4js.CustomEvent = function() { this.listeners = []; }; Log4js.CustomEvent.prototype = { /** * @param method method to be added */ addListener : function(method) { this.listeners.push(method); }, /** * @param method method to be removed */ removeListener : function(method) { var foundIndexes = this.findListenerIndexes(method); for(var i = 0; i < foundIndexes.length; i++) { this.listeners.splice(foundIndexes[i], 1); } }, /** * @param handler */ dispatch : function(handler) { for(var i = 0; i < this.listeners.length; i++) { try { this.listeners[i](handler); } catch (e) { log4jsLogger.warn("Could not run the listener " + this.listeners[i] + ". \n" + e); } } }, /** * @private * @param method */ findListenerIndexes : function(method) { var indexes = []; for(var i = 0; i < this.listeners.length; i++) { if (this.listeners[i] == method) { indexes.push(i); } } return indexes; } }; /** * Models a logging event. * @constructor * @param {String} categoryName name of category * @param {Log4js.Level} level level of message * @param {String} message message to log * @param {Log4js.Logger} logger the associated logger * @author Seth Chisamore */ Log4js.LoggingEvent = function(categoryName, level, message, exception, logger) { /** * the timestamp of the Logging Event * @type Date * @private */ this.startTime = new Date(); /** * category of event * @type String * @private */ this.categoryName = categoryName; /** * the logging message * @type String * @private */ this.message = message; /** * the logging exception * @type Exception * @private */ this.exception = exception; /** * level of log * @type Log4js.Level * @private */ this.level = level; /** * reference to logger * @type Log4js.Logger * @private */ this.logger = logger; }; Log4js.LoggingEvent.prototype = { /** * get the timestamp formatted as String. * @return {String} formatted timestamp * @see Log4js#setDateFormat() */ getFormattedTimestamp: function() { if(this.logger) { return this.logger.getFormattedTimestamp(this.startTime); } else { return this.startTime.toGMTString(); } } }; /** * Logger to log messages to the defined appender.

* Default appender is Appender, which is ignoring all messages. Please * use setAppender() to set a specific appender (e.g. WindowAppender). * use {@see Log4js#getLogger(String)} to get an instance. * @constructor * @param name name of category to log to * @author Stephan Strittmatter */ Log4js.Logger = function(name) { this.loggingEvents = []; this.appenders = []; /** category of logger */ this.category = name || ""; /** level to be logged */ this.level = Log4js.Level.FATAL; this.dateformat = Log4js.DateFormatter.DEFAULT_DATE_FORMAT; this.dateformatter = new Log4js.DateFormatter(); this.onlog = new Log4js.CustomEvent(); this.onclear = new Log4js.CustomEvent(); /** appender to write in */ this.appenders.push(new Log4js.Appender(this)); // if multiple log objects are instantiated this will only log to the log // object that is declared last can't seem to get the attachEvent method to // work correctly try { window.onerror = this.windowError.bind(this); } catch (e) { //log4jsLogger.fatal(e); } }; Log4js.Logger.prototype = { /** * add additional appender. DefaultAppender always is there. * @param appender additional wanted appender */ addAppender: function(appender) { if (appender instanceof Log4js.Appender) { appender.setLogger(this); this.appenders.push(appender); } else { throw "Not instance of an Appender: " + appender; } }, /** * set Array of appenders. Previous Appenders are cleared and removed. * @param {Array} appenders Array of Appenders */ setAppenders: function(appenders) { //clear first all existing appenders for(var i = 0; i < this.appenders.length; i++) { this.appenders[i].doClear(); } this.appenders = appenders; for(var j = 0; j < this.appenders.length; j++) { this.appenders[j].setLogger(this); } }, /** * Set the Loglevel default is LogLEvel.TRACE * @param level wanted logging level */ setLevel: function(level) { this.level = level; }, /** * main log method logging to all available appenders * @private */ log: function(logLevel, message, exception) { var loggingEvent = new Log4js.LoggingEvent(this.category, logLevel, message, exception, this); this.loggingEvents.push(loggingEvent); this.onlog.dispatch(loggingEvent); }, /** clear logging */ clear : function () { try{ this.loggingEvents = []; this.onclear.dispatch(); } catch(e){} }, /** checks if Level Trace is enabled */ isTraceEnabled: function() { if (this.level.valueOf() <= Log4js.Level.TRACE.valueOf()) { return true; } return false; }, /** * Trace messages * @param message {Object} message to be logged */ trace: function(message) { if (this.isTraceEnabled()) { this.log(Log4js.Level.TRACE, message, null); } }, /** checks if Level Debug is enabled */ isDebugEnabled: function() { if (this.level.valueOf() <= Log4js.Level.DEBUG.valueOf()) { return true; } return false; }, /** * Debug messages * @param message {Object} message to be logged */ debug: function(message) { if (this.isDebugEnabled()) { this.log(Log4js.Level.DEBUG, message, null); } }, /** * Debug messages * @param {Object} message message to be logged * @param {Throwable} throwable */ debug: function(message, throwable) { if (this.isDebugEnabled()) { this.log(Log4js.Level.DEBUG, message, throwable); } }, /** checks if Level Info is enabled */ isInfoEnabled: function() { if (this.level.valueOf() <= Log4js.Level.INFO.valueOf()) { return true; } return false; }, /** * logging info messages * @param {Object} message message to be logged */ info: function(message) { if (this.isInfoEnabled()) { this.log(Log4js.Level.INFO, message, null); } }, /** * logging info messages * @param {Object} message message to be logged * @param {Throwable} throwable */ info: function(message, throwable) { if (this.isInfoEnabled()) { this.log(Log4js.Level.INFO, message, throwable); } }, /** checks if Level Warn is enabled */ isWarnEnabled: function() { if (this.level.valueOf() <= Log4js.Level.WARN.valueOf()) { return true; } return false; }, /** logging warn messages */ warn: function(message) { if (this.isWarnEnabled()) { this.log(Log4js.Level.WARN, message, null); } }, /** logging warn messages */ warn: function(message, throwable) { if (this.isWarnEnabled()) { this.log(Log4js.Level.WARN, message, throwable); } }, /** checks if Level Error is enabled */ isErrorEnabled: function() { if (this.level.valueOf() <= Log4js.Level.ERROR.valueOf()) { return true; } return false; }, /** logging error messages */ error: function(message) { if (this.isErrorEnabled()) { this.log(Log4js.Level.ERROR, message, null); } }, /** logging error messages */ error: function(message, throwable) { if (this.isErrorEnabled()) { this.log(Log4js.Level.ERROR, message, throwable); } }, /** checks if Level Fatal is enabled */ isFatalEnabled: function() { if (this.level.valueOf() <= Log4js.Level.FATAL.valueOf()) { return true; } return false; }, /** logging fatal messages */ fatal: function(message) { if (this.isFatalEnabled()) { this.log(Log4js.Level.FATAL, message, null); } }, /** logging fatal messages */ fatal: function(message, throwable) { if (this.isFatalEnabled()) { this.log(Log4js.Level.FATAL, message, throwable); } }, /** * Capture main window errors and log as fatal. * @private */ windowError: function(msg, url, line){ var message = "Error in (" + (url || window.location) + ") on line "+ line +" with message (" + msg + ")"; this.log(Log4js.Level.FATAL, message, null); }, /** * Set the date format of logger. Following switches are supported: * * @param {String} format format String for the date * @see #getTimestamp */ setDateFormat: function(format) { this.dateformat = format; }, /** * Generates a timestamp using the format set in {Log4js.setDateFormat}. * @param {Date} date the date to format * @see #setDateFormat * @return A formatted timestamp with the current date and time. */ getFormattedTimestamp: function(date) { return this.dateformatter.formatDate(date, this.dateformat); } }; /** * Abstract base class for other appenders. * It is doing nothing. * * @constructor * @param {Log4js.Logger} logger log4js instance this appender is attached to * @author Stephan Strittmatter */ Log4js.Appender = function () { /** * Reference to calling logger * @type Log4js.Logger * @private */ this.logger = null; }; Log4js.Appender.prototype = { /** * appends the given loggingEvent appender specific * @param {Log4js.LoggingEvent} loggingEvent loggingEvent to append */ doAppend: function(loggingEvent) { return; }, /** * clears the Appender */ doClear: function() { return; }, /** * Set the Layout for this appender. * @param {Log4js.Layout} layout Layout for formatting loggingEvent */ setLayout: function(layout){ this.layout = layout; }, /** * Set reference to the logger. * @param {Log4js.Logger} the invoking logger */ setLogger: function(logger){ // add listener to the logger methods logger.onlog.addListener(Log4js.bind(this.doAppend, this)); logger.onclear.addListener(Log4js.bind(this.doClear, this)); this.logger = logger; } }; /** * Interface for Layouts. * Use this Layout as "interface" for other Layouts. It is doing nothing. * * @constructor * @author Stephan Strittmatter */ Log4js.Layout = function(){return;}; Log4js.Layout.prototype = { /** * Implement this method to create your own layout format. * @param {Log4js.LoggingEvent} loggingEvent loggingEvent to format * @return formatted String * @type String */ format: function(loggingEvent) { return ""; }, /** * Returns the content type output by this layout. * @return The base class returns "text/plain". * @type String */ getContentType: function() { return "text/plain"; }, /** * @return Returns the header for the layout format. The base class returns null. * @type String */ getHeader: function() { return null; }, /** * @return Returns the footer for the layout format. The base class returns null. * @type String */ getFooter: function() { return null; }, /** * @return Separator between events * @type String */ getSeparator: function() { return ""; } }; /** * Console Appender writes the logs to a console. If "inline" is * set to "false" the console launches in another window otherwise * the window is inline on the page and toggled on and off with "Alt-D". * Note: At FireFox &gb; 2.0 the keystroke is little different now: "SHIFT+ALT+D". * * @constructor * @extends Log4js.Appender * @param {boolean} isInline boolean value that indicates whether the console be placed inline, default is to launch in new window * * @author Corey Johnson - original console code in Lumberjack (http://gleepglop.com/javascripts/logger/) * @author Seth Chisamore - adapted for use as a log4js appender */ Log4js.ConsoleAppender = function(isInline) { /** * @type Log4js.Layout * @private */ this.layout = new Log4js.PatternLayout(Log4js.PatternLayout.TTCC_CONVERSION_PATTERN); /** * @type boolean * @private */ this.inline = isInline; /** * @type String * @private */ this.accesskey = "d"; /** * @private */ this.tagPattern = null; this.commandHistory = []; this.commandIndex = 0; /** * true if popup is blocked. */ this.popupBlocker = false; /** * current output div-element. */ this.outputElement = null; this.docReference = null; this.winReference = null; if(this.inline) { Log4js.attachEvent(window, 'load', Log4js.bind(this.initialize, this)); } }; Log4js.ConsoleAppender.prototype = Log4js.extend(new Log4js.Appender(), { /** * Set the access key to show/hide the inline console (default "e;d"e;) * @param key access key to show/hide the inline console */ setAccessKey : function(key) { this.accesskey = key; }, /** * @private */ initialize : function() { if(!this.inline) { var doc = null; var win = null; window.top.consoleWindow = window.open("", this.logger.category, "left=0,top=0,width=700,height=700,scrollbars=no,status=no,resizable=yes;toolbar=no"); window.top.consoleWindow.opener = self; win = window.top.consoleWindow; if (!win) { this.popupBlocker=true; alert("Popup window manager blocking the Log4js popup window to bedisplayed.\n\n" + "Please disabled this to properly see logged events."); } else { doc = win.document; doc.open(); doc.write("\n\n"); doc.write("Log4js - " + this.logger.category + "\n"); doc.write("\n"); win.blur(); win.focus(); } this.docReference = doc; this.winReference = win; } else { this.docReference = document; this.winReference = window; } this.outputCount = 0; this.tagPattern = ".*"; // I hate writing javascript in HTML... but what's a better alternative this.logElement = this.docReference.createElement('div'); this.docReference.body.appendChild(this.logElement); this.logElement.style.display = 'none'; this.logElement.style.position = "absolute"; this.logElement.style.left = '0px'; this.logElement.style.width = '100%'; this.logElement.style.textAlign = "left"; this.logElement.style.fontFamily = "lucida console"; this.logElement.style.fontSize = "100%"; this.logElement.style.backgroundColor = 'darkgray'; this.logElement.style.opacity = 0.9; this.logElement.style.zIndex = 2000; // Add toolbarElement this.toolbarElement = this.docReference.createElement('div'); this.logElement.appendChild(this.toolbarElement); this.toolbarElement.style.padding = "0 0 0 2px"; // Add buttons this.buttonsContainerElement = this.docReference.createElement('span'); this.toolbarElement.appendChild(this.buttonsContainerElement); if(this.inline) { var closeButton = this.docReference.createElement('button'); closeButton.style.cssFloat = "right"; closeButton.style.styleFloat = "right"; // IE dom bug...doesn't understand cssFloat closeButton.style.color = "black"; closeButton.innerHTML = "close"; closeButton.onclick = Log4js.bind(this.toggle, this); this.buttonsContainerElement.appendChild(closeButton); } var clearButton = this.docReference.createElement('button'); clearButton.style.cssFloat = "right"; clearButton.style.styleFloat = "right"; // IE dom bug...doesn't understand cssFloat clearButton.style.color = "black"; clearButton.innerHTML = "clear"; clearButton.onclick = Log4js.bind(this.logger.clear, this.logger); this.buttonsContainerElement.appendChild(clearButton); //Add CategoryName and Level Filter this.tagFilterContainerElement = this.docReference.createElement('span'); this.toolbarElement.appendChild(this.tagFilterContainerElement); this.tagFilterContainerElement.style.cssFloat = 'left'; this.tagFilterContainerElement.appendChild(this.docReference.createTextNode("Log4js - " + this.logger.category)); this.tagFilterContainerElement.appendChild(this.docReference.createTextNode(" | Level Filter: ")); this.tagFilterElement = this.docReference.createElement('input'); this.tagFilterContainerElement.appendChild(this.tagFilterElement); this.tagFilterElement.style.width = '200px'; this.tagFilterElement.value = this.tagPattern; this.tagFilterElement.setAttribute('autocomplete', 'off'); // So Firefox doesn't flip out Log4js.attachEvent(this.tagFilterElement, 'keyup', Log4js.bind(this.updateTags, this)); Log4js.attachEvent(this.tagFilterElement, 'click', Log4js.bind( function() {this.tagFilterElement.select();}, this)); // Add outputElement this.outputElement = this.docReference.createElement('div'); this.logElement.appendChild(this.outputElement); this.outputElement.style.overflow = "auto"; this.outputElement.style.clear = "both"; this.outputElement.style.height = (this.inline) ? ("200px"):("650px"); this.outputElement.style.width = "100%"; this.outputElement.style.backgroundColor = 'black'; this.inputContainerElement = this.docReference.createElement('div'); this.inputContainerElement.style.width = "100%"; this.logElement.appendChild(this.inputContainerElement); this.inputElement = this.docReference.createElement('input'); this.inputContainerElement.appendChild(this.inputElement); this.inputElement.style.width = '100%'; this.inputElement.style.borderWidth = '0px'; // Inputs with 100% width always seem to be too large (I HATE THEM) they only work if the border, margin and padding are 0 this.inputElement.style.margin = '0px'; this.inputElement.style.padding = '0px'; this.inputElement.value = 'Type command here'; this.inputElement.setAttribute('autocomplete', 'off'); // So Firefox doesn't flip out Log4js.attachEvent(this.inputElement, 'keyup', Log4js.bind(this.handleInput, this)); Log4js.attachEvent(this.inputElement, 'click', Log4js.bind( function() {this.inputElement.select();}, this)); if(this.inline){ window.setInterval(Log4js.bind(this.repositionWindow, this), 500); this.repositionWindow(); // Allow acess key link var accessElement = this.docReference.createElement('button'); accessElement.style.position = "absolute"; accessElement.style.top = "-100px"; accessElement.accessKey = this.accesskey; accessElement.onclick = Log4js.bind(this.toggle, this); this.docReference.body.appendChild(accessElement); } else { this.show(); } }, /** * shows/hide an element * @private * @return true if shown */ toggle : function() { if (this.logElement.style.display == 'none') { this.show(); return true; } else { this.hide(); return false; } }, /** * @private */ show : function() { this.logElement.style.display = ''; this.outputElement.scrollTop = this.outputElement.scrollHeight; // Scroll to bottom when toggled this.inputElement.select(); }, /** * @private */ hide : function() { this.logElement.style.display = 'none'; }, /** * @private * @param message * @style */ output : function(message, style) { // If we are at the bottom of the window, then keep scrolling with the output var shouldScroll = (this.outputElement.scrollTop + (2 * this.outputElement.clientHeight)) >= this.outputElement.scrollHeight; this.outputCount++; style = (style ? style += ';' : ''); style += 'padding:1px;margin:0 0 5px 0'; if (this.outputCount % 2 === 0) { style += ";background-color:#101010"; } message = message || "undefined"; message = message.toString(); this.outputElement.innerHTML += "
" + message + "
"; if (shouldScroll) { this.outputElement.scrollTop = this.outputElement.scrollHeight; } }, /** * @private */ updateTags : function() { var pattern = this.tagFilterElement.value; if (this.tagPattern == pattern) { return; } try { new RegExp(pattern); } catch (e) { return; } this.tagPattern = pattern; this.outputElement.innerHTML = ""; // Go through each log entry again this.outputCount = 0; for (var i = 0; i < this.logger.loggingEvents.length; i++) { this.doAppend(this.logger.loggingEvents[i]); } }, /** * @private */ repositionWindow : function() { var offset = window.pageYOffset || this.docReference.documentElement.scrollTop || this.docReference.body.scrollTop; var pageHeight = self.innerHeight || this.docReference.documentElement.clientHeight || this.docReference.body.clientHeight; this.logElement.style.top = (offset + pageHeight - this.logElement.offsetHeight) + "px"; }, /** * @param loggingEvent event to be logged * @see Log4js.Appender#doAppend */ doAppend : function(loggingEvent) { if(this.popupBlocker) { //popup blocked, we return in this case return; } if ((!this.inline) && (!this.winReference || this.winReference.closed)) { this.initialize(); } if (this.tagPattern !== null && loggingEvent.level.toString().search(new RegExp(this.tagPattern, 'igm')) == -1) { return; } var style = ''; if (loggingEvent.level.toString().search(/ERROR/) != -1) { style += 'color:red'; } else if (loggingEvent.level.toString().search(/FATAL/) != -1) { style += 'color:red'; } else if (loggingEvent.level.toString().search(/WARN/) != -1) { style += 'color:orange'; } else if (loggingEvent.level.toString().search(/DEBUG/) != -1) { style += 'color:green'; } else if (loggingEvent.level.toString().search(/INFO/) != -1) { style += 'color:white'; } else { style += 'color:yellow'; } this.output(this.layout.format(loggingEvent), style); }, /** * @see Log4js.Appender#doClear */ doClear : function() { this.outputElement.innerHTML = ""; }, /** * @private * @param e */ handleInput : function(e) { if (e.keyCode == 13 ) { var command = this.inputElement.value; switch(command) { case "clear": this.logger.clear(); break; default: var consoleOutput = ""; try { consoleOutput = eval(this.inputElement.value); } catch (e) { this.logger.error("Problem parsing input <" + command + ">" + e.message); break; } this.logger.trace(consoleOutput); break; } if (this.inputElement.value !== "" && this.inputElement.value !== this.commandHistory[0]) { this.commandHistory.unshift(this.inputElement.value); } this.commandIndex = 0; this.inputElement.value = ""; } else if (e.keyCode == 38 && this.commandHistory.length > 0) { this.inputElement.value = this.commandHistory[this.commandIndex]; if (this.commandIndex < this.commandHistory.length - 1) { this.commandIndex += 1; } } else if (e.keyCode == 40 && this.commandHistory.length > 0) { if (this.commandIndex > 0) { this.commandIndex -= 1; } this.inputElement.value = this.commandHistory[this.commandIndex]; } else { this.commandIndex = 0; } }, /** * toString */ toString: function() { return "Log4js.ConsoleAppender[inline=" + this.inline + "]"; } }); /** * Metatag Appender writing the logs to meta tags * * @extends Log4js.Appender * @constructor * @param logger log4js instance this appender is attached to * @author Stephan Strittmatter */ Log4js.MetatagAppender = function() { this.currentLine = 0; }; Log4js.MetatagAppender.prototype = Log4js.extend(new Log4js.Appender(), { /** * @param loggingEvent event to be logged * @see Log4js.Appender#doAppend */ doAppend: function(loggingEvent) { var now = new Date(); var lines = loggingEvent.message.split("\n"); var headTag = document.getElementsByTagName("head")[0]; for (var i = 1; i <= lines.length; i++) { var value = lines[i - 1]; if (i == 1) { value = loggingEvent.level.toString() + ": " + value; } else { value = "> " + value; } var metaTag = document.createElement("meta"); metaTag.setAttribute("name", "X-log4js:" + this.currentLine); metaTag.setAttribute("content", value); headTag.appendChild(metaTag); this.currentLine += 1; } }, /** * toString */ toString: function() { return "Log4js.MetatagAppender"; } }); /** * AJAX Appender sending {@link Log4js.LoggingEvent}s asynchron via * XMLHttpRequest to server.
* The {@link Log4js.LoggingEvent} is POSTed as response content and is * formatted by the accociated layout. Default layout is {@link Log4js.XMLLayout}. * The threshold defines when the logs * should be send to the server. By default every event is sent on its * own (threshold=1). If it is set to 10, then the events are send in groups of * 10 events. * * @extends Log4js.Appender * @constructor * @param {Log4js.Logger} logger log4js instance this appender is attached to * @param {String} loggingUrl url where appender will post log messages to * @author Stephan Strittmatter */ Log4js.AjaxAppender = function(loggingUrl) { /** * is still esnding data to server * @type boolean * @private */ this.isInProgress = false; /** * @type String * @private */ this.loggingUrl = loggingUrl || "logging.log4js"; /** * @type Integer * @private */ this.threshold = 1; /** * timeout when request is aborted. * @private */ this.timeout = 2000; /** * List of LoggingEvents which should be send after threshold is reached. * @type Map * @private */ this.loggingEventMap = new Log4js.FifoBuffer(); /** * @type Log4js.Layout * @private */ this.layout = new Log4js.XMLLayout(); /** * @type XMLHttpRequest * @private */ this.httpRequest = null; }; Log4js.AjaxAppender.prototype = Log4js.extend(new Log4js.Appender(), { /** * sends the logs to the server * @param loggingEvent event to be logged * @see Log4js.Appender#doAppend */ doAppend: function(loggingEvent) { log4jsLogger.trace("> AjaxAppender.append"); if (this.loggingEventMap.length() <= this.threshold || this.isInProgress === true) { this.loggingEventMap.push(loggingEvent); } if(this.loggingEventMap.length() >= this.threshold && this.isInProgress === false) { //if threshold is reached send the events and reset current threshold this.send(); } log4jsLogger.trace("< AjaxAppender.append"); }, /** @see Appender#doClear */ doClear: function() { log4jsLogger.trace("> AjaxAppender.doClear" ); if(this.loggingEventMap.length() > 0) { this.send(); } log4jsLogger.trace("< AjaxAppender.doClear" ); }, /** * Set the threshold when logs have to be send. Default threshold is 1. * @praram {int} threshold new threshold */ setThreshold: function(threshold) { log4jsLogger.trace("> AjaxAppender.setThreshold: " + threshold ); this.threshold = threshold; log4jsLogger.trace("< AjaxAppender.setThreshold" ); }, /** * Set the timeout in milli seconds until sending request is aborted. * Default is 2000 ms. * @param {int} milliseconds the new timeout */ setTimeout: function(milliseconds) { this.timeout = milliseconds; }, /** * send the request. */ send: function() { if(this.loggingEventMap.length() >0) { log4jsLogger.trace("> AjaxAppender.send"); this.isInProgress = true; var a = []; for(var i = 0; i < this.loggingEventMap.length() && i < this.threshold; i++) { a.push(this.layout.format(this.loggingEventMap.pull())); } var content = this.layout.getHeader(); content += a.join(this.layout.getSeparator()); content += this.layout.getFooter(); var appender = this; if(this.httpRequest === null){ this.httpRequest = this.getXmlHttpRequest(); } this.httpRequest.onreadystatechange = function() { appender.onReadyStateChanged.call(appender); }; this.httpRequest.open("POST", this.loggingUrl, true); // set the request headers. //this.httpRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); this.httpRequest.setRequestHeader("Content-type", this.layout.getContentType()); //REFERER will be the top-level // URI which may differ from the location of the error if // it occurs in an included .js file this.httpRequest.setRequestHeader("REFERER", location.href); this.httpRequest.setRequestHeader("Content-length", content.length); this.httpRequest.setRequestHeader("Connection", "close"); this.httpRequest.send( content ); appender = this; try { window.setTimeout(function(){ log4jsLogger.trace("> AjaxAppender.timeout"); appender.httpRequest.onreadystatechange = function(){return;}; appender.httpRequest.abort(); //this.httpRequest = null; appender.isInProgress = false; if(appender.loggingEventMap.length() > 0) { appender.send(); } log4jsLogger.trace("< AjaxAppender.timeout"); }, this.timeout); } catch (e) { log4jsLogger.fatal(e); } log4jsLogger.trace("> AjaxAppender.send"); } }, /** * @private */ onReadyStateChanged: function() { log4jsLogger.trace("> AjaxAppender.onReadyStateChanged"); var req = this.httpRequest; if (this.httpRequest.readyState != 4) { log4jsLogger.trace("< AjaxAppender.onReadyStateChanged: readyState " + req.readyState + " != 4"); return; } var success = ((typeof req.status === "undefined") || req.status === 0 || (req.status >= 200 && req.status < 300)); if (success) { log4jsLogger.trace(" AjaxAppender.onReadyStateChanged: success"); //ready sending data this.isInProgress = false; } else { var msg = " AjaxAppender.onReadyStateChanged: XMLHttpRequest request to URL " + this.loggingUrl + " returned status code " + this.httpRequest.status; log4jsLogger.error(msg); } log4jsLogger.trace("< AjaxAppender.onReadyStateChanged: readyState == 4"); }, /** * Get the XMLHttpRequest object independent of browser. * @private */ getXmlHttpRequest: function() { log4jsLogger.trace("> AjaxAppender.getXmlHttpRequest"); var httpRequest = false; try { if (window.XMLHttpRequest) { // Mozilla, Safari, IE7... httpRequest = new XMLHttpRequest(); if (httpRequest.overrideMimeType) { httpRequest.overrideMimeType(this.layout.getContentType()); } } else if (window.ActiveXObject) { // IE try { httpRequest = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { httpRequest = new ActiveXObject("Microsoft.XMLHTTP"); } } } catch (e) { httpRequest = false; } if (!httpRequest) { log4jsLogger.fatal("Unfortunatelly your browser does not support AjaxAppender for log4js!"); } log4jsLogger.trace("< AjaxAppender.getXmlHttpRequest"); return httpRequest; }, /** * toString */ toString: function() { return "Log4js.AjaxAppender[loggingUrl=" + this.loggingUrl + ", threshold=" + this.threshold + "]"; } }); /** * File Appender writing the logs to a text file. * PLEASE NOTE - Only works in IE and Mozilla * use ActiveX to write file on IE * use XPCom components to write file on Mozilla * * @extends Log4js.Appender * @constructor * @param logger log4js instance this appender is attached to * @param file file log messages will be written to * @author Seth Chisamore * @author Nicolas Justin njustin@idealx.com * @author Gregory Kokanosky gkokanosky@idealx.com */ Log4js.FileAppender = function(file) { this.layout = new Log4js.SimpleLayout(); this.isIE = 'undefined'; this.file = file || "log4js.log"; try{ this.fso = new ActiveXObject("Scripting.FileSystemObject"); this.isIE = true; } catch(e){ try { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); this.fso = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile); this.isIE = false; //mozilla & co } catch (e) { log4jsLogger.error(e); } } }; Log4js.FileAppender.prototype = Log4js.extend(new Log4js.Appender(), { /** * @param loggingEvent event to be logged * @see Log4js.Appender#doAppend */ doAppend: function(loggingEvent) { try { var fileHandle = null; if( this.isIE === 'undefined') { log4jsLogger.error("Unsupported ") } else if( this.isIE ){ // try opening existing file, create if needed fileHandle = this.fso.OpenTextFile(this.file, 8, true); // write out our data fileHandle.WriteLine(this.layout.format(loggingEvent)); fileHandle.close(); } else { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); this.fso.initWithPath(this.file); if(!this.fso.exists()) { //create file if needed this.fso.create(0x00, 0600); } fileHandle = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream); fileHandle.init( this.fso, 0x04 | 0x08 | 0x10, 064, 0); var line = this.layout.format(loggingEvent); fileHandle.write(line, line.length); //write data fileHandle.close(); } } catch (e) { log4jsLogger.error(e); } }, /* * @see Log4js.Appender#doClear */ doClear: function() { try { if( this.isIE ){ var fileHandle = this.fso.GetFile(this.file); fileHandle.Delete(); } else { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); this.fso.initWithPath(this.file); if(this.fso.exists()) { this.fso.remove(false); } } } catch (e) { log4jsLogger.error(e); } }, /** * toString */ toString: function() { return "Log4js.FileAppender[file=" + this.file + "]"; } }); /** * Windows Event Appender writes the logs to the Windows Event log. * PLEASE NOTE - Only works in IE..uses ActiveX to write to Windows Event log * * @extends Log4js.Appender * @constructor * @param logger log4js instance this appender is attached to * @author Seth Chisamore */ Log4js.WindowsEventAppender = function() { this.layout = new Log4js.SimpleLayout(); try { this.shell = new ActiveXObject("WScript.Shell"); } catch(e) { log4jsLogger.error(e); } }; Log4js.WindowsEventAppender.prototype = Log4js.extend(new Log4js.Appender(), { /** * @param loggingEvent event to be logged * @see Log4js.Appender#doAppend */ doAppend: function(loggingEvent) { var winLevel = 4; // Map log level to windows event log level. // Windows events: - SUCCESS: 0, ERROR: 1, WARNING: 2, INFORMATION: 4, AUDIT_SUCCESS: 8, AUDIT_FAILURE: 16 switch (loggingEvent.level) { case Log4js.Level.FATAL: winLevel = 1; break; case Log4js.Level.ERROR: winLevel = 1; break; case Log4js.Level.WARN: winLevel = 2; break; default: winLevel = 4; break; } try { this.shell.LogEvent(winLevel, this.level.format(loggingEvent)); } catch(e) { log4jsLogger.error(e); } }, /** * toString */ toString: function() { return "Log4js.WindowsEventAppender"; } }); /** * JS Alert Appender writes the logs to the JavaScript alert dialog box * @constructor * @extends Log4js.Appender * @param logger log4js instance this appender is attached to * @author Sébastien LECACHEUR */ Log4js.JSAlertAppender = function() { this.layout = new Log4js.SimpleLayout(); }; Log4js.JSAlertAppender.prototype = Log4js.extend(new Log4js.Appender(), { /** * @see Log4js.Appender#doAppend */ doAppend: function(loggingEvent) { alert(this.layout.getHeader() + this.layout.format(loggingEvent) + this.layout.getFooter()); }, /** * toString */ toString: function() { return "Log4js.JSAlertAppender"; } }); /** * Appender writes the logs to the JavaScript console of Mozilla browser * More infos: http://kb.mozillazine.org/index.php?title=JavaScript_Console&redirect=no * PLEASE NOTE - Only works in Mozilla browser * @constructor * @extends Log4js.Appender * @param logger log4js instance this appender is attached to * @author Stephan Strittmatter */ Log4js.MozillaJSConsoleAppender = function() { this.layout = new Log4js.SimpleLayout(); try { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); this.jsConsole = Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService); this.scriptError = Components.classes["@mozilla.org/scripterror;1"].createInstance(Components.interfaces.nsIScriptError); } catch (e) { log4jsLogger.error(e); } }; Log4js.MozillaJSConsoleAppender.prototype = Log4js.extend(new Log4js.Appender(), { /** * @see Log4js.Appender#doAppend */ doAppend: function(loggingEvent) { try { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); this.scriptError.init(this.layout.format(loggingEvent), null, null, null, null, this.levelCode(loggingEvent), loggingEvent.categoryName); this.jsConsole.logMessage(this.scriptError); } catch (e) { log4jsLogger.error(e); } }, /** * toString */ toString: function() { return "Log4js.MozillaJSConsoleAppender"; }, /** * Map Log4js.Level to jsConsole Flags: * * @private */ levelCode: function(loggingEvent) { var retval; switch (loggingEvent.level) { case Log4js.Level.FATAL: retval = 2;//nsIScriptError.exceptionFlag = 2 break; case Log4js.Level.ERROR: retval = 0;//nsIScriptError.errorFlag break; case Log4js.Level.WARN: retval = 1;//nsIScriptError.warningFlag = 1 break; default: retval = 1;//nsIScriptError.warningFlag = 1 break; } return retval; } }); /** * Appender writes the logs to the JavaScript console of Opera browser * PLEASE NOTE - Only works in Opera browser * @constructor * @extends Log4js.Appender * @param logger log4js instance this appender is attached to * @author Stephan Strittmatter */ Log4js.OperaJSConsoleAppender = function() { this.layout = new Log4js.SimpleLayout(); }; Log4js.OperaJSConsoleAppender.prototype = Log4js.extend(new Log4js.Appender(), { /** * @see Log4js.Appender#doAppend */ doAppend: function(loggingEvent) { opera.postError(this.layout.format(loggingEvent)); }, /** * toString */ toString: function() { return "Log4js.OperaJSConsoleAppender"; } }); /** * Appender writes the logs to the JavaScript console of Safari browser * PLEASE NOTE - Only works in Safari browser * @constructor * @extends Log4js.Appender * @param logger log4js instance this appender is attached to * @author Stephan Strittmatter */ Log4js.SafariJSConsoleAppender = function() { this.layout = new Log4js.SimpleLayout(); }; Log4js.SafariJSConsoleAppender.prototype = Log4js.extend(new Log4js.Appender(), { /** * @see Log4js.Appender#doAppend */ doAppend: function(loggingEvent) { window.console.log(this.layout.format(loggingEvent)); }, /** * toString */ toString: function() { return "Log4js.SafariJSConsoleAppender"; } }); /** * JavaScript Console Appender which is browser independent. * It checks internally for the current browser and adds delegate to * specific JavaScript Console Appender of the browser. * * @author Stephan Strittmatter * @since 1.0 */ Log4js.BrowserConsoleAppender = function() { /** * Delegate for browser specific implementation * @type Log4js.Appender * @private */ this.consoleDelegate = null; if (window.console) { this.consoleDelegate = new Log4js.SafariJSConsoleAppender(); } else if (window.opera) { this.consoleDelegate = new Log4js.OperaJSConsoleAppender(); } else if(netscape) { this.consoleDelegate = new Log4js.MozillaJSConsoleAppender(); } else { //@todo log4jsLogger.error("Unsupported Browser"); } }; Log4js.BrowserConsoleAppender.prototype = Log4js.extend(new Log4js.Appender(), { /** * @see Log4js.Appender#doAppend */ doAppend: function(loggingEvent) { this.consoleDelegate.doAppend(loggingEvent); }, /** * @see Log4js.Appender#doClear */ doClear: function() { this.consoleDelegate.doClear(); }, /** * @see Log4js.Appender#setLayout */ setLayout: function(layout){ this.consoleDelegate.setLayout(layout); }, /** * toString */ toString: function() { return "Log4js.BrowserConsoleAppender: " + this.consoleDelegate.toString(); } }); /** * SimpleLayout consists of the level of the log statement, followed by " - " * and then the log message itself. For example, * DEBUG - Hello world * * @constructor * @extends Log4js.Layout * @extends Layout * @author Stephan Strittmatter */ Log4js.SimpleLayout = function() { this.LINE_SEP = "\n"; this.LINE_SEP_LEN = 1; }; Log4js.SimpleLayout.prototype = Log4js.extend(new Log4js.Layout(), { /** * Implement this method to create your own layout format. * @param {Log4js.LoggingEvent} loggingEvent loggingEvent to format * @return formatted String * @type String */ format: function(loggingEvent) { return loggingEvent.level.toString() + " - " + loggingEvent.message + this.LINE_SEP; }, /** * Returns the content type output by this layout. * @return The base class returns "text/plain". * @type String */ getContentType: function() { return "text/plain"; }, /** * @return Returns the header for the layout format. The base class returns null. * @type String */ getHeader: function() { return ""; }, /** * @return Returns the footer for the layout format. The base class returns null. * @type String */ getFooter: function() { return ""; } }); /** * BasicLayout is a simple layout for storing the loggs. The loggs are stored * in following format: *
 * categoryName~startTime [logLevel] message\n
 * 
* * @constructor * @extends Log4js.Layout * @author Stephan Strittmatter */ Log4js.BasicLayout = function() { this.LINE_SEP = "\n"; }; Log4js.BasicLayout.prototype = Log4js.extend(new Log4js.Layout(), { /** * Implement this method to create your own layout format. * @param {Log4js.LoggingEvent} loggingEvent loggingEvent to format * @return formatted String * @type String */ format: function(loggingEvent) { return loggingEvent.categoryName + "~" + loggingEvent.startTime.toLocaleString() + " [" + loggingEvent.level.toString() + "] " + loggingEvent.message + this.LINE_SEP; }, /** * Returns the content type output by this layout. * @return The base class returns "text/plain". * @type String */ getContentType: function() { return "text/plain"; }, /** * @return Returns the header for the layout format. The base class returns null. * @type String */ getHeader: function() { return ""; }, /** * @return Returns the footer for the layout format. The base class returns null. * @type String */ getFooter: function() { return ""; } }); /** * HtmlLayout write the logs in Html format. * * @constructor * @extends Log4js.Layout * @author Stephan Strittmatter */ Log4js.HtmlLayout = function() {return;}; Log4js.HtmlLayout.prototype = Log4js.extend(new Log4js.Layout(), { /** * Implement this method to create your own layout format. * @param {Log4js.LoggingEvent} loggingEvent loggingEvent to format * @return formatted String * @type String */ format: function(loggingEvent) { return "
" + loggingEvent.getFormattedTimestamp() + " - " + loggingEvent.level.toString() + " - " + loggingEvent.message + "
\n"; }, /** * Returns the content type output by this layout. * @return The base class returns "text/html". * @type String */ getContentType: function() { return "text/html"; }, /** * @return Returns the header for the layout format. The base class returns null. * @type String */ getHeader: function() { return "log4js</head><body>"; }, /** * @return Returns the footer for the layout format. The base class returns null. * @type String */ getFooter: function() { return "</body></html>"; }, getStyle: function(loggingEvent) { var style; if (loggingEvent.level.toString().search(/ERROR/) != -1) { style = 'color:red'; } else if (loggingEvent.level.toString().search(/FATAL/) != -1) { style = 'color:red'; } else if (loggingEvent.level.toString().search(/WARN/) != -1) { style = 'color:orange'; } else if (loggingEvent.level.toString().search(/DEBUG/) != -1) { style = 'color:green'; } else if (loggingEvent.level.toString().search(/INFO/) != -1) { style = 'color:white'; } else { style = 'color:yellow'; } return style; } }); /** * XMLLayout write the logs in XML format. * Layout is simmilar to log4j's XMLLayout: * <pre> * <log4js:event category="category" level="Level" client="Client" referer="ref" timestam="Date"> * <log4js:message>Logged message</log4js:message> * </log4js:event> * </pre> * @constructor * @extends Layout * @author Stephan Strittmatter */ Log4js.XMLLayout = function(){return;}; Log4js.XMLLayout.prototype = Log4js.extend(new Log4js.Layout(), { /** * Implement this method to create your own layout format. * @param {Log4js.LoggingEvent} loggingEvent loggingEvent to format * @return formatted String * @type String */ format: function(loggingEvent) { var useragent = "unknown"; try { useragent = navigator.userAgent; } catch(e){ useragent = "unknown"; } var referer = "unknown"; try { referer = location.href; } catch(e){ referer = "unknown"; } var content = "<log4js:event logger=\""; content += loggingEvent.categoryName + "\" level=\""; content += loggingEvent.level.toString() + "\" useragent=\""; content += useragent + "\" referer=\""; content += referer.replace(/&/g, "&") + "\" timestamp=\""; content += loggingEvent.getFormattedTimestamp() + "\">\n"; content += "\t<log4js:message><![CDATA[" + this.escapeCdata(loggingEvent.message) + "]]></log4js:message>\n"; if (loggingEvent.exception) { content += this.formatException(loggingEvent.exception) ; } content += "</log4js:event>\n"; return content; }, /** * Returns the content type output by this layout. * @return The base class returns "text/xml". * @type String */ getContentType: function() { return "text/xml"; }, /** * @return Returns the header for the layout format. The base class returns null. * @type String */ getHeader: function() { return "<log4js:eventSet version=\"" + Log4js.version + "\" xmlns:log4js=\"http://log4js.berlios.de/2007/log4js/\">\n"; }, /** * @return Returns the footer for the layout format. The base class returns null. * @type String */ getFooter: function() { return "</log4js:eventSet>\n"; }, getSeparator: function() { return "\n"; }, /** * better readable formatted Exceptions. * @param ex {Exception} the exception to be formatted. * @return {String} the formatted String representation of the exception. * @private */ formatException: function(ex) { if (ex) { var exStr = "\t<log4js:throwable>"; if (ex.message) { exStr += "\t\t<log4js:message><![CDATA[" + this.escapeCdata(ex.message) + "]]></log4js:message>\n"; } if (ex.description) { exStr += "\t\t<log4js:description><![CDATA[" + this.escapeCdata(ex.description) + "]]></log4js:description>\n"; } exStr += "\t\t<log4js:stacktrace>"; exStr += "\t\t\t<log4js:location fileName=\""+ex.fileName+"\" lineNumber=\""+ex.lineNumber+"\" />"; exStr += "\t\t</log4js:stacktrace>"; exStr = "\t</log4js:throwable>"; return exStr; } return null; }, /** * Escape Cdata messages * @param str {String} message to escape * @return {String} the escaped message * @private */ escapeCdata: function(str) { return str.replace(/\]\]>/, "]]>]]><![CDATA["); } }); /** * JSONLayout write the logs in JSON format. * JSON library is required to use this Layout. See also {@link http://www.json.org} * @constructor * @extends Log4js.Layout * @author Stephan Strittmatter */ Log4js.JSONLayout = function() { this.df = new Log4js.DateFormatter(); }; Log4js.JSONLayout.prototype = Log4js.extend(new Log4js.Layout(), { /** * Implement this method to create your own layout format. * @param {Log4js.LoggingEvent} loggingEvent loggingEvent to format * @return formatted String * @type String */ format: function(loggingEvent) { var useragent = "unknown"; try { useragent = navigator.userAgent; } catch(e){ useragent = "unknown"; } var referer = "unknown"; try { referer = location.href; } catch(e){ referer = "unknown"; } var jsonString = "{\n \"LoggingEvent\": {\n"; jsonString += "\t\"logger\": \"" + loggingEvent.categoryName + "\",\n"; jsonString += "\t\"level\": \"" + loggingEvent.level.toString() + "\",\n"; jsonString += "\t\"message\": \"" + loggingEvent.message + "\",\n"; jsonString += "\t\"referer\": \"" + referer + "\",\n"; jsonString += "\t\"useragent\": \"" + useragent + "\",\n"; jsonString += "\t\"timestamp\": \"" + this.df.formatDate(loggingEvent.startTime, "yyyy-MM-ddThh:mm:ssZ") + "\",\n"; jsonString += "\t\"exception\": \"" + loggingEvent.exception + "\"\n"; jsonString += "}}"; return jsonString; }, /** * Returns the content type output by this layout. * @return The base class returns "text/xml". * @type String */ getContentType: function() { return "text/json"; }, /** * @return Returns the header for the layout format. The base class returns null. * @type String */ getHeader: function() { return "{\"Log4js\": [\n"; }, /** * @return Returns the footer for the layout format. The base class returns null. * @type String */ getFooter: function() { return "\n]}"; }, getSeparator: function() { return ",\n"; } }); /** * PatternLayout */ Log4js.PatternLayout = function(pattern) { if (pattern) { this.pattern = pattern; } else { this.pattern = Log4js.PatternLayout.DEFAULT_CONVERSION_PATTERN; } }; Log4js.PatternLayout.TTCC_CONVERSION_PATTERN = "%r %p %c - %m%n"; Log4js.PatternLayout.DEFAULT_CONVERSION_PATTERN = "%m%n"; Log4js.PatternLayout.ISO8601_DATEFORMAT = "yyyy-MM-dd HH:mm:ss,SSS"; Log4js.PatternLayout.DATETIME_DATEFORMAT = "dd MMM YYYY HH:mm:ss,SSS"; Log4js.PatternLayout.ABSOLUTETIME_DATEFORMAT = "HH:mm:ss,SSS"; Log4js.PatternLayout.prototype = Log4js.extend(new Log4js.Layout(), { /** * Returns the content type output by this layout. * @return "text/plain". * @type String */ getContentType: function() { return "text/plain"; }, /** * @return Returns the header for the layout format. * @type String */ getHeader: function() { return null; }, /** * @return Returns the footer for the layout format. * @type String */ getFooter: function() { return null; }, format: function(loggingEvent) { var regex = /%(-?[0-9]+)?(\.?[0-9]+)?([cdmnpr%])(\{([^\}]+)\})?|([^%]+)/; var formattedString = ""; var result; var searchString = this.pattern; // Cannot use regex global flag since it doesn't work in IE5 while ((result = regex.exec(searchString))) { var matchedString = result[0]; var padding = result[1]; var truncation = result[2]; var conversionCharacter = result[3]; var specifier = result[5]; var text = result[6]; // Check if the pattern matched was just normal text if (text) { formattedString += "" + text; } else { // Create a raw replacement string based on the conversion // character and specifier var replacement = ""; switch(conversionCharacter) { case "c": var loggerName = loggingEvent.categoryName; if (specifier) { var precision = parseInt(specifier, 10); var loggerNameBits = loggingEvent.categoryName.split("."); if (precision >= loggerNameBits.length) { replacement = loggerName; } else { replacement = loggerNameBits.slice(loggerNameBits.length - precision).join("."); } } else { replacement = loggerName; } break; case "d": var dateFormat = Log4js.PatternLayout.ISO8601_DATEFORMAT; if (specifier) { dateFormat = specifier; // Pick up special cases if (dateFormat == "ISO8601") { dateFormat = Log4js.PatternLayout.ISO8601_DATEFORMAT; } else if (dateFormat == "ABSOLUTE") { dateFormat = Log4js.PatternLayout.ABSOLUTETIME_DATEFORMAT; } else if (dateFormat == "DATE") { dateFormat = Log4js.PatternLayout.DATETIME_DATEFORMAT; } } // Format the date replacement = (new Log4js.SimpleDateFormat(dateFormat)).format(loggingEvent.startTime); break; case "m": replacement = loggingEvent.message; break; case "n": replacement = "\n"; break; case "p": replacement = loggingEvent.level.toString(); break; case "r": replacement = "" + loggingEvent.startTime.toLocaleTimeString(); //TODO: .getDifference(Log4js.applicationStartDate); break; case "%": replacement = "%"; break; default: replacement = matchedString; break; } // Format the replacement according to any padding or // truncation specified var len; // First, truncation if (truncation) { len = parseInt(truncation.substr(1), 10); replacement = replacement.substring(0, len); } // Next, padding if (padding) { if (padding.charAt(0) == "-") { len = parseInt(padding.substr(1), 10); // Right pad with spaces while (replacement.length < len) { replacement += " "; } } else { len = parseInt(padding, 10); // Left pad with spaces while (replacement.length < len) { replacement = " " + replacement; } } } formattedString += replacement; } searchString = searchString.substr(result.index + result[0].length); } return formattedString; } }); /** * @private * @ignore */ if (!Array.prototype.push) { /** * Functions taken from Prototype library, didn't want to require for just few * functions. * More info at {@link http:// * prototype.conio.net/} * @private */ Array.prototype.push = function() { var startLength = this.length; for (var i = 0; i < arguments.length; i++) { this[startLength + i] = arguments[i]; } return this.length; }; } /** * FIFO buffer * @private */ Log4js.FifoBuffer = function() { this.array = new Array(); }; Log4js.FifoBuffer.prototype = { /** * @param {Object} obj any object added to buffer */ push : function(obj) { this.array[this.array.length] = obj; return this.array.length; }, /** * @return first putted in Object */ pull : function() { if (this.array.length > 0) { var firstItem = this.array[0]; for (var i = 0; i < this.array.length - 1; i++) { this.array[i] = this.array[i + 1]; } this.array.length = this.array.length - 1; return firstItem; } return null; }, length : function() { return this.array.length; } }; /** * Date Formatter * addZero() and formatDate() are courtesy of Mike Golding: * http://www.mikezilla.com/exp0015.html * @private */ Log4js.DateFormatter = function() { return; }; /** * default format of date (ISO-8601) * @static * @final */ Log4js.DateFormatter.DEFAULT_DATE_FORMAT = "yyyy-MM-ddThh:mm:ssO"; Log4js.DateFormatter.prototype = { /** * Formats the given date by the given pattern.<br /> * Following switches are supported: * <ul> * <li>yyyy: The year</li> * <li>MM: the month</li> * <li>dd: the day of month<li> * <li>hh: the hour<li> * <li>mm: minutes</li> * <li>O: timezone offset</li> * </ul> * @param {Date} vDate the date to format * @param {String} vFormat the format pattern * @return {String} formatted date string * @static */ formatDate : function(vDate, vFormat) { var vDay = this.addZero(vDate.getDate()); var vMonth = this.addZero(vDate.getMonth()+1); var vYearLong = this.addZero(vDate.getFullYear()); var vYearShort = this.addZero(vDate.getFullYear().toString().substring(3,4)); var vYear = (vFormat.indexOf("yyyy")>-1?vYearLong:vYearShort); var vHour = this.addZero(vDate.getHours()); var vMinute = this.addZero(vDate.getMinutes()); var vSecond = this.addZero(vDate.getSeconds()); var vTimeZone = this.O(vDate); var vDateString = vFormat.replace(/dd/g, vDay).replace(/MM/g, vMonth).replace(/y{1,4}/g, vYear); vDateString = vDateString.replace(/hh/g, vHour).replace(/mm/g, vMinute).replace(/ss/g, vSecond); vDateString = vDateString.replace(/O/g, vTimeZone); return vDateString; }, /** * @private * @static */ addZero : function(vNumber) { return ((vNumber < 10) ? "0" : "") + vNumber; }, /** * Formates the TimeOffest * Thanks to http://www.svendtofte.com/code/date_format/ * @private */ O : function (date) { // Difference to Greenwich time (GMT) in hours var os = Math.abs(date.getTimezoneOffset()); var h = String(Math.floor(os/60)); var m = String(os%60); h.length == 1? h = "0"+h:1; m.length == 1? m = "0"+m:1; return date.getTimezoneOffset() < 0 ? "+"+h+m : "-"+h+m; } }; /** * internal Logger to be used * @private */ var log4jsLogger = Log4js.getLogger("Log4js"); log4jsLogger.addAppender(new Log4js.ConsoleAppender()); log4jsLogger.setLevel(Log4js.Level.ALL);