You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by gg...@apache.org on 2013/07/17 18:08:03 UTC
svn commit: r1504184 - in /logging/log4j/log4j2/trunk:
core/src/main/java/org/apache/logging/log4j/core/layout/
core/src/test/java/org/apache/logging/log4j/core/appender/
core/src/test/java/org/apache/logging/log4j/core/layout/
core/src/test/resources/...
Author: ggregory
Date: Wed Jul 17 16:08:03 2013
New Revision: 1504184
URL: http://svn.apache.org/r1504184
Log:
[LOG4J2-312] XML layout improvements (compact vs. pretty, namespace, namespace prefix, root element).
Added:
logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/XmlCompactFileAppenderTest.java (with props)
logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/XmlCompleteFileAppenderTest.java (with props)
logging/log4j/log4j2/trunk/core/src/test/resources/XmlCompactFileAppenderTest.xml (with props)
logging/log4j/log4j2/trunk/core/src/test/resources/XmlCompleteFileAppenderTest.xml (with props)
Modified:
logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/layout/XMLLayout.java
logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/FastXmlFileAppenderTest.java
logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/XmlFileAppenderTest.java
logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/layout/XMLLayoutTest.java
logging/log4j/log4j2/trunk/core/src/test/resources/XmlFileAppenderTest.xml
logging/log4j/log4j2/trunk/core/src/test/resources/log4j.dtd
logging/log4j/log4j2/trunk/src/changes/changes.xml
logging/log4j/log4j2/trunk/src/site/xdoc/manual/layouts.xml.vm
Modified: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/layout/XMLLayout.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/layout/XMLLayout.java?rev=1504184&r1=1504183&r2=1504184&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/layout/XMLLayout.java (original)
+++ logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/layout/XMLLayout.java Wed Jul 17 16:08:03 2013
@@ -33,62 +33,90 @@ import org.apache.logging.log4j.core.con
import org.apache.logging.log4j.core.config.plugins.PluginAttr;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.helpers.Charsets;
+import org.apache.logging.log4j.core.helpers.Strings;
import org.apache.logging.log4j.core.helpers.Transform;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.MultiformatMessage;
/**
- * The output of the XMLLayout consists of a series of log4j:event
- * elements as defined in the <a href="log4j.dtd">log4j.dtd</a>. If configured to do so it will
- * 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/>
- * <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:
- * <p/>
+ * Appends a series of {@code event} elements as defined in the <a href="log4j.dtd">log4j.dtd</a>.
+ *
+ * <h4>Complete well-formed XML vs. fragment XML</h4>
+ * <p>
+ * If you configure {@code complete="true"}, the appender outputs a well-formed XML document where the default namespace
+ * is the log4j namespace {@value #XML_NAMESPACE}. By default, with {@code complete="false"}, you should include the
+ * output as an <em>external entity</em> in a separate file to form a well-formed XML document, in which case the
+ * appender uses {@code namespacePrefix} with a default of {@value #DEFAULT_NS_PREFIX}.
+ * </p>
+ * <p>
+ * A well-formed XML document follows this pattern:
+ * </p>
+ *
* <pre>
* <?xml version="1.0" encoding="UTF-8"?>
- *
- * <!DOCTYPE log4j:eventSet SYSTEM "log4j.dtd" [<!ENTITY data SYSTEM "abc">]>
- *
- * <log4j:eventSet version="1.2" xmlns:log4j="http://logging.apache.org/log4j/">
- * &data;
- * </log4j:eventSet>
+ * <events xmlns="http://logging.apache.org/log4j/2.0">
+ * <event logger="com.foo.Bar" timestamp="1373436580419" level="INFO" thread="main">
+ * <message><![CDATA[This is a log message 1]]></message>
+ * </event>
+ * <event logger="com.foo.Baz" timestamp="1373436580420" level="INFO" thread="main">
+ * <message><![CDATA[This is a log message 2]]></message>
+ * </event>
+ * </events>
* </pre>
- * <p/>
- * <p>This approach enforces the independence of the XMLLayout and the
- * appender where it is embedded.
- * <p/>
- * <p>The <code>version</code> attribute helps components to correctly
- * interpret 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), "1.2" for release 1.2, and "2.0" for release 2.0 and
- * later.
- * <p/>
- * Appenders using this layout should have their encoding
- * set to UTF-8 or UTF-16, otherwise events containing
- * non ASCII characters could result in corrupted
- * log files.
+ * <p>
+ * If {@code complete="false"}, the appender does not write the XML processing instruction and the root element.
+ * </p>
+ * <p>
+ * This approach enforces the independence of the XMLLayout and the appender where you embed it.
+ * </p>
+ * <h4>Encoding</h4>
+ * <p>
+ * Appenders using this layout should have their {@code charset} set to {@code UTF-8} or {@code UTF-16}, otherwise
+ * events containing non ASCII characters could result in corrupted log files.
+ * </p>
+ * <h4>Pretty vs. compact XML</h4>
+ * <p>
+ * By default, the XML layout is not compact (a.k.a. not "pretty") with {@code compact="false"}, which means the
+ * appender uses end-of-line characters and indents lines to format the XML. If {@code compact="true"}, then no
+ * end-of-line or indentation is used. Message content may contain, of course, end-of-lines.
+ * </p>
*/
@Plugin(name = "XMLLayout", category = "Core", elementType = "layout", printObject = true)
public class XMLLayout extends AbstractStringLayout {
+ private static final String XML_NAMESPACE = "http://logging.apache.org/log4j/2.0/";
private static final int DEFAULT_SIZE = 256;
+
+ // We yield to \r\n for the default.
+ private static final String DEFAULT_EOL = "\r\n";
+ private static final String COMPACT_EOL = "";
+ private static final String DEFAULT_INDENT = " ";
+ private static final String COMPACT_INDENT = "";
+ private static final String DEFAULT_NS_PREFIX = "log4j";
private static final String[] FORMATS = new String[] {"xml"};
private final boolean locationInfo;
private final boolean properties;
private final boolean complete;
+ private final String namespacePrefix;
+ private final String eol;
+ private final String indent1;
+ private final String indent2;
+ private final String indent3;
protected XMLLayout(final boolean locationInfo, final boolean properties, final boolean complete,
- final Charset charset) {
+ boolean compact, final String nsPrefix, final Charset charset) {
super(charset);
this.locationInfo = locationInfo;
this.properties = properties;
this.complete = complete;
+ this.eol = compact ? COMPACT_EOL : DEFAULT_EOL;
+ this.indent1 = compact ? COMPACT_INDENT : DEFAULT_INDENT;
+ this.indent2 = this.indent1 + this.indent1;
+ this.indent3 = this.indent2 + this.indent1;
+ this.namespacePrefix = (Strings.isEmpty(nsPrefix) ? DEFAULT_NS_PREFIX : nsPrefix) + ":";
}
/**
@@ -101,9 +129,12 @@ public class XMLLayout extends AbstractS
public String toSerializable(final LogEvent event) {
final StringBuilder buf = new StringBuilder(DEFAULT_SIZE);
- // We yield to the \r\n heresy.
-
- buf.append("<log4j:event logger=\"");
+ buf.append(this.indent1);
+ buf.append('<');
+ if (!complete) {
+ buf.append(this.namespacePrefix);
+ }
+ buf.append("event logger=\"");
String name = event.getLoggerName();
if (name.isEmpty()) {
name = "root";
@@ -115,7 +146,8 @@ public class XMLLayout extends AbstractS
buf.append(Transform.escapeTags(String.valueOf(event.getLevel())));
buf.append("\" thread=\"");
buf.append(Transform.escapeTags(event.getThreadName()));
- buf.append("\">\r\n");
+ buf.append("\">");
+ buf.append(this.eol);
final Message msg = event.getMessage();
if (msg != null) {
@@ -129,39 +161,74 @@ public class XMLLayout extends AbstractS
}
}
}
+ buf.append(this.indent2);
+ buf.append('<');
+ if (!complete) {
+ buf.append(this.namespacePrefix);
+ }
+ buf.append("message>");
if (xmlSupported) {
- buf.append("<log4j:message>");
buf.append(((MultiformatMessage) msg).getFormattedMessage(FORMATS));
- buf.append("</log4j:message>");
} else {
- buf.append("<log4j:message><![CDATA[");
+ buf.append("<![CDATA[");
// Append the rendered message. Also make sure to escape any
// existing CDATA sections.
Transform.appendEscapingCDATA(buf, event.getMessage().getFormattedMessage());
- buf.append("]]></log4j:message>\r\n");
+ buf.append("]]>");
+ }
+ buf.append("</");
+ if (!complete) {
+ buf.append(this.namespacePrefix);
}
+ buf.append("message>");
+ buf.append(this.eol);
}
if (event.getContextStack().getDepth() > 0) {
- buf.append("<log4j:NDC><![CDATA[");
+ buf.append(this.indent2);
+ buf.append('<');
+ if (!complete) {
+ buf.append(this.namespacePrefix);
+ }
+ buf.append("NDC><![CDATA[");
Transform.appendEscapingCDATA(buf, event.getContextStack().toString());
- buf.append("]]></log4j:NDC>\r\n");
+ buf.append("]]></");
+ if (!complete) {
+ buf.append(this.namespacePrefix);
+ }
+ buf.append("NDC>");
+ buf.append(this.eol);
}
final Throwable throwable = event.getThrown();
if (throwable != null) {
final List<String> s = getThrowableString(throwable);
- buf.append("<log4j:throwable><![CDATA[");
+ buf.append(this.indent2);
+ buf.append('<');
+ if (!complete) {
+ buf.append(this.namespacePrefix);
+ }
+ buf.append("throwable><![CDATA[");
for (final String str : s) {
Transform.appendEscapingCDATA(buf, str);
- buf.append("\r\n");
+ buf.append(this.eol);
+ }
+ buf.append("]]></");
+ if (!complete) {
+ buf.append(this.namespacePrefix);
}
- buf.append("]]></log4j:throwable>\r\n");
+ buf.append("throwable>");
+ buf.append(this.eol);
}
if (locationInfo) {
final StackTraceElement element = event.getSource();
- buf.append("<log4j:locationInfo class=\"");
+ buf.append(this.indent2);
+ buf.append('<');
+ if (!complete) {
+ buf.append(this.namespacePrefix);
+ }
+ buf.append("locationInfo class=\"");
buf.append(Transform.escapeTags(element.getClassName()));
buf.append("\" method=\"");
buf.append(Transform.escapeTags(element.getMethodName()));
@@ -169,28 +236,58 @@ public class XMLLayout extends AbstractS
buf.append(Transform.escapeTags(element.getFileName()));
buf.append("\" line=\"");
buf.append(element.getLineNumber());
- buf.append("\"/>\r\n");
+ buf.append("\"/>");
+ buf.append(this.eol);
}
if (properties && event.getContextMap().size() > 0) {
- buf.append("<log4j:properties>\r\n");
+ buf.append(this.indent2);
+ buf.append('<');
+ if (!complete) {
+ buf.append(this.namespacePrefix);
+ }
+ buf.append("properties>");
+ buf.append(this.eol);
for (final Map.Entry<String, String> entry : event.getContextMap().entrySet()) {
- buf.append("<log4j:data name=\"");
+ buf.append(this.indent3);
+ buf.append('<');
+ if (!complete) {
+ buf.append(this.namespacePrefix);
+ }
+ buf.append("data name=\"");
buf.append(Transform.escapeTags(entry.getKey()));
buf.append("\" value=\"");
buf.append(Transform.escapeTags(String.valueOf(entry.getValue())));
- buf.append("\"/>\r\n");
+ buf.append("\"/>");
+ buf.append(this.eol);
+ }
+ buf.append(this.indent2);
+ buf.append("</");
+ if (!complete) {
+ buf.append(this.namespacePrefix);
}
- buf.append("</log4j:properties>\r\n");
+ buf.append("properties>");
+ buf.append(this.eol);
}
- buf.append("</log4j:event>\r\n\r\n");
+ buf.append(this.indent1);
+ buf.append("</");
+ if (!complete) {
+ buf.append(this.namespacePrefix);
+ }
+ buf.append("event>");
+ buf.append(this.eol);
return buf.toString();
}
/**
* Returns appropriate XML headers.
+ * <ol>
+ * <li>XML processing instruction</li>
+ * <li>XML root element</li>
+ * </ol>
+ *
* @return a byte array containing the header.
*/
@Override
@@ -198,38 +295,42 @@ public class XMLLayout extends AbstractS
if (!complete) {
return null;
}
- final StringBuilder sbuf = new StringBuilder();
- sbuf.append("<?xml version=\"1.0\" encoding=\"");
- sbuf.append(this.getCharset().name());
- sbuf.append("\"?>\r\n");
- sbuf.append("<log4j:eventSet xmlns:log4j=\"http://logging.apache.org/log4j/\">\r\n");
- return sbuf.toString().getBytes(this.getCharset());
+ final StringBuilder buf = new StringBuilder();
+ buf.append("<?xml version=\"1.0\" encoding=\"");
+ buf.append(this.getCharset().name());
+ buf.append("\"?>");
+ buf.append(this.eol);
+ // Make the log4j namespace the default namespace, no need to use more space with a namespace prefix.
+ buf.append("<events xmlns=\"" + XML_NAMESPACE + "\">");
+ buf.append(this.eol);
+ return buf.toString().getBytes(this.getCharset());
}
/**
* Returns appropriate XML footer.
- * @return a byte array containing the footer.
+ *
+ * @return a byte array containing the footer, closing the XML root element.
*/
@Override
public byte[] getFooter() {
if (!complete) {
return null;
}
- return "</log4j:eventSet>\r\n".getBytes(getCharset());
+ return ("</events>" + this.eol).getBytes(getCharset());
}
/**
* XMLLayout's content format is specified by:<p/>
* Key: "dtd" Value: "log4j.dtd"<p/>
- * Key: "version" Value: "1.2"
+ * Key: "version" Value: "2.0"
* @return Map of content format keys supporting XMLLayout
*/
@Override
public Map<String, String> getContentFormat() {
final Map<String, String> result = new HashMap<String, String>();
result.put("dtd", "log4j.dtd");
- result.put("version", "1.2");
+ result.put("version", "2.0");
return result;
}
@@ -270,21 +371,27 @@ public class XMLLayout extends AbstractS
/**
* Creates an XML Layout.
*
- * @param locationInfo If "true" include the location information in the generated XML.
- * @param properties If "true" include the thread context in the generated XML.
- * @param complete If "true" include the XML header.
- * @param charsetName The character set to use, if {@code null}, uses UTF-8.
+ * @param locationInfo If "true", includes the location information in the generated XML.
+ * @param properties If "true", includes the thread context in the generated XML.
+ * @param completeStr If "true", includes the XML header and footer, defaults to "false".
+ * @param compactStr If "true", does not use end-of-lines and indentation, defaults to "false".
+ * @param namespacePrefix The namespace prefix, defaults to {@value #DEFAULT_NS_PREFIX}
+ * @param charsetName The character set to use, if {@code null}, uses "UTF-8".
* @return An XML Layout.
*/
@PluginFactory
- public static XMLLayout createLayout(@PluginAttr("locationInfo") final String locationInfo,
+ public static XMLLayout createLayout(
+ @PluginAttr("locationInfo") final String locationInfo,
@PluginAttr("properties") final String properties,
- @PluginAttr("complete") final String complete,
+ @PluginAttr("complete") final String completeStr,
+ @PluginAttr("compact") final String compactStr,
+ @PluginAttr("namespacePrefix") final String nanespacePrefix,
@PluginAttr("charset") final String charsetName) {
final Charset charset = Charsets.getSupportedCharset(charsetName, Charsets.UTF_8);
final boolean info = Boolean.parseBoolean(locationInfo);
final boolean props = Boolean.parseBoolean(properties);
- final boolean comp = Boolean.parseBoolean(complete);
- return new XMLLayout(info, props, comp, charset);
+ final boolean complete = Boolean.parseBoolean(completeStr);
+ final boolean compact = Boolean.parseBoolean(compactStr);
+ return new XMLLayout(info, props, complete, compact, nanespacePrefix, charset);
}
}
Modified: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/FastXmlFileAppenderTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/FastXmlFileAppenderTest.java?rev=1504184&r1=1504183&r2=1504184&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/FastXmlFileAppenderTest.java (original)
+++ logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/FastXmlFileAppenderTest.java Wed Jul 17 16:08:03 2013
@@ -71,7 +71,7 @@ public class FastXmlFileAppenderTest {
assertTrue("line1 incorrect: [" + line1 + "], does not contain: [" + msg1 + "]", line1.equals(msg1));
assertNotNull("line2", line2);
- final String msg2 = "<log4j:eventSet xmlns:log4j=\"http://logging.apache.org/log4j/\">";
+ final String msg2 = "<log4j:events xmlns:log4j=\"http://logging.apache.org/log4j/\">";
assertTrue("line2 incorrect: [" + line2 + "], does not contain: [" + msg2 + "]", line2.equals(msg2));
assertNotNull("line3", line3);
Added: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/XmlCompactFileAppenderTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/XmlCompactFileAppenderTest.java?rev=1504184&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/XmlCompactFileAppenderTest.java (added)
+++ logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/XmlCompactFileAppenderTest.java Wed Jul 17 16:08:03 2013
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.appender;
+
+import static org.junit.Assert.*;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LifeCycle;
+import org.apache.logging.log4j.core.config.XMLConfigurationFactory;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Tests a "compact" XML file, no extra spaces or end of lines.
+ */
+public class XmlCompactFileAppenderTest {
+
+ @BeforeClass
+ public static void beforeClass() {
+ System.setProperty(XMLConfigurationFactory.CONFIGURATION_FILE_PROPERTY,
+ "XmlCompactFileAppenderTest.xml");
+ }
+
+ @Test
+ public void testFlushAtEndOfBatch() throws Exception {
+ final File f = new File("target", "XmlCompactFileAppenderTest.log");
+ // System.out.println(f.getAbsolutePath());
+ f.delete();
+ final Logger log = LogManager.getLogger("com.foo.Bar");
+ final String logMsg = "Message flushed with immediate flush=false";
+ log.info(logMsg);
+ ((LifeCycle) LogManager.getContext()).stop(); // stop async thread
+
+ final BufferedReader reader = new BufferedReader(new FileReader(f));
+ String line1;
+ try {
+ line1 = reader.readLine();
+ } finally {
+ reader.close();
+ f.delete();
+ }
+ assertNotNull("line1", line1);
+ final String msg1 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+ assertTrue("line1 incorrect: [" + line1 + "], does not contain: [" + msg1 + "]", line1.contains(msg1));
+
+ final String msg2 = "<events xmlns=\"http://logging.apache.org/log4j/2.0/\">";
+ assertTrue("line1 incorrect: [" + line1 + "], does not contain: [" + msg2 + "]", line1.contains(msg2));
+
+ final String msg3 = "<event ";
+ assertTrue("line1 incorrect: [" + line1 + "], does not contain: [" + msg3 + "]", line1.contains(msg3));
+
+ final String msg4 = logMsg;
+ assertTrue("line1 incorrect: [" + line1 + "], does not contain: [" + msg4 + "]", line1.contains(msg4));
+
+ final String location = "testFlushAtEndOfBatch";
+ assertTrue("no location", !line1.contains(location));
+
+ assertTrue(line1.indexOf('\r') == -1);
+ assertTrue(line1.indexOf('\n') == -1);
+ }
+}
Propchange: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/XmlCompactFileAppenderTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/XmlCompactFileAppenderTest.java
------------------------------------------------------------------------------
svn:keywords = Id
Added: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/XmlCompleteFileAppenderTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/XmlCompleteFileAppenderTest.java?rev=1504184&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/XmlCompleteFileAppenderTest.java (added)
+++ logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/XmlCompleteFileAppenderTest.java Wed Jul 17 16:08:03 2013
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.appender;
+
+import static org.junit.Assert.*;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LifeCycle;
+import org.apache.logging.log4j.core.config.XMLConfigurationFactory;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Tests a "complete" XML file a.k.a. a well-formed XML file.
+ */
+public class XmlCompleteFileAppenderTest {
+
+ @BeforeClass
+ public static void beforeClass() {
+ System.setProperty(XMLConfigurationFactory.CONFIGURATION_FILE_PROPERTY,
+ "XmlCompleteFileAppenderTest.xml");
+ }
+
+ @Test
+ public void testFlushAtEndOfBatch() throws Exception {
+ final File f = new File("target", "XmlCompleteFileAppenderTest.log");
+ // System.out.println(f.getAbsolutePath());
+ f.delete();
+ final Logger log = LogManager.getLogger("com.foo.Bar");
+ final String logMsg = "Message flushed with immediate flush=false";
+ log.info(logMsg);
+ ((LifeCycle) LogManager.getContext()).stop(); // stop async thread
+
+ final BufferedReader reader = new BufferedReader(new FileReader(f));
+ String line1;
+ String line2;
+ String line3;
+ String line4;
+ try {
+ line1 = reader.readLine();
+ line2 = reader.readLine();
+ line3 = reader.readLine();
+ line4 = reader.readLine();
+ } finally {
+ reader.close();
+ f.delete();
+ }
+ assertNotNull("line1", line1);
+ final String msg1 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+ assertTrue("line1 incorrect: [" + line1 + "], does not contain: [" + msg1 + "]", line1.equals(msg1));
+
+ assertNotNull("line2", line2);
+ final String msg2 = "<events xmlns=\"http://logging.apache.org/log4j/2.0/\">";
+ assertTrue("line2 incorrect: [" + line2 + "], does not contain: [" + msg2 + "]", line2.equals(msg2));
+
+ assertNotNull("line3", line3);
+ final String msg3 = "<event ";
+ assertTrue("line3 incorrect: [" + line3 + "], does not contain: [" + msg3 + "]", line3.contains(msg3));
+
+ assertNotNull("line4", line4);
+ final String msg4 = logMsg;
+ assertTrue("line4 incorrect: [" + line4 + "], does not contain: [" + msg4 + "]", line4.contains(msg4));
+
+ final String location = "testFlushAtEndOfBatch";
+ assertTrue("no location", !line1.contains(location));
+ }
+}
Propchange: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/XmlCompleteFileAppenderTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/XmlCompleteFileAppenderTest.java
------------------------------------------------------------------------------
svn:keywords = Id
Modified: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/XmlFileAppenderTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/XmlFileAppenderTest.java?rev=1504184&r1=1504183&r2=1504184&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/XmlFileAppenderTest.java (original)
+++ logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/XmlFileAppenderTest.java Wed Jul 17 16:08:03 2013
@@ -54,32 +54,28 @@ public class XmlFileAppenderTest {
String line1;
String line2;
String line3;
- String line4;
try {
line1 = reader.readLine();
line2 = reader.readLine();
line3 = reader.readLine();
- line4 = reader.readLine();
} finally {
reader.close();
f.delete();
}
assertNotNull("line1", line1);
- final String msg1 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
- assertTrue("line1 incorrect: [" + line1 + "], does not contain: [" + msg1 + "]", line1.equals(msg1));
+ assertNotNull("line1", line1);
+ final String msg1 = "<log4j:event ";
+ assertTrue("line1 incorrect: [" + line1 + "], does not contain: [" + msg1 + "]", line1.contains(msg1));
+
assertNotNull("line2", line2);
- final String msg2 = "<log4j:eventSet xmlns:log4j=\"http://logging.apache.org/log4j/\">";
- assertTrue("line2 incorrect: [" + line2 + "], does not contain: [" + msg2 + "]", line2.equals(msg2));
-
+ final String msg2 = logMsg;
+ assertTrue("line2 incorrect: [" + line2 + "], does not contain: [" + msg2 + "]", line2.contains(msg2));
+
assertNotNull("line3", line3);
- final String msg3 = "<log4j:event ";
+ final String msg3 = "</log4j:event>";
assertTrue("line3 incorrect: [" + line3 + "], does not contain: [" + msg3 + "]", line3.contains(msg3));
- assertNotNull("line4", line4);
- final String msg4 = logMsg;
- assertTrue("line4 incorrect: [" + line4 + "], does not contain: [" + msg4 + "]", line4.contains(msg4));
-
final String location = "testFlushAtEndOfBatch";
assertTrue("no location", !line1.contains(location));
}
Modified: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/layout/XMLLayoutTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/layout/XMLLayoutTest.java?rev=1504184&r1=1504183&r2=1504184&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/layout/XMLLayoutTest.java (original)
+++ logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/layout/XMLLayoutTest.java Wed Jul 17 16:08:03 2013
@@ -39,7 +39,7 @@ import static org.junit.Assert.assertTru
*/
public class XMLLayoutTest {
private static final String body =
- "<log4j:message><![CDATA[empty mdc]]></log4j:message>";
+ "<message><![CDATA[empty mdc]]></message>";
static ConfigurationFactory cf = new BasicConfigurationFactory();
@AfterClass
@@ -60,13 +60,13 @@ public class XMLLayoutTest {
@Test
public void testContentType() {
- final XMLLayout layout = XMLLayout.createLayout(null, null, null, null);
+ final XMLLayout layout = XMLLayout.createLayout(null, null, null, null, null, null);
assertEquals("text/xml; charset=UTF-8", layout.getContentType());
}
@Test
public void testDefaultCharset() {
- final XMLLayout layout = XMLLayout.createLayout(null, null, null, null);
+ final XMLLayout layout = XMLLayout.createLayout(null, null, null, null, null, null);
assertEquals(Charsets.UTF_8, layout.getCharset());
}
@@ -77,7 +77,7 @@ public class XMLLayoutTest {
public void testLayout() throws Exception {
// set up appender
- final XMLLayout layout = XMLLayout.createLayout("true", "true", "true", null);
+ final XMLLayout layout = XMLLayout.createLayout("true", "true", "true", null, null, null);
final ListAppender<String> appender = new ListAppender<String>("List", null, layout, true, false);
appender.start();
@@ -107,7 +107,7 @@ public class XMLLayoutTest {
assertTrue("Incorrect number of lines. Require at least 50 " + list.size(), list.size() > 50);
final String string = list.get(0);
assertTrue("Incorrect header: " + string, string.equals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"));
- assertTrue("Incorrect footer", list.get(list.size() - 1).equals("</log4j:eventSet>"));
- assertTrue("Incorrect body. Expected " + body + " Actual: " + list.get(8), list.get(8).equals(body));
+ assertTrue("Incorrect footer", list.get(list.size() - 1).equals("</events>"));
+ assertTrue("Incorrect body. Expected " + body + " Actual: " + list.get(7), list.get(7).trim().equals(body));
}
}
Added: logging/log4j/log4j2/trunk/core/src/test/resources/XmlCompactFileAppenderTest.xml
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/resources/XmlCompactFileAppenderTest.xml?rev=1504184&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/resources/XmlCompactFileAppenderTest.xml (added)
+++ logging/log4j/log4j2/trunk/core/src/test/resources/XmlCompactFileAppenderTest.xml Wed Jul 17 16:08:03 2013
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration status="OFF">
+ <appenders>
+ <File name="XmlFile" fileName="target/XmlCompactFileAppenderTest.log" immediateFlush="false" append="false">
+ <XMLLayout complete="true" compact="true"/>
+ </File>
+ </appenders>
+
+ <loggers>
+ <asyncRoot level="info" includeLocation="false">
+ <appender-ref ref="XmlFile"/>
+ </asyncRoot>
+ </loggers>
+</configuration>
Propchange: logging/log4j/log4j2/trunk/core/src/test/resources/XmlCompactFileAppenderTest.xml
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: logging/log4j/log4j2/trunk/core/src/test/resources/XmlCompactFileAppenderTest.xml
------------------------------------------------------------------------------
svn:keywords = Id
Added: logging/log4j/log4j2/trunk/core/src/test/resources/XmlCompleteFileAppenderTest.xml
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/resources/XmlCompleteFileAppenderTest.xml?rev=1504184&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/resources/XmlCompleteFileAppenderTest.xml (added)
+++ logging/log4j/log4j2/trunk/core/src/test/resources/XmlCompleteFileAppenderTest.xml Wed Jul 17 16:08:03 2013
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration status="OFF">
+ <appenders>
+ <File name="XmlFile" fileName="target/XmlCompleteFileAppenderTest.log" immediateFlush="false" append="false">
+ <XMLLayout complete="true"/>
+ </File>
+ </appenders>
+
+ <loggers>
+ <asyncRoot level="info" includeLocation="false">
+ <appender-ref ref="XmlFile"/>
+ </asyncRoot>
+ </loggers>
+</configuration>
Propchange: logging/log4j/log4j2/trunk/core/src/test/resources/XmlCompleteFileAppenderTest.xml
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: logging/log4j/log4j2/trunk/core/src/test/resources/XmlCompleteFileAppenderTest.xml
------------------------------------------------------------------------------
svn:keywords = Id
Modified: logging/log4j/log4j2/trunk/core/src/test/resources/XmlFileAppenderTest.xml
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/resources/XmlFileAppenderTest.xml?rev=1504184&r1=1504183&r2=1504184&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/resources/XmlFileAppenderTest.xml (original)
+++ logging/log4j/log4j2/trunk/core/src/test/resources/XmlFileAppenderTest.xml Wed Jul 17 16:08:03 2013
@@ -2,7 +2,7 @@
<configuration status="OFF">
<appenders>
<File name="XmlFile" fileName="target/XmlFileAppenderTest.log" immediateFlush="false" append="false">
- <XMLLayout complete="true"/>
+ <XMLLayout/>
</File>
</appenders>
Modified: logging/log4j/log4j2/trunk/core/src/test/resources/log4j.dtd
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/resources/log4j.dtd?rev=1504184&r1=1504183&r2=1504184&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/resources/log4j.dtd (original)
+++ logging/log4j/log4j2/trunk/core/src/test/resources/log4j.dtd Wed Jul 17 16:08:03 2013
@@ -136,8 +136,8 @@ element. -->
<!-- ==================================================================== -->
<!-- A logging event -->
<!-- ==================================================================== -->
-<!ELEMENT log4j:eventSet (log4j:event*)>
-<!ATTLIST log4j:eventSet
+<!ELEMENT log4j:events (log4j:event*)>
+<!ATTLIST log4j:events
xmlns:log4j CDATA #FIXED "http://jakarta.apache.org/log4j/"
version (1.1|1.2) "1.2"
includesLocationInfo (true|false) "true"
Modified: logging/log4j/log4j2/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/src/changes/changes.xml?rev=1504184&r1=1504183&r2=1504184&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/src/changes/changes.xml (original)
+++ logging/log4j/log4j2/trunk/src/changes/changes.xml Wed Jul 17 16:08:03 2013
@@ -21,6 +21,9 @@
</properties>
<body>
<release version="2.0-beta9" date="soon, very soon" description="Bug fixes and enhancements">
+ <action issue="LOG4J2-312" dev="ggregory" type="update">
+ XML layout improvements (compact vs. pretty, namespace, namespace prefix, root element).
+ </action>
<action issue="LOG4J2-308" dev="rpopma" type="update">
Clarified which library versions were used in Async Loggers performance test.
</action>
Modified: logging/log4j/log4j2/trunk/src/site/xdoc/manual/layouts.xml.vm
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/src/site/xdoc/manual/layouts.xml.vm?rev=1504184&r1=1504183&r2=1504184&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/src/site/xdoc/manual/layouts.xml.vm (original)
+++ logging/log4j/log4j2/trunk/src/site/xdoc/manual/layouts.xml.vm Wed Jul 17 16:08:03 2013
@@ -1148,29 +1148,48 @@ at org.apache.logging.log4j.core.pattern
</subsection>
<a name="XMLLayout"/>
<subsection name="XMLLayout">
- <p>The output of the XMLLayout consists of a series of log4j:event
- elements as defined in the <a href="log4j.dtd">log4j.dtd</a>. If configured to do so it will
- 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>
- <p>For example, if <code>abc</code> is the name of the file where
- the XMLLayout output goes, then a well-formed XML file would be:
- </p>
- <pre class="prettyprint linenums"><![CDATA[<?xml version="1.0" encoding="UTF-8">
-<!DOCTYPE log4j:eventSet SYSTEM "log4j.dtd" [<!ENTITY data SYSTEM "abc">]>
-<log4j:eventSet version="2.0" xmlns:log4j="http://logging.apache.org/log4j/">
-
-</log4j:eventSet>]]></pre>
- <p>This approach enforces the independence of the XMLLayout and the appender where it is embedded.
+ <!-- From Javadoc of org.apache.logging.log4j.core.layout.XMLLayout -->
+ <p>
+ Appends a series of <code>event</code> elements as defined in the <a href="log4j.dtd">log4j.dtd</a>.
</p>
- <p>The <code>version</code> attribute helps components to correctly intrepret output generated by XMLLayout.
- The value of this attribute should be "2.0".
+ <h4>Complete well-formed XML vs. fragment XML</h4>
+ <p>
+ If you configure <code>complete="true"</code>, the appender outputs a well-formed XML document where the
+ default namespace is the log4j namespace <code>"http://logging.apache.org/log4j/2.0/"</code>. By default,
+ with <code>complete="false"</code>, you should include the output as an <em>external entity</em> in a
+ separate file to form a well-formed XML document, in which case the appender uses
+ <code>namespacePrefix</code> with a default of <code>"log4j"</code>.
</p>
- <p>Appenders using this layout should have their encoding set to UTF-8 or UTF-16, otherwise events containing
- non ASCII characters could result in corrupted log files.
+ <p>
+ A well-formed XML document follows this pattern:
+ </p>
+ <pre class="prettyprint linenums"><?xml version="1.0" encoding="UTF-8"?>
+<events xmlns="http://logging.apache.org/log4j/2.0">
+ <event logger="com.foo.Bar" timestamp="1373436580419" level="INFO" thread="main">
+ <message><![CDATA[This is a log message 1]]></message>
+ </event>
+ <event logger="com.foo.Baz" timestamp="1373436580420" level="INFO" thread="main">
+ <message><![CDATA[This is a log message 2]]></message>
+ </event>
+</events></pre>
+ <p>
+ If <code>complete="false"</code>, the appender does not write the XML processing instruction and the root
+ element.
+ </p>
+ <p>
+ This approach enforces the independence of the XMLLayout and the appender where you embed it.
+ </p>
+ <h4>Encoding</h4>
+ <p>
+ Appenders using this layout should have their <code>charset</code> set to <code>UTF-8</code> or
+ <code>UTF-16</code>, otherwise events containing non ASCII characters could result in corrupted log files.
+ </p>
+ <h4>Pretty vs. compact XML</h4>
+ <p>
+ By default, the XML layout is not compact (a.k.a. not "pretty") with <code>compact="false"</code>, which
+ means the appender uses end-of-line characters and indents lines to format the XML. If
+ <code>compact="true"</code>, then no end-of-line or indentation is used. Message content may contain,
+ of course, end-of-lines.
</p>
</subsection>
<a name="LocationInformation"/>