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/03/28 10:11:40 UTC

[1/4] logging-log4j2 git commit: LOG4J2-1271 added unit test

Repository: logging-log4j2
Updated Branches:
  refs/heads/master 5c8acfb54 -> a21828565


LOG4J2-1271 added unit test


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

Branch: refs/heads/master
Commit: 0ed5b50d59b8095abae58837e61cc7f19da28920
Parents: 5c8acfb
Author: rpopma <rp...@apache.org>
Authored: Mon Mar 28 17:00:26 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Mon Mar 28 17:00:26 2016 +0900

----------------------------------------------------------------------
 .../logging/log4j/message/ParameterizedMessageTest.java      | 8 ++++++++
 1 file changed, 8 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0ed5b50d/log4j-api/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java b/log4j-api/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java
index 6b550b3..ac9895e 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java
+++ b/log4j-api/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java
@@ -79,6 +79,14 @@ public class ParameterizedMessageTest {
     }
 
     @Test
+    public void testFormatStringArgsWithTrailingText() {
+        final String testMsg = "Test message {}{} {}Text";
+        final String[] args = { "a", "b", "c" };
+        final String result = ParameterizedMessage.format(testMsg, args);
+        assertEquals("Test message ab cText", result);
+    }
+
+    @Test
     public void testFormatStringArgsWithTrailingEscapedEscape() {
         final String testMsg = "Test message {}{} {}\\\\";
         final String[] args = { "a", "b", "c" };


[3/4] logging-log4j2 git commit: LOG4J2-1271 added benchmark for different ways to format parameterized messages

Posted by rp...@apache.org.
LOG4J2-1271 added benchmark for different ways to format parameterized messages


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

Branch: refs/heads/master
Commit: 8242eb6b6234d0550a4285bc481ea9f3a0709ebb
Parents: b1e1db8
Author: rpopma <rp...@apache.org>
Authored: Mon Mar 28 17:10:35 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Mon Mar 28 17:10:35 2016 +0900

----------------------------------------------------------------------
 .../message/ParameterFormatterBenchmark.java    | 188 +++++++++++++++++++
 1 file changed, 188 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/8242eb6b/log4j-perf/src/main/java/org/apache/logging/log4j/message/ParameterFormatterBenchmark.java
----------------------------------------------------------------------
diff --git a/log4j-perf/src/main/java/org/apache/logging/log4j/message/ParameterFormatterBenchmark.java b/log4j-perf/src/main/java/org/apache/logging/log4j/message/ParameterFormatterBenchmark.java
new file mode 100644
index 0000000..d7dbf5d
--- /dev/null
+++ b/log4j-perf/src/main/java/org/apache/logging/log4j/message/ParameterFormatterBenchmark.java
@@ -0,0 +1,188 @@
+/*
+ * 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.concurrent.TimeUnit;
+
+import org.apache.logging.log4j.message.ParameterizedMessage;
+import org.apache.logging.log4j.perf.jmh.ParameterizedMessageBenchmark;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+
+/**
+ * This benchmark is not in the perf.jmh package because it tests the package-protected ParameterFormatter class.
+ */
+// ============================== HOW TO RUN THIS TEST: ====================================
+//
+// single thread:
+// java -jar log4j-perf/target/benchmarks.jar ".*ParameterFormatterBench.*" -f 1 -wi 5 -i 10
+//
+// multiple threads (for example, 4 threads):
+// java -jar log4j-perf/target/benchmarks.jar ".*ParameterFormatterBench.*" -f 1 -wi 5 -i 10 -t 4 -si true
+//
+// Usage help:
+// java -jar log4j-perf/target/benchmarks.jar -help
+//
+@State(Scope.Benchmark)
+public class ParameterFormatterBenchmark {
+    private static final Object[] ARGS = { "arg1", "arg2", "arg3", "arg4", "arg5", "arg6", "arg7", "arg8",
+            "arg9", "arg10",};
+
+    @State(Scope.Thread)
+    public static class ThreadState {
+        StringBuilder buffer = new StringBuilder(2048);
+        int[] indices = new int[255];
+        char[] copy = new char[4096];
+    }
+
+    @Benchmark
+    @BenchmarkMode(Mode.SampleTime)
+    @OutputTimeUnit(TimeUnit.NANOSECONDS)
+    public int latency3ParamsV3(final ThreadState state) {
+        state.buffer.setLength(0);
+        String STR = "p1={}, p2={}, p3={}";
+        int length = STR.length();
+        STR.getChars(0, length, state.copy, 0);
+        int count = ParameterFormatter.countArgumentPlaceholders3(state.copy, length, state.indices);
+        ParameterFormatter.formatMessage3(state.buffer, state.copy, length, ARGS, count, state.indices);
+        return state.buffer.length();
+    }
+
+    @Benchmark
+    @BenchmarkMode(Mode.SampleTime)
+    @OutputTimeUnit(TimeUnit.NANOSECONDS)
+    public int latency5ParamsV3(final ThreadState state) {
+        state.buffer.setLength(0);
+        String STR = "p1={}, p2={}, p3={}, p4={}, p5={}";
+        int length = STR.length();
+        STR.getChars(0, length, state.copy, 0);
+        int count = ParameterFormatter.countArgumentPlaceholders3(state.copy, length, state.indices);
+        ParameterFormatter.formatMessage3(state.buffer, state.copy, length, ARGS, count, state.indices);
+        return state.buffer.length();
+    }
+
+    @Benchmark
+    @BenchmarkMode(Mode.SampleTime)
+    @OutputTimeUnit(TimeUnit.NANOSECONDS)
+    public int latency7ParamsV3(final ThreadState state) {
+        state.buffer.setLength(0);
+        String STR = "p1={}, p2={}, p3={}, p4={}, p5={}, p6={}, p7={}";
+        int length = STR.length();
+        STR.getChars(0, length, state.copy, 0);
+        int count = ParameterFormatter.countArgumentPlaceholders3(state.copy, length, state.indices);
+        ParameterFormatter.formatMessage3(state.buffer, state.copy, length, ARGS, count, state.indices);
+        return state.buffer.length();
+    }
+
+    @Benchmark
+    @BenchmarkMode(Mode.SampleTime)
+    @OutputTimeUnit(TimeUnit.NANOSECONDS)
+    public int latency9ParamsV3(final ThreadState state) {
+        state.buffer.setLength(0);
+        String STR = "p1={}, p2={}, p3={}, p4={}, p5={}, p6={}, p7={}, p8={}, p9={}";
+        int length = STR.length();
+        STR.getChars(0, length, state.copy, 0);
+        int count = ParameterFormatter.countArgumentPlaceholders3(state.copy, length, state.indices);
+        ParameterFormatter.formatMessage3(state.buffer, state.copy, length, ARGS, count, state.indices);
+        return state.buffer.length();
+    }
+
+    @Benchmark
+    @BenchmarkMode(Mode.SampleTime)
+    @OutputTimeUnit(TimeUnit.NANOSECONDS)
+    public int latency3ParamsV2(final ThreadState state) {
+        state.buffer.setLength(0);
+        int count = ParameterFormatter.countArgumentPlaceholders2("p1={}, p2={}, p3={}", state.indices);
+        ParameterFormatter.formatMessage2(state.buffer, "p1={}, p2={}, p3={}", ARGS, count, state.indices);
+        return state.buffer.length();
+    }
+
+    @Benchmark
+    @BenchmarkMode(Mode.SampleTime)
+    @OutputTimeUnit(TimeUnit.NANOSECONDS)
+    public int latency5ParamsV2(final ThreadState state) {
+        state.buffer.setLength(0);
+        int count = ParameterFormatter.countArgumentPlaceholders2("p1={}, p2={}, p3={}, p4={}, p5={}", state.indices);
+        ParameterFormatter.formatMessage2(state.buffer, "p1={}, p2={}, p3={}, p4={}, p5={}", ARGS, count, state.indices);
+        return state.buffer.length();
+    }
+
+    @Benchmark
+    @BenchmarkMode(Mode.SampleTime)
+    @OutputTimeUnit(TimeUnit.NANOSECONDS)
+    public int latency7ParamsV2(final ThreadState state) {
+        state.buffer.setLength(0);
+        int count = ParameterFormatter.countArgumentPlaceholders2("p1={}, p2={}, p3={}, p4={}, p5={}, p6={}, p7={}", state.indices);
+        ParameterFormatter.formatMessage2(state.buffer, "p1={}, p2={}, p3={}, p4={}, p5={}, p6={}, p7={}", ARGS, count, state.indices);
+        return state.buffer.length();
+    }
+
+    @Benchmark
+    @BenchmarkMode(Mode.SampleTime)
+    @OutputTimeUnit(TimeUnit.NANOSECONDS)
+    public int latency9ParamsV2(final ThreadState state) {
+        state.buffer.setLength(0);
+        int count = ParameterFormatter.countArgumentPlaceholders2("p1={}, p2={}, p3={}, p4={}, p5={}, p6={}, p7={}, p8={}, p9={}", state.indices);
+        ParameterFormatter.formatMessage2(state.buffer, "p1={}, p2={}, p3={}, p4={}, p5={}, p6={}, p7={}, p8={}, p9={}", ARGS, count, state.indices);
+        return state.buffer.length();
+    }
+
+    @Benchmark
+    @BenchmarkMode(Mode.SampleTime)
+    @OutputTimeUnit(TimeUnit.NANOSECONDS)
+    public int latency3Params(final ThreadState state) {
+        state.buffer.setLength(0);
+        int count = ParameterFormatter.countArgumentPlaceholders("p1={}, p2={}, p3={}");
+        ParameterFormatter.formatMessage(state.buffer, "p1={}, p2={}, p3={}", ARGS, count);
+        return state.buffer.length();
+    }
+
+    @Benchmark
+    @BenchmarkMode(Mode.SampleTime)
+    @OutputTimeUnit(TimeUnit.NANOSECONDS)
+    public int latency5Params(final ThreadState state) {
+        state.buffer.setLength(0);
+        int count = ParameterFormatter.countArgumentPlaceholders("p1={}, p2={}, p3={}, p4={}, p5={}");
+        ParameterFormatter.formatMessage(state.buffer, "p1={}, p2={}, p3={}, p4={}, p5={}", ARGS, count);
+        return state.buffer.length();
+    }
+
+    @Benchmark
+    @BenchmarkMode(Mode.SampleTime)
+    @OutputTimeUnit(TimeUnit.NANOSECONDS)
+    public int latency7Params(final ThreadState state) {
+        state.buffer.setLength(0);
+        int count = ParameterFormatter.countArgumentPlaceholders("p1={}, p2={}, p3={}, p4={}, p5={}, p6={}, p7={}");
+        ParameterFormatter.formatMessage(state.buffer, "p1={}, p2={}, p3={}, p4={}, p5={}, p6={}, p7={}", ARGS, count);
+        return state.buffer.length();
+    }
+
+    @Benchmark
+    @BenchmarkMode(Mode.SampleTime)
+    @OutputTimeUnit(TimeUnit.NANOSECONDS)
+    public int latency9Params(final ThreadState state) {
+        state.buffer.setLength(0);
+        int count = ParameterFormatter.countArgumentPlaceholders("p1={}, p2={}, p3={}, p4={}, p5={}, p6={}, p7={}, p8={}, p9={}");
+        ParameterFormatter.formatMessage(state.buffer, "p1={}, p2={}, p3={}, p4={}, p5={}, p6={}, p7={}, p8={}, p9={}", ARGS, count);
+        return state.buffer.length();
+    }
+}


[4/4] logging-log4j2 git commit: LOG4J2-1271 use the System.arrayCopy-based fast copy where possible for formatting the message

Posted by rp...@apache.org.
LOG4J2-1271 use the System.arrayCopy-based fast copy where possible for formatting the message


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

Branch: refs/heads/master
Commit: a21828565ba164aea8fe6356d656d0a345d99f4e
Parents: 8242eb6
Author: rpopma <rp...@apache.org>
Authored: Mon Mar 28 17:11:43 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Mon Mar 28 17:11:43 2016 +0900

----------------------------------------------------------------------
 .../log4j/message/ParameterizedMessage.java     | 13 ++++++++++---
 .../message/ReusableParameterizedMessage.java   | 20 +++++++++++++++++---
 2 files changed, 27 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a2182856/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 fe22b02..e1ef2a5 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
@@ -67,6 +67,7 @@ public class ParameterizedMessage implements Message, StringBuilderFormattable {
 
     private String formattedMessage;
     private transient Throwable throwable;
+    private int[] indices;
 
     /**
      * Creates a parameterized message.
@@ -133,7 +134,8 @@ public class ParameterizedMessage implements Message, StringBuilderFormattable {
 
     private void init(String messagePattern) {
         this.messagePattern = messagePattern;
-        final int usedCount = countArgumentPlaceholders(messagePattern);
+        this.indices = new int[messagePattern == null ? 0 : messagePattern.length() << 1];
+        final int usedCount = ParameterFormatter.countArgumentPlaceholders2(messagePattern, indices);
         initThrowable(argArray, usedCount);
     }
 
@@ -207,8 +209,13 @@ public class ParameterizedMessage implements Message, StringBuilderFormattable {
         if (formattedMessage != null) {
             buffer.append(formattedMessage);
         } else {
-            ParameterFormatter.formatMessage(buffer, messagePattern, argArray,
-                    argArray == null ? 0 : argArray.length);
+            if (indices[0] < 0) {
+                ParameterFormatter.formatMessage(buffer, messagePattern, argArray,
+                        argArray == null ? 0 : argArray.length);
+            } else {
+                ParameterFormatter.formatMessage2(buffer, messagePattern, argArray,
+                        argArray == null ? 0 : argArray.length, indices);
+            }
         }
     }
 

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a2182856/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java
index bd6205f..bf7095c 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java
@@ -35,6 +35,7 @@ public class ReusableParameterizedMessage implements ReusableMessage {
 
     private String messagePattern;
     private int argCount;
+    private int[] indices = new int[256];
     private transient Object[] varargs;
     private transient Object[] params = new Object[10];
     private transient Throwable throwable;
@@ -53,14 +54,23 @@ public class ReusableParameterizedMessage implements ReusableMessage {
         return varargs == null ? params : varargs;
     }
 
-    private void init(String messagePattern, int argCount, Object[] paramArray) {
+    private void init(final String messagePattern, final int argCount, final Object[] paramArray) {
         this.varargs = null;
         this.messagePattern = messagePattern;
         this.argCount= argCount;
-        int usedCount = ParameterFormatter.countArgumentPlaceholders(messagePattern);
+        int usedCount = count(messagePattern, indices);
         initThrowable(paramArray, usedCount);
     }
 
+    private static int count(final String messagePattern, final int[] indices) {
+        try {
+            // try the fast path first
+            return ParameterFormatter.countArgumentPlaceholders2(messagePattern, indices);
+        } catch (final Exception ex) { // fallback if more than int[] length (256) parameter placeholders
+            return ParameterFormatter.countArgumentPlaceholders(messagePattern);
+        }
+    }
+
     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];
@@ -238,7 +248,11 @@ public class ReusableParameterizedMessage implements ReusableMessage {
 
     @Override
     public void formatTo(final StringBuilder buffer) {
-        ParameterFormatter.formatMessage(buffer, messagePattern, getParams(), argCount);
+        if (indices[0] < 0) {
+            ParameterFormatter.formatMessage(buffer, messagePattern, getParams(), argCount);
+        } else {
+            ParameterFormatter.formatMessage2(buffer, messagePattern, getParams(), argCount, indices);
+        }
     }
 
 


[2/4] logging-log4j2 git commit: LOG4J2-1271 added multiple optimized implementations

Posted by rp...@apache.org.
LOG4J2-1271 added multiple optimized implementations


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

Branch: refs/heads/master
Commit: b1e1db8be5efdf8ae816e5c542419b353aaf79f3
Parents: 0ed5b50
Author: rpopma <rp...@apache.org>
Authored: Mon Mar 28 17:10:00 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Mon Mar 28 17:10:00 2016 +0900

----------------------------------------------------------------------
 .../log4j/message/ParameterFormatter.java       | 103 +++++++++++++++++++
 1 file changed, 103 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/b1e1db8b/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
index 453cd52..4e1f825 100644
--- 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
@@ -96,6 +96,65 @@ final class 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 countArgumentPlaceholders2(final String messagePattern, final int[] indices) {
+        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;
+                indices[0] = -1; // escaping means fast path is not available...
+            } else if (curChar == DELIM_START) {
+                if (!isEscaped && messagePattern.charAt(i + 1) == DELIM_STOP) {
+                    indices[result] = i;
+                    result++;
+                    i++;
+                }
+                isEscaped = false;
+            } else {
+                isEscaped = false;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * 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 countArgumentPlaceholders3(final char[] messagePattern, int length, final int[] indices) {
+        int result = 0;
+        boolean isEscaped = false;
+        for (int i = 0; i < length - 1; i++) {
+            final char curChar = messagePattern[i];
+            if (curChar == ESCAPE_CHAR) {
+                isEscaped = !isEscaped;
+            } else if (curChar == DELIM_START) {
+                if (!isEscaped && messagePattern[i + 1] == DELIM_STOP) {
+                    indices[result] = i;
+                    result++;
+                    i++;
+                }
+                isEscaped = false;
+            } else {
+                isEscaped = false;
+            }
+        }
+        return result;
+    }
+
+    /**
      * Replace placeholders in the given messagePattern with arguments.
      *
      * @param messagePattern the message pattern containing placeholders.
@@ -116,6 +175,50 @@ final class ParameterFormatter {
      * @param messagePattern the message pattern containing placeholders.
      * @param arguments      the arguments to be used to replace placeholders.
      */
+    static void formatMessage2(final StringBuilder buffer, final String messagePattern,
+            final Object[] arguments, final int argCount, final int[] indices) {
+        if (messagePattern == null || arguments == null || argCount == 0) {
+            buffer.append(messagePattern);
+            return;
+        }
+        int previous = 0;
+        for (int i = 0; i < argCount; i++) {
+            buffer.append(messagePattern, previous, indices[i]);
+            previous = indices[i] + 2;
+            recursiveDeepToString(arguments[i], buffer, null);
+        }
+        buffer.append(messagePattern, previous, messagePattern.length());
+    }
+
+    /**
+     * 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 formatMessage3(final StringBuilder buffer, final char[] messagePattern, final int patternLength,
+            final Object[] arguments, final int argCount, final int[] indices) {
+        if (messagePattern == null || arguments == null || argCount == 0) {
+            buffer.append(messagePattern);
+            return;
+        }
+        int previous = 0;
+        for (int i = 0; i < argCount; i++) {
+            buffer.append(messagePattern, previous, indices[i]);
+            previous = indices[i] + 2;
+            recursiveDeepToString(arguments[i], buffer, null);
+        }
+        buffer.append(messagePattern, previous, patternLength);
+    }
+
+    /**
+     * 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) {