ceki 2002/06/11 06:20:05 Modified: src/java/org/apache/log4j/xml Tag: v1_2-branch XMLLayout.java src/java/org/apache/log4j/helpers Tag: v1_2-branch Transform.java tests/src/java/org/apache/log4j/xml Tag: v1_2-branch XMLLayoutTestCase.java Log: XMLLayout can now output messages which contain embedded CDATA sections. This resolves bug #9750. Many thanks Michael A. McAngus for supplying the relevant patch. Added a new test in XMLLayoutTestCase.java Revision Changes Path No revision No revision 1.18.2.2 +86 -88 jakarta-log4j/src/java/org/apache/log4j/xml/XMLLayout.java Index: XMLLayout.java =================================================================== RCS file: /home/cvs/jakarta-log4j/src/java/org/apache/log4j/xml/XMLLayout.java,v retrieving revision 1.18.2.1 retrieving revision 1.18.2.2 diff -u -r1.18.2.1 -r1.18.2.2 --- XMLLayout.java 28 May 2002 14:25:00 -0000 1.18.2.1 +++ XMLLayout.java 11 Jun 2002 13:20:05 -0000 1.18.2.2 @@ -17,37 +17,38 @@ import org.apache.log4j.helpers.Transform; /** - The output of the XMLLayout consists of a series of log4j:event - elements as defined in the <a - href="doc-files/log4j.dtd">log4j.dtd</a>. It does not output a - complete well-formed XML file. The output is designed to be - included as an <em>external entity</em> in a separate file to form - a correct XML file. - - <p>For example, if <code>abc</code> is the name of the file where - the XMLLayout ouput goes, then a well-formed XML file would be: - - <pre> + * The output of the XMLLayout consists of a series of log4j:event + * elements as defined in the <a + * href="doc-files/log4j.dtd">log4j.dtd</a>. It does not output a + * complete well-formed XML file. The output is designed to be + * included as an <em>external entity</em> in a separate file to form + * a correct XML file. + * + * <p>For example, if <code>abc</code> is the name of the file where + * the XMLLayout ouput goes, then a well-formed XML file would be: + * + <pre> <?xml version="1.0" ?> - - <!DOCTYPE log4j:eventSet SYSTEM "log4j.dtd" [<!ENTITY data SYSTEM "abc">]> - - <log4j:eventSet version="1.2" xmlns:log4j="http://jakarta.apache.org/log4j/"> - &data; - </log4j:eventSet> - </pre> - - <p>This approach enforces the independence of the XMLLayout and the - appender where it is embedded. - - <p>The <code>version</code> attribute helps components to correctly - intrepret output generated by XMLLayout. The value of this - attribute should be "1.1" for output generated by log4j versions - prior to log4j 1.2 (final release) and "1.2" for relase 1.2 and - later. - - @author Ceki Gülcü - @since 0.9.0 */ + + <!DOCTYPE log4j:eventSet SYSTEM "log4j.dtd" [<!ENTITY data SYSTEM "abc">]> + + <log4j:eventSet version="1.2" xmlns:log4j="http://jakarta.apache.org/log4j/"> + &data; + </log4j:eventSet> + </pre> + + * <p>This approach enforces the independence of the XMLLayout and the + * appender where it is embedded. + * + * <p>The <code>version</code> attribute helps components to correctly + * intrepret output generated by XMLLayout. The value of this + * attribute should be "1.1" for output generated by log4j versions + * prior to log4j 1.2 (final release) and "1.2" for relase 1.2 and + * later. + * + * @author Ceki Gülcü + * @since 0.9.0 + * */ public class XMLLayout extends Layout { private final int DEFAULT_SIZE = 256; @@ -55,41 +56,38 @@ private StringBuffer buf = new StringBuffer(DEFAULT_SIZE); private boolean locationInfo = 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. - - <p>If you are embedding this layout within an {@link - org.apache.log4j.net.SMTPAppender} then make sure to set the - <b>LocationInfo</b> option of that appender as well. - */ - public - void setLocationInfo(boolean flag) { + * 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. + * + * <p>If you are embedding this layout within an {@link + * org.apache.log4j.net.SMTPAppender} then make sure to set the + * <b>LocationInfo</b> option of that appender as well. + * */ + public void setLocationInfo(boolean flag) { locationInfo = flag; } /** Returns the current value of the <b>LocationInfo</b> option. */ - public - boolean getLocationInfo() { + public boolean getLocationInfo() { return locationInfo; } - public - void activateOptions() { + /** No options to activate. */ + public void activateOptions() { } /** - Formats a {@link LoggingEvent} in conformance with the log4j.dtd. */ - public - String format(LoggingEvent event) { + * Formats a {@link LoggingEvent} in conformance with the log4j.dtd. + * */ + public String format(LoggingEvent event) { // 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. @@ -111,43 +109,44 @@ buf.append(event.getThreadName()); buf.append("\">\r\n"); - - buf.append("<log4j:message><![CDATA["); - buf.append(event.getRenderedMessage()); - buf.append("]]></log4j:message>\r\n"); - - String ndc = event.getNDC(); - if(ndc != null) { - buf.append("<log4j:NDC><![CDATA["); - buf.append(ndc); - buf.append("]]></log4j:NDC>\r\n"); - } - - String[] s = event.getThrowableStrRep(); - if(s != null) { - buf.append("<log4j:throwable><![CDATA["); - for(int i = 0; i < s.length; i++) { - buf.append(s[i]); - buf.append("\r\n"); - } - buf.append("]]></log4j:throwable>\r\n"); - } - - if(locationInfo) { - LocationInfo locationInfo = event.getLocationInformation(); - buf.append("<log4j:locationInfo class=\""); - buf.append(locationInfo.getClassName()); - buf.append("\" method=\""); - buf.append(Transform.escapeTags(locationInfo.getMethodName())); - buf.append("\" file=\""); - buf.append(locationInfo.getFileName()); - buf.append("\" line=\""); - buf.append(locationInfo.getLineNumber()); - 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, event.getRenderedMessage()); + buf.append("]]></log4j:message>\r\n"); + + String ndc = event.getNDC(); + if(ndc != null) { + buf.append("<log4j:NDC><![CDATA["); + buf.append(ndc); + buf.append("]]></log4j:NDC>\r\n"); + } + + String[] s = event.getThrowableStrRep(); + if(s != null) { + buf.append("<log4j:throwable><![CDATA["); + for(int i = 0; i < s.length; i++) { + buf.append(s[i]); + buf.append("\r\n"); + } + buf.append("]]></log4j:throwable>\r\n"); + } + + if(locationInfo) { + LocationInfo locationInfo = event.getLocationInformation(); + buf.append("<log4j:locationInfo class=\""); + buf.append(locationInfo.getClassName()); + buf.append("\" method=\""); + buf.append(Transform.escapeTags(locationInfo.getMethodName())); + buf.append("\" file=\""); + buf.append(locationInfo.getFileName()); + buf.append("\" line=\""); + buf.append(locationInfo.getLineNumber()); + buf.append("\"/>\r\n"); + } + buf.append("</log4j:event>\r\n\r\n"); - + return buf.toString(); } @@ -155,8 +154,7 @@ The XMLLayout prints and does not ignore exceptions. Hence the return value <code>false</code>. */ - public - boolean ignoresThrowable() { + public boolean ignoresThrowable() { return false; } } No revision No revision 1.1.2.1 +47 -8 jakarta-log4j/src/java/org/apache/log4j/helpers/Transform.java Index: Transform.java =================================================================== RCS file: /home/cvs/jakarta-log4j/src/java/org/apache/log4j/helpers/Transform.java,v retrieving revision 1.1 retrieving revision 1.1.2.1 diff -u -r1.1 -r1.1.2.1 --- Transform.java 26 Apr 2002 12:31:42 -0000 1.1 +++ Transform.java 11 Jun 2002 13:20:05 -0000 1.1.2.1 @@ -12,21 +12,28 @@ /** Utility class for transforming strings. + + @author Ceki Gülcü + @author Michael A. McAngus */ 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 converts the '<' and '>' characters to their HTML escape - * sequences. + * This method takes a string which may contain HTML tags (ie, + * <b>, <table>, etc) and replaces any '<' and '>' + * characters with respective predefined entity references. * * @param input The text to be converted. * @return The input string with the characters '<' and '>' replaced with - * < and > respectively. - */ - static - public - String escapeTags(String input) { + * &lt; and &gt; respectively. + * */ + static public String escapeTags(String input) { //Check if the string is null or zero length -- if so, return //what was sent in. @@ -52,5 +59,37 @@ } } 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(StringBuffer buf, String str) { + int end = str.indexOf(CDATA_END); + + if (end < 0) { + buf.append(str); + return; + } + + 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)); } } No revision No revision 1.2.2.1 +14 -1 jakarta-log4j/tests/src/java/org/apache/log4j/xml/XMLLayoutTestCase.java Index: XMLLayoutTestCase.java =================================================================== RCS file: /home/cvs/jakarta-log4j/tests/src/java/org/apache/log4j/xml/XMLLayoutTestCase.java,v retrieving revision 1.2 retrieving revision 1.2.2.1 diff -u -r1.2 -r1.2.2.1 --- XMLLayoutTestCase.java 29 Apr 2002 14:41:23 -0000 1.2 +++ XMLLayoutTestCase.java 11 Jun 2002 13:20:05 -0000 1.2.2.1 @@ -58,6 +58,19 @@ assertTrue(Compare.compare(FILTERED, "witness/xmlLayout.2")); } + public void testCDATA() throws Exception { + XMLLayout xmlLayout = new XMLLayout(); + xmlLayout.setLocationInfo(true); + root.addAppender(new FileAppender(xmlLayout, TEMP, false)); + + logger.debug("Message with embedded <![CDATA[<hello>hi</hello>]]>."); + + Transformer.transform(TEMP, FILTERED, new Filter[] {new LineNumberFilter(), + new XMLTimestampFilter(), + new XMLLineAttributeFilter()}); + assertTrue(Compare.compare(FILTERED, "witness/xmlLayout.3")); + } + void common() { int i = -1; @@ -84,13 +97,13 @@ logger.error("Message " + ++i, e); root.error("Message " + i, e); - } public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest(new XMLLayoutTestCase("basic")); suite.addTest(new XMLLayoutTestCase("locationInfo")); + suite.addTest(new XMLLayoutTestCase("testCDATA")); return suite; }
-- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>