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 <[email protected]> Authored: Fri Feb 26 02:05:30 2016 +0900 Committer: rpopma <[email protected]> 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);
