Repository: logging-log4j2 Updated Branches: refs/heads/LOG4J2-1528 b1e8f6f3e -> 872359805
Fix XML configuration formatting Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/87235980 Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/87235980 Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/87235980 Branch: refs/heads/LOG4J2-1528 Commit: 8723598057dd938be060961988f6d460f82dfe90 Parents: b1e8f6f Author: Mikael Ståldal <mik...@staldal.nu> Authored: Sun Aug 21 15:44:28 2016 +0200 Committer: Mikael Ståldal <mik...@staldal.nu> Committed: Sun Aug 21 15:44:28 2016 +0200 ---------------------------------------------------------------------- .../core/config/AbstractConfiguration.java | 189 +++++++++++++++++-- .../impl/DefaultConfigurationBuilder.java | 27 ++- .../builder/ConfigurationBuilderTest.java | 58 +++++- 3 files changed, 245 insertions(+), 29 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/87235980/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java index a152ea7..f123991 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java @@ -16,24 +16,6 @@ */ package org.apache.logging.log4j.core.config; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.CopyOnWriteArrayList; - import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.Filter; @@ -65,6 +47,29 @@ import org.apache.logging.log4j.core.util.NanoClock; import org.apache.logging.log4j.core.util.WatchManager; import org.apache.logging.log4j.util.PropertiesUtil; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CopyOnWriteArrayList; + /** * The base Configuration. Many configuration implementations will extend this class. */ @@ -181,6 +186,154 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement return rootNode; } + private static final List<String> SECTION_NAMES = + Arrays.asList("Properties", "Scripts", "CustomLevels", "Filters", "Appenders", "Loggers"); + + public void writeXmlConfiguration(final OutputStream output) throws IOException { + try { + XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(output); + writeXmlConfiguration(xmlWriter); + xmlWriter.close(); + } catch (XMLStreamException e) { + if (e.getNestedException() instanceof IOException) { + throw (IOException)e.getNestedException(); + } else { + throw new RuntimeException(e); + } + } + } + + public String toXmlConfiguration() { + StringWriter sw = new StringWriter(); + try { + XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(sw); + writeXmlConfiguration(xmlWriter); + xmlWriter.close(); + } catch (XMLStreamException e) { + throw new RuntimeException(e); + } + return sw.toString(); + } + + private void writeXmlConfiguration(XMLStreamWriter xmlWriter) throws XMLStreamException { + xmlWriter.writeStartDocument(); + xmlWriter.writeCharacters(System.lineSeparator()); + + xmlWriter.writeStartElement("Configuration"); + + if (name != null) { + xmlWriter.writeAttribute("name", getName()); + } + /* TODO status logger config + if (level != null) { + xmlWriter.writeAttribute("status", level.name()); + } + if (verbosity != null) { + xmlWriter.writeAttribute("verbose", verbosity); + } + if (destination != null) { + xmlWriter.writeAttribute("dest", destination); + } + */ + if (!getPluginPackages().isEmpty()) { + xmlWriter.writeAttribute("packages", getPluginPackages().toString()); // TODO comma-separated string + } + if (!isShutdownHookEnabled()) { + xmlWriter.writeAttribute("shutdownHook", "disable"); + } + if (advertiserNode != null) { + xmlWriter.writeAttribute("advertiser", advertiserNode.getName()); + } + if (getWatchManager().getIntervalSeconds() > 0) { + xmlWriter.writeAttribute("monitorInterval", String.valueOf(getWatchManager().getIntervalSeconds())); + } + + xmlWriter.writeCharacters(System.lineSeparator()); + + writeXmlSection(xmlWriter, lookupSection("Properties")); + writeXmlSection(xmlWriter, lookupSection("Scripts")); + writeXmlSection(xmlWriter, lookupSection("CustomLevels")); // TODO customLevel level vs intLevel + Node filters = lookupSection("Filters"); + if (filters != null) { + writeXmlSection(xmlWriter, filters); + } else { + Node filter = lookupFilter(); + if (filter != null) { + writeXmlNode(xmlWriter, filter, 1); + } + } + + writeXmlSection(xmlWriter, lookupSection("Appenders")); + writeXmlSection(xmlWriter, lookupSection("Loggers")); + + xmlWriter.writeEndElement(); // "Configuration" + xmlWriter.writeCharacters(System.lineSeparator()); + + xmlWriter.writeEndDocument(); + } + + private Node lookupSection(String name) { + for (final Node child : rootNode.getChildren()) { + if (child.getName().equalsIgnoreCase(name)) { + return child; + } + } + return null; + } + + private Node lookupFilter() { + for (final Node child : rootNode.getChildren()) { + if (!SECTION_NAMES.contains(child.getName())) { + return child; + } + } + return null; + } + + private void writeXmlSection(XMLStreamWriter xmlWriter, Node node) throws XMLStreamException { + if (node != null && (!node.getAttributes().isEmpty() || !node.getChildren().isEmpty() || node.getValue() != null)) { + writeXmlNode(xmlWriter, node, 1); + } + } + + private void writeXmlNode(XMLStreamWriter xmlWriter, Node node, int nesting) throws XMLStreamException { + if (!node.getChildren().isEmpty() || node.getValue() != null) { + writeXmlIndent(xmlWriter, nesting); + xmlWriter.writeStartElement(node.getName()); + writeXmlAttributes(xmlWriter, node); + if (!node.getChildren().isEmpty()) { + xmlWriter.writeCharacters(System.lineSeparator()); + } + for (Node childNode : node.getChildren()) { + writeXmlNode(xmlWriter, childNode, nesting + 1); + } + if (node.getValue() != null) { + xmlWriter.writeCharacters(node.getValue()); + } + if (!node.getChildren().isEmpty()) { + writeXmlIndent(xmlWriter, nesting); + } + xmlWriter.writeEndElement(); + } else { + writeXmlIndent(xmlWriter, nesting); + xmlWriter.writeEmptyElement(node.getName()); + writeXmlAttributes(xmlWriter, node); + } + xmlWriter.writeCharacters(System.lineSeparator()); + } + + private void writeXmlIndent(XMLStreamWriter xmlWriter, int nesting) throws XMLStreamException { + for (int i = 0; i < nesting; i++) { + xmlWriter.writeCharacters("\t"); + } + } + + private void writeXmlAttributes(XMLStreamWriter xmlWriter, Node node) throws XMLStreamException { + for (Map.Entry<String, String> attribute : node.getAttributes().entrySet()) { + xmlWriter.writeAttribute(attribute.getKey(), attribute.getValue()); + } + } + @Override public AsyncLoggerConfigDelegate getAsyncLoggerConfigDelegate() { // lazily instantiate only when requested by AsyncLoggers: http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/87235980/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java index 47688f7..e3de1ae 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java @@ -255,11 +255,16 @@ public class DefaultConfigurationBuilder<T extends BuiltConfiguration> implement xmlWriter.writeCharacters(System.lineSeparator()); - for (Component component : root.getComponents()) { - if (!component.getAttributes().isEmpty() || !component.getComponents().isEmpty() || component.getValue() != null) { - writeXmlComponent(xmlWriter, component, 1); - } + writeXmlSection(xmlWriter, properties); + writeXmlSection(xmlWriter, scripts); + writeXmlSection(xmlWriter, customLevels); // TODO customLevel level vs intLevel + if (filters.getComponents().size() == 1) { + writeXmlComponent(xmlWriter, filters.getComponents().get(0), 1); + } else if (filters.getComponents().size() > 1) { + writeXmlSection(xmlWriter, filters); } + writeXmlSection(xmlWriter, appenders); + writeXmlSection(xmlWriter, loggers); xmlWriter.writeEndElement(); // "Configuration" xmlWriter.writeCharacters(System.lineSeparator()); @@ -267,19 +272,29 @@ public class DefaultConfigurationBuilder<T extends BuiltConfiguration> implement xmlWriter.writeEndDocument(); } + private void writeXmlSection(XMLStreamWriter xmlWriter, Component component) throws XMLStreamException { + if (!component.getAttributes().isEmpty() || !component.getComponents().isEmpty() || component.getValue() != null) { + writeXmlComponent(xmlWriter, component, 1); + } + } + private void writeXmlComponent(XMLStreamWriter xmlWriter, Component component, int nesting) throws XMLStreamException { if (!component.getComponents().isEmpty() || component.getValue() != null) { writeXmlIndent(xmlWriter, nesting); xmlWriter.writeStartElement(component.getPluginType()); writeXmlAttributes(xmlWriter, component); - xmlWriter.writeCharacters(System.lineSeparator()); + if (!component.getComponents().isEmpty()) { + xmlWriter.writeCharacters(System.lineSeparator()); + } for (Component subComponent : component.getComponents()) { writeXmlComponent(xmlWriter, subComponent, nesting + 1); } if (component.getValue() != null) { xmlWriter.writeCharacters(component.getValue()); } - writeXmlIndent(xmlWriter, nesting); + if (!component.getComponents().isEmpty()) { + writeXmlIndent(xmlWriter, nesting); + } xmlWriter.writeEndElement(); } else { writeXmlIndent(xmlWriter, nesting); http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/87235980/log4j-core/src/test/java/org/apache/logging/log4j/core/config/builder/ConfigurationBuilderTest.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/builder/ConfigurationBuilderTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/builder/ConfigurationBuilderTest.java index 3d8c4d2..b4b2bff 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/builder/ConfigurationBuilderTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/builder/ConfigurationBuilderTest.java @@ -16,6 +16,8 @@ */ package org.apache.logging.log4j.core.config.builder; +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.config.AbstractConfiguration; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; @@ -25,15 +27,19 @@ import static org.junit.Assert.assertEquals; public class ConfigurationBuilderTest { - private final static String expectedXml = + private final static String expectedXml1 = "<?xml version='1.0' encoding='UTF-8'?>" + System.lineSeparator() + "<Configuration name=\"config name\" status=\"ERROR\">" + System.lineSeparator() + + "\t<Properties>" + System.lineSeparator() + + "\t\t<Property name=\"MyKey\">MyValue</Property>" + System.lineSeparator() + + "\t</Properties>" + System.lineSeparator() + "\t<Scripts>" + System.lineSeparator() + "\t\t<ScriptFile path=\"target/test-classes/scripts/filter.groovy\" name=\"target/test-classes/scripts/filter.groovy\" isWatched=\"true\"/>" + System.lineSeparator() + "\t</Scripts>" + System.lineSeparator() + - "\t<Filters>" + System.lineSeparator() + - "\t\t<ThresholdFilter onMatch=\"ACCEPT\" level=\"DEBUG\" onMisMatch=\"NEUTRAL\"/>" + System.lineSeparator() + - "\t</Filters>" + System.lineSeparator() + + "\t<CustomLevels>" + System.lineSeparator() + + "\t\t<CustomLevel intLevel=\"17\" name=\"Panic\"/>" + System.lineSeparator() + + "\t</CustomLevels>" + System.lineSeparator() + + "\t<ThresholdFilter onMatch=\"ACCEPT\" level=\"DEBUG\" onMisMatch=\"NEUTRAL\"/>" + System.lineSeparator() + "\t<Appenders>" + System.lineSeparator() + "\t\t<CONSOLE name=\"Stdout\" target=\"SYSTEM_OUT\">" + System.lineSeparator() + "\t\t\t<PatternLayout pattern=\"%d [%t] %-5level: %msg%n%throwable\"/>" + System.lineSeparator() + @@ -54,8 +60,50 @@ public class ConfigurationBuilderTest { public void testXmlConstructing() throws Exception { final ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder(); CustomConfigurationFactory.addTestFixtures("config name", builder); + builder.addProperty("MyKey", "MyValue"); + builder.add(builder.newCustomLevel("Panic", 17)); final String xmlConfiguration = builder.toXmlConfiguration(); - assertEquals(expectedXml, xmlConfiguration); + assertEquals(expectedXml1, xmlConfiguration); } + private final static String expectedXml2 = + "<?xml version='1.0' encoding='UTF-8'?>" + System.lineSeparator() + + "<Configuration name=\"config name\" status=\"ERROR\">" + System.lineSeparator() + + "\t<Properties>" + System.lineSeparator() + + "\t\t<Property name=\"MyKey\">MyValue</Property>" + System.lineSeparator() + + "\t</Properties>" + System.lineSeparator() + + "\t<Scripts>" + System.lineSeparator() + + "\t\t<ScriptFile name=\"target/test-classes/scripts/filter.groovy\" path=\"target/test-classes/scripts/filter.groovy\" isWatched=\"true\"/>" + System.lineSeparator() + + "\t</Scripts>" + System.lineSeparator() + + "\t<CustomLevels>" + System.lineSeparator() + + "\t\t<CustomLevel name=\"Panic\" intLevel=\"17\"/>" + System.lineSeparator() + + "\t</CustomLevels>" + System.lineSeparator() + + "\t<ThresholdFilter onMatch=\"ACCEPT\" level=\"DEBUG\" onMisMatch=\"NEUTRAL\"/>" + System.lineSeparator() + + "\t<Appenders>" + System.lineSeparator() + + "\t\t<CONSOLE name=\"Stdout\" target=\"SYSTEM_OUT\">" + System.lineSeparator() + + "\t\t\t<PatternLayout pattern=\"%d [%t] %-5level: %msg%n%throwable\"/>" + System.lineSeparator() + + "\t\t\t<MarkerFilter onMatch=\"DENY\" onMisMatch=\"NEUTRAL\" marker=\"FLOW\"/>" + System.lineSeparator() + + "\t\t</CONSOLE>" + System.lineSeparator() + + "\t</Appenders>" + System.lineSeparator() + + "\t<Loggers>" + System.lineSeparator() + + "\t\t<Logger name=\"org.apache.logging.log4j\" additivity=\"false\" level=\"DEBUG\" includeLocation=\"true\">" + System.lineSeparator() + + "\t\t\t<AppenderRef ref=\"Stdout\"/>" + System.lineSeparator() + + "\t\t</Logger>" + System.lineSeparator() + + "\t\t<Root level=\"ERROR\" includeLocation=\"true\">" + System.lineSeparator() + + "\t\t\t<AppenderRef ref=\"Stdout\"/>" + System.lineSeparator() + + "\t\t</Root>" + System.lineSeparator() + + "\t</Loggers>" + System.lineSeparator() + + "</Configuration>" + System.lineSeparator(); + + @Test + public void testXmlConstructingWithAbstractConfiguration() throws Exception { + final ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder(); + CustomConfigurationFactory.addTestFixtures("config name", builder); + builder.addProperty("MyKey", "MyValue"); + builder.add(builder.newCustomLevel("Panic", 17)); + AbstractConfiguration configuration = builder.build(false); + configuration.setup(); + final String xmlConfiguration = configuration.toXmlConfiguration(); + assertEquals(expectedXml2, xmlConfiguration); + } }