r9708 - in helma-ng/trunk: modules/helma src/org/helma/util

[email protected] Tue, 5 May 2009 00:33:59 +0200 (CEST)
Newsgroups gmane.comp.java.helma.cvs
Message-ID <20090504223359.AD6B23D0D6@mia>
Author: hannes
Date: 2009-05-05 00:33:59 +0200 (Tue, 05 May 2009)
New Revision: 9708

Modified:
   helma-ng/trunk/modules/helma/logging.js
   helma-ng/trunk/src/org/helma/util/RhinoAppender.java
Log:
Change log4j RhinoAppender to use a thread-local callback, and use helma/webapp/util.ResponseFilter to insert the log messages right before the body close tag.

Details at http://dev.helma.org/trac/helma/changeset/9708

Modified: helma-ng/trunk/modules/helma/logging.js
===================================================================
--- helma-ng/trunk/modules/helma/logging.js	2009-05-04 19:12:23 UTC (rev 9707)
+++ helma-ng/trunk/modules/helma/logging.js	2009-05-04 22:33:59 UTC (rev 9708)
@@ -2,6 +2,9 @@
 include('helma/system');
 include('helma/buffer');
 
+importPackage(org.apache.log4j);
+importClass(org.apache.log4j.xml.DOMConfigurator);
+
 var __shared__ = true;
 
 var configured = false;
@@ -12,11 +15,10 @@
  * Make sure to set the reset property to true in the <log4j:configuration> header
  * e.g. <log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/' reset="true">
  */
-exports.setConfig = function(resource) {
+var setConfig = exports.setConfig = function(resource) {
     var {path, url} = resource;
     var configurator = path.endsWith('.properties') || path.endsWith('.props') ?
-                       org.apache.log4j.PropertyConfigurator :
-                       org.apache.log4j.xml.DOMConfigurator;
+                       PropertyConfigurator : DOMConfigurator;
     configurator.configure(url);
     try {
         configurator.configureAndWatch(path, 2000);
@@ -29,16 +31,16 @@
 /**
  * Get a logger for the given name.
  */
-exports.getLogger = function(name) {
+var getLogger = exports.getLogger = function(name) {
     if (!configured) {
         // getResource('foo').name gets us the absolute path to a local resource
         this.setConfig(getResource('config/log4j.properties'));
     }
-    return org.apache.log4j.Logger.getLogger(name.replace(/\//g, '.'));
+    return Logger.getLogger(name.replace(/\//g, '.'));
 }
 
 // now that getLogger is installed we can get our own log
-var log = exports.getLogger(__name__);
+var log = getLogger(__name__);
 
 /**
  * Render log4j messages to response buffer in the style of helma 1 res.debug().
@@ -48,47 +50,60 @@
     if (!responseLogEnabled) {
         return req.process();
     }
-    getRhinoContext().putThreadLocal('responseLog', new java.util.LinkedList());
 
-    var res = req.process();
+    var messages = [];
+    var appender = Logger.getRootLogger().getAppender("rhino") || {};
 
+    appender.callback = function(message, scriptStack, javaStack) {
+        messages.push([message, scriptStack, javaStack]);
+    };
+
+    var res;
+    try {
+        res = req.process();
+    } finally {
+        appender.callback = null;
+    }
+
     if (res && typeof res === 'object' && typeof res.close === 'function') {
         res = res.close();
     }
 
-    var [status, headers, body] = res;
-
-    if (status != 200 && status < 400) {
+    if (res[0] != 200 && res[0] < 400) {
         return res;
     }
 
-    var list = getRhinoContext().getThreadLocal('responseLog');
-
-    if (list && !list.isEmpty()) {
-        if (!(body instanceof Buffer)) {
-            body = res[2] = new Buffer(body);
-        }
-        for (var i = 0; i < list.size(); i++) {
-            var item = list.get(i);
-            var msg = item[0];
-            var multiline = msg && msg.trim().indexOf('\n') > 0 || msg.indexOf('\r')> 0;
-            body.write("<div class=\"helma-debug-line\" style=\"background: #fc3;");
-            body.write("color: black; border-top: 1px solid black;\">");
-            if (multiline) {
-                body.write("<pre>").write(msg).write("</pre>");
-            } else {
-                body.write(msg);
+    if (messages.length > 0) {
+        var ResponseFilter = require("helma/webapp/util").ResponseFilter;
+        res[2] = new ResponseFilter(res[2], function(part) {
+            if (typeof part != "string" && part.lastIndexOf("</body>") == -1) {
+                return part;
             }
-            if (item[1]) {
-                body.write("<h4 style='padding-left: 8px; margin: 4px;'>Script Stack</h4>");
-                body.write("<pre style='margin: 0;'>", item[1], "</pre>");
+            var buffer = new Buffer();
+            for (var i = 0; i < messages.length; i++) {
+                var item = messages[i];
+                var msg = item[0];
+                var multiline = msg && msg.trim().indexOf('\n') > 0 || msg.indexOf('\r')> 0;
+                buffer.write("<div class=\"helma-debug-line\" style=\"background: #fc3;");
+                buffer.write("color: black; border-top: 1px solid black;\">");
+                if (multiline) {
+                    buffer.write("<pre>").write(msg).write("</pre>");
+                } else {
+                    buffer.write(msg);
+                }
+                if (item[1]) {
+                    buffer.write("<h4 style='padding-left: 8px; margin: 4px;'>Script Stack</h4>");
+                    buffer.write("<pre style='margin: 0;'>", item[1], "</pre>");
+                }
+                if (item[2]) {
+                    buffer.write("<h4 style='padding-left: 8px; margin: 4px;'>Java Stack</h4>");
+                    buffer.write("<pre style='margin: 0;'>", item[2], "</pre>");
+                }
+                buffer.writeln("</div>");
             }
-            if (item[2]) {
-                body.write("<h4 style='padding-left: 8px; margin: 4px;'>Java Stack</h4>");
-                body.write("<pre style='margin: 0;'>", item[2], "</pre>");
-            }
-            body.writeln("</div>");
-        }
+            var insert = part.lastIndexOf("</body>");
+            return part.substring(0, insert) + buffer + part.substring(insert);
+        });
     }
 
     return res;

Modified: helma-ng/trunk/src/org/helma/util/RhinoAppender.java
===================================================================
--- helma-ng/trunk/src/org/helma/util/RhinoAppender.java	2009-05-04 19:12:23 UTC (rev 9707)
+++ helma-ng/trunk/src/org/helma/util/RhinoAppender.java	2009-05-04 22:33:59 UTC (rev 9708)
@@ -6,15 +6,16 @@
 import org.helma.javascript.RhinoEngine;
 import org.mozilla.javascript.Context;
 import org.mozilla.javascript.RhinoException;
+import org.mozilla.javascript.Function;
 
-import java.util.List;
-
 /**
  * A log4j appender that passes log events to a Rhino callback function
  * named <code>onLogEvent</code>.
  */
 public class RhinoAppender extends AppenderSkeleton {
 
+    private static ThreadLocal<Function> callback = new ThreadLocal<Function>();
+
     /**
      * Tries to get the current {@link RhinoEngine} and invoke a callback named
      * <code>onLogEvent</code>, passing the log message and the stack trace rendered
@@ -23,16 +24,11 @@
      */
     protected void append(LoggingEvent event) {
         Context cx = Context.getCurrentContext();
-        if (cx == null) {
+        Function cb = callback.get();
+        if (cx == null || cb == null) {
             return;
         }
-        Object responseLog = cx.getThreadLocal("responseLog");
-        if (responseLog == null || !(responseLog instanceof List)) {
-            return;
-        }
 
-        List logList = (List) responseLog;
-
         if(this.layout == null) {
             errorHandler.error("No layout set for the appender named ["+ name+"].");
             return;
@@ -60,10 +56,18 @@
             }
         }
 
-        logList.add(new String[] {message, scriptStack, javaStack});
+        Object[] args = new Object[] {message, scriptStack, javaStack};
+        cb.call(cx, cb.getParentScope(), null, args);
+    }
 
+    public static Function getCallback() {
+        return callback.get();
     }
 
+    public static void setCallback(Function callback) {
+        RhinoAppender.callback.set(callback);
+    }
+
     /**
      * We need a layout, so this returns true.
      * @return true