You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by vy...@apache.org on 2022/08/17 09:30:57 UTC

[logging-log4j2] 03/03: LOG4J2-3556 Rearrange stringified stack trace tests.

This is an automated email from the ASF dual-hosted git repository.

vy pushed a commit to branch LOG4J2-3556
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 100c6043d4476e8817e387e990d9ff457f850370
Author: Volkan Yazıcı <vo...@yazi.ci>
AuthorDate: Wed Aug 17 11:26:55 2022 +0200

    LOG4J2-3556 Rearrange stringified stack trace tests.
---
 .../template/json/JsonTemplateLayoutTest.java      | 276 +--------------
 .../resolver/StackTraceStringResolverTest.java     | 374 ++++++++++++++++-----
 2 files changed, 311 insertions(+), 339 deletions(-)

diff --git a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java
index 2cd2518872..f0b8b86d3e 100644
--- a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java
+++ b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java
@@ -34,31 +34,14 @@ import org.apache.logging.log4j.core.lookup.MainMapLookup;
 import org.apache.logging.log4j.core.net.Severity;
 import org.apache.logging.log4j.core.time.MutableInstant;
 import org.apache.logging.log4j.layout.template.json.JsonTemplateLayout.EventTemplateAdditionalField;
-import org.apache.logging.log4j.layout.template.json.resolver.EventResolver;
-import org.apache.logging.log4j.layout.template.json.resolver.EventResolverContext;
-import org.apache.logging.log4j.layout.template.json.resolver.EventResolverFactory;
-import org.apache.logging.log4j.layout.template.json.resolver.TemplateResolver;
-import org.apache.logging.log4j.layout.template.json.resolver.TemplateResolverConfig;
-import org.apache.logging.log4j.layout.template.json.resolver.TemplateResolverFactory;
+import org.apache.logging.log4j.layout.template.json.resolver.*;
 import org.apache.logging.log4j.layout.template.json.util.JsonWriter;
-import org.apache.logging.log4j.message.Message;
-import org.apache.logging.log4j.message.MessageFactory;
-import org.apache.logging.log4j.message.ObjectMessage;
-import org.apache.logging.log4j.message.ParameterizedMessageFactory;
-import org.apache.logging.log4j.message.ReusableMessageFactory;
-import org.apache.logging.log4j.message.SimpleMessage;
-import org.apache.logging.log4j.message.StringMapMessage;
+import org.apache.logging.log4j.message.*;
 import org.apache.logging.log4j.test.AvailablePortFinder;
 import org.apache.logging.log4j.util.Strings;
-import org.assertj.core.api.Assertions;
 import org.junit.jupiter.api.Test;
 
-import java.io.ByteArrayOutputStream;
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PrintStream;
-import java.io.UnsupportedEncodingException;
+import java.io.*;
 import java.math.BigDecimal;
 import java.net.ServerSocket;
 import java.net.Socket;
@@ -67,11 +50,7 @@ import java.nio.charset.Charset;
 import java.time.Instant;
 import java.time.temporal.ChronoField;
 import java.time.temporal.TemporalAccessor;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.TimeUnit;
@@ -81,6 +60,7 @@ import java.util.stream.IntStream;
 
 import static org.apache.logging.log4j.layout.template.json.TestHelpers.*;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 @SuppressWarnings("DoubleBraceInitialization")
 class JsonTemplateLayoutTest {
@@ -770,85 +750,17 @@ class JsonTemplateLayoutTest {
 
     }
 
-    private static final class NonAsciiUtf8MethodNameContainingException extends RuntimeException {
-
-        public static final long serialVersionUID = 0;
-
-        private static final String NON_ASCII_UTF8_TEXT = "அஆஇฬ๘";
-
-        private static final NonAsciiUtf8MethodNameContainingException INSTANCE =
-                createInstance();
-
-        private static NonAsciiUtf8MethodNameContainingException createInstance() {
-            try {
-                throwException_அஆஇฬ๘();
-                throw new IllegalStateException("should not have reached here");
-            } catch (final NonAsciiUtf8MethodNameContainingException exception) {
-                return exception;
-            }
-        }
-
-        @SuppressWarnings("NonAsciiCharacters")
-        private static void throwException_அஆஇฬ๘() {
-            throw new NonAsciiUtf8MethodNameContainingException(
-                    "exception with non-ASCII UTF-8 method name");
-        }
-
-        private NonAsciiUtf8MethodNameContainingException(final String message) {
-            super(message);
-        }
-
-    }
-
-    @Test
-    void test_exception_with_nonAscii_utf8_method_name() {
-
-        // Create the log event.
-        final SimpleMessage message = new SimpleMessage("Hello, World!");
-        final RuntimeException exception = NonAsciiUtf8MethodNameContainingException.INSTANCE;
-        final LogEvent logEvent = Log4jLogEvent
-                .newBuilder()
-                .setLoggerName(LOGGER_NAME)
-                .setLevel(Level.ERROR)
-                .setMessage(message)
-                .setThrown(exception)
-                .build();
-
-        // Create the event template.
-        final String eventTemplate = writeJson(asMap(
-                "ex_stacktrace", asMap(
-                        "$resolver", "exception",
-                        "field", "stackTrace",
-                        "stringified", true)));
-
-        // Create the layout.
-        final JsonTemplateLayout layout = JsonTemplateLayout
-                .newBuilder()
-                .setConfiguration(CONFIGURATION)
-                .setStackTraceEnabled(true)
-                .setEventTemplate(eventTemplate)
-                .build();
-
-        // Check the serialized event.
-        usingSerializedLogEventAccessor(layout, logEvent, accessor ->
-                assertThat(accessor.getString("ex_stacktrace"))
-                        .contains(NonAsciiUtf8MethodNameContainingException.NON_ASCII_UTF8_TEXT));
-
-    }
-
     @Test
     void test_event_template_additional_fields() {
 
         // Create the log event.
         final SimpleMessage message = new SimpleMessage("Hello, World!");
-        final RuntimeException exception = NonAsciiUtf8MethodNameContainingException.INSTANCE;
         final Level level = Level.ERROR;
         final LogEvent logEvent = Log4jLogEvent
                 .newBuilder()
                 .setLoggerName(LOGGER_NAME)
                 .setLevel(level)
                 .setMessage(message)
-                .setThrown(exception)
                 .build();
 
         // Create the event template.
@@ -971,8 +883,7 @@ class JsonTemplateLayoutTest {
             // Verify the test case.
             usingSerializedLogEventAccessor(layout, logEvent, accessor ->
                     testCase.forEach((key, expectedValue) ->
-                            Assertions
-                                    .assertThat(accessor.getObject(key))
+                            assertThat(accessor.getObject(key))
                                     .describedAs("key=%s", key)
                                     .isEqualTo(expectedValue)));
 
@@ -1139,161 +1050,6 @@ class JsonTemplateLayoutTest {
 
     }
 
-    @Test
-    void test_stringified_exception_resolver_with_maxStringLength() {
-
-        // Create the event template.
-        final String eventTemplate = writeJson(asMap(
-                "stackTrace", asMap(
-                        "$resolver", "exception",
-                        "field", "stackTrace",
-                        "stringified", true)));
-
-        // Create the layout.
-        final int maxStringLength = eventTemplate.length();
-        final JsonTemplateLayout layout = JsonTemplateLayout
-                .newBuilder()
-                .setConfiguration(CONFIGURATION)
-                .setEventTemplate(eventTemplate)
-                .setMaxStringLength(maxStringLength)
-                .setStackTraceEnabled(true)
-                .build();
-
-        // Create the log event.
-        final SimpleMessage message = new SimpleMessage("foo");
-        final LogEvent logEvent = Log4jLogEvent
-                .newBuilder()
-                .setLoggerName(LOGGER_NAME)
-                .setMessage(message)
-                .setThrown(NonAsciiUtf8MethodNameContainingException.INSTANCE)
-                .build();
-
-        // Check the serialized event.
-        usingSerializedLogEventAccessor(layout, logEvent, accessor -> {
-            final int expectedLength = maxStringLength +
-                    JsonTemplateLayoutDefaults.getTruncatedStringSuffix().length();
-            assertThat(accessor.getString("stackTrace").length()).isEqualTo(expectedLength);
-        });
-
-    }
-
-    @Test
-    void test_stack_trace_truncation() {
-
-        // Create the exception to be logged.
-        final Exception childError =
-                new Exception("unique child exception message");
-        final Exception parentError =
-                new Exception("unique parent exception message", childError);
-
-        // Create the event template.
-        final String truncationSuffix = "~";
-        final String eventTemplate = writeJson(asMap(
-                // Raw exception.
-                "ex", asMap(
-                        "$resolver", "exception",
-                        "field", "stackTrace",
-                        "stackTrace", asMap(
-                                "stringified", true)),
-                // Exception matcher using strings.
-                "stringMatchedEx", asMap(
-                        "$resolver", "exception",
-                        "field", "stackTrace",
-                        "stackTrace", asMap(
-                                "stringified", asMap(
-                                        "truncation", asMap(
-                                                "suffix", truncationSuffix,
-                                                "pointMatcherStrings", Arrays.asList(
-                                                        "this string shouldn't match with anything",
-                                                        parentError.getMessage()))))),
-                // Exception matcher using regexes.
-                "regexMatchedEx", asMap(
-                        "$resolver", "exception",
-                        "field", "stackTrace",
-                        "stackTrace", asMap(
-                                "stringified", asMap(
-                                        "truncation", asMap(
-                                                "suffix", truncationSuffix,
-                                                "pointMatcherRegexes", Arrays.asList(
-                                                        "this string shouldn't match with anything",
-                                                        parentError
-                                                                .getMessage()
-                                                                .replace("unique", "[xu]n.que")))))),
-                // Raw exception root cause.
-                "rootEx", asMap(
-                        "$resolver", "exceptionRootCause",
-                        "field", "stackTrace",
-                        "stackTrace", asMap(
-                                "stringified", true)),
-                // Exception root cause matcher using strings.
-                "stringMatchedRootEx", asMap(
-                        "$resolver", "exceptionRootCause",
-                        "field", "stackTrace",
-                        "stackTrace", asMap(
-                                "stringified", asMap(
-                                        "truncation", asMap(
-                                                "suffix", truncationSuffix,
-                                                "pointMatcherStrings", Arrays.asList(
-                                                        "this string shouldn't match with anything",
-                                                        childError.getMessage()))))),
-                // Exception root cause matcher using regexes.
-                "regexMatchedRootEx", asMap(
-                        "$resolver", "exceptionRootCause",
-                        "field", "stackTrace",
-                        "stackTrace", asMap(
-                                "stringified", asMap(
-                                        "truncation", asMap(
-                                                "suffix", truncationSuffix,
-                                                "pointMatcherRegexes", Arrays.asList(
-                                                        "this string shouldn't match with anything",
-                                                        childError
-                                                                .getMessage()
-                                                                .replace("unique", "[xu]n.que"))))))));
-
-        // Create the layout.
-        final JsonTemplateLayout layout = JsonTemplateLayout
-                .newBuilder()
-                .setConfiguration(CONFIGURATION)
-                .setEventTemplate(eventTemplate)
-                .setStackTraceEnabled(true)
-                .build();
-
-        // Create the log event.
-        final LogEvent logEvent = Log4jLogEvent
-                .newBuilder()
-                .setLoggerName(LOGGER_NAME)
-                .setThrown(parentError)
-                .build();
-
-        // Check the serialized event.
-        final String expectedMatchedExEnd =
-                parentError.getMessage() + truncationSuffix;
-        final String expectedMatchedRootExEnd =
-                childError.getMessage() + truncationSuffix;
-        usingSerializedLogEventAccessor(layout, logEvent, accessor -> {
-
-            // Check the serialized exception.
-            assertThat(accessor.getString("ex"))
-                    .doesNotEndWith(expectedMatchedExEnd)
-                    .doesNotEndWith(expectedMatchedRootExEnd);
-            assertThat(accessor.getString("stringMatchedEx"))
-                    .endsWith(expectedMatchedExEnd);
-            assertThat(accessor.getString("regexMatchedEx"))
-                    .endsWith(expectedMatchedExEnd);
-
-            // Check the serialized exception root cause.
-            assertThat(accessor.getString("rootEx"))
-                    .doesNotEndWith(expectedMatchedExEnd)
-                    .doesNotEndWith(expectedMatchedRootExEnd);
-            assertThat(accessor.getString("stringMatchedRootEx"))
-                    .endsWith(expectedMatchedRootExEnd);
-            assertThat(accessor.getString("regexMatchedRootEx"))
-                    .endsWith(expectedMatchedRootExEnd);
-
-        });
-
-    }
-
     @Test
     void test_inline_stack_trace_element_template() {
 
@@ -1326,9 +1082,9 @@ class JsonTemplateLayoutTest {
 
         // Check the serialized log event.
         final String expectedClassName = JsonTemplateLayoutTest.class.getCanonicalName();
-        usingSerializedLogEventAccessor(layout, logEvent, accessor -> Assertions
-                .assertThat(accessor.getList("stackTrace", String.class))
-                .contains(expectedClassName));
+        usingSerializedLogEventAccessor(layout, logEvent, accessor ->
+                assertThat(accessor.getList("stackTrace", String.class))
+                        .contains(expectedClassName));
 
     }
 
@@ -1355,9 +1111,9 @@ class JsonTemplateLayoutTest {
                 .build();
 
         // Check the serialized log event.
-        usingSerializedLogEventAccessor(layout, logEvent, accessor -> Assertions
-                .assertThat(accessor.getString("customField"))
-                .matches("CustomValue-[0-9]+"));
+        usingSerializedLogEventAccessor(layout, logEvent, accessor ->
+                assertThat(accessor.getString("customField"))
+                        .matches("CustomValue-[0-9]+"));
 
     }
 
@@ -1422,7 +1178,6 @@ class JsonTemplateLayoutTest {
                 .newBuilder()
                 .setLoggerName(LOGGER_NAME)
                 .setMessage(message)
-                .setThrown(NonAsciiUtf8MethodNameContainingException.INSTANCE)
                 .build();
 
         // Check the serialized event.
@@ -1814,9 +1569,7 @@ class JsonTemplateLayoutTest {
         final String expectedSerializedLogEventJson =
                 "{}" + JsonTemplateLayoutDefaults.getEventDelimiter();
         final String actualSerializedLogEventJson = layout.toSerializable(logEvent);
-        Assertions
-                .assertThat(actualSerializedLogEventJson)
-                .isEqualTo(expectedSerializedLogEventJson);
+        assertThat(actualSerializedLogEventJson).isEqualTo(expectedSerializedLogEventJson);
 
     }
 
@@ -1847,8 +1600,7 @@ class JsonTemplateLayoutTest {
                 .build();
 
         // Check the serialized event.
-        Assertions
-                .assertThatThrownBy(() -> layout.toSerializable(logEvent))
+        assertThatThrownBy(() -> layout.toSerializable(logEvent))
                 .isInstanceOf(StackOverflowError.class);
 
     }
diff --git a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/StackTraceStringResolverTest.java b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/StackTraceStringResolverTest.java
index c1aa485e21..6c93b1902f 100644
--- a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/StackTraceStringResolverTest.java
+++ b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/StackTraceStringResolverTest.java
@@ -19,8 +19,8 @@ package org.apache.logging.log4j.layout.template.json.resolver;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.impl.Log4jLogEvent;
 import org.apache.logging.log4j.layout.template.json.JsonTemplateLayout;
+import org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults;
 import org.assertj.core.api.AbstractStringAssert;
-import org.assertj.core.api.Assertions;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 
@@ -28,10 +28,12 @@ import java.io.ByteArrayOutputStream;
 import java.io.PrintStream;
 import java.math.BigDecimal;
 import java.net.ServerSocket;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.function.Consumer;
+import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -47,7 +49,7 @@ class StackTraceStringResolverTest {
     // Below we create arbitrary exceptions containing stack entries from non-Log4j packages.
     // Non-Log4j package origin is needed to avoid the truncation (e.g., `... 58 more`) done by `Throwable#printStackTrace()`.
 
-    private static final String EXCEPTION_REGEX_FLAGS = "(?m)(?s)";     // MULTILINE | DOTALL
+    private static final String EXCEPTION_REGEX_FLAGS = "(?s)";     // DOTALL
 
     private static final String TRUNCATION_SUFFIX = "<truncated>";
 
@@ -110,9 +112,7 @@ class StackTraceStringResolverTest {
         final Throwable error = exception1();
         final String stackTrace = stackTrace(error);
         final String regex = exception1Regex(false);
-        Assertions
-                .assertThat(stackTrace)
-                .matches(EXCEPTION_REGEX_FLAGS + regex);
+        assertThat(stackTrace).matches(EXCEPTION_REGEX_FLAGS + regex);
     }
 
     @Test
@@ -120,9 +120,7 @@ class StackTraceStringResolverTest {
         final Throwable error = exception2();
         final String stackTrace = stackTrace(error);
         final String regex = exception2Regex(false);
-        Assertions
-                .assertThat(stackTrace)
-                .matches(EXCEPTION_REGEX_FLAGS + regex);
+        assertThat(stackTrace).matches(EXCEPTION_REGEX_FLAGS + regex);
     }
 
     @Test
@@ -130,9 +128,7 @@ class StackTraceStringResolverTest {
         final Throwable error = exception3();
         final String stackTrace = stackTrace(error);
         final String regex = exception3Regex(false);
-        Assertions
-                .assertThat(stackTrace)
-                .matches(EXCEPTION_REGEX_FLAGS + regex);
+        assertThat(stackTrace).matches(EXCEPTION_REGEX_FLAGS + regex);
     }
 
     private static String stackTrace(final Throwable throwable) {
@@ -309,6 +305,35 @@ class StackTraceStringResolverTest {
                 final Throwable exception,
                 final String regex);
 
+        private static void assertSerializedException(
+                final Map<String, ?> exceptionResolverTemplate,
+                final Throwable exception,
+                final Consumer<AbstractStringAssert<?>> serializedExceptionAsserter) {
+
+            // Create the event template.
+            final String eventTemplate = writeJson(asMap("output", exceptionResolverTemplate));
+
+            // Create the layout.
+            final JsonTemplateLayout layout = JsonTemplateLayout
+                    .newBuilder()
+                    .setConfiguration(CONFIGURATION)
+                    .setEventTemplate(eventTemplate)
+                    .build();
+
+            // Create the log event.
+            final LogEvent logEvent = Log4jLogEvent
+                    .newBuilder()
+                    .setThrown(exception)
+                    .build();
+
+            // Check the serialized event.
+            usingSerializedLogEventAccessor(layout, logEvent, accessor -> {
+                AbstractStringAssert<?> serializedExceptionAssert = assertThat(accessor.getString("output"));
+                serializedExceptionAsserter.accept(serializedExceptionAssert);
+            });
+
+        }
+
     }
 
     ////////////////////////////////////////////////////////////////////////////
@@ -327,23 +352,59 @@ class StackTraceStringResolverTest {
             assertSerializedExceptionWithoutTruncation(exception, regex);
         }
 
-    }
+        private void assertSerializedExceptionWithoutTruncation(
+                final Throwable exception,
+                final String regex) {
 
-    private static void assertSerializedExceptionWithoutTruncation(
-            final Throwable exception,
-            final String regex) {
+            // Create the event template.
+            final Map<String, ?> exceptionResolverTemplate = asMap(
+                    "$resolver", "exception",
+                    "field", "stackTrace",
+                    "stackTrace", asMap("stringified", true));
 
-        // Create the event template.
-        final Map<String, ?> exceptionResolverTemplate = asMap(
-                "$resolver", "exception",
-                "field", "stackTrace",
-                "stackTrace", asMap("stringified", true));
+            // Check the serialized event.
+            AbstractTestCases.assertSerializedException(
+                    exceptionResolverTemplate,
+                    exception,
+                    serializedExceptionAssert -> serializedExceptionAssert.matches(regex));
 
-        // Check the serialized event.
-        assertSerializedException(
-                exceptionResolverTemplate,
-                exception,
-                serializedExceptionAssert -> serializedExceptionAssert.matches(regex));
+        }
+
+        @Test
+        void JsonWriter_maxStringLength_should_work() {
+
+            // Create the event template.
+            final String eventTemplate = writeJson(asMap(
+                    "ex", asMap(
+                            "$resolver", "exception",
+                            "field", "stackTrace",
+                            "stringified", true)));
+
+            // Create the layout.
+            final int maxStringLength = eventTemplate.length();
+            final JsonTemplateLayout layout = JsonTemplateLayout
+                    .newBuilder()
+                    .setConfiguration(CONFIGURATION)
+                    .setEventTemplate(eventTemplate)
+                    .setMaxStringLength(maxStringLength)
+                    .setStackTraceEnabled(true)
+                    .build();
+
+            // Create the log event.
+            Throwable exception = exception1();
+            final LogEvent logEvent = Log4jLogEvent
+                    .newBuilder()
+                    .setThrown(exception)
+                    .build();
+
+            // Check the serialized event.
+            usingSerializedLogEventAccessor(layout, logEvent, accessor -> {
+                final int expectedLength = maxStringLength +
+                        JsonTemplateLayoutDefaults.getTruncatedStringSuffix().length();
+                assertThat(accessor.getString("ex").length()).isEqualTo(expectedLength);
+            });
+
+        }
 
     }
 
@@ -352,9 +413,9 @@ class StackTraceStringResolverTest {
     ////////////////////////////////////////////////////////////////////////////
 
     @Nested
-    class WithStringTruncation extends AbstractTestCases {
+    class WithTruncation extends AbstractTestCases {
 
-        WithStringTruncation() {
+        WithTruncation() {
             super(true);
         }
 
@@ -362,75 +423,234 @@ class StackTraceStringResolverTest {
         void assertSerializedException(final Throwable exception, final String regex) {
             assertSerializedExceptionWithStringTruncation(exception, regex);
         }
-        
-    }
 
-    private static void assertSerializedExceptionWithStringTruncation(
-            final Throwable exception,
-            final String regex) {
+        private void assertSerializedExceptionWithStringTruncation(
+                final Throwable exception,
+                final String regex) {
+
+            // Create the event template.
+            final List<String> pointMatcherStrings = pointMatcherStrings();
+            final Map<String, ?> exceptionResolverTemplate = asMap(
+                    "$resolver", "exception",
+                    "field", "stackTrace",
+                    "stackTrace", asMap("stringified", asMap(
+                            "truncation", asMap(
+                                    "suffix", TRUNCATION_SUFFIX,
+                                    "pointMatcherStrings", pointMatcherStrings))));
+
+            // Check the serialized event.
+            AbstractTestCases.assertSerializedException(
+                    exceptionResolverTemplate,
+                    exception,
+                    serializedExceptionAssert -> serializedExceptionAssert.matches(regex));
 
-        // Create the event template.
-        final List<String> pointMatcherStrings = pointMatcherStrings();
-        final Map<String, ?> exceptionResolverTemplate = asMap(
-                "$resolver", "exception",
-                "field", "stackTrace",
-                "stackTrace", asMap("stringified", asMap(
-                        "truncation", asMap(
-                                "suffix", TRUNCATION_SUFFIX,
-                                "pointMatcherStrings", pointMatcherStrings))));
+        }
 
-        // Check the serialized event.
-        assertSerializedException(
-                exceptionResolverTemplate,
-                exception,
-                serializedExceptionAssert -> serializedExceptionAssert.matches(regex));
+        private List<String> pointMatcherStrings() {
+            final Throwable exception1 = exception1();
+            final Throwable exception2 = exception2();
+            final Throwable exception3 = exception3();
+            return Stream
+                    .of(exception1, exception2, exception3)
+                    .map(this::pointMatcherString)
+                    .collect(Collectors.toList());
+        }
 
-    }
+        @Test
+        void point_matchers_should_work() {
+
+            // Create the exception to be logged.
+            final Throwable parentError = exception1();
+            final Throwable childError = exception3();
+            parentError.initCause(childError);
+
+            // Create the event template.
+            final String eventTemplate = writeJson(asMap(
+
+                    // Raw exception
+                    "ex", asMap(
+                            "$resolver", "exception",
+                            "field", "stackTrace",
+                            "stackTrace", asMap(
+                                    "stringified", true)),
+
+                    // Exception matcher using strings
+                    "stringMatchedEx", asMap(
+                            "$resolver", "exception",
+                            "field", "stackTrace",
+                            "stackTrace", asMap(
+                                    "stringified", asMap(
+                                            "truncation", asMap(
+                                                    "suffix", TRUNCATION_SUFFIX,
+                                                    "pointMatcherStrings", Arrays.asList(
+                                                            "this string shouldn't match with anything",
+                                                            pointMatcherString(parentError)))))),
+
+                    // Exception matcher using regexes
+                    "regexMatchedEx", asMap(
+                            "$resolver", "exception",
+                            "field", "stackTrace",
+                            "stackTrace", asMap(
+                                    "stringified", asMap(
+                                            "truncation", asMap(
+                                                    "suffix", TRUNCATION_SUFFIX,
+                                                    "pointMatcherRegexes", Arrays.asList(
+                                                            "this string shouldn't match with anything",
+                                                            pointMatcherRegex(parentError)))))),
+
+                    // Raw exception root cause
+                    "rootEx", asMap(
+                            "$resolver", "exceptionRootCause",
+                            "field", "stackTrace",
+                            "stackTrace", asMap(
+                                    "stringified", true)),
+
+                    // Exception root cause matcher using strings
+                    "stringMatchedRootEx", asMap(
+                            "$resolver", "exceptionRootCause",
+                            "field", "stackTrace",
+                            "stackTrace", asMap(
+                                    "stringified", asMap(
+                                            "truncation", asMap(
+                                                    "suffix", TRUNCATION_SUFFIX,
+                                                    "pointMatcherStrings", Arrays.asList(
+                                                            "this string shouldn't match with anything",
+                                                            pointMatcherString(childError)))))),
+
+                    // Exception root cause matcher using regexes
+                    "regexMatchedRootEx", asMap(
+                            "$resolver", "exceptionRootCause",
+                            "field", "stackTrace",
+                            "stackTrace", asMap(
+                                    "stringified", asMap(
+                                            "truncation", asMap(
+                                                    "suffix", TRUNCATION_SUFFIX,
+                                                    "pointMatcherRegexes", Arrays.asList(
+                                                            "this string shouldn't match with anything",
+                                                            pointMatcherRegex(childError))))))));
+
+            // Create the layout.
+            final JsonTemplateLayout layout = JsonTemplateLayout
+                    .newBuilder()
+                    .setConfiguration(CONFIGURATION)
+                    .setEventTemplate(eventTemplate)
+                    .build();
+
+            // Create the log event.
+            final LogEvent logEvent = Log4jLogEvent
+                    .newBuilder()
+                    .setThrown(parentError)
+                    .build();
+
+            // Check the serialized event.
+            usingSerializedLogEventAccessor(layout, logEvent, accessor -> {
+
+                // Check the raw parent exception.
+                final String exPattern = EXCEPTION_REGEX_FLAGS +
+                        exception1Regex(false) +
+                        "\nCaused by: " + exception3Regex(false);
+                assertThat(accessor.getString("ex")).matches(exPattern);
+
+                // Check the matcher usage on parent exception.
+                final String matchedExPattern = EXCEPTION_REGEX_FLAGS +
+                        exception1Regex(true) +
+                        "\nCaused by: " + exception3Regex(false);
+                assertThat(accessor.getString("stringMatchedEx")).matches(matchedExPattern);
+                assertThat(accessor.getString("regexMatchedEx")).matches(matchedExPattern);
+
+                // Check the raw child exception.
+                final String rootExPattern = EXCEPTION_REGEX_FLAGS +
+                        exception3Regex(false);
+                assertThat(accessor.getString("rootEx")).matches(rootExPattern);
+
+                // Check the matcher usage on child exception.
+                final String matchedRootExPattern = EXCEPTION_REGEX_FLAGS +
+                        exception3Regex(true);
+                assertThat(accessor.getString("stringMatchedRootEx")).matches(matchedRootExPattern);
+                assertThat(accessor.getString("regexMatchedRootEx")).matches(matchedRootExPattern);
+
+            });
+
+        }
+
+        private String pointMatcherString(Throwable exception) {
+            final StackTraceElement stackTraceElement = exception.getStackTrace()[0];
+            final String className = stackTraceElement.getClassName();
+            return "at " + className;
+        }
+
+        private String pointMatcherRegex(Throwable exception) {
+            String string = pointMatcherString(exception);
+            return matchingRegex(string);
+        }
 
-    private static List<String> pointMatcherStrings() {
-        final Throwable exception1 = exception1();
-        final Throwable exception2 = exception2();
-        final Throwable exception3 = exception3();
-        return Stream
-                .of(exception1, exception2, exception3)
-                .map(exception -> {
-                    final StackTraceElement stackTraceElement = exception.getStackTrace()[0];
-                    final String className = stackTraceElement.getClassName();
-                    return "at " + className;
-                })
-                .collect(Collectors.toList());
+        /**
+         * @return a regex matching the given input
+         */
+        private String matchingRegex(String string) {
+            return "[" + string.charAt(0) + "]" + Pattern.quote(string.substring(1));
+        }
+        
     }
 
-    ////////////////////////////////////////////////////////////////////////////
-    // utilities ///////////////////////////////////////////////////////////////
-    ////////////////////////////////////////////////////////////////////////////
+    @Test
+    void nonAscii_utf8_method_name_should_get_serialized() {
 
-    private static void assertSerializedException(
-            final Map<String, ?> exceptionResolverTemplate,
-            final Throwable exception,
-            final Consumer<AbstractStringAssert<?>> serializedExceptionAsserter) {
+        // Create the log event.
+        final LogEvent logEvent = Log4jLogEvent
+                .newBuilder()
+                .setThrown(NonAsciiUtf8MethodNameContainingException.INSTANCE)
+                .build();
 
         // Create the event template.
-        final String eventTemplate = writeJson(asMap("output", exceptionResolverTemplate));
+        final String eventTemplate = writeJson(asMap(
+                "ex_stacktrace", asMap(
+                        "$resolver", "exception",
+                        "field", "stackTrace",
+                        "stringified", true)));
 
         // Create the layout.
         final JsonTemplateLayout layout = JsonTemplateLayout
                 .newBuilder()
                 .setConfiguration(CONFIGURATION)
+                .setStackTraceEnabled(true)
                 .setEventTemplate(eventTemplate)
                 .build();
 
-        // Create the log event.
-        final LogEvent logEvent = Log4jLogEvent
-                .newBuilder()
-                .setThrown(exception)
-                .build();
-
         // Check the serialized event.
-        usingSerializedLogEventAccessor(layout, logEvent, accessor -> {
-            AbstractStringAssert<?> serializedExceptionAssert = assertThat(accessor.getString("output"));
-            serializedExceptionAsserter.accept(serializedExceptionAssert);
-        });
+        usingSerializedLogEventAccessor(layout, logEvent, accessor ->
+                assertThat(accessor.getString("ex_stacktrace"))
+                        .contains(NonAsciiUtf8MethodNameContainingException.NON_ASCII_UTF8_TEXT));
+
+    }
+
+    private static final class NonAsciiUtf8MethodNameContainingException extends RuntimeException {
+
+        public static final long serialVersionUID = 0;
+
+        private static final String NON_ASCII_UTF8_TEXT = "அஆஇฬ๘";
+
+        private static final NonAsciiUtf8MethodNameContainingException INSTANCE =
+                createInstance();
+
+        private static NonAsciiUtf8MethodNameContainingException createInstance() {
+            try {
+                throwException_அஆஇฬ๘();
+                throw new IllegalStateException("should not have reached here");
+            } catch (final NonAsciiUtf8MethodNameContainingException exception) {
+                return exception;
+            }
+        }
+
+        @SuppressWarnings("NonAsciiCharacters")
+        private static void throwException_அஆஇฬ๘() {
+            throw new NonAsciiUtf8MethodNameContainingException(
+                    "exception with non-ASCII UTF-8 method name");
+        }
+
+        private NonAsciiUtf8MethodNameContainingException(final String message) {
+            super(message);
+        }
 
     }