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

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

commit 1d4732133eae1bb19c55e044fe6274fabf0fcafb
Author: Volkan Yazıcı <volkan.yaz...@gmail.com>
AuthorDate: Sun Jun 7 22:23:45 2020 +0200

    #335 Skip unresolvable entries.
---
 .../json/template/resolver/TemplateResolvers.java  | 133 ++++++++++++++++-----
 .../log4j/layout/json/template/JsonLayoutTest.java |  45 ++-----
 .../json/template/JsonTemplateLayoutTest.java      |  61 ++++++++++
 3 files changed, 170 insertions(+), 69 deletions(-)

diff --git 
a/log4j-layout-json-template/src/main/java/org/apache/logging/log4j/layout/json/template/resolver/TemplateResolvers.java
 
b/log4j-layout-json-template/src/main/java/org/apache/logging/log4j/layout/json/template/resolver/TemplateResolvers.java
index ca304b7..8bb11d6 100644
--- 
a/log4j-layout-json-template/src/main/java/org/apache/logging/log4j/layout/json/template/resolver/TemplateResolvers.java
+++ 
b/log4j-layout-json-template/src/main/java/org/apache/logging/log4j/layout/json/template/resolver/TemplateResolvers.java
@@ -30,21 +30,46 @@ public enum TemplateResolvers {;
 
     private static final String RESOLVER_FIELD_NAME = "$resolver";
 
+    private static abstract class UnresolvableTemplateResolver
+            implements TemplateResolver<Object> {
+
+        @Override
+        public final boolean isResolvable() {
+            return false;
+        }
+
+        @Override
+        public final boolean isResolvable(Object value) {
+            return false;
+        }
+
+    }
+
     private static final TemplateResolver<?> EMPTY_ARRAY_RESOLVER =
-            (final Object ignored, final JsonWriter jsonWriter) -> {
-                jsonWriter.writeArrayStart();
-                jsonWriter.writeArrayEnd();
+            new UnresolvableTemplateResolver() {
+                @Override
+                public void resolve(final Object value, final JsonWriter 
jsonWriter) {
+                    jsonWriter.writeArrayStart();
+                    jsonWriter.writeArrayEnd();
+                }
             };
 
     private static final TemplateResolver<?> EMPTY_OBJECT_RESOLVER =
-            (final Object ignored, final JsonWriter jsonWriter) -> {
-                jsonWriter.writeObjectStart();
-                jsonWriter.writeObjectEnd();
+            new UnresolvableTemplateResolver() {
+                @Override
+                public void resolve(final Object value, final JsonWriter 
jsonWriter) {
+                    jsonWriter.writeObjectStart();
+                    jsonWriter.writeObjectEnd();
+                }
             };
 
     private static final TemplateResolver<?> NULL_RESOLVER =
-            (final Object ignored, final JsonWriter jsonWriter) ->
+            new UnresolvableTemplateResolver() {
+                @Override
+                public void resolve(final Object value, final JsonWriter 
jsonWriter) {
                     jsonWriter.writeNull();
+                }
+            };
 
     public static <V, C extends TemplateResolverContext<V, C>> 
TemplateResolver<V> ofTemplate(
             final C context,
@@ -236,36 +261,80 @@ public enum TemplateResolvers {;
                 })
                 .collect(Collectors.toList());
 
-        // Create a parent resolver collecting each object field resolver 
execution.
-        return (value, jsonWriter) -> {
-            final StringBuilder jsonWriterStringBuilder = 
jsonWriter.getStringBuilder();
-            jsonWriter.writeObjectStart();
-            for (int resolvedFieldCount = 0, fieldIndex = 0; fieldIndex < 
fieldCount; fieldIndex++) {
-                final TemplateResolver<V> fieldResolver = 
fieldResolvers.get(fieldIndex);
-                final boolean resolvable = fieldResolver.isResolvable(value);
-                if (!resolvable) {
-                    continue;
+        return new TemplateResolver<V>() {
+
+            /**
+             * The parent resolver checking if each child is resolvable.
+             *
+             * This is an optimization to skip the rendering of a parent if all
+             * its children are not resolvable.
+             */
+            @Override
+            public boolean isResolvable() {
+                for (int fieldIndex = 0; fieldIndex < fieldCount; 
fieldIndex++) {
+                    final TemplateResolver<V> fieldResolver = 
fieldResolvers.get(fieldIndex);
+                    final boolean resolvable = fieldResolver.isResolvable();
+                    if (resolvable) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            /**
+             * The parent resolver checking if each child is resolvable given
+             * the passed {@code value}.
+             *
+             * This is an optimization to skip the rendering of a parent if all
+             * its children are not resolvable given the passed {@code value}.
+             */
+            @Override
+            public boolean isResolvable(final V value) {
+                for (int fieldIndex = 0; fieldIndex < fieldCount; 
fieldIndex++) {
+                    final TemplateResolver<V> fieldResolver = 
fieldResolvers.get(fieldIndex);
+                    final boolean resolvable = 
fieldResolver.isResolvable(value);
+                    if (resolvable) {
+                        return true;
+                    }
                 }
-                final boolean succeedingEntry = resolvedFieldCount > 0;
-                final boolean flattening = fieldResolver.isFlattening();
-                if (flattening) {
-                    final int initLength = jsonWriterStringBuilder.length();
-                    fieldResolver.resolve(value, jsonWriter, succeedingEntry);
-                    final boolean resolved = jsonWriterStringBuilder.length() 
> initLength;
-                    if (resolved) {
-                        resolvedFieldCount++;
+                return false;
+            }
+
+            /**
+             * The parent resolver combining all child resolver executions.
+              */
+            @Override
+            public void resolve(final V value, final JsonWriter jsonWriter) {
+                final StringBuilder jsonWriterStringBuilder = 
jsonWriter.getStringBuilder();
+                jsonWriter.writeObjectStart();
+                for (int resolvedFieldCount = 0, fieldIndex = 0; fieldIndex < 
fieldCount; fieldIndex++) {
+                    final TemplateResolver<V> fieldResolver = 
fieldResolvers.get(fieldIndex);
+                    final boolean resolvable = 
fieldResolver.isResolvable(value);
+                    if (!resolvable) {
+                        continue;
                     }
-                } else {
-                    if (succeedingEntry) {
-                        jsonWriter.writeSeparator();
+                    final boolean succeedingEntry = resolvedFieldCount > 0;
+                    final boolean flattening = fieldResolver.isFlattening();
+                    if (flattening) {
+                        final int initLength = 
jsonWriterStringBuilder.length();
+                        fieldResolver.resolve(value, jsonWriter, 
succeedingEntry);
+                        final boolean resolved = 
jsonWriterStringBuilder.length() > initLength;
+                        if (resolved) {
+                            resolvedFieldCount++;
+                        }
+                    } else {
+                        if (succeedingEntry) {
+                            jsonWriter.writeSeparator();
+                        }
+                        final String fieldPrefix = 
fieldPrefixes.get(fieldIndex);
+                        jsonWriter.writeRawString(fieldPrefix);
+                        fieldResolver.resolve(value, jsonWriter, 
succeedingEntry);
+                        resolvedFieldCount++;
                     }
-                    final String fieldPrefix = fieldPrefixes.get(fieldIndex);
-                    jsonWriter.writeRawString(fieldPrefix);
-                    fieldResolver.resolve(value, jsonWriter, succeedingEntry);
-                    resolvedFieldCount++;
                 }
+                jsonWriter.writeObjectEnd();
             }
-            jsonWriter.writeObjectEnd();
+
         };
 
     }
diff --git 
a/log4j-layout-json-template/src/test/java/org/apache/logging/log4j/layout/json/template/JsonLayoutTest.java
 
b/log4j-layout-json-template/src/test/java/org/apache/logging/log4j/layout/json/template/JsonLayoutTest.java
index ec8458e..6996ea5 100644
--- 
a/log4j-layout-json-template/src/test/java/org/apache/logging/log4j/layout/json/template/JsonLayoutTest.java
+++ 
b/log4j-layout-json-template/src/test/java/org/apache/logging/log4j/layout/json/template/JsonLayoutTest.java
@@ -8,11 +8,8 @@ import org.assertj.core.api.Assertions;
 import org.junit.Test;
 
 import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 
 import static 
org.apache.logging.log4j.layout.json.template.LayoutComparisonHelpers.renderUsing;
 
@@ -52,49 +49,23 @@ public class JsonLayoutTest {
     private static void test(final LogEvent logEvent) {
         final Map<String, Object> jsonTemplateLayoutMap = 
renderUsingJsonTemplateLayout(logEvent);
         final Map<String, Object> jsonLayoutMap = 
renderUsingJsonLayout(logEvent);
-        Assertions.assertThat(jsonTemplateLayoutMap).isEqualTo(jsonLayoutMap);
-    }
-
-    private static Map<String, Object> renderUsingJsonTemplateLayout(
-            final LogEvent logEvent) {
-        final Map<String, Object> map = renderUsing(logEvent, 
JSON_TEMPLATE_LAYOUT);
-        final Map<String, Object> emptySourceExcludedMap = 
removeEmptyObject(map, "source");
         // JsonLayout blindly serializes the Throwable as a POJO, this is,
         // to say the least, quite wrong, and I ain't gonna try to emulate
         // this behaviour in JsonTemplateLayout. Hence, discarding the "thrown"
         // field.
-        emptySourceExcludedMap.remove("thrown");
-        return emptySourceExcludedMap;
+        jsonTemplateLayoutMap.remove("thrown");
+        jsonLayoutMap.remove("thrown");
+        Assertions.assertThat(jsonTemplateLayoutMap).isEqualTo(jsonLayoutMap);
     }
 
-    private static Map<String, Object> renderUsingJsonLayout(
+    private static Map<String, Object> renderUsingJsonTemplateLayout(
             final LogEvent logEvent) {
-        final Map<String, Object> map = renderUsing(logEvent, JSON_LAYOUT);
-        // JsonLayout blindly serializes the Throwable as a POJO, this is,
-        // to say the least, quite wrong, and I ain't gonna try to emulate
-        // this behaviour in JsonTemplateLayout. Hence, discarding the "thrown"
-        // field.
-        map.remove("thrown");
-        return map;
+        return renderUsing(logEvent, JSON_TEMPLATE_LAYOUT);
     }
 
-    private static Map<String, Object> removeEmptyObject(
-            final Map<String, Object> root,
-            final String key) {
-        @SuppressWarnings("unchecked")
-        final Map<String, Object> source =
-                (Map<String, Object>) root.getOrDefault(
-                        key, Collections.emptyMap());
-        boolean emptySource = source
-                .values()
-                .stream()
-                .allMatch(Objects::isNull);
-        if (!emptySource) {
-            return root;
-        }
-        final Map<String, Object> trimmedRoot = new LinkedHashMap<>(root);
-        trimmedRoot.remove(key);
-        return trimmedRoot;
+    private static Map<String, Object> renderUsingJsonLayout(
+            final LogEvent logEvent) {
+        return renderUsing(logEvent, JSON_LAYOUT);
     }
 
 }
diff --git 
a/log4j-layout-json-template/src/test/java/org/apache/logging/log4j/layout/json/template/JsonTemplateLayoutTest.java
 
b/log4j-layout-json-template/src/test/java/org/apache/logging/log4j/layout/json/template/JsonTemplateLayoutTest.java
index 315e50f..38ef58f 100644
--- 
a/log4j-layout-json-template/src/test/java/org/apache/logging/log4j/layout/json/template/JsonTemplateLayoutTest.java
+++ 
b/log4j-layout-json-template/src/test/java/org/apache/logging/log4j/layout/json/template/JsonTemplateLayoutTest.java
@@ -1694,6 +1694,67 @@ public class JsonTemplateLayoutTest {
 
     }
 
+    @Test
+    public void test_unresolvable_nested_fields_are_skipped() {
+
+        // Create the event template.
+        final String eventTemplate = writeJson(Map(
+                "exception", Map(
+                        "message", Map(
+                                "$resolver", "exception",
+                                "field", "message"),
+                        "className", Map(
+                                "$resolver", "exception",
+                                "field", "className")),
+                "exceptionRootCause", Map(
+                        "message", Map(
+                                "$resolver", "exceptionRootCause",
+                                "field", "message"),
+                        "className", Map(
+                                "$resolver", "exceptionRootCause",
+                                "field", "className")),
+                "source", Map(
+                        "lineNumber", Map(
+                                "$resolver", "source",
+                                "field", "lineNumber"),
+                        "fileName", Map(
+                                "$resolver", "source",
+                                "field", "fileName")),
+                "emptyMap", Collections.emptyMap(),
+                "emptyList", Collections.emptyList(),
+                "null", null));
+
+        // Create the layout.
+        final JsonTemplateLayout layout = JsonTemplateLayout
+                .newBuilder()
+                .setConfiguration(CONFIGURATION)
+                .setEventTemplate(eventTemplate)
+                .setStackTraceEnabled(false)        // Disable "exception" and 
"exceptionRootCause" resolvers.
+                .setLocationInfoEnabled(false)      // Disable the "source" 
resolver.
+                .build();
+
+        // Create the log event.
+        final SimpleMessage message = new SimpleMessage("foo");
+        final Level level = Level.FATAL;
+        final Exception thrown = new RuntimeException("bar");
+        final LogEvent logEvent = Log4jLogEvent
+                .newBuilder()
+                .setLoggerName(LOGGER_NAME)
+                .setMessage(message)
+                .setLevel(level)
+                .setThrown(thrown)
+                .build();
+
+        // Check the serialized event.
+        final String expectedSerializedLogEventJson =
+                "{}" + JsonTemplateLayoutDefaults.getEventDelimiter();
+        final String actualSerializedLogEventJson = 
layout.toSerializable(logEvent);
+        Assertions
+                .assertThat(actualSerializedLogEventJson)
+                .isEqualTo(expectedSerializedLogEventJson);
+
+    }
+
     private static String writeJson(final Object value) {
         final StringBuilder stringBuilder = JSON_WRITER.getStringBuilder();
         stringBuilder.setLength(0);

Reply via email to