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 2018/02/20 02:09:21 UTC
[04/12] logging-log4j2 git commit: [LOG4J2-2237] Move Jackson-based
layouts to their own modules: JSON, XML, and YAML.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-xml/src/main/java/org/apache/logging/log4j/jackson/xml/parser/XmlLogEventParser.java
----------------------------------------------------------------------
diff --git a/log4j-layout-jackson-xml/src/main/java/org/apache/logging/log4j/jackson/xml/parser/XmlLogEventParser.java b/log4j-layout-jackson-xml/src/main/java/org/apache/logging/log4j/jackson/xml/parser/XmlLogEventParser.java
new file mode 100644
index 0000000..32776a2
--- /dev/null
+++ b/log4j-layout-jackson-xml/src/main/java/org/apache/logging/log4j/jackson/xml/parser/XmlLogEventParser.java
@@ -0,0 +1,32 @@
+/*
+ * 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.jackson.xml.parser;
+
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.parser.AbstractJacksonLogEventParser;
+import org.apache.logging.log4j.jackson.xml.Log4jXmlObjectMapper;
+
+/**
+ * Parses the output from XmlLayout layout into instances of {@link LogEvent}.
+ */
+public class XmlLogEventParser extends AbstractJacksonLogEventParser {
+
+ public XmlLogEventParser() {
+ super(new Log4jXmlObjectMapper());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-xml/src/site/manual/index.md
----------------------------------------------------------------------
diff --git a/log4j-layout-jackson-xml/src/site/manual/index.md b/log4j-layout-jackson-xml/src/site/manual/index.md
new file mode 100644
index 0000000..216fe05
--- /dev/null
+++ b/log4j-layout-jackson-xml/src/site/manual/index.md
@@ -0,0 +1,33 @@
+<!-- vim: set syn=markdown : -->
+<!--
+ 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.
+-->
+
+# Apache Log4j Layout for Jackson XML module
+
+As of Log4j 3.0.0, the layout based on Jackson XML has moved from the existing module logj-core to the new modules log4j-layout-jackson-xml.
+
+## Requirements
+
+This module was introduced in Log4j 2.11.0 and requires Jackson.
+
+Some features may require optional
+[dependencies](../runtime-dependencies.html). These dependencies are specified in the
+documentation for those features.
+
+Some Log4j features require external dependencies.
+See the [Dependency Tree](dependencies.html#Dependency_Tree)
+for the exact list of JAR files needed for these features.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-xml/src/site/site.xml
----------------------------------------------------------------------
diff --git a/log4j-layout-jackson-xml/src/site/site.xml b/log4j-layout-jackson-xml/src/site/site.xml
new file mode 100644
index 0000000..6d4cddc
--- /dev/null
+++ b/log4j-layout-jackson-xml/src/site/site.xml
@@ -0,0 +1,52 @@
+<!--
+ 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.
+
+-->
+<project name="Log4j Core"
+ xmlns="http://maven.apache.org/DECORATION/1.4.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/DECORATION/1.4.0 http://maven.apache.org/xsd/decoration-1.4.0.xsd">
+ <body>
+ <links>
+ <item name="Apache" href="http://www.apache.org/" />
+ <item name="Logging Services" href="http://logging.apache.org/"/>
+ <item name="Log4j" href="../index.html"/>
+ </links>
+
+ <!-- Component-specific reports -->
+ <menu ref="reports"/>
+
+ <!-- Overall Project Info -->
+ <menu name="Log4j Project Information" img="icon-info-sign">
+ <item name="Dependencies" href="../dependencies.html" />
+ <item name="Dependency Convergence" href="../dependency-convergence.html" />
+ <item name="Dependency Management" href="../dependency-management.html" />
+ <item name="Project Team" href="../team-list.html" />
+ <item name="Mailing Lists" href="../mail-lists.html" />
+ <item name="Issue Tracking" href="../issue-tracking.html" />
+ <item name="Project License" href="../license.html" />
+ <item name="Source Repository" href="../source-repository.html" />
+ <item name="Project Summary" href="../project-summary.html" />
+ </menu>
+
+ <menu name="Log4j Project Reports" img="icon-cog">
+ <item name="Changes Report" href="../changes-report.html" />
+ <item name="JIRA Report" href="../jira-report.html" />
+ <item name="Surefire Report" href="../surefire-report.html" />
+ <item name="RAT Report" href="../rat-report.html" />
+ </menu>
+ </body>
+</project>
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/LevelMixInXmlTest.java
----------------------------------------------------------------------
diff --git a/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/LevelMixInXmlTest.java b/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/LevelMixInXmlTest.java
new file mode 100644
index 0000000..36e8532
--- /dev/null
+++ b/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/LevelMixInXmlTest.java
@@ -0,0 +1,36 @@
+/*
+ * 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.jackson.xml;
+
+import org.apache.logging.log4j.categories.Layouts;
+import org.apache.logging.log4j.jackson.LevelMixInTest;
+import org.junit.Ignore;
+import org.junit.experimental.categories.Category;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@Ignore("Fails for #testNameOnly()")
+@Category(Layouts.Xml.class)
+public class LevelMixInXmlTest extends LevelMixInTest {
+
+ @Override
+ protected ObjectMapper newObjectMapper() {
+ return new Log4jXmlObjectMapper();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/MarkerMixInXmlTest.java
----------------------------------------------------------------------
diff --git a/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/MarkerMixInXmlTest.java b/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/MarkerMixInXmlTest.java
new file mode 100644
index 0000000..b909453
--- /dev/null
+++ b/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/MarkerMixInXmlTest.java
@@ -0,0 +1,34 @@
+/*
+* 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.jackson.xml;
+
+import org.apache.logging.log4j.categories.Layouts;
+import org.apache.logging.log4j.jackson.AbstractMarkerMixInTest;
+import org.junit.experimental.categories.Category;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@Category(Layouts.Xml.class)
+public class MarkerMixInXmlTest extends AbstractMarkerMixInTest {
+
+ @Override
+ protected ObjectMapper newObjectMapper() {
+ return new Log4jXmlObjectMapper(true, false);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/StackTraceElementXmlMixInTest.java
----------------------------------------------------------------------
diff --git a/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/StackTraceElementXmlMixInTest.java b/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/StackTraceElementXmlMixInTest.java
new file mode 100644
index 0000000..1d45672
--- /dev/null
+++ b/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/StackTraceElementXmlMixInTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.jackson.xml;
+
+import java.io.IOException;
+
+import org.apache.logging.log4j.categories.Layouts;
+import org.apache.logging.log4j.jackson.Log4jStackTraceElementDeserializer;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+@Category(Layouts.Json.class)
+public class StackTraceElementXmlMixInTest {
+
+ protected String aposToQuotes(final String json) {
+ return json.replace("'", "\"");
+ }
+
+ private void roundtrip(final ObjectMapper mapper)
+ throws JsonProcessingException, IOException, JsonParseException, JsonMappingException {
+ final StackTraceElement expected = new StackTraceElement("package.SomeClass", "someMethod", "SomeClass.java",
+ 123);
+ final String s = mapper.writeValueAsString(expected);
+ final StackTraceElement actual = mapper.readValue(s, StackTraceElement.class);
+ Assert.assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testFromJsonWithLog4jModule() throws Exception {
+ final ObjectMapper mapper = new ObjectMapper();
+ final boolean encodeThreadContextAsList = false;
+ final Log4jXmlModule module = new Log4jXmlModule(true, false);
+ module.addDeserializer(StackTraceElement.class, new Log4jStackTraceElementDeserializer());
+ mapper.registerModule(module);
+ final StackTraceElement expected = new StackTraceElement("package.SomeClass", "someMethod", "SomeClass.java",
+ 123);
+ final String s = this
+ .aposToQuotes("{'class':'package.SomeClass','method':'someMethod','file':'SomeClass.java','line':123}");
+ final StackTraceElement actual = mapper.readValue(s, StackTraceElement.class);
+ Assert.assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testFromJsonWithSimpleModule() throws Exception {
+ final ObjectMapper mapper = new ObjectMapper();
+ final SimpleModule module = new SimpleModule();
+ module.addDeserializer(StackTraceElement.class, new Log4jStackTraceElementDeserializer());
+ mapper.registerModule(module);
+ final StackTraceElement expected = new StackTraceElement("package.SomeClass", "someMethod", "SomeClass.java",
+ 123);
+ final String s = this
+ .aposToQuotes("{'class':'package.SomeClass','method':'someMethod','file':'SomeClass.java','line':123}");
+ final StackTraceElement actual = mapper.readValue(s, StackTraceElement.class);
+ Assert.assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testLog4jXmlObjectMapper() throws Exception {
+ this.roundtrip(new Log4jXmlObjectMapper());
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/layout/ConcurrentLoggingWithXmlLayoutTest.java
----------------------------------------------------------------------
diff --git a/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/layout/ConcurrentLoggingWithXmlLayoutTest.java b/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/layout/ConcurrentLoggingWithXmlLayoutTest.java
new file mode 100644
index 0000000..afbff8a
--- /dev/null
+++ b/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/layout/ConcurrentLoggingWithXmlLayoutTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.jackson.xml.layout;
+
+import static org.hamcrest.CoreMatchers.endsWith;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.junit.Assert.assertThat;
+
+import java.io.File;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.junit.LoggerContextRule;
+import org.junit.AfterClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+/**
+ * Like the test for LOG4J2-1769.
+ */
+public class ConcurrentLoggingWithXmlLayoutTest {
+
+ private class LoggingThread extends Thread {
+ private final Set<Thread> threads;
+ private final Logger log;
+
+ LoggingThread(final Set<Thread> threads, final Logger log) {
+ this.threads = threads;
+ this.log = log;
+ }
+
+ @Override
+ public void run() {
+ log.info(threads.size());
+ try {
+ for (int i = 0; i < 64; i++) {
+ log.info("First message.");
+ log.info("Second message.");
+ }
+ } finally {
+ threads.remove(this);
+ }
+ }
+ }
+
+ @ClassRule
+ public static LoggerContextRule context = new LoggerContextRule("log4j2-xml-layout.xml");
+
+ private static final String PATH = "target/test-xml-layout.log";
+
+ @AfterClass
+ public static void after() {
+ new File(PATH).delete();
+ }
+
+ @Test
+ public void testConcurrentLogging() throws Throwable {
+ final Logger log = context.getLogger(ConcurrentLoggingWithXmlLayoutTest.class);
+ final Set<Thread> threads = Collections.synchronizedSet(new HashSet<Thread>());
+ final List<Throwable> thrown = Collections.synchronizedList(new ArrayList<Throwable>());
+
+ for (int x = 0; x < Runtime.getRuntime().availableProcessors() * 2; x++) {
+ final Thread t = new LoggingThread(threads, log);
+ threads.add(t);
+
+ // Appender is configured with ignoreExceptions="false";
+ // any exceptions are propagated to the caller, so we can catch them here.
+ t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+ @Override
+ public void uncaughtException(final Thread t, final Throwable e) {
+ thrown.add(e);
+ }
+ });
+ t.start();
+ }
+
+ while (!threads.isEmpty()) {
+ log.info("not done going to sleep...");
+ Thread.sleep(10);
+ }
+
+ // if any error occurred, fail this test
+ if (!thrown.isEmpty()) {
+ throw thrown.get(0);
+ }
+
+ // simple test to ensure content is not corrupted
+ if (new File(PATH).exists()) {
+ final List<String> lines = Files.readAllLines(new File(PATH).toPath(), Charset.defaultCharset());
+ for (final String line : lines) {
+ assertThat(line, startsWith("<Event "));
+ assertThat(line, endsWith("</Event>"));
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/layout/ThrowableProxyXmlTest.java
----------------------------------------------------------------------
diff --git a/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/layout/ThrowableProxyXmlTest.java b/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/layout/ThrowableProxyXmlTest.java
new file mode 100644
index 0000000..7fb9177
--- /dev/null
+++ b/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/layout/ThrowableProxyXmlTest.java
@@ -0,0 +1,33 @@
+/*
+ * 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.jackson.xml.layout;
+
+import java.io.IOException;
+
+import org.apache.logging.log4j.jackson.ThrowableProxyJacksonTest;
+import org.apache.logging.log4j.jackson.xml.Log4jXmlObjectMapper;
+import org.junit.Test;
+
+public class ThrowableProxyXmlTest extends ThrowableProxyJacksonTest {
+
+ @Test
+ public void testIoContainerAsXml() throws IOException {
+ testIoContainer(new Log4jXmlObjectMapper());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/layout/XmlLayoutTest.java
----------------------------------------------------------------------
diff --git a/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/layout/XmlLayoutTest.java b/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/layout/XmlLayoutTest.java
new file mode 100644
index 0000000..5323163
--- /dev/null
+++ b/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/layout/XmlLayoutTest.java
@@ -0,0 +1,440 @@
+/*
+ * 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.jackson.xml.layout;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.MarkerManager;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.categories.Layouts;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.BasicConfigurationFactory;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.core.layout.LogEventFixtures;
+import org.apache.logging.log4j.core.lookup.JavaLookup;
+import org.apache.logging.log4j.core.util.KeyValuePair;
+import org.apache.logging.log4j.jackson.AbstractJacksonLayout;
+import org.apache.logging.log4j.jackson.XmlConstants;
+import org.apache.logging.log4j.jackson.xml.AbstractLogEventXmlMixIn;
+import org.apache.logging.log4j.jackson.xml.Log4jXmlObjectMapper;
+import org.apache.logging.log4j.junit.ThreadContextRule;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.spi.AbstractLogger;
+import org.apache.logging.log4j.test.appender.ListAppender;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+
+/**
+ * Tests {@link XmlLayout}.
+ */
+@Category(Layouts.Xml.class)
+public class XmlLayoutTest {
+ private static final int NOT_FOUND = -1;
+ private static final String body = "<Message>empty mdc</Message>";
+ static ConfigurationFactory cf = new BasicConfigurationFactory();
+ private static final String markerTag = "<Marker name=\"EVENT\"/>";
+
+ @AfterClass
+ public static void cleanupClass() {
+ ConfigurationFactory.removeConfigurationFactory(cf);
+ }
+
+ @BeforeClass
+ public static void setupClass() {
+ ConfigurationFactory.setConfigurationFactory(cf);
+ final LoggerContext ctx = LoggerContext.getContext();
+ ctx.reconfigure();
+ }
+
+ @Rule
+ public final ThreadContextRule threadContextRule = new ThreadContextRule();
+
+ LoggerContext ctx = LoggerContext.getContext();
+
+ Logger rootLogger = this.ctx.getRootLogger();
+
+ private void checkAttribute(final String name, final String value, final boolean compact, final String str) {
+ Assert.assertTrue(str, str.contains(name + "=\"" + value + "\""));
+ }
+
+ private void checkAttributeName(final String name, final boolean compact, final String str) {
+ Assert.assertTrue(str, str.contains(name + "=\""));
+ }
+
+ private void checkContains(final String expected, final List<String> list) {
+ for (final String string : list) {
+ final String trimedLine = string.trim();
+ if (trimedLine.contains(expected)) {
+ return;
+ }
+ }
+ Assert.fail("Cannot find " + expected + " in " + list);
+ }
+
+ private void checkContextMapElement(final String key, final String value, final boolean compact, final String str) {
+ // <item key="MDC.A" value="A_Value"/>
+ assertTrue(str, str.contains(String.format("<item key=\"%s\" value=\"%s\"/>", key, value)));
+ }
+
+ private void checkContextStackElement(final String value, final boolean compact, final String str) {
+ // <ContextStackItem>stack_msg1</ContextStackItem>
+ assertTrue(str, str.contains(String.format("<ContextStackItem>%s</ContextStackItem>", value)));
+ }
+
+ private void checkElementName(final String name, final boolean compact, final String str,
+ final boolean withAttributes, final boolean withChildren) {
+ // simple checks, don't try to be too smart here, we're just looking for the names and basic shape.
+ // start
+ final String startStr = withAttributes ? "<" + name + " " : "<" + name + ">";
+ final int startPos = str.indexOf(startStr);
+ Assert.assertTrue(String.format("Missing text '%s' in: %s", startStr, str), startPos >= 0);
+ // end
+ final String endStr = withChildren ? "</" + name + ">" : "/>";
+ final int endPos = str.indexOf(endStr, startPos + startStr.length());
+ Assert.assertTrue(str, endPos >= 0);
+ }
+
+ private void checkElementNameAbsent(final String name, final boolean compact, final String str) {
+ Assert.assertFalse(str.contains("<" + name));
+ }
+
+ private void checkJsonPropertyOrder(final boolean includeContextStack, final boolean includeContextMap,
+ final boolean includeStacktrace, final String str) {
+ final JsonPropertyOrder annotation = AbstractLogEventXmlMixIn.class.getAnnotation(JsonPropertyOrder.class);
+ Assert.assertNotNull(annotation);
+ int previousIndex = 0;
+ String previousName = null;
+ for (final String name : annotation.value()) {
+ final int currentIndex = str.indexOf(name);
+ if (!includeContextStack && XmlConstants.ELT_CONTEXT_STACK.equals(name)) {
+ Assert.assertTrue(String.format("Unexpected element '%s' in: %s", name, str),
+ currentIndex == NOT_FOUND);
+ break;
+ }
+ if (!includeContextMap && XmlConstants.ELT_CONTEXT_MAP.equals(name)) {
+ Assert.assertTrue(String.format("Unexpected element '%s' in: %s", name, str),
+ currentIndex == NOT_FOUND);
+ break;
+ }
+ if (!includeStacktrace && XmlConstants.ELT_EXTENDED_STACK_TRACE.equals(name)) {
+ Assert.assertTrue(String.format("Unexpected element '%s' in: %s", name, str),
+ currentIndex == NOT_FOUND);
+ break;
+ }
+ if (!includeStacktrace && XmlConstants.ELT_EXTENDED_STACK_TRACE_ITEM.equals(name)) {
+ Assert.assertTrue(String.format("Unexpected element '%s' in: %s", name, str),
+ currentIndex == NOT_FOUND);
+ break;
+ }
+ // TODO
+ // Bug: The method
+ // com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector._sortProperties(Map<String,
+ // POJOPropertyBuilder>) messes up the order defined in AbstractXmlLogEventMixIn's JsonPropertyOrder
+ // annotations.
+ // Assert.assertTrue(String.format("name='%s', previousIndex=%,d, previousName='%s', currentIndex=%,d: %s",
+ // name, previousIndex, previousName, currentIndex, str), previousIndex < currentIndex);
+ previousIndex = currentIndex;
+ previousName = name;
+ }
+ }
+
+ private String prepareXMLForStacktraceTests(final boolean stacktraceAsString) {
+ final Log4jLogEvent expected = LogEventFixtures.createLogEvent();
+ // @formatter:off
+ final AbstractJacksonLayout layout = XmlLayout.newBuilder()
+ .setCompact(true)
+ .setIncludeStacktrace(true)
+ .setStacktraceAsString(stacktraceAsString)
+ .build();
+ // @formatter:off
+ return layout.toSerializable(expected);
+ }
+
+ @Test
+ public void testAdditionalFields() throws Exception {
+ final AbstractJacksonLayout layout = XmlLayout.newBuilder().setLocationInfo(false).setProperties(false)
+ .setIncludeStacktrace(false)
+ .setAdditionalFields(new KeyValuePair[] { new KeyValuePair("KEY1", "VALUE1"),
+ new KeyValuePair("KEY2", "${java:runtime}"), })
+ .setCharset(StandardCharsets.UTF_8).setConfiguration(ctx.getConfiguration()).build();
+ final String str = layout.toSerializable(LogEventFixtures.createLogEvent());
+ assertTrue(str, str.contains("<KEY1>VALUE1</KEY1>"));
+ assertTrue(str, str.contains("<KEY2>" + new JavaLookup().getRuntime() + "</KEY2>"));
+ }
+
+ /**
+ * @param includeLocationInfo
+ * TODO
+ * @param compact
+ * @param includeContextMap
+ * TODO
+ * @param includeContextStack
+ * TODO
+ * @throws IOException
+ * @throws JsonParseException
+ * @throws JsonMappingException
+ */
+ private void testAllFeatures(final boolean includeLocationInfo, final boolean compact,
+ final boolean includeContextMap, final boolean includeContextStack, final boolean includeStacktrace)
+ throws IOException, JsonParseException, JsonMappingException {
+ final Log4jLogEvent expected = LogEventFixtures.createLogEvent();
+ // @formatter:off
+ final XmlLayout layout = XmlLayout.newBuilder()
+ .setLocationInfo(includeLocationInfo)
+ .setProperties(includeContextMap)
+ .setComplete(false)
+ .setCompact(compact)
+ .setIncludeStacktrace(includeStacktrace)
+ .setCharset(StandardCharsets.UTF_8)
+ .build();
+ final String str = layout.toSerializable(expected);
+ // @formatter:on
+ // System.out.println(str);
+ assertEquals(str, !compact, str.contains("\n"));
+ assertEquals(str, includeLocationInfo, str.contains("Source"));
+ assertEquals(str, includeContextMap, str.contains("ContextMap"));
+ final Log4jLogEvent actual = new Log4jXmlObjectMapper().readValue(str, Log4jLogEvent.class);
+ LogEventFixtures.assertEqualLogEvents(expected, actual, includeLocationInfo, includeContextMap,
+ includeStacktrace);
+ if (includeContextMap) {
+ this.checkContextMapElement("MDC.A", "A_Value", compact, str);
+ this.checkContextMapElement("MDC.B", "B_Value", compact, str);
+ }
+ if (includeContextStack) {
+ this.checkContextStackElement("stack_msg1", compact, str);
+ this.checkContextStackElement("stack_msg2", compact, str);
+ }
+
+ //
+ assertNull(actual.getThrown());
+ // check some attrs
+ assertTrue(str, str.contains("loggerFqcn=\"f.q.c.n\""));
+ assertTrue(str, str.contains("loggerName=\"a.B\""));
+ // make sure short names are used
+ assertTrue(str, str.contains("<Event "));
+ if (includeStacktrace) {
+ assertTrue("Missing \"class=\" in: " + str, str.contains("class="));
+ assertTrue("Missing \"method=\" in: " + str, str.contains("method="));
+ assertTrue("Missing \"file=\" in: " + str, str.contains("file="));
+ assertTrue("Missing \"line=\" in: " + str, str.contains("line="));
+ }
+ //
+ // make sure the names we want are used
+ // this.checkAttributeName("timeMillis", compact, str);
+ this.checkElementName("Instant", compact, str, true, false);
+ this.checkAttributeName("epochSecond", compact, str);
+ this.checkAttributeName("nanoOfSecond", compact, str);
+ this.checkAttributeName("thread", compact, str); // and not threadName
+ this.checkAttributeName("level", compact, str);
+ this.checkAttributeName("loggerName", compact, str);
+ this.checkElementName("Marker", compact, str, true, true);
+ this.checkAttributeName("name", compact, str);
+ this.checkElementName("Parents", compact, str, false, true);
+ this.checkElementName("Message", compact, str, false, true);
+ this.checkElementName("Thrown", compact, str, true, true);
+ this.checkElementName("Cause", compact, str, true, includeStacktrace);
+ this.checkAttributeName("commonElementCount", compact, str);
+ this.checkAttributeName("message", compact, str);
+ this.checkAttributeName("localizedMessage", compact, str);
+ if (includeStacktrace) {
+ this.checkElementName("ExtendedStackTrace", compact, str, false, true);
+ this.checkAttributeName("class", compact, str);
+ this.checkAttributeName("method", compact, str);
+ this.checkAttributeName("file", compact, str);
+ this.checkAttributeName("line", compact, str);
+ this.checkAttributeName("exact", compact, str);
+ this.checkAttributeName("location", compact, str);
+ this.checkAttributeName("version", compact, str);
+ } else {
+ this.checkElementNameAbsent("ExtendedStackTrace", compact, str);
+ }
+ this.checkElementName("Suppressed", compact, str, false, true);
+ this.checkAttributeName("loggerFqcn", compact, str);
+ this.checkAttributeName("endOfBatch", compact, str);
+ if (includeContextMap) {
+ this.checkElementName("ContextMap", compact, str, false, true);
+ } else {
+ this.checkElementNameAbsent("ContextMap", compact, str);
+ }
+ this.checkElementName("ContextStack", compact, str, false, true);
+ if (includeLocationInfo) {
+ this.checkElementName("Source", compact, str, true, false);
+ } else {
+ this.checkElementNameAbsent("Source", compact, str);
+ }
+ // check some attrs
+ this.checkAttribute("loggerFqcn", "f.q.c.n", compact, str);
+ this.checkAttribute("loggerName", "a.B", compact, str);
+ this.checkJsonPropertyOrder(includeContextStack, includeContextMap, includeStacktrace, str);
+ }
+
+ @Test
+ public void testContentType() {
+ final XmlLayout layout = XmlLayout.createDefaultLayout();
+ assertEquals("text/xml; charset=UTF-8", layout.getContentType());
+ }
+
+ @Test
+ public void testDefaultCharset() {
+ final XmlLayout layout = XmlLayout.createDefaultLayout();
+ assertEquals(StandardCharsets.UTF_8, layout.getCharset());
+ }
+
+ @Test
+ public void testExcludeStacktrace() throws Exception {
+ this.testAllFeatures(false, false, false, false, false);
+ }
+
+ @Test
+ public void testIncludeNullDelimiterFalse() throws Exception {
+ final AbstractJacksonLayout layout = XmlLayout.newBuilder().setCompact(true).setIncludeNullDelimiter(false)
+ .build();
+ final String str = layout.toSerializable(LogEventFixtures.createLogEvent());
+ assertFalse(str.endsWith("\0"));
+ }
+
+ @Test
+ public void testIncludeNullDelimiterTrue() throws Exception {
+ final AbstractJacksonLayout layout = XmlLayout.newBuilder().setCompact(true).setIncludeNullDelimiter(true)
+ .build();
+ final String str = layout.toSerializable(LogEventFixtures.createLogEvent());
+ assertTrue(str.endsWith("\0"));
+ }
+
+ /**
+ * Test case for MDC conversion pattern.
+ */
+ @Test
+ public void testLayout() throws Exception {
+ final Map<String, Appender> appenders = this.rootLogger.getAppenders();
+ for (final Appender appender : appenders.values()) {
+ this.rootLogger.removeAppender(appender);
+ }
+ // set up appender
+ final XmlLayout layout = XmlLayout.newBuilder().setLocationInfo(true).setProperties(true).setComplete(true)
+ .setCompact(false).setIncludeStacktrace(true).build();
+
+ final ListAppender appender = new ListAppender("List", null, layout, true, false);
+ appender.start();
+
+ // set appender on root and set level to debug
+ this.rootLogger.addAppender(appender);
+ this.rootLogger.setLevel(Level.DEBUG);
+
+ // output starting message
+ this.rootLogger.debug("starting mdc pattern test");
+
+ this.rootLogger.debug("empty mdc");
+
+ ThreadContext.put("key1", "value1");
+ ThreadContext.put("key2", "value2");
+
+ this.rootLogger.debug("filled mdc");
+
+ ThreadContext.remove("key1");
+ ThreadContext.remove("key2");
+
+ this.rootLogger.error("finished mdc pattern test", new NullPointerException("test"));
+
+ final Marker marker = MarkerManager.getMarker("EVENT");
+ this.rootLogger.error(marker, "marker test");
+
+ appender.stop();
+
+ final List<String> list = appender.getMessages();
+
+ 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("</Events>"));
+ this.checkContains("loggerFqcn=\"" + AbstractLogger.class.getName() + "\"", list);
+ this.checkContains("level=\"DEBUG\"", list);
+ this.checkContains(">starting mdc pattern test</Message>", list);
+ // this.checkContains("<Message>starting mdc pattern test</Message>", list);
+
+ // <Marker xmlns="" _class="org.apache.logging.log4j.MarkerManager..Log4jMarker" name="EVENT"/>
+ this.checkContains("<Marker", list);
+ this.checkContains("name=\"EVENT\"/>", list);
+
+ for (final Appender app : appenders.values()) {
+ this.rootLogger.addAppender(app);
+ }
+ }
+
+ @Test
+ public void testLayoutLoggerName() {
+ final XmlLayout layout = XmlLayout.newBuilder().setLocationInfo(false).setProperties(true).setComplete(true)
+ .setCompact(false).setIncludeStacktrace(true).build();
+
+ final Log4jLogEvent event = Log4jLogEvent.newBuilder() //
+ .setLoggerName("a.B") //
+ .setLoggerFqcn("f.q.c.n") //
+ .setLevel(Level.DEBUG) //
+ .setMessage(new SimpleMessage("M")) //
+ .setThreadName("threadName") //
+ .setTimeMillis(1).build();
+ final String str = layout.toSerializable(event);
+ assertTrue(str, str.contains("loggerName=\"a.B\""));
+ }
+
+ @Test
+ public void testLocationOffCompactOffMdcOff() throws Exception {
+ this.testAllFeatures(false, false, false, false, true);
+ }
+
+ @Test
+ public void testLocationOnCompactOnMdcOn() throws Exception {
+ this.testAllFeatures(true, true, true, true, true);
+ }
+
+ @Test
+ public void testLocationOnCompactOnNdcOn() throws Exception {
+ this.testAllFeatures(false, false, false, true, false);
+ }
+
+ @Test
+ public void testStacktraceAsNonString() throws Exception {
+ final String str = prepareXMLForStacktraceTests(false);
+ assertTrue(str, str.contains("<ExtendedStackTrace><ExtendedStackTraceItem"));
+ }
+
+ @Test
+ public void testStacktraceAsString() throws Exception {
+ final String str = prepareXMLForStacktraceTests(true);
+ assertTrue(str, str.contains("<ExtendedStackTrace>java.lang.NullPointerException"));
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/parser/XmlLogEventParserTest.java
----------------------------------------------------------------------
diff --git a/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/parser/XmlLogEventParserTest.java b/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/parser/XmlLogEventParserTest.java
new file mode 100644
index 0000000..93e30b5
--- /dev/null
+++ b/log4j-layout-jackson-xml/src/test/java/org/apache/logging/log4j/jackson/xml/parser/XmlLogEventParserTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.jackson.xml.parser;
+
+import java.nio.charset.StandardCharsets;
+
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.parser.AbstractLogEventParserTest;
+import org.apache.logging.log4j.core.parser.ParseException;
+import org.junit.Before;
+import org.junit.Test;
+
+public class XmlLogEventParserTest extends AbstractLogEventParserTest {
+
+ private static final String XML = "<Event xmlns=\"http://logging.apache.org/log4j/2.0/events\"\n" +
+ " timeMillis=\"1493121664118\"\n" +
+ " level=\"INFO\"\n" +
+ " loggerName=\"HelloWorld\"\n" +
+ " endOfBatch=\"false\"\n" +
+ " thread=\"main\"\n" +
+ " loggerFqcn=\"org.apache.logging.log4j.spi.AbstractLogger\"\n" +
+ " threadId=\"1\"\n" +
+ " threadPriority=\"5\">\n" +
+ " <Instant epochSecond=\"1493121664\" nanoOfSecond=\"118000000\"/>\n" +
+ " <Marker name=\"child\">\n" +
+ " <Parents>\n" +
+ " <Marker name=\"parent\">\n" +
+ " <Parents>\n" +
+ " <Marker name=\"grandparent\"/>\n" +
+ " </Parents>\n" +
+ " </Marker>\n" +
+ " </Parents>\n" +
+ " </Marker>\n" +
+ " <Message>Hello, world!</Message>\n" +
+ " <ContextMap>\n" +
+ " <item key=\"bar\" value=\"BAR\"/>\n" +
+ " <item key=\"foo\" value=\"FOO\"/>\n" +
+ " </ContextMap>\n" +
+ " <ContextStack>\n" +
+ " <ContextStackItem>one</ContextStackItem>\n" +
+ " <ContextStackItem>two</ContextStackItem>\n" +
+ " </ContextStack>\n" +
+ " <Source\n" +
+ " class=\"logtest.Main\"\n" +
+ " method=\"main\"\n" +
+ " file=\"Main.java\"\n" +
+ " line=\"29\"/>\n" +
+ " <Thrown commonElementCount=\"0\" message=\"error message\" name=\"java.lang.RuntimeException\">\n" +
+ " <ExtendedStackTrace>\n" +
+ " <ExtendedStackTraceItem\n" +
+ " class=\"logtest.Main\"\n" +
+ " method=\"main\"\n" +
+ " file=\"Main.java\"\n" +
+ " line=\"29\"\n" +
+ " exact=\"true\"\n" +
+ " location=\"classes/\"\n" +
+ " version=\"?\"/>\n" +
+ " </ExtendedStackTrace>\n" +
+ " </Thrown>\n" +
+ "</Event>";
+
+ private XmlLogEventParser parser;
+
+ @Before
+ public void setup() {
+ parser = new XmlLogEventParser();
+ }
+
+ @Test
+ public void testByteArray() throws ParseException {
+ final LogEvent logEvent = parser.parseFrom(XML.getBytes(StandardCharsets.UTF_8));
+ assertLogEvent(logEvent);
+ }
+
+ @Test
+ public void testByteArrayOffsetLength() throws ParseException {
+ final byte[] bytes = ("abc" + XML + "def").getBytes(StandardCharsets.UTF_8);
+ final LogEvent logEvent = parser.parseFrom(bytes, 3, bytes.length - 6);
+ assertLogEvent(logEvent);
+ }
+
+ @Test
+ public void testEmptyObject() throws ParseException {
+ parser.parseFrom("<Event></Event>");
+ }
+
+ @Test
+ public void testString() throws ParseException {
+ final LogEvent logEvent = parser.parseFrom(XML);
+ assertLogEvent(logEvent);
+ }
+
+ @Test(expected = ParseException.class)
+ public void testStringEmpty() throws ParseException {
+ parser.parseFrom("");
+ }
+
+ @Test
+ public void testStringIgnoreInvalidProperty() throws ParseException {
+ parser.parseFrom("<Event><foo>bar</foo></Event>");
+ }
+
+ @Test(expected = ParseException.class)
+ public void testStringInvalidXml() throws ParseException {
+ parser.parseFrom("foobar");
+ }
+
+ @Test(expected = ParseException.class)
+ public void testStringWrongPropertyType() throws ParseException {
+ parser.parseFrom("<Event><Instant epochSecond=\"bar\">foobar</Instant></Event>");
+ }
+
+ @Test
+ public void testTimeMillisIgnored() throws ParseException {
+ parser.parseFrom("<Event><timeMillis>foobar</timeMillis></Event>");
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-xml/src/test/resources/log4j2-xml-layout.xml
----------------------------------------------------------------------
diff --git a/log4j-layout-jackson-xml/src/test/resources/log4j2-xml-layout.xml b/log4j-layout-jackson-xml/src/test/resources/log4j2-xml-layout.xml
new file mode 100644
index 0000000..f0b1709
--- /dev/null
+++ b/log4j-layout-jackson-xml/src/test/resources/log4j2-xml-layout.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+
+-->
+<Configuration status="INFO" packages="">
+ <Appenders>
+ <File name="stdout" ignoreExceptions="false" fileName="target/test-xml-layout.log" append="false">
+ <XmlLayout compact="true" eventEol="true" />
+ </File>
+ </Appenders>
+
+ <Loggers>
+ <Root level="INFO">
+ <AppenderRef ref="stdout" />
+ </Root>
+ </Loggers>
+</Configuration>
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-yaml/pom.xml
----------------------------------------------------------------------
diff --git a/log4j-layout-jackson-yaml/pom.xml b/log4j-layout-jackson-yaml/pom.xml
new file mode 100644
index 0000000..9df9743
--- /dev/null
+++ b/log4j-layout-jackson-yaml/pom.xml
@@ -0,0 +1,171 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- ~ 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. -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>3.0.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>log4j-layout-jackson-yaml</artifactId>
+ <name>Apache Log4j Layout for Jackson YAML</name>
+ <description>
+ Apache Log4j Layout for Jackson YAML.
+ </description>
+ <properties>
+ <log4jParentDir>${basedir}/..</log4jParentDir>
+ <docLabel>Log4j Layout for Jackson YAML Documentation</docLabel>
+ <projectDir>/log4j-layout-yaml</projectDir>
+ <module.name>org.apache.logging.log4j.jackson.yaml</module.name>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-layout-jackson</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.dataformat</groupId>
+ <artifactId>jackson-dataformat-yaml</artifactId>
+ </dependency>
+ <!-- Test Dependencies -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ <type>test-jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <type>test-jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-layout-jackson</artifactId>
+ <type>test-jar</type>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Fragment-Host>org.apache.logging.log4j.jackson.yaml</Fragment-Host>
+ <Export-Package>*</Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-changes-plugin</artifactId>
+ <version>${changes.plugin.version}</version>
+ <reportSets>
+ <reportSet>
+ <reports>
+ <report>changes-report</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ <configuration>
+ <issueLinkTemplate>%URL%/show_bug.cgi?id=%ISSUE%</issueLinkTemplate>
+ <useJql>true</useJql>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <version>${checkstyle.plugin.version}</version>
+ <configuration>
+ <!--<propertiesLocation>${vfs.parent.dir}/checkstyle.properties</propertiesLocation> -->
+ <configLocation>${log4jParentDir}/checkstyle.xml</configLocation>
+ <suppressionsLocation>${log4jParentDir}/checkstyle-suppressions.xml</suppressionsLocation>
+ <enableRulesSummary>false</enableRulesSummary>
+ <propertyExpansion>basedir=${basedir}</propertyExpansion>
+ <propertyExpansion>licensedir=${log4jParentDir}/checkstyle-header.txt</propertyExpansion>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>${javadoc.plugin.version}</version>
+ <configuration>
+ <bottom><![CDATA[<p align="center">Copyright © {inceptionYear}-{currentYear} {organizationName}. All Rights Reserved.<br />
+ Apache Logging, Apache Log4j, Log4j, Apache, the Apache feather logo, the Apache Logging project logo,
+ and the Apache Log4j logo are trademarks of The Apache Software Foundation.</p>]]></bottom>
+ <!-- module link generation is completely broken in the javadoc plugin for a multi-module non-aggregating project -->
+ <detectOfflineLinks>false</detectOfflineLinks>
+ <linksource>true</linksource>
+ </configuration>
+ <reportSets>
+ <reportSet>
+ <id>non-aggregate</id>
+ <reports>
+ <report>javadoc</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>findbugs-maven-plugin</artifactId>
+ <version>${findbugs.plugin.version}</version>
+ <configuration>
+ <fork>true</fork>
+ <jvmArgs>-Duser.language=en</jvmArgs>
+ <threshold>Normal</threshold>
+ <effort>Default</effort>
+ <excludeFilterFile>${log4jParentDir}/findbugs-exclude-filter.xml</excludeFilterFile>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jxr-plugin</artifactId>
+ <version>${jxr.plugin.version}</version>
+ <reportSets>
+ <reportSet>
+ <id>non-aggregate</id>
+ <reports>
+ <report>jxr</report>
+ </reports>
+ </reportSet>
+ <reportSet>
+ <id>aggregate</id>
+ <reports>
+ <report>aggregate</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-pmd-plugin</artifactId>
+ <version>${pmd.plugin.version}</version>
+ <configuration>
+ <targetJdk>${maven.compiler.target}</targetJdk>
+ </configuration>
+ </plugin>
+ </plugins>
+ </reporting>
+</project>
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/Log4jYamlModule.java
----------------------------------------------------------------------
diff --git a/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/Log4jYamlModule.java b/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/Log4jYamlModule.java
new file mode 100644
index 0000000..8446a9d
--- /dev/null
+++ b/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/Log4jYamlModule.java
@@ -0,0 +1,59 @@
+/*
+ * 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.jackson.yaml;
+
+import org.apache.logging.log4j.jackson.SetupContextInitializer;
+import org.apache.logging.log4j.jackson.SimpleModuleInitializer;
+
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+/**
+ * <p>
+ * <em>Consider this class private.</em>
+ * </p>
+ */
+final class Log4jYamlModule extends SimpleModule {
+
+ private static final long serialVersionUID = 1L;
+ private final boolean encodeThreadContextAsList;
+ private final boolean includeStacktrace;
+ private final boolean stacktraceAsString;
+
+ Log4jYamlModule(final boolean encodeThreadContextAsList, final boolean includeStacktrace,
+ final boolean stacktraceAsString) {
+ super(Log4jYamlModule.class.getName(), new Version(2, 0, 0, null, null, null));
+ this.encodeThreadContextAsList = encodeThreadContextAsList;
+ this.includeStacktrace = includeStacktrace;
+ this.stacktraceAsString = stacktraceAsString;
+ // MUST call initialize() here.
+ // Calling this from setupModule is too late!
+ // noinspection ThisEscapedInObjectConstruction
+ new SimpleModuleInitializer().initialize(this, false);
+ }
+
+ @Override
+ public void setupModule(final SetupContext context) {
+ // MUST call super here.
+ super.setupModule(context);
+ if (encodeThreadContextAsList) {
+ new SetupContextInitializer().setupModule(context, includeStacktrace, stacktraceAsString);
+ } else {
+ new YamlSetupContextInitializer().setupModule(context, includeStacktrace, stacktraceAsString);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/Log4jYamlObjectMapper.java
----------------------------------------------------------------------
diff --git a/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/Log4jYamlObjectMapper.java b/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/Log4jYamlObjectMapper.java
new file mode 100644
index 0000000..62c6483
--- /dev/null
+++ b/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/Log4jYamlObjectMapper.java
@@ -0,0 +1,48 @@
+/*
+ * 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.jackson.yaml;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
+
+/**
+ * A Jackson {@link ObjectMapper} initialized for Log4j.
+ * <p>
+ * <em>Consider this class private.</em>
+ * </p>
+ */
+public class Log4jYamlObjectMapper extends YAMLMapper {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create a new instance using the {@link Log4jYamlModule}.
+ */
+ public Log4jYamlObjectMapper() {
+ this(false, true, false);
+ }
+
+ /**
+ * Create a new instance using the {@link Log4jYamlModule}.
+ */
+ public Log4jYamlObjectMapper(final boolean encodeThreadContextAsList, final boolean includeStacktrace, final boolean stacktraceAsString) {
+ this.registerModule(new Log4jYamlModule(encodeThreadContextAsList, includeStacktrace, stacktraceAsString));
+ this.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/YamlSetupContextInitializer.java
----------------------------------------------------------------------
diff --git a/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/YamlSetupContextInitializer.java b/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/YamlSetupContextInitializer.java
new file mode 100644
index 0000000..67f6fd1
--- /dev/null
+++ b/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/YamlSetupContextInitializer.java
@@ -0,0 +1,47 @@
+package org.apache.logging.log4j.jackson.yaml;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.impl.ExtendedStackTraceElement;
+import org.apache.logging.log4j.core.impl.ThrowableProxy;
+import org.apache.logging.log4j.core.time.Instant;
+import org.apache.logging.log4j.jackson.ExtendedStackTraceElementMixIn;
+import org.apache.logging.log4j.jackson.InstantMixIn;
+import org.apache.logging.log4j.jackson.LevelMixIn;
+import org.apache.logging.log4j.jackson.LogEventJsonMixIn;
+import org.apache.logging.log4j.jackson.MarkerMixIn;
+import org.apache.logging.log4j.jackson.StackTraceElementMixIn;
+import org.apache.logging.log4j.jackson.ThrowableProxyMixIn;
+import org.apache.logging.log4j.jackson.ThrowableProxyWithStacktraceAsStringMixIn;
+import org.apache.logging.log4j.jackson.ThrowableProxyWithoutStacktraceMixIn;
+
+import com.fasterxml.jackson.databind.Module.SetupContext;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+/**
+ * Used to set up {@link SetupContext} from different {@link SimpleModule}s.
+ * <p>
+ * <em>Consider this class private.</em>
+ * </p>
+ */
+class YamlSetupContextInitializer {
+
+ public void setupModule(final SetupContext context, final boolean includeStacktrace,
+ final boolean stacktraceAsString) {
+ // JRE classes: we cannot edit those with Jackson annotations
+ context.setMixInAnnotations(StackTraceElement.class, StackTraceElementMixIn.class);
+ // Log4j API classes: we do not want to edit those with Jackson annotations because the API module should not
+ // depend on Jackson.
+ context.setMixInAnnotations(Marker.class, MarkerMixIn.class);
+ context.setMixInAnnotations(Level.class, LevelMixIn.class);
+ context.setMixInAnnotations(Instant.class, InstantMixIn.class);
+ context.setMixInAnnotations(LogEvent.class, LogEventJsonMixIn.class); // different ThreadContext handling
+ // Log4j Core classes: we do not want to bring in Jackson at runtime if we do not have to.
+ context.setMixInAnnotations(ExtendedStackTraceElement.class, ExtendedStackTraceElementMixIn.class);
+ context.setMixInAnnotations(ThrowableProxy.class, includeStacktrace
+ ? (stacktraceAsString ? ThrowableProxyWithStacktraceAsStringMixIn.class : ThrowableProxyMixIn.class)
+ : ThrowableProxyWithoutStacktraceMixIn.class);
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/layout/YamlConstants.java
----------------------------------------------------------------------
diff --git a/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/layout/YamlConstants.java b/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/layout/YamlConstants.java
new file mode 100644
index 0000000..856a201
--- /dev/null
+++ b/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/layout/YamlConstants.java
@@ -0,0 +1,35 @@
+/*
+ * 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.jackson.yaml.layout;
+
+public class YamlConstants {
+
+ static final String EVENT = "event";
+ static final String ELT_CAUSE = "cause";
+ static final String ELT_CONTEXT_MAP = "contextMap";
+ static final String ELT_CONTEXT_STACK = "contextStack";
+ static final String ELT_MARKER = "marker";
+ static final String ELT_PARENTS = "parents";
+ static final String ELT_SOURCE = "source";
+ static final String ELT_SUPPRESSED = "suppressed";
+ static final String ELT_THROWN = "thrown";
+ static final String ELT_MESSAGE = "message";
+ static final String ELT_EXTENDED_STACK_TRACE = "extendedStackTrace";
+ static final String ELT_NANO_TIME = "nanoTime";
+ static final String ELT_INSTANT = "instant";
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/layout/YamlJacksonFactory.java
----------------------------------------------------------------------
diff --git a/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/layout/YamlJacksonFactory.java b/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/layout/YamlJacksonFactory.java
new file mode 100644
index 0000000..e32041a
--- /dev/null
+++ b/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/layout/YamlJacksonFactory.java
@@ -0,0 +1,68 @@
+/*
+ * 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.jackson.yaml.layout;
+
+import org.apache.logging.log4j.jackson.AbstractJacksonFactory;
+import org.apache.logging.log4j.jackson.yaml.Log4jYamlObjectMapper;
+
+import com.fasterxml.jackson.core.PrettyPrinter;
+import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
+import com.fasterxml.jackson.core.util.MinimalPrettyPrinter;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+class YamlJacksonFactory extends AbstractJacksonFactory {
+
+ public YamlJacksonFactory(final boolean includeStacktrace, final boolean stacktraceAsString) {
+ super(includeStacktrace, stacktraceAsString);
+ }
+
+ @Override
+ protected String getPropertyNameForContextMap() {
+ return YamlConstants.ELT_CONTEXT_MAP;
+ }
+
+ @Override
+ protected String getPropertyNameForNanoTime() {
+ return YamlConstants.ELT_NANO_TIME;
+ }
+
+ @Override
+ protected String getPropertyNameForSource() {
+ return YamlConstants.ELT_SOURCE;
+ }
+
+ @Override
+ protected String getPropertyNameForStackTrace() {
+ return YamlConstants.ELT_EXTENDED_STACK_TRACE;
+ }
+
+ @Override
+ protected PrettyPrinter newCompactPrinter() {
+ return new MinimalPrettyPrinter();
+ }
+
+ @Override
+ protected ObjectMapper newObjectMapper() {
+ return new Log4jYamlObjectMapper(false, includeStacktrace, stacktraceAsString);
+ }
+
+ @Override
+ protected PrettyPrinter newPrettyPrinter() {
+ return new DefaultPrettyPrinter();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/layout/YamlLayout.java
----------------------------------------------------------------------
diff --git a/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/layout/YamlLayout.java b/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/layout/YamlLayout.java
new file mode 100644
index 0000000..418ef9a
--- /dev/null
+++ b/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/layout/YamlLayout.java
@@ -0,0 +1,239 @@
+/*
+ * 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.jackson.yaml.layout;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.DefaultConfiguration;
+import org.apache.logging.log4j.core.config.Node;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.core.util.KeyValuePair;
+import org.apache.logging.log4j.jackson.AbstractJacksonLayout;
+import org.apache.logging.log4j.util.Strings;
+
+import com.fasterxml.jackson.annotation.JsonAnyGetter;
+import com.fasterxml.jackson.annotation.JsonRootName;
+import com.fasterxml.jackson.annotation.JsonUnwrapped;
+
+/**
+ * Appends a series of YAML events as strings serialized as bytes.
+ *
+ * <h3>Encoding</h3>
+ * <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>
+ * <h3>Additional Fields</h3>
+ * <p>
+ * This property allows addition of custom fields into generated JSON.
+ * {@code <YamlLayout><KeyValuePair key="foo" value="bar"/></YamlLayout>} inserts {@code foo: "bar"} directly into YAML
+ * output. Supports Lookup expressions.
+ * </p>
+ */
+@Plugin(name = "YamlLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true)
+public final class YamlLayout extends AbstractJacksonLayout {
+
+ public static class Builder<B extends Builder<B>> extends AbstractJacksonLayout.Builder<B>
+ implements org.apache.logging.log4j.core.util.Builder<YamlLayout> {
+
+ public Builder() {
+ super();
+ setCharset(StandardCharsets.UTF_8);
+ }
+
+ @Override
+ public YamlLayout build() {
+ final String headerPattern = toStringOrNull(getHeader());
+ final String footerPattern = toStringOrNull(getFooter());
+ return new YamlLayout(getConfiguration(), isLocationInfo(), isProperties(), isComplete(), isCompact(),
+ getEventEol(), headerPattern, footerPattern, getCharset(), isIncludeStacktrace(),
+ isStacktraceAsString(), isIncludeNullDelimiter(), getAdditionalFields());
+ }
+ }
+
+ @JsonRootName(YamlConstants.EVENT)
+ public static class YamlLogEventWithAdditionalFields extends LogEventWithAdditionalFields {
+
+ public YamlLogEventWithAdditionalFields(final Object logEvent, final Map<String, String> additionalFields) {
+ super(logEvent, additionalFields);
+ }
+
+ @Override
+ @JsonAnyGetter
+ public Map<String, String> getAdditionalFields() {
+ return super.getAdditionalFields();
+ }
+
+ @Override
+ @JsonUnwrapped
+ public Object getLogEvent() {
+ return super.getLogEvent();
+ }
+
+ }
+
+ private static final String DEFAULT_FOOTER = Strings.EMPTY;
+
+ private static final String DEFAULT_HEADER = Strings.EMPTY;
+
+ static final String CONTENT_TYPE = "application/yaml";
+
+ /**
+ * Creates a YAML Layout using the default settings. Useful for testing.
+ *
+ * @return A YAML Layout.
+ */
+ public static AbstractJacksonLayout createDefaultLayout() {
+ return new YamlLayout(new DefaultConfiguration(), false, false, false, false, false, DEFAULT_HEADER,
+ DEFAULT_FOOTER, StandardCharsets.UTF_8, true, false, false, null);
+ }
+
+ /**
+ * Creates a YAML Layout.
+ *
+ * @param config
+ * The plugin configuration.
+ * @param locationInfo
+ * If "true", includes the location information in the generated YAML.
+ * @param properties
+ * If "true", includes the thread context map in the generated YAML.
+ * @param headerPattern
+ * The header pattern, defaults to {@code ""} if null.
+ * @param footerPattern
+ * The header pattern, defaults to {@code ""} if null.
+ * @param charset
+ * The character set to use, if {@code null}, uses "UTF-8".
+ * @param includeStacktrace
+ * If "true", includes the stacktrace of any Throwable in the generated YAML, defaults to "true".
+ * @return A YAML Layout.
+ *
+ * @deprecated Use {@link #newBuilder()} instead
+ */
+ @Deprecated
+ public static AbstractJacksonLayout createLayout(final Configuration config, final boolean locationInfo,
+ final boolean properties, final String headerPattern, final String footerPattern, final Charset charset,
+ final boolean includeStacktrace) {
+ return new YamlLayout(config, locationInfo, properties, false, false, true, headerPattern, footerPattern,
+ charset, includeStacktrace, false, false, null);
+ }
+
+ @PluginBuilderFactory
+ public static <B extends Builder<B>> B newBuilder() {
+ return new Builder<B>().asBuilder();
+ }
+
+ /**
+ * @deprecated Use {@link #newBuilder()} instead
+ */
+ @Deprecated
+ protected YamlLayout(final Configuration config, final boolean locationInfo, final boolean properties,
+ final boolean complete, final boolean compact, final boolean eventEol, final String headerPattern,
+ final String footerPattern, final Charset charset, final boolean includeStacktrace) {
+ super(config, new YamlJacksonFactory(includeStacktrace, false).newWriter(locationInfo, properties, compact),
+ charset, compact, complete, eventEol,
+ PatternLayout.newSerializerBuilder().setConfiguration(config).setPattern(headerPattern)
+ .setDefaultPattern(DEFAULT_HEADER).build(),
+ PatternLayout.newSerializerBuilder().setConfiguration(config).setPattern(footerPattern)
+ .setDefaultPattern(DEFAULT_FOOTER).build(),
+ false, null);
+ }
+
+ private YamlLayout(final Configuration config, final boolean locationInfo, final boolean properties,
+ final boolean complete, final boolean compact, final boolean eventEol, final String headerPattern,
+ final String footerPattern, final Charset charset, final boolean includeStacktrace,
+ final boolean stacktraceAsString, final boolean includeNullDelimiter,
+ final KeyValuePair[] additionalFields) {
+ super(config,
+ new YamlJacksonFactory(includeStacktrace, stacktraceAsString).newWriter(locationInfo, properties,
+ compact),
+ charset, compact, complete, eventEol,
+ PatternLayout.newSerializerBuilder().setConfiguration(config).setPattern(headerPattern)
+ .setDefaultPattern(DEFAULT_HEADER).build(),
+ PatternLayout.newSerializerBuilder().setConfiguration(config).setPattern(footerPattern)
+ .setDefaultPattern(DEFAULT_FOOTER).build(),
+ includeNullDelimiter, additionalFields);
+ }
+
+ @Override
+ protected LogEventWithAdditionalFields createLogEventWithAdditionalFields(final LogEvent event,
+ final Map<String, String> additionalFieldsMap) {
+ return new YamlLogEventWithAdditionalFields(event, additionalFieldsMap);
+ }
+
+ @Override
+ public Map<String, String> getContentFormat() {
+ final Map<String, String> result = new HashMap<>();
+ result.put("version", "3.0");
+ return result;
+ }
+
+ /**
+ * @return The content type.
+ */
+ @Override
+ public String getContentType() {
+ return CONTENT_TYPE + "; charset=" + this.getCharset();
+ }
+
+ /**
+ * Returns appropriate YAML footer.
+ *
+ * @return a byte array containing the footer, closing the YAML array.
+ */
+ @Override
+ public byte[] getFooter() {
+ if (!this.complete) {
+ return null;
+ }
+ final StringBuilder buf = new StringBuilder();
+ buf.append(this.eol);
+ final String str = serializeToString(getFooterSerializer());
+ if (str != null) {
+ buf.append(str);
+ }
+ buf.append(this.eol);
+ return getBytes(buf.toString());
+ }
+
+ /**
+ * Returns appropriate YAML header.
+ *
+ * @return a byte array containing the header, opening the YAML array.
+ */
+ @Override
+ public byte[] getHeader() {
+ if (!this.complete) {
+ return null;
+ }
+ final StringBuilder buf = new StringBuilder();
+ final String str = serializeToString(getHeaderSerializer());
+ if (str != null) {
+ buf.append(str);
+ }
+ buf.append(this.eol);
+ return getBytes(buf.toString());
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/parser/YamlLogEventParser.java
----------------------------------------------------------------------
diff --git a/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/parser/YamlLogEventParser.java b/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/parser/YamlLogEventParser.java
new file mode 100644
index 0000000..0ef4049
--- /dev/null
+++ b/log4j-layout-jackson-yaml/src/main/java/org/apache/logging/log4j/jackson/yaml/parser/YamlLogEventParser.java
@@ -0,0 +1,32 @@
+/*
+ * 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.jackson.yaml.parser;
+
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.parser.AbstractJacksonLogEventParser;
+import org.apache.logging.log4j.jackson.yaml.Log4jYamlObjectMapper;
+
+/**
+ * Parses the output from YamlLayout layout into instances of {@link LogEvent}.
+ */
+public class YamlLogEventParser extends AbstractJacksonLogEventParser {
+
+ public YamlLogEventParser() {
+ super(new Log4jYamlObjectMapper());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-yaml/src/site/manual/index.md
----------------------------------------------------------------------
diff --git a/log4j-layout-jackson-yaml/src/site/manual/index.md b/log4j-layout-jackson-yaml/src/site/manual/index.md
new file mode 100644
index 0000000..83112af
--- /dev/null
+++ b/log4j-layout-jackson-yaml/src/site/manual/index.md
@@ -0,0 +1,33 @@
+<!-- vim: set syn=markdown : -->
+<!--
+ 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.
+-->
+
+# Apache Log4j Layout for Jackson YAML module
+
+As of Log4j 3.0.0, the layout based on Jackson YAML has moved from the existing module logj-core to the new modules log4j-layout-jackson-yaml.
+
+## Requirements
+
+This module was introduced in Log4j 2.11.0 and requires Jackson.
+
+Some features may require optional
+[dependencies](../runtime-dependencies.html). These dependencies are specified in the
+documentation for those features.
+
+Some Log4j features require external dependencies.
+See the [Dependency Tree](dependencies.html#Dependency_Tree)
+for the exact list of JAR files needed for these features.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-yaml/src/site/site.xml
----------------------------------------------------------------------
diff --git a/log4j-layout-jackson-yaml/src/site/site.xml b/log4j-layout-jackson-yaml/src/site/site.xml
new file mode 100644
index 0000000..6d4cddc
--- /dev/null
+++ b/log4j-layout-jackson-yaml/src/site/site.xml
@@ -0,0 +1,52 @@
+<!--
+ 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.
+
+-->
+<project name="Log4j Core"
+ xmlns="http://maven.apache.org/DECORATION/1.4.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/DECORATION/1.4.0 http://maven.apache.org/xsd/decoration-1.4.0.xsd">
+ <body>
+ <links>
+ <item name="Apache" href="http://www.apache.org/" />
+ <item name="Logging Services" href="http://logging.apache.org/"/>
+ <item name="Log4j" href="../index.html"/>
+ </links>
+
+ <!-- Component-specific reports -->
+ <menu ref="reports"/>
+
+ <!-- Overall Project Info -->
+ <menu name="Log4j Project Information" img="icon-info-sign">
+ <item name="Dependencies" href="../dependencies.html" />
+ <item name="Dependency Convergence" href="../dependency-convergence.html" />
+ <item name="Dependency Management" href="../dependency-management.html" />
+ <item name="Project Team" href="../team-list.html" />
+ <item name="Mailing Lists" href="../mail-lists.html" />
+ <item name="Issue Tracking" href="../issue-tracking.html" />
+ <item name="Project License" href="../license.html" />
+ <item name="Source Repository" href="../source-repository.html" />
+ <item name="Project Summary" href="../project-summary.html" />
+ </menu>
+
+ <menu name="Log4j Project Reports" img="icon-cog">
+ <item name="Changes Report" href="../changes-report.html" />
+ <item name="JIRA Report" href="../jira-report.html" />
+ <item name="Surefire Report" href="../surefire-report.html" />
+ <item name="RAT Report" href="../rat-report.html" />
+ </menu>
+ </body>
+</project>