2 * Copyright 2013 Tim Down.
\r
4 * Licensed under the Apache License, Version 2.0 (the "License");
\r
5 * you may not use this file except in compliance with the License.
\r
6 * You may obtain a copy of the License at
\r
8 * http://www.apache.org/licenses/LICENSE-2.0
\r
10 * Unless required by applicable law or agreed to in writing, software
\r
11 * distributed under the License is distributed on an "AS IS" BASIS,
\r
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
13 * See the License for the specific language governing permissions and
\r
14 * limitations under the License.
\r
17 if (!Array.prototype.shift) {
\r
18 Array.prototype.shift = function() {
\r
19 if (this.length > 0) {
\r
20 var firstItem = this[0];
\r
21 for (var i = 0, len = this.length - 1; i < len; i++) {
\r
22 this[i] = this[i + 1];
\r
33 var newLine = "\r\n";
\r
34 function Log4JavaScript() {}
\r
35 log4javascript = new Log4JavaScript();
\r
36 log4javascript.version = "1.4.6";
\r
37 log4javascript.edition = "log4javascript_lite";
\r
39 function getExceptionMessage(ex) {
\r
42 } else if (ex.description) {
\r
43 return ex.description;
\r
49 // Gets the portion of the URL after the last slash
\r
50 function getUrlFileName(url) {
\r
51 var lastSlashIndex = Math.max(url.lastIndexOf("/"), url.lastIndexOf("\\"));
\r
52 return url.substr(lastSlashIndex + 1);
\r
55 // Returns a nicely formatted representation of an error
\r
56 function getExceptionStringRep(ex) {
\r
58 var exStr = "Exception: " + getExceptionMessage(ex);
\r
60 if (ex.lineNumber) {
\r
61 exStr += " on line number " + ex.lineNumber;
\r
64 exStr += " in file " + getUrlFileName(ex.fileName);
\r
68 if (showStackTraces && ex.stack) {
\r
69 exStr += newLine + "Stack trace:" + newLine + ex.stack;
\r
76 function isError(err) {
\r
77 return (err instanceof Error);
\r
80 function bool(obj) {
\r
81 return Boolean(obj);
\r
84 var enabled = (typeof log4javascript_disabled != "undefined") &&
\r
85 log4javascript_disabled ? false : true;
\r
87 log4javascript.setEnabled = function(enable) {
\r
88 enabled = bool(enable);
\r
91 log4javascript.isEnabled = function() {
\r
95 var showStackTraces = false;
\r
97 log4javascript.setShowStackTraces = function(show) {
\r
98 showStackTraces = bool(show);
\r
101 /* ---------------------------------------------------------------------- */
\r
104 var Level = function(level, name) {
\r
105 this.level = level;
\r
109 Level.prototype = {
\r
110 toString: function() {
\r
113 equals: function(level) {
\r
114 return this.level == level.level;
\r
116 isGreaterOrEqual: function(level) {
\r
117 return this.level >= level.level;
\r
121 Level.ALL = new Level(Number.MIN_VALUE, "ALL");
\r
122 Level.TRACE = new Level(10000, "TRACE");
\r
123 Level.DEBUG = new Level(20000, "DEBUG");
\r
124 Level.INFO = new Level(30000, "INFO");
\r
125 Level.WARN = new Level(40000, "WARN");
\r
126 Level.ERROR = new Level(50000, "ERROR");
\r
127 Level.FATAL = new Level(60000, "FATAL");
\r
128 Level.OFF = new Level(Number.MAX_VALUE, "OFF");
\r
130 log4javascript.Level = Level;
\r
132 /* ---------------------------------------------------------------------- */
\r
135 function Appender() {
\r
136 var getConsoleHtmlLines = function() {
\r
138 '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
\r
139 '<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">',
\r
141 ' <title>log4javascript</title>',
\r
142 ' <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />',
\r
143 ' <!-- Make IE8 behave like IE7, having gone to all the trouble of making IE work -->',
\r
144 ' <meta http-equiv="X-UA-Compatible" content="IE=7" />',
\r
145 ' <script type="text/javascript">',
\r
147 ' var loggingEnabled = true;',
\r
148 ' var messagesBeforeDocLoaded = [];',
\r
150 ' function toggleLoggingEnabled() {',
\r
151 ' setLoggingEnabled($("enableLogging").checked);',
\r
154 ' function setLoggingEnabled(enable) {',
\r
155 ' loggingEnabled = enable;',
\r
158 ' function scrollToLatestEntry() {',
\r
159 ' var l = getLogContainer();',
\r
160 ' if (typeof l.scrollTop != "undefined") {',
\r
161 ' var latestLogEntry = l.lastChild;',
\r
162 ' if (latestLogEntry) {',
\r
163 ' l.scrollTop = l.scrollHeight;',
\r
168 ' function log(logLevel, formattedMessage) {',
\r
169 ' if (loggingEnabled) {',
\r
171 ' doLog(logLevel, formattedMessage);',
\r
173 ' messagesBeforeDocLoaded.push([logLevel, formattedMessage]);',
\r
178 ' function doLog(logLevel, formattedMessage) {',
\r
179 ' var logEntry = document.createElement("div");',
\r
180 ' logEntry.appendChild(document.createTextNode(formattedMessage));',
\r
181 ' logEntry.className = "logentry " + logLevel.name;',
\r
182 ' getLogContainer().appendChild(logEntry);',
\r
183 ' scrollToLatestEntry();',
\r
186 ' function mainPageReloaded() {',
\r
187 ' var separator = document.createElement("div");',
\r
188 ' separator.className = "separator";',
\r
189 ' separator.innerHTML = " ";',
\r
190 ' getLogContainer().appendChild(separator);',
\r
193 ' var loaded = false;',
\r
194 ' var logLevels = ["DEBUG", "INFO", "WARN", "ERROR", "FATAL"];',
\r
196 ' window.onload = function() {',
\r
197 ' setLogContainerHeight();',
\r
198 ' toggleLoggingEnabled();',
\r
199 ' for (var i = 0; i < messagesBeforeDocLoaded.length; i++) {',
\r
200 ' doLog(messagesBeforeDocLoaded[i][0], messagesBeforeDocLoaded[i][1]);',
\r
202 ' messagesBeforeDocLoaded = [];',
\r
205 ' // Workaround to make sure log div starts at the correct size',
\r
206 ' setTimeout(setLogContainerHeight, 20);',
\r
209 ' function getLogContainer() {',
\r
210 ' return $("log");',
\r
213 ' function clearLog() {',
\r
214 ' getLogContainer().innerHTML = "";',
\r
217 ' /* ------------------------------------------------------------------------- */',
\r
219 ' // Other utility functions',
\r
221 ' // Syntax borrowed from Prototype library',
\r
222 ' function $(id) {',
\r
223 ' return document.getElementById(id);',
\r
226 ' function getWindowHeight() {',
\r
227 ' if (window.innerHeight) {',
\r
228 ' return window.innerHeight;',
\r
229 ' } else if (document.documentElement && document.documentElement.clientHeight) {',
\r
230 ' return document.documentElement.clientHeight;',
\r
231 ' } else if (document.body) {',
\r
232 ' return document.body.clientHeight;',
\r
237 ' function getChromeHeight() {',
\r
238 ' return $("toolbar").offsetHeight;',
\r
241 ' function setLogContainerHeight() {',
\r
242 ' var windowHeight = getWindowHeight();',
\r
243 ' $("body").style.height = getWindowHeight() + "px";',
\r
244 ' getLogContainer().style.height = "" +',
\r
245 ' Math.max(0, windowHeight - getChromeHeight()) + "px";',
\r
248 ' window.onresize = function() {',
\r
249 ' setLogContainerHeight();',
\r
254 ' <style type="text/css">',
\r
256 ' background-color: white;',
\r
260 ' font-family: tahoma, verdana, arial, helvetica, sans-serif;',
\r
261 ' overflow: hidden;',
\r
265 ' border-top: solid #ffffff 1px;',
\r
266 ' border-bottom: solid #aca899 1px;',
\r
267 ' background-color: #f1efe7;',
\r
268 ' padding: 3px 5px;',
\r
269 ' font-size: 68.75%;',
\r
272 ' div#toolbar input.button {',
\r
273 ' padding: 0 5px;',
\r
274 ' font-size: 100%;',
\r
278 ' font-family: Courier New, Courier;',
\r
279 ' font-size: 75%;',
\r
281 ' overflow: auto;',
\r
286 ' overflow: visible;',
\r
287 ' white-space: pre;',
\r
291 ' color: #666666;',
\r
299 ' color: #000099;',
\r
303 ' color: #999900;',
\r
311 ' color: #660066;',
\r
314 ' div#log div.separator {',
\r
315 ' background-color: #cccccc;',
\r
317 ' line-height: 1px;',
\r
322 ' <body id="body">',
\r
323 ' <div id="toolbar">',
\r
325 ' <input type="checkbox" id="enableLogging" onclick="toggleLoggingEnabled()" class="stateful" checked="checked" title="Enable/disable logging" /><label for="enableLogging" id="enableLoggingLabel">Enable logging</label>',
\r
326 ' <input type="button" id="clearButton" value="Clear" onclick="clearLog()" class="stateful button" title="Clear all log messages" />',
\r
327 ' <input type="button" id="closeButton" value="Close" onclick="window.close()" class="stateful button" title="Close the window" />',
\r
329 ' <div id="log" class="TRACE DEBUG INFO WARN ERROR FATAL"></div>',
\r
336 var popUpsBlocked = false;
\r
337 var popUpClosed = false;
\r
338 var popUpLoaded = false;
\r
339 var complainAboutPopUpBlocking = true;
\r
340 var initialized = false;
\r
341 var isSupported = true;
\r
344 var focusPopUp = false;
\r
345 var queuedLoggingEvents = new Array();
\r
347 function isLoaded(win) {
\r
349 return bool(win.loaded);
\r
355 function finalInit() {
\r
356 popUpLoaded = true;
\r
357 appendQueuedLoggingEvents();
\r
360 function writeHtml(doc) {
\r
361 var lines = getConsoleHtmlLines();
\r
363 for (var i = 0, len = lines.length; i < len; i++) {
\r
364 doc.writeln(lines[i]);
\r
369 function pollConsoleWindow() {
\r
370 function pollConsoleWindowLoaded() {
\r
372 clearInterval(poll);
\r
373 } else if (bool(popUp) && isLoaded(popUp)) {
\r
374 clearInterval(poll);
\r
379 // Poll the pop-up since the onload event is not reliable
\r
380 var poll = setInterval(pollConsoleWindowLoaded, 100);
\r
384 var windowProperties = "width=" + width + ",height=" + height + ",status,resizable";
\r
385 var windowName = "log4javascriptLitePopUp" + location.host.replace(/[^a-z0-9]/gi, "_");
\r
387 popUp = window.open("", windowName, windowProperties);
\r
388 popUpClosed = false;
\r
390 if (isLoaded(popUp)) {
\r
391 popUp.mainPageReloaded();
\r
394 writeHtml(popUp.document);
\r
396 // Check if the pop-up window object is available
\r
397 if (isLoaded(popUp)) {
\r
400 pollConsoleWindow();
\r
404 isSupported = false;
\r
405 if (complainAboutPopUpBlocking) {
\r
406 alert("log4javascript: pop-up windows appear to be blocked. Please unblock them to use pop-up logging.");
\r
409 initialized = true;
\r
412 function safeToAppend() {
\r
413 if (!popUpsBlocked && !popUpClosed) {
\r
414 if (popUp.closed) {
\r
415 popUpClosed = true;
\r
418 if (!popUpLoaded && popUp.loaded) {
\r
419 popUpLoaded = true;
\r
422 return !popUpsBlocked && popUpLoaded && !popUpClosed;
\r
425 function padWithZeroes(num, len) {
\r
426 var str = "" + num;
\r
427 while (str.length < len) {
\r
433 function padWithSpaces(str, len) {
\r
434 while (str.length < len) {
\r
440 this.append = function(loggingEvent) {
\r
441 if (!initialized) {
\r
444 queuedLoggingEvents.push(loggingEvent);
\r
445 if (safeToAppend()) {
\r
446 appendQueuedLoggingEvents();
\r
450 function appendQueuedLoggingEvents() {
\r
451 if (safeToAppend()) {
\r
452 while (queuedLoggingEvents.length > 0) {
\r
453 var currentLoggingEvent = queuedLoggingEvents.shift();
\r
454 var date = currentLoggingEvent.timeStamp;
\r
455 var formattedDate = padWithZeroes(date.getHours(), 2) + ":" +
\r
456 padWithZeroes(date.getMinutes(), 2) + ":" + padWithZeroes(date.getSeconds(), 2);
\r
457 var formattedMessage = formattedDate + " " + padWithSpaces(currentLoggingEvent.level.name, 5) +
\r
458 " - " + currentLoggingEvent.getCombinedMessages();
\r
459 var throwableStringRep = currentLoggingEvent.getThrowableStrRep();
\r
460 if (throwableStringRep) {
\r
461 formattedMessage += newLine + throwableStringRep;
\r
463 popUp.log(currentLoggingEvent.level, formattedMessage);
\r
472 log4javascript.Appender = Appender;
\r
474 /* ---------------------------------------------------------------------- */
\r
477 function Logger() {
\r
478 var appender = new Appender();
\r
479 var loggerLevel = Level.ALL;
\r
481 this.log = function(level, params) {
\r
482 if (enabled && level.isGreaterOrEqual(this.getLevel())) {
\r
483 // Check whether last param is an exception
\r
485 var finalParamIndex = params.length - 1;
\r
486 var lastParam = params[params.length - 1];
\r
487 if (params.length > 1 && isError(lastParam)) {
\r
488 exception = lastParam;
\r
492 // Construct genuine array for the params
\r
494 for (var i = 0; i <= finalParamIndex; i++) {
\r
495 messages[i] = params[i];
\r
498 var loggingEvent = new LoggingEvent(
\r
499 this, new Date(), level, messages, exception);
\r
501 appender.append(loggingEvent);
\r
505 this.setLevel = function(level) {
\r
506 loggerLevel = level;
\r
509 this.getLevel = function() {
\r
510 return loggerLevel;
\r
514 Logger.prototype = {
\r
515 trace: function() {
\r
516 this.log(Level.TRACE, arguments);
\r
519 debug: function() {
\r
520 this.log(Level.DEBUG, arguments);
\r
524 this.log(Level.INFO, arguments);
\r
528 this.log(Level.WARN, arguments);
\r
531 error: function() {
\r
532 this.log(Level.ERROR, arguments);
\r
535 fatal: function() {
\r
536 this.log(Level.FATAL, arguments);
\r
539 isEnabledFor: function(level) {
\r
540 return level.isGreaterOrEqual(this.getLevel());
\r
543 isTraceEnabled: function() {
\r
544 return this.isEnabledFor(Level.TRACE);
\r
547 isDebugEnabled: function() {
\r
548 return this.isEnabledFor(Level.DEBUG);
\r
551 isInfoEnabled: function() {
\r
552 return this.isEnabledFor(Level.INFO);
\r
555 isWarnEnabled: function() {
\r
556 return this.isEnabledFor(Level.WARN);
\r
559 isErrorEnabled: function() {
\r
560 return this.isEnabledFor(Level.ERROR);
\r
563 isFatalEnabled: function() {
\r
564 return this.isEnabledFor(Level.FATAL);
\r
568 /* ---------------------------------------------------------------------- */
\r
569 // Logger access methods
\r
571 var defaultLogger = null;
\r
572 log4javascript.getDefaultLogger = function() {
\r
573 if (!defaultLogger) {
\r
574 defaultLogger = new Logger();
\r
576 return defaultLogger;
\r
579 log4javascript.getLogger = log4javascript.getDefaultLogger;
\r
581 var nullLogger = null;
\r
582 log4javascript.getNullLogger = function() {
\r
584 nullLogger = new Logger();
\r
585 nullLogger.setLevel(Level.OFF);
\r
590 /* ---------------------------------------------------------------------- */
\r
593 var LoggingEvent = function(logger, timeStamp, level, messages,
\r
595 this.logger = logger;
\r
596 this.timeStamp = timeStamp;
\r
597 this.level = level;
\r
598 this.messages = messages;
\r
599 this.exception = exception;
\r
602 LoggingEvent.prototype = {
\r
603 getThrowableStrRep: function() {
\r
604 return this.exception ?
\r
605 getExceptionStringRep(this.exception) : "";
\r
608 getCombinedMessages: function() {
\r
609 return (this.messages.length === 1) ? this.messages[0] :
\r
610 this.messages.join(newLine);
\r
614 log4javascript.LoggingEvent = LoggingEvent;
\r
616 // Ensure that the log4javascript object is available in the window. This
\r
617 // is necessary for log4javascript to be available in IE if loaded using
\r
618 // Dojo's module system
\r
619 window.log4javascript = log4javascript;
\r