diff --git a/.project b/.project new file mode 100644 index 0000000..8f0a85e --- /dev/null +++ b/.project @@ -0,0 +1,11 @@ + + + log4js + + + + + + + + diff --git a/CHANGELOG.txt b/CHANGELOG.txt new file mode 100644 index 0000000..b5772bd --- /dev/null +++ b/CHANGELOG.txt @@ -0,0 +1,26 @@ +VERSION 0.1.5 +-Changed the way log levels are modeled to mirror Log4j conventions. +-Change Log4js.LogLevel to Log4js.Level (Log4j has a LogLevel class and a Level class, LogLevel is actually part of the LogFactor5 3rd party package) to match Log4j naming conventions +-Created static objects representing each log level. +-Added OFF and ALL levels to mirror Log4j levels. +-Renamed Log4js.LogEntry to Log4js.LoggingEvent to mirror Log4j naming conventions. + +VERSION 0.1.4 +-Combined HtmlConsoleAppender and WindowAppender into a single appender since they are really the same thing (a console) just in different locations +-New appender is called ConsoleAppender, you indicate whether the console should run inline during construction (default is to launch in new window) + +VERSION 0.1.3 +-created an Log4js.LogEntry object to model a log entry, has category, level, and message properties, it has a method to return a formatted string representing the message (need to replace this with actual Layouts) +-logs entries stored in central log entry array +-appenders are notified of new log entries through event notificiation and use of Log4js.Event objects +-when a new log entry appears all appenders automatically notified. +-when log entries are cleared all appenders are notified and cleared. +-added HtmlConsoleAppender that writes log messages to a hidden "console" that can be toggled with "Alt-d" - thanks to Corey Johnson whose Lumberjack (http://gleepglop.com/javascripts/logger/) code was adapted for this appender + +VERSION 0.1.2 +-pulled LogLevel and Logger under a main "class" called Log4js, updated all documentation an test functions to reflect changes. +-changed version to 0.1.2 (not sure how version number should change?) +-fixed "clear" and "close" buttons on WindowAppender +-fixed scrolling issues in WindowAppender +-fixed IE javascript bug if user chooses "no" on ActiveX dialouge +-updated all tests and comments to reflect above changes \ No newline at end of file diff --git a/INSTALL.txt b/INSTALL.txt new file mode 100644 index 0000000..0c43c1c --- /dev/null +++ b/INSTALL.txt @@ -0,0 +1,54 @@ + +============ +Using log4js +============ + +1) First untar or unzip the distribution file. + +2) Assuming you chose to extract the distribution in to the + PATH_OF_YOUR_CHOICE, untarring the distribution file should create + a logging-log4js-VERSION directory, where VERSION is the log4js + version number, under PATH_OF_YOUR_CHOICE. We will refer to the + directory PATH_OF_YOUR_CHOICE/logging-log4j-VERSION/ as $LOG4JS_HOME/. + +@TODO + +================== +log4js dependencies +================== + +Log4js is based on EMCA Script (JavaScript) with the following additional +requirements: + + ---------------------------- + JSDoc + ---------------------------- + + JSDoc is a Perl implementation to generate API documentation for JavaScripts. + It is available at http://jsdoc.sf.net + + ---------------------------- + Package ANT + ---------------------------- + + ANT is a Java based make tool to generate the releases. It is availabel + at http://ant.apache.org + + ---------------------------- + Package log4j.jar + ---------------------------- + + log4j.jar is the logging API for Java. It is only required for the + AJAXAppender example to log the messages on server side. + + +============== +Building log4js +============== + +Like most java appilicatios today, log4js relies on ANT as its build +tool. ANT is availale from "http://ant.apache.org/". ANT +requires a build file called build.xml which is part of this +distribution. Required components from other projects are specified in +the build.properties and example of which is supplied in the +build.properties.sample file. \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + 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. diff --git a/NOTICE.txt b/NOTICE.txt new file mode 100644 index 0000000..35fb908 --- /dev/null +++ b/NOTICE.txt @@ -0,0 +1,4 @@ +This product includes software developed by +The Apache Software Foundation (http://www.apache.org/). + +This product includes source code based on ... diff --git a/build.properties b/build.properties new file mode 100644 index 0000000..e69de29 diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..2997079 --- /dev/null +++ b/build.xml @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + Logging library for JavaScript which is using almost same API than log4j + + + + + generate complete distribution archive: ${dist.dir}/log4js-${version}.zip + + + + + + + + + + + + + + + + + + + + + + + + + + + generate example web archive: ${dist.dir}/${example.war.name} + + + + + + + + + + + + + + + + + + + + + generate documentation archive: ${dist.dir}/log4js-doc-${version}.zip + + + + + + + + + + + + + + + + + + + + + + + + + Gererate JSDoc to ${apidoc.dir} + + + + + + + + + + + + + + + + + + + + + + + + + To use this Maven-like freedom with respect to dependency setup, + do the following: + + - run ant as you normally would, but add the following target to the command + line: + + download-dependencies + + For example: + C:>myproject\ant download-dependencies dist + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/WEB-INF/classes/log4j.properties b/examples/WEB-INF/classes/log4j.properties new file mode 100644 index 0000000..8678725 --- /dev/null +++ b/examples/WEB-INF/classes/log4j.properties @@ -0,0 +1,5 @@ +log4j.rootLogger=DEBUG, ConsoleAppender + +log4j.appender.ConsoleAppender=org.apache.log4j.ConsoleAppender +log4j.appender.ConsoleAppender.layout=org.apache.log4j.PatternLayout +log4j.appender.ConsoleAppender.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c: %m%n \ No newline at end of file diff --git a/examples/WEB-INF/web.xml b/examples/WEB-INF/web.xml new file mode 100644 index 0000000..4dae129 --- /dev/null +++ b/examples/WEB-INF/web.xml @@ -0,0 +1,11 @@ + + + + Log4js + + Log4js Example + + diff --git a/examples/WEB-INF/web.xml.bak b/examples/WEB-INF/web.xml.bak new file mode 100644 index 0000000..7a26bf6 --- /dev/null +++ b/examples/WEB-INF/web.xml.bak @@ -0,0 +1,11 @@ + + + + Tomcat Documentation + + Tomcat Documentation. + + diff --git a/examples/index.html b/examples/index.html new file mode 100644 index 0000000..69c5545 --- /dev/null +++ b/examples/index.html @@ -0,0 +1,97 @@ + + + + Log4js tests + + + + + +

Log4js Tests

+
Window Console Logging + + +
+ + + + + + +    + +
+ +
Inline Console Logging (toggle on with ALT-D) + + +
+ + + + + + +
+ +
Ajax Logging + + +
+ + + + + + +
+ +
Local File Logging (Internet Explorer only) (C:\log4js.log) + + +
+ + + + + + +    + +
+ +
Window Event Logging (Internet Explorer only) (Control Panel > Administrative Tools > Event Viewer) + + +
+ + + + + + +
+ + \ No newline at end of file diff --git a/examples/log4j.jsp b/examples/log4j.jsp new file mode 100644 index 0000000..d443c85 --- /dev/null +++ b/examples/log4j.jsp @@ -0,0 +1,20 @@ +<%@ page import="org.apache.log4j.Logger" %> +<% +// Log4J + String category = request.getParameter("log4js.category"); + String message = request.getParameter("log4js.msg"); + String level = request.getParameter("log4js.level"); + String client = request.getParameter("log4js.client"); + + Logger logger = Logger.getLogger( category); + String msg = request.getRemoteAddr() + " - " + client + ": " + message; + System.out.println(msg); + if("debug".equals(level)) + { + logger.debug(msg); + } else { + logger.error(msg); + } + //System.out.println(msg); +%> + \ No newline at end of file diff --git a/src/js/log4js.js b/src/js/log4js.js new file mode 100644 index 0000000..66cc6a1 --- /dev/null +++ b/src/js/log4js.js @@ -0,0 +1,1096 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +/** + * @fileoverview log4js is a helper to log in JavaScript in simmilar manner than in log4j + * for Java. The API should be nearly the same. + *

Example:

+ *
+ *  //logging see log4js
+ *  var log = new Log4js.Logger("some-category-name"); 
+ *  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 inline in the page
+ *  log.addAppender(new FileAppender("C:\\somefile.log")); // file appender logs to C:\\somefile.log
+ *  ...
+ *  //call the log
+ *  log.trace("trace me" );
+ * 
+ * + * @author Stephan Strittmatter - http://jroller.com/page/stritti + * @author Seth Chisamore - http://www.chisamore.com + * @author Corey Johnson - some parts inspired by Lumberjack (http://gleepglop.com/javascripts/logger/) + * @author S?bastien LECACHEUR - JSAlertAppender + */ + +var Log4js = { + /** + * current version of log4js + * @static + */ + version: "0.1.5", + + /** + * @param element + * @param name + * @param observer + */ + attachEvent: function (element, name, observer) { + if (element.addEventListener) { + element.addEventListener(name, observer, false); + } else if (element.attachEvent) { + element.attachEvent('on' + name, observer); + } + } +} + +/** + * Log4js.Level Enumeration + * @constructor + * @private + * @param {Number} level + * @param {String} levelString + */ +Log4js.Level = function(level, levelStr) { + this.level = level; + this.levelStr = levelStr; +}; + +Log4js.Level.prototype = { + /** + * converts given String to 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 + */ +Log4js.Level.OFF = new Log4js.Level(Log4js.Level.OFF_INT, "OFF"); +Log4js.Level.FATAL = new Log4js.Level(Log4js.Level.FATAL_INT, "FATAL"); +Log4js.Level.ERROR = new Log4js.Level(Log4js.Level.ERROR_INT, "ERROR"); +Log4js.Level.WARN = new Log4js.Level(Log4js.Level.WARN_INT, "WARN"); +Log4js.Level.INFO = new Log4js.Level(Log4js.Level.INFO_INT, "INFO"); +Log4js.Level.DEBUG = new Log4js.Level(Log4js.Level.DEBUG_INT, "DEBUG"); +Log4js.Level.TRACE = new Log4js.Level(Log4js.Level.TRACE_INT, "TRACE"); +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 + */ +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) { + alert("Could not run the listener " + this.listeners[i] + ". " + e.message) + } + } + }, + + /** + * @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 logger + * @author Seth Chisamore + */ +Log4js.LoggingEvent = function(categoryName, level, message, logger) { + /** + * @type Date + * @private + */ + this.startTime = new Date(); + /** + * @type String + * @private + */ + this.categoryName = categoryName || "."; + /** + * @type String + * @private + */ + this.message = message || ""; + /** + * @type Log4js.Level + * @private + */ + this.level = level || Log4js.Level.TRACE; + /** + * @type Log4js.Logger + * @private + */ + this.logger = logger; +} +Log4js.LoggingEvent.prototype = { + // TODO: Need to add support Layouts + /** + * Returns the layouted message line + * @return layouted Message + * @type String + */ + getRenderedMessage: function() { + return this.categoryName + "~" + this.startTime.toLocaleString() + " [" + this.level.toString() + "] " + this.message; + } +} + +/** + * 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). + * @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.onlog = new Log4js.CustomEvent(); + this.onclear = new Log4js.CustomEvent(); + + /** appender to write in */ + this.appenders.push(new Appender(this)); + + // if multiple log objects are instanciated this will only log to the log object that is declared last + // can't seem to get the attachEvent method to work correctly + window.onerror = this.windowError.bind(this); +} + +Log4js.Logger.prototype = { + /** + * Get a logger instance + * @param name name of category to log to + * @todo probably cache the logger here? + */ + getLogger: function(name) { + return new Logger(name); + }, + /** + * add additional appender. DefaultAppender always is there. + * @param appender additional wanted appender + */ + addAppender: function(appender) { + this.appenders.push(appender); + }, + + /** + * 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 */ + log: function(message, logLevel) { + var loggingEvent = new Log4js.LoggingEvent(this.category, logLevel, message, this); + this.loggingEvents.push(loggingEvent); + this.onlog.dispatch(loggingEvent); + }, + + /** clear logging */ + clear : function () { + this.loggingEvents = []; + this.onclear.dispatch(); + }, + + /** checks if Level Trace is enabled */ + isTraceEnabled: function() { + if (this.level.valueOf() <= Log4js.Level.TRACE.valueOf()) { + return true; + } + return false; + }, + /** logging trace messages */ + trace: function(message) { + if (this.isTraceEnabled()) { + this.log(message, Log4js.Level.TRACE); + } + }, + /** checks if Level Debug is enabled */ + isDebugEnabled: function() { + if (this.level.valueOf() <= Log4js.Level.DEBUG.valueOf()) { + return true; + } + return false; + }, + /** logging debug messages */ + debug: function(message) { + if (this.isDebugEnabled()) { + this.log(message, Log4js.Level.DEBUG); + } + }, + /** checks if Level Info is enabled */ + isInfoEnabled: function() { + if (this.level.valueOf() <= Log4js.Level.INFO.valueOf()) { + return true; + } + return false; + }, + /** logging info messages */ + info: function(message) { + if (this.isInfoEnabled()) { + this.log(message, Log4js.Level.INFO); + } + }, + /** 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(message, Log4js.Level.WARN); + } + }, + /** 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(message, Log4js.Level.ERROR); + } + }, + /** 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(message, Log4js.Level.FATAL); + } + }, + + /** capture main window errors and log as fatal */ + windowError: function(msg, url, line){ + var message = "Error in (" + (url || window.location) + ") on line "+ line +" with message (" + msg + ")"; + this.log(message, Log4js.Level.FATAL); + } +} + +/** + * Interface for Appender. + * Use this appender as "interface" for other appenders. It is doing nothing. + * + * @constructor + * @param {Log4js.Logger} logger log4js instance this appender is attached to + * @author Stephan Strittmatter + */ +function Appender(logger) { + // add listener to the logger methods + logger.onlog.addListener(this.doAppend.bind(this)); + logger.onclear.addListener(this.doClear.bind(this)); + /** + * set reference to calling logger + * @type Log4js.Logger + */ + this.logger = logger; +} +Appender.prototype = { + /** + * appends the given loggingEvent appender specific + * @param {Log4js.LoggingEvent} loggingEvent loggingEvent to append + */ + doAppend: function(loggingEvent) {}, + /** + * clears the Appender + */ + doClear: function() {} +} + +/** + * Console Appender writes the logs to a console. If "useWindow" is + * set to "true" the console launches in another window otherwise + * the window is inline on the page and toggled on and off with "Alt-D". + + * @constructor + * @extends Appender + * @param {Log4js.Logger} logger log4js instance this appender is attached to + * @param {boolean} inline 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 + */ +ConsoleAppender = function(logger, inline) { + // add listener to the logger methods + logger.onlog.addListener(this.doAppend.bind(this)); + logger.onclear.addListener(this.doClear.bind(this)); + /** + * set reference to calling logger + * @type Log4js.Logger + */ + this.logger = logger; + this.inline = inline || false; + + if(this.inline) { + Log4js.attachEvent(window, 'load', this.initialize.bind(this)); + } +} + +ConsoleAppender.prototype = { + + commandHistory : [], + commandIndex : 0, + + /** + * @private + */ + initialize : function() { + + var doc = document; + var win = window; + + if(!this.inline) { + window.top.consoleWindow = window.open("", "log4jsconsole", "left=0,top=0,width=700,height=700,scrollbars=no,status=no,resizable=no;toolbar=no"); + window.top.consoleWindow.opener = self; + win = window.top.consoleWindow; + doc = win.document; + doc.open(); + doc.write("\n\n"); + doc.write("log4js\n"); + doc.write("\n"); + win.blur(); + win.focus(); + } + + this.docReference = doc; + this.winReference = win; + + this.outputCount = 0; + this.tagPattern = ".*"; + + // I hate writing javascript in HTML... but what's a better alternative + this.logElement = doc.createElement('div'); + doc.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 = doc.createElement('div'); + this.logElement.appendChild(this.toolbarElement); + this.toolbarElement.style.padding = "0 0 0 2px"; + + // Add buttons + this.buttonsContainerElement = doc.createElement('span'); + this.toolbarElement.appendChild(this.buttonsContainerElement); + + if(this.inline) { + var closeButton = doc.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 = this.toggle.bind(this); + this.buttonsContainerElement.appendChild(closeButton); + } + + var clearButton = doc.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 = this.logger.clear.bind(this.logger); + this.buttonsContainerElement.appendChild(clearButton); + + //Add Level Filter + this.tagFilterContainerElement = doc.createElement('span'); + this.toolbarElement.appendChild(this.tagFilterContainerElement); + this.tagFilterContainerElement.style.cssFloat = 'left'; + this.tagFilterContainerElement.appendChild(doc.createTextNode("Level Filter")); + + this.tagFilterElement = doc.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', this.updateTags.bind(this)); + Log4js.attachEvent(this.tagFilterElement, 'click', function() {this.tagFilterElement.select()}.bind(this)); + + // Add outputElement + this.outputElement = doc.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 = doc.createElement('div'); + this.inputContainerElement.style.width = "100%"; + this.logElement.appendChild(this.inputContainerElement); + + this.inputElement = doc.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', this.handleInput.bind(this)); + Log4js.attachEvent(this.inputElement, 'click', function() {this.inputElement.select()}.bind(this)); + + if(this.inline){ + window.setInterval(this.repositionWindow.bind(this), 500); + this.repositionWindow(); + // Allow acess key link + var accessElement = doc.createElement('button'); + accessElement.style.position = "absolute"; + accessElement.style.top = "-100px"; + accessElement.accessKey = "d"; + accessElement.onclick = this.toggle.bind(this); + doc.body.appendChild(accessElement); + } else { + this.show(); + } + }, + /** + * shows/hide an element + * @private + */ + toggle : function() { + if (this.logElement.style.display == 'none') { + this.show(); + } else { + this.hide(); + } + }, + /** + * @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 Appender#doAppend + */ + doAppend : function(loggingEvent) { + + if ((!this.inline) && (!this.winReference || this.winReference.closed)) { + this.initialize(); + } + + if (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(loggingEvent.getRenderedMessage(), style); + }, + + /** + * @see 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; + } + } +} + +/** + * Metatag Appender writing the logs to meta tags + * + * @extends Appender + * @constructor + * @param logger log4js instance this appender is attached to + * @author Stephan Strittmatter + */ +function MetatagAppender(logger) { + // add listener to the logger methods + logger.onlog.addListener(this.doAppend.bind(this)); + logger.onclear.addListener(this.doClear.bind(this)); + /** + * set reference to calling logger + * @type Log4js.Logger + */ + this.logger = logger; +} + +MetatagAppender.prototype = { + /** + * @param loggingEvent event to be logged + * @see 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.logger.currentLine++); + metaTag.setAttribute("content", value); + headTag.appendChild(metaTag); + } + }, + /** + * do nothing + * @see Appender#doClear + */ + doClear: function() {} +}; + +/** + * AJAX Appender sending logging messages asynchron via XMLHttpREquest to server + * + * @extends 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 + */ +function AjaxAppender(logger, loggingUrl) { + // add listener to the logger methods + logger.onlog.addListener(this.doAppend.bind(this)); + logger.onclear.addListener(this.doClear.bind(this)); + /** + * set reference to calling logger + * @type Log4js.Logger + */ + this.logger = logger; + /** + * @type XMLHttpRequest + */ + this.httpRequest = false; + this.loggingUrl = loggingUrl || "log4js.jsp"; + + if (window.XMLHttpRequest) { // Mozilla, Safari,... + this.httpRequest = new XMLHttpRequest(); + if (this.httpRequest.overrideMimeType) { + this.httpRequest.overrideMimeType('text/xml'); + } + } else if (window.ActiveXObject) { // IE + try { + this.httpRequest = new ActiveXObject("Msxml2.XMLHTTP"); + } catch (e) { + this.httpRequest = new ActiveXObject("Microsoft.XMLHTTP"); + } + } + if (!this.httpRequest) { + alert('Unfortunatelly you browser doesn\'t support AJAX appender for log4js!'); + }else{ + // this.httpRequest.onreadystatechange = this.logged; + } +}; + +AjaxAppender.prototype = { + /** + * sends the logs to the server + * @param loggingEvent event to be logged + * @see Appender#doAppend + */ + doAppend: function(loggingEvent) { + var msg = "log4js.client=" + navigator.userAgent + + "&log4js.category=" + loggingEvent.categoryName + + "&log4js.level=" + loggingEvent.level.toString() + + "&log4js.msg=" + loggingEvent.message; + + this.httpRequest.open("POST", this.loggingUrl + "?" + msg , true); + this.httpRequest.send("" + loggingEvent.categoryName + "" + + loggingEvent.level.toString() + "" + + navigator.userAgent + "" + + loggingEvent.message + ""); + }, + /** + * callback method only to verify if sending was successful + */ + logged: function() { + if (this.httpRequest.readyState == 4) { + if (this.httpRequest.status != 200 ) { + alert('There was a problem with the log4js AjaxAppender: ' + this.httpRequest.responseText ); + } + } + }, + /** + * @see Appender#doClear + */ + doClear: function() {} +}; + +/** + * File Appender writing the logs to a text file. + * PLEASE NOTE - Only works in IE..uses ActiveX to write file + * + * @extends Appender + * @constructor + * @param logger log4js instance this appender is attached to + * @param file file log messages will be written to + * @author Seth Chisamore + */ +function FileAppender(logger, file) { + // add listener to the logger methods + logger.onlog.addListener(this.doAppend.bind(this)); + logger.onclear.addListener(this.doClear.bind(this)); + /** + * set reference to calling logger + * @type Log4js.Logger + */ + this.logger = logger; + + this.file = file || "C:\\log4js.log"; + try{ + this.fso = new ActiveXObject("Scripting.FileSystemObject"); + } catch(e){} +}; + +FileAppender.prototype = { + /** + * @param loggingEvent event to be logged + * @see Appender#doAppend + */ + doAppend: function(loggingEvent) { + try { + // try opening existing file, create if needed + var fileHandle = this.fso.OpenTextFile(this.file, 8, true); + // write out our data + fileHandle.WriteLine(loggingEvent.getRenderedMessage()); + fileHandle.close(); + } catch (e) {} + }, + /* + * @see Appender#doClear + */ + doClear: function() { + try { + var fileHandle = this.fso.GetFile(this.file); + fileHandle.Delete(); + } catch (e) { + + } + } +}; + +/** + * 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 Appender + * @constructor + * @param logger log4js instance this appender is attached to + * @author Seth Chisamore + */ +function WindowsEventAppender(logger) { + // add listener to the logger methods + logger.onlog.addListener(this.doAppend.bind(this)); + logger.onclear.addListener(this.doClear.bind(this)); + /** + * set reference to calling logger + * @type Log4js.Logger + */ + this.logger = logger; + + try { + this.shell = new ActiveXObject("WScript.Shell"); + } catch(e) {} +}; + +WindowsEventAppender.prototype = { + /** + * @param loggingEvent event to be logged + * @see 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, loggingEvent.getRenderedMessage()); + } catch(e) { + + } + }, + /** + * @see Appender#doClear + */ + doClear: function() {} +}; + +/** + * JS Alert Appender writes the logs to the JavaScript alert dialog box + * @constructor + * @extends Appender + * @param logger log4js instance this appender is attached to + * @author S?bastien LECACHEUR + */ +function JSAlertAppender(logger) { + // add listener to the logger methods + logger.onlog.addListener(this.doAppend.bind(this)); + logger.onclear.addListener(this.doClear.bind(this)); + /** + * set reference to calling logger + * @type Log4js.Logger + */ + this.logger = logger; +}; + +JSAlertAppender.prototype = { + /** + * @see Appender#doAppend + */ + doAppend: function(category, message, level) { + var now = new Date(); + alert(category + " " + now.toLocaleString() + " [" + LogLevel.valueOf(level) + "] " + message); + }, + /** + * @see Appender#doClear + */ + doClear: function() {} +}; + +/** + * Functions taken from Prototype library, + * didn't want to require for just few functions + * More info at {@link http://prototype.conio.net/} + */ +if (!Array.prototype.push) { + Array.prototype.push = function() { + var startLength = this.length; + for (var i = 0; i < arguments.length; i++) + this[startLength + i] = arguments[i]; + return this.length; + } +} + +if(!Function.prototype.bind) { + /** + * Functions taken from Prototype library, + * didn't want to require for just few functions + * More info at {@link http://prototype.conio.net/} + */ + Function.prototype.bind = function(object) { + var __method = this; + return function() { + return __method.apply(object, arguments); + } + } +} \ No newline at end of file