diff --git a/CHANGELOG.md b/CHANGELOG.md index 781d0f4..4c535bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ - Fix `ArrayIndexOutOfBoundsException` thrown when `stackTrace:text` produces an output violating the truncation limit. (#57) +- Implement work around for FasterXML/jackson-core#609 triggered when + `maxStringLength` is used in combination with + `emptyPropertyExclusionEnabled=true`. (#55) + ### (2020-01-21) v1.0.1 - Fix NPE in `StringTruncatingGeneratorDelegate`. (#53) diff --git a/layout/src/main/java/com/vlkan/log4j2/logstash/layout/LogstashLayoutSerializationContexts.java b/layout/src/main/java/com/vlkan/log4j2/logstash/layout/LogstashLayoutSerializationContexts.java index d85fac9..97e5b22 100644 --- a/layout/src/main/java/com/vlkan/log4j2/logstash/layout/LogstashLayoutSerializationContexts.java +++ b/layout/src/main/java/com/vlkan/log4j2/logstash/layout/LogstashLayoutSerializationContexts.java @@ -91,12 +91,13 @@ private static JsonGenerator createJsonGenerator( if (prettyPrintEnabled) { jsonGenerator.setPrettyPrinter(PRETTY_PRINTER); } - if (emptyPropertyExclusionEnabled) { - jsonGenerator = new FilteringGeneratorDelegate(jsonGenerator, NullExcludingTokenFilter.INSTANCE, true, true); - } if (maxStringLength > 0) { jsonGenerator = new StringTruncatingGeneratorDelegate(jsonGenerator, maxStringLength); } + // TokenFilter has to come last due to FasterXML/jackson-core#609. + if (emptyPropertyExclusionEnabled) { + jsonGenerator = new FilteringGeneratorDelegate(jsonGenerator, NullExcludingTokenFilter.INSTANCE, true, true); + } return jsonGenerator; } catch (IOException error) { throw new RuntimeException("failed creating JsonGenerator", error); diff --git a/layout/src/test/java/com/vlkan/log4j2/logstash/layout/LogstashLayoutTest.java b/layout/src/test/java/com/vlkan/log4j2/logstash/layout/LogstashLayoutTest.java index 8af4183..b2da603 100644 --- a/layout/src/test/java/com/vlkan/log4j2/logstash/layout/LogstashLayoutTest.java +++ b/layout/src/test/java/com/vlkan/log4j2/logstash/layout/LogstashLayoutTest.java @@ -24,6 +24,7 @@ import com.fasterxml.jackson.databind.node.MissingNode; import com.fasterxml.jackson.databind.node.NullNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import joptsimple.internal.Strings; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Marker; @@ -32,7 +33,6 @@ import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.DefaultConfiguration; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; -import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; import org.apache.logging.log4j.core.impl.Log4jLogEvent; import org.apache.logging.log4j.core.layout.ByteBufferDestination; import org.apache.logging.log4j.core.lookup.MainMapLookup; @@ -245,7 +245,7 @@ public void test_inline_template() throws Exception { String eventTemplate = eventTemplateRootNode.toString(); // Create the layout. - BuiltConfiguration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + Configuration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); String timeZoneId = TimeZone.getTimeZone("Europe/Amsterdam").getID(); LogstashLayout layout = LogstashLayout .newBuilder() @@ -275,7 +275,7 @@ public void test_log4j_deferred_runtime_resolver_for_MapMessage() throws Excepti String eventTemplate = eventTemplateRootNode.toString(); // Create the layout. - BuiltConfiguration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + Configuration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); LogstashLayout layout = LogstashLayout .newBuilder() .setConfiguration(configuration) @@ -312,7 +312,7 @@ public void test_MapMessage_serialization() throws Exception { String eventTemplate = eventTemplateRootNode.toString(); // Create the layout. - BuiltConfiguration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + Configuration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); LogstashLayout layout = LogstashLayout .newBuilder() .setConfiguration(configuration) @@ -403,7 +403,7 @@ public void test_empty_root_cause() throws Exception { String eventTemplate = eventTemplateRootNode.toString(); // Create the layout. - BuiltConfiguration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + Configuration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); LogstashLayout layout = LogstashLayout .newBuilder() .setConfiguration(configuration) @@ -449,7 +449,7 @@ public void test_root_cause() throws Exception { String eventTemplate = eventTemplateRootNode.toString(); // Create the layout. - BuiltConfiguration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + Configuration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); LogstashLayout layout = LogstashLayout .newBuilder() .setConfiguration(configuration) @@ -493,7 +493,7 @@ public void test_marker_name() throws IOException { String eventTemplate = eventTemplateRootNode.toString(); // Create the layout. - BuiltConfiguration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + Configuration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); LogstashLayout layout = LogstashLayout .newBuilder() .setConfiguration(configuration) @@ -529,7 +529,7 @@ public void test_lineSeparator_suffix() { private void test_lineSeparator_suffix(LogEvent logEvent, boolean prettyPrintEnabled) { // Create the layout. - BuiltConfiguration config = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + Configuration config = ConfigurationBuilderFactory.newConfigurationBuilder().build(); LogstashLayout layout = LogstashLayout .newBuilder() .setConfiguration(config) @@ -575,7 +575,7 @@ public void test_main_key_access() throws IOException { String template = templateRootNode.toString(); // Create the layout. - BuiltConfiguration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + Configuration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); LogstashLayout layout = LogstashLayout .newBuilder() .setConfiguration(configuration) @@ -627,7 +627,7 @@ public void test_mdc_key_access() throws IOException { String eventTemplate = eventTemplateRootNode.toString(); // Create the layout. - BuiltConfiguration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + Configuration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); LogstashLayout layout = LogstashLayout .newBuilder() .setConfiguration(configuration) @@ -665,7 +665,7 @@ public void test_MapResolver() throws IOException { String eventTemplate = eventTemplateRootNode.toString(); // Create the layout. - BuiltConfiguration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + Configuration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); LogstashLayout layout = LogstashLayout .newBuilder() .setConfiguration(configuration) @@ -789,7 +789,7 @@ public void test_message_json() throws IOException { String eventTemplate = eventTemplateRootNode.toString(); // Create the layout. - BuiltConfiguration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + Configuration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); LogstashLayout layout = LogstashLayout .newBuilder() .setConfiguration(configuration) @@ -823,7 +823,7 @@ public void test_message_json_fallback() throws IOException { String eventTemplate = eventTemplateRootNode.toString(); // Create the layout. - BuiltConfiguration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + Configuration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); LogstashLayout layout = LogstashLayout .newBuilder() .setConfiguration(configuration) @@ -874,7 +874,7 @@ public void test_message_object() throws IOException { String eventTemplate = eventTemplateRootNode.toString(); // Create the layout. - BuiltConfiguration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + Configuration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); LogstashLayout layout = LogstashLayout .newBuilder() .setConfiguration(configuration) @@ -912,7 +912,7 @@ public void test_StackTraceElement_template() throws IOException { String eventTemplate = eventTemplateRootNode.toString(); // Create the layout. - BuiltConfiguration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + Configuration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); LogstashLayout layout = LogstashLayout .newBuilder() .setConfiguration(configuration) @@ -956,7 +956,7 @@ public void test_StackTraceElement_template() throws IOException { public void test_toSerializable_toByteArray_encode_outputs() { // Create the layout. - BuiltConfiguration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + Configuration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); LogstashLayout layout = LogstashLayout .newBuilder() .setConfiguration(configuration) @@ -1035,7 +1035,7 @@ public void test_maxStringLength() throws IOException { String eventTemplate = eventTemplateRootNode.toString(); // Create the layout. - BuiltConfiguration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + Configuration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); LogstashLayout layout = LogstashLayout .newBuilder() .setConfiguration(configuration) @@ -1098,7 +1098,7 @@ public void test_exception_with_nonAscii_utf8_method_name() throws IOException { String eventTemplate = eventTemplateRootNode.toString(); // Create the layout. - BuiltConfiguration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + Configuration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); LogstashLayout layout = LogstashLayout .newBuilder() .setConfiguration(configuration) @@ -1133,7 +1133,7 @@ public void test_custom_ObjectMapper_factory_method() throws IOException { String eventTemplate = eventTemplateRootNode.toString(); // Create the layout. - BuiltConfiguration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + Configuration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); LogstashLayout layout = LogstashLayout .newBuilder() .setObjectMapperFactoryMethod("com.vlkan.log4j2.logstash.layout.LogstashLayoutTest.getCustomObjectMapper") @@ -1175,7 +1175,7 @@ public void test_event_template_additional_fields() throws IOException { String eventTemplate = eventTemplateRootNode.toString(); // Create the layout. - BuiltConfiguration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + Configuration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); KeyValuePair additionalField1 = new KeyValuePair("message", "${json:message}"); KeyValuePair additionalField2 = new KeyValuePair("@version", "1"); KeyValuePair[] additionalFieldPairs = {additionalField1, additionalField2}; @@ -1236,7 +1236,7 @@ public void test_timestamp_divisor() throws IOException { String eventTemplate = eventTemplateRootNode.toString(); // Create the layout. - BuiltConfiguration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + Configuration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); LogstashLayout layout = LogstashLayout .newBuilder() .setConfiguration(configuration) @@ -1272,7 +1272,7 @@ public void test_level_severity() throws IOException { String eventTemplate = eventTemplateRootNode.toString(); // Create the layout. - BuiltConfiguration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + Configuration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); LogstashLayout layout = LogstashLayout .newBuilder() .setConfiguration(configuration) @@ -1324,7 +1324,7 @@ public void test_exception_resolvers_against_no_exceptions() throws IOException String eventTemplate = eventTemplateRootNode.toString(); // Create the layout. - BuiltConfiguration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + Configuration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); LogstashLayout layout = LogstashLayout .newBuilder() .setConfiguration(configuration) @@ -1363,7 +1363,7 @@ public void test_timestamp_resolver() throws IOException { String eventTemplate = eventTemplateRootNode.toString(); // Create the layout. - BuiltConfiguration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + Configuration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); LogstashLayout layout = LogstashLayout .newBuilder() .setConfiguration(configuration) @@ -1442,4 +1442,39 @@ public void test_StackTraceTextResolver_with_maxStringLength() throws Exception } + @Test + public void test_maxStringLength_with_emptyPropertyExclusionEnabled() throws Exception { + + // Create the event template. + ObjectNode eventTemplateRootNode = JSON_NODE_FACTORY.objectNode(); + eventTemplateRootNode.put("message", "${json:message}"); + String eventTemplate = eventTemplateRootNode.toString(); + + // Create the layout. + int maxStringLength = eventTemplate.length(); + Configuration configuration = ConfigurationBuilderFactory.newConfigurationBuilder().build(); + LogstashLayout layout = LogstashLayout + .newBuilder() + .setConfiguration(configuration) + .setEventTemplate(eventTemplate) + .setMaxStringLength(maxStringLength) + .setEmptyPropertyExclusionEnabled(true) + .build(); + + // Create the log event. + SimpleMessage message = new SimpleMessage(Strings.repeat('m', maxStringLength) + 'x'); + LogEvent logEvent = Log4jLogEvent + .newBuilder() + .setLoggerName(LogstashLayoutTest.class.getSimpleName()) + .setMessage(message) + .build(); + + // Check the serialized event. + String serializedLogEvent = layout.toSerializable(logEvent); + JsonNode rootNode = OBJECT_MAPPER.readTree(serializedLogEvent); + String expectedMessage = message.getFormattedMessage().substring(0, maxStringLength); + assertThat(point(rootNode, "message").asText()).isEqualTo(expectedMessage); + + } + }