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/master 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;
