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 = "]]>";
+ 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,
+ * <b>, <table>, 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("<");
+ } else if(ch == '>') {
+ buf.append(">");
+ } else if(ch == '&') {
+ buf.append("&");
+ } else if(ch == '"') {
+ buf.append(""");
+ } 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