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 2020/11/06 16:19:00 UTC

[logging-log4j2] branch release-2.x updated: LOG4J2-2936 Make JSON template layout message parameter resolver garbage-free.

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

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


The following commit(s) were added to refs/heads/release-2.x by this push:
     new 319ea73  LOG4J2-2936 Make JSON template layout message parameter resolver garbage-free.
319ea73 is described below

commit 319ea73d97962cea298249da3608e4a38764d569
Author: Volkan Yazici <vo...@gmail.com>
AuthorDate: Fri Nov 6 17:18:43 2020 +0100

    LOG4J2-2936 Make JSON template layout message parameter resolver garbage-free.
---
 .../json/resolver/MessageParameterResolver.java    | 80 +++++++++++++++++++++-
 .../resolver/MessageParameterResolverFactory.java  |  2 +-
 .../template/json/JsonTemplateLayoutTest.java      | 17 ++++-
 .../asciidoc/manual/json-template-layout.adoc.vm   |  5 +-
 4 files changed, 96 insertions(+), 8 deletions(-)

diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MessageParameterResolver.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MessageParameterResolver.java
index a21dff6..b4d39f3 100644
--- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MessageParameterResolver.java
+++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MessageParameterResolver.java
@@ -18,7 +18,10 @@ package org.apache.logging.log4j.layout.template.json.resolver;
 
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.layout.template.json.util.JsonWriter;
+import org.apache.logging.log4j.layout.template.json.util.Recycler;
 import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.ParameterConsumer;
+import org.apache.logging.log4j.message.ParameterVisitable;
 
 /**
  * {@link Message} parameter (i.e., {@link Message#getParameters()}) resolver.
@@ -70,11 +73,18 @@ import org.apache.logging.log4j.message.Message;
  */
 final class MessageParameterResolver implements EventResolver {
 
+    private final Recycler<ParameterConsumerState> parameterConsumerStateRecycler;
+
     private final boolean stringified;
 
     private final int index;
 
-    MessageParameterResolver(final TemplateResolverConfig config) {
+    MessageParameterResolver(
+            final EventResolverContext context,
+            final TemplateResolverConfig config) {
+        this.parameterConsumerStateRecycler = context
+                .getRecyclerFactory()
+                .create(ParameterConsumerState::new);
         this.stringified = config.getBoolean("stringified", false);
         final Integer index = config.getInteger("index");
         if (index != null && index < 0) {
@@ -90,9 +100,17 @@ final class MessageParameterResolver implements EventResolver {
     @Override
     public void resolve(final LogEvent logEvent, final JsonWriter jsonWriter) {
 
+        // If possible, perform a garbage-free resolution.
+        final Message message = logEvent.getMessage();
+        if (message instanceof ParameterVisitable) {
+            final ParameterVisitable parameterVisitable = (ParameterVisitable) message;
+            resolve(parameterVisitable, jsonWriter);
+            return;
+        }
+
         // Short-circuit if there are no parameters.
-        final Object[] parameters = logEvent.getMessage().getParameters();
-        if (parameters.length == 0) {
+        final Object[] parameters = message.getParameters();
+        if (parameters == null || parameters.length == 0) {
             jsonWriter.writeNull();
             return;
         }
@@ -128,4 +146,60 @@ final class MessageParameterResolver implements EventResolver {
 
     }
 
+    /**
+     * Perform a garbage-free resolution via {@link ParameterVisitable} interface.
+     */
+    private void resolve(
+            final ParameterVisitable parameterVisitable,
+            final JsonWriter jsonWriter) {
+        final ParameterConsumerState parameterConsumerState =
+                parameterConsumerStateRecycler.acquire();
+        try {
+            final boolean arrayNeeded = index < 0;
+            if (arrayNeeded) {
+                jsonWriter.writeArrayStart();
+            }
+            parameterConsumerState.resolver = this;
+            parameterConsumerState.jsonWriter = jsonWriter;
+            parameterVisitable.forEachParameter(
+                    PARAMETER_CONSUMER, parameterConsumerState);
+            if (arrayNeeded) {
+                jsonWriter.writeArrayEnd();
+            }
+        } finally {
+            parameterConsumerStateRecycler.release(parameterConsumerState);
+        }
+    }
+
+    private static final class ParameterConsumerState {
+
+        private MessageParameterResolver resolver;
+
+        private JsonWriter jsonWriter;
+
+        private ParameterConsumerState() {}
+
+    }
+
+    private static final ParameterConsumer<ParameterConsumerState> PARAMETER_CONSUMER =
+            (final Object parameter, final int index, final ParameterConsumerState state) -> {
+
+                // Write the separator, if needed.
+                final boolean arrayNeeded = state.resolver.index < 0;
+                if (arrayNeeded && index > 0) {
+                    state.jsonWriter.writeSeparator();
+                }
+
+                // Write the value.
+                if (arrayNeeded || state.resolver.index == index) {
+                    if (state.resolver.stringified) {
+                        final String stringifiedParameter = String.valueOf(parameter);
+                        state.jsonWriter.writeString(stringifiedParameter);
+                    } else {
+                        state.jsonWriter.writeValue(parameter);
+                    }
+                }
+
+            };
+
 }
diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MessageParameterResolverFactory.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MessageParameterResolverFactory.java
index db1c369..055071c 100644
--- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MessageParameterResolverFactory.java
+++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MessageParameterResolverFactory.java
@@ -35,7 +35,7 @@ final class MessageParameterResolverFactory implements EventResolverFactory<Mess
     public MessageParameterResolver create(
             final EventResolverContext context,
             final TemplateResolverConfig config) {
-        return new MessageParameterResolver(config);
+        return new MessageParameterResolver(context, config);
     }
 
 }
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 6c5565e..9d2c3d9 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
@@ -39,8 +39,10 @@ import org.apache.logging.log4j.layout.template.json.util.JsonReader;
 import org.apache.logging.log4j.layout.template.json.util.JsonWriter;
 import org.apache.logging.log4j.layout.template.json.util.MapAccessor;
 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.ParameterizedMessage;
+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.test.AvailablePortFinder;
@@ -1787,7 +1789,16 @@ public class JsonTemplateLayoutTest {
     }
 
     @Test
-    public void test_MessageParameterResolver() {
+    public void test_MessageParameterResolver_with_ParameterizedMessageFactory() {
+        testMessageParameterResolver(ParameterizedMessageFactory.INSTANCE);
+    }
+
+    @Test
+    public void test_MessageParameterResolver_with_ReusableMessageFactory() {
+        testMessageParameterResolver(ReusableMessageFactory.INSTANCE);
+    }
+
+    private void testMessageParameterResolver(MessageFactory messageFactory) {
 
         // Create the event template.
         final String eventTemplate = writeJson(Map(
@@ -1813,7 +1824,7 @@ public class JsonTemplateLayoutTest {
 
         // Create the log event.
         final Object[] parameters = {1L + (long) Integer.MAX_VALUE, "foo", 56};
-        final Message message = new ParameterizedMessage("foo", parameters);
+        final Message message = messageFactory.newMessage("foo", parameters);
         final Level level = Level.FATAL;
         final LogEvent logEvent = Log4jLogEvent
                 .newBuilder()
diff --git a/src/site/asciidoc/manual/json-template-layout.adoc.vm b/src/site/asciidoc/manual/json-template-layout.adoc.vm
index 2bc7e44..d90f607 100644
--- a/src/site/asciidoc/manual/json-template-layout.adoc.vm
+++ b/src/site/asciidoc/manual/json-template-layout.adoc.vm
@@ -14,6 +14,7 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 ////
+#set($dollar = '$')
 = JSON Template Layout
 Volkan Yazıcı <vy...@apache.org>
 
@@ -814,7 +815,9 @@ index       = "index" -> number
 ----
 | `logEvent.getMessage().getParameters()`
 | `stringified` flag translates to `String.valueOf(value)`, hence mind
-  not-`String`-typed values.
+  not-`String`-typed values. Further, `logEvent.getMessage()` is expected to
+  implement `ParameterVisitable` interface, which is the case if
+  `log4j2.enableThreadlocals` property set to true.
 a|
 Resolve the message parameters into an array: