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); } } }
