LOG4J2-1342 added methods to ReusableMessage to enable passing parameters to 
async logger background threads


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/79e51b23
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/79e51b23
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/79e51b23

Branch: refs/heads/LOG4j2-494
Commit: 79e51b23e1b74b9233c603067fd1da2f0a721130
Parents: 873e19b
Author: rpopma <[email protected]>
Authored: Fri Apr 22 12:10:34 2016 +0900
Committer: Ralph Goers <[email protected]>
Committed: Mon Apr 25 21:30:28 2016 -0700

----------------------------------------------------------------------
 .../logging/log4j/message/ReusableMessage.java  | 31 +++++++
 .../log4j/message/ReusableObjectMessage.java    | 19 ++++
 .../message/ReusableParameterizedMessage.java   | 20 ++++
 .../log4j/message/ReusableSimpleMessage.java    | 18 ++++
 .../core/async/AsyncLoggerConfigDisruptor.java  |  2 +-
 .../log4j/core/async/RingBufferLogEvent.java    | 98 +++++++++++++-------
 .../log4j/core/impl/MutableLogEvent.java        | 67 ++++++++++---
 7 files changed, 209 insertions(+), 46 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/79e51b23/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableMessage.java
----------------------------------------------------------------------
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableMessage.java 
b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableMessage.java
index a6da9c4..0536b23 100644
--- 
a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableMessage.java
+++ 
b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableMessage.java
@@ -30,4 +30,35 @@ import 
org.apache.logging.log4j.util.StringBuilderFormattable;
  */
 @PerformanceSensitive("allocation")
 public interface ReusableMessage extends Message, StringBuilderFormattable {
+
+    /**
+     * Returns the parameter array that was used to initialize this reusable 
message and replaces it with the specified
+     * array. The returned parameter array will no longer be modified by this 
reusable message. The specified array is
+     * now "owned" by this reusable message and can be modified if necessary 
for the next log event.
+     * </p><p>
+     * ReusableMessages that have no parameters return the specified array.
+     * </p><p>
+     * This method is used by asynchronous loggers to pass the parameter array 
to a background thread without
+     * allocating new objects.
+     * The actual number of parameters in the returned array can be determined 
with {@link #getParameterCount()}.
+     * </p>
+     *
+     * @param emptyReplacement the parameter array that can be used for 
subsequent uses of this reusable message.
+     *         This replacement array must have at least 10 elements (the 
number of varargs supported by the Logger
+     *         API).
+     * @return the parameter array for the current message content. This may 
be a vararg array of any length, or it may
+     *         be a reusable array of 10 elements used to hold the unrolled 
vararg parameters.
+     * @see #getParameterCount()
+     */
+    Object[] swapParameters(Object[] emptyReplacement);
+
+    /**
+     * Returns the number of parameters that was used to initialize this 
reusable message for the current content.
+     * <p>
+     * The parameter array returned by {@link #swapParameters(Object[])} may 
be larger than the actual number of
+     * parameters. Callers should use this method to determine how many 
elements the array contains.
+     * </p>
+     * @return the current number of parameters
+     */
+    short getParameterCount();
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/79e51b23/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableObjectMessage.java
----------------------------------------------------------------------
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableObjectMessage.java
 
b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableObjectMessage.java
index 12f62f4..920eff4 100644
--- 
a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableObjectMessage.java
+++ 
b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableObjectMessage.java
@@ -91,4 +91,23 @@ public class ReusableObjectMessage implements 
ReusableMessage {
     public Throwable getThrowable() {
         return obj instanceof Throwable ? (Throwable) obj : null;
     }
+
+    /**
+     * This message does not have any parameters, so this method returns the 
specified array.
+     * @param emptyReplacement the parameter array to return
+     * @return the specified array
+     */
+    @Override
+    public Object[] swapParameters(final Object[] emptyReplacement) {
+        return emptyReplacement;
+    }
+
+    /**
+     * This message does not have any parameters so this method always returns 
zero.
+     * @return 0 (zero)
+     */
+    @Override
+    public short getParameterCount() {
+        return 0;
+    }
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/79e51b23/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java
----------------------------------------------------------------------
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java
 
b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java
index 87cdc1d..4913c64 100644
--- 
a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java
+++ 
b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java
@@ -55,6 +55,26 @@ public class ReusableParameterizedMessage implements 
ReusableMessage {
         return varargs == null ? params : varargs;
     }
 
+    // see interface javadoc
+    @Override
+    public Object[] swapParameters(final Object[] emptyReplacement) {
+        Object[] result;
+        if (varargs == null) {
+            result = params;
+            params = emptyReplacement;
+        } else {
+            result = varargs;
+            varargs = emptyReplacement;
+        }
+        return result;
+    }
+
+    // see interface javadoc
+    @Override
+    public short getParameterCount() {
+        return (short) argCount;
+    }
+
     private void init(final String messagePattern, final int argCount, final 
Object[] paramArray) {
         this.varargs = null;
         this.messagePattern = messagePattern;

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/79e51b23/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableSimpleMessage.java
----------------------------------------------------------------------
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableSimpleMessage.java
 
b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableSimpleMessage.java
index ed6e39e..9fae16c 100644
--- 
a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableSimpleMessage.java
+++ 
b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableSimpleMessage.java
@@ -61,6 +61,24 @@ public class ReusableSimpleMessage implements 
ReusableMessage, CharSequence {
         buffer.append(charSequence);
     }
 
+    /**
+     * This message does not have any parameters, so this method returns the 
specified array.
+     * @param emptyReplacement the parameter array to return
+     * @return the specified array
+     */
+    @Override
+    public Object[] swapParameters(final Object[] emptyReplacement) {
+        return emptyReplacement;
+    }
+
+    /**
+     * This message does not have any parameters so this method always returns 
zero.
+     * @return 0 (zero)
+     */
+    @Override
+    public short getParameterCount() {
+        return 0;
+    }
 
     // CharSequence impl
 

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/79e51b23/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigDisruptor.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigDisruptor.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigDisruptor.java
index 2c1b84f..503e7d8 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigDisruptor.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigDisruptor.java
@@ -145,7 +145,7 @@ public class AsyncLoggerConfigDisruptor implements 
AsyncLoggerConfigDelegate {
     private static final EventFactory<Log4jEventWrapper> MUTABLE_FACTORY = new 
EventFactory<Log4jEventWrapper>() {
         @Override
         public Log4jEventWrapper newInstance() {
-            return new Log4jEventWrapper(new MutableLogEvent());
+            return new Log4jEventWrapper(new MutableLogEvent(new Object[10]));
         }
     };
 

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/79e51b23/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
----------------------------------------------------------------------
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 e1e6ab0..72ae8ac 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
@@ -16,7 +16,11 @@
  */
 package org.apache.logging.log4j.core.async;
 
-import com.lmax.disruptor.EventFactory;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.Marker;
 import org.apache.logging.log4j.ThreadContext.ContextStack;
@@ -32,9 +36,7 @@ import org.apache.logging.log4j.message.SimpleMessage;
 import org.apache.logging.log4j.message.TimestampMessage;
 import org.apache.logging.log4j.util.Strings;
 
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
+import com.lmax.disruptor.EventFactory;
 
 /**
  * When the Disruptor is started, the RingBuffer is populated with event 
objects. These objects are then re-used during
@@ -46,7 +48,6 @@ public class RingBufferLogEvent implements LogEvent, 
ReusableMessage, CharSequen
     public static final Factory FACTORY = new Factory();
 
     private static final long serialVersionUID = 8462119088943934758L;
-    private static final Object[] PARAMS = new Object[0];
     private static final Message EMPTY = new SimpleMessage(Strings.EMPTY);
 
     /**
@@ -59,56 +60,63 @@ public class RingBufferLogEvent implements LogEvent, 
ReusableMessage, CharSequen
             RingBufferLogEvent result = new RingBufferLogEvent();
             if (Constants.ENABLE_THREADLOCALS) {
                 result.messageText = new 
StringBuilder(Constants.INITIAL_REUSABLE_MESSAGE_SIZE);
+                result.parameters = new Object[10];
             }
             return result;
         }
     }
 
-    private transient AsyncLogger asyncLogger;
-    private String loggerName;
-    private Marker marker;
-    private String fqcn;
+    private int threadPriority;
+    private long threadId;
+    private long currentTimeMillis;
+    private long nanoTime;
+    private short parameterCount;
+    private boolean includeLocation;
+    private boolean endOfBatch = false;
     private Level level;
-    private StringBuilder messageText;
+    private String threadName;
+    private String loggerName;
     private Message message;
+    private StringBuilder messageText;
+    private Object[] parameters;
     private transient Throwable thrown;
     private ThrowableProxy thrownProxy;
     private Map<String, String> contextMap;
-    private ContextStack contextStack;
-    private long threadId;
-    private String threadName;
-    private int threadPriority;
+    private Marker marker;
+    private String fqcn;
     private StackTraceElement location;
-    private long currentTimeMillis;
-    private boolean endOfBatch;
-    private boolean includeLocation;
-    private long nanoTime;
+    private ContextStack contextStack;
+
+    private transient AsyncLogger asyncLogger;
 
     public void setValues(final AsyncLogger anAsyncLogger, final String 
aLoggerName, final Marker aMarker,
             final String theFqcn, final Level aLevel, final Message msg, final 
Throwable aThrowable,
             final Map<String, String> aMap, final ContextStack aContextStack, 
long threadId,
             final String threadName, int threadPriority, final 
StackTraceElement aLocation, final long aCurrentTimeMillis, final long 
aNanoTime) {
-        this.asyncLogger = anAsyncLogger;
-        this.loggerName = aLoggerName;
-        this.marker = aMarker;
-        this.fqcn = theFqcn;
+        this.threadPriority = threadPriority;
+        this.threadId = threadId;
+        this.currentTimeMillis = aCurrentTimeMillis;
+        this.nanoTime = aNanoTime;
         this.level = aLevel;
+        this.threadName = threadName;
+        this.loggerName = aLoggerName;
+        setMessage(msg);
         this.thrown = aThrowable;
         this.thrownProxy = null;
         this.contextMap = aMap;
-        this.contextStack = aContextStack;
-        this.threadId = threadId;
-        this.threadName = threadName;
-        this.threadPriority = threadPriority;
+        this.marker = aMarker;
+        this.fqcn = theFqcn;
         this.location = aLocation;
-        this.currentTimeMillis = aCurrentTimeMillis;
-        this.nanoTime = aNanoTime;
-        setMessage(msg);
+        this.contextStack = aContextStack;
+        this.asyncLogger = anAsyncLogger;
     }
 
     private void setMessage(final Message msg) {
         if (msg instanceof ReusableMessage) {
-            ((ReusableMessage) msg).formatTo(getMessageTextForWriting());
+            ReusableMessage reusable = (ReusableMessage) msg;
+            reusable.formatTo(getMessageTextForWriting());
+            parameters = reusable.swapParameters(parameters);
+            parameterCount = reusable.getParameterCount();
         } else {
             // if the Message instance is reused, there is no point in 
freezing its message here
             if (!Constants.FORMAT_MESSAGES_IN_BACKGROUND && msg != null) { // 
LOG4J2-898: user may choose
@@ -215,7 +223,7 @@ public class RingBufferLogEvent implements LogEvent, 
ReusableMessage, CharSequen
      */
     @Override
     public Object[] getParameters() {
-        return PARAMS;
+        return parameters == null ? null : Arrays.copyOf(parameters, 
parameterCount);
     }
 
     /**
@@ -234,6 +242,27 @@ public class RingBufferLogEvent implements LogEvent, 
ReusableMessage, CharSequen
         buffer.append(messageText);
     }
 
+    /**
+     * Replaces this ReusableMessage's parameter array with the specified 
value and return the original array
+     * @param emptyReplacement the parameter array that can be used for 
subsequent uses of this reusable message
+     * @return the original parameter array
+     * @see ReusableMessage#swapParameters(Object[])
+     */
+    @Override
+    public Object[] swapParameters(final Object[] emptyReplacement) {
+        final Object[] result = this.parameters;
+        this.parameters = emptyReplacement;
+        return result;
+    }
+
+    /*
+     * @see ReusableMessage#getParameterCount
+     */
+    @Override
+    public short getParameterCount() {
+        return parameterCount;
+    }
+
 
     // CharSequence impl
 
@@ -362,7 +391,14 @@ public class RingBufferLogEvent implements LogEvent, 
ReusableMessage, CharSequen
         this.contextMap = null;
         this.contextStack = null;
         this.location = null;
+
         trimMessageText();
+
+        if (parameters != null) {
+            for (int i = 0; i < parameters.length; i++) {
+                parameters[i] = null;
+            }
+        }
     }
 
     // ensure that excessively long char[] arrays are not kept in memory 
forever

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/79e51b23/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java
----------------------------------------------------------------------
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 b619615..0393b0c 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
@@ -18,6 +18,7 @@ package org.apache.logging.log4j.core.impl;
 
 import java.io.InvalidObjectException;
 import java.io.ObjectInputStream;
+import java.util.Arrays;
 import java.util.Map;
 
 import org.apache.logging.log4j.Level;
@@ -35,27 +36,36 @@ import org.apache.logging.log4j.util.Strings;
  * @since 2.6
  */
 public class MutableLogEvent implements LogEvent, ReusableMessage {
-    private static final Object[] PARAMS = new Object[0];
     private static final Message EMPTY = new SimpleMessage(Strings.EMPTY);
 
-    private String loggerFqcn;
-    private Marker marker;
+    private int threadPriority;
+    private long threadId;
+    private long timeMillis;
+    private long nanoTime;
+    private short parameterCount;
+    private boolean includeLocation;
+    private boolean endOfBatch = false;
     private Level level;
+    private String threadName;
     private String loggerName;
     private Message message;
-    private long timeMillis;
+    private StringBuilder messageText;
+    private Object[] parameters;
     private Throwable thrown;
     private ThrowableProxy thrownProxy;
     private Map<String, String> contextMap;
-    private ThreadContext.ContextStack contextStack;
-    private long threadId;
-    private String threadName;
-    private int threadPriority;
+    private Marker marker;
+    private String loggerFqcn;
     private StackTraceElement source;
-    private boolean includeLocation;
-    private boolean endOfBatch = false;
-    private long nanoTime;
-    private StringBuilder messageText;
+    private ThreadContext.ContextStack contextStack;
+
+    public MutableLogEvent() {
+        this(null);
+    }
+
+    public MutableLogEvent(final Object[] replacementParameters) {
+        this.parameters = replacementParameters;
+    }
 
     /**
      * Initialize the fields of this {@code MutableLogEvent} from another 
event.
@@ -107,6 +117,11 @@ public class MutableLogEvent implements LogEvent, 
ReusableMessage {
         // threadName = null; // no need to clear threadName
 
         trimMessageText();
+        if (parameters != null) {
+            for (int i = 0; i < parameters.length; i++) {
+                parameters[i] = null;
+            }
+        }
 
         // primitive fields that cannot be cleared:
         //timeMillis;
@@ -171,7 +186,10 @@ public class MutableLogEvent implements LogEvent, 
ReusableMessage {
 
     public void setMessage(final Message msg) {
         if (msg instanceof ReusableMessage) {
-            ((ReusableMessage) msg).formatTo(getMessageTextForWriting()); // 
init messageText
+            ReusableMessage reusable = (ReusableMessage) msg;
+            reusable.formatTo(getMessageTextForWriting());
+            parameters = reusable.swapParameters(parameters);
+            parameterCount = reusable.getParameterCount();
         } else {
             // if the Message instance is reused, there is no point in 
freezing its message here
             if (!Constants.FORMAT_MESSAGES_IN_BACKGROUND && msg != null) { // 
LOG4J2-898: user may choose
@@ -212,7 +230,7 @@ public class MutableLogEvent implements LogEvent, 
ReusableMessage {
      */
     @Override
     public Object[] getParameters() {
-        return PARAMS;
+        return parameters == null ? null : Arrays.copyOf(parameters, 
parameterCount);
     }
 
     /**
@@ -231,6 +249,27 @@ public class MutableLogEvent implements LogEvent, 
ReusableMessage {
         buffer.append(messageText);
     }
 
+    /**
+     * Replaces this ReusableMessage's parameter array with the specified 
value and return the original array
+     * @param emptyReplacement the parameter array that can be used for 
subsequent uses of this reusable message
+     * @return the original parameter array
+     * @see ReusableMessage#swapParameters(Object[])
+     */
+    @Override
+    public Object[] swapParameters(final Object[] emptyReplacement) {
+        final Object[] result = this.parameters;
+        this.parameters = emptyReplacement;
+        return result;
+    }
+
+    /*
+     * @see ReusableMessage#getParameterCount
+     */
+    @Override
+    public short getParameterCount() {
+        return parameterCount;
+    }
+
     @Override
     public Throwable getThrown() {
         return thrown;

Reply via email to