You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rp...@apache.org on 2016/02/25 18:05:47 UTC
[1/2] logging-log4j2 git commit: LOG4J2-1296 separate
ReusableParameterizedMessage from ParameterizedMessage
Repository: logging-log4j2
Updated Branches:
refs/heads/master 87f7fd167 -> 20e9d7688
LOG4J2-1296 separate ReusableParameterizedMessage from ParameterizedMessage
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/4cf4326f
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/4cf4326f
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/4cf4326f
Branch: refs/heads/master
Commit: 4cf4326f18338c09276e820474091cc418dff8f8
Parents: 954dc7d
Author: rpopma <rp...@apache.org>
Authored: Fri Feb 26 02:05:30 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Fri Feb 26 02:05:30 2016 +0900
----------------------------------------------------------------------
.../log4j/message/ParameterFormatter.java | 533 ++++++++++++++++
.../log4j/message/ParameterizedMessage.java | 638 +------------------
.../message/ReusableParameterizedMessage.java | 271 ++++++++
.../ReusableParameterizedMessageFactory.java | 13 +-
4 files changed, 845 insertions(+), 610 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4cf4326f/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterFormatter.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterFormatter.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterFormatter.java
new file mode 100644
index 0000000..960fe9a
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterFormatter.java
@@ -0,0 +1,533 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.message;
+
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.logging.log4j.util.StringBuilderFormattable;
+
+/**
+ * Supports parameter formatting as used in ParameterizedMessage and ReusableParameterizedMessage.
+ */
+final class ParameterFormatter {
+ /**
+ * Prefix for recursion.
+ */
+ static final String RECURSION_PREFIX = "[...";
+ /**
+ * Suffix for recursion.
+ */
+ static final String RECURSION_SUFFIX = "...]";
+
+ /**
+ * Prefix for errors.
+ */
+ static final String ERROR_PREFIX = "[!!!";
+ /**
+ * Separator for errors.
+ */
+ static final String ERROR_SEPARATOR = "=>";
+ /**
+ * Separator for error messages.
+ */
+ static final String ERROR_MSG_SEPARATOR = ":";
+ /**
+ * Suffix for errors.
+ */
+ static final String ERROR_SUFFIX = "!!!]";
+
+ private static final char DELIM_START = '{';
+ private static final char DELIM_STOP = '}';
+ private static final char ESCAPE_CHAR = '\\';
+
+ private static ThreadLocal<SimpleDateFormat> threadLocalSimpleDateFormat = new ThreadLocal<>();
+
+ private ParameterFormatter() {
+ }
+
+ /**
+ * Counts the number of unescaped placeholders in the given messagePattern.
+ *
+ * @param messagePattern the message pattern to be analyzed.
+ * @return the number of unescaped placeholders.
+ */
+ static int countArgumentPlaceholders(final String messagePattern) {
+ if (messagePattern == null) {
+ return 0;
+ }
+ int length = messagePattern.length();
+ int result = 0;
+ boolean isEscaped = false;
+ for (int i = 0; i < length - 1; i++) {
+ final char curChar = messagePattern.charAt(i);
+ if (curChar == ESCAPE_CHAR) {
+ isEscaped = !isEscaped;
+ } else if (curChar == DELIM_START) {
+ if (!isEscaped && messagePattern.charAt(i + 1) == DELIM_STOP) {
+ result++;
+ i++;
+ }
+ isEscaped = false;
+ } else {
+ isEscaped = false;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Replace placeholders in the given messagePattern with arguments.
+ *
+ * @param messagePattern the message pattern containing placeholders.
+ * @param arguments the arguments to be used to replace placeholders.
+ * @return the formatted message.
+ */
+ static String format(final String messagePattern, final Object[] arguments) {
+ final StringBuilder result = new StringBuilder();
+ final int argCount = arguments == null ? 0 : arguments.length;
+ formatMessage(result, messagePattern, arguments, argCount);
+ return result.toString();
+ }
+
+ /**
+ * Replace placeholders in the given messagePattern with arguments.
+ *
+ * @param buffer the buffer to write the formatted message into
+ * @param messagePattern the message pattern containing placeholders.
+ * @param arguments the arguments to be used to replace placeholders.
+ */
+ static void formatMessage(final StringBuilder buffer, final String messagePattern,
+ final Object[] arguments, final int argCount) {
+ if (messagePattern == null || arguments == null || argCount == 0) {
+ buffer.append(messagePattern);
+ return;
+ }
+ int escapeCounter = 0;
+ int currentArgument = 0;
+ int i = 0;
+ int len = messagePattern.length();
+ for (; i < len - 1; i++) { // last char is excluded from the loop
+ final char curChar = messagePattern.charAt(i);
+ if (curChar == ESCAPE_CHAR) {
+ escapeCounter++;
+ } else {
+ if (isDelimPair(curChar, messagePattern, i)) { // looks ahead one char
+ i++;
+
+ // write escaped escape chars
+ writeEscapedEscapeChars(escapeCounter, buffer);
+
+ if (isOdd(escapeCounter)) {
+ // i.e. escaped: write escaped escape chars
+ writeDelimPair(buffer);
+ } else {
+ // unescaped
+ writeArgOrDelimPair(arguments, argCount, currentArgument, buffer);
+ currentArgument++;
+ }
+ } else {
+ handleLiteralChar(buffer, escapeCounter, curChar);
+ }
+ escapeCounter = 0;
+ }
+ }
+ handleRemainingCharIfAny(messagePattern, len, buffer, escapeCounter, i);
+ }
+
+ /**
+ * Returns {@code true} if the specified char and the char at {@code curCharIndex + 1} in the specified message
+ * pattern together form a "{}" delimiter pair, returns {@code false} otherwise.
+ */
+ // Profiling showed this method is important to log4j performance. Modify with care!
+ // 22 bytes (allows immediate JVM inlining: < 35 bytes) LOG4J2-1096
+ private static boolean isDelimPair(final char curChar, final String messagePattern, final int curCharIndex) {
+ return curChar == DELIM_START && messagePattern.charAt(curCharIndex + 1) == DELIM_STOP;
+ }
+
+ /**
+ * Detects whether the message pattern has been fully processed or if an unprocessed character remains and processes
+ * it if necessary, returning the resulting position in the result char array.
+ */
+ // Profiling showed this method is important to log4j performance. Modify with care!
+ // 28 bytes (allows immediate JVM inlining: < 35 bytes) LOG4J2-1096
+ private static void handleRemainingCharIfAny(final String messagePattern, final int len,
+ final StringBuilder buffer, int escapeCounter, int i) {
+ if (i == len - 1) {
+ final char curChar = messagePattern.charAt(i);
+ handleLastChar(buffer, escapeCounter, curChar);
+ }
+ }
+
+ /**
+ * Processes the last unprocessed character and returns the resulting position in the result char array.
+ */
+ // Profiling showed this method is important to log4j performance. Modify with care!
+ // 28 bytes (allows immediate JVM inlining: < 35 bytes) LOG4J2-1096
+ private static void handleLastChar(final StringBuilder buffer, final int escapeCounter, final char curChar) {
+ if (curChar == ESCAPE_CHAR) {
+ writeUnescapedEscapeChars(escapeCounter + 1, buffer);
+ } else {
+ handleLiteralChar(buffer, escapeCounter, curChar);
+ }
+ }
+
+ /**
+ * Processes a literal char (neither an '\' escape char nor a "{}" delimiter pair) and returns the resulting
+ * position.
+ */
+ // Profiling showed this method is important to log4j performance. Modify with care!
+ // 16 bytes (allows immediate JVM inlining: < 35 bytes) LOG4J2-1096
+ private static void handleLiteralChar(final StringBuilder buffer, final int escapeCounter, final char curChar) {
+ // any other char beside ESCAPE or DELIM_START/STOP-combo
+ // write unescaped escape chars
+ writeUnescapedEscapeChars(escapeCounter, buffer);
+ buffer.append(curChar);
+ }
+
+ /**
+ * Writes "{}" to the specified result array at the specified position and returns the resulting position.
+ */
+ // Profiling showed this method is important to log4j performance. Modify with care!
+ // 18 bytes (allows immediate JVM inlining: < 35 bytes) LOG4J2-1096
+ private static void writeDelimPair(final StringBuilder buffer) {
+ buffer.append(DELIM_START);
+ buffer.append(DELIM_STOP);
+ }
+
+ /**
+ * Returns {@code true} if the specified parameter is odd.
+ */
+ // Profiling showed this method is important to log4j performance. Modify with care!
+ // 11 bytes (allows immediate JVM inlining: < 35 bytes) LOG4J2-1096
+ private static boolean isOdd(final int number) {
+ return (number & 1) == 1;
+ }
+
+ /**
+ * Writes a '\' char to the specified result array (starting at the specified position) for each <em>pair</em> of
+ * '\' escape chars encountered in the message format and returns the resulting position.
+ */
+ // Profiling showed this method is important to log4j performance. Modify with care!
+ // 11 bytes (allows immediate JVM inlining: < 35 bytes) LOG4J2-1096
+ private static void writeEscapedEscapeChars(final int escapeCounter, final StringBuilder buffer) {
+ final int escapedEscapes = escapeCounter >> 1; // divide by two
+ writeUnescapedEscapeChars(escapedEscapes, buffer);
+ }
+
+ /**
+ * Writes the specified number of '\' chars to the specified result array (starting at the specified position) and
+ * returns the resulting position.
+ */
+ // Profiling showed this method is important to log4j performance. Modify with care!
+ // 20 bytes (allows immediate JVM inlining: < 35 bytes) LOG4J2-1096
+ private static void writeUnescapedEscapeChars(int escapeCounter, final StringBuilder buffer) {
+ while (escapeCounter > 0) {
+ buffer.append(ESCAPE_CHAR);
+ escapeCounter--;
+ }
+ }
+
+ /**
+ * Appends the argument at the specified argument index (or, if no such argument exists, the "{}" delimiter pair) to
+ * the specified result char array at the specified position and returns the resulting position.
+ */
+ // Profiling showed this method is important to log4j performance. Modify with care!
+ // 25 bytes (allows immediate JVM inlining: < 35 bytes) LOG4J2-1096
+ private static void writeArgOrDelimPair(final Object[] arguments, final int argCount, final int currentArgument,
+ final StringBuilder buffer) {
+ if (currentArgument < argCount) {
+ recursiveDeepToString(arguments[currentArgument], buffer, null);
+ } else {
+ writeDelimPair(buffer);
+ }
+ }
+
+ /**
+ * This method performs a deep toString of the given Object.
+ * Primitive arrays are converted using their respective Arrays.toString methods while
+ * special handling is implemented for "container types", i.e. Object[], Map and Collection because those could
+ * contain themselves.
+ * <p>
+ * It should be noted that neither AbstractMap.toString() nor AbstractCollection.toString() implement such a
+ * behavior. They only check if the container is directly contained in itself, but not if a contained container
+ * contains the original one. Because of that, Arrays.toString(Object[]) isn't safe either.
+ * Confusing? Just read the last paragraph again and check the respective toString() implementation.
+ * </p>
+ * <p>
+ * This means, in effect, that logging would produce a usable output even if an ordinary System.out.println(o)
+ * would produce a relatively hard-to-debug StackOverflowError.
+ * </p>
+ * @param o The object.
+ * @return The String representation.
+ */
+ static String deepToString(final Object o) {
+ if (o == null) {
+ return null;
+ }
+ if (o instanceof String) {
+ return (String) o;
+ }
+ final StringBuilder str = new StringBuilder();
+ final Set<String> dejaVu = new HashSet<>(); // that's actually a neat name ;)
+ recursiveDeepToString(o, str, dejaVu);
+ return str.toString();
+ }
+
+ /**
+ * This method performs a deep toString of the given Object.
+ * Primitive arrays are converted using their respective Arrays.toString methods while
+ * special handling is implemented for "container types", i.e. Object[], Map and Collection because those could
+ * contain themselves.
+ * <p>
+ * dejaVu is used in case of those container types to prevent an endless recursion.
+ * </p>
+ * <p>
+ * It should be noted that neither AbstractMap.toString() nor AbstractCollection.toString() implement such a
+ * behavior.
+ * They only check if the container is directly contained in itself, but not if a contained container contains the
+ * original one. Because of that, Arrays.toString(Object[]) isn't safe either.
+ * Confusing? Just read the last paragraph again and check the respective toString() implementation.
+ * </p>
+ * <p>
+ * This means, in effect, that logging would produce a usable output even if an ordinary System.out.println(o)
+ * would produce a relatively hard-to-debug StackOverflowError.
+ * </p>
+ *
+ * @param o the Object to convert into a String
+ * @param str the StringBuilder that o will be appended to
+ * @param dejaVu a list of container identities that were already used.
+ */
+ private static void recursiveDeepToString(final Object o, final StringBuilder str, final Set<String> dejaVu) {
+ if (appendSpecialTypes(o, str)) {
+ return;
+ }
+ if (isMaybeRecursive(o)) {
+ appendPotentiallyRecursiveValue(o, str, dejaVu);
+ } else {
+ tryObjectToString(o, str);
+ }
+ }
+
+ private static boolean appendSpecialTypes(final Object o, final StringBuilder str) {
+ if (o == null || o instanceof String) {
+ str.append((String) o);
+ return true;
+ } else if (o instanceof StringBuilder) {
+ str.append((StringBuilder) o);
+ return true;
+ } else if (o instanceof StringBuilderFormattable) {
+ ((StringBuilderFormattable) o).formatTo(str);
+ return true;
+ }
+ return appendDate(o, str);
+ }
+
+ private static boolean appendDate(final Object o, final StringBuilder str) {
+ if (!(o instanceof Date)) {
+ return false;
+ }
+ final Date date = (Date) o;
+ final SimpleDateFormat format = getSimpleDateFormat();
+ str.append(format.format(date));
+ return true;
+ }
+
+ private static SimpleDateFormat getSimpleDateFormat() {
+ SimpleDateFormat result = threadLocalSimpleDateFormat.get();
+ if (result == null) {
+ result = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
+ threadLocalSimpleDateFormat.set(result);
+ }
+ return result;
+ }
+
+ /**
+ * Returns {@code true} if the specified object is an array, a Map or a Collection.
+ */
+ private static boolean isMaybeRecursive(final Object o) {
+ return o.getClass().isArray() || o instanceof Map || o instanceof Collection;
+ }
+
+ private static void appendPotentiallyRecursiveValue(final Object o, final StringBuilder str,
+ final Set<String> dejaVu) {
+ final Class<?> oClass = o.getClass();
+ if (oClass.isArray()) {
+ appendArray(o, str, dejaVu, oClass);
+ } else if (o instanceof Map) {
+ appendMap(o, str, dejaVu);
+ } else if (o instanceof Collection) {
+ appendCollection(o, str, dejaVu);
+ }
+ }
+
+ private static void appendArray(final Object o, final StringBuilder str, Set<String> dejaVu,
+ final Class<?> oClass) {
+ if (oClass == byte[].class) {
+ str.append(Arrays.toString((byte[]) o));
+ } else if (oClass == short[].class) {
+ str.append(Arrays.toString((short[]) o));
+ } else if (oClass == int[].class) {
+ str.append(Arrays.toString((int[]) o));
+ } else if (oClass == long[].class) {
+ str.append(Arrays.toString((long[]) o));
+ } else if (oClass == float[].class) {
+ str.append(Arrays.toString((float[]) o));
+ } else if (oClass == double[].class) {
+ str.append(Arrays.toString((double[]) o));
+ } else if (oClass == boolean[].class) {
+ str.append(Arrays.toString((boolean[]) o));
+ } else if (oClass == char[].class) {
+ str.append(Arrays.toString((char[]) o));
+ } else {
+ if (dejaVu == null) {
+ dejaVu = new HashSet<>();
+ }
+ // special handling of container Object[]
+ final String id = identityToString(o);
+ if (dejaVu.contains(id)) {
+ str.append(RECURSION_PREFIX).append(id).append(RECURSION_SUFFIX);
+ } else {
+ dejaVu.add(id);
+ final Object[] oArray = (Object[]) o;
+ str.append('[');
+ boolean first = true;
+ for (final Object current : oArray) {
+ if (first) {
+ first = false;
+ } else {
+ str.append(", ");
+ }
+ recursiveDeepToString(current, str, new HashSet<>(dejaVu));
+ }
+ str.append(']');
+ }
+ //str.append(Arrays.deepToString((Object[]) o));
+ }
+ }
+
+ private static void appendMap(final Object o, final StringBuilder str, Set<String> dejaVu) {
+ // special handling of container Map
+ if (dejaVu == null) {
+ dejaVu = new HashSet<>();
+ }
+ final String id = identityToString(o);
+ if (dejaVu.contains(id)) {
+ str.append(RECURSION_PREFIX).append(id).append(RECURSION_SUFFIX);
+ } else {
+ dejaVu.add(id);
+ final Map<?, ?> oMap = (Map<?, ?>) o;
+ str.append('{');
+ boolean isFirst = true;
+ for (final Object o1 : oMap.entrySet()) {
+ final Map.Entry<?, ?> current = (Map.Entry<?, ?>) o1;
+ if (isFirst) {
+ isFirst = false;
+ } else {
+ str.append(", ");
+ }
+ final Object key = current.getKey();
+ final Object value = current.getValue();
+ recursiveDeepToString(key, str, new HashSet<>(dejaVu));
+ str.append('=');
+ recursiveDeepToString(value, str, new HashSet<>(dejaVu));
+ }
+ str.append('}');
+ }
+ }
+
+ private static void appendCollection(final Object o, final StringBuilder str, Set<String> dejaVu) {
+ // special handling of container Collection
+ if (dejaVu == null) {
+ dejaVu = new HashSet<>();
+ }
+ final String id = identityToString(o);
+ if (dejaVu.contains(id)) {
+ str.append(RECURSION_PREFIX).append(id).append(RECURSION_SUFFIX);
+ } else {
+ dejaVu.add(id);
+ final Collection<?> oCol = (Collection<?>) o;
+ str.append('[');
+ boolean isFirst = true;
+ for (final Object anOCol : oCol) {
+ if (isFirst) {
+ isFirst = false;
+ } else {
+ str.append(", ");
+ }
+ recursiveDeepToString(anOCol, str, new HashSet<>(dejaVu));
+ }
+ str.append(']');
+ }
+ }
+
+ private static void tryObjectToString(final Object o, final StringBuilder str) {
+ // it's just some other Object, we can only use toString().
+ try {
+ str.append(o.toString());
+ } catch (final Throwable t) {
+ handleErrorInObjectToString(o, str, t);
+ }
+ }
+
+ private static void handleErrorInObjectToString(final Object o, final StringBuilder str, final Throwable t) {
+ str.append(ERROR_PREFIX);
+ str.append(identityToString(o));
+ str.append(ERROR_SEPARATOR);
+ final String msg = t.getMessage();
+ final String className = t.getClass().getName();
+ str.append(className);
+ if (!className.equals(msg)) {
+ str.append(ERROR_MSG_SEPARATOR);
+ str.append(msg);
+ }
+ str.append(ERROR_SUFFIX);
+ }
+
+ /**
+ * This method returns the same as if Object.toString() would not have been
+ * overridden in obj.
+ * <p>
+ * Note that this isn't 100% secure as collisions can always happen with hash codes.
+ * </p>
+ * <p>
+ * Copied from Object.hashCode():
+ * </p>
+ * <blockquote>
+ * As much as is reasonably practical, the hashCode method defined by
+ * class {@code Object} does return distinct integers for distinct
+ * objects. (This is typically implemented by converting the internal
+ * address of the object into an integer, but this implementation
+ * technique is not required by the Java™ programming language.)
+ * </blockquote>
+ *
+ * @param obj the Object that is to be converted into an identity string.
+ * @return the identity string as also defined in Object.toString()
+ */
+ static String identityToString(final Object obj) {
+ if (obj == null) {
+ return null;
+ }
+ return obj.getClass().getName() + '@' + Integer.toHexString(System.identityHashCode(obj));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4cf4326f/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessage.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessage.java
index 8db4f62..64d3bb9 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessage.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessage.java
@@ -16,13 +16,7 @@
*/
package org.apache.logging.log4j.message;
-import java.text.SimpleDateFormat;
import java.util.Arrays;
-import java.util.Collection;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
import org.apache.logging.log4j.util.StringBuilderFormattable;
@@ -34,54 +28,45 @@ import org.apache.logging.log4j.util.StringBuilderFormattable;
* licensed under the LGPL. It has been relicensed here with his permission providing that this attribution remain.
* </p>
*/
-public class ParameterizedMessage implements ReusableMessage {
-
+public class ParameterizedMessage implements Message, StringBuilderFormattable {
/**
* Prefix for recursion.
*/
- public static final String RECURSION_PREFIX = "[...";
+ public static final String RECURSION_PREFIX = ParameterFormatter.RECURSION_PREFIX;
/**
* Suffix for recursion.
*/
- public static final String RECURSION_SUFFIX = "...]";
+ public static final String RECURSION_SUFFIX = ParameterFormatter.RECURSION_SUFFIX;
/**
* Prefix for errors.
*/
- public static final String ERROR_PREFIX = "[!!!";
+ public static final String ERROR_PREFIX = ParameterFormatter.ERROR_PREFIX;
/**
* Separator for errors.
*/
- public static final String ERROR_SEPARATOR = "=>";
+ public static final String ERROR_SEPARATOR = ParameterFormatter.ERROR_SEPARATOR;
/**
* Separator for error messages.
*/
- public static final String ERROR_MSG_SEPARATOR = ":";
+ public static final String ERROR_MSG_SEPARATOR = ParameterFormatter.ERROR_MSG_SEPARATOR;
/**
* Suffix for errors.
*/
- public static final String ERROR_SUFFIX = "!!!]";
+ public static final String ERROR_SUFFIX = ParameterFormatter.ERROR_SUFFIX;
private static final long serialVersionUID = -665975803997290697L;
private static final int HASHVAL = 31;
- private static final char DELIM_START = '{';
- private static final char DELIM_STOP = '}';
- private static final char ESCAPE_CHAR = '\\';
-
// storing JDK classes in ThreadLocals does not cause memory leaks in web apps, so this is okay
private static ThreadLocal<StringBuilder> threadLocalStringBuilder = new ThreadLocal<>();
- private static ThreadLocal<SimpleDateFormat> threadLocalSimpleDateFormat = new ThreadLocal<>();
- private static ThreadLocal<Object[]> threadLocalUnrolledArgs = new ThreadLocal<>();
private String messagePattern;
- private int argCount;
private transient Object[] argArray;
- private boolean isThreadLocalMessageInitialized;
+ private String formattedMessage;
private transient Throwable throwable;
- private boolean reused;
/**
* Creates a parameterized message.
@@ -94,7 +79,7 @@ public class ParameterizedMessage implements ReusableMessage {
public ParameterizedMessage(final String messagePattern, final String[] arguments, final Throwable throwable) {
this.argArray = arguments;
this.throwable = throwable;
- init(messagePattern, arguments == null ? 0 : arguments.length);
+ init(messagePattern);
}
/**
@@ -107,7 +92,7 @@ public class ParameterizedMessage implements ReusableMessage {
public ParameterizedMessage(final String messagePattern, final Object[] arguments, final Throwable throwable) {
this.argArray = arguments;
this.throwable = throwable;
- init(messagePattern, arguments == null ? 0 : arguments.length);
+ init(messagePattern);
}
/**
@@ -123,7 +108,7 @@ public class ParameterizedMessage implements ReusableMessage {
*/
public ParameterizedMessage(final String messagePattern, final Object[] arguments) {
this.argArray = arguments;
- init(messagePattern, arguments == null ? 0 : arguments.length);
+ init(messagePattern);
}
/**
@@ -132,9 +117,7 @@ public class ParameterizedMessage implements ReusableMessage {
* @param arg The parameter.
*/
public ParameterizedMessage(final String messagePattern, final Object arg) {
- Object[] args = unrolledArgs();
- args[0] = arg;
- init(messagePattern, 1);
+ this(messagePattern, new Object[]{arg});
}
/**
@@ -144,173 +127,21 @@ public class ParameterizedMessage implements ReusableMessage {
* @param arg1 The second parameter.
*/
public ParameterizedMessage(final String messagePattern, final Object arg0, final Object arg1) {
- Object[] args = unrolledArgs();
- args[0] = arg0;
- args[1] = arg1;
- init(messagePattern, 2);
- }
-
- public boolean isReused() {
- return reused;
- }
-
- void setReused(boolean reused) {
- this.reused = reused;
+ this(messagePattern, new Object[]{arg0, arg1});
}
- ParameterizedMessage set(String messagePattern, Object... arguments) {
- this.argArray = arguments;
- init(messagePattern, arguments == null ? 0 : arguments.length);
- return this;
- }
-
- private void init(String messagePattern, int argCount) {
+ private void init(String messagePattern) {
this.messagePattern = messagePattern;
- this.argCount = argCount;
- this.isThreadLocalMessageInitialized = false;
- int usedCount = countArgumentPlaceholders(messagePattern);
- initThrowable(getParameters(), usedCount);
+ final int usedCount = countArgumentPlaceholders(messagePattern);
+ initThrowable(argArray, usedCount);
}
private void initThrowable(final Object[] params, final int usedParams) {
- if (usedParams < argCount && this.throwable == null && params[argCount - 1] instanceof Throwable) {
- this.throwable = (Throwable) params[argCount - 1];
- argCount--;
- }
- }
-
- ParameterizedMessage set(String messagePattern, Object p0) {
- Object[] args = unrolledArgs();
- args[0] = p0;
- init(messagePattern, 1);
- return this;
- }
-
- ParameterizedMessage set(String messagePattern, Object p0, Object p1) {
- Object[] args = unrolledArgs();
- args[0] = p0;
- args[1] = p1;
- init(messagePattern, 2);
- return this;
- }
-
- ParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2) {
- Object[] args = unrolledArgs();
- args[0] = p0;
- args[1] = p1;
- args[2] = p2;
- init(messagePattern, 3);
- return this;
- }
-
- ParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3) {
- Object[] args = unrolledArgs();
- args[0] = p0;
- args[1] = p1;
- args[2] = p2;
- args[3] = p3;
- init(messagePattern, 4);
- return this;
- }
-
- ParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4) {
- Object[] args = unrolledArgs();
- args[0] = p0;
- args[1] = p1;
- args[2] = p2;
- args[3] = p3;
- args[4] = p4;
- init(messagePattern, 5);
- return this;
- }
-
- ParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5) {
- Object[] args = unrolledArgs();
- args[0] = p0;
- args[1] = p1;
- args[2] = p2;
- args[3] = p3;
- args[4] = p4;
- args[5] = p5;
- init(messagePattern, 6);
- return this;
- }
-
- ParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5,
- Object p6) {
- Object[] args = unrolledArgs();
- args[0] = p0;
- args[1] = p1;
- args[2] = p2;
- args[3] = p3;
- args[4] = p4;
- args[5] = p5;
- args[6] = p6;
- init(messagePattern, 7);
- return this;
- }
-
- ParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5,
- Object p6, Object p7) {
- Object[] args = unrolledArgs();
- args[0] = p0;
- args[1] = p1;
- args[2] = p2;
- args[3] = p3;
- args[4] = p4;
- args[5] = p5;
- args[6] = p6;
- args[7] = p7;
- init(messagePattern, 8);
- return this;
- }
-
- ParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5,
- Object p6, Object p7, Object p8) {
- Object[] args = unrolledArgs();
- args[0] = p0;
- args[1] = p1;
- args[2] = p2;
- args[3] = p3;
- args[4] = p4;
- args[5] = p5;
- args[6] = p6;
- args[7] = p7;
- args[8] = p8;
- init(messagePattern, 9);
- return this;
- }
-
- ParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5,
- Object p6, Object p7, Object p8, Object p9) {
- Object[] args = unrolledArgs();
- args[0] = p0;
- args[1] = p1;
- args[2] = p2;
- args[3] = p3;
- args[4] = p4;
- args[5] = p5;
- args[6] = p6;
- args[7] = p7;
- args[8] = p8;
- args[9] = p9;
- init(messagePattern, 10);
- return this;
- }
-
- private static Object[] unrolledArgs() {
- Object[] result = threadLocalUnrolledArgs.get();
- if (result == null) {
- result = new Object[10]; // array must be as big as number of unrolled varargs
- threadLocalUnrolledArgs.set(result);
- }
- return result;
- }
-
- private void clearUnrolledArgs() {
- final Object[] args = unrolledArgs();
- for (int i = 0; i < argCount; i++) {
- args[i] = null;
+ if (params != null) {
+ int argCount = params.length;
+ if (usedParams < argCount && this.throwable == null && params[argCount - 1] instanceof Throwable) {
+ this.throwable = (Throwable) params[argCount - 1];
+ }
}
}
@@ -329,7 +160,7 @@ public class ParameterizedMessage implements ReusableMessage {
*/
@Override
public Object[] getParameters() {
- return argArray == null && argCount > 0 ? unrolledArgs() : argArray;
+ return argArray;
}
/**
@@ -352,16 +183,12 @@ public class ParameterizedMessage implements ReusableMessage {
*/
@Override
public String getFormattedMessage() {
- if (!isThreadLocalMessageInitialized) {
- initFormattedMessage();
+ if (formattedMessage == null) {
+ final StringBuilder buffer = getThreadLocalStringBuilder();
+ formatTo(buffer);
+ formattedMessage = buffer.toString();
}
- return threadLocalStringBuilder.get().toString();
- }
-
- private void initFormattedMessage() {
- final StringBuilder buffer = getThreadLocalStringBuilder();
- formatTo(buffer);
- isThreadLocalMessageInitialized = true;
+ return formattedMessage;
}
private static StringBuilder getThreadLocalStringBuilder() {
@@ -376,15 +203,11 @@ public class ParameterizedMessage implements ReusableMessage {
@Override
public void formatTo(final StringBuilder buffer) {
- if (isThreadLocalMessageInitialized) {
- final StringBuilder msg = threadLocalStringBuilder.get();
- if (msg != buffer) {
- buffer.append(msg);
- }
- return;
+ if (formattedMessage != null) {
+ buffer.append(formattedMessage);
+ } else {
+ ParameterFormatter.formatMessage(buffer, messagePattern, argArray, argArray.length);
}
- formatMessage(buffer, messagePattern, getParameters(), argCount);
- clearUnrolledArgs();
}
/**
@@ -395,163 +218,7 @@ public class ParameterizedMessage implements ReusableMessage {
* @return the formatted message.
*/
public static String format(final String messagePattern, final Object[] arguments) {
- final StringBuilder result = getThreadLocalStringBuilder();
- final int argCount = arguments == null ? 0 : arguments.length;
- formatMessage(result, messagePattern, arguments, argCount);
- return result.toString();
- }
-
- /**
- * Replace placeholders in the given messagePattern with arguments.
- *
- * @param buffer the buffer to write the formatted message into
- * @param messagePattern the message pattern containing placeholders.
- * @param arguments the arguments to be used to replace placeholders.
- */
- private static void formatMessage(final StringBuilder buffer, final String messagePattern,
- final Object[] arguments, final int argCount) {
- if (messagePattern == null || arguments == null || argCount == 0) {
- buffer.append(messagePattern);
- return;
- }
- int escapeCounter = 0;
- int currentArgument = 0;
- int i = 0;
- int len = messagePattern.length();
- for (; i < len - 1; i++) { // last char is excluded from the loop
- final char curChar = messagePattern.charAt(i);
- if (curChar == ESCAPE_CHAR) {
- escapeCounter++;
- } else {
- if (isDelimPair(curChar, messagePattern, i)) { // looks ahead one char
- i++;
-
- // write escaped escape chars
- writeEscapedEscapeChars(escapeCounter, buffer);
-
- if (isOdd(escapeCounter)) {
- // i.e. escaped: write escaped escape chars
- writeDelimPair(buffer);
- } else {
- // unescaped
- writeArgOrDelimPair(arguments, argCount, currentArgument, buffer);
- currentArgument++;
- }
- } else {
- handleLiteralChar(buffer, escapeCounter, curChar);
- }
- escapeCounter = 0;
- }
- }
- handleRemainingCharIfAny(messagePattern, len, buffer, escapeCounter, i);
- }
-
- /**
- * Returns {@code true} if the specified char and the char at {@code curCharIndex + 1} in the specified message
- * pattern together form a "{}" delimiter pair, returns {@code false} otherwise.
- */
- // Profiling showed this method is important to log4j performance. Modify with care!
- // 22 bytes (allows immediate JVM inlining: < 35 bytes) LOG4J2-1096
- private static boolean isDelimPair(final char curChar, final String messagePattern, final int curCharIndex) {
- return curChar == DELIM_START && messagePattern.charAt(curCharIndex + 1) == DELIM_STOP;
- }
-
- /**
- * Detects whether the message pattern has been fully processed or if an unprocessed character remains and processes
- * it if necessary, returning the resulting position in the result char array.
- */
- // Profiling showed this method is important to log4j performance. Modify with care!
- // 28 bytes (allows immediate JVM inlining: < 35 bytes) LOG4J2-1096
- private static void handleRemainingCharIfAny(final String messagePattern, final int len,
- final StringBuilder buffer, int escapeCounter, int i) {
- if (i == len - 1) {
- final char curChar = messagePattern.charAt(i);
- handleLastChar(buffer, escapeCounter, curChar);
- }
- }
-
- /**
- * Processes the last unprocessed character and returns the resulting position in the result char array.
- */
- // Profiling showed this method is important to log4j performance. Modify with care!
- // 28 bytes (allows immediate JVM inlining: < 35 bytes) LOG4J2-1096
- private static void handleLastChar(final StringBuilder buffer, final int escapeCounter, final char curChar) {
- if (curChar == ESCAPE_CHAR) {
- writeUnescapedEscapeChars(escapeCounter + 1, buffer);
- } else {
- handleLiteralChar(buffer, escapeCounter, curChar);
- }
- }
-
- /**
- * Processes a literal char (neither an '\' escape char nor a "{}" delimiter pair) and returns the resulting
- * position.
- */
- // Profiling showed this method is important to log4j performance. Modify with care!
- // 16 bytes (allows immediate JVM inlining: < 35 bytes) LOG4J2-1096
- private static void handleLiteralChar(final StringBuilder buffer, final int escapeCounter, final char curChar) {
- // any other char beside ESCAPE or DELIM_START/STOP-combo
- // write unescaped escape chars
- writeUnescapedEscapeChars(escapeCounter, buffer);
- buffer.append(curChar);
- }
-
- /**
- * Writes "{}" to the specified result array at the specified position and returns the resulting position.
- */
- // Profiling showed this method is important to log4j performance. Modify with care!
- // 18 bytes (allows immediate JVM inlining: < 35 bytes) LOG4J2-1096
- private static void writeDelimPair(final StringBuilder buffer) {
- buffer.append(DELIM_START);
- buffer.append(DELIM_STOP);
- }
-
- /**
- * Returns {@code true} if the specified parameter is odd.
- */
- // Profiling showed this method is important to log4j performance. Modify with care!
- // 11 bytes (allows immediate JVM inlining: < 35 bytes) LOG4J2-1096
- private static boolean isOdd(final int number) {
- return (number & 1) == 1;
- }
-
- /**
- * Writes a '\' char to the specified result array (starting at the specified position) for each <em>pair</em> of
- * '\' escape chars encountered in the message format and returns the resulting position.
- */
- // Profiling showed this method is important to log4j performance. Modify with care!
- // 11 bytes (allows immediate JVM inlining: < 35 bytes) LOG4J2-1096
- private static void writeEscapedEscapeChars(final int escapeCounter, final StringBuilder buffer) {
- final int escapedEscapes = escapeCounter >> 1; // divide by two
- writeUnescapedEscapeChars(escapedEscapes, buffer);
- }
-
- /**
- * Writes the specified number of '\' chars to the specified result array (starting at the specified position) and
- * returns the resulting position.
- */
- // Profiling showed this method is important to log4j performance. Modify with care!
- // 20 bytes (allows immediate JVM inlining: < 35 bytes) LOG4J2-1096
- private static void writeUnescapedEscapeChars(int escapeCounter, final StringBuilder buffer) {
- while (escapeCounter > 0) {
- buffer.append(ESCAPE_CHAR);
- escapeCounter--;
- }
- }
-
- /**
- * Appends the argument at the specified argument index (or, if no such argument exists, the "{}" delimiter pair) to
- * the specified result char array at the specified position and returns the resulting position.
- */
- // Profiling showed this method is important to log4j performance. Modify with care!
- // 25 bytes (allows immediate JVM inlining: < 35 bytes) LOG4J2-1096
- private static void writeArgOrDelimPair(final Object[] arguments, final int argCount, final int currentArgument,
- final StringBuilder buffer) {
- if (currentArgument < argCount) {
- recursiveDeepToString(arguments[currentArgument], buffer, null);
- } else {
- writeDelimPair(buffer);
- }
+ return ParameterFormatter.format(messagePattern, arguments);
}
@Override
@@ -590,27 +257,7 @@ public class ParameterizedMessage implements ReusableMessage {
* @return the number of unescaped placeholders.
*/
public static int countArgumentPlaceholders(final String messagePattern) {
- if (messagePattern == null) {
- return 0;
- }
- int length = messagePattern.length();
- int result = 0;
- boolean isEscaped = false;
- for (int i = 0; i < length - 1; i++) {
- final char curChar = messagePattern.charAt(i);
- if (curChar == ESCAPE_CHAR) {
- isEscaped = !isEscaped;
- } else if (curChar == DELIM_START) {
- if (!isEscaped && messagePattern.charAt(i + 1) == DELIM_STOP) {
- result++;
- i++;
- }
- isEscaped = false;
- } else {
- isEscaped = false;
- }
- }
- return result;
+ return ParameterFormatter.countArgumentPlaceholders(messagePattern);
}
/**
@@ -632,219 +279,7 @@ public class ParameterizedMessage implements ReusableMessage {
* @return The String representation.
*/
public static String deepToString(final Object o) {
- if (o == null) {
- return null;
- }
- if (o instanceof String) {
- return (String) o;
- }
- final StringBuilder str = new StringBuilder();
- final Set<String> dejaVu = new HashSet<>(); // that's actually a neat name ;)
- recursiveDeepToString(o, str, dejaVu);
- return str.toString();
- }
-
- /**
- * This method performs a deep toString of the given Object.
- * Primitive arrays are converted using their respective Arrays.toString methods while
- * special handling is implemented for "container types", i.e. Object[], Map and Collection because those could
- * contain themselves.
- * <p>
- * dejaVu is used in case of those container types to prevent an endless recursion.
- * </p>
- * <p>
- * It should be noted that neither AbstractMap.toString() nor AbstractCollection.toString() implement such a
- * behavior.
- * They only check if the container is directly contained in itself, but not if a contained container contains the
- * original one. Because of that, Arrays.toString(Object[]) isn't safe either.
- * Confusing? Just read the last paragraph again and check the respective toString() implementation.
- * </p>
- * <p>
- * This means, in effect, that logging would produce a usable output even if an ordinary System.out.println(o)
- * would produce a relatively hard-to-debug StackOverflowError.
- * </p>
- *
- * @param o the Object to convert into a String
- * @param str the StringBuilder that o will be appended to
- * @param dejaVu a list of container identities that were already used.
- */
- private static void recursiveDeepToString(final Object o, final StringBuilder str, final Set<String> dejaVu) {
- if (appendSpecialTypes(o, str)) {
- return;
- }
- if (isMaybeRecursive(o)) {
- appendPotentiallyRecursiveValue(o, str, dejaVu);
- } else {
- tryObjectToString(o, str);
- }
- }
-
- private static boolean appendSpecialTypes(final Object o, final StringBuilder str) {
- if (o == null || o instanceof String) {
- str.append((String) o);
- return true;
- } else if (o instanceof StringBuilder) {
- str.append((StringBuilder) o);
- return true;
- } else if (o instanceof StringBuilderFormattable) {
- ((StringBuilderFormattable) o).formatTo(str);
- return true;
- }
- return appendDate(o, str);
- }
-
- private static boolean appendDate(final Object o, final StringBuilder str) {
- if (!(o instanceof Date)) {
- return false;
- }
- final Date date = (Date) o;
- final SimpleDateFormat format = getSimpleDateFormat();
- str.append(format.format(date));
- return true;
- }
-
- private static SimpleDateFormat getSimpleDateFormat() {
- SimpleDateFormat result = threadLocalSimpleDateFormat.get();
- if (result == null) {
- result = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
- threadLocalSimpleDateFormat.set(result);
- }
- return result;
- }
-
- /**
- * Returns {@code true} if the specified object is an array, a Map or a Collection.
- */
- private static boolean isMaybeRecursive(final Object o) {
- return o.getClass().isArray() || o instanceof Map || o instanceof Collection;
- }
-
- private static void appendPotentiallyRecursiveValue(final Object o, final StringBuilder str, Set<String> dejaVu) {
- if (dejaVu == null) {
- dejaVu = new HashSet<>();
- }
- final Class<?> oClass = o.getClass();
- if (oClass.isArray()) {
- appendArray(o, str, dejaVu, oClass);
- } else if (o instanceof Map) {
- appendMap(o, str, dejaVu);
- } else if (o instanceof Collection) {
- appendCollection(o, str, dejaVu);
- }
- }
-
- private static void appendArray(final Object o, final StringBuilder str, final Set<String> dejaVu,
- final Class<?> oClass) {
- if (oClass == byte[].class) {
- str.append(Arrays.toString((byte[]) o));
- } else if (oClass == short[].class) {
- str.append(Arrays.toString((short[]) o));
- } else if (oClass == int[].class) {
- str.append(Arrays.toString((int[]) o));
- } else if (oClass == long[].class) {
- str.append(Arrays.toString((long[]) o));
- } else if (oClass == float[].class) {
- str.append(Arrays.toString((float[]) o));
- } else if (oClass == double[].class) {
- str.append(Arrays.toString((double[]) o));
- } else if (oClass == boolean[].class) {
- str.append(Arrays.toString((boolean[]) o));
- } else if (oClass == char[].class) {
- str.append(Arrays.toString((char[]) o));
- } else {
- // special handling of container Object[]
- final String id = identityToString(o);
- if (dejaVu.contains(id)) {
- str.append(RECURSION_PREFIX).append(id).append(RECURSION_SUFFIX);
- } else {
- dejaVu.add(id);
- final Object[] oArray = (Object[]) o;
- str.append('[');
- boolean first = true;
- for (final Object current : oArray) {
- if (first) {
- first = false;
- } else {
- str.append(", ");
- }
- recursiveDeepToString(current, str, new HashSet<>(dejaVu));
- }
- str.append(']');
- }
- //str.append(Arrays.deepToString((Object[]) o));
- }
- }
-
- private static void appendMap(final Object o, final StringBuilder str, final Set<String> dejaVu) {
- // special handling of container Map
- final String id = identityToString(o);
- if (dejaVu.contains(id)) {
- str.append(RECURSION_PREFIX).append(id).append(RECURSION_SUFFIX);
- } else {
- dejaVu.add(id);
- final Map<?, ?> oMap = (Map<?, ?>) o;
- str.append('{');
- boolean isFirst = true;
- for (final Object o1 : oMap.entrySet()) {
- final Map.Entry<?, ?> current = (Map.Entry<?, ?>) o1;
- if (isFirst) {
- isFirst = false;
- } else {
- str.append(", ");
- }
- final Object key = current.getKey();
- final Object value = current.getValue();
- recursiveDeepToString(key, str, new HashSet<>(dejaVu));
- str.append('=');
- recursiveDeepToString(value, str, new HashSet<>(dejaVu));
- }
- str.append('}');
- }
- }
-
- private static void appendCollection(final Object o, final StringBuilder str, final Set<String> dejaVu) {
- // special handling of container Collection
- final String id = identityToString(o);
- if (dejaVu.contains(id)) {
- str.append(RECURSION_PREFIX).append(id).append(RECURSION_SUFFIX);
- } else {
- dejaVu.add(id);
- final Collection<?> oCol = (Collection<?>) o;
- str.append('[');
- boolean isFirst = true;
- for (final Object anOCol : oCol) {
- if (isFirst) {
- isFirst = false;
- } else {
- str.append(", ");
- }
- recursiveDeepToString(anOCol, str, new HashSet<>(dejaVu));
- }
- str.append(']');
- }
- }
-
- private static void tryObjectToString(final Object o, final StringBuilder str) {
- // it's just some other Object, we can only use toString().
- try {
- str.append(o.toString());
- } catch (final Throwable t) {
- handleErrorInObjectToString(o, str, t);
- }
- }
-
- private static void handleErrorInObjectToString(final Object o, final StringBuilder str, final Throwable t) {
- str.append(ERROR_PREFIX);
- str.append(identityToString(o));
- str.append(ERROR_SEPARATOR);
- final String msg = t.getMessage();
- final String className = t.getClass().getName();
- str.append(className);
- if (!className.equals(msg)) {
- str.append(ERROR_MSG_SEPARATOR);
- str.append(msg);
- }
- str.append(ERROR_SUFFIX);
+ return ParameterFormatter.deepToString(o);
}
/**
@@ -868,10 +303,7 @@ public class ParameterizedMessage implements ReusableMessage {
* @return the identity string as also defined in Object.toString()
*/
public static String identityToString(final Object obj) {
- if (obj == null) {
- return null;
- }
- return obj.getClass().getName() + '@' + Integer.toHexString(System.identityHashCode(obj));
+ return ParameterFormatter.identityToString(obj);
}
@Override
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4cf4326f/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
new file mode 100644
index 0000000..608d893
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java
@@ -0,0 +1,271 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.message;
+
+import java.util.Arrays;
+
+/**
+ * Reusable parameterized message.
+ * @see ParameterizedMessage
+ * @since 2.6
+ */
+public class ReusableParameterizedMessage implements ReusableMessage {
+
+ private static final long serialVersionUID = 7800075879295123856L;
+
+ static class InternalState {
+ private StringBuilder buffer = new StringBuilder(2048);
+ private String messagePattern;
+ private int argCount;
+ private transient Object[] varargs;
+ private transient Object[] params = new Object[10];
+ private transient Throwable throwable;
+
+ private Object[] getTrimmedParams() {
+ return varargs == null ? Arrays.copyOf(params, argCount) : varargs;
+ }
+
+ private Object[] getParams() {
+ return varargs == null ? params : varargs;
+ }
+
+ private void init(String messagePattern, int argCount, Object[] paramArray) {
+ this.varargs = null;
+ this.buffer.setLength(0);
+
+ this.messagePattern = messagePattern;
+ this.argCount= argCount;
+ //this.formattedMessage = null;
+ int usedCount = ParameterFormatter.countArgumentPlaceholders(messagePattern);
+ initThrowable(paramArray, usedCount);
+ }
+
+ private void initThrowable(final Object[] params, final int usedParams) {
+ if (usedParams < argCount && this.throwable == null && params[argCount - 1] instanceof Throwable) {
+ this.throwable = (Throwable) params[argCount - 1];
+ argCount--;
+ }
+ }
+ }
+
+ // storing non-JDK classes in ThreadLocals causes memory leaks in web apps, do not use in web apps!
+ private static ThreadLocal<InternalState> state = new ThreadLocal<>();
+
+ /**
+ * Creates a reusable message.
+ */
+ public ReusableParameterizedMessage() {
+ }
+
+ public boolean isReused() {
+ return true;
+ }
+
+ private InternalState getState() {
+ InternalState result = state.get();
+ if (result == null) {
+ result = new InternalState();
+ state.set(result);
+ }
+ return result;
+ }
+
+ public StringBuilder get() {
+ return getState().buffer;
+ }
+
+ ReusableParameterizedMessage set(String messagePattern, Object... arguments) {
+ InternalState state = getState();
+ state.init(messagePattern, arguments == null ? 0 : arguments.length, arguments);
+ state.varargs = arguments;
+ return this;
+ }
+
+ ReusableParameterizedMessage set(String messagePattern, Object p0) {
+ InternalState state = getState();
+ state.params[0] = p0;
+ state.init(messagePattern, 1, state.params);
+ return this;
+ }
+
+ ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1) {
+ InternalState state = getState();
+ state.params[0] = p0;
+ state.params[1] = p1;
+ state.init(messagePattern, 2, state.params);
+ return this;
+ }
+
+ ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2) {
+ InternalState state = getState();
+ state.params[0] = p0;
+ state.params[1] = p1;
+ state.params[2] = p2;
+ state.init(messagePattern, 3, state.params);
+ return this;
+ }
+
+ ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3) {
+ InternalState state = getState();
+ state.params[0] = p0;
+ state.params[1] = p1;
+ state.params[2] = p2;
+ state.params[3] = p3;
+ state.init(messagePattern, 4, state.params);
+ return this;
+ }
+
+ ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4) {
+ InternalState state = getState();
+ state.params[0] = p0;
+ state.params[1] = p1;
+ state.params[2] = p2;
+ state.params[3] = p3;
+ state.params[4] = p4;
+ state.init(messagePattern, 5, state.params);
+ return this;
+ }
+
+ ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5) {
+ InternalState state = getState();
+ state.params[0] = p0;
+ state.params[1] = p1;
+ state.params[2] = p2;
+ state.params[3] = p3;
+ state.params[4] = p4;
+ state.params[5] = p5;
+ state.init(messagePattern, 6, state.params);
+ return this;
+ }
+
+ ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5,
+ Object p6) {
+ InternalState state = getState();
+ state.params[0] = p0;
+ state.params[1] = p1;
+ state.params[2] = p2;
+ state.params[3] = p3;
+ state.params[4] = p4;
+ state.params[5] = p5;
+ state.params[6] = p6;
+ state.init(messagePattern, 7, state.params);
+ return this;
+ }
+
+ ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5,
+ Object p6, Object p7) {
+ InternalState state = getState();
+ state.params[0] = p0;
+ state.params[1] = p1;
+ state.params[2] = p2;
+ state.params[3] = p3;
+ state.params[4] = p4;
+ state.params[5] = p5;
+ state.params[6] = p6;
+ state.params[7] = p7;
+ state.init(messagePattern, 8, state.params);
+ return this;
+ }
+
+ ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5,
+ Object p6, Object p7, Object p8) {
+ InternalState state = getState();
+ state.params[0] = p0;
+ state.params[1] = p1;
+ state.params[2] = p2;
+ state.params[3] = p3;
+ state.params[4] = p4;
+ state.params[5] = p5;
+ state.params[6] = p6;
+ state.params[7] = p7;
+ state.params[8] = p8;
+ state.init(messagePattern, 9, state.params);
+ return this;
+ }
+
+ ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5,
+ Object p6, Object p7, Object p8, Object p9) {
+ InternalState state = getState();
+ state.params[0] = p0;
+ state.params[1] = p1;
+ state.params[2] = p2;
+ state.params[3] = p3;
+ state.params[4] = p4;
+ state.params[5] = p5;
+ state.params[6] = p6;
+ state.params[7] = p7;
+ state.params[8] = p8;
+ state.params[9] = p9;
+ state.init(messagePattern, 10, state.params);
+ return this;
+ }
+
+ /**
+ * Returns the message pattern.
+ * @return the message pattern.
+ */
+ @Override
+ public String getFormat() {
+ return getState().messagePattern;
+ }
+
+ /**
+ * Returns the message parameters.
+ * @return the message parameters.
+ */
+ @Override
+ public Object[] getParameters() {
+ return getState().getTrimmedParams();
+ }
+
+ /**
+ * Returns the Throwable that was given as the last argument, if any.
+ * It will not survive serialization. The Throwable exists as part of the message
+ * primarily so that it can be extracted from the end of the list of parameters
+ * and then be added to the LogEvent. As such, the Throwable in the event should
+ * not be used once the LogEvent has been constructed.
+ *
+ * @return the Throwable, if any.
+ */
+ @Override
+ public Throwable getThrowable() {
+ return getState().throwable;
+ }
+
+ /**
+ * Returns the formatted message.
+ * @return the formatted message.
+ */
+ @Override
+ public String getFormattedMessage() {
+ final InternalState state = getState();
+ final StringBuilder buffer = state.buffer;
+ formatTo(buffer);
+ return buffer.toString();
+ }
+
+ @Override
+ public void formatTo(final StringBuilder buffer) {
+ InternalState state = getState();
+ ParameterFormatter.formatMessage(buffer, state.messagePattern, state.getParams(), state.argCount);
+ }
+
+ @Override
+ public String toString() {
+ return getFormattedMessage();
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4cf4326f/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessageFactory.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessageFactory.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessageFactory.java
index 568f5bc..b5aaa5f 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessageFactory.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessageFactory.java
@@ -19,7 +19,7 @@ package org.apache.logging.log4j.message;
/**
* Enables use of <code>{}</code> parameter markers in message strings.
* <p>
- * Reuses a ThreadLocal {@link ParameterizedMessage} instance for {@link #newMessage(String, Object...)}.
+ * Reuses a ThreadLocal {@link ReusableParameterizedMessage} instance for {@link #newMessage(String, Object...)}.
* </p>
* <p>
* This class is immutable.
@@ -28,12 +28,12 @@ package org.apache.logging.log4j.message;
public final class ReusableParameterizedMessageFactory extends AbstractMessageFactory {
/**
- * Instance of StringFormatterMessageFactory.
+ * Instance of ReusableParameterizedMessageFactory.
*/
public static final ReusableParameterizedMessageFactory INSTANCE = new ReusableParameterizedMessageFactory();
private static final long serialVersionUID = -8970940216592525651L;
- private static ThreadLocal<ParameterizedMessage> threadLocalMessage = new ThreadLocal<>();
+ private static ThreadLocal<ReusableParameterizedMessage> threadLocalMessage = new ThreadLocal<>();
/**
* Constructs a message factory.
@@ -43,7 +43,7 @@ public final class ReusableParameterizedMessageFactory extends AbstractMessageFa
}
/**
- * Creates {@link ParameterizedMessage} instances.
+ * Creates {@link ReusableParameterizedMessage} instances.
*
* @param message The message pattern.
* @param params The message parameters.
@@ -53,10 +53,9 @@ public final class ReusableParameterizedMessageFactory extends AbstractMessageFa
*/
@Override
public Message newMessage(final String message, final Object... params) {
- ParameterizedMessage result = threadLocalMessage.get();
+ ReusableParameterizedMessage result = threadLocalMessage.get();
if (result == null) {
- result = new ParameterizedMessage(message, params);
- result.setReused(true);
+ result = new ReusableParameterizedMessage();
threadLocalMessage.set(result);
} else {
result.set(message, params);
[2/2] logging-log4j2 git commit: Merge remote-tracking branch
'origin/master'
Posted by rp...@apache.org.
Merge remote-tracking branch 'origin/master'
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/20e9d768
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/20e9d768
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/20e9d768
Branch: refs/heads/master
Commit: 20e9d768820580801d5a1652137898bd581a57ab
Parents: 4cf4326 87f7fd1
Author: rpopma <rp...@apache.org>
Authored: Fri Feb 26 02:05:49 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Fri Feb 26 02:05:49 2016 +0900
----------------------------------------------------------------------
.../java/org/apache/logging/log4j/core/Logger.java | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
----------------------------------------------------------------------