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);