This is an automated email from the ASF dual-hosted git repository.

vy pushed a commit to branch recycler-api-3.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 623c47c7ecd9eb12c2597181bfe40af13db2a7a5
Author: Volkan Yazıcı <[email protected]>
AuthorDate: Tue Mar 28 19:55:18 2023 +0200

    More `ThreadLocal`-to-recycler refactoring
---
 .../apache/log4j/layout/Log4j1SyslogLayout.java    |  18 ++--
 .../org/apache/log4j/layout/Log4j1XmlLayout.java   |  25 +++--
 .../core/layout/AbstractStringLayoutTest.java      |  15 +--
 .../java/org/apache/logging/log4j/core/Layout.java |   1 +
 .../logging/log4j/core/layout/AbstractLayout.java  |  70 +++++++++---
 .../log4j/core/layout/AbstractStringLayout.java    | 119 +++++++--------------
 .../logging/log4j/core/layout/GelfLayout.java      |  49 +++++----
 .../logging/log4j/core/layout/HtmlLayout.java      |  45 +++++---
 .../core/layout/LockingStringBuilderEncoder.java   |  13 ++-
 .../logging/log4j/core/layout/PatternLayout.java   |  26 ++---
 .../logging/log4j/core/layout/Rfc5424Layout.java   |   6 +-
 .../log4j/core/layout/StringBuilderEncoder.java    |  84 +++++++--------
 .../logging/log4j/core/layout/SyslogLayout.java    |  16 ++-
 .../log4j/core/layout/TextEncoderHelper.java       |   6 --
 .../log4j/csv/layout/CsvLogEventLayout.java        |   4 +-
 .../log4j/csv/layout/CsvParameterLayout.java       |   4 +-
 .../layout/template/json/JsonTemplateLayout.java   |  68 ++----------
 .../template/json/JsonTemplateLayoutDefaults.java  |   6 +-
 .../json/resolver/EventResolverContext.java        |  10 +-
 ...bstractStringLayoutStringEncodingBenchmark.java |  21 ++--
 20 files changed, 293 insertions(+), 313 deletions(-)

diff --git 
a/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1SyslogLayout.java 
b/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1SyslogLayout.java
index 0787dbbd45..b97a7a16d3 100644
--- 
a/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1SyslogLayout.java
+++ 
b/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1SyslogLayout.java
@@ -24,6 +24,7 @@ import java.util.Map;
 import org.apache.logging.log4j.core.Layout;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.StringLayout;
+import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
 import org.apache.logging.log4j.core.layout.AbstractStringLayout;
 import org.apache.logging.log4j.core.net.Facility;
@@ -83,7 +84,7 @@ public final class Log4j1SyslogLayout  extends 
AbstractStringLayout {
                 LOGGER.error("Log4j1SyslogLayout: the message layout must be a 
StringLayout.");
                 return null;
             }
-            return new Log4j1SyslogLayout(facility, facilityPrinting, header, 
(StringLayout) messageLayout, getCharset());
+            return new Log4j1SyslogLayout(getConfiguration(), facility, 
facilityPrinting, header, (StringLayout) messageLayout, getCharset());
         }
 
         public Facility getFacility() {
@@ -145,9 +146,14 @@ public final class Log4j1SyslogLayout  extends 
AbstractStringLayout {
     private final LogEventPatternConverter dateConverter =  
DatePatternConverter.newInstance(dateFormatOptions);
 
 
-    private Log4j1SyslogLayout(final Facility facility, final boolean 
facilityPrinting, final boolean header,
-            final StringLayout messageLayout, final Charset charset) {
-        super(charset);
+    private Log4j1SyslogLayout(
+            final Configuration config,
+            final Facility facility,
+            final boolean facilityPrinting,
+            final boolean header,
+            final StringLayout messageLayout,
+            final Charset charset) {
+        super(config, charset);
         this.facility = facility;
         this.facilityPrinting = facilityPrinting;
         this.header = header;
@@ -166,7 +172,7 @@ public final class Log4j1SyslogLayout  extends 
AbstractStringLayout {
         // so we generate the message first
         final String message = messageLayout != null ? 
messageLayout.toSerializable(event)
                 : event.getMessage().getFormattedMessage();
-        final StringBuilder buf = acquireStringBuilder();
+        final StringBuilder buf = stringBuilderRecycler.acquire();
 
         try {
             buf.append('<');
@@ -194,7 +200,7 @@ public final class Log4j1SyslogLayout  extends 
AbstractStringLayout {
             // TODO: splitting message into 1024 byte chunks?
             return buf.toString();
         } finally {
-            releaseStringBuilder(buf);
+            stringBuilderRecycler.release(buf);
         }
     }
 
diff --git 
a/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1XmlLayout.java 
b/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1XmlLayout.java
index 4f48fdd091..0055df3fb7 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1XmlLayout.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1XmlLayout.java
@@ -24,8 +24,11 @@ import java.util.Objects;
 
 import org.apache.logging.log4j.core.Layout;
 import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
 import org.apache.logging.log4j.core.layout.AbstractStringLayout;
 import org.apache.logging.log4j.core.layout.ByteBufferDestination;
+import org.apache.logging.log4j.core.layout.Encoder;
 import org.apache.logging.log4j.core.util.Transform;
 import org.apache.logging.log4j.plugins.Configurable;
 import org.apache.logging.log4j.plugins.Plugin;
@@ -52,15 +55,16 @@ public final class Log4j1XmlLayout extends 
AbstractStringLayout {
     @PluginFactory
     public static Log4j1XmlLayout createLayout(
             // @formatter:off
+            @PluginConfiguration Configuration configuration,
             @PluginAttribute(value = "locationInfo") final boolean 
locationInfo,
             @PluginAttribute(value = "properties") final boolean properties
             // @formatter:on
     ) {
-        return new Log4j1XmlLayout(locationInfo, properties);
+        return new Log4j1XmlLayout(configuration, locationInfo, properties);
     }
 
-    private Log4j1XmlLayout(final boolean locationInfo, final boolean 
properties) {
-        super(StandardCharsets.UTF_8);
+    private Log4j1XmlLayout(final Configuration configuration, final boolean 
locationInfo, final boolean properties) {
+        super(configuration, StandardCharsets.UTF_8);
         this.locationInfo = locationInfo;
         this.properties = properties;
     }
@@ -75,23 +79,28 @@ public final class Log4j1XmlLayout extends 
AbstractStringLayout {
 
     @Override
     public void encode(final LogEvent event, final ByteBufferDestination 
destination) {
-        final StringBuilder text = acquireStringBuilder();
+        final StringBuilder text = stringBuilderRecycler.acquire();
         try {
             formatTo(event, text);
-            getStringBuilderEncoder().encode(text, destination);
+            final Encoder<StringBuilder> stringBuilderEncoder = 
stringBuilderEncoderRecycler.acquire();
+            try {
+                stringBuilderEncoder.encode(text, destination);
+            } finally {
+                stringBuilderEncoderRecycler.release(stringBuilderEncoder);
+            }
         } finally {
-            releaseStringBuilder(text);
+            stringBuilderRecycler.release(text);
         }
     }
 
     @Override
     public String toSerializable(final LogEvent event) {
-        final StringBuilder text = acquireStringBuilder();
+        final StringBuilder text = stringBuilderRecycler.acquire();
         try {
             formatTo(event, text);
             return text.toString();
         } finally {
-            releaseStringBuilder(text);
+            stringBuilderRecycler.release(text);
         }
     }
 
diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/AbstractStringLayoutTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/AbstractStringLayoutTest.java
index 175bacc9ae..0827a4b082 100644
--- 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/AbstractStringLayoutTest.java
+++ 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/AbstractStringLayoutTest.java
@@ -19,6 +19,7 @@ package org.apache.logging.log4j.core.layout;
 import java.nio.charset.Charset;
 
 import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.DefaultConfiguration;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -33,7 +34,7 @@ public class AbstractStringLayoutTest {
         public static int MAX_STRING_BUILDER_SIZE = 
AbstractStringLayout.MAX_STRING_BUILDER_SIZE;
 
         public ConcreteStringLayout() {
-            super(Charset.defaultCharset());
+            super(new DefaultConfiguration(), Charset.defaultCharset());
         }
 
         @Override
@@ -46,7 +47,7 @@ public class AbstractStringLayoutTest {
     public void testGetStringBuilderCapacityRestrictedToMax() {
 
         final ConcreteStringLayout layout = new ConcreteStringLayout();
-        final StringBuilder sb = layout.acquireStringBuilder();
+        final StringBuilder sb = layout.stringBuilderRecycler.acquire();
         final int initialCapacity = sb.capacity();
         try {
             assertEquals(ConcreteStringLayout.DEFAULT_STRING_BUILDER_SIZE, 
sb.capacity(), "initial capacity");
@@ -57,10 +58,10 @@ public class AbstractStringLayoutTest {
             assertEquals(initialCapacity, sb.capacity(), "capacity not grown");
             assertEquals(SMALL, sb.length(), "length=msg length");
         } finally {
-            layout.releaseStringBuilder(sb);
+            layout.stringBuilderRecycler.release(sb);
         }
 
-        final StringBuilder sb2 = layout.acquireStringBuilder();
+        final StringBuilder sb2 = layout.stringBuilderRecycler.acquire();
         try {
             assertEquals(sb2.capacity(), initialCapacity, "capacity 
unchanged");
             assertEquals(0, sb2.length(), "empty, ready for use");
@@ -76,16 +77,16 @@ public class AbstractStringLayoutTest {
             assertEquals(0, sb2.length(), "empty, cleared");
             assertTrue(sb2.capacity() >= 
ConcreteStringLayout.MAX_STRING_BUILDER_SIZE, "capacity remains very large");
         } finally {
-            layout.releaseStringBuilder(sb2);
+            layout.stringBuilderRecycler.release(sb2);
         }
 
-        final StringBuilder sb3 = layout.acquireStringBuilder();
+        final StringBuilder sb3 = layout.stringBuilderRecycler.acquire();
         try {
             assertEquals(ConcreteStringLayout.MAX_STRING_BUILDER_SIZE, 
sb3.capacity(),
                     "capacity, trimmed to MAX_STRING_BUILDER_SIZE");
             assertEquals(0, sb3.length(), "empty, ready for use");
         } finally {
-            layout.releaseStringBuilder(sb3);
+            layout.stringBuilderRecycler.release(sb3);
         }
 
     }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/Layout.java 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/Layout.java
index c2308bdb1a..b833b060de 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/Layout.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/Layout.java
@@ -97,4 +97,5 @@ public interface Layout extends Encoder<LogEvent> {
     default boolean requiresLocation() {
         return false;
     }
+
 }
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractLayout.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractLayout.java
index a0fdc153a1..34a595e33b 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractLayout.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractLayout.java
@@ -16,8 +16,11 @@
  */
 package org.apache.logging.log4j.core.layout;
 
-import java.util.HashMap;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
 import java.util.Map;
+import java.util.Objects;
 
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.core.Layout;
@@ -33,6 +36,8 @@ import org.apache.logging.log4j.util.Cast;
  */
 public abstract class AbstractLayout implements Layout {
 
+    private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
+
     /**
      * Subclasses can extend this abstract Builder.
      *
@@ -43,6 +48,9 @@ public abstract class AbstractLayout implements Layout {
         @PluginConfiguration
         private Configuration configuration;
 
+        @PluginBuilderAttribute
+        private Charset charset;
+
         @PluginBuilderAttribute
         private byte[] footer;
 
@@ -57,6 +65,10 @@ public abstract class AbstractLayout implements Layout {
             return configuration;
         }
 
+        public Charset getCharset() {
+            return charset;
+        }
+
         public byte[] getFooter() {
             return footer;
         }
@@ -70,6 +82,11 @@ public abstract class AbstractLayout implements Layout {
             return asBuilder();
         }
 
+        public B setCharset(final Charset charset) {
+            this.charset = charset;
+            return asBuilder();
+        }
+
         public B setFooter(final byte[] footer) {
             this.footer = footer;
             return asBuilder();
@@ -92,6 +109,11 @@ public abstract class AbstractLayout implements Layout {
      */
     protected final Configuration configuration;
 
+    /**
+     * The character set used for encoding log events.
+     */
+    protected final Charset charset;
+
     /**
      * The number of events successfully processed by this layout.
      */
@@ -107,19 +129,38 @@ public abstract class AbstractLayout implements Layout {
      */
     protected final byte[] header;
 
+    /**
+     * Constructs a UTF-8 encoded layout with an optional header and footer.
+     *
+     * @param configuration a configuration
+     * @param header a header to include when the stream is opened, may be null
+     * @param footer the footer to add when the stream is closed, may be null
+     * @deprecated use {@link AbstractLayout#AbstractLayout(Configuration, 
Charset, byte[], byte[])} instead
+     */
+    @Deprecated
+    public AbstractLayout(
+            final Configuration configuration,
+            final byte[] header,
+            final byte[] footer) {
+        this(configuration, DEFAULT_CHARSET, header, footer);
+    }
+
     /**
      * Constructs a layout with an optional header and footer.
      *
-     * @param configuration
-     *            The configuration
-     * @param header
-     *            The header to include when the stream is opened. May be null.
-     * @param footer
-     *            The footer to add when the stream is closed. May be null.
+     * @param configuration a configuration
+     * @param charset a character set used for encoding log events; if null, 
UTF-8 will be used
+     * @param header a header to include when the stream is opened, may be null
+     * @param footer the footer to add when the stream is closed, may be null
      */
-    public AbstractLayout(final Configuration configuration, final byte[] 
header, final byte[] footer) {
+    public AbstractLayout(
+            final Configuration configuration,
+            final Charset charset,
+            final byte[] header,
+            final byte[] footer) {
         super();
-        this.configuration = configuration;
+        this.configuration = Objects.requireNonNull(configuration, 
"configuration");
+        this.charset = charset != null ? charset : DEFAULT_CHARSET;
         this.header = header;
         this.footer = footer;
     }
@@ -128,9 +169,14 @@ public abstract class AbstractLayout implements Layout {
         return configuration;
     }
 
+    @Override
+    public Charset getCharset() {
+        return charset;
+    }
+
     @Override
     public Map<String, String> getContentFormat() {
-        return new HashMap<>();
+        return Collections.emptyMap();
     }
 
     /**
@@ -189,13 +235,11 @@ public abstract class AbstractLayout implements Layout {
      *
      * @param event the LogEvent to encode.
      * @param destination holds the ByteBuffer to write into.
-     * @see AbstractStringLayout#acquireStringBuilder()
-     * @see AbstractStringLayout#releaseStringBuilder(StringBuilder)
-     * @see AbstractStringLayout#getStringBuilderEncoder()
      */
     @Override
     public void encode(final LogEvent event, final ByteBufferDestination 
destination) {
         final byte[] data = toByteArray(event);
         destination.writeBytes(data, 0, data.length);
     }
+
 }
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractStringLayout.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractStringLayout.java
index 55572b4252..dee942e936 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractStringLayout.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractStringLayout.java
@@ -17,7 +17,6 @@
 package org.apache.logging.log4j.core.layout;
 
 import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
 
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.StringLayout;
@@ -25,11 +24,8 @@ import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.config.LoggerConfig;
 import org.apache.logging.log4j.core.impl.Log4jProperties;
 import org.apache.logging.log4j.core.impl.LogEventFactory;
-import org.apache.logging.log4j.core.util.Constants;
 import org.apache.logging.log4j.core.util.StringEncoder;
-import org.apache.logging.log4j.plugins.PluginBuilderAttribute;
 import org.apache.logging.log4j.plugins.PluginElement;
-import org.apache.logging.log4j.spi.LoggingSystem;
 import org.apache.logging.log4j.spi.Recycler;
 import org.apache.logging.log4j.spi.RecyclerFactory;
 import org.apache.logging.log4j.util.PropertiesUtil;
@@ -47,19 +43,12 @@ public abstract class AbstractStringLayout extends 
AbstractLayout implements Str
 
     public abstract static class Builder<B extends Builder<B>> extends 
AbstractLayout.Builder<B> {
 
-        @PluginBuilderAttribute(value = "charset")
-        private Charset charset;
-
         @PluginElement("footerSerializer")
         private Serializer footerSerializer;
 
         @PluginElement("headerSerializer")
         private Serializer headerSerializer;
 
-        public Charset getCharset() {
-            return charset;
-        }
-
         public Serializer getFooterSerializer() {
             return footerSerializer;
         }
@@ -68,11 +57,6 @@ public abstract class AbstractStringLayout extends 
AbstractLayout implements Str
             return headerSerializer;
         }
 
-        public B setCharset(final Charset charset) {
-            this.charset = charset;
-            return asBuilder();
-        }
-
         public B setFooterSerializer(final Serializer footerSerializer) {
             this.footerSerializer = footerSerializer;
             return asBuilder();
@@ -86,6 +70,7 @@ public abstract class AbstractStringLayout extends 
AbstractLayout implements Str
     }
 
     public interface Serializer extends Serializer2 {
+
         String toSerializable(final LogEvent event);
 
         default boolean requiresLocation() {
@@ -97,6 +82,7 @@ public abstract class AbstractStringLayout extends 
AbstractLayout implements Str
             builder.append(toSerializable(event));
             return builder;
         }
+
     }
 
     /**
@@ -113,14 +99,15 @@ public abstract class AbstractStringLayout extends 
AbstractLayout implements Str
      */
     protected static final int DEFAULT_STRING_BUILDER_SIZE = 1024;
 
-    protected static final int MAX_STRING_BUILDER_SIZE = 
Math.max(DEFAULT_STRING_BUILDER_SIZE,
-            size(Log4jProperties.GC_LAYOUT_STRING_BUILDER_MAX_SIZE, 2 * 1024));
+    protected static final int MAX_STRING_BUILDER_SIZE = Math.max(
+            DEFAULT_STRING_BUILDER_SIZE,
+            PropertiesUtil
+                    .getProperties()
+                    .getIntegerProperty(
+                            Log4jProperties.GC_LAYOUT_STRING_BUILDER_MAX_SIZE,
+                            Math.multiplyExact(2, 
DEFAULT_STRING_BUILDER_SIZE)));
 
-    private static int size(final String property, final int defaultValue) {
-        return PropertiesUtil.getProperties().getIntegerProperty(property, 
defaultValue);
-    }
-
-    protected static Recycler<StringBuilder> createRecycler(final 
RecyclerFactory recyclerFactory) {
+    protected static Recycler<StringBuilder> createStringBuilderRecycler(final 
RecyclerFactory recyclerFactory) {
         return recyclerFactory.create(
                 () -> new StringBuilder(DEFAULT_STRING_BUILDER_SIZE),
                 stringBuilder -> {
@@ -130,58 +117,61 @@ public abstract class AbstractStringLayout extends 
AbstractLayout implements Str
         );
     }
 
-    private Encoder<StringBuilder> textEncoder;
-    /**
-     * The charset for the formatted message.
-     */
-    private final Charset charset;
-
     private final Serializer footerSerializer;
 
     private final Serializer headerSerializer;
 
-    private final RecyclerFactory recyclerFactory;
+    protected final Recycler<Encoder<StringBuilder>> 
stringBuilderEncoderRecycler;
 
-    private final Recycler<StringBuilder> recycler;
+    protected final Recycler<StringBuilder> stringBuilderRecycler;
 
-    protected AbstractStringLayout(final Charset charset) {
-        this(charset, (byte[]) null, (byte[]) null);
+    protected AbstractStringLayout(final Configuration configuration, final 
Charset charset) {
+        this(configuration, charset, null, (byte[]) null);
     }
 
     /**
      * Builds a new layout.
-     * @param aCharset the charset used to encode the header bytes, footer 
bytes and anything else that needs to be
-     *      converted from strings to bytes.
+     * @param configuration a configuration
      * @param header the header bytes
      * @param footer the footer bytes
      */
-    protected AbstractStringLayout(final Charset aCharset, final byte[] 
header, final byte[] footer) {
-        super(null, header, footer);
+    protected AbstractStringLayout(
+            final Configuration configuration,
+            final Charset charset,
+            final byte[] header,
+            final byte[] footer) {
+        super(configuration, charset, header, footer);
         this.headerSerializer = null;
         this.footerSerializer = null;
-        this.charset = aCharset == null ? StandardCharsets.UTF_8 : aCharset;
-        textEncoder = Constants.ENABLE_DIRECT_ENCODERS ? new 
StringBuilderEncoder(charset) : null;
-        recyclerFactory = LoggingSystem.getRecyclerFactory();
-        recycler = createRecycler(recyclerFactory);
+        final RecyclerFactory recyclerFactory = 
configuration.getRecyclerFactory();
+        this.stringBuilderEncoderRecycler = 
createStringBuilderEncoderRecycler(recyclerFactory, getCharset());
+        this.stringBuilderRecycler = 
createStringBuilderRecycler(recyclerFactory);
     }
 
     /**
      * Builds a new layout.
-     * @param config the configuration
-     * @param aCharset the charset used to encode the header bytes, footer 
bytes and anything else that needs to be
+     * @param configuration the configuration
+     * @param charset the charset used to encode the header bytes, footer 
bytes and anything else that needs to be
      *      converted from strings to bytes.
      * @param headerSerializer the header bytes serializer
      * @param footerSerializer the footer bytes serializer
      */
-    protected AbstractStringLayout(final Configuration config, final Charset 
aCharset,
-            final Serializer headerSerializer, final Serializer 
footerSerializer) {
-        super(config, null, null);
+    protected AbstractStringLayout(
+            final Configuration configuration,
+            final Charset charset,
+            final Serializer headerSerializer,
+            final Serializer footerSerializer) {
+        super(configuration, charset, null, null);
         this.headerSerializer = headerSerializer;
         this.footerSerializer = footerSerializer;
-        this.charset = aCharset == null ? StandardCharsets.UTF_8 : aCharset;
-        textEncoder = Constants.ENABLE_DIRECT_ENCODERS ? new 
StringBuilderEncoder(charset) : null;
-        recyclerFactory = config != null ? config.getRecyclerFactory() : 
LoggingSystem.getRecyclerFactory();
-        recycler = createRecycler(recyclerFactory);
+        this.stringBuilderEncoderRecycler = 
createStringBuilderEncoderRecycler(configuration.getRecyclerFactory(), 
getCharset());
+        this.stringBuilderRecycler = 
createStringBuilderRecycler(configuration.getRecyclerFactory());
+    }
+
+    private static Recycler<Encoder<StringBuilder>> 
createStringBuilderEncoderRecycler(
+            final RecyclerFactory recyclerFactory,
+            final Charset charset) {
+        return recyclerFactory.create(() -> new StringBuilderEncoder(charset));
     }
 
     protected byte[] getBytes(final String s) {
@@ -229,32 +219,6 @@ public abstract class AbstractStringLayout extends 
AbstractLayout implements Str
         return headerSerializer;
     }
 
-    /**
-     * Returns a {@code Encoder<StringBuilder>} that this Layout 
implementation can use for encoding log events.
-     *
-     * @return a {@code Encoder<StringBuilder>}
-     */
-    protected Encoder<StringBuilder> getStringBuilderEncoder() {
-        if (textEncoder == null) {
-            textEncoder = new StringBuilderEncoder(getCharset());
-        }
-        return textEncoder;
-    }
-
-    /**
-     * Returns a StringBuilder that may be recycled via {@link 
#releaseStringBuilder(StringBuilder)} when done being used.
-     */
-    protected StringBuilder acquireStringBuilder() {
-        return recycler.acquire();
-    }
-
-    /**
-     * Recycles a StringBuilder acquired via {@link #acquireStringBuilder()} 
so that it may be acquired again later.
-     */
-    protected void releaseStringBuilder(final StringBuilder stringBuilder) {
-        recycler.release(stringBuilder);
-    }
-
     protected byte[] serializeToBytes(final Serializer serializer, final 
byte[] defaultValue) {
         final String serializable = serializeToString(serializer);
         if (serializable == null) {
@@ -289,7 +253,4 @@ public abstract class AbstractStringLayout extends 
AbstractLayout implements Str
     @Override
     public abstract String toSerializable(LogEvent event);
 
-    public RecyclerFactory getRecyclerFactory() {
-        return recyclerFactory;
-    }
 }
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/GelfLayout.java 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/GelfLayout.java
index bd911e8e8b..eb43cd551e 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/GelfLayout.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/GelfLayout.java
@@ -16,6 +16,15 @@
  */
 package org.apache.logging.log4j.core.layout;
 
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.GZIPOutputStream;
+
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.core.Layout;
 import org.apache.logging.log4j.core.LogEvent;
@@ -36,15 +45,6 @@ import org.apache.logging.log4j.util.StringBuilders;
 import org.apache.logging.log4j.util.Strings;
 import org.apache.logging.log4j.util.TriConsumer;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
-import java.util.*;
-import java.util.zip.DeflaterOutputStream;
-import java.util.zip.GZIPOutputStream;
-
 /**
  * Lays out events in the Graylog Extended Log Format (GELF) 1.1.
  * <p>
@@ -437,7 +437,7 @@ public final class GelfLayout extends AbstractStringLayout {
             final boolean includeNewLineDelimiter, final boolean 
omitEmptyFields, final ListChecker mdcChecker,
             final ListChecker mapChecker, final PatternLayout patternLayout, 
final String mdcPrefix,
             final String mapPrefix) {
-        super(config, StandardCharsets.UTF_8, null, null);
+        super(config, StandardCharsets.UTF_8);
         this.host = host != null ? host : NetUtils.getLocalHostname();
         this.additionalFields = additionalFields != null ? additionalFields : 
new KeyValuePair[0];
         if (config == null) {
@@ -512,12 +512,12 @@ public final class GelfLayout extends 
AbstractStringLayout {
 
     @Override
     public byte[] toByteArray(final LogEvent event) {
-        final StringBuilder text = acquireStringBuilder();
+        final StringBuilder text = stringBuilderRecycler.acquire();
         final byte[] bytes;
         try {
             bytes = getBytes(toText(event, text, false).toString());
         } finally {
-            releaseStringBuilder(text);
+            stringBuilderRecycler.release(text);
         }
         return compressionType != CompressionType.OFF && bytes.length > 
compressionThreshold ? compress(bytes) : bytes;
     }
@@ -528,12 +528,17 @@ public final class GelfLayout extends 
AbstractStringLayout {
             super.encode(event, destination);
             return;
         }
-        final StringBuilder text = acquireStringBuilder();
+        final StringBuilder text = stringBuilderRecycler.acquire();
         try {
-            final Encoder<StringBuilder> helper = getStringBuilderEncoder();
-            helper.encode(toText(event, text, true), destination);
+            final StringBuilder encodedEvent = toText(event, text, true);
+            final Encoder<StringBuilder> helper = 
stringBuilderEncoderRecycler.acquire();
+            try {
+                helper.encode(encodedEvent, destination);
+            } finally {
+                stringBuilderEncoderRecycler.release(helper);
+            }
         } finally {
-            releaseStringBuilder(text);
+            stringBuilderRecycler.release(text);
         }
     }
 
@@ -561,11 +566,11 @@ public final class GelfLayout extends 
AbstractStringLayout {
 
     @Override
     public String toSerializable(final LogEvent event) {
-        final StringBuilder text = acquireStringBuilder();
+        final StringBuilder text = stringBuilderRecycler.acquire();
         try {
             return toText(event, text, false).toString();
         } finally {
-            releaseStringBuilder(text);
+            stringBuilderRecycler.release(text);
         }
     }
 
@@ -614,12 +619,12 @@ public final class GelfLayout extends 
AbstractStringLayout {
         if (event.getThrown() != null || layout != null) {
             builder.append("\"full_message\":\"");
             if (layout != null) {
-                final StringBuilder messageBuffer = acquireStringBuilder();
+                final StringBuilder messageBuffer = 
stringBuilderRecycler.acquire();
                 try {
                     layout.serialize(event, messageBuffer);
                     JsonUtils.quoteAsString(messageBuffer, builder);
                 } finally {
-                    releaseStringBuilder(messageBuffer);
+                    stringBuilderRecycler.release(messageBuffer);
                 }
             } else {
                 if (includeStacktrace) {
@@ -642,12 +647,12 @@ public final class GelfLayout extends 
AbstractStringLayout {
         if (message instanceof CharSequence) {
             JsonUtils.quoteAsString(((CharSequence) message), builder);
         } else if (gcFree && message instanceof StringBuilderFormattable) {
-            final StringBuilder messageBuffer = acquireStringBuilder();
+            final StringBuilder messageBuffer = 
stringBuilderRecycler.acquire();
             try {
                 ((StringBuilderFormattable) message).formatTo(messageBuffer);
                 JsonUtils.quoteAsString(messageBuffer, builder);
             } finally {
-                releaseStringBuilder(messageBuffer);
+                stringBuilderRecycler.release(messageBuffer);
             }
         } else {
             
JsonUtils.quoteAsString(toNullSafeString(message.getFormattedMessage()), 
builder);
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/HtmlLayout.java 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/HtmlLayout.java
index 74afc2db8f..3401ec8985 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/HtmlLayout.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/HtmlLayout.java
@@ -31,7 +31,9 @@ import java.util.Date;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.core.Layout;
 import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
 import org.apache.logging.log4j.core.pattern.DatePatternConverter;
 import org.apache.logging.log4j.core.util.Transform;
 import org.apache.logging.log4j.plugins.Configurable;
@@ -102,9 +104,18 @@ public final class HtmlLayout extends AbstractStringLayout 
{
         }
     }
 
-    private HtmlLayout(final boolean locationInfo, final String title, final 
String contentType, final Charset charset,
-            final String font, final String fontSize, final String headerSize, 
final String datePattern, final String timezone) {
-        super(charset);
+    private HtmlLayout(
+            final Configuration configuration,
+            final boolean locationInfo,
+            final String title,
+            final String contentType,
+            final Charset charset,
+            final String font,
+            final String fontSize,
+            final String headerSize,
+            final String datePattern,
+            final String timezone) {
+        super(configuration, charset);
         this.locationInfo = locationInfo;
         this.title = title;
         this.contentType = addCharsetToContentType(contentType);
@@ -149,7 +160,7 @@ public final class HtmlLayout extends AbstractStringLayout {
      */
     @Override
     public String toSerializable(final LogEvent event) {
-        final StringBuilder sbuf = acquireStringBuilder();
+        final StringBuilder sbuf = stringBuilderRecycler.acquire();
         try {
 
             
sbuf.append(Strings.LINE_SEPARATOR).append("<tr>").append(Strings.LINE_SEPARATOR);
@@ -230,14 +241,11 @@ public final class HtmlLayout extends 
AbstractStringLayout {
 
             return sbuf.toString();
         } finally {
-            releaseStringBuilder(sbuf);
+            stringBuilderRecycler.release(sbuf);
         }
     }
 
     @Override
-    /**
-     * @return The content type.
-     */
     public String getContentType() {
         return contentType;
     }
@@ -293,7 +301,7 @@ public final class HtmlLayout extends AbstractStringLayout {
      */
     @Override
     public byte[] getHeader() {
-        final StringBuilder sbuf = acquireStringBuilder();
+        final StringBuilder sbuf = stringBuilderRecycler.acquire();
         try {
             append(sbuf, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 
Transitional//EN\" ");
             appendLs(sbuf, "\"http://www.w3.org/TR/html4/loose.dtd\";>");
@@ -330,7 +338,7 @@ public final class HtmlLayout extends AbstractStringLayout {
             appendLs(sbuf, "</tr>");
             return sbuf.toString().getBytes(getCharset());
         } finally {
-            releaseStringBuilder(sbuf);
+            stringBuilderRecycler.release(sbuf);
         }
     }
 
@@ -340,14 +348,14 @@ public final class HtmlLayout extends 
AbstractStringLayout {
      */
     @Override
     public byte[] getFooter() {
-        final StringBuilder sbuf = acquireStringBuilder();
+        final StringBuilder sbuf = stringBuilderRecycler.acquire();
         try {
             appendLs(sbuf, "</table>");
             appendLs(sbuf, "<br>");
             appendLs(sbuf, "</body></html>");
             return getBytes(sbuf.toString());
         } finally {
-            releaseStringBuilder(sbuf);
+            stringBuilderRecycler.release(sbuf);
         }
     }
 
@@ -367,6 +375,9 @@ public final class HtmlLayout extends AbstractStringLayout {
 
     public static class Builder implements 
org.apache.logging.log4j.plugins.util.Builder<HtmlLayout> {
 
+        @PluginConfiguration
+        private Configuration configuration;
+
         @PluginBuilderAttribute
         private boolean locationInfo = false;
 
@@ -394,6 +405,11 @@ public final class HtmlLayout extends AbstractStringLayout 
{
         private Builder() {
         }
 
+        public Builder setConfiguration(final Configuration configuration) {
+            this.configuration = configuration;
+            return this;
+        }
+
         public Builder setLocationInfo(final boolean locationInfo) {
             this.locationInfo = locationInfo;
             return this;
@@ -440,8 +456,9 @@ public final class HtmlLayout extends AbstractStringLayout {
             if (contentType == null) {
                 contentType = DEFAULT_CONTENT_TYPE + "; charset=" + charset;
             }
-            return new HtmlLayout(locationInfo, title, contentType, charset, 
fontName, fontSize.getFontSize(),
-                fontSize.larger().getFontSize(), datePattern, timezone);
+            return new HtmlLayout(
+                    configuration, locationInfo, title, contentType, charset, 
fontName, fontSize.getFontSize(),
+                    fontSize.larger().getFontSize(), datePattern, timezone);
         }
     }
 }
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/LockingStringBuilderEncoder.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/LockingStringBuilderEncoder.java
index 5bb9a0aacf..666ad84d91 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/LockingStringBuilderEncoder.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/LockingStringBuilderEncoder.java
@@ -30,6 +30,8 @@ import java.util.Objects;
  */
 public class LockingStringBuilderEncoder implements Encoder<StringBuilder> {
 
+    private static final StatusLogger LOGGER = StatusLogger.getLogger();
+
     private final Charset charset;
     private final CharsetEncoder charsetEncoder;
     private final CharBuffer cachedCharBuffer;
@@ -57,15 +59,12 @@ public class LockingStringBuilderEncoder implements 
Encoder<StringBuilder> {
                 TextEncoderHelper.encodeText(charsetEncoder, cachedCharBuffer, 
destination.getByteBuffer(), source,
                     destination);
             }
-        } catch (final Exception ex) {
-            logEncodeTextException(ex, source, destination);
-            TextEncoderHelper.encodeTextFallBack(charset, source, destination);
+        } catch (final Exception error) {
+            LOGGER.error("Due to `TextEncoderHelper.encodeText()` failure, 
falling back to `String#getBytes(Charset)`", error);
+            byte[] sourceBytes = source.toString().getBytes(charset);
+            destination.writeBytes(sourceBytes, 0, sourceBytes.length);
         }
 
     }
 
-    private void logEncodeTextException(final Exception ex, final 
StringBuilder text,
-                                        final ByteBufferDestination 
destination) {
-        StatusLogger.getLogger().error("Recovering from 
LockingStringBuilderEncoder.encode('{}') error", text, ex);
-    }
 }
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java
index b34f25c249..a1840c8b01 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java
@@ -205,14 +205,18 @@ public final class PatternLayout extends 
AbstractStringLayout {
 
     @Override
     public void encode(final LogEvent event, final ByteBufferDestination 
destination) {
-        final StringBuilder builder = acquireStringBuilder();
+        final StringBuilder builder = stringBuilderRecycler.acquire();
         StringBuilder text = builder;
         try {
             text = eventSerializer.toSerializable(event, builder);
-            final Encoder<StringBuilder> encoder = getStringBuilderEncoder();
-            encoder.encode(text, destination);
+            final Encoder<StringBuilder> encoder = 
stringBuilderEncoderRecycler.acquire();
+            try {
+                encoder.encode(text, destination);
+            } finally {
+                stringBuilderEncoderRecycler.release(encoder);
+            }
         } finally {
-            releaseStringBuilder(text);
+            stringBuilderRecycler.release(text);
         }
     }
 
@@ -379,7 +383,6 @@ public final class PatternLayout extends 
AbstractStringLayout {
     public static class SerializerBuilder implements 
org.apache.logging.log4j.plugins.util.Builder<Serializer> {
 
         private Configuration configuration;
-        private RecyclerFactory recyclerFactory;
         private RegexReplacement replace;
         private String pattern;
         private String defaultPattern;
@@ -393,12 +396,7 @@ public final class PatternLayout extends 
AbstractStringLayout {
             if (Strings.isEmpty(pattern) && Strings.isEmpty(defaultPattern)) {
                 return null;
             }
-            if (recyclerFactory == null) {
-                recyclerFactory = configuration != null
-                        ? configuration.getRecyclerFactory()
-                        : LoggingSystem.getRecyclerFactory();
-            }
-            final Recycler<StringBuilder> recycler = 
createRecycler(recyclerFactory);
+            final Recycler<StringBuilder> recycler = 
createStringBuilderRecycler(configuration.getRecyclerFactory());
             if (patternSelector == null) {
                 try {
                     final PatternParser parser = 
createPatternParser(configuration);
@@ -431,11 +429,6 @@ public final class PatternLayout extends 
AbstractStringLayout {
             return this;
         }
 
-        public SerializerBuilder setRecyclerFactory(final RecyclerFactory 
recyclerFactory) {
-            this.recyclerFactory = recyclerFactory;
-            return this;
-        }
-
         public SerializerBuilder setReplace(final RegexReplacement replace) {
             this.replace = replace;
             return this;
@@ -603,6 +596,7 @@ public final class PatternLayout extends 
AbstractStringLayout {
         private String footer;
 
         private Builder() {
+            setCharset(Charset.defaultCharset());   // LOG4J2-783 Default 
should not be UTF-8
         }
 
         private boolean useAnsiEscapeCodes() {
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/Rfc5424Layout.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/Rfc5424Layout.java
index 71f8497f65..a9846012cc 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/Rfc5424Layout.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/Rfc5424Layout.java
@@ -137,7 +137,7 @@ public final class Rfc5424Layout extends 
AbstractStringLayout {
               final String mdcPrefix, final String eventPrefix, final String 
appName, final String messageId,
               final String excludes, final String includes, final String 
required, final Charset charset,
               final String exceptionPattern, final boolean 
useTLSMessageFormat, final LoggerFields[] loggerFields) {
-        super(charset);
+        super(config, charset);
         final PatternParser exceptionParser = createPatternParser(config, 
ThrowablePatternConverter.class);
         exceptionFormatters = exceptionPattern == null ? null : 
exceptionParser.parse(exceptionPattern);
         this.facility = facility;
@@ -274,7 +274,7 @@ public final class Rfc5424Layout extends 
AbstractStringLayout {
      */
     @Override
     public String toSerializable(final LogEvent event) {
-        final StringBuilder buf = acquireStringBuilder();
+        final StringBuilder buf = stringBuilderRecycler.acquire();
         try {
             appendPriority(buf, event.getLevel());
             appendTimestamp(buf, event.getTimeMillis());
@@ -294,7 +294,7 @@ public final class Rfc5424Layout extends 
AbstractStringLayout {
             }
             return buf.toString();
         } finally {
-            releaseStringBuilder(buf);
+            stringBuilderRecycler.release(buf);
         }
     }
 
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/StringBuilderEncoder.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/StringBuilderEncoder.java
index 9af331634f..6ddf39112c 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/StringBuilderEncoder.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/StringBuilderEncoder.java
@@ -24,73 +24,65 @@ import java.nio.charset.CodingErrorAction;
 import java.util.Objects;
 
 import org.apache.logging.log4j.core.util.Constants;
+import org.apache.logging.log4j.spi.RecyclerFactory;
 import org.apache.logging.log4j.status.StatusLogger;
 
 /**
- * Encoder for StringBuilders that uses ThreadLocals to avoid locking as much 
as possible.
+ * {@link Encoder} for {@link StringBuilder}s.
+ * <p>
+ * <b>{@link StringBuilderEncoder#encode(StringBuilder, ByteBufferDestination) 
encode()} is not thread-safe!</b>
+ * Users are expected to recycle {@link StringBuilderEncoder} instances, e.g., 
using a {@link RecyclerFactory}.
+ * </p>
  */
 public class StringBuilderEncoder implements Encoder<StringBuilder> {
 
-    /**
-     * This ThreadLocal uses raw and inconvenient Object[] to store three 
heterogeneous objects (CharEncoder, CharBuffer
-     * and ByteBuffer) instead of a custom class, because it needs to contain 
JDK classes, no custom (Log4j) classes.
-     * Where possible putting only JDK classes in ThreadLocals is preferable 
to avoid memory leaks in web containers:
-     * the Log4j classes may be loaded by a separate class loader which cannot 
be garbage collected if a thread pool
-     * threadlocal still has a reference to it.
-     *
-     * Using just one ThreadLocal instead of three separate ones is an 
optimization: {@link ThreadLocal.ThreadLocalMap}
-     * is polluted less, {@link ThreadLocal.ThreadLocalMap#get()} is called 
only once on each call to {@link #encode}
-     * instead of three times.
-     */
-    private final ThreadLocal<Object[]> threadLocal = new ThreadLocal<>();
+    private static final StatusLogger LOGGER = StatusLogger.getLogger();
+
+    private final CharsetEncoder charsetEncoder;
+
+    private final CharBuffer charBuffer;
+
+    private final ByteBuffer byteBuffer;
+
     private final Charset charset;
-    private final int charBufferSize;
-    private final int byteBufferSize;
 
     public StringBuilderEncoder(final Charset charset) {
         this(charset, Constants.ENCODER_CHAR_BUFFER_SIZE, 
Constants.ENCODER_BYTE_BUFFER_SIZE);
     }
 
     public StringBuilderEncoder(final Charset charset, final int 
charBufferSize, final int byteBufferSize) {
-        this.charBufferSize = charBufferSize;
-        this.byteBufferSize = byteBufferSize;
         this.charset = Objects.requireNonNull(charset, "charset");
+        this.charsetEncoder = charset
+                .newEncoder()
+                .onMalformedInput(CodingErrorAction.REPLACE)
+                .onUnmappableCharacter(CodingErrorAction.REPLACE);
+        this.charBuffer = CharBuffer.allocate(charBufferSize);
+        this.byteBuffer = ByteBuffer.allocate(byteBufferSize);
     }
 
+    /**
+     * Encodes the given source to the given destination.
+     * <p>
+     * <b>This method is not thread-safe!</b>
+     * Users are expected to recycle {@link StringBuilderEncoder} instances, 
e.g., using a {@link RecyclerFactory}.
+     * </p>
+     *
+     * @param source a source
+     * @param destination a destination
+     */
     @Override
     public void encode(final StringBuilder source, final ByteBufferDestination 
destination) {
         try {
-            final Object[] threadLocalState = getThreadLocalState();
-            final CharsetEncoder charsetEncoder = (CharsetEncoder) 
threadLocalState[0];
-            final CharBuffer charBuffer = (CharBuffer) threadLocalState[1];
-            final ByteBuffer byteBuffer = (ByteBuffer) threadLocalState[2];
             TextEncoderHelper.encodeText(charsetEncoder, charBuffer, 
byteBuffer, source, destination);
-        } catch (final Exception ex) {
-            logEncodeTextException(ex, source);
-            TextEncoderHelper.encodeTextFallBack(charset, source, destination);
+        } catch (final Exception error) {
+            LOGGER.error("Due to `TextEncoderHelper.encodeText()` failure, 
falling back to `String#getBytes(Charset)`", error);
+            byte[] sourceBytes = source.toString().getBytes(charset);
+            destination.writeBytes(sourceBytes, 0, sourceBytes.length);
+        } finally {
+            charsetEncoder.reset();
+            charBuffer.clear();
+            byteBuffer.clear();
         }
     }
 
-    private Object[] getThreadLocalState() {
-        Object[] threadLocalState = threadLocal.get();
-        if (threadLocalState == null) {
-            threadLocalState = new Object[] {
-                    
charset.newEncoder().onMalformedInput(CodingErrorAction.REPLACE)
-                            .onUnmappableCharacter(CodingErrorAction.REPLACE),
-                    CharBuffer.allocate(charBufferSize),
-                    ByteBuffer.allocate(byteBufferSize)
-            };
-            threadLocal.set(threadLocalState);
-        } else {
-            ((CharsetEncoder) threadLocalState[0]).reset();
-            ((CharBuffer) threadLocalState[1]).clear();
-            ((ByteBuffer) threadLocalState[2]).clear();
-        }
-        return threadLocalState;
-    }
-
-    private static void logEncodeTextException(final Exception ex, final 
StringBuilder text) {
-        StatusLogger.getLogger().error("Recovering from 
StringBuilderEncoder.encode('{}') error: {}", text, ex, ex);
-    }
-
 }
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/SyslogLayout.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/SyslogLayout.java
index be88b18009..c854809607 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/SyslogLayout.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/SyslogLayout.java
@@ -28,6 +28,7 @@ import java.util.regex.Pattern;
 
 import org.apache.logging.log4j.core.Layout;
 import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.net.Facility;
 import org.apache.logging.log4j.core.net.Priority;
 import org.apache.logging.log4j.core.util.NetUtils;
@@ -74,7 +75,7 @@ public final class SyslogLayout extends AbstractStringLayout {
 
         @Override
         public SyslogLayout build() {
-            return new SyslogLayout(facility, includeNewLine, escapeNL, 
getCharset());
+            return new SyslogLayout(getConfiguration(), facility, 
includeNewLine, escapeNL, getCharset());
         }
 
         public Facility getFacility() {
@@ -130,8 +131,13 @@ public final class SyslogLayout extends 
AbstractStringLayout {
      */
     private final String localHostname = NetUtils.getLocalHostname();
 
-    protected SyslogLayout(final Facility facility, final boolean includeNL, 
final String escapeNL, final Charset charset) {
-        super(charset);
+    private SyslogLayout(
+            final Configuration configuration,
+            final Facility facility,
+            final boolean includeNL,
+            final String escapeNL,
+            final Charset charset) {
+        super(configuration, charset);
         this.facility = facility;
         this.includeNewLine = includeNL;
         this.escapeNewLine = escapeNL == null ? null : 
Matcher.quoteReplacement(escapeNL);
@@ -145,7 +151,7 @@ public final class SyslogLayout extends 
AbstractStringLayout {
      */
     @Override
     public String toSerializable(final LogEvent event) {
-        final StringBuilder buf = acquireStringBuilder();
+        final StringBuilder buf = stringBuilderRecycler.acquire();
 
         try {
             buf.append('<');
@@ -167,7 +173,7 @@ public final class SyslogLayout extends 
AbstractStringLayout {
             }
             return buf.toString();
         } finally {
-            releaseStringBuilder(buf);
+            stringBuilderRecycler.release(buf);
         }
     }
 
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/TextEncoderHelper.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/TextEncoderHelper.java
index 4191f57121..2b5d643fb9 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/TextEncoderHelper.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/TextEncoderHelper.java
@@ -33,12 +33,6 @@ public class TextEncoderHelper {
     private TextEncoderHelper() {
     }
 
-    static void encodeTextFallBack(final Charset charset, final StringBuilder 
text,
-            final ByteBufferDestination destination) {
-        final byte[] bytes = text.toString().getBytes(charset);
-        destination.writeBytes(bytes, 0, bytes.length);
-    }
-
     /**
      * Converts the specified text to bytes and writes the resulting bytes to 
the specified destination.
      * Attempts to postpone synchronizing on the destination as long as 
possible to minimize lock contention.
diff --git 
a/log4j-csv/src/main/java/org/apache/logging/log4j/csv/layout/CsvLogEventLayout.java
 
b/log4j-csv/src/main/java/org/apache/logging/log4j/csv/layout/CsvLogEventLayout.java
index 216af9a7f1..e755bc3fbc 100644
--- 
a/log4j-csv/src/main/java/org/apache/logging/log4j/csv/layout/CsvLogEventLayout.java
+++ 
b/log4j-csv/src/main/java/org/apache/logging/log4j/csv/layout/CsvLogEventLayout.java
@@ -77,7 +77,7 @@ public class CsvLogEventLayout extends AbstractCsvLayout {
 
     @Override
     public String toSerializable(final LogEvent event) {
-        final StringBuilder buffer = acquireStringBuilder();
+        final StringBuilder buffer = stringBuilderRecycler.acquire();
         final CSVFormat format = getFormat();
         try {
             format.print(event.getNanoTime(), buffer, true);
@@ -100,7 +100,7 @@ public class CsvLogEventLayout extends AbstractCsvLayout {
             StatusLogger.getLogger().error(event.toString(), e);
             return format.getCommentMarker() + " " + e;
         } finally {
-            releaseStringBuilder(buffer);
+            stringBuilderRecycler.release(buffer);
         }
     }
 
diff --git 
a/log4j-csv/src/main/java/org/apache/logging/log4j/csv/layout/CsvParameterLayout.java
 
b/log4j-csv/src/main/java/org/apache/logging/log4j/csv/layout/CsvParameterLayout.java
index cd83093fa0..c176c066f8 100644
--- 
a/log4j-csv/src/main/java/org/apache/logging/log4j/csv/layout/CsvParameterLayout.java
+++ 
b/log4j-csv/src/main/java/org/apache/logging/log4j/csv/layout/CsvParameterLayout.java
@@ -88,7 +88,7 @@ public class CsvParameterLayout extends AbstractCsvLayout {
     public String toSerializable(final LogEvent event) {
         final Message message = event.getMessage();
         final Object[] parameters = message.getParameters();
-        final StringBuilder buffer = acquireStringBuilder();
+        final StringBuilder buffer = stringBuilderRecycler.acquire();
         try {
             getFormat().printRecord(buffer, parameters);
             return buffer.toString();
@@ -96,7 +96,7 @@ public class CsvParameterLayout extends AbstractCsvLayout {
             StatusLogger.getLogger().error(message, e);
             return getFormat().getCommentMarker() + " " + e;
         } finally {
-            releaseStringBuilder(buffer);
+            stringBuilderRecycler.release(buffer);
         }
     }
 
diff --git 
a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java
 
b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java
index a9edfb8b76..51f3b89fce 100644
--- 
a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java
+++ 
b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java
@@ -16,6 +16,12 @@
  */
 package org.apache.logging.log4j.layout.template.json;
 
+import java.nio.charset.Charset;
+import java.util.*;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
 import org.apache.logging.log4j.core.Layout;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.StringLayout;
@@ -23,8 +29,7 @@ import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
 import org.apache.logging.log4j.core.layout.ByteBufferDestination;
 import org.apache.logging.log4j.core.layout.Encoder;
-import org.apache.logging.log4j.core.layout.TextEncoderHelper;
-import org.apache.logging.log4j.core.util.Constants;
+import org.apache.logging.log4j.core.layout.StringBuilderEncoder;
 import org.apache.logging.log4j.core.util.StringEncoder;
 import org.apache.logging.log4j.layout.template.json.resolver.*;
 import org.apache.logging.log4j.layout.template.json.util.JsonWriter;
@@ -32,19 +37,8 @@ import 
org.apache.logging.log4j.layout.template.json.util.Uris;
 import org.apache.logging.log4j.plugins.*;
 import org.apache.logging.log4j.plugins.di.Key;
 import org.apache.logging.log4j.spi.Recycler;
-import org.apache.logging.log4j.status.StatusLogger;
 import org.apache.logging.log4j.util.Strings;
 
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.Charset;
-import java.nio.charset.CharsetEncoder;
-import java.nio.charset.CodingErrorAction;
-import java.util.*;
-import java.util.function.Function;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-
 @Configurable(elementType = Layout.ELEMENT_TYPE)
 @Plugin
 public class JsonTemplateLayout implements StringLayout {
@@ -196,54 +190,6 @@ public class JsonTemplateLayout implements StringLayout {
         };
     }
 
-    /**
-     * {@link org.apache.logging.log4j.core.layout.StringBuilderEncoder} clone 
replacing thread-local allocations with instance fields.
-     */
-    private static final class StringBuilderEncoder implements 
Encoder<StringBuilder> {
-
-        private final Charset charset;
-
-        private final CharsetEncoder charsetEncoder;
-
-        private final CharBuffer charBuffer;
-
-        private final ByteBuffer byteBuffer;
-
-        private StringBuilderEncoder(final Charset charset) {
-            this.charset = charset;
-            this.charsetEncoder = charset
-                    .newEncoder()
-                    .onMalformedInput(CodingErrorAction.REPLACE)
-                    .onUnmappableCharacter(CodingErrorAction.REPLACE);
-            this.charBuffer = 
CharBuffer.allocate(Constants.ENCODER_CHAR_BUFFER_SIZE);
-            this.byteBuffer = 
ByteBuffer.allocate(Constants.ENCODER_BYTE_BUFFER_SIZE);
-        }
-
-        @Override
-        public void encode(
-                final StringBuilder source,
-                final ByteBufferDestination destination) {
-            try {
-                TextEncoderHelper.encodeText(charsetEncoder, charBuffer, 
byteBuffer, source, destination);
-            } catch (final Exception error) {
-                fallbackEncode(charset, source, destination, error);
-            }
-        }
-
-        private /* for JIT-ergonomics: */ static void fallbackEncode(
-                final Charset charset,
-                final StringBuilder source,
-                final ByteBufferDestination destination,
-                final Exception error) {
-            StatusLogger
-                    .getLogger()
-                    .error("TextEncoderHelper.encodeText() failure", error);
-            final byte[] bytes = source.toString().getBytes(charset);
-            destination.writeBytes(bytes, 0, bytes.length);
-        }
-
-    }
-
     @Override
     public byte[] toByteArray(final LogEvent event) {
         final String eventJson = toSerializable(event);
diff --git 
a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutDefaults.java
 
b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutDefaults.java
index 962853fa81..ddec6703d1 100644
--- 
a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutDefaults.java
+++ 
b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutDefaults.java
@@ -16,14 +16,14 @@
  */
 package org.apache.logging.log4j.layout.template.json;
 
-import org.apache.logging.log4j.util.PropertiesUtil;
-import org.apache.logging.log4j.util.PropertyEnvironment;
-
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.util.Locale;
 import java.util.TimeZone;
 
+import org.apache.logging.log4j.util.PropertiesUtil;
+import org.apache.logging.log4j.util.PropertyEnvironment;
+
 public final class JsonTemplateLayoutDefaults {
 
     private JsonTemplateLayoutDefaults() {}
diff --git 
a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/EventResolverContext.java
 
b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/EventResolverContext.java
index aa5cf04261..1b3430b513 100644
--- 
a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/EventResolverContext.java
+++ 
b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/EventResolverContext.java
@@ -16,17 +16,17 @@
  */
 package org.apache.logging.log4j.layout.template.json.resolver;
 
+import java.nio.charset.Charset;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.Configuration;
 import 
org.apache.logging.log4j.layout.template.json.JsonTemplateLayout.EventTemplateAdditionalField;
 import org.apache.logging.log4j.layout.template.json.util.JsonWriter;
 import org.apache.logging.log4j.util.Strings;
 
-import java.nio.charset.Charset;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
 /**
  * {@link TemplateResolverContext} specialized for {@link LogEvent}s.
  *
diff --git 
a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/AbstractStringLayoutStringEncodingBenchmark.java
 
b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/AbstractStringLayoutStringEncodingBenchmark.java
index 1020250bcd..f38613b5fd 100644
--- 
a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/AbstractStringLayoutStringEncodingBenchmark.java
+++ 
b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/AbstractStringLayoutStringEncodingBenchmark.java
@@ -24,6 +24,7 @@ import org.apache.logging.log4j.Marker;
 import org.apache.logging.log4j.ThreadContext;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.StringLayout;
+import org.apache.logging.log4j.core.config.DefaultConfiguration;
 import org.apache.logging.log4j.core.impl.Log4jLogEvent;
 import org.apache.logging.log4j.core.layout.AbstractStringLayout;
 import org.apache.logging.log4j.core.layout.ByteBufferDestination;
@@ -204,7 +205,7 @@ public class AbstractStringLayoutStringEncodingBenchmark {
 
     private static class GetBytesLayout extends AbstractStringLayout {
         public GetBytesLayout(final Charset charset) {
-            super(charset);
+            super(new DefaultConfiguration(), charset);
         }
 
         @Override
@@ -214,19 +215,19 @@ public class AbstractStringLayoutStringEncodingBenchmark {
 
         @Override
         public byte[] toByteArray(final LogEvent event) {
-            final StringBuilder sb = acquireStringBuilder();
+            final StringBuilder sb = stringBuilderRecycler.acquire();
             try {
                 ((StringBuilderFormattable) event.getMessage()).formatTo(sb);
                 return getBytes(sb.toString());
             } finally {
-                releaseStringBuilder(sb);
+                stringBuilderRecycler.release(sb);
             }
         }
     }
 
     private static class EncodeLayout extends AbstractStringLayout {
         public EncodeLayout(final Charset charset) {
-            super(charset);
+            super(new DefaultConfiguration(), charset);
         }
 
         @Override
@@ -241,13 +242,17 @@ public class AbstractStringLayoutStringEncodingBenchmark {
 
         @Override
         public void encode(final LogEvent event, final ByteBufferDestination 
destination) {
-            final StringBuilder sb = acquireStringBuilder();
+            final StringBuilder sb = stringBuilderRecycler.acquire();
             try {
                 ((StringBuilderFormattable) event.getMessage()).formatTo(sb);
-                final Encoder<StringBuilder> helper = 
getStringBuilderEncoder();
-                helper.encode(sb, destination);
+                final Encoder<StringBuilder> helper = 
stringBuilderEncoderRecycler.acquire();
+                try {
+                    helper.encode(sb, destination);
+                } finally {
+                    stringBuilderEncoderRecycler.release(helper);
+                }
             } finally {
-                releaseStringBuilder(sb);
+                stringBuilderRecycler.release(sb);
             }
         }
     }

Reply via email to