From fa69d2a388b9ba67abfea00aa23afddfd0aa5cf7 Mon Sep 17 00:00:00 2001 From: "Spindler, Justin" Date: Mon, 4 Apr 2022 17:04:28 -0400 Subject: [PATCH 1/8] Configurable exception pattern for log4j2 --- .../co/elastic/logging/log4j2/EcsLayout.java | 47 ++++++++++++++++--- .../src/test/resources/log4j2-test.xml | 2 +- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/log4j2-ecs-layout/src/main/java/co/elastic/logging/log4j2/EcsLayout.java b/log4j2-ecs-layout/src/main/java/co/elastic/logging/log4j2/EcsLayout.java index 5c963d1a..a3e996f8 100644 --- a/log4j2-ecs-layout/src/main/java/co/elastic/logging/log4j2/EcsLayout.java +++ b/log4j2-ecs-layout/src/main/java/co/elastic/logging/log4j2/EcsLayout.java @@ -75,10 +75,11 @@ public class EcsLayout extends AbstractStringLayout { private final String eventDataset; private final boolean includeMarkers; private final boolean includeOrigin; + private final PatternFormatter[] exceptionPatternFormatter; private final ConcurrentMap, Boolean> supportsJson = new ConcurrentHashMap, Boolean>(); private EcsLayout(Configuration config, String serviceName, String serviceNodeName, String eventDataset, boolean includeMarkers, - KeyValuePair[] additionalFields, boolean includeOrigin, boolean stackTraceAsArray) { + KeyValuePair[] additionalFields, boolean includeOrigin, String exceptionPattern, boolean stackTraceAsArray) { super(config, UTF_8, null, null); this.serviceName = serviceName; this.serviceNodeName = serviceNodeName; @@ -96,6 +97,14 @@ private EcsLayout(Configuration config, String serviceName, String serviceNodeNa .toArray(new PatternFormatter[0]); } } + + if (exceptionPattern != null && !exceptionPattern.isEmpty()) { + exceptionPatternFormatter = PatternLayout.createPatternParser(config) + .parse(exceptionPattern) + .toArray(new PatternFormatter[0]); + } else { + exceptionPatternFormatter = null; + } } @PluginBuilderFactory @@ -140,7 +149,7 @@ private StringBuilder toText(LogEvent event, StringBuilder builder, boolean gcFr if (includeOrigin) { EcsJsonSerializer.serializeOrigin(builder, event.getSource()); } - EcsJsonSerializer.serializeException(builder, event.getThrown(), stackTraceAsArray); + serializeException(builder, event); EcsJsonSerializer.serializeObjectEnd(builder); return builder; } @@ -325,6 +334,20 @@ private boolean supportsJson(MultiformatMessage message) { return supportsJson; } + private void serializeException(StringBuilder messageBuffer, LogEvent event) { + Throwable thrown = event.getThrown(); + if (thrown != null) { + if (exceptionPatternFormatter != null) { + StringBuilder builder = EcsJsonSerializer.getMessageStringBuilder(); + formatPattern(event, exceptionPatternFormatter, builder); + String stackTrace = builder.toString(); + EcsJsonSerializer.serializeException(messageBuffer, thrown.getClass().getName(), thrown.getMessage(), stackTrace, stackTraceAsArray); + } else { + EcsJsonSerializer.serializeException(messageBuffer, thrown, stackTraceAsArray); + } + } + } + public static class Builder implements org.apache.logging.log4j.core.util.Builder { @PluginConfiguration @@ -337,6 +360,8 @@ public static class Builder implements org.apache.logging.log4j.core.util.Builde private String eventDataset; @PluginBuilderAttribute("includeMarkers") private boolean includeMarkers = false; + @PluginBuilderAttribute("exceptionPattern") + private String exceptionPattern; @PluginBuilderAttribute("stackTraceAsArray") private boolean stackTraceAsArray = false; @PluginElement("AdditionalField") @@ -380,6 +405,14 @@ public boolean isIncludeOrigin() { return includeOrigin; } + public boolean isStackTraceAsArray() { + return stackTraceAsArray; + } + + public String getExceptionPattern() { + return exceptionPattern; + } + /** * Additional fields to set on each log event. * @@ -420,14 +453,14 @@ public EcsLayout.Builder setStackTraceAsArray(boolean stackTraceAsArray) { return this; } + public void setExceptionPattern(String exceptionPattern) { + this.exceptionPattern = exceptionPattern; + } + @Override public EcsLayout build() { return new EcsLayout(getConfiguration(), serviceName, serviceNodeName, EcsJsonSerializer.computeEventDataset(eventDataset, serviceName), - includeMarkers, additionalFields, includeOrigin, stackTraceAsArray); - } - - public boolean isStackTraceAsArray() { - return stackTraceAsArray; + includeMarkers, additionalFields, includeOrigin, exceptionPattern, stackTraceAsArray); } } } diff --git a/log4j2-ecs-layout/src/test/resources/log4j2-test.xml b/log4j2-ecs-layout/src/test/resources/log4j2-test.xml index 967c0a32..3145d32c 100644 --- a/log4j2-ecs-layout/src/test/resources/log4j2-test.xml +++ b/log4j2-ecs-layout/src/test/resources/log4j2-test.xml @@ -6,7 +6,7 @@ + eventDataset="testdataset" exceptionPattern="%ex{4}"> From 11cad95b190c9c2dd1f1173820528c2f611c9cdb Mon Sep 17 00:00:00 2001 From: "Spindler, Justin" Date: Mon, 4 Apr 2022 17:19:06 -0400 Subject: [PATCH 2/8] Add unit test to confirm exception pattern with specific depth --- .../main/java/co/elastic/logging/log4j2/EcsLayout.java | 3 ++- .../logging/log4j2/CurrentLog4j2EcsLayoutTest.java | 10 ++++++++++ .../co/elastic/logging/log4j2/Log4j2EcsLayoutTest.java | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/log4j2-ecs-layout/src/main/java/co/elastic/logging/log4j2/EcsLayout.java b/log4j2-ecs-layout/src/main/java/co/elastic/logging/log4j2/EcsLayout.java index a3e996f8..d4002baf 100644 --- a/log4j2-ecs-layout/src/main/java/co/elastic/logging/log4j2/EcsLayout.java +++ b/log4j2-ecs-layout/src/main/java/co/elastic/logging/log4j2/EcsLayout.java @@ -453,8 +453,9 @@ public EcsLayout.Builder setStackTraceAsArray(boolean stackTraceAsArray) { return this; } - public void setExceptionPattern(String exceptionPattern) { + public EcsLayout.Builder setExceptionPattern(String exceptionPattern) { this.exceptionPattern = exceptionPattern; + return this; } @Override diff --git a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/CurrentLog4j2EcsLayoutTest.java b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/CurrentLog4j2EcsLayoutTest.java index 6e7fb96d..ce9c5be4 100644 --- a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/CurrentLog4j2EcsLayoutTest.java +++ b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/CurrentLog4j2EcsLayoutTest.java @@ -56,6 +56,16 @@ void testJsonMessageObject() throws Exception { assertThat(getLastLogLine().get("baz").booleanValue()).isEqualTo(true); } + @Test + void testExceptionPattern() throws Exception { + error("test", new RuntimeException("test")); + JsonNode log = getAndValidateLastLogLine(); + assertThat(log.get("log.level").textValue()).isIn("ERROR", "SEVERE"); + assertThat(log.get("error.message").textValue()).isEqualTo("test"); + assertThat(log.get("error.type").textValue()).isEqualTo(RuntimeException.class.getName()); + assertThat(log.get("error.stack_trace").textValue().split("\\n").length).isEqualTo(4L); + } + public static class TestClass { String foo; int bar; diff --git a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutTest.java b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutTest.java index 2b2b09e5..97462d38 100644 --- a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutTest.java +++ b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutTest.java @@ -71,6 +71,7 @@ void setUp() { .setIncludeMarkers(true) .setIncludeOrigin(true) .setEventDataset("testdataset") + .setExceptionPattern("%ex{4}") .setAdditionalFields(new KeyValuePair[]{ new KeyValuePair("cluster.uuid", "9fe9134b-20b0-465e-acf9-8cc09ac9053b"), new KeyValuePair("node.id", "${node.id}"), From d24dc0984fece78197d4f18ef65dbbc5a5abea32 Mon Sep 17 00:00:00 2001 From: "Spindler, Justin" Date: Mon, 4 Apr 2022 18:27:04 -0400 Subject: [PATCH 3/8] Test stack frame package filters in log4j2 throwable converter --- .../co/elastic/logging/log4j2/CurrentLog4j2EcsLayoutTest.java | 1 + .../java/co/elastic/logging/log4j2/Log4j2EcsLayoutTest.java | 2 +- log4j2-ecs-layout/src/test/resources/log4j2-test.xml | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/CurrentLog4j2EcsLayoutTest.java b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/CurrentLog4j2EcsLayoutTest.java index ce9c5be4..bb3e9089 100644 --- a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/CurrentLog4j2EcsLayoutTest.java +++ b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/CurrentLog4j2EcsLayoutTest.java @@ -64,6 +64,7 @@ void testExceptionPattern() throws Exception { assertThat(log.get("error.message").textValue()).isEqualTo("test"); assertThat(log.get("error.type").textValue()).isEqualTo(RuntimeException.class.getName()); assertThat(log.get("error.stack_trace").textValue().split("\\n").length).isEqualTo(4L); + assertThat(log.get("error.stack_trace").textValue()).doesNotContain("co.elastic.logging.log4j2"); } public static class TestClass { diff --git a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutTest.java b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutTest.java index 97462d38..6d7500e8 100644 --- a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutTest.java +++ b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutTest.java @@ -71,7 +71,7 @@ void setUp() { .setIncludeMarkers(true) .setIncludeOrigin(true) .setEventDataset("testdataset") - .setExceptionPattern("%ex{4}") + .setExceptionPattern("%rEx{4,filters(co.elastic.logging.log4j2)}") .setAdditionalFields(new KeyValuePair[]{ new KeyValuePair("cluster.uuid", "9fe9134b-20b0-465e-acf9-8cc09ac9053b"), new KeyValuePair("node.id", "${node.id}"), diff --git a/log4j2-ecs-layout/src/test/resources/log4j2-test.xml b/log4j2-ecs-layout/src/test/resources/log4j2-test.xml index 3145d32c..573263f5 100644 --- a/log4j2-ecs-layout/src/test/resources/log4j2-test.xml +++ b/log4j2-ecs-layout/src/test/resources/log4j2-test.xml @@ -6,7 +6,7 @@ + eventDataset="testdataset" exceptionPattern="%rEx{4,filters(co.elastic.logging.log4j2)}"> From 1d9c5866a67d50b6c28251da38ca250923614e55 Mon Sep 17 00:00:00 2001 From: "Spindler, Justin" Date: Tue, 26 Apr 2022 13:51:52 -0400 Subject: [PATCH 4/8] Add unit test for rendering stack trace as array --- .../EcsLayoutWithStackTraceAsArrayTest.java | 41 +++++++++++++++++++ .../logging/log4j2/Log4j2EcsLayoutTest.java | 23 +++++++---- 2 files changed, 55 insertions(+), 9 deletions(-) create mode 100644 log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithStackTraceAsArrayTest.java diff --git a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithStackTraceAsArrayTest.java b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithStackTraceAsArrayTest.java new file mode 100644 index 00000000..a2bc7653 --- /dev/null +++ b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithStackTraceAsArrayTest.java @@ -0,0 +1,41 @@ +package co.elastic.logging.log4j2; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import org.apache.logging.log4j.core.LoggerContext; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class EcsLayoutWithStackTraceAsArrayTest extends Log4j2EcsLayoutTest { + + @Override + protected EcsLayout.Builder configureLayout(LoggerContext context) { + return super.configureLayout(context) + .setStackTraceAsArray(true); + } + + @Test + void testLogException() throws Exception { + error("test", new RuntimeException("test")); + JsonNode log = getLastLogLine(); + assertThat(log.get("log.level").textValue()).isIn("ERROR", "SEVERE"); + assertThat(log.get("error.message").textValue()).isEqualTo("test"); + assertThat(log.get("error.type").textValue()).isEqualTo(RuntimeException.class.getName()); + assertThat(log.get("error.stack_trace").isArray()).isTrue(); + ArrayNode arrayNode = (ArrayNode) log.get("error.stack_trace"); + assertThat(arrayNode.size()).isEqualTo(4); + assertThat(arrayNode.get(0).textValue()).isEqualTo("java.lang.RuntimeException: test"); + for (JsonNode element : arrayNode) { + assertThat(element.textValue()).doesNotContain("co.elastic.logging.log4j2"); + } + } + + @Test + void testLogExceptionNullMessage() throws Exception { + error("test", new RuntimeException()); + JsonNode log = getLastLogLine();; + assertThat(log.get("error.type").textValue()).isEqualTo(RuntimeException.class.getName()); + assertThat(log.get("error.message")).isNull(); + } +} diff --git a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutTest.java b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutTest.java index 6d7500e8..f4aed0ce 100644 --- a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutTest.java +++ b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutTest.java @@ -29,6 +29,7 @@ import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.BasicConfigurationFactory; 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.util.KeyValuePair; import org.apache.logging.log4j.test.appender.ListAppender; @@ -64,8 +65,18 @@ void setUp() { for (final Appender appender : root.getAppenders().values()) { root.removeAppender(appender); } - EcsLayout ecsLayout = EcsLayout.newBuilder() - .setConfiguration(ctx.getConfiguration()) + EcsLayout ecsLayout = configureLayout(ctx) + .build(); + + listAppender = new ListAppender("ecs", null, ecsLayout, false, false); + listAppender.start(); + root.addAppender(listAppender); + root.setLevel(Level.DEBUG); + } + + protected EcsLayout.Builder configureLayout(LoggerContext context) { + return EcsLayout.newBuilder() + .setConfiguration(context.getConfiguration()) .setServiceName("test") .setServiceNodeName("test-node") .setIncludeMarkers(true) @@ -81,13 +92,7 @@ void setUp() { new KeyValuePair("emptyPattern", "%notEmpty{%invalidPattern}"), new KeyValuePair("key1", "value1"), new KeyValuePair("key2", "value2"), - }) - .build(); - - listAppender = new ListAppender("ecs", null, ecsLayout, false, false); - listAppender.start(); - root.addAppender(listAppender); - root.setLevel(Level.DEBUG); + }); } @AfterEach From c0a4a71c038c73b03154bdb8d928eee8632ee478 Mon Sep 17 00:00:00 2001 From: "Spindler, Justin" Date: Tue, 26 Apr 2022 14:16:04 -0400 Subject: [PATCH 5/8] Split exception pattern test out --- .../log4j2/CurrentLog4j2EcsLayoutTest.java | 11 ----- .../CustomExceptionPatternConverter.java | 45 +++++++++++++++++++ .../EcsLayoutWithExceptionPatternTest.java | 33 ++++++++++++++ .../EcsLayoutWithStackTraceAsArrayTest.java | 12 +++-- .../Log4j2EcsLayoutIntegrationTest.java | 3 ++ .../logging/log4j2/Log4j2EcsLayoutTest.java | 3 +- .../src/test/resources/log4j2-test.xml | 2 +- 7 files changed, 91 insertions(+), 18 deletions(-) create mode 100644 log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/CustomExceptionPatternConverter.java create mode 100644 log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithExceptionPatternTest.java diff --git a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/CurrentLog4j2EcsLayoutTest.java b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/CurrentLog4j2EcsLayoutTest.java index bb3e9089..6e7fb96d 100644 --- a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/CurrentLog4j2EcsLayoutTest.java +++ b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/CurrentLog4j2EcsLayoutTest.java @@ -56,17 +56,6 @@ void testJsonMessageObject() throws Exception { assertThat(getLastLogLine().get("baz").booleanValue()).isEqualTo(true); } - @Test - void testExceptionPattern() throws Exception { - error("test", new RuntimeException("test")); - JsonNode log = getAndValidateLastLogLine(); - assertThat(log.get("log.level").textValue()).isIn("ERROR", "SEVERE"); - assertThat(log.get("error.message").textValue()).isEqualTo("test"); - assertThat(log.get("error.type").textValue()).isEqualTo(RuntimeException.class.getName()); - assertThat(log.get("error.stack_trace").textValue().split("\\n").length).isEqualTo(4L); - assertThat(log.get("error.stack_trace").textValue()).doesNotContain("co.elastic.logging.log4j2"); - } - public static class TestClass { String foo; int bar; diff --git a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/CustomExceptionPatternConverter.java b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/CustomExceptionPatternConverter.java new file mode 100644 index 00000000..12be9222 --- /dev/null +++ b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/CustomExceptionPatternConverter.java @@ -0,0 +1,45 @@ +package co.elastic.logging.log4j2; + +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.pattern.ConverterKeys; +import org.apache.logging.log4j.core.pattern.LogEventPatternConverter; +import org.apache.logging.log4j.core.pattern.PatternConverter; + +@Plugin(category = PatternConverter.CATEGORY, name = "CustomExceptionPatternConverter") +@ConverterKeys({"cEx"}) +public class CustomExceptionPatternConverter extends LogEventPatternConverter { + + public CustomExceptionPatternConverter(final String[] options) { + super("Custom", "custom"); + } + + public static CustomExceptionPatternConverter newInstance(final String[] options) { + return new CustomExceptionPatternConverter(options); + } + + + @Override + public void format(LogEvent event, StringBuilder toAppendTo) { + Throwable thrown = event.getThrown(); + if (thrown != null) { + String message = thrown.getMessage(); + if (message == null || message.isEmpty()) { + toAppendTo.append(thrown.getClass().getName()) + .append('\n'); + } else { + toAppendTo.append(thrown.getClass().getName()) + .append(": ") + .append(message) + .append('\n'); + } + + toAppendTo.append("STACK_TRACE!"); + } + } + + @Override + public boolean handlesThrowable() { + return true; + } +} diff --git a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithExceptionPatternTest.java b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithExceptionPatternTest.java new file mode 100644 index 00000000..a0b8a6e5 --- /dev/null +++ b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithExceptionPatternTest.java @@ -0,0 +1,33 @@ +package co.elastic.logging.log4j2; + +import com.fasterxml.jackson.databind.JsonNode; +import org.apache.logging.log4j.core.LoggerContext; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class EcsLayoutWithExceptionPatternTest extends Log4j2EcsLayoutTest { + @Override + protected EcsLayout.Builder configureLayout(LoggerContext context) { + return super.configureLayout(context) + .setExceptionPattern("%cEx"); + } + + @Test + void testLogException() throws Exception { + error("test", new RuntimeException("test")); + JsonNode log = getLastLogLine(); + assertThat(log.get("log.level").textValue()).isIn("ERROR", "SEVERE"); + assertThat(log.get("error.message").textValue()).isEqualTo("test"); + assertThat(log.get("error.type").textValue()).isEqualTo(RuntimeException.class.getName()); + assertThat(log.get("error.stack_trace").textValue()).isEqualTo("java.lang.RuntimeException: test\nSTACK_TRACE!"); + } + + @Test + void testLogExceptionNullMessage() throws Exception { + error("test", new RuntimeException()); + JsonNode log = getLastLogLine();; + assertThat(log.get("error.type").textValue()).isEqualTo(RuntimeException.class.getName()); + assertThat(log.get("error.message")).isNull(); + } +} diff --git a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithStackTraceAsArrayTest.java b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithStackTraceAsArrayTest.java index a2bc7653..0eb335cb 100644 --- a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithStackTraceAsArrayTest.java +++ b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithStackTraceAsArrayTest.java @@ -12,6 +12,7 @@ public class EcsLayoutWithStackTraceAsArrayTest extends Log4j2EcsLayoutTest { @Override protected EcsLayout.Builder configureLayout(LoggerContext context) { return super.configureLayout(context) + .setExceptionPattern("%cEx") .setStackTraceAsArray(true); } @@ -24,11 +25,9 @@ void testLogException() throws Exception { assertThat(log.get("error.type").textValue()).isEqualTo(RuntimeException.class.getName()); assertThat(log.get("error.stack_trace").isArray()).isTrue(); ArrayNode arrayNode = (ArrayNode) log.get("error.stack_trace"); - assertThat(arrayNode.size()).isEqualTo(4); + assertThat(arrayNode.size()).isEqualTo(2); assertThat(arrayNode.get(0).textValue()).isEqualTo("java.lang.RuntimeException: test"); - for (JsonNode element : arrayNode) { - assertThat(element.textValue()).doesNotContain("co.elastic.logging.log4j2"); - } + assertThat(arrayNode.get(1).textValue()).isEqualTo("STACK_TRACE!"); } @Test @@ -37,5 +36,10 @@ void testLogExceptionNullMessage() throws Exception { JsonNode log = getLastLogLine();; assertThat(log.get("error.type").textValue()).isEqualTo(RuntimeException.class.getName()); assertThat(log.get("error.message")).isNull(); + assertThat(log.get("error.stack_trace").isArray()).isTrue(); + ArrayNode arrayNode = (ArrayNode) log.get("error.stack_trace"); + assertThat(arrayNode.size()).isEqualTo(2); + assertThat(arrayNode.get(0).textValue()).isEqualTo("java.lang.RuntimeException"); + assertThat(arrayNode.get(1).textValue()).isEqualTo("STACK_TRACE!"); } } diff --git a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutIntegrationTest.java b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutIntegrationTest.java index 5e410c1d..24fa667b 100644 --- a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutIntegrationTest.java +++ b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutIntegrationTest.java @@ -29,9 +29,12 @@ import org.apache.logging.log4j.test.appender.ListAppender; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.IOException; +import static org.assertj.core.api.Assertions.assertThat; + class Log4j2EcsLayoutIntegrationTest extends AbstractLog4j2EcsLayoutTest { @BeforeEach void setUp() { diff --git a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutTest.java b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutTest.java index f4aed0ce..9b38820c 100644 --- a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutTest.java +++ b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutTest.java @@ -29,7 +29,6 @@ import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.BasicConfigurationFactory; 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.util.KeyValuePair; import org.apache.logging.log4j.test.appender.ListAppender; @@ -82,7 +81,7 @@ protected EcsLayout.Builder configureLayout(LoggerContext context) { .setIncludeMarkers(true) .setIncludeOrigin(true) .setEventDataset("testdataset") - .setExceptionPattern("%rEx{4,filters(co.elastic.logging.log4j2)}") + .setExceptionPattern("%ex{4}") .setAdditionalFields(new KeyValuePair[]{ new KeyValuePair("cluster.uuid", "9fe9134b-20b0-465e-acf9-8cc09ac9053b"), new KeyValuePair("node.id", "${node.id}"), diff --git a/log4j2-ecs-layout/src/test/resources/log4j2-test.xml b/log4j2-ecs-layout/src/test/resources/log4j2-test.xml index 573263f5..3145d32c 100644 --- a/log4j2-ecs-layout/src/test/resources/log4j2-test.xml +++ b/log4j2-ecs-layout/src/test/resources/log4j2-test.xml @@ -6,7 +6,7 @@ + eventDataset="testdataset" exceptionPattern="%ex{4}"> From 69956b638a31af7a1f0475d601c38c785539c839 Mon Sep 17 00:00:00 2001 From: "Spindler, Justin" Date: Wed, 27 Apr 2022 10:00:04 -0400 Subject: [PATCH 6/8] Fix tests based on PR comments --- .../EcsLayoutWithExceptionPatternTest.java | 1 + .../EcsLayoutWithStackTraceAsArrayTest.java | 32 +++++++++++++------ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithExceptionPatternTest.java b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithExceptionPatternTest.java index a0b8a6e5..1606cdbd 100644 --- a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithExceptionPatternTest.java +++ b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithExceptionPatternTest.java @@ -29,5 +29,6 @@ void testLogExceptionNullMessage() throws Exception { JsonNode log = getLastLogLine();; assertThat(log.get("error.type").textValue()).isEqualTo(RuntimeException.class.getName()); assertThat(log.get("error.message")).isNull(); + assertThat(log.get("error.stack_trace").textValue()).isEqualTo("java.lang.RuntimeException\nSTACK_TRACE!"); } } diff --git a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithStackTraceAsArrayTest.java b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithStackTraceAsArrayTest.java index 0eb335cb..b31fda00 100644 --- a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithStackTraceAsArrayTest.java +++ b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithStackTraceAsArrayTest.java @@ -12,34 +12,46 @@ public class EcsLayoutWithStackTraceAsArrayTest extends Log4j2EcsLayoutTest { @Override protected EcsLayout.Builder configureLayout(LoggerContext context) { return super.configureLayout(context) - .setExceptionPattern("%cEx") + .setExceptionPattern("%rEx{4,filters(java.base,java.lang)}") .setStackTraceAsArray(true); } @Test void testLogException() throws Exception { - error("test", new RuntimeException("test")); + error("test", numberFormatException()); JsonNode log = getLastLogLine(); assertThat(log.get("log.level").textValue()).isIn("ERROR", "SEVERE"); - assertThat(log.get("error.message").textValue()).isEqualTo("test"); - assertThat(log.get("error.type").textValue()).isEqualTo(RuntimeException.class.getName()); + assertThat(log.get("error.message").textValue()).isEqualTo("For input string: \"NOT_AN_INT\""); + assertThat(log.get("error.type").textValue()).isEqualTo(NumberFormatException.class.getName()); assertThat(log.get("error.stack_trace").isArray()).isTrue(); ArrayNode arrayNode = (ArrayNode) log.get("error.stack_trace"); - assertThat(arrayNode.size()).isEqualTo(2); - assertThat(arrayNode.get(0).textValue()).isEqualTo("java.lang.RuntimeException: test"); - assertThat(arrayNode.get(1).textValue()).isEqualTo("STACK_TRACE!"); + assertThat(arrayNode.size()).isEqualTo(4); + assertThat(arrayNode.get(0).textValue()).isEqualTo("java.lang.NumberFormatException: For input string: \"NOT_AN_INT\""); + assertThat(arrayNode.get(1).textValue()).isEqualTo("\t... suppressed 3 lines"); + assertThat(arrayNode.get(2).textValue()).startsWith("\tat co.elastic.logging.log4j2.EcsLayoutWithStackTraceAsArrayTest.numberFormatException"); + assertThat(arrayNode.get(3).textValue()).startsWith("\tat co.elastic.logging.log4j2.EcsLayoutWithStackTraceAsArrayTest.testLogException"); + } + + private static Throwable numberFormatException() { + try { + Integer.parseInt("NOT_AN_INT"); + return null; + } catch (Exception ex) { + return ex; + } } @Test void testLogExceptionNullMessage() throws Exception { error("test", new RuntimeException()); - JsonNode log = getLastLogLine();; + // skip validation that error.stack_trace is a string + JsonNode log = getLastLogLine(); assertThat(log.get("error.type").textValue()).isEqualTo(RuntimeException.class.getName()); assertThat(log.get("error.message")).isNull(); assertThat(log.get("error.stack_trace").isArray()).isTrue(); ArrayNode arrayNode = (ArrayNode) log.get("error.stack_trace"); - assertThat(arrayNode.size()).isEqualTo(2); + assertThat(arrayNode.size()).isEqualTo(4); assertThat(arrayNode.get(0).textValue()).isEqualTo("java.lang.RuntimeException"); - assertThat(arrayNode.get(1).textValue()).isEqualTo("STACK_TRACE!"); + assertThat(arrayNode.get(1).textValue()).startsWith("\tat co.elastic.logging.log4j2.EcsLayoutWithStackTraceAsArrayTest.testLogExceptionNullMessage"); } } From c4d171411bb7ba0e2e730176b5b7d20b024aaf48 Mon Sep 17 00:00:00 2001 From: "Spindler, Justin" Date: Wed, 27 Apr 2022 10:23:13 -0400 Subject: [PATCH 7/8] PR comments --- .../logging/log4j2/EcsLayoutWithStackTraceAsArrayTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithStackTraceAsArrayTest.java b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithStackTraceAsArrayTest.java index b31fda00..38c9218b 100644 --- a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithStackTraceAsArrayTest.java +++ b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithStackTraceAsArrayTest.java @@ -27,7 +27,7 @@ void testLogException() throws Exception { ArrayNode arrayNode = (ArrayNode) log.get("error.stack_trace"); assertThat(arrayNode.size()).isEqualTo(4); assertThat(arrayNode.get(0).textValue()).isEqualTo("java.lang.NumberFormatException: For input string: \"NOT_AN_INT\""); - assertThat(arrayNode.get(1).textValue()).isEqualTo("\t... suppressed 3 lines"); + assertThat(arrayNode.get(1).textValue()).startsWith("\t... suppressed"); assertThat(arrayNode.get(2).textValue()).startsWith("\tat co.elastic.logging.log4j2.EcsLayoutWithStackTraceAsArrayTest.numberFormatException"); assertThat(arrayNode.get(3).textValue()).startsWith("\tat co.elastic.logging.log4j2.EcsLayoutWithStackTraceAsArrayTest.testLogException"); } From 2b7ebcaab2df660b13297ddd1b2186fe8c0eafab Mon Sep 17 00:00:00 2001 From: eyalkoren <41850454+eyalkoren@users.noreply.github.com> Date: Thu, 28 Apr 2022 07:00:53 +0300 Subject: [PATCH 8/8] Removing extra semicolon --- .../logging/log4j2/EcsLayoutWithExceptionPatternTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithExceptionPatternTest.java b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithExceptionPatternTest.java index 1606cdbd..72650d28 100644 --- a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithExceptionPatternTest.java +++ b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/EcsLayoutWithExceptionPatternTest.java @@ -26,7 +26,7 @@ void testLogException() throws Exception { @Test void testLogExceptionNullMessage() throws Exception { error("test", new RuntimeException()); - JsonNode log = getLastLogLine();; + JsonNode log = getLastLogLine(); assertThat(log.get("error.type").textValue()).isEqualTo(RuntimeException.class.getName()); assertThat(log.get("error.message")).isNull(); assertThat(log.get("error.stack_trace").textValue()).isEqualTo("java.lang.RuntimeException\nSTACK_TRACE!");