blob: 12e97d8f18a3296c27c3927721617a4ab0e5a37c [file] [log] [blame]
Siobhan Tullye18b3442014-02-23 14:23:34 -05001/**
2 * Copyright 2013 Tim Down.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17if (!Array.prototype.shift) {
18 Array.prototype.shift = function() {
19 if (this.length > 0) {
20 var firstItem = this[0];
21 for (var i = 0, len = this.length - 1; i < len; i++) {
22 this[i] = this[i + 1];
23 }
24 this.length--;
25 return firstItem;
26 }
27 };
28}
29
30var log4javascript;
31
32(function() {
33 var newLine = "\r\n";
34 function Log4JavaScript() {}
35 log4javascript = new Log4JavaScript();
36 log4javascript.version = "1.4.6";
37 log4javascript.edition = "log4javascript_lite";
38
39 function getExceptionMessage(ex) {
40 if (ex.message) {
41 return ex.message;
42 } else if (ex.description) {
43 return ex.description;
44 } else {
45 return String(ex);
46 }
47 }
48
49 // Gets the portion of the URL after the last slash
50 function getUrlFileName(url) {
51 var lastSlashIndex = Math.max(url.lastIndexOf("/"), url.lastIndexOf("\\"));
52 return url.substr(lastSlashIndex + 1);
53 }
54
55 // Returns a nicely formatted representation of an error
56 function getExceptionStringRep(ex) {
57 if (ex) {
58 var exStr = "Exception: " + getExceptionMessage(ex);
59 try {
60 if (ex.lineNumber) {
61 exStr += " on line number " + ex.lineNumber;
62 }
63 if (ex.fileName) {
64 exStr += " in file " + getUrlFileName(ex.fileName);
65 }
66 } catch (localEx) {
67 }
68 if (showStackTraces && ex.stack) {
69 exStr += newLine + "Stack trace:" + newLine + ex.stack;
70 }
71 return exStr;
72 }
73 return null;
74 }
75
76 function isError(err) {
77 return (err instanceof Error);
78 }
79
80 function bool(obj) {
81 return Boolean(obj);
82 }
83
84 var enabled = (typeof log4javascript_disabled != "undefined") &&
85 log4javascript_disabled ? false : true;
86
87 log4javascript.setEnabled = function(enable) {
88 enabled = bool(enable);
89 };
90
91 log4javascript.isEnabled = function() {
92 return enabled;
93 };
94
95 var showStackTraces = false;
96
97 log4javascript.setShowStackTraces = function(show) {
98 showStackTraces = bool(show);
99 };
100
101 /* ---------------------------------------------------------------------- */
102 // Levels
103
104 var Level = function(level, name) {
105 this.level = level;
106 this.name = name;
107 };
108
109 Level.prototype = {
110 toString: function() {
111 return this.name;
112 },
113 equals: function(level) {
114 return this.level == level.level;
115 },
116 isGreaterOrEqual: function(level) {
117 return this.level >= level.level;
118 }
119 };
120
121 Level.ALL = new Level(Number.MIN_VALUE, "ALL");
122 Level.TRACE = new Level(10000, "TRACE");
123 Level.DEBUG = new Level(20000, "DEBUG");
124 Level.INFO = new Level(30000, "INFO");
125 Level.WARN = new Level(40000, "WARN");
126 Level.ERROR = new Level(50000, "ERROR");
127 Level.FATAL = new Level(60000, "FATAL");
128 Level.OFF = new Level(Number.MAX_VALUE, "OFF");
129
130 log4javascript.Level = Level;
131
132 /* ---------------------------------------------------------------------- */
133 // Appenders
134
135 function Appender() {
136 var getConsoleHtmlLines = function() {
137 return [
138'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
139'<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">',
140' <head>',
141' <title>log4javascript</title>',
142' <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />',
143' <!-- Make IE8 behave like IE7, having gone to all the trouble of making IE work -->',
144' <meta http-equiv="X-UA-Compatible" content="IE=7" />',
145' <script type="text/javascript">',
146' //<![CDATA[',
147' var loggingEnabled = true;',
148' var messagesBeforeDocLoaded = [];',
149'',
150' function toggleLoggingEnabled() {',
151' setLoggingEnabled($("enableLogging").checked);',
152' }',
153'',
154' function setLoggingEnabled(enable) {',
155' loggingEnabled = enable;',
156' }',
157'',
158' function scrollToLatestEntry() {',
159' var l = getLogContainer();',
160' if (typeof l.scrollTop != "undefined") {',
161' var latestLogEntry = l.lastChild;',
162' if (latestLogEntry) {',
163' l.scrollTop = l.scrollHeight;',
164' }',
165' }',
166' }',
167'',
168' function log(logLevel, formattedMessage) {',
169' if (loggingEnabled) {',
170' if (loaded) {',
171' doLog(logLevel, formattedMessage);',
172' } else {',
173' messagesBeforeDocLoaded.push([logLevel, formattedMessage]);',
174' }',
175' }',
176' }',
177'',
178' function doLog(logLevel, formattedMessage) {',
179' var logEntry = document.createElement("div");',
180' logEntry.appendChild(document.createTextNode(formattedMessage));',
181' logEntry.className = "logentry " + logLevel.name;',
182' getLogContainer().appendChild(logEntry);',
183' scrollToLatestEntry();',
184' }',
185'',
186' function mainPageReloaded() {',
187' var separator = document.createElement("div");',
188' separator.className = "separator";',
189' separator.innerHTML = "&nbsp;";',
190' getLogContainer().appendChild(separator);',
191' }',
192'',
193' var loaded = false;',
194' var logLevels = ["DEBUG", "INFO", "WARN", "ERROR", "FATAL"];',
195'',
196' window.onload = function() {',
197' setLogContainerHeight();',
198' toggleLoggingEnabled();',
199' for (var i = 0; i < messagesBeforeDocLoaded.length; i++) {',
200' doLog(messagesBeforeDocLoaded[i][0], messagesBeforeDocLoaded[i][1]);',
201' }',
202' messagesBeforeDocLoaded = [];',
203' loaded = true;',
204'',
205' // Workaround to make sure log div starts at the correct size',
206' setTimeout(setLogContainerHeight, 20);',
207' };',
208'',
209' function getLogContainer() {',
210' return $("log");',
211' }',
212'',
213' function clearLog() {',
214' getLogContainer().innerHTML = "";',
215' }',
216'',
217' /* ------------------------------------------------------------------------- */',
218'',
219' // Other utility functions',
220'',
221' // Syntax borrowed from Prototype library',
222' function $(id) {',
223' return document.getElementById(id);',
224' }',
225'',
226' function getWindowHeight() {',
227' if (window.innerHeight) {',
228' return window.innerHeight;',
229' } else if (document.documentElement && document.documentElement.clientHeight) {',
230' return document.documentElement.clientHeight;',
231' } else if (document.body) {',
232' return document.body.clientHeight;',
233' }',
234' return 0;',
235' }',
236'',
237' function getChromeHeight() {',
238' return $("toolbar").offsetHeight;',
239' }',
240'',
241' function setLogContainerHeight() {',
242' var windowHeight = getWindowHeight();',
243' $("body").style.height = getWindowHeight() + "px";',
244' getLogContainer().style.height = "" +',
245' Math.max(0, windowHeight - getChromeHeight()) + "px";',
246' }',
247'',
248' window.onresize = function() {',
249' setLogContainerHeight();',
250' };',
251'',
252' //]]>',
253' </script>',
254' <style type="text/css">',
255' body {',
256' background-color: white;',
257' color: black;',
258' padding: 0;',
259' margin: 0;',
260' font-family: tahoma, verdana, arial, helvetica, sans-serif;',
261' overflow: hidden;',
262' }',
263' ',
264' div#toolbar {',
265' border-top: solid #ffffff 1px;',
266' border-bottom: solid #aca899 1px;',
267' background-color: #f1efe7;',
268' padding: 3px 5px;',
269' font-size: 68.75%;',
270' }',
271'',
272' div#toolbar input.button {',
273' padding: 0 5px;',
274' font-size: 100%;',
275' }',
276'',
277' div#log {',
278' font-family: Courier New, Courier;',
279' font-size: 75%;',
280' width: 100%;',
281' overflow: auto;',
282' clear: both;',
283' }',
284'',
285' *.logentry {',
286' overflow: visible;',
287' white-space: pre;',
288' }',
289'',
290' *.TRACE {',
291' color: #666666;',
292' }',
293'',
294' *.DEBUG {',
295' color: green;',
296' }',
297'',
298' *.INFO {',
299' color: #000099;',
300' }',
301'',
302' *.WARN {',
303' color: #999900;',
304' }',
305'',
306' *.ERROR {',
307' color: red;',
308' }',
309'',
310' *.FATAL {',
311' color: #660066;',
312' }',
313'',
314' div#log div.separator {',
315' background-color: #cccccc;',
316' margin: 5px 0;',
317' line-height: 1px;',
318' }',
319' </style>',
320' </head>',
321'',
322' <body id="body">',
323' <div id="toolbar">',
324' Options:',
325' <input type="checkbox" id="enableLogging" onclick="toggleLoggingEnabled()" class="stateful" checked="checked" title="Enable/disable logging" /><label for="enableLogging" id="enableLoggingLabel">Enable logging</label>',
326' <input type="button" id="clearButton" value="Clear" onclick="clearLog()" class="stateful button" title="Clear all log messages" />',
327' <input type="button" id="closeButton" value="Close" onclick="window.close()" class="stateful button" title="Close the window" />',
328' </div>',
329' <div id="log" class="TRACE DEBUG INFO WARN ERROR FATAL"></div>',
330' </body>',
331'</html>'
332];
333 };
334
335 var popUp = null;
336 var popUpsBlocked = false;
337 var popUpClosed = false;
338 var popUpLoaded = false;
339 var complainAboutPopUpBlocking = true;
340 var initialized = false;
341 var isSupported = true;
342 var width = 600;
343 var height = 400;
344 var focusPopUp = false;
345 var queuedLoggingEvents = new Array();
346
347 function isLoaded(win) {
348 try {
349 return bool(win.loaded);
350 } catch (ex) {
351 return false;
352 }
353 }
354
355 function finalInit() {
356 popUpLoaded = true;
357 appendQueuedLoggingEvents();
358 }
359
360 function writeHtml(doc) {
361 var lines = getConsoleHtmlLines();
362 doc.open();
363 for (var i = 0, len = lines.length; i < len; i++) {
364 doc.writeln(lines[i]);
365 }
366 doc.close();
367 }
368
369 function pollConsoleWindow() {
370 function pollConsoleWindowLoaded() {
371 if (popUpLoaded) {
372 clearInterval(poll);
373 } else if (bool(popUp) && isLoaded(popUp)) {
374 clearInterval(poll);
375 finalInit();
376 }
377 }
378
379 // Poll the pop-up since the onload event is not reliable
380 var poll = setInterval(pollConsoleWindowLoaded, 100);
381 }
382
383 function init() {
384 var windowProperties = "width=" + width + ",height=" + height + ",status,resizable";
385 var windowName = "log4javascriptLitePopUp" + location.host.replace(/[^a-z0-9]/gi, "_");
386
387 popUp = window.open("", windowName, windowProperties);
388 popUpClosed = false;
389 if (popUp) {
390 if (isLoaded(popUp)) {
391 popUp.mainPageReloaded();
392 finalInit();
393 } else {
394 writeHtml(popUp.document);
395
396 // Check if the pop-up window object is available
397 if (isLoaded(popUp)) {
398 finalInit();
399 } else {
400 pollConsoleWindow();
401 }
402 }
403 } else {
404 isSupported = false;
405 if (complainAboutPopUpBlocking) {
406 alert("log4javascript: pop-up windows appear to be blocked. Please unblock them to use pop-up logging.");
407 }
408 }
409 initialized = true;
410 }
411
412 function safeToAppend() {
413 if (!popUpsBlocked && !popUpClosed) {
414 if (popUp.closed) {
415 popUpClosed = true;
416 return false;
417 }
418 if (!popUpLoaded && popUp.loaded) {
419 popUpLoaded = true;
420 }
421 }
422 return !popUpsBlocked && popUpLoaded && !popUpClosed;
423 }
424
425 function padWithZeroes(num, len) {
426 var str = "" + num;
427 while (str.length < len) {
428 str = "0" + str;
429 }
430 return str;
431 }
432
433 function padWithSpaces(str, len) {
434 while (str.length < len) {
435 str += " ";
436 }
437 return str;
438 }
439
440 this.append = function(loggingEvent) {
441 if (!initialized) {
442 init();
443 }
444 queuedLoggingEvents.push(loggingEvent);
445 if (safeToAppend()) {
446 appendQueuedLoggingEvents();
447 }
448 };
449
450 function appendQueuedLoggingEvents() {
451 if (safeToAppend()) {
452 while (queuedLoggingEvents.length > 0) {
453 var currentLoggingEvent = queuedLoggingEvents.shift();
454 var date = currentLoggingEvent.timeStamp;
455 var formattedDate = padWithZeroes(date.getHours(), 2) + ":" +
456 padWithZeroes(date.getMinutes(), 2) + ":" + padWithZeroes(date.getSeconds(), 2);
457 var formattedMessage = formattedDate + " " + padWithSpaces(currentLoggingEvent.level.name, 5) +
458 " - " + currentLoggingEvent.getCombinedMessages();
459 var throwableStringRep = currentLoggingEvent.getThrowableStrRep();
460 if (throwableStringRep) {
461 formattedMessage += newLine + throwableStringRep;
462 }
463 popUp.log(currentLoggingEvent.level, formattedMessage);
464 }
465 if (focusPopUp) {
466 popUp.focus();
467 }
468 }
469 }
470 }
471
472 log4javascript.Appender = Appender;
473
474 /* ---------------------------------------------------------------------- */
475 // Loggers
476
477 function Logger() {
478 var appender = new Appender();
479 var loggerLevel = Level.ALL;
480
481 this.log = function(level, params) {
482 if (enabled && level.isGreaterOrEqual(this.getLevel())) {
483 // Check whether last param is an exception
484 var exception;
485 var finalParamIndex = params.length - 1;
486 var lastParam = params[params.length - 1];
487 if (params.length > 1 && isError(lastParam)) {
488 exception = lastParam;
489 finalParamIndex--;
490 }
491
492 // Construct genuine array for the params
493 var messages = [];
494 for (var i = 0; i <= finalParamIndex; i++) {
495 messages[i] = params[i];
496 }
497
498 var loggingEvent = new LoggingEvent(
499 this, new Date(), level, messages, exception);
500
501 appender.append(loggingEvent);
502 }
503 };
504
505 this.setLevel = function(level) {
506 loggerLevel = level;
507 };
508
509 this.getLevel = function() {
510 return loggerLevel;
511 };
512 }
513
514 Logger.prototype = {
515 trace: function() {
516 this.log(Level.TRACE, arguments);
517 },
518
519 debug: function() {
520 this.log(Level.DEBUG, arguments);
521 },
522
523 info: function() {
524 this.log(Level.INFO, arguments);
525 },
526
527 warn: function() {
528 this.log(Level.WARN, arguments);
529 },
530
531 error: function() {
532 this.log(Level.ERROR, arguments);
533 },
534
535 fatal: function() {
536 this.log(Level.FATAL, arguments);
537 },
538
539 isEnabledFor: function(level) {
540 return level.isGreaterOrEqual(this.getLevel());
541 },
542
543 isTraceEnabled: function() {
544 return this.isEnabledFor(Level.TRACE);
545 },
546
547 isDebugEnabled: function() {
548 return this.isEnabledFor(Level.DEBUG);
549 },
550
551 isInfoEnabled: function() {
552 return this.isEnabledFor(Level.INFO);
553 },
554
555 isWarnEnabled: function() {
556 return this.isEnabledFor(Level.WARN);
557 },
558
559 isErrorEnabled: function() {
560 return this.isEnabledFor(Level.ERROR);
561 },
562
563 isFatalEnabled: function() {
564 return this.isEnabledFor(Level.FATAL);
565 }
566 };
567
568 /* ---------------------------------------------------------------------- */
569 // Logger access methods
570
571 var defaultLogger = null;
572 log4javascript.getDefaultLogger = function() {
573 if (!defaultLogger) {
574 defaultLogger = new Logger();
575 }
576 return defaultLogger;
577 };
578
579 log4javascript.getLogger = log4javascript.getDefaultLogger;
580
581 var nullLogger = null;
582 log4javascript.getNullLogger = function() {
583 if (!nullLogger) {
584 nullLogger = new Logger();
585 nullLogger.setLevel(Level.OFF);
586 }
587 return nullLogger;
588 };
589
590 /* ---------------------------------------------------------------------- */
591 // Logging events
592
593 var LoggingEvent = function(logger, timeStamp, level, messages,
594 exception) {
595 this.logger = logger;
596 this.timeStamp = timeStamp;
597 this.level = level;
598 this.messages = messages;
599 this.exception = exception;
600 };
601
602 LoggingEvent.prototype = {
603 getThrowableStrRep: function() {
604 return this.exception ?
605 getExceptionStringRep(this.exception) : "";
606 },
607
608 getCombinedMessages: function() {
609 return (this.messages.length === 1) ? this.messages[0] :
610 this.messages.join(newLine);
611 }
612 };
613
614 log4javascript.LoggingEvent = LoggingEvent;
615
616 // Ensure that the log4javascript object is available in the window. This
617 // is necessary for log4javascript to be available in IE if loaded using
618 // Dojo's module system
619 window.log4javascript = log4javascript;
620})();