You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rg...@apache.org on 2012/08/10 09:02:23 UTC

svn commit: r1371582 - in /logging/log4j/log4j2/trunk: api/src/main/java/org/apache/logging/log4j/message/ api/src/test/java/org/apache/logging/log4j/ api/src/test/java/org/apache/logging/log4j/message/ src/changes/

Author: rgoers
Date: Fri Aug 10 07:02:22 2012
New Revision: 1371582

URL: http://svn.apache.org/viewvc?rev=1371582&view=rev
Log:
Add support for formatting using String.format()

Added:
    logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/message/StringFormattedMessage.java
      - copied, changed from r1371500, logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessage.java
    logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/Timer.java
      - copied, changed from r1371500, logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/Timer.java
    logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/message/StringFormattedMessageTest.java
      - copied, changed from r1371500, logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java
Modified:
    logging/log4j/log4j2/trunk/src/changes/changes.xml

Copied: logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/message/StringFormattedMessage.java (from r1371500, logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessage.java)
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/message/StringFormattedMessage.java?p2=logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/message/StringFormattedMessage.java&p1=logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessage.java&r1=1371500&r2=1371582&rev=1371582&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessage.java (original)
+++ logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/message/StringFormattedMessage.java Fri Aug 10 07:02:22 2012
@@ -16,156 +16,43 @@
  */
 package org.apache.logging.log4j.message;
 
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.status.StatusLogger;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.io.Serializable;
-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 java.util.IllegalFormatException;
 
 /**
- * Handles messages that consist of a format string containing '{}' to represent each replaceable token, and
- * the parameters.
- * <p/>
- * This class was originally written for Lillith (http://mac.freshmeat.net/projects/lilith-viewer) by
- * Joern Huxhorn where it is licensed under the LGPL. It has been relicensed here with his permission
- * providing that this attribution remain.
+ * Handles messages that consist of a format string conforming to java.util.Formatter
  */
-public class ParameterizedMessage implements Message, Serializable {
+public class StringFormattedMessage implements Message, Serializable {
 
-    /**
-     * Prefix for recursion.
-     */
-    public static final String RECURSION_PREFIX = "[...";
-    /**
-     * Suffix for recursion.
-     */
-    public static final String RECURSION_SUFFIX = "...]";
-
-    /**
-     * Prefix for errors.
-     */
-    public static final String ERROR_PREFIX = "[!!!";
-    /**
-     * Separator for errors.
-     */
-    public static final String ERROR_SEPARATOR = "=>";
-    /**
-     * Separator for error messages.
-     */
-    public static final String ERROR_MSG_SEPARATOR = ":";
-    /**
-     * Suffix for errors.
-     */
-    public static final String ERROR_SUFFIX = "!!!]";
+    private static final Logger LOGGER = StatusLogger.getLogger();
 
     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 = '\\';
-
     private String messagePattern;
-    private String[] stringArgs;
     private transient Object[] argArray;
+    private String[] stringArgs;
     private transient String formattedMessage;
-    private transient Throwable throwable;
 
     /**
-     * Create the ParameterizedMessage.
+     * Create the StringFormattedMessage.
      */
-    public ParameterizedMessage() {
+    public StringFormattedMessage() {
         this(null, null, null);
     }
 
-    /**
-     * Create the parameterizedMessage.
-     * @param messagePattern The message "format" string. This will be a String containing "{}" placeholders
-     * where parameters should be substituted.
-     * @param stringArgs The arguments for substitution.
-     * @param throwable A Throwable.
-     */
-    public ParameterizedMessage(String messagePattern, String[] stringArgs, Throwable throwable) {
-        this.messagePattern = messagePattern;
-        this.stringArgs = stringArgs;
-        this.throwable = throwable;
-    }
-
-    public ParameterizedMessage(String messagePattern, Object[] arguments, Throwable throwable) {
-        this.messagePattern = messagePattern;
-        this.throwable = throwable;
-        if (arguments != null) {
-            parseArguments(arguments);
-        }
-    }
 
-    /**
-     * <p>This method returns a ParameterizedMessage which contains the arguments converted to String
-     * as well as an optional Throwable.</p>
-     * <p/>
-     * <p>If the last argument is a Throwable and is NOT used up by a placeholder in the message pattern it is returned
-     * in ParameterizedMessage.getThrowable() and won't be contained in the created String[].<br/>
-     * If it is used up ParameterizedMessage.getThrowable() will return null even if the last argument was a
-     * Throwable!</p>
-     *
-     * @param messagePattern the message pattern that to be checked for placeholders.
-     * @param arguments      the argument array to be converted.
-     */
-    public ParameterizedMessage(String messagePattern, Object[] arguments) {
+    public StringFormattedMessage(String messagePattern, Object... arguments) {
         this.messagePattern = messagePattern;
-        if (arguments == null) {
-            return;
-        }
-        parseArguments(arguments);
-    }
-
-    /**
-     * Constructor with a pattern and a single parameter.
-     * @param messagePattern The message pattern.
-     * @param arg The parameter.
-     */
-    public ParameterizedMessage(String messagePattern, Object arg) {
-        this(messagePattern, new Object[]{arg});
-    }
-
-    /**
-     * Constructor with a pattern and two parameters.
-     * @param messagePattern The message pattern.
-     * @param arg1 The first parameter.
-     * @param arg2 The second parameter.
-     */
-    public ParameterizedMessage(String messagePattern, Object arg1, Object arg2) {
-        this(messagePattern, new Object[]{arg1, arg2});
-    }
-
-    private void parseArguments(Object[] arguments) {
-        int argsCount = countArgumentPlaceholders(messagePattern);
-        int resultArgCount = arguments.length;
-        if (argsCount < arguments.length) {
-            if (throwable == null && arguments[arguments.length - 1] instanceof Throwable) {
-                throwable = (Throwable) arguments[arguments.length - 1];
-                resultArgCount--;
-            }
-        }
-        argArray = new Object[resultArgCount];
-        for (int i = 0; i < resultArgCount; ++i) {
-            argArray[i] = arguments[i];
-        }
-
-        if (argsCount == 1 && throwable == null && arguments.length > 1) {
-            // special case
-            stringArgs = new String[1];
-            stringArgs[0] = deepToString(arguments);
-        } else {
-            stringArgs = new String[resultArgCount];
-            for (int i = 0; i < stringArgs.length; i++) {
-                stringArgs[i] = deepToString(arguments[i]);
-            }
-        }
+        this.argArray = arguments;
     }
 
     /**
@@ -174,7 +61,7 @@ public class ParameterizedMessage implem
      */
     public String getFormattedMessage() {
         if (formattedMessage == null) {
-            formattedMessage = formatMessage(messagePattern, stringArgs);
+            formattedMessage = formatMessage(messagePattern, argArray);
         }
         return formattedMessage;
     }
@@ -211,43 +98,18 @@ public class ParameterizedMessage implem
      * Sets the parameters for the message.
      * @param parameters The parameters.
      */
-    public void setParameters(String[] parameters) {
-        this.stringArgs = parameters;
-        this.formattedMessage = null;
-    }
-
-    /**
-     * Sets the parameters for the message.
-     * @param parameters The parameters.
-     */
     public void setParameters(Object[] parameters) {
-        parseArguments(parameters);
+        this.argArray = parameters;
         this.formattedMessage = null;
     }
 
-    /**
-     * Set the Throwable for the message.
-     * @param throwable The Throwable.
-     */
-    public void setThrowable(Throwable throwable) {
-        this.throwable = throwable;
-    }
-
-    /**
-     * 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.
-     */
-    public Throwable getThrowable() {
-        return throwable;
-    }
-
-    protected String formatMessage(String msgPattern, String[] sArgs) {
-        return format(msgPattern, sArgs);
+    protected String formatMessage(String msgPattern, Object... args) {
+        try {
+            return String.format(msgPattern, args);
+        } catch (IllegalFormatException ife) {
+            LOGGER.error("Unable to format msg: " + msgPattern, ife);
+            return msgPattern;
+        }
     }
 
     public boolean equals(Object o) {
@@ -258,7 +120,7 @@ public class ParameterizedMessage implem
             return false;
         }
 
-        ParameterizedMessage that = (ParameterizedMessage) o;
+        StringFormattedMessage that = (StringFormattedMessage) o;
 
         if (messagePattern != null ? !messagePattern.equals(that.messagePattern) : that.messagePattern != null) {
             return false;
@@ -266,7 +128,6 @@ public class ParameterizedMessage implem
         if (!Arrays.equals(stringArgs, that.stringArgs)) {
             return false;
         }
-        //if (throwable != null ? !throwable.equals(that.throwable) : that.throwable != null) return false;
 
         return true;
     }
@@ -277,306 +138,34 @@ public class ParameterizedMessage implem
         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.
-     */
-    public static String format(String messagePattern, Object[] arguments) {
-        if (messagePattern == null || arguments == null || arguments.length == 0) {
-            return messagePattern;
-        }
-
-        StringBuilder result = new StringBuilder();
-        int escapeCounter = 0;
-        int currentArgument = 0;
-        for (int i = 0; i < messagePattern.length(); i++) {
-            char curChar = messagePattern.charAt(i);
-            if (curChar == ESCAPE_CHAR) {
-                escapeCounter++;
-            } else {
-                if (curChar == DELIM_START) {
-                    if (i < messagePattern.length() - 1) {
-                        if (messagePattern.charAt(i + 1) == DELIM_STOP) {
-                            // write escaped escape chars
-                            int escapedEscapes = escapeCounter / 2;
-                            for (int j = 0; j < escapedEscapes; j++) {
-                                result.append(ESCAPE_CHAR);
-                            }
-
-                            if (escapeCounter % 2 == 1) {
-                                // i.e. escaped
-                                // write escaped escape chars
-                                result.append(DELIM_START);
-                                result.append(DELIM_STOP);
-                            } else {
-                                // unescaped
-                                if (currentArgument < arguments.length) {
-                                    result.append(arguments[currentArgument]);
-                                } else {
-                                    result.append(DELIM_START).append(DELIM_STOP);
-                                }
-                                currentArgument++;
-                            }
-                            i++;
-                            escapeCounter = 0;
-                            continue;
-                        }
-                    }
-                }
-                // any other char beside ESCAPE or DELIM_START/STOP-combo
-                // write unescaped escape chars
-                if (escapeCounter > 0) {
-                    for (int j = 0; j < escapeCounter; j++) {
-                        result.append(ESCAPE_CHAR);
-                    }
-                    escapeCounter = 0;
-                }
-                result.append(curChar);
-            }
-        }
-        return result.toString();
-    }
-
-    /**
-     * Counts the number of unescaped placeholders in the given messagePattern.
-     *
-     * @param messagePattern the message pattern to be analyzed.
-     * @return the number of unescaped placeholders.
-     */
-    public static int countArgumentPlaceholders(String messagePattern) {
-        if (messagePattern == null) {
-            return 0;
-        }
-
-        int delim = messagePattern.indexOf(DELIM_START);
-
-        if (delim == -1) {
-            // special case, no placeholders at all.
-            return 0;
-        }
-        int result = 0;
-        boolean isEscaped = false;
-        for (int i = 0; i < messagePattern.length(); i++) {
-            char curChar = messagePattern.charAt(i);
-            if (curChar == ESCAPE_CHAR) {
-                isEscaped = !isEscaped;
-            } else if (curChar == DELIM_START) {
-                if (!isEscaped) {
-                    if (i < messagePattern.length() - 1) {
-                        if (messagePattern.charAt(i + 1) == DELIM_STOP) {
-                            result++;
-                            i++;
-                        }
-                    }
-                }
-                isEscaped = false;
-            } else {
-                isEscaped = false;
-            }
-        }
-        return result;
-    }
 
-    /**
-     * 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/>
-     * 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.
-     * @param o The object.
-     * @return The String representation.
-     */
-    public static String deepToString(Object o) {
-        if (o == null) {
-            return null;
-        }
-        if (o instanceof String) {
-            return (String) o;
-        }
-        StringBuilder str = new StringBuilder();
-        Set<String> dejaVu = new HashSet<String>(); // that's actually a neat name ;)
-        recursiveDeepToString(o, str, dejaVu);
-        return str.toString();
+    public String toString() {
+        return "StringFormatMessage[messagePattern=" + messagePattern + ", args=" +
+            Arrays.toString(argArray) +  "]";
     }
 
-    /**
-     * 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/>
-     * 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/>
-     * 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.
-     *
-     * @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(Object o, StringBuilder str, Set<String> dejaVu) {
-        if (o == null) {
-            str.append("null");
-            return;
-        }
-        if (o instanceof String) {
-            str.append(o);
-            return;
-        }
-
-        Class oClass = o.getClass();
-        if (oClass.isArray()) {
-            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[]
-                String id = identityToString(o);
-                if (dejaVu.contains(id)) {
-                    str.append(RECURSION_PREFIX).append(id).append(RECURSION_SUFFIX);
-                } else {
-                    dejaVu.add(id);
-                    Object[] oArray = (Object[]) o;
-                    str.append("[");
-                    boolean first = true;
-                    for (Object current : oArray) {
-                        if (first) {
-                            first = false;
-                        } else {
-                            str.append(", ");
-                        }
-                        recursiveDeepToString(current, str, new HashSet<String>(dejaVu));
-                    }
-                    str.append("]");
-                }
-                //str.append(Arrays.deepToString((Object[]) o));
-            }
-        } else if (o instanceof Map) {
-            // special handling of container Map
-            String id = identityToString(o);
-            if (dejaVu.contains(id)) {
-                str.append(RECURSION_PREFIX).append(id).append(RECURSION_SUFFIX);
-            } else {
-                dejaVu.add(id);
-                Map oMap = (Map) o;
-                str.append("{");
-                boolean isFirst = true;
-                for (Object o1 : oMap.entrySet()) {
-                    Map.Entry current = (Map.Entry) o1;
-                    if (isFirst) {
-                        isFirst = false;
-                    } else {
-                        str.append(", ");
-                    }
-                    Object key = current.getKey();
-                    Object value = current.getValue();
-                    recursiveDeepToString(key, str, new HashSet<String>(dejaVu));
-                    str.append("=");
-                    recursiveDeepToString(value, str, new HashSet<String>(dejaVu));
-                }
-                str.append("}");
-            }
-        } else if (o instanceof Collection) {
-            // special handling of container Collection
-            String id = identityToString(o);
-            if (dejaVu.contains(id)) {
-                str.append(RECURSION_PREFIX).append(id).append(RECURSION_SUFFIX);
-            } else {
-                dejaVu.add(id);
-                Collection oCol = (Collection) o;
-                str.append("[");
-                boolean isFirst = true;
-                for (Object anOCol : oCol) {
-                    if (isFirst) {
-                        isFirst = false;
-                    } else {
-                        str.append(", ");
-                    }
-                    recursiveDeepToString(anOCol, str, new HashSet<String>(dejaVu));
-                }
-                str.append("]");
-            }
-        } else if (o instanceof Date) {
-            Date date = (Date) o;
-            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
-            // I'll leave it like this for the moment... this could probably be optimized using ThreadLocal...
-            str.append(format.format(date));
-        } else {
-            // it's just some other Object, we can only use toString().
-            try {
-                str.append(o.toString());
-            } catch (Throwable t) {
-                str.append(ERROR_PREFIX);
-                str.append(identityToString(o));
-                str.append(ERROR_SEPARATOR);
-                String msg = t.getMessage();
-                String className = t.getClass().getName();
-                str.append(className);
-                if (!className.equals(msg)) {
-                    str.append(ERROR_MSG_SEPARATOR);
-                    str.append(msg);
-                }
-                str.append(ERROR_SUFFIX);
-            }
+    private void writeObject(ObjectOutputStream out) throws IOException {
+        out.defaultWriteObject();
+        getFormattedMessage();
+        out.writeUTF(formattedMessage);
+        out.writeUTF(messagePattern);
+        out.writeInt(argArray.length);
+        stringArgs = new String[argArray.length];
+        int i = 0;
+        for (Object obj: argArray) {
+            stringArgs[i] = obj.toString();
+            ++i;
         }
     }
 
-    /**
-     * 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/>
-     * Copied from Object.hashCode():
-     * As much as is reasonably practical, the hashCode method defined by
-     * class <tt>Object</tt> 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<font size="-2"><sup>TM</sup></font>
-     * programming language.)
-     *
-     * @param obj the Object that is to be converted into an identity string.
-     * @return the identity string as also defined in Object.toString()
-     */
-    public static String identityToString(Object obj) {
-        if (obj == null) {
-            return null;
+    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+        in.defaultReadObject();
+        formattedMessage = in.readUTF();
+        messagePattern = in.readUTF();
+        int length = in.readInt();
+        stringArgs = new String[length];
+        for (int i=0; i < length; ++i) {
+            stringArgs[i] = in.readUTF();
         }
-        return obj.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(obj));
-    }
-
-    public String toString() {
-        return "ParameterizedMessage[messagePattern=" + messagePattern + ", stringArgs=" +
-            Arrays.toString(stringArgs) + ", throwable=" + throwable + "]";
     }
 }

Copied: logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/Timer.java (from r1371500, logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/Timer.java)
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/Timer.java?p2=logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/Timer.java&p1=logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/Timer.java&r1=1371500&r2=1371582&rev=1371582&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/Timer.java (original)
+++ logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/Timer.java Fri Aug 10 07:02:22 2012
@@ -14,7 +14,7 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-package org.apache.logging.log4j.core;
+package org.apache.logging.log4j;
 
 import java.io.Serializable;
 import java.text.DecimalFormat;

Copied: logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/message/StringFormattedMessageTest.java (from r1371500, logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java)
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/message/StringFormattedMessageTest.java?p2=logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/message/StringFormattedMessageTest.java&p1=logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java&r1=1371500&r2=1371582&rev=1371582&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java (original)
+++ logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/message/StringFormattedMessageTest.java Fri Aug 10 07:02:22 2012
@@ -16,27 +16,82 @@
  */
 package org.apache.logging.log4j.message;
 
+import org.apache.logging.log4j.Timer;
+import org.junit.AfterClass;
 import org.junit.Test;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 
 /**
  *
  */
-public class ParameterizedMessageTest {
+public class StringFormattedMessageTest {
+
+    private static final int LOOP_CNT = 500;
+    String[] array = new String[LOOP_CNT];
+    private static long stringTime = 0;
+    private static long paramTime = 0;
+
+    @AfterClass
+    public static void after() {
+        if (stringTime > paramTime) {
+            System.out.println(String.format("Parameterized is %1$.2f times faster than StringFormat.",
+                ((float) stringTime / paramTime)));
+        } else if (stringTime > paramTime) {
+            System.out.println(String.format("Parameterized is %1$.2f times slower than StringFormat.",
+                ((float) paramTime / stringTime)));
+        } else {
+            System.out.println("The speed of Parameterized and StringFormat are the same");
+        }
+    }
 
     @Test
     public void testNoArgs() {
-        String testMsg = "Test message {}";
-        ParameterizedMessage msg = new ParameterizedMessage(testMsg, null);
+        String testMsg = "Test message %1s";
+        StringFormattedMessage msg = new StringFormattedMessage(testMsg, null);
         String result = msg.getFormattedMessage();
-        assertEquals(testMsg, result);
+        String expected = "Test message null";
+        assertEquals(expected, result);
         Object[] array = null;
-        msg = new ParameterizedMessage(testMsg, array, null);
+        msg = new StringFormattedMessage(testMsg, array, null);
         result = msg.getFormattedMessage();
-        assertEquals(testMsg, result);
+        assertEquals(expected, result);
+    }
+
+    @Test
+    public void testOneArg() {
+        String testMsg = "Test message %1s";
+        StringFormattedMessage msg = new StringFormattedMessage(testMsg, "Apache");
+        String result = msg.getFormattedMessage();
+        String expected = "Test message Apache";
+        assertEquals(expected, result);
+    }
+
+    @Test
+    public void testStringPerf() {
+        String testMsg = "Test message %1s %2s";
+        Timer timer = new Timer("StringFormat", LOOP_CNT);
+        timer.start();
+        for (int i = 0; i < LOOP_CNT; ++i) {
+            StringFormattedMessage msg = new StringFormattedMessage(testMsg, "Apache", "Log4j");
+            array[i] = msg.getFormattedMessage();
+        }
+        timer.stop();
+        stringTime = timer.getElapsedNanoTime();
+        System.out.println(timer.toString());
+    }
+
+    @Test
+    public void testParameterizedPerf() {
+        String testMsg = "Test message {} {}";
+        Timer timer = new Timer("Parameterized", LOOP_CNT);
+        timer.start();
+        for (int i = 0; i < LOOP_CNT; ++i) {
+            ParameterizedMessage msg = new ParameterizedMessage(testMsg, "Apache", "Log4j");
+            array[i] = msg.getFormattedMessage();
+        }
+        timer.stop();
+        paramTime = timer.getElapsedNanoTime();
+        System.out.println(timer.toString());
     }
 }

Modified: logging/log4j/log4j2/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/src/changes/changes.xml?rev=1371582&r1=1371581&r2=1371582&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/src/changes/changes.xml (original)
+++ logging/log4j/log4j2/trunk/src/changes/changes.xml Fri Aug 10 07:02:22 2012
@@ -23,6 +23,9 @@
 
   <body>
     <release version="2.0-alpha2" date="TBD" description="Bug fixes and minor enhancements">
+      <action dev="rgoers" type="add">
+        Add support for formatting using String.format().
+      </action>
       <action issue="LOG4J2-67" dev="rgoers" type="add">
         Allow components besides core to create a PluginMap for faster plugin loading and not
         having to specify the plugin package in the configuration.