You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ck...@apache.org on 2019/12/31 14:47:41 UTC

[logging-log4j2] branch master updated: LOG4J2-2752: MutableLogEvent and RingBufferLogEvent lazily create buffers

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 8504933  LOG4J2-2752: MutableLogEvent and RingBufferLogEvent lazily create buffers
8504933 is described below

commit 8504933502bd9e6cf801968adda4c85532a0ea7b
Author: Carter Kozak <ck...@ckozak.net>
AuthorDate: Mon Dec 30 17:12:11 2019 -0500

    LOG4J2-2752: MutableLogEvent and RingBufferLogEvent lazily create buffers
    
    When reusable events are used with non-reusable message implementations
    (ParameterizedMessageFactory) there's no need to create these buffers.
---
 .../log4j/core/async/RingBufferLogEvent.java       | 31 +++++++++++-----------
 .../logging/log4j/core/impl/MutableLogEvent.java   | 16 +++++------
 .../log4j/core/impl/MutableLogEventTest.java       |  5 ++++
 src/changes/changes.xml                            |  3 +++
 4 files changed, 29 insertions(+), 26 deletions(-)

diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
index 030ce56..0434eb6 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
@@ -60,12 +60,7 @@ public class RingBufferLogEvent implements LogEvent, ReusableMessage, CharSequen
 
         @Override
         public RingBufferLogEvent newInstance() {
-            final RingBufferLogEvent result = new RingBufferLogEvent();
-            if (Constants.ENABLE_THREADLOCALS) {
-                result.messageText = new StringBuilder(Constants.INITIAL_REUSABLE_MESSAGE_SIZE);
-                result.parameters = new Object[10];
-            }
-            return result;
+            return new RingBufferLogEvent();
         }
     }
 
@@ -134,10 +129,8 @@ public class RingBufferLogEvent implements LogEvent, ReusableMessage, CharSequen
             final ReusableMessage reusable = (ReusableMessage) msg;
             reusable.formatTo(getMessageTextForWriting());
             messageFormat = reusable.getFormat();
-            if (parameters != null) {
-                parameters = reusable.swapParameters(parameters);
-                parameterCount = reusable.getParameterCount();
-            }
+            parameters = reusable.swapParameters(parameters == null ? new Object[10] : parameters);
+            parameterCount = reusable.getParameterCount();
         } else {
             this.message = InternalAsyncUtil.makeMessageImmutable(msg);
         }
@@ -145,8 +138,8 @@ public class RingBufferLogEvent implements LogEvent, ReusableMessage, CharSequen
 
     private StringBuilder getMessageTextForWriting() {
         if (messageText == null) {
-            // Should never happen:
-            // only happens if user logs a custom reused message when Constants.ENABLE_THREADLOCALS is false
+            // Happens the first time messageText is requested or if a user logs
+            // a custom reused message when Constants.ENABLE_THREADLOCALS is false
             messageText = new StringBuilder(Constants.INITIAL_REUSABLE_MESSAGE_SIZE);
         }
         messageText.setLength(0);
@@ -412,12 +405,18 @@ public class RingBufferLogEvent implements LogEvent, ReusableMessage, CharSequen
         }
 
         // ensure that excessively long char[] arrays are not kept in memory forever
-        StringBuilders.trimToMaxSize(messageText, Constants.MAX_REUSABLE_MESSAGE_SIZE);
+        if (Constants.ENABLE_THREADLOCALS) {
+            StringBuilders.trimToMaxSize(messageText, Constants.MAX_REUSABLE_MESSAGE_SIZE);
 
-        if (parameters != null) {
-            for (int i = 0; i < parameters.length; i++) {
-                parameters[i] = null;
+            if (parameters != null) {
+                Arrays.fill(parameters, null);
             }
+        } else {
+            // A user may have manually logged a ReusableMessage implementation, when thread locals are
+            // disabled we remove the reference in order to avoid permanently holding references to these
+            // buffers.
+            messageText = null;
+            parameters = null;
         }
     }
 
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java
index e38afae..7674491 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java
@@ -69,7 +69,8 @@ public class MutableLogEvent implements LogEvent, ReusableMessage, ParameterVisi
     transient boolean reserved = false;
 
     public MutableLogEvent() {
-        this(new StringBuilder(Constants.INITIAL_REUSABLE_MESSAGE_SIZE), new Object[10]);
+        // messageText and the parameter array are lazily initialized
+        this(null, null);
     }
 
     public MutableLogEvent(final StringBuilder msgText, final Object[] replacementParameters) {
@@ -148,9 +149,7 @@ public class MutableLogEvent implements LogEvent, ReusableMessage, ParameterVisi
         StringBuilders.trimToMaxSize(messageText, Constants.MAX_REUSABLE_MESSAGE_SIZE);
 
         if (parameters != null) {
-            for (int i = 0; i < parameters.length; i++) {
-                parameters[i] = null;
-            }
+            Arrays.fill(parameters, null);
         }
 
         // primitive fields that cannot be cleared:
@@ -214,10 +213,8 @@ public class MutableLogEvent implements LogEvent, ReusableMessage, ParameterVisi
             final ReusableMessage reusable = (ReusableMessage) msg;
             reusable.formatTo(getMessageTextForWriting());
             this.messageFormat = msg.getFormat();
-            if (parameters != null) {
-                parameters = reusable.swapParameters(parameters);
-                parameterCount = reusable.getParameterCount();
-            }
+            parameters = reusable.swapParameters(parameters == null ? new Object[10] : parameters);
+            parameterCount = reusable.getParameterCount();
         } else {
             this.message = InternalAsyncUtil.makeMessageImmutable(msg);
         }
@@ -225,8 +222,7 @@ public class MutableLogEvent implements LogEvent, ReusableMessage, ParameterVisi
 
     private StringBuilder getMessageTextForWriting() {
         if (messageText == null) {
-            // Should never happen:
-            // only happens if user logs a custom reused message when Constants.ENABLE_THREADLOCALS is false
+            // Happens the first time messageText is requested
             messageText = new StringBuilder(Constants.INITIAL_REUSABLE_MESSAGE_SIZE);
         }
         messageText.setLength(0);
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java
index a1f4f91..7e94ca6 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java
@@ -31,6 +31,7 @@ import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.message.Message;
 import org.apache.logging.log4j.message.ParameterizedMessage;
 import org.apache.logging.log4j.message.ReusableMessageFactory;
+import org.apache.logging.log4j.message.ReusableSimpleMessage;
 import org.apache.logging.log4j.message.SimpleMessage;
 import org.apache.logging.log4j.util.FilteredObjectInputStream;
 import org.apache.logging.log4j.util.SortedArrayStringMap;
@@ -189,6 +190,10 @@ public class MutableLogEventTest {
     @Test
     public void testClear() {
         final MutableLogEvent mutable = new MutableLogEvent();
+        // initialize the event with an empty message
+        ReusableSimpleMessage simpleMessage = new ReusableSimpleMessage();
+        simpleMessage.set("");
+        mutable.setMessage(simpleMessage);
         assertEquals("context data", 0, mutable.getContextData().size());
         assertNull("context stack", mutable.getContextStack());
         assertFalse("end of batch", mutable.isEndOfBatch());
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 92222b6..3bc396f 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -193,6 +193,9 @@
       <action issue="LOG4J2-2751" dev="ckozak" type="fix">
         Fix StackLocator.getCallerClass performance in cases where Reflection.getCallerClass is not accessible.
       </action>
+      <action issue="LOG4J2-2752" dev="ckozak" type="fix">
+        MutableLogEvent and RingBufferLogEvent avoid StringBuffer and parameter array allocation unless reusable messages are used.
+      </action>
     </release>
     <release version="2.13.0" date="2019-12-11" description="GA Release 2.13.0">
       <action issue="LOG4J2-2058" dev="rgoers" type="fix">