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>