Author: maarten
Date: Mon Feb 11 13:31:39 2008
New Revision: 620617

URL: http://svn.apache.org/viewvc?rev=620617&view=rev
Log:
resolved DIRMINA-513 : added an MDC-aware formatter for java.util.logging

Added:
    mina/trunk/core/src/main/java/org/apache/mina/util/Log4jXmlFormatter.java   
(with props)
    mina/trunk/core/src/main/java/org/apache/mina/util/Transform.java   (with 
props)

Added: mina/trunk/core/src/main/java/org/apache/mina/util/Log4jXmlFormatter.java
URL: 
http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/util/Log4jXmlFormatter.java?rev=620617&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/util/Log4jXmlFormatter.java 
(added)
+++ mina/trunk/core/src/main/java/org/apache/mina/util/Log4jXmlFormatter.java 
Mon Feb 11 13:31:39 2008
@@ -0,0 +1,148 @@
+package org.apache.mina.util;
+
+import org.slf4j.MDC;
+import org.slf4j.helpers.BasicMDCAdapter;
+
+import java.util.logging.Formatter;
+import java.util.logging.LogRecord;
+import java.util.Set;
+import java.util.Arrays;
+
+/**
+ * Implementation of [EMAIL PROTECTED] java.util.logging.Formatter} that 
generates xml in the log4j format.
+ * <p>
+ * The generated xml corresponds 100% with what is generated by
+ * log4j's <a 
href=http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/XMLLayout.html";>XMLLayout</a>
+ * <p>
+ * The MDC properties will only be correct when <code>format</code> is called 
from the same thread
+ * that generated the LogRecord.
+ * <p>
+ * The file and line attributes in the locationInfo element will always be "?"
+ * since java.util.logging.LogRecord does not provide that info.
+ * <p>
+ * The implementation is heavily based on org.apache.log4j.xml.XMLLayout
+ * </p>
+ */
+public class Log4jXmlFormatter extends Formatter {
+
+    private final int DEFAULT_SIZE = 256;
+    private final int UPPER_LIMIT = 2048;
+
+    private StringBuffer buf = new StringBuffer(DEFAULT_SIZE);
+    private boolean locationInfo = false;
+    private boolean properties = false;
+
+    /**
+     * The <b>LocationInfo</b> option takes a boolean value. By default,
+     * it is set to false which means there will be no location
+     * information output by this layout. If the the option is set to
+     * true, then the file name and line number of the statement at the
+     * origin of the log statement will be output.
+     *
+     * @param flag whether locationInfo should be output by this layout
+     */
+    public void setLocationInfo(boolean flag) {
+        locationInfo = flag;
+    }
+
+    /**
+     * Returns the current value of the <b>LocationInfo</b> option.
+     *
+     * @return whether locationInfo will be output by this layout
+     */
+    public boolean getLocationInfo() {
+        return locationInfo;
+    }
+
+    /**
+     * Sets whether MDC key-value pairs should be output, default false.
+     *
+     * @param flag new value.
+     */
+    public void setProperties(final boolean flag) {
+        properties = flag;
+    }
+
+    /**
+     * Gets whether MDC key-value pairs should be output.
+     *
+     * @return true if MDC key-value pairs are output.
+     */
+    public boolean getProperties() {
+        return properties;
+    }
+
+    public String format(final LogRecord record) {
+        // Reset working buffer. If the buffer is too large, then we need a new
+        // one in order to avoid the penalty of creating a large array.
+        if (buf.capacity() > UPPER_LIMIT) {
+            buf = new StringBuffer(DEFAULT_SIZE);
+        } else {
+            buf.setLength(0);
+        }
+        buf.append("<log4j:event logger=\"");
+        buf.append(Transform.escapeTags(record.getLoggerName()));
+        buf.append("\" timestamp=\"");
+        buf.append(record.getMillis());
+        buf.append("\" level=\"");
+
+        buf.append(Transform.escapeTags(record.getLevel().getName()));
+        buf.append("\" thread=\"");
+        buf.append(String.valueOf(record.getThreadID()));
+        buf.append("\">\r\n");
+
+        buf.append("<log4j:message><![CDATA[");
+        // Append the rendered message. Also make sure to escape any
+        // existing CDATA sections.
+        Transform.appendEscapingCDATA(buf, record.getMessage());
+        buf.append("]]></log4j:message>\r\n");
+
+        if (record.getThrown() != null) {
+            String[] s = Transform.getThrowableStrRep(record.getThrown());
+            if (s != null) {
+                buf.append("<log4j:throwable><![CDATA[");
+                for (String value : s) {
+                    Transform.appendEscapingCDATA(buf, value);
+                    buf.append("\r\n");
+                }
+                buf.append("]]></log4j:throwable>\r\n");
+            }
+        }
+
+        if (locationInfo) {
+            buf.append("<log4j:locationInfo class=\"");
+            buf.append(Transform.escapeTags(record.getSourceClassName()));
+            buf.append("\" method=\"");
+            buf.append(Transform.escapeTags(record.getSourceMethodName()));
+            buf.append("\" file=\"?\" line=\"?\"/>\r\n");
+        }
+
+        if (properties) {
+            if (MDC.getMDCAdapter() instanceof BasicMDCAdapter) {
+                BasicMDCAdapter mdcAdapter = (BasicMDCAdapter) 
MDC.getMDCAdapter();
+                Set keySet = mdcAdapter.getKeys();
+                if (keySet != null && keySet.size() > 0) {
+                    buf.append("<log4j:properties>\r\n");
+                    Object[] keys = keySet.toArray();
+                    Arrays.sort(keys);
+                    for (Object key1 : keys) {
+                        String key = key1.toString();
+                        Object val = mdcAdapter.get(key);
+                        if (val != null) {
+                            buf.append("<log4j:data name=\"");
+                            buf.append(Transform.escapeTags(key));
+                            buf.append("\" value=\"");
+                            
buf.append(Transform.escapeTags(String.valueOf(val)));
+                            buf.append("\"/>\r\n");
+                        }
+                    }
+                    buf.append("</log4j:properties>\r\n");
+                }
+            }
+        }
+        buf.append("</log4j:event>\r\n\r\n");
+
+        return buf.toString();
+    }
+
+}

Propchange: 
mina/trunk/core/src/main/java/org/apache/mina/util/Log4jXmlFormatter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
mina/trunk/core/src/main/java/org/apache/mina/util/Log4jXmlFormatter.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/trunk/core/src/main/java/org/apache/mina/util/Transform.java
URL: 
http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/util/Transform.java?rev=620617&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/util/Transform.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/util/Transform.java Mon Feb 
11 13:31:39 2008
@@ -0,0 +1,126 @@
+package org.apache.mina.util;
+
+import java.io.IOException;
+import java.io.LineNumberReader;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
+
+/**
+ * Utility class for working with xml data
+ *
+ * Implementation is heavily based on org.apache.log4j.helpers.Transform
+ */
+public class Transform {
+
+    private static final String CDATA_START  = "<![CDATA[";
+    private static final String CDATA_END    = "]]>";
+    private static final String CDATA_PSEUDO_END = "]]&gt;";
+    private static final String CDATA_EMBEDED_END = CDATA_END + 
CDATA_PSEUDO_END + CDATA_START;
+    private static final int CDATA_END_LEN = CDATA_END.length();
+
+    /**
+     * This method takes a string which may contain HTML tags (ie,
+     * &lt;b&gt;, &lt;table&gt;, etc) and replaces any
+     * '<',  '>' , '&' or '"'
+     * characters with respective predefined entity references.
+     *
+     * @param input The text to be converted.
+     * @return The input string with the special characters replaced.
+     * */
+    static public String escapeTags(final String input) {
+        // Check if the string is null, zero length or devoid of special 
characters
+        // if so, return what was sent in.
+
+        if(input == null
+            || input.length() == 0
+            || (input.indexOf('"') == -1 &&
+            input.indexOf('&') == -1 &&
+            input.indexOf('<') == -1 &&
+            input.indexOf('>') == -1)) {
+            return input;
+        }
+
+        StringBuffer buf = new StringBuffer(input.length() + 6);
+        char ch;
+
+        int len = input.length();
+        for(int i=0; i < len; i++) {
+            ch = input.charAt(i);
+            if (ch > '>') {
+                buf.append(ch);
+            } else if(ch == '<') {
+                buf.append("&lt;");
+            } else if(ch == '>') {
+                buf.append("&gt;");
+            } else if(ch == '&') {
+                buf.append("&amp;");
+            } else if(ch == '"') {
+                buf.append("&quot;");
+            } else {
+                buf.append(ch);
+            }
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Ensures that embeded CDEnd strings (]]>) are handled properly
+     * within message, NDC and throwable tag text.
+     *
+     * @param buf StringBuffer holding the XML data to this point.  The
+     * initial CDStart (<![CDATA[) and final CDEnd (]]>) of the CDATA
+     * section are the responsibility of the calling method.
+     * @param str The String that is inserted into an existing CDATA Section 
within buf.
+     * */
+    static public void appendEscapingCDATA(final StringBuffer buf,
+                                           final String str) {
+        if (str != null) {
+            int end = str.indexOf(CDATA_END);
+            if (end < 0) {
+                buf.append(str);
+            } else {
+                int start = 0;
+                while (end > -1) {
+                    buf.append(str.substring(start, end));
+                    buf.append(CDATA_EMBEDED_END);
+                    start = end + CDATA_END_LEN;
+                    if (start < str.length()) {
+                        end = str.indexOf(CDATA_END, start);
+                    } else {
+                        return;
+                    }
+                }
+                buf.append(str.substring(start));
+            }
+        }
+    }
+
+    /**
+     * convert a Throwable into an array of Strings
+     * @param throwable
+     * @return string representation of the throwable
+     */
+    public static String[] getThrowableStrRep(Throwable throwable) {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        throwable.printStackTrace(pw);
+        pw.flush();
+        LineNumberReader reader = new LineNumberReader(new 
StringReader(sw.toString()));
+        ArrayList<String> lines = new ArrayList<String>();
+        try {
+            String line = reader.readLine();
+            while(line != null) {
+                lines.add(line);
+                line = reader.readLine();
+            }
+        } catch(IOException ex) {
+            lines.add(ex.toString());
+        }
+        String[] rep = new String[lines.size()];
+        lines.toArray(rep);
+        return rep;
+    }
+
+}

Propchange: mina/trunk/core/src/main/java/org/apache/mina/util/Transform.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/trunk/core/src/main/java/org/apache/mina/util/Transform.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date


Reply via email to