http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/LevelMixInYamlTest.java ---------------------------------------------------------------------- diff --git a/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/LevelMixInYamlTest.java b/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/LevelMixInYamlTest.java new file mode 100644 index 0000000..86b9984 --- /dev/null +++ b/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/LevelMixInYamlTest.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.yaml; + +import org.apache.logging.log4j.categories.Layouts; +import org.apache.logging.log4j.jackson.LevelMixInTest; +import org.junit.experimental.categories.Category; + +import com.fasterxml.jackson.databind.ObjectMapper; + +@Category(Layouts.Yaml.class) +public class LevelMixInYamlTest extends LevelMixInTest { + + @Override + protected ObjectMapper newObjectMapper() { + return new Log4jYamlObjectMapper(); + } + +}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/MarkerMixInYamlTest.java ---------------------------------------------------------------------- diff --git a/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/MarkerMixInYamlTest.java b/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/MarkerMixInYamlTest.java new file mode 100644 index 0000000..98132cd --- /dev/null +++ b/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/MarkerMixInYamlTest.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.yaml; + +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.Yaml.class) +public class MarkerMixInYamlTest extends AbstractMarkerMixInTest { + + @Override + protected ObjectMapper newObjectMapper() { + return new Log4jYamlObjectMapper(); + } + +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/StackTraceElementYamlMixInTest.java ---------------------------------------------------------------------- diff --git a/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/StackTraceElementYamlMixInTest.java b/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/StackTraceElementYamlMixInTest.java new file mode 100644 index 0000000..9746655 --- /dev/null +++ b/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/StackTraceElementYamlMixInTest.java @@ -0,0 +1,83 @@ +/* + * 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 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; +import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; + +@Category(Layouts.Json.class) +public class StackTraceElementYamlMixInTest { + + 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 testFromYamlWithLog4jModule() throws Exception { + final ObjectMapper mapper = new YAMLMapper(); + final boolean encodeThreadContextAsList = false; + final SimpleModule module = new Log4jYamlModule(encodeThreadContextAsList, true, false); + module.addDeserializer(StackTraceElement.class, new Log4jStackTraceElementDeserializer()); + mapper.registerModule(module); + final StackTraceElement expected = new StackTraceElement("package.SomeClass", "someMethod", "SomeClass.java", + 123); + final StackTraceElement actual = mapper.readValue( + "---\nclass: package.SomeClass\nmethod: someMethod\nfile: SomeClass.java\nline: 123\n...", + StackTraceElement.class); + Assert.assertEquals(expected, actual); + } + + @Test + public void testFromYamlWithSimpleModule() throws Exception { + final ObjectMapper mapper = new YAMLMapper(); + 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 StackTraceElement actual = mapper.readValue( + "---\nclass: package.SomeClass\nmethod: someMethod\nfile: SomeClass.java\nline: 123\n...", + StackTraceElement.class); + Assert.assertEquals(expected, actual); + } + + @Test + public void testLog4jYamlObjectMapper() throws Exception { + this.roundtrip(new Log4jYamlObjectMapper()); + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/layout/ConcurrentLoggingWithYamlLayoutTest.java ---------------------------------------------------------------------- diff --git a/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/layout/ConcurrentLoggingWithYamlLayoutTest.java b/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/layout/ConcurrentLoggingWithYamlLayoutTest.java new file mode 100644 index 0000000..a0e821c --- /dev/null +++ b/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/layout/ConcurrentLoggingWithYamlLayoutTest.java @@ -0,0 +1,113 @@ +/* + * 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 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 ConcurrentLoggingWithYamlLayoutTest { + + 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-yaml-layout.xml"); + + private static final String PATH = "target/test-yaml-layout.log"; + + @AfterClass + public static void after() { + new File(PATH).delete(); + } + + @Test + public void testConcurrentLogging() throws Throwable { + final Logger log = context.getLogger(ConcurrentLoggingWithYamlLayoutTest.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()); + assertThat(lines.get(0), startsWith("---")); + // TODO more + } + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/layout/YamlLayoutTest.java ---------------------------------------------------------------------- diff --git a/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/layout/YamlLayoutTest.java b/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/layout/YamlLayoutTest.java new file mode 100644 index 0000000..b8e3ee6 --- /dev/null +++ b/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/layout/YamlLayoutTest.java @@ -0,0 +1,382 @@ +/* + * 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 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.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; + +import org.apache.logging.log4j.Level; +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.Configuration; +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.yaml.Log4jYamlObjectMapper; +import org.apache.logging.log4j.message.SimpleMessage; +import org.apache.logging.log4j.spi.AbstractLogger; +import org.apache.logging.log4j.test.appender.ListAppender; +import org.apache.logging.log4j.util.Strings; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +/** + * Tests the YamlLayout class. + */ +@Category(Layouts.Yaml.class) +public class YamlLayoutTest { + static ConfigurationFactory cf = new BasicConfigurationFactory(); + + @AfterClass + public static void cleanupClass() { + ConfigurationFactory.removeConfigurationFactory(cf); + ThreadContext.clearAll(); + } + + @BeforeClass + public static void setupClass() { + ThreadContext.clearAll(); + ConfigurationFactory.setConfigurationFactory(cf); + final LoggerContext ctx = LoggerContext.getContext(); + ctx.reconfigure(); + } + + LoggerContext ctx = LoggerContext.getContext(); + + Logger rootLogger = this.ctx.getRootLogger(); + + private void checkAt(final String expected, final int lineIndex, final List<String> list) { + final String trimedLine = list.get(lineIndex).trim(); + assertTrue("Incorrect line index " + lineIndex + ": " + Strings.dquote(trimedLine), + trimedLine.equals(expected)); + } + + private void checkContains(final String expected, final List<String> list) { + for (final String string : list) { + final String trimedLine = string.trim(); + if (trimedLine.equals(expected)) { + return; + } + } + Assert.fail("Cannot find " + expected + " in " + list); + } + + private void checkMapEntry(final String key, final String value, final boolean compact, final String str) { + // "name":"value" + //final String expected = String.format("- key: \"%s\"\n value: \"%s\"", key, value); + final String expected = String.format("%s: \"%s\"", key, value); + assertTrue("Cannot find " + expected + " in " + str, str.contains(expected)); + } + + private void checkProperty(final String key, final String value, final boolean compact, final String str, + final boolean isValue) { + final String propSep = this.toPropertySeparator(compact, isValue); + // {"key":"MDC.B","value":"B_Value"} + final String expected = String.format("%s%s\"%s\"", key, propSep, value); + assertTrue("Cannot find " + expected + " in " + str, str.contains(expected)); + } + + private void checkPropertyName(final String name, final boolean compact, final String str, final boolean isValue) { + final String propSep = this.toPropertySeparator(compact, isValue); + assertTrue(str, str.contains(name + propSep)); + } + + private void checkPropertyNameAbsent(final String name, final boolean compact, final String str, final boolean isValue) { + final String propSep = this.toPropertySeparator(compact, isValue); + assertFalse(str, str.contains(name + propSep)); + } + + private String prepareYAMLForStacktraceTests(final boolean stacktraceAsString) { + final Log4jLogEvent expected = LogEventFixtures.createLogEvent(); + // @formatter:off + final AbstractJacksonLayout layout = YamlLayout.newBuilder() + .setIncludeStacktrace(true) + .setStacktraceAsString(stacktraceAsString) + .build(); + // @formatter:off + return layout.toSerializable(expected); + } + + @Test + public void testAdditionalFields() throws Exception { + final AbstractJacksonLayout layout = YamlLayout.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\"")); + assertTrue(str, str.contains("KEY2: \"" + new JavaLookup().getRuntime() + "\"")); + } + + private void testAllFeatures(final boolean includeSource, final boolean compact, final boolean eventEol, + final boolean includeContext, final boolean contextMapAslist, final boolean includeStacktrace) throws Exception { + final Log4jLogEvent expected = LogEventFixtures.createLogEvent(); + final AbstractJacksonLayout layout = YamlLayout.newBuilder() + .setLocationInfo(includeSource) + .setProperties(includeContext) + .setIncludeStacktrace(includeStacktrace) + .setCharset(StandardCharsets.UTF_8) + .build(); + final String str = layout.toSerializable(expected); + // System.out.println(str); + // Just check for \n since \r might or might not be there. + assertEquals(str, !compact || eventEol, str.contains("\n")); + assertEquals(str, includeSource, str.contains("source")); + assertEquals(str, includeContext, str.contains("contextMap")); + final Log4jLogEvent actual = new Log4jYamlObjectMapper(contextMapAslist, includeStacktrace,false).readValue(str, Log4jLogEvent.class); + LogEventFixtures.assertEqualLogEvents(expected, actual, includeSource, includeContext, includeStacktrace); + if (includeContext) { + this.checkMapEntry("MDC.A", "A_Value", compact, str); + this.checkMapEntry("MDC.B", "B_Value", compact, str); + } + // + assertNull(actual.getThrown()); + // make sure the names we want are used + this.checkPropertyName("instant", compact, str, false); + this.checkPropertyName("thread", compact, str, true); // and not threadName + this.checkPropertyName("level", compact, str, true); + this.checkPropertyName("loggerName", compact, str, true); + this.checkPropertyName("marker", compact, str, false); + this.checkPropertyName("name", compact, str, true); + this.checkPropertyName("parents", compact, str, false); + this.checkPropertyName("message", compact, str, true); + this.checkPropertyName("thrown", compact, str, false); + this.checkPropertyName("cause", compact, str, false); + this.checkPropertyName("commonElementCount", compact, str, true); + this.checkPropertyName("localizedMessage", compact, str, true); + if (includeStacktrace) { + this.checkPropertyName("extendedStackTrace", compact, str, false); + this.checkPropertyName("class", compact, str, true); + this.checkPropertyName("method", compact, str, true); + this.checkPropertyName("file", compact, str, true); + this.checkPropertyName("line", compact, str, true); + this.checkPropertyName("exact", compact, str, true); + this.checkPropertyName("location", compact, str, true); + this.checkPropertyName("version", compact, str, true); + } else { + this.checkPropertyNameAbsent("extendedStackTrace", compact, str, false); + } + this.checkPropertyName("suppressed", compact, str, false); + this.checkPropertyName("loggerFqcn", compact, str, true); + this.checkPropertyName("endOfBatch", compact, str, true); + if (includeContext) { + this.checkPropertyName("contextMap", compact, str, false); + } else { + this.checkPropertyNameAbsent("contextMap", compact, str, false); + } + this.checkPropertyName("contextStack", compact, str, false); + if (includeSource) { + this.checkPropertyName("source", compact, str, false); + } else { + this.checkPropertyNameAbsent("source", compact, str, false); + } + // check some attrs + this.checkProperty("loggerFqcn", "f.q.c.n", compact, str, true); + this.checkProperty("loggerName", "a.B", compact, str, true); + } + + @Test + public void testContentType() { + final AbstractJacksonLayout layout = YamlLayout.createDefaultLayout(); + assertEquals("application/yaml; charset=UTF-8", layout.getContentType()); + } + + @Test + public void testDefaultCharset() { + final AbstractJacksonLayout layout = YamlLayout.createDefaultLayout(); + assertEquals(StandardCharsets.UTF_8, layout.getCharset()); + } + + @Test + public void testEscapeLayout() throws Exception { + final Map<String, Appender> appenders = this.rootLogger.getAppenders(); + for (final Appender appender : appenders.values()) { + this.rootLogger.removeAppender(appender); + } + final Configuration configuration = rootLogger.getContext().getConfiguration(); + // set up appender + final AbstractJacksonLayout layout = YamlLayout.newBuilder() + .setLocationInfo(true) + .setProperties(true) + .setIncludeStacktrace(true) + .setConfiguration(configuration) + .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("Here is a quote ' and then a double quote \""); + + appender.stop(); + + final List<String> list = appender.getMessages(); + + this.checkAt("---", 0, list); + this.checkContains("level: \"DEBUG\"", list); + this.checkContains("message: \"Here is a quote ' and then a double quote \\\"\"", list); + this.checkContains("loggerFqcn: \"" + AbstractLogger.class.getName() + "\"", list); + for (final Appender app : appenders.values()) { + this.rootLogger.addAppender(app); + } + } + + @Test + public void testExcludeStacktrace() throws Exception { + this.testAllFeatures(false, false, false, false, false, false); + } + + @Test + public void testIncludeNullDelimiterFalse() throws Exception { + final AbstractJacksonLayout layout = YamlLayout.newBuilder() + .setIncludeNullDelimiter(false) + .build(); + final String str = layout.toSerializable(LogEventFixtures.createLogEvent()); + assertFalse(str.endsWith("\0")); + } + + @Test + public void testIncludeNullDelimiterTrue() throws Exception { + final AbstractJacksonLayout layout = YamlLayout.newBuilder() + .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); + } + final Configuration configuration = rootLogger.getContext().getConfiguration(); + // set up appender + // Use [[ and ]] to test header and footer (instead of [ and ]) + final AbstractJacksonLayout layout = YamlLayout.createLayout(configuration, true, true, "[[", "]]", null, true); + 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")); + + appender.stop(); + + final List<String> list = appender.getMessages(); + + this.checkAt("---", 0, list); + this.checkContains("loggerFqcn: \"" + AbstractLogger.class.getName() + "\"", list); + this.checkContains("level: \"DEBUG\"", list); + this.checkContains("message: \"starting mdc pattern test\"", list); + for (final Appender app : appenders.values()) { + this.rootLogger.addAppender(app); + } + } + + @Test + public void testLayoutLoggerName() throws Exception { + final AbstractJacksonLayout layout = YamlLayout.newBuilder() + .setLocationInfo(false) + .setProperties(false) + .setIncludeStacktrace(true) + .setCharset(StandardCharsets.UTF_8) + .build(); + final Log4jLogEvent expected = 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(expected); + assertTrue(str, str.contains("loggerName: \"a.B\"")); + final Log4jLogEvent actual = new Log4jYamlObjectMapper(false, true, false).readValue(str, Log4jLogEvent.class); + assertEquals(expected.getLoggerName(), actual.getLoggerName()); + assertEquals(expected, actual); + } + + @Test + public void testLocationOffCompactOffMdcOff() throws Exception { + this.testAllFeatures(false, false, false, false, false, true); + } + + @Test + public void testLocationOnCompactOffEventEolOffMdcOn() throws Exception { + this.testAllFeatures(true, false, false, true, false, true); + } + + @Test + public void testStacktraceAsNonString() throws Exception { + final String str = prepareYAMLForStacktraceTests(false); + assertTrue(str, str.contains("extendedStackTrace:\n - ")); + } + + @Test + public void testStacktraceAsString() throws Exception { + final String str = prepareYAMLForStacktraceTests(true); + assertTrue(str, str.contains("extendedStackTrace: \"java.lang.NullPointerException")); + } + + private String toPropertySeparator(final boolean compact, final boolean value) { + return value ? ": " : ":"; + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/parser/YamlLogEventParserTest.java ---------------------------------------------------------------------- diff --git a/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/parser/YamlLogEventParserTest.java b/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/parser/YamlLogEventParserTest.java new file mode 100644 index 0000000..7e689e6 --- /dev/null +++ b/log4j-layout-jackson-yaml/src/test/java/org/apache/logging/log4j/jackson/yaml/parser/YamlLogEventParserTest.java @@ -0,0 +1,128 @@ +/* + * 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 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 YamlLogEventParserTest extends AbstractLogEventParserTest { + + private static final String YAML = "---\n" + + "timeMillis: 1493121664118\n" + + "instant:\n" + + " epochSecond: 1493121664\n" + + " nanoOfSecond: 118000000\n" + + "thread: \"main\"\n" + + "level: \"INFO\"\n" + + "loggerName: \"HelloWorld\"\n" + + "marker:\n" + + " name: \"child\"\n" + + " parents:\n" + + " - name: \"parent\"\n" + + " parents:\n" + + " - name: \"grandparent\"\n" + + "message: \"Hello, world!\"\n" + + "thrown:\n" + + " commonElementCount: 0\n" + + " message: \"error message\"\n" + + " name: \"java.lang.RuntimeException\"\n" + + " extendedStackTrace:\n" + + " - class: \"logtest.Main\"\n" + + " method: \"main\"\n" + + " file: \"Main.java\"\n" + + " line: 29\n" + + " exact: true\n" + + " location: \"classes/\"\n" + + " version: \"?\"\n" + + "contextStack:\n" + + "- \"one\"\n" + + "- \"two\"\n" + + "endOfBatch: false\n" + + "loggerFqcn: \"org.apache.logging.log4j.spi.AbstractLogger\"\n" + + "contextMap:\n" + + " bar: \"BAR\"\n" + + " foo: \"FOO\"\n" + + "threadId: 1\n" + + "threadPriority: 5\n" + + "source:\n" + + " class: \"logtest.Main\"\n" + + " method: \"main\"\n" + + " file: \"Main.java\"\n" + + " line: 29"; + + private YamlLogEventParser parser; + + @Before + public void setup() { + parser = new YamlLogEventParser(); + } + + @Test + public void testByteArray() throws ParseException { + final LogEvent logEvent = parser.parseFrom(YAML.getBytes(StandardCharsets.UTF_8)); + assertLogEvent(logEvent); + } + + @Test + public void testByteArrayOffsetLength() throws ParseException { + final byte[] bytes = ("abc" + YAML + "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("---\n"); + } + + @Test + public void testString() throws ParseException { + final LogEvent logEvent = parser.parseFrom(YAML); + assertLogEvent(logEvent); + } + + @Test(expected = ParseException.class) + public void testStringEmpty() throws ParseException { + parser.parseFrom(""); + } + + @Test + public void testStringIgnoreInvalidProperty() throws ParseException { + parser.parseFrom("---\nfoo: \"bar\"\n"); + } + + @Test(expected = ParseException.class) + public void testStringInvalidYaml() throws ParseException { + parser.parseFrom("foobar"); + } + + @Test(expected = ParseException.class) + public void testStringWrongPropertyType() throws ParseException { + parser.parseFrom("---\nthreadId: \"foobar\"\n"); + } + + @Test + public void testTimeMillisIgnored() throws ParseException { + parser.parseFrom("---\ntimeMillis: \"foobar\"\n"); + } + +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson-yaml/src/test/resources/log4j2-yaml-layout.xml ---------------------------------------------------------------------- diff --git a/log4j-layout-jackson-yaml/src/test/resources/log4j2-yaml-layout.xml b/log4j-layout-jackson-yaml/src/test/resources/log4j2-yaml-layout.xml new file mode 100644 index 0000000..f01d1e6 --- /dev/null +++ b/log4j-layout-jackson-yaml/src/test/resources/log4j2-yaml-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-yaml-layout.log" append="false"> + <YamlLayout 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/pom.xml ---------------------------------------------------------------------- diff --git a/log4j-layout-jackson/pom.xml b/log4j-layout-jackson/pom.xml new file mode 100644 index 0000000..ad61835 --- /dev/null +++ b/log4j-layout-jackson/pom.xml @@ -0,0 +1,214 @@ +<?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</artifactId> + <name>Apache Log4j Layout for Jackson</name> + <description> + Apache Log4j Layout for Jackson. + </description> + <properties> + <log4jParentDir>${basedir}/..</log4jParentDir> + <docLabel>Log4j Layout for Jackson Documentation</docLabel> + <projectDir>/log4j-layout-jackson</projectDir> + <module.name>org.apache.logging.log4j.jackson</module.name> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</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> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <executions> + <execution> + <id>default-jar</id> + <goals> + <goal>jar</goal> + </goals> + <configuration> + <archive> + <manifestFile>${manifestfile}</manifestFile> + <manifestEntries> + <Specification-Title>${project.name}</Specification-Title> + <Specification-Version>${project.version}</Specification-Version> + <Specification-Vendor>${project.organization.name}</Specification-Vendor> + <Implementation-Title>${project.name}</Implementation-Title> + <Implementation-Version>${project.version}</Implementation-Version> + <Implementation-Vendor>${project.organization.name}</Implementation-Vendor> + <Implementation-Vendor-Id>org.apache</Implementation-Vendor-Id> + <X-Compile-Source-JDK>${maven.compiler.source}</X-Compile-Source-JDK> + <X-Compile-Target-JDK>${maven.compiler.target}</X-Compile-Target-JDK> + </manifestEntries> + </archive> + </configuration> + </execution> + <execution> + <id>default</id> + <goals> + <goal>test-jar</goal> + </goals> + <configuration> + <archive> + <manifestFile>${manifestfile}</manifestFile> + <manifestEntries> + <Specification-Title>${project.name}</Specification-Title> + <Specification-Version>${project.version}</Specification-Version> + <Specification-Vendor>${project.organization.name}</Specification-Vendor> + <Implementation-Title>${project.name}</Implementation-Title> + <Implementation-Version>${project.version}</Implementation-Version> + <Implementation-Vendor>${project.organization.name}</Implementation-Vendor> + <Implementation-Vendor-Id>org.apache</Implementation-Vendor-Id> + <X-Compile-Source-JDK>${maven.compiler.source}</X-Compile-Source-JDK> + <X-Compile-Target-JDK>${maven.compiler.target}</X-Compile-Target-JDK> + </manifestEntries> + </archive> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <configuration> + <instructions> + <Fragment-Host>org.apache.logging.log4j.jackson</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/src/main/java/org/apache/logging/log4j/jackson/AbstractJacksonFactory.java ---------------------------------------------------------------------- diff --git a/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/AbstractJacksonFactory.java b/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/AbstractJacksonFactory.java new file mode 100644 index 0000000..ad4503c --- /dev/null +++ b/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/AbstractJacksonFactory.java @@ -0,0 +1,74 @@ +/* + * 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; + +import java.util.HashSet; +import java.util.Set; + +import org.apache.logging.log4j.core.impl.Log4jLogEvent; + +import com.fasterxml.jackson.core.PrettyPrinter; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; +import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; + +public abstract class AbstractJacksonFactory { + + protected final boolean includeStacktrace; + + protected final boolean stacktraceAsString; + public AbstractJacksonFactory(final boolean includeStacktrace, final boolean stacktraceAsString) { + super(); + this.includeStacktrace = includeStacktrace; + this.stacktraceAsString = stacktraceAsString; + } + + abstract protected String getPropertyNameForContextMap(); + + abstract protected String getPropertyNameForNanoTime(); + + abstract protected String getPropertyNameForSource(); + + abstract protected String getPropertyNameForStackTrace(); + + abstract protected PrettyPrinter newCompactPrinter(); + + abstract protected ObjectMapper newObjectMapper(); + + abstract protected PrettyPrinter newPrettyPrinter(); + + public ObjectWriter newWriter(final boolean locationInfo, final boolean properties, final boolean compact) { + final SimpleFilterProvider filters = new SimpleFilterProvider(); + final Set<String> except = new HashSet<>(4); + if (!locationInfo) { + except.add(this.getPropertyNameForSource()); + } + if (!properties) { + except.add(this.getPropertyNameForContextMap()); + } + if (!includeStacktrace) { + except.add(this.getPropertyNameForStackTrace()); + } + except.add(this.getPropertyNameForNanoTime()); + filters.addFilter(Log4jLogEvent.class.getName(), SimpleBeanPropertyFilter.serializeAllExcept(except)); + final ObjectWriter writer = this.newObjectMapper() + .writer(compact ? this.newCompactPrinter() : this.newPrettyPrinter()); + return writer.with(filters); + } + +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/AbstractJacksonLayout.java ---------------------------------------------------------------------- diff --git a/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/AbstractJacksonLayout.java b/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/AbstractJacksonLayout.java new file mode 100644 index 0000000..da66b9d --- /dev/null +++ b/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/AbstractJacksonLayout.java @@ -0,0 +1,351 @@ +/* + * 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; + +import java.io.IOException; +import java.io.Writer; +import java.nio.charset.Charset; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginElement; +import org.apache.logging.log4j.core.impl.MutableLogEvent; +import org.apache.logging.log4j.core.layout.AbstractStringLayout; +import org.apache.logging.log4j.core.lookup.StrSubstitutor; +import org.apache.logging.log4j.core.util.KeyValuePair; +import org.apache.logging.log4j.core.util.StringBuilderWriter; +import org.apache.logging.log4j.util.Strings; + +import com.fasterxml.jackson.databind.ObjectWriter; + +public abstract class AbstractJacksonLayout extends AbstractStringLayout { + + public static abstract class Builder<B extends Builder<B>> extends AbstractStringLayout.Builder<B> { + + @PluginBuilderAttribute + private boolean eventEol; + + @PluginBuilderAttribute + private boolean compact; + + @PluginBuilderAttribute + private boolean complete; + + @PluginBuilderAttribute + private boolean locationInfo; + + @PluginBuilderAttribute + private boolean properties; + + @PluginBuilderAttribute + private boolean includeStacktrace = true; + + @PluginBuilderAttribute + private boolean stacktraceAsString = false; + + @PluginBuilderAttribute + private boolean includeNullDelimiter = false; + + @PluginElement("AdditionalField") + private KeyValuePair[] additionalFields; + + public KeyValuePair[] getAdditionalFields() { + return additionalFields; + } + + public boolean getEventEol() { + return eventEol; + } + + public boolean isCompact() { + return compact; + } + + public boolean isComplete() { + return complete; + } + + public boolean isIncludeNullDelimiter() { + return includeNullDelimiter; + } + + /** + * If "true", includes the stack trace of any Throwable in the generated data, defaults to "true". + * + * @return If "true", includes the stack trace of any Throwable in the generated data, defaults to "true". + */ + public boolean isIncludeStacktrace() { + return includeStacktrace; + } + + public boolean isLocationInfo() { + return locationInfo; + } + + public boolean isProperties() { + return properties; + } + + public boolean isStacktraceAsString() { + return stacktraceAsString; + } + + /** + * Additional fields to set on each log event. + * + * @return this builder + */ + public B setAdditionalFields(final KeyValuePair[] additionalFields) { + this.additionalFields = additionalFields; + return asBuilder(); + } + + public B setCompact(final boolean compact) { + this.compact = compact; + return asBuilder(); + } + + public B setComplete(final boolean complete) { + this.complete = complete; + return asBuilder(); + } + + public B setEventEol(final boolean eventEol) { + this.eventEol = eventEol; + return asBuilder(); + } + + /** + * Whether to include NULL byte as delimiter after each event (optional, default to false). + * + * @return this builder + */ + public B setIncludeNullDelimiter(final boolean includeNullDelimiter) { + this.includeNullDelimiter = includeNullDelimiter; + return asBuilder(); + } + + /** + * If "true", includes the stacktrace of any Throwable in the generated JSON, defaults to "true". + * + * @param includeStacktrace + * If "true", includes the stacktrace of any Throwable in the generated JSON, defaults to "true". + * @return this builder + */ + public B setIncludeStacktrace(final boolean includeStacktrace) { + this.includeStacktrace = includeStacktrace; + return asBuilder(); + } + + public B setLocationInfo(final boolean locationInfo) { + this.locationInfo = locationInfo; + return asBuilder(); + } + + public B setProperties(final boolean properties) { + this.properties = properties; + return asBuilder(); + } + + /** + * Whether to format the stacktrace as a string, and not a nested object (optional, defaults to false). + * + * @return this builder + */ + public B setStacktraceAsString(final boolean stacktraceAsString) { + this.stacktraceAsString = stacktraceAsString; + return asBuilder(); + } + + protected String toStringOrNull(final byte[] header) { + return header == null ? null : new String(header, Charset.defaultCharset()); + } + } + /** + * Subclasses can annotate with Jackson annotations for JSON example. + */ + public static class LogEventWithAdditionalFields { + + private final Object logEvent; + private final Map<String, String> additionalFields; + + public LogEventWithAdditionalFields(final Object logEvent, final Map<String, String> additionalFields) { + this.logEvent = logEvent; + this.additionalFields = additionalFields; + } + + public Map<String, String> getAdditionalFields() { + return additionalFields; + } + + public Object getLogEvent() { + return logEvent; + } + } + + protected static class ResolvableKeyValuePair { + + final String key; + final String value; + final boolean valueNeedsLookup; + + ResolvableKeyValuePair(final KeyValuePair pair) { + this.key = pair.getKey(); + this.value = pair.getValue(); + this.valueNeedsLookup = AbstractJacksonLayout.valueNeedsLookup(this.value); + } + } + + protected static final String DEFAULT_EOL = "\r\n"; + protected static final String COMPACT_EOL = Strings.EMPTY; + private static LogEvent convertMutableToLog4jEvent(final LogEvent event) { + // TODO Jackson-based layouts have certain filters set up for Log4jLogEvent. + // TODO Need to set up the same filters for MutableLogEvent but don't know how... + // This is a workaround. + return event instanceof MutableLogEvent ? ((MutableLogEvent) event).createMemento() : event; + } + private static ResolvableKeyValuePair[] prepareAdditionalFields(final Configuration config, + final KeyValuePair[] additionalFields) { + if (additionalFields == null || additionalFields.length == 0) { + // No fields set + return new ResolvableKeyValuePair[0]; + } + + // Convert to specific class which already determines whether values needs lookup during serialization + final ResolvableKeyValuePair[] resolvableFields = new ResolvableKeyValuePair[additionalFields.length]; + + for (int i = 0; i < additionalFields.length; i++) { + final ResolvableKeyValuePair resolvable = resolvableFields[i] = new ResolvableKeyValuePair(additionalFields[i]); + + // Validate + if (config == null && resolvable.valueNeedsLookup) { + throw new IllegalArgumentException( + "configuration needs to be set when there are additional fields with variables"); + } + } + + return resolvableFields; + } + protected static boolean valueNeedsLookup(final String value) { + return value != null && value.contains("${"); + } + protected final String eol; + + protected final ObjectWriter objectWriter; + + protected final boolean compact; + + protected final boolean complete; + + protected final boolean includeNullDelimiter; + + protected final ResolvableKeyValuePair[] additionalFields; + + @Deprecated + protected AbstractJacksonLayout(final Configuration config, final ObjectWriter objectWriter, final Charset charset, + final boolean compact, final boolean complete, final boolean eventEol, final Serializer headerSerializer, + final Serializer footerSerializer) { + this(config, objectWriter, charset, compact, complete, eventEol, headerSerializer, footerSerializer, false); + } + + @Deprecated + protected AbstractJacksonLayout(final Configuration config, final ObjectWriter objectWriter, final Charset charset, + final boolean compact, final boolean complete, final boolean eventEol, final Serializer headerSerializer, + final Serializer footerSerializer, final boolean includeNullDelimiter) { + this(config, objectWriter, charset, compact, complete, eventEol, headerSerializer, footerSerializer, + includeNullDelimiter, null); + } + + protected AbstractJacksonLayout(final Configuration config, final ObjectWriter objectWriter, final Charset charset, + final boolean compact, final boolean complete, final boolean eventEol, final Serializer headerSerializer, + final Serializer footerSerializer, final boolean includeNullDelimiter, + final KeyValuePair[] additionalFields) { + super(config, charset, headerSerializer, footerSerializer); + this.objectWriter = objectWriter; + this.compact = compact; + this.complete = complete; + this.eol = compact && !eventEol ? COMPACT_EOL : DEFAULT_EOL; + this.includeNullDelimiter = includeNullDelimiter; + this.additionalFields = prepareAdditionalFields(config, additionalFields); + } + + protected LogEventWithAdditionalFields createLogEventWithAdditionalFields(final LogEvent event, + final Map<String, String> additionalFieldsMap) { + return new LogEventWithAdditionalFields(event, additionalFieldsMap); + } + + private Map<String, String> resolveAdditionalFields(final LogEvent logEvent) { + // Note: LinkedHashMap retains order + final Map<String, String> additionalFieldsMap = new LinkedHashMap<>(additionalFields.length); + final StrSubstitutor strSubstitutor = configuration.getStrSubstitutor(); + + // Go over each field + for (final ResolvableKeyValuePair pair : additionalFields) { + if (pair.valueNeedsLookup) { + // Resolve value + additionalFieldsMap.put(pair.key, strSubstitutor.replace(logEvent, pair.value)); + } else { + // Plain text value + additionalFieldsMap.put(pair.key, pair.value); + } + } + + return additionalFieldsMap; + } + + /** + * Formats a {@link org.apache.logging.log4j.core.LogEvent}. + * + * @param event + * The LogEvent. + * @return The XML representation of the LogEvent. + */ + @Override + public String toSerializable(final LogEvent event) { + try (final StringBuilderWriter writer = new StringBuilderWriter()) { + toSerializable(event, writer); + return writer.toString(); + } catch (final IOException e) { + // Should this be an ISE or IAE? + LOGGER.error(e); + return Strings.EMPTY; + } + } + + public void toSerializable(final LogEvent event, final Writer writer) throws IOException { + objectWriter.writeValue(writer, wrapLogEvent(convertMutableToLog4jEvent(event))); + writer.write(eol); + if (includeNullDelimiter) { + writer.write('\0'); + } + markEvent(); + } + + protected Object wrapLogEvent(final LogEvent event) { + if (additionalFields.length > 0) { + // Construct map for serialization - note that we are intentionally using original LogEvent + final Map<String, String> additionalFieldsMap = resolveAdditionalFields(event); + // This class combines LogEvent with AdditionalFields during serialization + return createLogEventWithAdditionalFields(event, additionalFieldsMap); + } + // No additional fields, return original object + return event; + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/AbstractLogEventMixIn.java ---------------------------------------------------------------------- diff --git a/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/AbstractLogEventMixIn.java b/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/AbstractLogEventMixIn.java new file mode 100644 index 0000000..43784d6 --- /dev/null +++ b/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/AbstractLogEventMixIn.java @@ -0,0 +1,84 @@ +/* + * 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; + +import java.util.Map; + +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.message.Message; + +import com.fasterxml.jackson.annotation.JsonFilter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +/** + * <pre> + * AbstractLogEventMixIn +*ââ AbstractLogEventXmlMixIn +*âââââ LogEventWithContextListXmlMixIn +*âââââ LogEventWithContextMapXmlMixIn +*ââ AbstractLogEventJsonMixIn +*âââââ LogEventWithContextListJsonMixIn +*âââââ LogEventWithContextMapJsonMixIn + * </pre> + */ +@JsonFilter(AbstractLogEventMixIn.JSON_FILTER_ID) +public abstract class AbstractLogEventMixIn implements LogEvent { + + public static final String ATTR_END_OF_BATCH = "endOfBatch"; + public static final String ATTR_LEVEL = "level"; + public static final String ATTR_LOGGER_FQCN = "loggerFqcn"; + public static final String ATTR_LOGGER_NAME = "loggerName"; + public static final String ATTR_MARKER = "marker"; + public static final String ATTR_THREAD = "thread"; + public static final String ATTR_THREAD_ID = "threadId"; + public static final String ATTR_THREAD_PRIORITY = "threadPriority"; + public static final String ELT_MESSAGE = "message"; + public static final String JSON_FILTER_ID = "org.apache.logging.log4j.core.impl.Log4jLogEvent"; + + private static final long serialVersionUID = 1L; + + @Deprecated + @Override + @JsonIgnore + public abstract Map<String, String> getContextMap(); + + @JsonSerialize(using = MessageSerializer.class) + @JsonDeserialize(using = SimpleMessageDeserializer.class) + @Override + public abstract Message getMessage(); + + @JsonIgnore + @Override + public abstract Throwable getThrown(); + + @JsonIgnore // ignore from 2.11.0 + @Override + public abstract long getTimeMillis(); + + @JsonIgnore + @Override + public abstract boolean isIncludeLocation(); + + @Override + public abstract void setEndOfBatch(boolean endOfBatch); + + @Override + public abstract void setIncludeLocation(boolean locationRequired); + +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/ContextDataAsEntryListDeserializer.java ---------------------------------------------------------------------- diff --git a/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/ContextDataAsEntryListDeserializer.java b/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/ContextDataAsEntryListDeserializer.java new file mode 100644 index 0000000..6efee4f --- /dev/null +++ b/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/ContextDataAsEntryListDeserializer.java @@ -0,0 +1,57 @@ +/* + * 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; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.apache.logging.log4j.core.impl.ContextDataFactory; +import org.apache.logging.log4j.util.StringMap; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +/** + * <p> + * <em>Consider this class private.</em> + * </p> + */ +public class ContextDataAsEntryListDeserializer extends StdDeserializer<StringMap> { + + private static final long serialVersionUID = 1L; + + ContextDataAsEntryListDeserializer() { + super(Map.class); + } + + @Override + public StringMap deserialize(final JsonParser jp, final DeserializationContext ctxt) + throws IOException, JsonProcessingException { + final List<MapEntry> list = jp.readValueAs(new TypeReference<List<MapEntry>>() { + // do nothing + }); + final StringMap contextData = ContextDataFactory.createContextData(); + for (final MapEntry mapEntry : list) { + contextData.putValue(mapEntry.getKey(), mapEntry.getValue()); + } + return contextData; + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/ContextDataAsEntryListSerializer.java ---------------------------------------------------------------------- diff --git a/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/ContextDataAsEntryListSerializer.java b/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/ContextDataAsEntryListSerializer.java new file mode 100644 index 0000000..d3c7551 --- /dev/null +++ b/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/ContextDataAsEntryListSerializer.java @@ -0,0 +1,67 @@ +/* + * 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; + +import java.io.IOException; +import java.util.Map; + +import org.apache.logging.log4j.util.BiConsumer; +import org.apache.logging.log4j.util.ReadOnlyStringMap; + +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +/** + * <p> + * <em>Consider this class private.</em> + * </p> + */ +public class ContextDataAsEntryListSerializer extends StdSerializer<ReadOnlyStringMap> { + + private static final long serialVersionUID = 1L; + + protected ContextDataAsEntryListSerializer() { + super(Map.class, false); + } + + protected MapEntry createMapEntry(final String key, final String value) { + return new MapEntry(key, value); + } + + protected MapEntry[] createMapEntryArray(final int size) { + return new MapEntry[size]; + } + + @Override + public void serialize(final ReadOnlyStringMap contextData, final JsonGenerator jgen, + final SerializerProvider provider) throws IOException, JsonGenerationException { + + final MapEntry[] pairs = createMapEntryArray(contextData.size()); + contextData.forEach(new BiConsumer<String, Object>() { + int i = 0; + + @Override + public void accept(final String key, final Object value) { + pairs[i++] = createMapEntry(key, String.valueOf(value)); + } + }); + jgen.writeObject(pairs); + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/ContextDataDeserializer.java ---------------------------------------------------------------------- diff --git a/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/ContextDataDeserializer.java b/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/ContextDataDeserializer.java new file mode 100644 index 0000000..9d6843a --- /dev/null +++ b/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/ContextDataDeserializer.java @@ -0,0 +1,65 @@ + +/* + * 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; + +import java.io.IOException; +import java.util.Map; + +import org.apache.logging.log4j.core.impl.ContextDataFactory; +import org.apache.logging.log4j.util.StringMap; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +/** + * <p> + * <em>Consider this class private.</em> + * </p> + */ +public class ContextDataDeserializer extends StdDeserializer<StringMap> { + + private static final long serialVersionUID = 1L; + + ContextDataDeserializer() { + super(Map.class); + } + + @Override + public StringMap deserialize(final JsonParser jp, final DeserializationContext ctxt) throws IOException, + JsonProcessingException { + + // Sanity check: verify that we got "Json Object": +// JsonToken tok = jp.nextToken(); +// if (tok != JsonToken.START_OBJECT) { +// throw new IOException("Expected data to start with an Object"); +// } + final StringMap contextData = ContextDataFactory.createContextData(); + // Iterate over object fields: + while (jp.nextToken() != JsonToken.END_OBJECT) { + final String fieldName = jp.getCurrentName(); + + // move to value + jp.nextToken(); + contextData.putValue(fieldName, jp.getText()); + } + return contextData; + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/ContextDataSerializer.java ---------------------------------------------------------------------- diff --git a/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/ContextDataSerializer.java b/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/ContextDataSerializer.java new file mode 100644 index 0000000..245e436 --- /dev/null +++ b/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/ContextDataSerializer.java @@ -0,0 +1,64 @@ +/* + * 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; + +import java.io.IOException; +import java.util.Map; + +import org.apache.logging.log4j.util.ReadOnlyStringMap; +import org.apache.logging.log4j.util.TriConsumer; + +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +/** + * <p> + * <em>Consider this class private.</em> + * </p> + */ +public class ContextDataSerializer extends StdSerializer<ReadOnlyStringMap> { + + private static final long serialVersionUID = 1L; + + private static final TriConsumer<String, Object, JsonGenerator> WRITE_STRING_FIELD_INTO = + new TriConsumer<String, Object, JsonGenerator>() { + + @Override + public void accept(final String key, final Object value, final JsonGenerator jsonGenerator) { + try { + jsonGenerator.writeStringField(key, String.valueOf(value)); + } catch (final Exception ex) { + throw new IllegalStateException("Problem with key " + key, ex); + } + } + }; + + protected ContextDataSerializer() { + super(Map.class, false); + } + + @Override + public void serialize(final ReadOnlyStringMap contextData, final JsonGenerator jgen, + final SerializerProvider provider) throws IOException, JsonGenerationException { + + jgen.writeStartObject(); + contextData.forEach(WRITE_STRING_FIELD_INTO, jgen); + jgen.writeEndObject(); + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/ExtendedStackTraceElementMixIn.java ---------------------------------------------------------------------- diff --git a/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/ExtendedStackTraceElementMixIn.java b/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/ExtendedStackTraceElementMixIn.java new file mode 100644 index 0000000..2642e42 --- /dev/null +++ b/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/ExtendedStackTraceElementMixIn.java @@ -0,0 +1,100 @@ +/* + * 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; + +import java.io.Serializable; + +import org.apache.logging.log4j.core.impl.ExtendedClassInfo; +import org.apache.logging.log4j.core.impl.ExtendedStackTraceElement; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + +/** + * Mix-in for {@link ExtendedStackTraceElement}. + */ +@JsonPropertyOrder({ + //@formatter:off + ExtendedStackTraceElementMixIn.ATTR_CLASS, + ExtendedStackTraceElementMixIn.ATTR_METHOD, + ExtendedStackTraceElementMixIn.ATTR_FILE, + ExtendedStackTraceElementMixIn.ATTR_LINE, + ExtendedStackTraceElementMixIn.ATTR_EXACT, + ExtendedStackTraceElementMixIn.ATTR_LOCATION, + ExtendedStackTraceElementMixIn.ATTR_VERSION + //@formatter:on +}) +public abstract class ExtendedStackTraceElementMixIn implements Serializable { + + protected static final String ATTR_CLASS = StackTraceElementConstants.ATTR_CLASS; + protected static final String ATTR_METHOD = StackTraceElementConstants.ATTR_METHOD; + protected static final String ATTR_FILE = StackTraceElementConstants.ATTR_FILE; + protected static final String ATTR_LINE = StackTraceElementConstants.ATTR_LINE; + protected static final String ATTR_EXACT = "exact"; + protected static final String ATTR_LOCATION = "location"; + protected static final String ATTR_VERSION = "version"; + + private static final long serialVersionUID = 1L; + + @JsonCreator + public ExtendedStackTraceElementMixIn( + // @formatter:off + @JsonProperty(ATTR_CLASS) final String declaringClass, + @JsonProperty(ATTR_METHOD) final String methodName, + @JsonProperty(ATTR_FILE) final String fileName, + @JsonProperty(ATTR_LINE) final int lineNumber, + @JsonProperty(ATTR_EXACT) final boolean exact, + @JsonProperty(ATTR_LOCATION) final String location, + @JsonProperty(ATTR_VERSION) final String version + // @formatter:on + ) { + // empty + } + + @JsonProperty(ATTR_CLASS) + public abstract String getClassName(); + + @JsonProperty(ATTR_EXACT) + public abstract boolean getExact(); + + @JsonIgnore + public abstract ExtendedClassInfo getExtraClassInfo(); + + @JsonProperty(ATTR_FILE) + public abstract String getFileName(); + + @JsonProperty(ATTR_LINE) + public abstract int getLineNumber(); + + @JsonProperty(ATTR_LOCATION) + public abstract String getLocation(); + + @JsonProperty(ATTR_METHOD) + public abstract String getMethodName(); + + @JsonIgnore + abstract StackTraceElement getStackTraceElement(); + + @JsonProperty(ATTR_VERSION) + public abstract String getVersion(); + + @JsonIgnore + public abstract boolean isNativeMethod(); + +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0eb5212e/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/InstantMixIn.java ---------------------------------------------------------------------- diff --git a/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/InstantMixIn.java b/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/InstantMixIn.java new file mode 100644 index 0000000..16a3bb5 --- /dev/null +++ b/log4j-layout-jackson/src/main/java/org/apache/logging/log4j/jackson/InstantMixIn.java @@ -0,0 +1,55 @@ +/* + * 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; + +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.core.time.Instant; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Jackson mix-in for {@link Instant}. + * <p> + * <em>Consider this class private.</em> + * </p> + * + * @see Marker + */ +@JsonIgnoreProperties({ "epochMillisecond", "nanoOfMillisecond" }) +public abstract class InstantMixIn { + + protected static final String ATTR_NANO_OF_SECOND = "nanoOfSecond"; + protected static final String ATTR_EPOCH_SECOND = "epochSecond"; + + @JsonCreator + protected InstantMixIn( + // @formatter:off + @JsonProperty(ATTR_EPOCH_SECOND) final long epochSecond, + @JsonProperty(ATTR_NANO_OF_SECOND) final int nanoOfSecond) + // @formatter:on + { + // empty + } + + @JsonProperty(ATTR_EPOCH_SECOND) + public abstract long getEpochSecond(); + + @JsonProperty(ATTR_NANO_OF_SECOND) + public abstract int getNanoOfSecond(); +}
