You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by vy...@apache.org on 2023/02/27 09:56:34 UTC

[logging-log4j2] 15/15: replace StringBuilder based API with Buffer-based API (providing separate TextBuffer and BinaryBuffer implementations where appropriate)

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

vy pushed a commit to branch scheduled-for-deletion/LOG4J2-930
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 08386d0e0dd578fc1d020f24975da54da9f15651
Author: rpopma <rp...@apache.org>
AuthorDate: Mon Jan 12 17:42:30 2015 +0900

    replace StringBuilder based API with Buffer-based API (providing
    separate TextBuffer and BinaryBuffer implementations where appropriate)
---
 .../logging/log4j/core/layout/PatternLayout.java   |  154 ++-
 .../logging/log4j/core/layout/Rfc5424Layout.java   | 1438 ++++++++++----------
 .../core/pattern/AbstractStyleNameConverter.java   |  129 +-
 .../log4j/core/pattern/ArrayPatternConverter.java  |    4 +-
 .../core/pattern/ClassNamePatternConverter.java    |   36 +-
 .../log4j/core/pattern/DatePatternConverter.java   |  187 ++-
 .../core/pattern/EncodingPatternConverter.java     |   95 +-
 .../pattern/ExtendedThrowablePatternConverter.java |   44 +-
 .../core/pattern/FileDatePatternConverter.java     |   14 +-
 .../core/pattern/FileLocationPatternConverter.java |   30 +-
 .../logging/log4j/core/pattern/FormattingInfo.java |   86 +-
 .../core/pattern/FullLocationPatternConverter.java |   29 +-
 .../log4j/core/pattern/HighlightConverter.java     |  472 +++----
 .../core/pattern/IntegerPatternConverter.java      |   19 +-
 .../log4j/core/pattern/LevelPatternConverter.java  |  102 +-
 .../core/pattern/LineLocationPatternConverter.java |   43 +-
 .../pattern/LineSeparatorPatternConverter.java     |   32 +-
 .../core/pattern/LiteralPatternConverter.java      |   38 +-
 .../core/pattern/LogEventPatternConverter.java     |  151 +-
 .../log4j/core/pattern/LoggerPatternConverter.java |   32 +-
 .../log4j/core/pattern/MapPatternConverter.java    |   63 +-
 .../log4j/core/pattern/MarkerPatternConverter.java |   26 +-
 .../log4j/core/pattern/MdcPatternConverter.java    |   66 +-
 .../core/pattern/MessagePatternConverter.java      |   59 +-
 .../pattern/MethodLocationPatternConverter.java    |   29 +-
 .../log4j/core/pattern/NamePatternConverter.java   |   39 +-
 .../log4j/core/pattern/NdcPatternConverter.java    |   59 +-
 .../core/pattern/RegexReplacementConverter.java    |   40 +-
 .../core/pattern/RelativeTimePatternConverter.java |   38 +-
 .../pattern/RootThrowablePatternConverter.java     |   42 +-
 .../pattern/SequenceNumberPatternConverter.java    |   37 +-
 .../logging/log4j/core/pattern/StyleConverter.java |   80 +-
 .../log4j/core/pattern/ThreadPatternConverter.java |   42 +-
 .../core/pattern/ThrowablePatternConverter.java    |   80 +-
 .../log4j/core/pattern/UuidPatternConverter.java   |   28 +-
 .../core/pattern/DatePatternConverterTest.java     |   41 +-
 .../core/pattern/EncodingPatternConverterTest.java |    4 +-
 .../ExtendedThrowablePatternConverterTest.java     |   32 +-
 .../log4j/core/pattern/FormattingInfoTest.java     |  142 ++
 .../core/pattern/LevelPatternConverterTest.java    |   30 +-
 .../core/pattern/MapPatternConverterTest.java      |    8 +-
 .../core/pattern/MarkerPatternConverterTest.java   |    4 +-
 .../core/pattern/MessagePatternConverterTest.java  |   16 +-
 .../log4j/core/pattern/PatternParserTest.java      |    6 +-
 .../log4j/core/pattern/PatternParserTest2.java     |   16 +-
 .../pattern/RegexReplacementConverterTest.java     |    4 +-
 .../pattern/RootThrowablePatternConverterTest.java |    8 +-
 .../pattern/ThrowablePatternConverterTest.java     |   37 +-
 48 files changed, 2479 insertions(+), 1732 deletions(-)

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 45079fa188..75ce6a4c1f 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
@@ -33,10 +33,13 @@ import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
 import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
 import org.apache.logging.log4j.core.config.plugins.PluginElement;
 import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.pattern.BinaryBuffer;
 import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
 import org.apache.logging.log4j.core.pattern.PatternFormatter;
 import org.apache.logging.log4j.core.pattern.PatternParser;
 import org.apache.logging.log4j.core.pattern.RegexReplacement;
+import org.apache.logging.log4j.core.pattern.TextBuffer;
+import org.apache.logging.log4j.util.PropertiesUtil;
 
 /**
  * A flexible layout configurable with pattern string.
@@ -58,25 +61,20 @@ public final class PatternLayout extends AbstractStringLayout {
     private static final long serialVersionUID = 1L;
 
     /**
-     * Default pattern string for log output. Currently set to the
-     * string <b>"%m%n"</b> which just prints the application supplied
-     * message.
+     * Default pattern string for log output. Currently set to the string <b>"%m%n"</b> which just prints the
+     * application supplied message.
      */
     public static final String DEFAULT_CONVERSION_PATTERN = "%m%n";
 
     /**
-     * A conversion pattern equivalent to the TTCCCLayout.
-     * Current value is <b>%r [%t] %p %c %x - %m%n</b>.
+     * A conversion pattern equivalent to the TTCCCLayout. Current value is <b>%r [%t] %p %c %x - %m%n</b>.
      */
-    public static final String TTCC_CONVERSION_PATTERN =
-        "%r [%t] %p %c %x - %m%n";
+    public static final String TTCC_CONVERSION_PATTERN = "%r [%t] %p %c %x - %m%n";
 
     /**
-     * A simple pattern.
-     * Current value is <b>%d [%t] %p %c - %m%n</b>.
+     * A simple pattern. Current value is <b>%d [%t] %p %c - %m%n</b>.
      */
-    public static final String SIMPLE_CONVERSION_PATTERN =
-        "%d [%t] %p %c - %m%n";
+    public static final String SIMPLE_CONVERSION_PATTERN = "%d [%t] %p %c - %m%n";
 
     /** Key to identify pattern converters. */
     public static final String KEY = "Converter";
@@ -84,14 +82,14 @@ public final class PatternLayout extends AbstractStringLayout {
     /**
      * Initial converter for pattern.
      */
-    private final List<PatternFormatter> formatters;
+    // store in raw array instead of a collection for performance reasons
+    private final PatternFormatter[] formatters;
 
     /**
      * Conversion pattern.
      */
     private final String conversionPattern;
 
-
     /**
      * The current Configuration.
      */
@@ -103,22 +101,24 @@ public final class PatternLayout extends AbstractStringLayout {
 
     private final boolean noConsoleNoAnsi;
 
+    private final boolean supportsBinaryOutput;
+
     /**
-     * Constructs a EnhancedPatternLayout using the supplied conversion pattern.
+     * Constructs a PatternLayout using the supplied conversion pattern.
      *
      * @param config The Configuration.
      * @param replace The regular expression to match.
      * @param pattern conversion pattern.
      * @param charset The character set.
      * @param alwaysWriteExceptions Whether or not exceptions should always be handled in this pattern (if {@code true},
-     *                         exceptions will be written even if the pattern does not specify so).
-     * @param noConsoleNoAnsi
-     *            If {@code "true"} (default) and {@link System#console()} is null, do not output ANSI escape codes
+     *            exceptions will be written even if the pattern does not specify so).
+     * @param noConsoleNoAnsi If {@code "true"} (default) and {@link System#console()} is null, do not output ANSI
+     *            escape codes
      * @param header
      */
     private PatternLayout(final Configuration config, final RegexReplacement replace, final String pattern,
-                          final Charset charset, final boolean alwaysWriteExceptions, final boolean noConsoleNoAnsi,
-                          final String header, final String footer) {
+            final Charset charset, final boolean alwaysWriteExceptions, final boolean noConsoleNoAnsi,
+            final String header, final String footer) {
         super(charset, toBytes(header, charset), toBytes(footer, charset));
         this.replace = replace;
         this.conversionPattern = pattern;
@@ -126,7 +126,17 @@ public final class PatternLayout extends AbstractStringLayout {
         this.alwaysWriteExceptions = alwaysWriteExceptions;
         this.noConsoleNoAnsi = noConsoleNoAnsi;
         final PatternParser parser = createPatternParser(config);
-        this.formatters = parser.parse(pattern == null ? DEFAULT_CONVERSION_PATTERN : pattern, this.alwaysWriteExceptions, this.noConsoleNoAnsi);
+        final List<PatternFormatter> list = parser.parse(pattern == null ? DEFAULT_CONVERSION_PATTERN : pattern,
+                this.alwaysWriteExceptions, this.noConsoleNoAnsi);
+        this.formatters = (PatternFormatter[]) list.toArray(new PatternFormatter[list.size()]);
+        this.supportsBinaryOutput = replace == null && supportBinaryOutput(formatters);
+        for (PatternFormatter patternFormatter : list) {
+            patternFormatter.getConverter().setCharset(charset);
+        }
+    }
+
+    private static boolean supportBinaryOutput(final PatternFormatter[] formatters) {
+        return PropertiesUtil.getProperties().getBooleanProperty("log4j.use.BinaryBuffers", true);
     }
 
     private static byte[] toBytes(final String str, final Charset charset) {
@@ -136,7 +146,7 @@ public final class PatternLayout extends AbstractStringLayout {
         return null;
     }
 
-    private byte[] strSubstitutorReplace(final byte... b) {
+    private byte[] strSubstitutorReplace(final byte[] b) {
         if (b != null && config != null) {
             return getBytes(config.getStrSubstitutor().replace(new String(b, getCharset())));
         }
@@ -173,14 +183,20 @@ public final class PatternLayout extends AbstractStringLayout {
      * @return Map of content format keys supporting PatternLayout
      */
     @Override
-    public Map<String, String> getContentFormat()
-    {
+    public Map<String, String> getContentFormat() {
         final Map<String, String> result = new HashMap<String, String>();
         result.put("structured", "false");
         result.put("formatType", "conversion");
         result.put("format", conversionPattern);
         return result;
     }
+    
+    private ThreadLocal<TextBuffer> textBuffer = new ThreadLocal<TextBuffer>() {
+        @Override
+        protected TextBuffer initialValue() {
+            return new TextBuffer();
+        }
+    };
 
     /**
      * Formats a logging event to a writer.
@@ -191,9 +207,12 @@ public final class PatternLayout extends AbstractStringLayout {
      */
     @Override
     public String toSerializable(final LogEvent event) {
-        final StringBuilder buf = new StringBuilder();
-        for (final PatternFormatter formatter : formatters) {
-            formatter.format(event, buf);
+        final TextBuffer buf = textBuffer.get();
+        buf.setLength(0);
+        
+        // PERFORMANCE-SENSITIVE CODE: do not refactor to for-each loop
+        for (int i = 0, len = formatters.length; i < len; i++) {
+            formatters[i].format(event, buf);
         }
         String str = buf.toString();
         if (replace != null) {
@@ -201,9 +220,39 @@ public final class PatternLayout extends AbstractStringLayout {
         }
         return str;
     }
+    
+    private ThreadLocal<BinaryBuffer> binaryBuffer = new ThreadLocal<BinaryBuffer>() {
+        @Override
+        protected BinaryBuffer initialValue() {
+            return new BinaryBuffer(getCharset());
+        }
+    };
+
+    /**
+     * Formats the Log Event as a byte array.
+     *
+     * @param event The Log Event.
+     * @return The formatted event as a byte array.
+     */
+    @Override
+    public byte[] toByteArray(final LogEvent event) {
+        if (!supportsBinaryOutput) {
+            return getBytes(toSerializable(event));
+        }
+        final Charset charset = getCharset();
+        final BinaryBuffer buf = binaryBuffer.get();
+        buf.setLength(0);
+
+        // PERFORMANCE-SENSITIVE CODE: do not refactor to for-each loop
+        for (int i = 0, len = formatters.length; i < len; i++) {
+            formatters[i].format(event, buf, charset);
+        }
+        return buf.toByteArray();
+    }
 
     /**
      * Create a PatternParser.
+     * 
      * @param config The Configuration.
      * @return The PatternParser.
      */
@@ -228,22 +277,16 @@ public final class PatternLayout extends AbstractStringLayout {
     /**
      * Create a pattern layout.
      *
-     * @param pattern
-     *        The pattern. If not specified, defaults to DEFAULT_CONVERSION_PATTERN.
-     * @param config
-     *        The Configuration. Some Converters require access to the Interpolator.
-     * @param replace
-     *        A Regex replacement String.
-     * @param charset
-     *        The character set.
-     * @param alwaysWriteExceptions
-     *        If {@code "true"} (default) exceptions are always written even if the pattern contains no exception tokens.
-     * @param noConsoleNoAnsi
-     *        If {@code "true"} (default is false) and {@link System#console()} is null, do not output ANSI escape codes
-     * @param header
-     *        The footer to place at the top of the document, once.
-     * @param footer
-     *        The footer to place at the bottom of the document, once.
+     * @param pattern The pattern. If not specified, defaults to DEFAULT_CONVERSION_PATTERN.
+     * @param config The Configuration. Some Converters require access to the Interpolator.
+     * @param replace A Regex replacement String.
+     * @param charset The character set.
+     * @param alwaysWriteExceptions If {@code "true"} (default) exceptions are always written even if the pattern
+     *            contains no exception tokens.
+     * @param noConsoleNoAnsi If {@code "true"} (default is false) and {@link System#console()} is null, do not output
+     *            ANSI escape codes
+     * @param header The footer to place at the top of the document, once.
+     * @param footer The footer to place at the bottom of the document, once.
      * @return The PatternLayout.
      */
     @PluginFactory
@@ -251,21 +294,14 @@ public final class PatternLayout extends AbstractStringLayout {
             @PluginAttribute(value = "pattern", defaultString = DEFAULT_CONVERSION_PATTERN) final String pattern,
             @PluginConfiguration final Configuration config,
             @PluginElement("Replace") final RegexReplacement replace,
-            @PluginAttribute(value = "charset", defaultString = "UTF-8") final Charset charset,
+            /* Cannot do defaultString = Charset.defaultCharset().name() for charset... */
+            @PluginAttribute(value = "charset") final Charset charset,
             @PluginAttribute(value = "alwaysWriteExceptions", defaultBoolean = true) final boolean alwaysWriteExceptions,
             @PluginAttribute(value = "noConsoleNoAnsi", defaultBoolean = false) final boolean noConsoleNoAnsi,
-            @PluginAttribute("header") final String header,
-            @PluginAttribute("footer") final String footer) {
-        return newBuilder()
-            .withPattern(pattern)
-            .withConfiguration(config)
-            .withRegexReplacement(replace)
-            .withCharset(charset)
-            .withAlwaysWriteExceptions(alwaysWriteExceptions)
-            .withNoConsoleNoAnsi(noConsoleNoAnsi)
-            .withHeader(header)
-            .withFooter(footer)
-            .build();
+            @PluginAttribute("header") final String header, @PluginAttribute("footer") final String footer) {
+        return newBuilder().withPattern(pattern).withConfiguration(config).withRegexReplacement(replace)
+                .withCharset(charset).withAlwaysWriteExceptions(alwaysWriteExceptions)
+                .withNoConsoleNoAnsi(noConsoleNoAnsi).withHeader(header).withFooter(footer).build();
     }
 
     /**
@@ -281,6 +317,7 @@ public final class PatternLayout extends AbstractStringLayout {
 
     /**
      * Creates a builder for a custom PatternLayout.
+     * 
      * @return a PatternLayout builder.
      */
     @PluginBuilderFactory
@@ -331,7 +368,6 @@ public final class PatternLayout extends AbstractStringLayout {
             return this;
         }
 
-
         public Builder withConfiguration(final Configuration configuration) {
             this.configuration = configuration;
             return this;
@@ -343,7 +379,9 @@ public final class PatternLayout extends AbstractStringLayout {
         }
 
         public Builder withCharset(final Charset charset) {
-            this.charset = charset;
+            if (charset != null) {
+                this.charset = charset; // else use platform default (NOT utf-8!)
+            }
             return this;
         }
 
@@ -374,7 +412,7 @@ public final class PatternLayout extends AbstractStringLayout {
                 configuration = new DefaultConfiguration();
             }
             return new PatternLayout(configuration, regexReplacement, pattern, charset, alwaysWriteExceptions,
-                noConsoleNoAnsi, header, footer);
+                    noConsoleNoAnsi, header, footer);
         }
     }
 }
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 340c5b382a..8aa7ab19d8 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
@@ -1,718 +1,720 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.core.layout;
-
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.GregorianCalendar;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.SortedMap;
-import java.util.TreeMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.logging.log4j.Level;
-import org.apache.logging.log4j.LoggingException;
-import org.apache.logging.log4j.core.Layout;
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.appender.TlsSyslogFrame;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.config.Node;
-import org.apache.logging.log4j.core.config.plugins.Plugin;
-import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
-import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
-import org.apache.logging.log4j.core.config.plugins.PluginElement;
-import org.apache.logging.log4j.core.config.plugins.PluginFactory;
-import org.apache.logging.log4j.core.net.Facility;
-import org.apache.logging.log4j.core.net.Priority;
-import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
-import org.apache.logging.log4j.core.pattern.PatternConverter;
-import org.apache.logging.log4j.core.pattern.PatternFormatter;
-import org.apache.logging.log4j.core.pattern.PatternParser;
-import org.apache.logging.log4j.core.pattern.ThrowablePatternConverter;
-import org.apache.logging.log4j.core.util.Charsets;
-import org.apache.logging.log4j.core.util.NetUtils;
-import org.apache.logging.log4j.core.util.Patterns;
-import org.apache.logging.log4j.message.Message;
-import org.apache.logging.log4j.message.StructuredDataId;
-import org.apache.logging.log4j.message.StructuredDataMessage;
-import org.apache.logging.log4j.util.Strings;
-
-
-/**
- * Formats a log event in accordance with RFC 5424.
- *
- * @see <a href="https://tools.ietf.org/html/rfc5424">RFC 5424</a>
- */
-@Plugin(name = "Rfc5424Layout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true)
-public final class Rfc5424Layout extends AbstractStringLayout {
-
-    private static final long serialVersionUID = 1L;
-
-    private static final String LF = "\n";
-
-    /**
-     * Not a very good default - it is the Apache Software Foundation's enterprise number.
-     */
-    public static final int DEFAULT_ENTERPRISE_NUMBER = 18060;
-    /**
-     * The default event id.
-     */
-    public static final String DEFAULT_ID = "Audit";
-    /**
-     * Match newlines in a platform-independent manner.
-     */
-    public static final Pattern NEWLINE_PATTERN = Pattern.compile("\\r?\\n");
-    /**
-     * Match characters which require escaping
-     */
-    public static final Pattern PARAM_VALUE_ESCAPE_PATTERN = Pattern.compile("[\\\"\\]\\\\]");
-
-    public static final String DEFAULT_MDCID = "mdc";
-    private static final int TWO_DIGITS = 10;
-    private static final int THREE_DIGITS = 100;
-    private static final int MILLIS_PER_MINUTE = 60000;
-    private static final int MINUTES_PER_HOUR = 60;
-
-    private static final String COMPONENT_KEY = "RFC5424-Converter";
-
-    private final Facility facility;
-    private final String defaultId;
-    private final int enterpriseNumber;
-    private final boolean includeMdc;
-    private final String mdcId;
-    private final StructuredDataId mdcSdId;
-    private final String localHostName;
-    private final String appName;
-    private final String messageId;
-    private final String configName;
-    private final String mdcPrefix;
-    private final String eventPrefix;
-    private final List<String> mdcExcludes;
-    private final List<String> mdcIncludes;
-    private final List<String> mdcRequired;
-    private final ListChecker checker;
-    private final ListChecker noopChecker = new NoopChecker();
-    private final boolean includeNewLine;
-    private final String escapeNewLine;
-    private final boolean useTlsMessageFormat;
-
-    private long lastTimestamp = -1;
-    private String timestamppStr;
-
-    private final List<PatternFormatter> exceptionFormatters;
-    private final Map<String,  FieldFormatter> fieldFormatters;
-
-    private Rfc5424Layout(final Configuration config, final Facility facility, final String id, final int ein,
-                          final boolean includeMDC, final boolean includeNL, final String escapeNL, final String mdcId,
-                          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);
-        final PatternParser exceptionParser = createPatternParser(config, ThrowablePatternConverter.class);
-        exceptionFormatters = exceptionPattern == null ? null : exceptionParser.parse(exceptionPattern, false, false);
-        this.facility = facility;
-        this.defaultId = id == null ? DEFAULT_ID : id;
-        this.enterpriseNumber = ein;
-        this.includeMdc = includeMDC;
-        this.includeNewLine = includeNL;
-        this.escapeNewLine = escapeNL == null ? null : Matcher.quoteReplacement(escapeNL);
-        this.mdcId = mdcId;
-        this.mdcSdId = new StructuredDataId(mdcId, enterpriseNumber, null, null);
-        this.mdcPrefix = mdcPrefix;
-        this.eventPrefix = eventPrefix;
-        this.appName = appName;
-        this.messageId = messageId;
-        this.useTlsMessageFormat = useTLSMessageFormat;
-        this.localHostName = NetUtils.getLocalHostname();
-        ListChecker c = null;
-        if (excludes != null) {
-            final String[] array = excludes.split(Patterns.COMMA_SEPARATOR);
-            if (array.length > 0) {
-                c = new ExcludeChecker();
-                mdcExcludes = new ArrayList<String>(array.length);
-                for (final String str : array) {
-                    mdcExcludes.add(str.trim());
-                }
-            } else {
-                mdcExcludes = null;
-            }
-        } else {
-            mdcExcludes = null;
-        }
-        if (includes != null) {
-            final String[] array = includes.split(Patterns.COMMA_SEPARATOR);
-            if (array.length > 0) {
-                c = new IncludeChecker();
-                mdcIncludes = new ArrayList<String>(array.length);
-                for (final String str : array) {
-                    mdcIncludes.add(str.trim());
-                }
-            } else {
-                mdcIncludes = null;
-            }
-        } else {
-            mdcIncludes = null;
-        }
-        if (required != null) {
-            final String[] array = required.split(Patterns.COMMA_SEPARATOR);
-            if (array.length > 0) {
-                mdcRequired = new ArrayList<String>(array.length);
-                for (final String str : array) {
-                    mdcRequired.add(str.trim());
-                }
-            } else {
-                mdcRequired = null;
-            }
-
-        } else {
-            mdcRequired = null;
-        }
-        this.checker = c != null ? c : noopChecker;
-        final String name = config == null ? null : config.getName();
-        configName = name != null && name.length() > 0 ? name : null;
-        this.fieldFormatters = createFieldFormatters(loggerFields, config);
-    }
-
-    private Map<String, FieldFormatter> createFieldFormatters(final LoggerFields[] loggerFields,
-            final Configuration config) {
-        final Map<String, FieldFormatter> sdIdMap = new HashMap<String, FieldFormatter>();
-
-        if (loggerFields != null) {
-            for (final LoggerFields lField : loggerFields) {
-                final StructuredDataId key = lField.getSdId() == null ? mdcSdId : lField.getSdId();
-                final Map<String, List<PatternFormatter>> sdParams = new HashMap<String, List<PatternFormatter>>();
-                final Map<String, String> fields = lField.getMap();
-                if (!fields.isEmpty()) {
-                    final PatternParser fieldParser = createPatternParser(config, null);
-
-                    for (final Map.Entry<String, String> entry : fields.entrySet()) {
-                        final List<PatternFormatter> formatters = fieldParser.parse(entry.getValue(), false, false);
-                        sdParams.put(entry.getKey(), formatters);
-                    }
-                    final FieldFormatter fieldFormatter = new FieldFormatter(sdParams,
-                            lField.getDiscardIfAllFieldsAreEmpty());
-                    sdIdMap.put(key.toString(), fieldFormatter);
-                }
-            }
-        }
-        return sdIdMap.size() > 0 ? sdIdMap : null;
-    }
-
-    /**
-     * Create a PatternParser.
-     *
-     * @param config The Configuration.
-     * @param filterClass Filter the returned plugins after calling the plugin manager.
-     * @return The PatternParser.
-     */
-    private static PatternParser createPatternParser(final Configuration config,
-            final Class<? extends PatternConverter> filterClass) {
-        if (config == null) {
-            return new PatternParser(config, PatternLayout.KEY, LogEventPatternConverter.class, filterClass);
-        }
-        PatternParser parser = config.getComponent(COMPONENT_KEY);
-        if (parser == null) {
-            parser = new PatternParser(config, PatternLayout.KEY, ThrowablePatternConverter.class);
-            config.addComponent(COMPONENT_KEY, parser);
-            parser = (PatternParser) config.getComponent(COMPONENT_KEY);
-        }
-        return parser;
-    }
-
-    /**
-     * Gets this Rfc5424Layout's content format. Specified by:
-     * <ul>
-     * <li>Key: "structured" Value: "true"</li>
-     * <li>Key: "format" Value: "RFC5424"</li>
-     * </ul>
-     * 
-     * @return Map of content format keys supporting Rfc5424Layout
-     */
-    @Override
-    public Map<String, String> getContentFormat() {
-        final Map<String, String> result = new HashMap<String, String>();
-        result.put("structured", "true");
-        result.put("formatType", "RFC5424");
-        return result;
-    }
-
-    /**
-     * Formats a {@link org.apache.logging.log4j.core.LogEvent} in conformance with the RFC 5424 Syslog specification.
-     *
-     * @param event The LogEvent.
-     * @return The RFC 5424 String representation of the LogEvent.
-     */
-    @Override
-    public String toSerializable(final LogEvent event) {
-        final StringBuilder buf = new StringBuilder();
-        appendPriority(buf, event.getLevel());
-        appendTimestamp(buf, event.getTimeMillis());
-        appendSpace(buf);
-        appendHostName(buf);
-        appendSpace(buf);
-        appendAppName(buf);
-        appendSpace(buf);
-        appendProcessId(buf);
-        appendSpace(buf);
-        appendMessageId(buf, event.getMessage());
-        appendSpace(buf);
-        appendStructuredElements(buf, event);
-        appendMessage(buf, event);
-        if (useTlsMessageFormat) {
-            return new TlsSyslogFrame(buf.toString()).toString();
-        }
-        return buf.toString();
-    }
-
-    private void appendPriority(final StringBuilder buffer, final Level logLevel) {
-        buffer.append('<');
-        buffer.append(Priority.getPriority(facility, logLevel));
-        buffer.append(">1 ");
-    }
-
-    private void appendTimestamp(final StringBuilder buffer, final long milliseconds)  {
-        buffer.append(computeTimeStampString(milliseconds));
-    }
-
-    private void appendSpace(final StringBuilder buffer) {
-        buffer.append(' ');
-    }
-
-    private void appendHostName(final StringBuilder buffer) {
-        buffer.append(localHostName);
-    }
-
-    private void appendAppName(final StringBuilder buffer) {
-        if (appName != null) {
-            buffer.append(appName);
-        } else if (configName != null) {
-            buffer.append(configName);
-        } else {
-            buffer.append('-');
-        }
-    }
-
-    private void appendProcessId(final StringBuilder buffer) {
-        buffer.append(getProcId());
-    }
-
-    private void appendMessageId(final StringBuilder buffer, final Message message) {
-        final boolean isStructured = message instanceof StructuredDataMessage;
-        final String type = isStructured ? ((StructuredDataMessage) message).getType() : null;
-        if (type != null) {
-            buffer.append(type);
-        } else if (messageId != null) {
-            buffer.append(messageId);
-        } else {
-            buffer.append('-');
-        }
-    }
-
-    private void appendMessage(final StringBuilder buffer, final LogEvent event) {
-        final Message message = event.getMessage();
-        // This layout formats StructuredDataMessages instead of delegating to the Message itself.
-        final String text = (message instanceof StructuredDataMessage) ? message.getFormat() : message.getFormattedMessage();
-
-        if (text != null && text.length() > 0) {
-            buffer.append(' ').append(escapeNewlines(text, escapeNewLine));
-        }
-
-        if (exceptionFormatters != null && event.getThrown() != null) {
-            final StringBuilder exception = new StringBuilder(LF);
-            for (final PatternFormatter formatter : exceptionFormatters) {
-                formatter.format(event, exception);
-            }
-            buffer.append(escapeNewlines(exception.toString(), escapeNewLine));
-        }
-        if (includeNewLine) {
-            buffer.append(LF);
-        }
-    }
-
-    private void appendStructuredElements(final StringBuilder buffer, final LogEvent event) {
-        final Message message = event.getMessage();
-        final boolean isStructured = message instanceof StructuredDataMessage;
-
-        if (!isStructured && (fieldFormatters!= null && fieldFormatters.isEmpty()) && !includeMdc) {
-            buffer.append('-');
-            return;
-        }
-
-        final Map<String, StructuredDataElement> sdElements = new HashMap<String, StructuredDataElement>();
-        final Map<String, String> contextMap = event.getContextMap();
-
-        if (mdcRequired != null) {
-            checkRequired(contextMap);
-        }
-
-        if (fieldFormatters != null) {
-            for (final Map.Entry<String, FieldFormatter> sdElement: fieldFormatters.entrySet()) {
-                final String sdId = sdElement.getKey();
-                final StructuredDataElement elem = sdElement.getValue().format(event);
-                sdElements.put(sdId, elem);
-            }
-        }
-
-        if (includeMdc && contextMap.size() > 0) {
-            if (sdElements.containsKey(mdcSdId.toString())) {
-                final StructuredDataElement union = sdElements.get(mdcSdId.toString());
-                union.union(contextMap);
-                sdElements.put(mdcSdId.toString(), union);
-            } else {
-                final StructuredDataElement formattedContextMap = new StructuredDataElement(contextMap, false);
-                sdElements.put(mdcSdId.toString(), formattedContextMap);
-            }
-        }
-
-        if (isStructured) {
-            final StructuredDataMessage data = (StructuredDataMessage) message;
-            final Map<String, String> map = data.getData();
-            final StructuredDataId id = data.getId();
-            final String sdId = getId(id);
-
-            if (sdElements.containsKey(sdId)) {
-                final StructuredDataElement union = sdElements.get(id.toString());
-                union.union(map);
-                sdElements.put(sdId, union);
-            } else {
-                final StructuredDataElement formattedData = new StructuredDataElement(map, false);
-                sdElements.put(sdId, formattedData);
-            }
-        }
-
-        if (sdElements.isEmpty()) {
-            buffer.append('-');
-            return;
-        }
-
-        for (final Map.Entry<String, StructuredDataElement> entry: sdElements.entrySet()) {
-            formatStructuredElement(entry.getKey(), mdcPrefix, entry.getValue(), buffer, checker);
-        }
-    }
-
-    private String escapeNewlines(final String text, final String escapeNewLine) {
-        if (null == escapeNewLine) {
-            return text;
-        }
-        return NEWLINE_PATTERN.matcher(text).replaceAll(escapeNewLine);
-    }
-
-    protected String getProcId() {
-        return "-";
-    }
-
-    protected List<String> getMdcExcludes() {
-        return mdcExcludes;
-    }
-
-    protected List<String> getMdcIncludes() {
-        return mdcIncludes;
-    }
-
-    private String computeTimeStampString(final long now) {
-        long last;
-        synchronized (this) {
-            last = lastTimestamp;
-            if (now == lastTimestamp) {
-                return timestamppStr;
-            }
-        }
-
-        final StringBuilder buffer = new StringBuilder();
-        final Calendar cal = new GregorianCalendar();
-        cal.setTimeInMillis(now);
-        buffer.append(Integer.toString(cal.get(Calendar.YEAR)));
-        buffer.append('-');
-        pad(cal.get(Calendar.MONTH) + 1, TWO_DIGITS, buffer);
-        buffer.append('-');
-        pad(cal.get(Calendar.DAY_OF_MONTH), TWO_DIGITS, buffer);
-        buffer.append('T');
-        pad(cal.get(Calendar.HOUR_OF_DAY), TWO_DIGITS, buffer);
-        buffer.append(':');
-        pad(cal.get(Calendar.MINUTE), TWO_DIGITS, buffer);
-        buffer.append(':');
-        pad(cal.get(Calendar.SECOND), TWO_DIGITS, buffer);
-
-        final int millis = cal.get(Calendar.MILLISECOND);
-        if (millis != 0) {
-            buffer.append('.');
-            pad(millis, THREE_DIGITS, buffer);
-        }
-
-        int tzmin = (cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET)) / MILLIS_PER_MINUTE;
-        if (tzmin == 0) {
-            buffer.append('Z');
-        } else {
-            if (tzmin < 0) {
-                tzmin = -tzmin;
-                buffer.append('-');
-            } else {
-                buffer.append('+');
-            }
-            final int tzhour = tzmin / MINUTES_PER_HOUR;
-            tzmin -= tzhour * MINUTES_PER_HOUR;
-            pad(tzhour, TWO_DIGITS, buffer);
-            buffer.append(':');
-            pad(tzmin, TWO_DIGITS, buffer);
-        }
-        synchronized (this) {
-            if (last == lastTimestamp) {
-                lastTimestamp = now;
-                timestamppStr = buffer.toString();
-            }
-        }
-        return buffer.toString();
-    }
-
-    private void pad(final int val, int max, final StringBuilder buf) {
-        while (max > 1) {
-            if (val < max) {
-                buf.append('0');
-            }
-            max = max / TWO_DIGITS;
-        }
-        buf.append(Integer.toString(val));
-    }
-
-    private void formatStructuredElement(final String id, final String prefix, final StructuredDataElement data,
-                                         final StringBuilder sb, final ListChecker checker) {
-        if ((id == null && defaultId == null) || data.discard()) {
-            return;
-        }
-
-        sb.append('[');
-        sb.append(id);
-        if (!mdcSdId.toString().equals(id)) {
-            appendMap(prefix, data.getFields(), sb, noopChecker);
-        } else {
-            appendMap(prefix, data.getFields(), sb, checker);
-        }
-        sb.append(']');
-    }
-
-    private String getId(final StructuredDataId id) {
-        final StringBuilder sb = new StringBuilder();
-        if (id == null || id.getName() == null) {
-            sb.append(defaultId);
-        } else {
-            sb.append(id.getName());
-        }
-        int ein = id != null ? id.getEnterpriseNumber() : enterpriseNumber;
-        if (ein < 0) {
-            ein = enterpriseNumber;
-        }
-        if (ein >= 0) {
-            sb.append('@').append(ein);
-        }
-        return sb.toString();
-    }
-
-    private void checkRequired(final Map<String, String> map) {
-        for (final String key : mdcRequired) {
-            final String value = map.get(key);
-            if (value == null) {
-                throw new LoggingException("Required key " + key + " is missing from the " + mdcId);
-            }
-        }
-    }
-
-    private void appendMap(final String prefix, final Map<String, String> map, final StringBuilder sb,
-                           final ListChecker checker) {
-        final SortedMap<String, String> sorted = new TreeMap<String, String>(map);
-        for (final Map.Entry<String, String> entry : sorted.entrySet()) {
-            if (checker.check(entry.getKey()) && entry.getValue() != null) {
-                sb.append(' ');
-                if (prefix != null) {
-                    sb.append(prefix);
-                }
-                sb.append(escapeNewlines(escapeSDParams(entry.getKey()), escapeNewLine)).append("=\"")
-                    .append(escapeNewlines(escapeSDParams(entry.getValue()), escapeNewLine)).append('"');
-            }
-        }
-    }
-
-    private String escapeSDParams(final String value) {
-        return PARAM_VALUE_ESCAPE_PATTERN.matcher(value).replaceAll("\\\\$0");
-    }
-
-    /**
-     * Interface used to check keys in a Map.
-     */
-    private interface ListChecker {
-        boolean check(String key);
-    }
-
-    /**
-     * Includes only the listed keys.
-     */
-    private class IncludeChecker implements ListChecker {
-        @Override
-        public boolean check(final String key) {
-            return mdcIncludes.contains(key);
-        }
-    }
-
-    /**
-     * Excludes the listed keys.
-     */
-    private class ExcludeChecker implements ListChecker {
-        @Override
-        public boolean check(final String key) {
-            return !mdcExcludes.contains(key);
-        }
-    }
-
-    /**
-     * Does nothing.
-     */
-    private class NoopChecker implements ListChecker {
-        @Override
-        public boolean check(final String key) {
-            return true;
-        }
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append("facility=").append(facility.name());
-        sb.append(" appName=").append(appName);
-        sb.append(" defaultId=").append(defaultId);
-        sb.append(" enterpriseNumber=").append(enterpriseNumber);
-        sb.append(" newLine=").append(includeNewLine);
-        sb.append(" includeMDC=").append(includeMdc);
-        sb.append(" messageId=").append(messageId);
-        return sb.toString();
-    }
-
-    /**
-     * Create the RFC 5424 Layout.
-     *
-     * @param facility         The Facility is used to try to classify the message.
-     * @param id               The default structured data id to use when formatting according to RFC 5424.
-     * @param enterpriseNumber The IANA enterprise number.
-     * @param includeMDC       Indicates whether data from the ThreadContextMap will be included in the RFC 5424 Syslog
-     *                         record. Defaults to "true:.
-     * @param mdcId            The id to use for the MDC Structured Data Element.
-     * @param mdcPrefix        The prefix to add to MDC key names.
-     * @param eventPrefix      The prefix to add to event key names.
-     * @param newLine          If true, a newline will be appended to the end of the syslog record. The default is false.
-     * @param escapeNL         String that should be used to replace newlines within the message text.
-     * @param appName          The value to use as the APP-NAME in the RFC 5424 syslog record.
-     * @param msgId            The default value to be used in the MSGID field of RFC 5424 syslog records.
-     * @param excludes         A comma separated list of MDC keys that should be excluded from the LogEvent.
-     * @param includes         A comma separated list of MDC keys that should be included in the FlumeEvent.
-     * @param required         A comma separated list of MDC keys that must be present in the MDC.
-     * @param exceptionPattern The pattern for formatting exceptions.
-     * @param useTlsMessageFormat If true the message will be formatted according to RFC 5425.
-     * @param loggerFields     Container for the KeyValuePairs containing the patterns
-     * @param config           The Configuration. Some Converters require access to the Interpolator.
-     * @return An Rfc5424Layout.
-     */
-    @PluginFactory
-    public static Rfc5424Layout createLayout(
-            @PluginAttribute(value = "facility", defaultString = "LOCAL0") final Facility facility,
-            @PluginAttribute("id") final String id,
-            @PluginAttribute(value = "enterpriseNumber", defaultInt = DEFAULT_ENTERPRISE_NUMBER) final int enterpriseNumber,
-            @PluginAttribute(value = "includeMDC", defaultBoolean = true) final boolean includeMDC,
-            @PluginAttribute(value = "mdcId", defaultString = DEFAULT_MDCID) final String mdcId,
-            @PluginAttribute("mdcPrefix") final String mdcPrefix,
-            @PluginAttribute("eventPrefix") final String eventPrefix,
-            @PluginAttribute(value = "newLine", defaultBoolean = false) final boolean newLine,
-            @PluginAttribute("newLineEscape") final String escapeNL,
-            @PluginAttribute("appName") final String appName,
-            @PluginAttribute("messageId") final String msgId,
-            @PluginAttribute("mdcExcludes") final String excludes,
-            @PluginAttribute("mdcIncludes") String includes,
-            @PluginAttribute("mdcRequired") final String required,
-            @PluginAttribute("exceptionPattern") final String exceptionPattern,
-            @PluginAttribute(value = "useTlsMessageFormat", defaultBoolean = false) final boolean useTlsMessageFormat, // RFC 5425
-            @PluginElement("LoggerFields") final LoggerFields[] loggerFields,
-            @PluginConfiguration final Configuration config) {
-        final Charset charset = Charsets.UTF_8;
-        if (includes != null && excludes != null) {
-            LOGGER.error("mdcIncludes and mdcExcludes are mutually exclusive. Includes wil be ignored");
-            includes = null;
-        }
-
-        return new Rfc5424Layout(config, facility, id, enterpriseNumber, includeMDC, newLine, escapeNL, mdcId, mdcPrefix,
-                eventPrefix, appName, msgId, excludes, includes, required, charset, exceptionPattern,
-                useTlsMessageFormat, loggerFields);
-    }
-
-    private class FieldFormatter {
-
-        private final Map<String, List<PatternFormatter>> delegateMap;
-        private final boolean discardIfEmpty;
-
-        public FieldFormatter(final Map<String, List<PatternFormatter>> fieldMap, final boolean discardIfEmpty) {
-            this.discardIfEmpty = discardIfEmpty;
-            this.delegateMap = fieldMap;
-        }
-
-        public StructuredDataElement format(final LogEvent event) {
-            final Map<String, String> map = new HashMap<String, String>();
-
-            for (final Map.Entry<String, List<PatternFormatter>> entry : delegateMap.entrySet()) {
-                final StringBuilder buffer = new StringBuilder();
-                for (final PatternFormatter formatter : entry.getValue()) {
-                    formatter.format(event, buffer);
-                }
-                map.put(entry.getKey(), buffer.toString());
-            }
-            return new StructuredDataElement(map, discardIfEmpty);
-        }
-    }
-
-    private class StructuredDataElement {
-
-        private final Map<String, String> fields;
-        private final boolean discardIfEmpty;
-
-        public StructuredDataElement(final Map<String, String> fields, final boolean discardIfEmpty) {
-            this.discardIfEmpty = discardIfEmpty;
-            this.fields = fields;
-        }
-
-        boolean discard() {
-            if (discardIfEmpty == false) {
-                return false;
-            }
-            boolean foundNotEmptyValue = false;
-            for (final Map.Entry<String, String> entry : fields.entrySet()) {
-                if (Strings.isNotEmpty(entry.getValue())) {
-                    foundNotEmptyValue = true;
-                    break;
-                }
-            }
-            return !foundNotEmptyValue;
-        }
-
-        void union(final Map<String, String> fields) {
-            this.fields.putAll(fields);
-        }
-
-        Map<String, String> getFields() {
-            return this.fields;
-        }
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.layout;
+
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LoggingException;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.TlsSyslogFrame;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.Node;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.net.Facility;
+import org.apache.logging.log4j.core.net.Priority;
+import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
+import org.apache.logging.log4j.core.pattern.PatternConverter;
+import org.apache.logging.log4j.core.pattern.PatternFormatter;
+import org.apache.logging.log4j.core.pattern.PatternParser;
+import org.apache.logging.log4j.core.pattern.TextBuffer;
+import org.apache.logging.log4j.core.pattern.ThrowablePatternConverter;
+import org.apache.logging.log4j.core.util.Charsets;
+import org.apache.logging.log4j.core.util.NetUtils;
+import org.apache.logging.log4j.core.util.Patterns;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.StructuredDataId;
+import org.apache.logging.log4j.message.StructuredDataMessage;
+import org.apache.logging.log4j.util.Strings;
+
+
+/**
+ * Formats a log event in accordance with RFC 5424.
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc5424">RFC 5424</a>
+ */
+@Plugin(name = "Rfc5424Layout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true)
+public final class Rfc5424Layout extends AbstractStringLayout {
+
+    private static final long serialVersionUID = 1L;
+
+    private static final String LF = "\n";
+
+    /**
+     * Not a very good default - it is the Apache Software Foundation's enterprise number.
+     */
+    public static final int DEFAULT_ENTERPRISE_NUMBER = 18060;
+    /**
+     * The default event id.
+     */
+    public static final String DEFAULT_ID = "Audit";
+    /**
+     * Match newlines in a platform-independent manner.
+     */
+    public static final Pattern NEWLINE_PATTERN = Pattern.compile("\\r?\\n");
+    /**
+     * Match characters which require escaping
+     */
+    public static final Pattern PARAM_VALUE_ESCAPE_PATTERN = Pattern.compile("[\\\"\\]\\\\]");
+
+    public static final String DEFAULT_MDCID = "mdc";
+    private static final int TWO_DIGITS = 10;
+    private static final int THREE_DIGITS = 100;
+    private static final int MILLIS_PER_MINUTE = 60000;
+    private static final int MINUTES_PER_HOUR = 60;
+
+    private static final String COMPONENT_KEY = "RFC5424-Converter";
+
+    private final Facility facility;
+    private final String defaultId;
+    private final int enterpriseNumber;
+    private final boolean includeMdc;
+    private final String mdcId;
+    private final StructuredDataId mdcSdId;
+    private final String localHostName;
+    private final String appName;
+    private final String messageId;
+    private final String configName;
+    private final String mdcPrefix;
+    private final String eventPrefix;
+    private final List<String> mdcExcludes;
+    private final List<String> mdcIncludes;
+    private final List<String> mdcRequired;
+    private final ListChecker checker;
+    private final ListChecker noopChecker = new NoopChecker();
+    private final boolean includeNewLine;
+    private final String escapeNewLine;
+    private final boolean useTlsMessageFormat;
+
+    private long lastTimestamp = -1;
+    private String timestamppStr;
+
+    private final List<PatternFormatter> exceptionFormatters;
+    private final Map<String,  FieldFormatter> fieldFormatters;
+
+    private Rfc5424Layout(final Configuration config, final Facility facility, final String id, final int ein,
+                          final boolean includeMDC, final boolean includeNL, final String escapeNL, final String mdcId,
+                          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);
+        final PatternParser exceptionParser = createPatternParser(config, ThrowablePatternConverter.class);
+        exceptionFormatters = exceptionPattern == null ? null : exceptionParser.parse(exceptionPattern, false, false);
+        this.facility = facility;
+        this.defaultId = id == null ? DEFAULT_ID : id;
+        this.enterpriseNumber = ein;
+        this.includeMdc = includeMDC;
+        this.includeNewLine = includeNL;
+        this.escapeNewLine = escapeNL == null ? null : Matcher.quoteReplacement(escapeNL);
+        this.mdcId = mdcId;
+        this.mdcSdId = new StructuredDataId(mdcId, enterpriseNumber, null, null);
+        this.mdcPrefix = mdcPrefix;
+        this.eventPrefix = eventPrefix;
+        this.appName = appName;
+        this.messageId = messageId;
+        this.useTlsMessageFormat = useTLSMessageFormat;
+        this.localHostName = NetUtils.getLocalHostname();
+        ListChecker c = null;
+        if (excludes != null) {
+            final String[] array = excludes.split(Patterns.COMMA_SEPARATOR);
+            if (array.length > 0) {
+                c = new ExcludeChecker();
+                mdcExcludes = new ArrayList<String>(array.length);
+                for (final String str : array) {
+                    mdcExcludes.add(str.trim());
+                }
+            } else {
+                mdcExcludes = null;
+            }
+        } else {
+            mdcExcludes = null;
+        }
+        if (includes != null) {
+            final String[] array = includes.split(Patterns.COMMA_SEPARATOR);
+            if (array.length > 0) {
+                c = new IncludeChecker();
+                mdcIncludes = new ArrayList<String>(array.length);
+                for (final String str : array) {
+                    mdcIncludes.add(str.trim());
+                }
+            } else {
+                mdcIncludes = null;
+            }
+        } else {
+            mdcIncludes = null;
+        }
+        if (required != null) {
+            final String[] array = required.split(Patterns.COMMA_SEPARATOR);
+            if (array.length > 0) {
+                mdcRequired = new ArrayList<String>(array.length);
+                for (final String str : array) {
+                    mdcRequired.add(str.trim());
+                }
+            } else {
+                mdcRequired = null;
+            }
+
+        } else {
+            mdcRequired = null;
+        }
+        this.checker = c != null ? c : noopChecker;
+        final String name = config == null ? null : config.getName();
+        configName = name != null && name.length() > 0 ? name : null;
+        this.fieldFormatters = createFieldFormatters(loggerFields, config);
+    }
+
+    private Map<String, FieldFormatter> createFieldFormatters(final LoggerFields[] loggerFields,
+            final Configuration config) {
+        final Map<String, FieldFormatter> sdIdMap = new HashMap<String, FieldFormatter>();
+
+        if (loggerFields != null) {
+            for (final LoggerFields lField : loggerFields) {
+                final StructuredDataId key = lField.getSdId() == null ? mdcSdId : lField.getSdId();
+                final Map<String, List<PatternFormatter>> sdParams = new HashMap<String, List<PatternFormatter>>();
+                final Map<String, String> fields = lField.getMap();
+                if (!fields.isEmpty()) {
+                    final PatternParser fieldParser = createPatternParser(config, null);
+
+                    for (final Map.Entry<String, String> entry : fields.entrySet()) {
+                        final List<PatternFormatter> formatters = fieldParser.parse(entry.getValue(), false, false);
+                        sdParams.put(entry.getKey(), formatters);
+                    }
+                    final FieldFormatter fieldFormatter = new FieldFormatter(sdParams,
+                            lField.getDiscardIfAllFieldsAreEmpty());
+                    sdIdMap.put(key.toString(), fieldFormatter);
+                }
+            }
+        }
+        return sdIdMap.size() > 0 ? sdIdMap : null;
+    }
+
+    /**
+     * Create a PatternParser.
+     *
+     * @param config The Configuration.
+     * @param filterClass Filter the returned plugins after calling the plugin manager.
+     * @return The PatternParser.
+     */
+    private static PatternParser createPatternParser(final Configuration config,
+            final Class<? extends PatternConverter> filterClass) {
+        if (config == null) {
+            return new PatternParser(config, PatternLayout.KEY, LogEventPatternConverter.class, filterClass);
+        }
+        PatternParser parser = config.getComponent(COMPONENT_KEY);
+        if (parser == null) {
+            parser = new PatternParser(config, PatternLayout.KEY, ThrowablePatternConverter.class);
+            config.addComponent(COMPONENT_KEY, parser);
+            parser = (PatternParser) config.getComponent(COMPONENT_KEY);
+        }
+        return parser;
+    }
+
+    /**
+     * Gets this Rfc5424Layout's content format. Specified by:
+     * <ul>
+     * <li>Key: "structured" Value: "true"</li>
+     * <li>Key: "format" Value: "RFC5424"</li>
+     * </ul>
+     * 
+     * @return Map of content format keys supporting Rfc5424Layout
+     */
+    @Override
+    public Map<String, String> getContentFormat() {
+        final Map<String, String> result = new HashMap<String, String>();
+        result.put("structured", "true");
+        result.put("formatType", "RFC5424");
+        return result;
+    }
+
+    /**
+     * Formats a {@link org.apache.logging.log4j.core.LogEvent} in conformance with the RFC 5424 Syslog specification.
+     *
+     * @param event The LogEvent.
+     * @return The RFC 5424 String representation of the LogEvent.
+     */
+    @Override
+    public String toSerializable(final LogEvent event) {
+        final StringBuilder buf = new StringBuilder();
+        appendPriority(buf, event.getLevel());
+        appendTimestamp(buf, event.getTimeMillis());
+        appendSpace(buf);
+        appendHostName(buf);
+        appendSpace(buf);
+        appendAppName(buf);
+        appendSpace(buf);
+        appendProcessId(buf);
+        appendSpace(buf);
+        appendMessageId(buf, event.getMessage());
+        appendSpace(buf);
+        appendStructuredElements(buf, event);
+        appendMessage(buf, event);
+        if (useTlsMessageFormat) {
+            return new TlsSyslogFrame(buf.toString()).toString();
+        }
+        return buf.toString();
+    }
+
+    private void appendPriority(final StringBuilder buffer, final Level logLevel) {
+        buffer.append('<');
+        buffer.append(Priority.getPriority(facility, logLevel));
+        buffer.append(">1 ");
+    }
+
+    private void appendTimestamp(final StringBuilder buffer, final long milliseconds)  {
+        buffer.append(computeTimeStampString(milliseconds));
+    }
+
+    private void appendSpace(final StringBuilder buffer) {
+        buffer.append(' ');
+    }
+
+    private void appendHostName(final StringBuilder buffer) {
+        buffer.append(localHostName);
+    }
+
+    private void appendAppName(final StringBuilder buffer) {
+        if (appName != null) {
+            buffer.append(appName);
+        } else if (configName != null) {
+            buffer.append(configName);
+        } else {
+            buffer.append('-');
+        }
+    }
+
+    private void appendProcessId(final StringBuilder buffer) {
+        buffer.append(getProcId());
+    }
+
+    private void appendMessageId(final StringBuilder buffer, final Message message) {
+        final boolean isStructured = message instanceof StructuredDataMessage;
+        final String type = isStructured ? ((StructuredDataMessage) message).getType() : null;
+        if (type != null) {
+            buffer.append(type);
+        } else if (messageId != null) {
+            buffer.append(messageId);
+        } else {
+            buffer.append('-');
+        }
+    }
+
+    private void appendMessage(final StringBuilder buffer, final LogEvent event) {
+        final Message message = event.getMessage();
+        // This layout formats StructuredDataMessages instead of delegating to the Message itself.
+        final String text = (message instanceof StructuredDataMessage) ? message.getFormat() : message.getFormattedMessage();
+
+        if (text != null && text.length() > 0) {
+            buffer.append(' ').append(escapeNewlines(text, escapeNewLine));
+        }
+
+        if (exceptionFormatters != null && event.getThrown() != null) {
+            final TextBuffer exception = new TextBuffer();
+            exception.append(LF);
+            for (final PatternFormatter formatter : exceptionFormatters) {
+                formatter.format(event, exception);
+            }
+            buffer.append(escapeNewlines(exception.toString(), escapeNewLine));
+        }
+        if (includeNewLine) {
+            buffer.append(LF);
+        }
+    }
+
+    private void appendStructuredElements(final StringBuilder buffer, final LogEvent event) {
+        final Message message = event.getMessage();
+        final boolean isStructured = message instanceof StructuredDataMessage;
+
+        if (!isStructured && (fieldFormatters!= null && fieldFormatters.isEmpty()) && !includeMdc) {
+            buffer.append('-');
+            return;
+        }
+
+        final Map<String, StructuredDataElement> sdElements = new HashMap<String, StructuredDataElement>();
+        final Map<String, String> contextMap = event.getContextMap();
+
+        if (mdcRequired != null) {
+            checkRequired(contextMap);
+        }
+
+        if (fieldFormatters != null) {
+            for (final Map.Entry<String, FieldFormatter> sdElement: fieldFormatters.entrySet()) {
+                final String sdId = sdElement.getKey();
+                final StructuredDataElement elem = sdElement.getValue().format(event);
+                sdElements.put(sdId, elem);
+            }
+        }
+
+        if (includeMdc && contextMap.size() > 0) {
+            if (sdElements.containsKey(mdcSdId.toString())) {
+                final StructuredDataElement union = sdElements.get(mdcSdId.toString());
+                union.union(contextMap);
+                sdElements.put(mdcSdId.toString(), union);
+            } else {
+                final StructuredDataElement formattedContextMap = new StructuredDataElement(contextMap, false);
+                sdElements.put(mdcSdId.toString(), formattedContextMap);
+            }
+        }
+
+        if (isStructured) {
+            final StructuredDataMessage data = (StructuredDataMessage) message;
+            final Map<String, String> map = data.getData();
+            final StructuredDataId id = data.getId();
+            final String sdId = getId(id);
+
+            if (sdElements.containsKey(sdId)) {
+                final StructuredDataElement union = sdElements.get(id.toString());
+                union.union(map);
+                sdElements.put(sdId, union);
+            } else {
+                final StructuredDataElement formattedData = new StructuredDataElement(map, false);
+                sdElements.put(sdId, formattedData);
+            }
+        }
+
+        if (sdElements.isEmpty()) {
+            buffer.append('-');
+            return;
+        }
+
+        for (final Map.Entry<String, StructuredDataElement> entry: sdElements.entrySet()) {
+            formatStructuredElement(entry.getKey(), mdcPrefix, entry.getValue(), buffer, checker);
+        }
+    }
+
+    private String escapeNewlines(final String text, final String escapeNewLine) {
+        if (null == escapeNewLine) {
+            return text;
+        }
+        return NEWLINE_PATTERN.matcher(text).replaceAll(escapeNewLine);
+    }
+
+    protected String getProcId() {
+        return "-";
+    }
+
+    protected List<String> getMdcExcludes() {
+        return mdcExcludes;
+    }
+
+    protected List<String> getMdcIncludes() {
+        return mdcIncludes;
+    }
+
+    private String computeTimeStampString(final long now) {
+        long last;
+        synchronized (this) {
+            last = lastTimestamp;
+            if (now == lastTimestamp) {
+                return timestamppStr;
+            }
+        }
+
+        final StringBuilder buffer = new StringBuilder();
+        final Calendar cal = new GregorianCalendar();
+        cal.setTimeInMillis(now);
+        buffer.append(Integer.toString(cal.get(Calendar.YEAR)));
+        buffer.append('-');
+        pad(cal.get(Calendar.MONTH) + 1, TWO_DIGITS, buffer);
+        buffer.append('-');
+        pad(cal.get(Calendar.DAY_OF_MONTH), TWO_DIGITS, buffer);
+        buffer.append('T');
+        pad(cal.get(Calendar.HOUR_OF_DAY), TWO_DIGITS, buffer);
+        buffer.append(':');
+        pad(cal.get(Calendar.MINUTE), TWO_DIGITS, buffer);
+        buffer.append(':');
+        pad(cal.get(Calendar.SECOND), TWO_DIGITS, buffer);
+
+        final int millis = cal.get(Calendar.MILLISECOND);
+        if (millis != 0) {
+            buffer.append('.');
+            pad(millis, THREE_DIGITS, buffer);
+        }
+
+        int tzmin = (cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET)) / MILLIS_PER_MINUTE;
+        if (tzmin == 0) {
+            buffer.append('Z');
+        } else {
+            if (tzmin < 0) {
+                tzmin = -tzmin;
+                buffer.append('-');
+            } else {
+                buffer.append('+');
+            }
+            final int tzhour = tzmin / MINUTES_PER_HOUR;
+            tzmin -= tzhour * MINUTES_PER_HOUR;
+            pad(tzhour, TWO_DIGITS, buffer);
+            buffer.append(':');
+            pad(tzmin, TWO_DIGITS, buffer);
+        }
+        synchronized (this) {
+            if (last == lastTimestamp) {
+                lastTimestamp = now;
+                timestamppStr = buffer.toString();
+            }
+        }
+        return buffer.toString();
+    }
+
+    private void pad(final int val, int max, final StringBuilder buf) {
+        while (max > 1) {
+            if (val < max) {
+                buf.append('0');
+            }
+            max = max / TWO_DIGITS;
+        }
+        buf.append(Integer.toString(val));
+    }
+
+    private void formatStructuredElement(final String id, final String prefix, final StructuredDataElement data,
+                                         final StringBuilder sb, final ListChecker checker) {
+        if ((id == null && defaultId == null) || data.discard()) {
+            return;
+        }
+
+        sb.append('[');
+        sb.append(id);
+        if (!mdcSdId.toString().equals(id)) {
+            appendMap(prefix, data.getFields(), sb, noopChecker);
+        } else {
+            appendMap(prefix, data.getFields(), sb, checker);
+        }
+        sb.append(']');
+    }
+
+    private String getId(final StructuredDataId id) {
+        final StringBuilder sb = new StringBuilder();
+        if (id == null || id.getName() == null) {
+            sb.append(defaultId);
+        } else {
+            sb.append(id.getName());
+        }
+        int ein = id != null ? id.getEnterpriseNumber() : enterpriseNumber;
+        if (ein < 0) {
+            ein = enterpriseNumber;
+        }
+        if (ein >= 0) {
+            sb.append('@').append(ein);
+        }
+        return sb.toString();
+    }
+
+    private void checkRequired(final Map<String, String> map) {
+        for (final String key : mdcRequired) {
+            final String value = map.get(key);
+            if (value == null) {
+                throw new LoggingException("Required key " + key + " is missing from the " + mdcId);
+            }
+        }
+    }
+
+    private void appendMap(final String prefix, final Map<String, String> map, final StringBuilder sb,
+                           final ListChecker checker) {
+        final SortedMap<String, String> sorted = new TreeMap<String, String>(map);
+        for (final Map.Entry<String, String> entry : sorted.entrySet()) {
+            if (checker.check(entry.getKey()) && entry.getValue() != null) {
+                sb.append(' ');
+                if (prefix != null) {
+                    sb.append(prefix);
+                }
+                sb.append(escapeNewlines(escapeSDParams(entry.getKey()), escapeNewLine)).append("=\"")
+                    .append(escapeNewlines(escapeSDParams(entry.getValue()), escapeNewLine)).append('"');
+            }
+        }
+    }
+
+    private String escapeSDParams(final String value) {
+        return PARAM_VALUE_ESCAPE_PATTERN.matcher(value).replaceAll("\\\\$0");
+    }
+
+    /**
+     * Interface used to check keys in a Map.
+     */
+    private interface ListChecker {
+        boolean check(String key);
+    }
+
+    /**
+     * Includes only the listed keys.
+     */
+    private class IncludeChecker implements ListChecker {
+        @Override
+        public boolean check(final String key) {
+            return mdcIncludes.contains(key);
+        }
+    }
+
+    /**
+     * Excludes the listed keys.
+     */
+    private class ExcludeChecker implements ListChecker {
+        @Override
+        public boolean check(final String key) {
+            return !mdcExcludes.contains(key);
+        }
+    }
+
+    /**
+     * Does nothing.
+     */
+    private class NoopChecker implements ListChecker {
+        @Override
+        public boolean check(final String key) {
+            return true;
+        }
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("facility=").append(facility.name());
+        sb.append(" appName=").append(appName);
+        sb.append(" defaultId=").append(defaultId);
+        sb.append(" enterpriseNumber=").append(enterpriseNumber);
+        sb.append(" newLine=").append(includeNewLine);
+        sb.append(" includeMDC=").append(includeMdc);
+        sb.append(" messageId=").append(messageId);
+        return sb.toString();
+    }
+
+    /**
+     * Create the RFC 5424 Layout.
+     *
+     * @param facility         The Facility is used to try to classify the message.
+     * @param id               The default structured data id to use when formatting according to RFC 5424.
+     * @param enterpriseNumber The IANA enterprise number.
+     * @param includeMDC       Indicates whether data from the ThreadContextMap will be included in the RFC 5424 Syslog
+     *                         record. Defaults to "true:.
+     * @param mdcId            The id to use for the MDC Structured Data Element.
+     * @param mdcPrefix        The prefix to add to MDC key names.
+     * @param eventPrefix      The prefix to add to event key names.
+     * @param newLine          If true, a newline will be appended to the end of the syslog record. The default is false.
+     * @param escapeNL         String that should be used to replace newlines within the message text.
+     * @param appName          The value to use as the APP-NAME in the RFC 5424 syslog record.
+     * @param msgId            The default value to be used in the MSGID field of RFC 5424 syslog records.
+     * @param excludes         A comma separated list of MDC keys that should be excluded from the LogEvent.
+     * @param includes         A comma separated list of MDC keys that should be included in the FlumeEvent.
+     * @param required         A comma separated list of MDC keys that must be present in the MDC.
+     * @param exceptionPattern The pattern for formatting exceptions.
+     * @param useTlsMessageFormat If true the message will be formatted according to RFC 5425.
+     * @param loggerFields     Container for the KeyValuePairs containing the patterns
+     * @param config           The Configuration. Some Converters require access to the Interpolator.
+     * @return An Rfc5424Layout.
+     */
+    @PluginFactory
+    public static Rfc5424Layout createLayout(
+            @PluginAttribute(value = "facility", defaultString = "LOCAL0") final Facility facility,
+            @PluginAttribute("id") final String id,
+            @PluginAttribute(value = "enterpriseNumber", defaultInt = DEFAULT_ENTERPRISE_NUMBER) final int enterpriseNumber,
+            @PluginAttribute(value = "includeMDC", defaultBoolean = true) final boolean includeMDC,
+            @PluginAttribute(value = "mdcId", defaultString = DEFAULT_MDCID) final String mdcId,
+            @PluginAttribute("mdcPrefix") final String mdcPrefix,
+            @PluginAttribute("eventPrefix") final String eventPrefix,
+            @PluginAttribute(value = "newLine", defaultBoolean = false) final boolean newLine,
+            @PluginAttribute("newLineEscape") final String escapeNL,
+            @PluginAttribute("appName") final String appName,
+            @PluginAttribute("messageId") final String msgId,
+            @PluginAttribute("mdcExcludes") final String excludes,
+            @PluginAttribute("mdcIncludes") String includes,
+            @PluginAttribute("mdcRequired") final String required,
+            @PluginAttribute("exceptionPattern") final String exceptionPattern,
+            @PluginAttribute(value = "useTlsMessageFormat", defaultBoolean = false) final boolean useTlsMessageFormat, // RFC 5425
+            @PluginElement("LoggerFields") final LoggerFields[] loggerFields,
+            @PluginConfiguration final Configuration config) {
+        final Charset charset = Charsets.UTF_8;
+        if (includes != null && excludes != null) {
+            LOGGER.error("mdcIncludes and mdcExcludes are mutually exclusive. Includes wil be ignored");
+            includes = null;
+        }
+
+        return new Rfc5424Layout(config, facility, id, enterpriseNumber, includeMDC, newLine, escapeNL, mdcId, mdcPrefix,
+                eventPrefix, appName, msgId, excludes, includes, required, charset, exceptionPattern,
+                useTlsMessageFormat, loggerFields);
+    }
+
+    private class FieldFormatter {
+
+        private final Map<String, List<PatternFormatter>> delegateMap;
+        private final boolean discardIfEmpty;
+
+        public FieldFormatter(final Map<String, List<PatternFormatter>> fieldMap, final boolean discardIfEmpty) {
+            this.discardIfEmpty = discardIfEmpty;
+            this.delegateMap = fieldMap;
+        }
+
+        public StructuredDataElement format(final LogEvent event) {
+            final Map<String, String> map = new HashMap<String, String>();
+
+            for (final Map.Entry<String, List<PatternFormatter>> entry : delegateMap.entrySet()) {
+                final TextBuffer buffer = new TextBuffer();
+                for (final PatternFormatter formatter : entry.getValue()) {
+                    formatter.format(event, buffer);
+                }
+                map.put(entry.getKey(), buffer.toString());
+            }
+            return new StructuredDataElement(map, discardIfEmpty);
+        }
+    }
+
+    private class StructuredDataElement {
+
+        private final Map<String, String> fields;
+        private final boolean discardIfEmpty;
+
+        public StructuredDataElement(final Map<String, String> fields, final boolean discardIfEmpty) {
+            this.discardIfEmpty = discardIfEmpty;
+            this.fields = fields;
+        }
+
+        boolean discard() {
+            if (discardIfEmpty == false) {
+                return false;
+            }
+            boolean foundNotEmptyValue = false;
+            for (final Map.Entry<String, String> entry : fields.entrySet()) {
+                if (Strings.isNotEmpty(entry.getValue())) {
+                    foundNotEmptyValue = true;
+                    break;
+                }
+            }
+            return !foundNotEmptyValue;
+        }
+
+        void union(final Map<String, String> fields) {
+            this.fields.putAll(fields);
+        }
+
+        Map<String, String> getFields() {
+            return this.fields;
+        }
+    }
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/AbstractStyleNameConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/AbstractStyleNameConverter.java
index c15c9fc4e6..5a86560c88 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/AbstractStyleNameConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/AbstractStyleNameConverter.java
@@ -18,6 +18,7 @@ package org.apache.logging.log4j.core.pattern;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
+import java.nio.charset.Charset;
 import java.util.Arrays;
 import java.util.List;
 
@@ -29,7 +30,7 @@ import org.apache.logging.log4j.core.layout.PatternLayout;
 /**
  * Style pattern converter. Adds ANSI color styling to the result of the enclosed pattern.
  */
-public abstract class AbstractStyleNameConverter extends LogEventPatternConverter /*TODO: implements AnsiConverter*/ {
+public abstract class AbstractStyleNameConverter extends LogEventPatternConverter /* TODO: implements AnsiConverter */{
 
     private final List<PatternFormatter> formatters;
 
@@ -42,8 +43,8 @@ public abstract class AbstractStyleNameConverter extends LogEventPatternConverte
      * @param styling The styling that should encapsulate the pattern.
      */
     protected AbstractStyleNameConverter(final String name, final List<PatternFormatter> formatters,
-                                         final String styling) {
-        super(name, "style");
+            final String styling, final FormattingInfo formattingInfo) {
+        super(name, "style", formattingInfo);
         this.formatters = formatters;
         this.style = styling;
     }
@@ -64,8 +65,8 @@ public abstract class AbstractStyleNameConverter extends LogEventPatternConverte
          * @param formatters The PatternFormatters to generate the text to manipulate.
          * @param styling The styling that should encapsulate the pattern.
          */
-        public Black(final List<PatternFormatter> formatters, final String styling) {
-            super(NAME, formatters, styling);
+        public Black(final List<PatternFormatter> formatters, final String styling, final FormattingInfo formattingInfo) {
+            super(NAME, formatters, styling, formattingInfo);
         }
 
         /**
@@ -76,8 +77,9 @@ public abstract class AbstractStyleNameConverter extends LogEventPatternConverte
          *            throwable will be formatted.
          * @return new instance of class or null
          */
-        public static Black newInstance(final Configuration config, final String[] options) {
-            return newInstance(Black.class, NAME, config, options);
+        public static Black newInstance(final Configuration config, final String[] options,
+                final FormattingInfo formattingInfo) {
+            return newInstance(Black.class, NAME, config, options, formattingInfo);
         }
     }
 
@@ -97,8 +99,8 @@ public abstract class AbstractStyleNameConverter extends LogEventPatternConverte
          * @param formatters The PatternFormatters to generate the text to manipulate.
          * @param styling The styling that should encapsulate the pattern.
          */
-        public Blue(final List<PatternFormatter> formatters, final String styling) {
-            super(NAME, formatters, styling);
+        public Blue(final List<PatternFormatter> formatters, final String styling, final FormattingInfo formattingInfo) {
+            super(NAME, formatters, styling, formattingInfo);
         }
 
         /**
@@ -106,11 +108,12 @@ public abstract class AbstractStyleNameConverter extends LogEventPatternConverte
          *
          * @param config The current Configuration.
          * @param options The pattern options, may be null. If the first element is "short", only the first line of the
-         *                throwable will be formatted.
+         *            throwable will be formatted.
          * @return new instance of class or null
          */
-        public static Blue newInstance(final Configuration config, final String[] options) {
-            return newInstance(Blue.class, NAME, config, options);
+        public static Blue newInstance(final Configuration config, final String[] options,
+                final FormattingInfo formattingInfo) {
+            return newInstance(Blue.class, NAME, config, options, formattingInfo);
         }
     }
 
@@ -130,8 +133,8 @@ public abstract class AbstractStyleNameConverter extends LogEventPatternConverte
          * @param formatters The PatternFormatters to generate the text to manipulate.
          * @param styling The styling that should encapsulate the pattern.
          */
-        public Cyan(final List<PatternFormatter> formatters, final String styling) {
-            super(NAME, formatters, styling);
+        public Cyan(final List<PatternFormatter> formatters, final String styling, final FormattingInfo formattingInfo) {
+            super(NAME, formatters, styling, formattingInfo);
         }
 
         /**
@@ -139,11 +142,12 @@ public abstract class AbstractStyleNameConverter extends LogEventPatternConverte
          *
          * @param config The current Configuration.
          * @param options The pattern options, may be null. If the first element is "short", only the first line of the
-         *                throwable will be formatted.
+         *            throwable will be formatted.
          * @return new instance of class or null
          */
-        public static Cyan newInstance(final Configuration config, final String[] options) {
-            return newInstance(Cyan.class, NAME, config, options);
+        public static Cyan newInstance(final Configuration config, final String[] options,
+                final FormattingInfo formattingInfo) {
+            return newInstance(Cyan.class, NAME, config, options, formattingInfo);
         }
     }
 
@@ -163,8 +167,8 @@ public abstract class AbstractStyleNameConverter extends LogEventPatternConverte
          * @param formatters The PatternFormatters to generate the text to manipulate.
          * @param styling The styling that should encapsulate the pattern.
          */
-        public Green(final List<PatternFormatter> formatters, final String styling) {
-            super(NAME, formatters, styling);
+        public Green(final List<PatternFormatter> formatters, final String styling, final FormattingInfo formattingInfo) {
+            super(NAME, formatters, styling, formattingInfo);
         }
 
         /**
@@ -172,11 +176,12 @@ public abstract class AbstractStyleNameConverter extends LogEventPatternConverte
          *
          * @param config The current Configuration.
          * @param options The pattern options, may be null. If the first element is "short", only the first line of the
-         *                throwable will be formatted.
+         *            throwable will be formatted.
          * @return new instance of class or null
          */
-        public static Green newInstance(final Configuration config, final String[] options) {
-            return newInstance(Green.class, NAME, config, options);
+        public static Green newInstance(final Configuration config, final String[] options,
+                final FormattingInfo formattingInfo) {
+            return newInstance(Green.class, NAME, config, options, formattingInfo);
         }
     }
 
@@ -196,8 +201,9 @@ public abstract class AbstractStyleNameConverter extends LogEventPatternConverte
          * @param formatters The PatternFormatters to generate the text to manipulate.
          * @param styling The styling that should encapsulate the pattern.
          */
-        public Magenta(final List<PatternFormatter> formatters, final String styling) {
-            super(NAME, formatters, styling);
+        public Magenta(final List<PatternFormatter> formatters, final String styling,
+                final FormattingInfo formattingInfo) {
+            super(NAME, formatters, styling, formattingInfo);
         }
 
         /**
@@ -205,11 +211,12 @@ public abstract class AbstractStyleNameConverter extends LogEventPatternConverte
          *
          * @param config The current Configuration.
          * @param options The pattern options, may be null. If the first element is "short", only the first line of the
-         *                throwable will be formatted.
+         *            throwable will be formatted.
          * @return new instance of class or null
          */
-        public static Magenta newInstance(final Configuration config, final String[] options) {
-            return newInstance(Magenta.class, NAME, config, options);
+        public static Magenta newInstance(final Configuration config, final String[] options,
+                final FormattingInfo formattingInfo) {
+            return newInstance(Magenta.class, NAME, config, options, formattingInfo);
         }
     }
 
@@ -229,8 +236,8 @@ public abstract class AbstractStyleNameConverter extends LogEventPatternConverte
          * @param formatters The PatternFormatters to generate the text to manipulate.
          * @param styling The styling that should encapsulate the pattern.
          */
-        public Red(final List<PatternFormatter> formatters, final String styling) {
-            super(NAME, formatters, styling);
+        public Red(final List<PatternFormatter> formatters, final String styling, final FormattingInfo formattingInfo) {
+            super(NAME, formatters, styling, formattingInfo);
         }
 
         /**
@@ -238,11 +245,12 @@ public abstract class AbstractStyleNameConverter extends LogEventPatternConverte
          *
          * @param config The current Configuration.
          * @param options The pattern options, may be null. If the first element is "short", only the first line of the
-         *                throwable will be formatted.
+         *            throwable will be formatted.
          * @return new instance of class or null
          */
-        public static Red newInstance(final Configuration config, final String[] options) {
-            return newInstance(Red.class, NAME, config, options);
+        public static Red newInstance(final Configuration config, final String[] options,
+                final FormattingInfo formattingInfo) {
+            return newInstance(Red.class, NAME, config, options, formattingInfo);
         }
     }
 
@@ -262,8 +270,8 @@ public abstract class AbstractStyleNameConverter extends LogEventPatternConverte
          * @param formatters The PatternFormatters to generate the text to manipulate.
          * @param styling The styling that should encapsulate the pattern.
          */
-        public White(final List<PatternFormatter> formatters, final String styling) {
-            super(NAME, formatters, styling);
+        public White(final List<PatternFormatter> formatters, final String styling, final FormattingInfo formattingInfo) {
+            super(NAME, formatters, styling, formattingInfo);
         }
 
         /**
@@ -271,11 +279,12 @@ public abstract class AbstractStyleNameConverter extends LogEventPatternConverte
          *
          * @param config The current Configuration.
          * @param options The pattern options, may be null. If the first element is "short", only the first line of the
-         *                throwable will be formatted.
+         *            throwable will be formatted.
          * @return new instance of class or null
          */
-        public static White newInstance(final Configuration config, final String[] options) {
-            return newInstance(White.class, NAME, config, options);
+        public static White newInstance(final Configuration config, final String[] options,
+                final FormattingInfo formattingInfo) {
+            return newInstance(White.class, NAME, config, options, formattingInfo);
         }
     }
 
@@ -295,8 +304,8 @@ public abstract class AbstractStyleNameConverter extends LogEventPatternConverte
          * @param formatters The PatternFormatters to generate the text to manipulate.
          * @param styling The styling that should encapsulate the pattern.
          */
-        public Yellow(final List<PatternFormatter> formatters, final String styling) {
-            super(NAME, formatters, styling);
+        public Yellow(final List<PatternFormatter> formatters, final String styling, final FormattingInfo formattingInfo) {
+            super(NAME, formatters, styling, formattingInfo);
         }
 
         /**
@@ -304,11 +313,12 @@ public abstract class AbstractStyleNameConverter extends LogEventPatternConverte
          *
          * @param config The current Configuration.
          * @param options The pattern options, may be null. If the first element is "short", only the first line of the
-         *                throwable will be formatted.
+         *            throwable will be formatted.
          * @return new instance of class or null
          */
-        public static Yellow newInstance(final Configuration config, final String[] options) {
-            return newInstance(Yellow.class, NAME, config, options);
+        public static Yellow newInstance(final Configuration config, final String[] options,
+                final FormattingInfo formattingInfo) {
+            return newInstance(Yellow.class, NAME, config, options, formattingInfo);
         }
     }
 
@@ -317,19 +327,18 @@ public abstract class AbstractStyleNameConverter extends LogEventPatternConverte
      *
      * @param config The current Configuration.
      * @param options The pattern options, may be null. If the first element is "short", only the first line of the
-     *                throwable will be formatted.
+     *            throwable will be formatted.
      * @return new instance of class or null
      */
     protected static <T extends AbstractStyleNameConverter> T newInstance(final Class<T> asnConverterClass,
-                                                                          final String name, final Configuration config,
-                                                                          final String[] options) {
+            final String name, final Configuration config, final String[] options, final FormattingInfo formattingInfo) {
         final List<PatternFormatter> formatters = toPatternFormatterList(config, options);
         if (formatters == null) {
             return null;
         }
         try {
             final Constructor<T> constructor = asnConverterClass.getConstructor(List.class, String.class);
-            return constructor.newInstance(formatters, AnsiEscape.createSequence(name));
+            return constructor.newInstance(formatters, AnsiEscape.createSequence(name), formattingInfo);
         } catch (final SecurityException e) {
             LOGGER.error(e.toString(), e);
         } catch (final NoSuchMethodException e) {
@@ -370,13 +379,27 @@ public abstract class AbstractStyleNameConverter extends LogEventPatternConverte
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder toAppendTo) {
-        final StringBuilder buf = new StringBuilder();
-        for (final PatternFormatter formatter : formatters) {
-            formatter.format(event, buf);
-        }
-        if (buf.length() > 0) {
-            toAppendTo.append(style).append(buf.toString()).append(AnsiEscape.getDefaultStyle());
+    public void format(final LogEvent event, final TextBuffer toAppendTo) {
+        format0(event, toAppendTo, null);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
+        format0(event, toAppendTo, charset);
+    }
+
+    private void format0(final LogEvent event, final Buffer toAppendTo, final Charset charset) {
+        StyleConverter.formatNested(event, toAppendTo, charset, false, formatters, style, AnsiEscape.getDefaultStyle());
+    }
+    
+    @Override
+    public void setCharset(final Charset charset) {
+        super.setCharset(charset);
+        for (PatternFormatter paf : formatters) {
+            paf.getConverter().setCharset(charset);
         }
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ArrayPatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ArrayPatternConverter.java
index c571b459f5..b73139046c 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ArrayPatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ArrayPatternConverter.java
@@ -23,8 +23,8 @@ public interface ArrayPatternConverter extends PatternConverter {
 
     /**
      * Formats an array of Objects.
-     * @param toAppendTo The StringBuilder to add the content to.
+     * @param toAppendTo The Buffer to add the content to.
      * @param objects The Object array.
      */
-    void format(final StringBuilder toAppendTo, Object... objects);
+    void format(final Buffer toAppendTo, Object... objects);
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ClassNamePatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ClassNamePatternConverter.java
index a2e0f2aefd..1d1713955e 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ClassNamePatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ClassNamePatternConverter.java
@@ -16,10 +16,11 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
+
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
 
-
 /**
  * Formats the class name of the site of the logging request.
  */
@@ -27,16 +28,15 @@ import org.apache.logging.log4j.core.config.plugins.Plugin;
 @ConverterKeys({ "C", "class" })
 public final class ClassNamePatternConverter extends NamePatternConverter {
 
-    private static final String NA = "?";
+    private static final char NA = '?';
 
     /**
      * Private constructor.
      *
      * @param options options, may be null.
      */
-    private ClassNamePatternConverter(
-        final String[] options) {
-        super("Class Name", "class name", options);
+    private ClassNamePatternConverter(final String[] options, final FormattingInfo formattingInfo) {
+        super("Class Name", "class name", options, formattingInfo);
     }
 
     /**
@@ -45,18 +45,18 @@ public final class ClassNamePatternConverter extends NamePatternConverter {
      * @param options options, may be null.
      * @return instance of pattern converter.
      */
-    public static ClassNamePatternConverter newInstance(final String[] options) {
-        return new ClassNamePatternConverter(options);
+    public static ClassNamePatternConverter newInstance(final String[] options, final FormattingInfo formattingInfo) {
+        return new ClassNamePatternConverter(options, formattingInfo);
     }
 
     /**
      * Format a logging event.
      *
-     * @param event      event to format.
-     * @param toAppendTo string buffer to which class name will be appended.
+     * @param event event to format.
+     * @param toAppendTo buffer to which class name will be appended.
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder toAppendTo) {
+    public void format(final LogEvent event, final TextBuffer toAppendTo) {
         final StackTraceElement element = event.getSource();
         if (element == null) {
             toAppendTo.append(NA);
@@ -64,4 +64,20 @@ public final class ClassNamePatternConverter extends NamePatternConverter {
             toAppendTo.append(abbreviate(element.getClassName()));
         }
     }
+
+    /**
+     * Format a logging event.
+     *
+     * @param event event to format.
+     * @param toAppendTo buffer to which class name will be appended.
+     */
+    @Override
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
+        final StackTraceElement element = event.getSource();
+        if (element == null) {
+            toAppendTo.append((byte) NA);
+        } else {
+            toAppendTo.append(abbreviateToBinary(element.getClassName(), charset));
+        }
+    }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/DatePatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/DatePatternConverter.java
index 4e34d5087c..6415abd72d 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/DatePatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/DatePatternConverter.java
@@ -16,12 +16,14 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.TimeZone;
 
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.util.Assert;
 
 /**
  * Convert and format the event's date in a StringBuilder.
@@ -30,48 +32,112 @@ import org.apache.logging.log4j.core.config.plugins.Plugin;
 @ConverterKeys({ "d", "date" })
 public final class DatePatternConverter extends LogEventPatternConverter implements ArrayPatternConverter {
 
-    private abstract static class Formatter {
-        abstract String format(long time);
+    /**
+     * ADT for formatter helpers.
+     */
+    public abstract static interface Formatter {
+        void format(long time, Buffer buffer);
 
-        public String toPattern() {
-            return null;
-        }
+        void format(Date time, Buffer buffer);
+
+        void format(long timestamp, BinaryBuffer output, Charset charset);
+
+        String toPattern();
     }
 
-    private static class PatternFormatter extends Formatter {
-        private final SimpleDateFormat simpleDateFormat;
+    private static class PatternFormatter implements Formatter {
+        private ThreadLocal<SimpleDateFormat> simpleDateFormat = new ThreadLocal<SimpleDateFormat>() {
+            @Override
+            protected SimpleDateFormat initialValue() {
+                final SimpleDateFormat result = new SimpleDateFormat(pattern);
+                if (timeZone != null) {
+                    result.setTimeZone(timeZone);
+                }
+                return result;
+            }
+        };
+        private final String pattern;
+        private final TimeZone timeZone;
+        private final boolean useFastFormat;
+
+        PatternFormatter(final String pattern, final TimeZone timeZone) {
+            this.pattern = Assert.requireNonNull(pattern, "pattern is null");
+            this.timeZone = timeZone;
+            this.useFastFormat = ABSOLUTE_TIME_PATTERN.equals(pattern);
+        }
 
-        PatternFormatter(final SimpleDateFormat simpleDateFormat) {
-            this.simpleDateFormat = simpleDateFormat;
+        @Override
+        public void format(final long time, final Buffer buffer) {
+            format(new Date(time), buffer);
         }
 
         @Override
-        String format(final long time) {
-            return simpleDateFormat.format(Long.valueOf(time));
+        public void format(final Date time, final Buffer buffer) {
+            if (useFastFormat) {
+
+            } else {
+                final String formatted = simpleDateFormat.get().format(time);
+                buffer.append(formatted);
+            }
+        }
+
+        @Override
+        public void format(final long timestamp, final BinaryBuffer output, final Charset charset) {
+            if (useFastFormat) {
+
+            } else {
+                format(new Date(timestamp), output); // use SimpleDateFormat
+            }
         }
 
         @Override
         public String toPattern() {
-            return simpleDateFormat.toPattern();
+            return pattern;
         }
     }
 
-    private static class UnixFormatter extends Formatter {
+    private static class UnixFormatter implements Formatter {
+        @Override
+        public void format(final long time, final Buffer buffer) {
+            buffer.append(time / 1000);
+        }
 
         @Override
-        String format(final long time) {
-            return Long.toString(time / 1000);
+        public void format(final Date time, final Buffer buffer) {
+            format(time.getTime(), buffer);
         }
 
+        @Override
+        public void format(final long timestamp, final BinaryBuffer output, final Charset charset) {
+            output.append(timestamp / 1000);
+        }
+
+        @Override
+        public String toPattern() {
+            return null;
+        }
     }
 
-    private static class UnixMillisFormatter extends Formatter {
+    private static class UnixMillisFormatter implements Formatter {
+        @Override
+        public void format(final long time, final Buffer buffer) {
+            buffer.append(time);
+        }
+
+        @Override
+        public void format(final Date time, final Buffer buffer) {
+            format(time.getTime(), buffer);
+        }
 
         @Override
-        String format(final long time) {
-            return Long.toString(time);
+        public void format(final long timestamp, final BinaryBuffer output, final Charset charset) {
+            output.append(timestamp);
         }
 
+        @Override
+        public String toPattern() {
+            return null;
+        }
     }
 
     /**
@@ -82,7 +148,7 @@ public final class DatePatternConverter extends LogEventPatternConverter impleme
     /**
      * SimpleTimePattern for ABSOLUTE.
      */
-    private static final String ABSOLUTE_TIME_PATTERN = "HH:mm:ss,SSS";
+    static final String ABSOLUTE_TIME_PATTERN = "HH:mm:ss,SSS";
 
     /**
      * COMPACT string literal.
@@ -150,31 +216,22 @@ public final class DatePatternConverter extends LogEventPatternConverter impleme
     /**
      * Obtains an instance of pattern converter.
      *
-     * @param options
-     *            options, may be null.
+     * @param options options, may be null.
      * @return instance of pattern converter.
      */
-    public static DatePatternConverter newInstance(final String[] options) {
-        return new DatePatternConverter(options);
+    public static DatePatternConverter newInstance(final String[] options, final FormattingInfo formattingInfo) {
+        return new DatePatternConverter(options, formattingInfo);
     }
 
-    /**
-     * Date format.
-     */
-    private String cachedDateString;
-
-    private final Formatter formatter;
-
-    private long lastTimestamp;
+    private Formatter formatter;
 
     /**
      * Private constructor.
      *
-     * @param options
-     *            options, may be null.
+     * @param options options, may be null.
      */
-    private DatePatternConverter(final String[] options) {
-        super("Date", "date");
+    private DatePatternConverter(final String[] options, final FormattingInfo formattingInfo) {
+        super("Date", "date", formattingInfo);
 
         // null patternOption is OK.
         final String patternOption = options != null && options.length > 0 ? options[0] : null;
@@ -203,62 +260,66 @@ public final class DatePatternConverter extends LogEventPatternConverter impleme
         }
 
         if (pattern != null) {
-            SimpleDateFormat tempFormat;
-
             try {
-                tempFormat = new SimpleDateFormat(pattern);
+                new SimpleDateFormat(pattern);
             } catch (final IllegalArgumentException e) {
                 LOGGER.warn("Could not instantiate SimpleDateFormat with pattern " + patternOption, e);
 
                 // default to the DEFAULT format
-                tempFormat = new SimpleDateFormat(DEFAULT_PATTERN);
+                pattern = DEFAULT_PATTERN;
             }
 
             // if the option list contains a TZ option, then set it.
-            if (options != null && options.length > 1) {
-                final TimeZone tz = TimeZone.getTimeZone(options[1]);
-                tempFormat.setTimeZone(tz);
-            }
-            tempFormatter = new PatternFormatter(tempFormat);
+            final TimeZone tz = (options != null && options.length > 1) ? TimeZone.getTimeZone(options[1]) : null;
+            tempFormatter = new PatternFormatter(pattern, tz);
         }
         formatter = tempFormatter;
     }
 
+    @Override
+    public void setCharset(Charset charset) {
+        super.setCharset(charset);
+        if (formatter instanceof PatternFormatter) {
+            PatternFormatter paf = (PatternFormatter) formatter;
+            
+            // FastTimeFormat does not support time zones
+            if (paf.timeZone == null && paf.pattern.endsWith(ABSOLUTE_TIME_PATTERN)) {
+                formatter = new FastTimeFormatter(paf.pattern, charset);
+            }
+        }
+    }
+
     /**
      * Append formatted date to string buffer.
      *
-     * @param date
-     *            date
-     * @param toAppendTo
-     *            buffer to which formatted date is appended.
+     * @param date date
+     * @param toAppendTo buffer to which formatted date is appended.
      */
-    public void format(final Date date, final StringBuilder toAppendTo) {
-        synchronized (this) {
-            toAppendTo.append(formatter.format(date.getTime()));
-        }
+    public void format(final Date date, final Buffer toAppendTo) {
+        formatter.format(date, toAppendTo);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder output) {
-        final long timestamp = event.getTimeMillis();
+    public void format(final LogEvent event, final TextBuffer output) {
+        formatter.format(event.getTimeMillis(), output);
+    }
 
-        synchronized (this) {
-            if (timestamp != lastTimestamp) {
-                lastTimestamp = timestamp;
-                cachedDateString = formatter.format(timestamp);
-            }
-        }
-        output.append(cachedDateString);
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final BinaryBuffer output, final Charset charset) {
+        formatter.format(event.getTimeMillis(), output, charset);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void format(final Object obj, final StringBuilder output) {
+    public void format(final Object obj, final Buffer output) {
         if (obj instanceof Date) {
             format((Date) obj, output);
         }
@@ -266,7 +327,7 @@ public final class DatePatternConverter extends LogEventPatternConverter impleme
     }
 
     @Override
-    public void format(final StringBuilder toAppendTo, final Object... objects) {
+    public void format(final Buffer toAppendTo, final Object... objects) {
         for (final Object obj : objects) {
             if (obj instanceof Date) {
                 format(obj, toAppendTo);
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/EncodingPatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/EncodingPatternConverter.java
index b4373091e8..64ecf9f4e8 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/EncodingPatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/EncodingPatternConverter.java
@@ -16,6 +16,7 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
 import java.util.List;
 
 import org.apache.logging.log4j.core.LogEvent;
@@ -37,8 +38,8 @@ public final class EncodingPatternConverter extends LogEventPatternConverter {
      *
      * @param formatters The PatternFormatters to generate the text to manipulate.
      */
-    private EncodingPatternConverter(final List<PatternFormatter> formatters) {
-        super("encode", "encode");
+    private EncodingPatternConverter(final List<PatternFormatter> formatters, final FormattingInfo formattingInfo) {
+        super("encode", "encode", formattingInfo);
         this.formatters = formatters;
     }
 
@@ -49,7 +50,7 @@ public final class EncodingPatternConverter extends LogEventPatternConverter {
      * @param options options, may be null.
      * @return instance of pattern converter.
      */
-    public static EncodingPatternConverter newInstance(final Configuration config, final String[] options) {
+    public static EncodingPatternConverter newInstance(final Configuration config, final String[] options, final FormattingInfo formattingInfo) {
         if (options.length != 1) {
             LOGGER.error("Incorrect number of options on escape. Expected 1, received " + options.length);
             return null;
@@ -60,49 +61,73 @@ public final class EncodingPatternConverter extends LogEventPatternConverter {
         }
         final PatternParser parser = PatternLayout.createPatternParser(config);
         final List<PatternFormatter> formatters = parser.parse(options[0]);
-        return new EncodingPatternConverter(formatters);
+        return new EncodingPatternConverter(formatters, formattingInfo);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder toAppendTo) {
-        final StringBuilder buf = new StringBuilder();
+    public void format(final LogEvent event, final TextBuffer toAppendTo) {
+        replaceAndAppend(event, toAppendTo);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
+        replaceAndAppend(event, toAppendTo);
+    }
+
+    private void replaceAndAppend(final LogEvent event, final Buffer toAppendTo) {
+        final TextBuffer buf = new TextBuffer();
         for (final PatternFormatter formatter : formatters) {
             formatter.format(event, buf);
         }
         for (int i = 0; i < buf.length(); i++) {
             final char c = buf.charAt(i);
-            switch (c) {
-                case '\r':
-                    toAppendTo.append("\\r");
-                    break;
-                case '\n':
-                    toAppendTo.append("\\n");
-                    break;
-                case '&':
-                    toAppendTo.append("&amp;");
-                    break;
-                case '<':
-                    toAppendTo.append("&lt;");
-                    break;
-                case '>':
-                    toAppendTo.append("&gt;");
-                    break;
-                case '"':
-                    toAppendTo.append("&quot;");
-                    break;
-                case '\'':
-                    toAppendTo.append("&apos;");
-                    break;
-                case '/':
-                    toAppendTo.append("&#x2F;");
-                    break;
-                default:
-                    toAppendTo.append(c);
-                    break;
-            }
+            replace(c, toAppendTo);
+        }
+    }
+
+    private void replace(final char c, final Buffer toAppendTo) {
+        switch (c) {
+            case '\r':
+                toAppendTo.append("\\r");
+                break;
+            case '\n':
+                toAppendTo.append("\\n");
+                break;
+            case '&':
+                toAppendTo.append("&amp;");
+                break;
+            case '<':
+                toAppendTo.append("&lt;");
+                break;
+            case '>':
+                toAppendTo.append("&gt;");
+                break;
+            case '"':
+                toAppendTo.append("&quot;");
+                break;
+            case '\'':
+                toAppendTo.append("&apos;");
+                break;
+            case '/':
+                toAppendTo.append("&#x2F;");
+                break;
+            default:
+                toAppendTo.append(c);
+                break;
+        }
+    }
+    
+    @Override
+    public void setCharset(final Charset charset) {
+        super.setCharset(charset);
+        for (PatternFormatter paf : formatters) {
+            paf.getConverter().setCharset(charset);
         }
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ExtendedThrowablePatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ExtendedThrowablePatternConverter.java
index cf6f764bdd..522404204d 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ExtendedThrowablePatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ExtendedThrowablePatternConverter.java
@@ -16,6 +16,8 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
+
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
 import org.apache.logging.log4j.core.impl.Log4jLogEvent;
@@ -23,12 +25,11 @@ import org.apache.logging.log4j.core.impl.ThrowableProxy;
 import org.apache.logging.log4j.core.util.Constants;
 
 /**
- * Outputs the Throwable portion of the LoggingEvent as a full stacktrace
- * unless this converter's option is 'short', where it just outputs the first line of the trace, or if
- * the number of lines to print is explicitly specified.
+ * Outputs the Throwable portion of the LoggingEvent as a full stacktrace unless this converter's option is 'short',
+ * where it just outputs the first line of the trace, or if the number of lines to print is explicitly specified.
  * <p>
- * The extended stack trace will also include the location of where the class was loaded from and the
- * version of the jar if available.
+ * The extended stack trace will also include the location of where the class was loaded from and the version of the jar
+ * if available.
  */
 @Plugin(name = "ExtendedThrowablePatternConverter", category = PatternConverter.CATEGORY)
 @ConverterKeys({ "xEx", "xThrowable", "xException" })
@@ -39,26 +40,40 @@ public final class ExtendedThrowablePatternConverter extends ThrowablePatternCon
      *
      * @param options options, may be null.
      */
-    private ExtendedThrowablePatternConverter(final String[] options) {
-        super("ExtendedThrowable", "throwable", options);
+    private ExtendedThrowablePatternConverter(final String[] options, final FormattingInfo formattingInfo) {
+        super("ExtendedThrowable", "throwable", options, formattingInfo);
     }
 
     /**
      * Gets an instance of the class.
      *
-     * @param options pattern options, may be null.  If first element is "short",
-     *                only the first line of the throwable will be formatted.
+     * @param options pattern options, may be null. If first element is "short", only the first line of the throwable
+     *            will be formatted.
      * @return instance of class.
      */
-    public static ExtendedThrowablePatternConverter newInstance(final String[] options) {
-        return new ExtendedThrowablePatternConverter(options);
+    public static ExtendedThrowablePatternConverter newInstance(final String[] options,
+            final FormattingInfo formattingInfo) {
+        return new ExtendedThrowablePatternConverter(options, formattingInfo);
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final TextBuffer toAppendTo) {
+        handle(event, toAppendTo);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder toAppendTo) {
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
+        handle(event, toAppendTo);
+    }
+
+    private void handle(final LogEvent event, final Buffer toAppendTo) {
         ThrowableProxy proxy = null;
         if (event instanceof Log4jLogEvent) {
             proxy = ((Log4jLogEvent) event).getThrownProxy();
@@ -70,13 +85,12 @@ public final class ExtendedThrowablePatternConverter extends ThrowablePatternCon
                 return;
             }
             final String trace = proxy.getExtendedStackTraceAsString(options.getPackages());
-            final int len = toAppendTo.length();
-            if (len > 0 && !Character.isWhitespace(toAppendTo.charAt(len - 1))) {
+            if (!toAppendTo.hasTrailingWhitespace()) {
                 toAppendTo.append(' ');
             }
             if (!options.allLines() || !Constants.LINE_SEPARATOR.equals(options.getSeparator())) {
-                final StringBuilder sb = new StringBuilder();
                 final String[] array = trace.split(Constants.LINE_SEPARATOR);
+                final StringBuilder sb = new StringBuilder(array.length << 7); // *128: estimate 128 chars per line
                 final int limit = options.minLines(array.length) - 1;
                 for (int i = 0; i <= limit; ++i) {
                     sb.append(array[i]);
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FileDatePatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FileDatePatternConverter.java
index 7a83d78aba..a6fb36157d 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FileDatePatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FileDatePatternConverter.java
@@ -19,9 +19,8 @@ package org.apache.logging.log4j.core.pattern;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
 
 /**
- * Formats a date by delegating to {@link DatePatternConverter}.  The default
- * date pattern for a %d specifier in a file name is different than
- * the %d pattern in pattern layout.
+ * Formats a date by delegating to {@link DatePatternConverter}. The default date pattern for a %d specifier in a file
+ * name is different than the %d pattern in pattern layout.
  */
 @Plugin(name = "FileDatePatternConverter", category = "FileConverter")
 @ConverterKeys({ "d", "date" })
@@ -38,14 +37,11 @@ public final class FileDatePatternConverter {
      * @param options options, may be null.
      * @return instance of pattern converter.
      */
-    public static PatternConverter newInstance(final String[] options) {
+    public static PatternConverter newInstance(final String[] options, final FormattingInfo formattingInfo) {
         if (options == null || options.length == 0) {
-            return DatePatternConverter.newInstance(
-                new String[]{
-                    "yyyy-MM-dd"
-                });
+            return DatePatternConverter.newInstance(new String[] { "yyyy-MM-dd" }, formattingInfo);
         }
 
-        return DatePatternConverter.newInstance(options);
+        return DatePatternConverter.newInstance(options, formattingInfo);
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FileLocationPatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FileLocationPatternConverter.java
index 7fcd33d53b..5a7a9af08f 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FileLocationPatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FileLocationPatternConverter.java
@@ -16,27 +16,23 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
+
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
 
-
 /**
  * Returns the event's line location information in a StringBuilder.
  */
 @Plugin(name = "FileLocationPatternConverter", category = PatternConverter.CATEGORY)
 @ConverterKeys({ "F", "file" })
 public final class FileLocationPatternConverter extends LogEventPatternConverter {
-    /**
-     * Singleton.
-     */
-    private static final FileLocationPatternConverter INSTANCE =
-        new FileLocationPatternConverter();
 
     /**
      * Private constructor.
      */
-    private FileLocationPatternConverter() {
-        super("File Location", "file");
+    private FileLocationPatternConverter(final FormattingInfo formattingInfo) {
+        super("File Location", "file", formattingInfo);
     }
 
     /**
@@ -45,19 +41,29 @@ public final class FileLocationPatternConverter extends LogEventPatternConverter
      * @param options options, may be null.
      * @return instance of pattern converter.
      */
-    public static FileLocationPatternConverter newInstance(final String[] options) {
-        return INSTANCE;
+    public static FileLocationPatternConverter newInstance(final String[] options, final FormattingInfo formattingInfo) {
+        return new FileLocationPatternConverter(formattingInfo);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder output) {
+    public void format(final LogEvent event, final TextBuffer output) {
         final StackTraceElement element = event.getSource();
+        if (element != null) {
+            output.append(getCachedFormattedString(element.getFileName()));
+        }
+    }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
+        final StackTraceElement element = event.getSource();
         if (element != null) {
-            output.append(element.getFileName());
+            toAppendTo.append(getCachedFormattedBytes(element.getFileName(), charset));
         }
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FormattingInfo.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FormattingInfo.java
index 950a7d5ea3..a02c2fc2fe 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FormattingInfo.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FormattingInfo.java
@@ -17,6 +17,9 @@
 
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
 /**
  * Modifies the output of a pattern converter for a specified minimum and maximum width and alignment.
  */
@@ -24,7 +27,14 @@ public final class FormattingInfo {
     /**
      * Array of spaces.
      */
-    private static final char[] SPACES = new char[] { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' };
+    private static final char[] SPACES = new char[128];
+    static {
+        Arrays.fill(SPACES, ' ');
+    }
+    private static final byte[] SPACEBYTES = new byte[128];
+    static {
+        Arrays.fill(SPACEBYTES, (byte) ' ');
+    }
 
     /**
      * Default instance.
@@ -49,14 +59,22 @@ public final class FormattingInfo {
     /**
      * Creates new instance.
      *
-     * @param leftAlign
-     *            left align if true.
-     * @param minLength
-     *            minimum length.
-     * @param maxLength
-     *            maximum length.
+     * @param leftAlign left align if true.
+     * @param minLength minimum length (must be non-negative and not exceed maxLength).
+     * @param maxLength maximum length (must be non-negative).
      */
     public FormattingInfo(final boolean leftAlign, final int minLength, final int maxLength) {
+        if (minLength < 0) {
+            throw new IllegalArgumentException("MinLength must be at least 0 but was " + minLength);
+        }
+        if (maxLength < 0) {
+            throw new IllegalArgumentException("MaxLength must be at least 0 but was " + maxLength);
+        }
+        // Note: I'd like to assert that min <= max, but this breaks the PatternParser unit tests (and perhaps user
+        // code)
+        // if (minLength > maxLength) {
+        // throw new IllegalArgumentException("MinLength " + minLength + " exceeds MaxLength " + maxLength);
+        // }
         this.leftAlign = leftAlign;
         this.minLength = minLength;
         this.maxLength = maxLength;
@@ -101,12 +119,13 @@ public final class FormattingInfo {
     /**
      * Adjust the content of the buffer based on the specified lengths and alignment.
      *
-     * @param fieldStart
-     *            start of field in buffer.
-     * @param buffer
-     *            buffer to be modified.
+     * @param fieldStart start of field in buffer.
+     * @param buffer buffer to be modified.
      */
     public void format(final int fieldStart, final StringBuilder buffer) {
+        if (this == DEFAULT) {
+            return; // no formatting
+        }
         final int rawLength = buffer.length() - fieldStart;
 
         if (rawLength > maxLength) {
@@ -150,4 +169,49 @@ public final class FormattingInfo {
         return sb.toString();
     }
 
+    /**
+     * Adjust the content of the buffer based on the specified lengths and alignment.
+     *
+     * @param fieldStart start of field in buffer.
+     * @param buffer buffer to be modified.
+     */
+    public void format(int fieldStart, ByteBuffer buffer) {
+        if (this == DEFAULT) {
+            return; // no formatting
+        }
+        final int rawLength = buffer.position() - fieldStart;
+
+        if (rawLength > maxLength) {
+            buffer.position(buffer.position() - maxLength);
+            final ByteBuffer src = buffer.slice();
+            src.limit(maxLength);
+            buffer.position(fieldStart);
+            buffer.put(src);
+        } else if (rawLength < minLength) {
+            if (leftAlign) {
+                final int fieldEnd = buffer.position();
+                buffer.position(fieldStart + minLength);
+
+                for (int i = fieldEnd; i < buffer.position(); i++) {
+                    buffer.put(i, (byte) ' ');
+                }
+            } else {
+                int old = buffer.position();
+                int padLength = minLength - rawLength;
+
+                buffer.position(fieldStart);
+                final ByteBuffer src = buffer.slice();
+                src.limit(old - fieldStart);
+                buffer.position(fieldStart + padLength);
+                buffer.put(src);
+
+                buffer.position(fieldStart);
+                for (; padLength > SPACES.length; padLength -= SPACES.length) {
+                    buffer.put(SPACEBYTES, 0, padLength);
+                }
+                buffer.put(SPACEBYTES, 0, padLength);
+                buffer.position(minLength + fieldStart);
+            }
+        }
+    }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FullLocationPatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FullLocationPatternConverter.java
index 120a671863..e6c43eff89 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FullLocationPatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FullLocationPatternConverter.java
@@ -16,6 +16,8 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
+
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
 
@@ -26,17 +28,12 @@ import org.apache.logging.log4j.core.config.plugins.Plugin;
 @Plugin(name = "FullLocationPatternConverter", category = PatternConverter.CATEGORY)
 @ConverterKeys({ "l", "location" })
 public final class FullLocationPatternConverter extends LogEventPatternConverter {
-    /**
-     * Singleton.
-     */
-    private static final FullLocationPatternConverter INSTANCE =
-        new FullLocationPatternConverter();
 
     /**
      * Private constructor.
      */
-    private FullLocationPatternConverter() {
-        super("Full Location", "fullLocation");
+    private FullLocationPatternConverter(final FormattingInfo formattingInfo) {
+        super("Full Location", "fullLocation", formattingInfo);
     }
 
     /**
@@ -45,19 +42,29 @@ public final class FullLocationPatternConverter extends LogEventPatternConverter
      * @param options options, may be null.
      * @return instance of pattern converter.
      */
-    public static FullLocationPatternConverter newInstance(final String[] options) {
-        return INSTANCE;
+    public static FullLocationPatternConverter newInstance(final String[] options, final FormattingInfo formattingInfo) {
+        return new FullLocationPatternConverter(formattingInfo);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder output) {
+    public void format(final LogEvent event, final TextBuffer output) {
         final StackTraceElement element = event.getSource();
+        if (element != null) {
+            output.append(getCachedFormattedString(element));
+        }
+    }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
+        final StackTraceElement element = event.getSource();
         if (element != null) {
-            output.append(element.toString());
+            toAppendTo.append(getCachedFormattedBytes(element, charset));
         }
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/HighlightConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/HighlightConverter.java
index 3fc0ef0f8b..473f8d284a 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/HighlightConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/HighlightConverter.java
@@ -1,229 +1,243 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.core.pattern;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-
-import org.apache.logging.log4j.Level;
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.config.plugins.Plugin;
-import org.apache.logging.log4j.core.layout.PatternLayout;
-import org.apache.logging.log4j.util.Strings;
-
-/**
- * Highlight pattern converter. Formats the result of a pattern using a color appropriate for the Level in the LogEvent.
- * <p>
- * For example:
- * </p>
- * 
- * <pre>
- * %highlight{%d{ ISO8601 } [%t] %-5level: %msg%n%throwable}
- * </pre>
- * <p>
- * You can define custom colors for each Level:
- * </p>
- * 
- * <pre>
- * %highlight{%d{ ISO8601 } [%t] %-5level: %msg%n%throwable}{FATAL=red, ERROR=red, WARN=yellow, INFO=green, DEBUG=cyan,
- * TRACE=black}
- * </pre>
- * <p>
- * You can use a predefined style:
- * </p>
- * 
- * <pre>
- * %highlight{%d{ ISO8601 } [%t] %-5level: %msg%n%throwable}{STYLE=Log4j}
- * </pre>
- * <p>
- * The available predefined styles are:
- * </p>
- * <ul>
- * <li>{@code Default}</li>
- * <li>{@code Log4j} - The same as {@code Default}</li>
- * <li>{@code Logback}</li>
- * </ul>
- * <p>
- * You can use whitespace around the comma and equal sign. The names in values MUST come from the
- * {@linkplain AnsiEscape} enum, case is normalized to upper-case internally.
- * </p>
- */
-@Plugin(name = "highlight", category = PatternConverter.CATEGORY)
-@ConverterKeys({ "highlight" })
-public final class HighlightConverter extends LogEventPatternConverter implements AnsiConverter {
-
-    private static final Map<Level, String> DEFAULT_STYLES = new HashMap<Level, String>();
-
-    private static final Map<Level, String> LOGBACK_STYLES = new HashMap<Level, String>();
-
-    private static final String STYLE_KEY = "STYLE";
-
-    private static final String STYLE_KEY_DEFAULT = "DEFAULT";
-
-    private static final String STYLE_KEY_LOGBACK = "LOGBACK";
-
-    private static final Map<String, Map<Level, String>> STYLES = new HashMap<String, Map<Level, String>>();
-
-    static {
-        // Default styles:
-        DEFAULT_STYLES.put(Level.FATAL, AnsiEscape.createSequence("BRIGHT", "RED"));
-        DEFAULT_STYLES.put(Level.ERROR, AnsiEscape.createSequence("BRIGHT", "RED"));
-        DEFAULT_STYLES.put(Level.WARN, AnsiEscape.createSequence("YELLOW"));
-        DEFAULT_STYLES.put(Level.INFO, AnsiEscape.createSequence("GREEN"));
-        DEFAULT_STYLES.put(Level.DEBUG, AnsiEscape.createSequence("CYAN"));
-        DEFAULT_STYLES.put(Level.TRACE, AnsiEscape.createSequence("BLACK"));
-        // Logback styles:
-        LOGBACK_STYLES.put(Level.FATAL, AnsiEscape.createSequence("BLINK", "BRIGHT", "RED"));
-        LOGBACK_STYLES.put(Level.ERROR, AnsiEscape.createSequence("BRIGHT", "RED"));
-        LOGBACK_STYLES.put(Level.WARN, AnsiEscape.createSequence("RED"));
-        LOGBACK_STYLES.put(Level.INFO, AnsiEscape.createSequence("BLUE"));
-        LOGBACK_STYLES.put(Level.DEBUG, AnsiEscape.createSequence((String[]) null));
-        LOGBACK_STYLES.put(Level.TRACE, AnsiEscape.createSequence((String[]) null));
-        // Style map:
-        STYLES.put(STYLE_KEY_DEFAULT, DEFAULT_STYLES);
-        STYLES.put(STYLE_KEY_LOGBACK, LOGBACK_STYLES);
-    }
-
-    /**
-     * Creates a level style map where values are ANSI escape sequences given configuration options in {@code option[1]}
-     * .
-     * <p>
-     * The format of the option string in {@code option[1]} is:
-     * </p>
-     * 
-     * <pre>
-     * Level1=Value, Level2=Value, ...
-     * </pre>
-     *
-     * <p>
-     * For example:
-     * </p>
-     *
-     * <pre>
-     * ERROR=red bold, WARN=yellow bold, INFO=green, ...
-     * </pre>
-     *
-     * <p>
-     * You can use whitespace around the comma and equal sign. The names in values MUST come from the
-     * {@linkplain AnsiEscape} enum, case is normalized to upper-case internally.
-     * </p>
-     *
-     * @param options
-     *        The second slot can optionally contain the style map.
-     * @return a new map
-     */
-    private static Map<Level, String> createLevelStyleMap(final String[] options) {
-        if (options.length < 2) {
-            return DEFAULT_STYLES;
-        }
-        // Feels like a hack. Should String[] options change to a Map<String,String>?
-        final String string = options[1].replaceAll(PatternParser.NO_CONSOLE_NO_ANSI + "=(true|false)", Strings.EMPTY);
-        //
-        final Map<String, String> styles = AnsiEscape.createMap(string, new String[] {STYLE_KEY});
-        final Map<Level, String> levelStyles = new HashMap<Level, String>(DEFAULT_STYLES);
-        for (final Map.Entry<String, String> entry : styles.entrySet()) {
-            final String key = entry.getKey().toUpperCase(Locale.ENGLISH);
-            final String value = entry.getValue();
-            if (STYLE_KEY.equalsIgnoreCase(key)) {
-                final Map<Level, String> enumMap = STYLES.get(value.toUpperCase(Locale.ENGLISH));
-                if (enumMap == null) {
-                    LOGGER.error("Unknown level style: " + value + ". Use one of " +
-                        Arrays.toString(STYLES.keySet().toArray()));
-                } else {
-                    levelStyles.putAll(enumMap);
-                }
-            } else {
-                final Level level = Level.toLevel(key);
-                if (level == null) {
-                    LOGGER.error("Unknown level name: " + key + ". Use one of " +
-                        Arrays.toString(DEFAULT_STYLES.keySet().toArray()));
-                } else {
-                    levelStyles.put(level, value);
-                }
-            }
-        }
-        return levelStyles;
-    }
-
-    /**
-     * Gets an instance of the class.
-     *
-     * @param config The current Configuration.
-     * @param options pattern options, may be null. If first element is "short", only the first line of the
-     *                throwable will be formatted.
-     * @return instance of class.
-     */
-    public static HighlightConverter newInstance(final Configuration config, final String[] options) {
-        if (options.length < 1) {
-            LOGGER.error("Incorrect number of options on style. Expected at least 1, received " + options.length);
-            return null;
-        }
-        if (options[0] == null) {
-            LOGGER.error("No pattern supplied on style");
-            return null;
-        }
-        final PatternParser parser = PatternLayout.createPatternParser(config);
-        final List<PatternFormatter> formatters = parser.parse(options[0]);
-        return new HighlightConverter(formatters, createLevelStyleMap(options));
-    }
-
-    private final Map<Level, String> levelStyles;
-
-    private final List<PatternFormatter> patternFormatters;
-
-    /**
-     * Construct the converter.
-     *
-     * @param patternFormatters
-     *            The PatternFormatters to generate the text to manipulate.
-     */
-    private HighlightConverter(final List<PatternFormatter> patternFormatters, final Map<Level, String> levelStyles) {
-        super("style", "style");
-        this.patternFormatters = patternFormatters;
-        this.levelStyles = levelStyles;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void format(final LogEvent event, final StringBuilder toAppendTo) {
-        final StringBuilder buf = new StringBuilder();
-        for (final PatternFormatter formatter : patternFormatters) {
-            formatter.format(event, buf);
-        }
-
-        if (buf.length() > 0) {
-            toAppendTo.append(levelStyles.get(event.getLevel())).append(buf.toString()).
-                append(AnsiEscape.getDefaultStyle());
-        }
-    }
-
-    @Override
-    public boolean handlesThrowable() {
-        for (final PatternFormatter formatter : patternFormatters) {
-            if (formatter .handlesThrowable()) {
-                return true;
-            }
-        }
-        return false;
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.pattern;
+
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.util.Strings;
+
+/**
+ * Highlight pattern converter. Formats the result of a pattern using a color appropriate for the Level in the LogEvent.
+ * <p>
+ * For example:
+ * </p>
+ * 
+ * <pre>
+ * %highlight{%d{ ISO8601 } [%t] %-5level: %msg%n%throwable}
+ * </pre>
+ * <p>
+ * You can define custom colors for each Level:
+ * </p>
+ * 
+ * <pre>
+ * %highlight{%d{ ISO8601 } [%t] %-5level: %msg%n%throwable}{FATAL=red, ERROR=red, WARN=yellow, INFO=green, DEBUG=cyan,
+ * TRACE=black}
+ * </pre>
+ * <p>
+ * You can use a predefined style:
+ * </p>
+ * 
+ * <pre>
+ * %highlight{%d{ ISO8601 } [%t] %-5level: %msg%n%throwable}{STYLE=Log4j}
+ * </pre>
+ * <p>
+ * The available predefined styles are:
+ * </p>
+ * <ul>
+ * <li>{@code Default}</li>
+ * <li>{@code Log4j} - The same as {@code Default}</li>
+ * <li>{@code Logback}</li>
+ * </ul>
+ * <p>
+ * You can use whitespace around the comma and equal sign. The names in values MUST come from the
+ * {@linkplain AnsiEscape} enum, case is normalized to upper-case internally.
+ * </p>
+ */
+@Plugin(name = "highlight", category = PatternConverter.CATEGORY)
+@ConverterKeys({ "highlight" })
+public final class HighlightConverter extends LogEventPatternConverter implements AnsiConverter {
+
+    private static final Map<Level, String> DEFAULT_STYLES = new HashMap<Level, String>();
+
+    private static final Map<Level, String> LOGBACK_STYLES = new HashMap<Level, String>();
+
+    private static final String STYLE_KEY = "STYLE";
+
+    private static final String STYLE_KEY_DEFAULT = "DEFAULT";
+
+    private static final String STYLE_KEY_LOGBACK = "LOGBACK";
+
+    private static final Map<String, Map<Level, String>> STYLES = new HashMap<String, Map<Level, String>>();
+
+    static {
+        // Default styles:
+        DEFAULT_STYLES.put(Level.FATAL, AnsiEscape.createSequence("BRIGHT", "RED"));
+        DEFAULT_STYLES.put(Level.ERROR, AnsiEscape.createSequence("BRIGHT", "RED"));
+        DEFAULT_STYLES.put(Level.WARN, AnsiEscape.createSequence("YELLOW"));
+        DEFAULT_STYLES.put(Level.INFO, AnsiEscape.createSequence("GREEN"));
+        DEFAULT_STYLES.put(Level.DEBUG, AnsiEscape.createSequence("CYAN"));
+        DEFAULT_STYLES.put(Level.TRACE, AnsiEscape.createSequence("BLACK"));
+        // Logback styles:
+        LOGBACK_STYLES.put(Level.FATAL, AnsiEscape.createSequence("BLINK", "BRIGHT", "RED"));
+        LOGBACK_STYLES.put(Level.ERROR, AnsiEscape.createSequence("BRIGHT", "RED"));
+        LOGBACK_STYLES.put(Level.WARN, AnsiEscape.createSequence("RED"));
+        LOGBACK_STYLES.put(Level.INFO, AnsiEscape.createSequence("BLUE"));
+        LOGBACK_STYLES.put(Level.DEBUG, AnsiEscape.createSequence((String[]) null));
+        LOGBACK_STYLES.put(Level.TRACE, AnsiEscape.createSequence((String[]) null));
+        // Style map:
+        STYLES.put(STYLE_KEY_DEFAULT, DEFAULT_STYLES);
+        STYLES.put(STYLE_KEY_LOGBACK, LOGBACK_STYLES);
+    }
+
+    /**
+     * Creates a level style map where values are ANSI escape sequences given configuration options in {@code option[1]}
+     * .
+     * <p>
+     * The format of the option string in {@code option[1]} is:
+     * </p>
+     * 
+     * <pre>
+     * Level1=Value, Level2=Value, ...
+     * </pre>
+     *
+     * <p>
+     * For example:
+     * </p>
+     *
+     * <pre>
+     * ERROR=red bold, WARN=yellow bold, INFO=green, ...
+     * </pre>
+     *
+     * <p>
+     * You can use whitespace around the comma and equal sign. The names in values MUST come from the
+     * {@linkplain AnsiEscape} enum, case is normalized to upper-case internally.
+     * </p>
+     *
+     * @param options The second slot can optionally contain the style map.
+     * @return a new map
+     */
+    private static Map<Level, String> createLevelStyleMap(final String[] options) {
+        if (options.length < 2) {
+            return DEFAULT_STYLES;
+        }
+        // Feels like a hack. Should String[] options change to a Map<String,String>?
+        final String string = options[1].replaceAll(PatternParser.NO_CONSOLE_NO_ANSI + "=(true|false)", Strings.EMPTY);
+        //
+        final Map<String, String> styles = AnsiEscape.createMap(string, new String[] { STYLE_KEY });
+        final Map<Level, String> levelStyles = new HashMap<Level, String>(DEFAULT_STYLES);
+        for (final Map.Entry<String, String> entry : styles.entrySet()) {
+            final String key = entry.getKey().toUpperCase(Locale.ENGLISH);
+            final String value = entry.getValue();
+            if (STYLE_KEY.equalsIgnoreCase(key)) {
+                final Map<Level, String> enumMap = STYLES.get(value.toUpperCase(Locale.ENGLISH));
+                if (enumMap == null) {
+                    LOGGER.error("Unknown level style: " + value + ". Use one of "
+                            + Arrays.toString(STYLES.keySet().toArray()));
+                } else {
+                    levelStyles.putAll(enumMap);
+                }
+            } else {
+                final Level level = Level.toLevel(key);
+                if (level == null) {
+                    LOGGER.error("Unknown level name: " + key + ". Use one of "
+                            + Arrays.toString(DEFAULT_STYLES.keySet().toArray()));
+                } else {
+                    levelStyles.put(level, value);
+                }
+            }
+        }
+        return levelStyles;
+    }
+
+    /**
+     * Gets an instance of the class.
+     *
+     * @param config The current Configuration.
+     * @param options pattern options, may be null. If first element is "short", only the first line of the throwable
+     *            will be formatted.
+     * @return instance of class.
+     */
+    public static HighlightConverter newInstance(final Configuration config, final String[] options,
+            final FormattingInfo formattingInfo) {
+        if (options.length < 1) {
+            LOGGER.error("Incorrect number of options on style. Expected at least 1, received " + options.length);
+            return null;
+        }
+        if (options[0] == null) {
+            LOGGER.error("No pattern supplied on style");
+            return null;
+        }
+        final PatternParser parser = PatternLayout.createPatternParser(config);
+        final List<PatternFormatter> formatters = parser.parse(options[0]);
+        return new HighlightConverter(formatters, createLevelStyleMap(options), formattingInfo);
+    }
+
+    private final Map<Level, String> levelStyles;
+
+    private final List<PatternFormatter> patternFormatters;
+
+    /**
+     * Construct the converter.
+     *
+     * @param patternFormatters The PatternFormatters to generate the text to manipulate.
+     */
+    private HighlightConverter(final List<PatternFormatter> patternFormatters, final Map<Level, String> levelStyles,
+            final FormattingInfo formattingInfo) {
+        super("style", "style", formattingInfo);
+        this.patternFormatters = patternFormatters;
+        this.levelStyles = levelStyles;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final TextBuffer toAppendTo) {
+        format0(event, toAppendTo, null);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
+        format0(event, toAppendTo, charset);
+    }
+
+    private void format0(final LogEvent event, final Buffer toAppendTo, final Charset charset) {
+        StyleConverter.formatNested(event, toAppendTo, charset, false, patternFormatters,
+                levelStyles.get(event.getLevel()), AnsiEscape.getDefaultStyle());
+    }
+
+    @Override
+    public boolean handlesThrowable() {
+        for (final PatternFormatter formatter : patternFormatters) {
+            if (formatter.handlesThrowable()) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    @Override
+    public void setCharset(final Charset charset) {
+        super.setCharset(charset);
+        for (PatternFormatter paf : patternFormatters) {
+            paf.getConverter().setCharset(charset);
+        }
+    }
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/IntegerPatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/IntegerPatternConverter.java
index 5380d45650..c352cac051 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/IntegerPatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/IntegerPatternConverter.java
@@ -20,24 +20,18 @@ import java.util.Date;
 
 import org.apache.logging.log4j.core.config.plugins.Plugin;
 
-
 /**
  * Formats an integer.
  */
 @Plugin(name = "IntegerPatternConverter", category = "FileConverter")
 @ConverterKeys({ "i", "index" })
 public final class IntegerPatternConverter extends AbstractPatternConverter implements ArrayPatternConverter {
-    
-    /**
-     * Singleton.
-     */
-    private static final IntegerPatternConverter INSTANCE = new IntegerPatternConverter();
 
     /**
      * Private constructor.
      */
-    private IntegerPatternConverter() {
-        super("Integer", "integer");
+    private IntegerPatternConverter(final FormattingInfo formattingInfo) {
+        super("Integer", "integer", formattingInfo);
     }
 
     /**
@@ -46,13 +40,12 @@ public final class IntegerPatternConverter extends AbstractPatternConverter impl
      * @param options options, may be null.
      * @return instance of pattern converter.
      */
-    public static IntegerPatternConverter newInstance(
-        final String[] options) {
-        return INSTANCE;
+    public static IntegerPatternConverter newInstance(final String[] options, final FormattingInfo formattingInfo) {
+        return new IntegerPatternConverter(formattingInfo);
     }
 
     @Override
-    public void format(final StringBuilder toAppendTo, final Object... objects) {
+    public void format(final Buffer toAppendTo, final Object... objects) {
         for (final Object obj : objects) {
             if (obj instanceof Integer) {
                 format(obj, toAppendTo);
@@ -65,7 +58,7 @@ public final class IntegerPatternConverter extends AbstractPatternConverter impl
      * {@inheritDoc}
      */
     @Override
-    public void format(final Object obj, final StringBuilder toAppendTo) {
+    public void format(final Object obj, final Buffer toAppendTo) {
         if (obj instanceof Integer) {
             toAppendTo.append(obj.toString());
         } else if (obj instanceof Date) {
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LevelPatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LevelPatternConverter.java
index 1975fcf1f8..7d31fc889d 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LevelPatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LevelPatternConverter.java
@@ -16,6 +16,7 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
@@ -23,6 +24,7 @@ import java.util.Map;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.util.Charsets;
 import org.apache.logging.log4j.core.util.Patterns;
 
 /**
@@ -34,33 +36,29 @@ public final class LevelPatternConverter extends LogEventPatternConverter {
     private static final String OPTION_LENGTH = "length";
     private static final String OPTION_LOWER = "lowerCase";
 
-    /**
-     * Singleton.
-     */
-    private static final LevelPatternConverter INSTANCE = new LevelPatternConverter(null);
-
-    private final Map<Level, String> levelMap;
+    private final Map<Level, String> levelTextMap;
+    private final Map<Level, byte[]> levelBinaryMap = new HashMap<Level, byte[]>();
 
     /**
      * Private constructor.
      */
-    private LevelPatternConverter(final Map<Level, String> map) {
-        super("Level", "level");
-        this.levelMap = map;
+    private LevelPatternConverter(final Map<Level, String> map, final FormattingInfo formattingInfo) {
+        super("Level", "level", formattingInfo);
+        this.levelTextMap = map;
     }
 
     /**
      * Obtains an instance of pattern converter.
      *
-     * @param options
-     *            options, may be null. May contain a list of level names and The value that should be displayed for the
-     *            Level.
+     * @param options options, may be null. May contain a list of level names and The value that should be displayed for
+     *            the Level.
      * @return instance of pattern converter.
      */
-    public static LevelPatternConverter newInstance(final String[] options) {
+    public static LevelPatternConverter newInstance(final String[] options, final FormattingInfo formattingInfo) {
         if (options == null || options.length == 0) {
-            return INSTANCE;
+            return createDefaultLevelPatternConverter(formattingInfo);
         }
+        final StringBuilder sb = new StringBuilder(64);
         final Map<Level, String> levelMap = new HashMap<Level, String>();
         int length = Integer.MAX_VALUE; // More than the longest level name.
         boolean lowerCase = false;
@@ -82,29 +80,63 @@ public final class LevelPatternConverter extends LogEventPatternConverter {
                 if (level == null) {
                     LOGGER.error("Invalid Level {}", key);
                 } else {
-                    levelMap.put(level, value);
+                    levelMap.put(level, format(formattingInfo, sb, value));
                 }
             }
         }
         if (levelMap.isEmpty() && length == Integer.MAX_VALUE && !lowerCase) {
-            return INSTANCE;
+            return createDefaultLevelPatternConverter(formattingInfo);
         }
         for (final Level level : Level.values()) {
             if (!levelMap.containsKey(level)) {
                 final String left = left(level, length);
-                levelMap.put(level, lowerCase ? left.toLowerCase(Locale.US) : left);
+                final String value = lowerCase ? left.toLowerCase(Locale.US) : left;
+                levelMap.put(level, format(formattingInfo, sb, value));
             }
         }
-        return new LevelPatternConverter(levelMap);
+        return new LevelPatternConverter(levelMap, formattingInfo);
+    }
+
+    private static LevelPatternConverter createDefaultLevelPatternConverter(final FormattingInfo formattingInfo) {
+        final FormattingInfo fi = formattingInfo;
+        return new LevelPatternConverter(createTextLevelMap(fi), fi);
+    }
+
+    private static Map<Level, String> createTextLevelMap(final FormattingInfo formattingInfo) {
+        if (formattingInfo == null || formattingInfo == FormattingInfo.getDefault()) {
+            return null; // indicates no formatting
+        }
+        final Map<Level, String> result = new HashMap<Level, String>();
+        final StringBuilder sb = new StringBuilder(64);
+        for (final Level level : Level.values()) {
+            final String formatted = format(formattingInfo, sb, level.name());
+            result.put(level, formatted);
+        }
+        return result;
+    }    
+
+    private static Map<Level, byte[]> createBinaryLevelMap(final FormattingInfo formattingInfo, final Charset charset) {
+        final Map<Level, byte[]> result = new HashMap<Level, byte[]>();
+        final StringBuilder sb = new StringBuilder(64);
+        for (final Level level : Level.values()) {
+            final String formatted = format(formattingInfo, sb, level.name());
+            result.put(level, Charsets.getBytes(formatted, charset));
+        }
+        return result;
+    }
+
+    private static String format(final FormattingInfo formattingInfo, final StringBuilder sb, final String raw) {
+        sb.setLength(0);
+        sb.append(raw);
+        formattingInfo.format(0, sb);
+        return sb.toString();
     }
 
     /**
      * Returns the leftmost chars of the level name for the given level.
      *
-     * @param level
-     *            The level
-     * @param length
-     *            How many chars to return
+     * @param level The level
+     * @param length How many chars to return
      * @return The abbreviated level name, or the whole level name if the {@code length} is greater than the level name
      *         length,
      */
@@ -115,13 +147,34 @@ public final class LevelPatternConverter extends LogEventPatternConverter {
         }
         return string.substring(0, length);
     }
+    
+    @Override
+    public void setCharset(final Charset charset) {
+        super.setCharset(charset);
+        if (this.levelTextMap == null) {
+            this.levelBinaryMap.putAll(createBinaryLevelMap(getFormattingInfo(), charset));
+        } else {
+            for (final Level level : levelTextMap.keySet()) {
+                final String value = levelTextMap.get(level);
+                levelBinaryMap.put(level, Charsets.getBytes(value, charset));
+            }
+        }
+    }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder output) {
-        output.append(levelMap == null ? event.getLevel().toString() : levelMap.get(event.getLevel()));
+    public void format(final LogEvent event, final TextBuffer output) {
+        output.append(levelTextMap == null ? event.getLevel().toString() : levelTextMap.get(event.getLevel()));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final BinaryBuffer output, final Charset charset) {
+        output.append(levelBinaryMap.get(event.getLevel()));
     }
 
     /**
@@ -132,7 +185,6 @@ public final class LevelPatternConverter extends LogEventPatternConverter {
         if (e instanceof LogEvent) {
             return "level " + ((LogEvent) e).getLevel().name().toLowerCase(Locale.ENGLISH);
         }
-
         return "level";
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LineLocationPatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LineLocationPatternConverter.java
index 2b912d7f54..64ac2c93c5 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LineLocationPatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LineLocationPatternConverter.java
@@ -16,6 +16,8 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
+
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
 
@@ -25,17 +27,12 @@ import org.apache.logging.log4j.core.config.plugins.Plugin;
 @Plugin(name = "LineLocationPatternConverter", category = PatternConverter.CATEGORY)
 @ConverterKeys({ "L", "line" })
 public final class LineLocationPatternConverter extends LogEventPatternConverter {
-    /**
-     * Singleton.
-     */
-    private static final LineLocationPatternConverter INSTANCE =
-        new LineLocationPatternConverter();
 
     /**
      * Private constructor.
      */
-    private LineLocationPatternConverter() {
-        super("Line", "line");
+    private LineLocationPatternConverter(final FormattingInfo formattingInfo) {
+        super("Line", "line", formattingInfo);
     }
 
     /**
@@ -44,20 +41,42 @@ public final class LineLocationPatternConverter extends LogEventPatternConverter
      * @param options options, may be null.
      * @return instance of pattern converter.
      */
-    public static LineLocationPatternConverter newInstance(
-        final String[] options) {
-        return INSTANCE;
+    public static LineLocationPatternConverter newInstance(final String[] options, final FormattingInfo formattingInfo) {
+        return new LineLocationPatternConverter(formattingInfo);
+    }
+
+    @Override
+    public String convert(final Object element) {
+        return String.valueOf(((StackTraceElement) element).getLineNumber());
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder output) {
+    public void format(final LogEvent event, final TextBuffer output) {
         final StackTraceElement element = event.getSource();
+        if (element != null) {
+            if (hasFormattingInfo) {
+                output.append(getCachedFormattedString(element));
+            } else {
+                output.append(element.getLineNumber());
+            }
+        }
+    }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
+        final StackTraceElement element = event.getSource();
         if (element != null) {
-            output.append(element.getLineNumber());
+            if (hasFormattingInfo) {
+                toAppendTo.append(getCachedFormattedBytes(element, charset));
+            } else {
+                toAppendTo.append(element.getLineNumber());
+            }
         }
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LineSeparatorPatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LineSeparatorPatternConverter.java
index 6d41bf9131..6886a889da 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LineSeparatorPatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LineSeparatorPatternConverter.java
@@ -16,6 +16,8 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
+
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
 import org.apache.logging.log4j.core.util.Constants;
@@ -27,40 +29,46 @@ import org.apache.logging.log4j.core.util.Constants;
 @ConverterKeys({ "n" })
 public final class LineSeparatorPatternConverter extends LogEventPatternConverter {
 
-    /**
-     * Singleton.
-     */
-    private static final LineSeparatorPatternConverter INSTANCE = new LineSeparatorPatternConverter();
-
     /**
      * Line separator.
      */
     private final String lineSep;
+    private final byte[] lineSepBytes;
 
     /**
      * Private constructor.
      */
-    private LineSeparatorPatternConverter() {
-        super("Line Sep", "lineSep");
+    private LineSeparatorPatternConverter(final FormattingInfo formattingInfo) {
+        super("Line Sep", "lineSep", formattingInfo);
         lineSep = Constants.LINE_SEPARATOR;
+        lineSepBytes = lineSep.getBytes(); // Charset does not matter for this particular string
     }
 
     /**
      * Obtains an instance of pattern converter.
      *
-     * @param options
-     *        options, may be null.
+     * @param options options, may be null.
      * @return instance of pattern converter.
      */
-    public static LineSeparatorPatternConverter newInstance(final String[] options) {
-        return INSTANCE;
+    public static LineSeparatorPatternConverter newInstance(final String[] options, final FormattingInfo formattingInfo) {
+        return new LineSeparatorPatternConverter(formattingInfo);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder toAppendTo) {
+    public void format(final LogEvent event, final TextBuffer toAppendTo) {
+        // note: this converter ignores FormattingInfo on purpose
         toAppendTo.append(lineSep);
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
+        // note: this converter ignores FormattingInfo on purpose
+        toAppendTo.append(lineSepBytes);
+    }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LiteralPatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LiteralPatternConverter.java
index 64c41fe3fe..8c3407cd54 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LiteralPatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LiteralPatternConverter.java
@@ -16,11 +16,13 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
+
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.util.Charsets;
 import org.apache.logging.log4j.core.util.OptionConverter;
 
-
 /**
  * Formats a string literal.
  */
@@ -29,9 +31,8 @@ public final class LiteralPatternConverter extends LogEventPatternConverter impl
      * String literal.
      */
     private final String literal;
-
+    private byte[] cachedLiteralBytes;
     private final Configuration config;
-
     private final boolean substitute;
 
     /**
@@ -43,7 +44,7 @@ public final class LiteralPatternConverter extends LogEventPatternConverter impl
      *            sequences like "\" followed by "t" (backslash+t) are converted to special characters like '\t' (tab).
      */
     public LiteralPatternConverter(final Configuration config, final String literal, final boolean convertBackslashes) {
-        super("Literal", "literal");
+        super("Literal", "literal", FormattingInfo.getDefault());
         this.literal = convertBackslashes ? OptionConverter.convertSpecialChars(literal) : literal; // LOG4J2-829
         this.config = config;
         substitute = config != null && literal.contains("${");
@@ -53,15 +54,36 @@ public final class LiteralPatternConverter extends LogEventPatternConverter impl
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder toAppendTo) {
+    public void format(final LogEvent event, final TextBuffer toAppendTo) {
+        // note: this converter ignores FormattingInfo on purpose
         toAppendTo.append(substitute ? config.getStrSubstitutor().replace(event, literal) : literal);
     }
-    
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
+        // note: this converter ignores FormattingInfo on purpose
+        if (substitute) { // cannot use cache...
+            toAppendTo.append(config.getStrSubstitutor().replace(event, literal));
+        } else {
+            toAppendTo.append(cachedLiteral(charset));
+        }
+    }
+
+    private byte[] cachedLiteral(Charset charset) {
+        if (cachedLiteralBytes == null) {
+            cachedLiteralBytes = Charsets.getBytes(literal, charset);
+        }
+        return cachedLiteralBytes;
+    }
+
     /**
      * {@inheritDoc}
      */
     @Override
-    public void format(final Object obj, final StringBuilder output) {
+    public void format(final Object obj, final Buffer output) {
         output.append(substitute ? config.getStrSubstitutor().replace(literal) : literal);
     }
 
@@ -69,7 +91,7 @@ public final class LiteralPatternConverter extends LogEventPatternConverter impl
      * {@inheritDoc}
      */
     @Override
-    public void format(final StringBuilder output, final Object... objects) {
+    public void format(final Buffer output, final Object... objects) {
         output.append(substitute ? config.getStrSubstitutor().replace(literal) : literal);
     }
 
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LogEventPatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LogEventPatternConverter.java
index d42f300a9f..a820b44c00 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LogEventPatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LogEventPatternConverter.java
@@ -1,67 +1,84 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.core.pattern;
-
-import org.apache.logging.log4j.core.LogEvent;
-
-/**
- * LoggingEventPatternConverter is a base class for pattern converters
- * that can format information from instances of LoggingEvent.
- */
-public abstract class LogEventPatternConverter extends AbstractPatternConverter {
-
-    /**
-     * Constructs an instance of LoggingEventPatternConverter.
-     *
-     * @param name  name of converter.
-     * @param style CSS style for output.
-     */
-    protected LogEventPatternConverter(final String name, final String style) {
-        super(name, style);
-    }
-
-    /**
-     * Formats an event into a string buffer.
-     *
-     * @param event      event to format, may not be null.
-     * @param toAppendTo string buffer to which the formatted event will be appended.  May not be null.
-     */
-    public abstract void format(final LogEvent event, final StringBuilder toAppendTo);
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void format(final Object obj, final StringBuilder output) {
-        if (obj instanceof LogEvent) {
-            format((LogEvent) obj, output);
-        }
-    }
-
-    /**
-     * Normally pattern converters are not meant to handle Exceptions although few pattern converters might.
-     * <p>
-     * By examining the return values for this method, the containing layout will determine whether it handles
-     * throwables or not.
-     * </p>
-     *
-     * @return true if this PatternConverter handles throwables
-     */
-    public boolean handlesThrowable() {
-        return false;
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.pattern;
+
+import java.nio.charset.Charset;
+
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.util.Assert;
+
+/**
+ * LogEventPatternConverter is a base class for pattern converters that can format information from instances of
+ * LoggingEvent.
+ */
+public abstract class LogEventPatternConverter extends AbstractPatternConverter {
+
+    /**
+     * Constructs an instance of LogEventPatternConverter.
+     *
+     * @param name name of converter.
+     * @param style CSS style for output.
+     * @param formattingInfo the formatting info (must be non-{@code null})
+     */
+    protected LogEventPatternConverter(final String name, final String style, final FormattingInfo formattingInfo) {
+        super(name, style, Assert.requireNonNull(formattingInfo, "formattingInfo"));
+    }
+
+    /**
+     * Formats an event into the specified text buffer.
+     *
+     * @param event event to format, may not be null.
+     * @param toAppendTo text buffer to which the formatted event will be appended. May not be null.
+     */
+    public abstract void format(final LogEvent event, final TextBuffer toAppendTo);
+
+    /**
+     * Formats an event into the specified binary buffer.
+     *
+     * @param event event to format, may not be null.
+     * @param toAppendTo binary buffer to which the formatted event will be appended. May not be null.
+     * @param charset the Charset to use when converting text to bytes
+     */
+    public abstract void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset);
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final Object obj, final Buffer output) {
+        if (obj instanceof LogEvent) {
+            if (output instanceof TextBuffer) {
+                format((LogEvent) obj, (TextBuffer) output);
+            } else {
+                format((LogEvent) obj, (BinaryBuffer) output, getCharset());
+            }
+        }
+    }
+
+    /**
+     * Normally pattern converters are not meant to handle Exceptions although few pattern converters might.
+     * <p>
+     * By examining the return values for this method, the containing layout will determine whether it handles
+     * throwables or not.
+     * </p>
+     *
+     * @return true if this PatternConverter handles throwables
+     */
+    public boolean handlesThrowable() {
+        return false;
+    }
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LoggerPatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LoggerPatternConverter.java
index 2b5651b6f8..5c4c90e131 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LoggerPatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/LoggerPatternConverter.java
@@ -16,29 +16,25 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
+
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
 
-
 /**
  * Formats a logger name.
  */
 @Plugin(name = "LoggerPatternConverter", category = PatternConverter.CATEGORY)
 @ConverterKeys({ "c", "logger" })
 public final class LoggerPatternConverter extends NamePatternConverter {
-    /**
-     * Singleton.
-     */
-    private static final LoggerPatternConverter INSTANCE =
-        new LoggerPatternConverter(null);
 
     /**
      * Private constructor.
      *
      * @param options options, may be null.
      */
-    private LoggerPatternConverter(final String[] options) {
-        super("Logger", "logger", options);
+    private LoggerPatternConverter(final String[] options, final FormattingInfo formattingInfo) {
+        super("Logger", "logger", options, formattingInfo);
     }
 
     /**
@@ -47,20 +43,30 @@ public final class LoggerPatternConverter extends NamePatternConverter {
      * @param options options, may be null.
      * @return instance of pattern converter.
      */
-    public static LoggerPatternConverter newInstance(
-        final String[] options) {
+    public static LoggerPatternConverter newInstance(final String[] options, final FormattingInfo formattingInfo) {
         if (options == null || options.length == 0) {
-            return INSTANCE;
+            return new LoggerPatternConverter(null, formattingInfo);
         }
 
-        return new LoggerPatternConverter(options);
+        return new LoggerPatternConverter(options, formattingInfo);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder toAppendTo) {
+    public void format(final LogEvent event, final TextBuffer toAppendTo) {
         toAppendTo.append(abbreviate(event.getLoggerName()));
     }
+
+    /**
+     * Format a logging event.
+     *
+     * @param event event to format.
+     * @param toAppendTo buffer to which class name will be appended.
+     */
+    @Override
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
+        toAppendTo.append(abbreviateToBinary(event.getLoggerName(), charset));
+    }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MapPatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MapPatternConverter.java
index e75181bf0a..ed7a1934a5 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MapPatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MapPatternConverter.java
@@ -16,19 +16,16 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
 import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
 
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
 import org.apache.logging.log4j.message.MapMessage;
 
 /**
- * Able to handle the contents of the LogEvent's MapMessage and either
- * output the entire contents of the properties in a similar format to the
- * java.util.Hashtable.toString(), or to output the value of a specific key
- * within the Map.
+ * Able to handle the contents of the LogEvent's MapMessage and either output the entire contents of the properties in a
+ * similar format to the java.util.Hashtable.toString(), or to output the value of a specific key within the Map.
  */
 @Plugin(name = "MapPatternConverter", category = PatternConverter.CATEGORY)
 @ConverterKeys({ "K", "map", "MAP" })
@@ -43,8 +40,8 @@ public final class MapPatternConverter extends LogEventPatternConverter {
      *
      * @param options options, may be null.
      */
-    private MapPatternConverter(final String[] options) {
-        super(options != null && options.length > 0 ? "MAP{" + options[0] + '}' : "MAP", "map");
+    private MapPatternConverter(final String[] options, final FormattingInfo formattingInfo) {
+        super((options != null && options.length > 0) ? "MAP{" + options[0] + '}' : "MAP", "map", formattingInfo);
         key = options != null && options.length > 0 ? options[0] : null;
     }
 
@@ -54,46 +51,50 @@ public final class MapPatternConverter extends LogEventPatternConverter {
      * @param options options, may be null or first element contains name of property to format.
      * @return instance of PropertiesPatternConverter.
      */
-    public static MapPatternConverter newInstance(final String[] options) {
-        return new MapPatternConverter(options);
+    public static MapPatternConverter newInstance(final String[] options, final FormattingInfo formattingInfo) {
+        return new MapPatternConverter(options, formattingInfo);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder toAppendTo) {
-        MapMessage msg;
-        if (event.getMessage() instanceof MapMessage) {
-            msg = (MapMessage) event.getMessage();
-        } else {
+    public void format(final LogEvent event, final TextBuffer toAppendTo) {
+        if (!(event.getMessage() instanceof MapMessage)) {
             return;
         }
-        final Map<String, String> map = msg.getData();
+        final Map<String, String> map = ((MapMessage) event.getMessage()).getData();
         // if there is no additional options, we output every single
         // Key/Value pair for the Map in a similar format to Hashtable.toString()
         if (key == null) {
-            if (map.isEmpty()) {
-                toAppendTo.append("{}");
-                return;
+            toAppendTo.append(getCachedFormattedString(map));
+        } else {
+            // otherwise they just want a single key output
+            final String val = map.get(key);
+            if (val != null) {
+                toAppendTo.append(getCachedFormattedString(val));
             }
-            final StringBuilder sb = new StringBuilder("{");
-            final Set<String> keys = new TreeSet<String>(map.keySet());
-            for (final String key : keys) {
-                if (sb.length() > 1) {
-                    sb.append(", ");
-                }
-                sb.append(key).append('=').append(map.get(key));
+        }
+    }
 
-            }
-            sb.append('}');
-            toAppendTo.append(sb);
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
+        if (!(event.getMessage() instanceof MapMessage)) {
+            return;
+        }
+        final Map<String, String> map = ((MapMessage) event.getMessage()).getData();
+        // if there is no additional options, we output every single
+        // Key/Value pair for the Map in a similar format to Hashtable.toString()
+        if (key == null) {
+            toAppendTo.append(getCachedFormattedBytes(map, charset));
         } else {
             // otherwise they just want a single key output
             final String val = map.get(key);
-
             if (val != null) {
-                toAppendTo.append(val);
+                toAppendTo.append(getCachedFormattedBytes(val, charset));
             }
         }
     }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MarkerPatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MarkerPatternConverter.java
index 020f832023..d2d48ed2e9 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MarkerPatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MarkerPatternConverter.java
@@ -16,6 +16,8 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
+
 import org.apache.logging.log4j.Marker;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
@@ -29,10 +31,11 @@ public final class MarkerPatternConverter extends LogEventPatternConverter {
 
     /**
      * Private constructor.
+     * 
      * @param options options, may be null.
      */
-    private MarkerPatternConverter(final String[] options) {
-        super("Marker", "marker");
+    private MarkerPatternConverter(final String[] options, final FormattingInfo formattingInfo) {
+        super("Marker", "marker", formattingInfo);
     }
 
     /**
@@ -41,18 +44,29 @@ public final class MarkerPatternConverter extends LogEventPatternConverter {
      * @param options options, may be null.
      * @return instance of pattern converter.
      */
-    public static MarkerPatternConverter newInstance(final String[] options) {
-        return new MarkerPatternConverter(options);
+    public static MarkerPatternConverter newInstance(final String[] options, final FormattingInfo formattingInfo) {
+        return new MarkerPatternConverter(options, formattingInfo);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final TextBuffer toAppendTo) {
+        final Marker marker = event.getMarker();
+        if (marker != null) {
+            toAppendTo.append(getCachedFormattedString(marker));
+        }
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder toAppendTo) {
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
         final Marker marker = event.getMarker();
         if (marker != null) {
-            toAppendTo.append(marker.toString());
+            toAppendTo.append(getCachedFormattedBytes(marker, charset));
         }
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MdcPatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MdcPatternConverter.java
index 665ce05c8f..8fa5b53e9c 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MdcPatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MdcPatternConverter.java
@@ -16,21 +16,21 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
 
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.message.MapMessage;
 
 /**
- * Able to handle the contents of the LogEvent's MDC and either
- * output the entire contents of the properties in a similar format to the
- * java.util.Hashtable.toString(), or to output the value of a specific key
- * within the property bundle
- * when this pattern converter has the option set.
+ * Able to handle the contents of the LogEvent's MDC and either output the entire contents of the properties in a
+ * similar format to the java.util.Hashtable.toString(), or to output the value of a specific key within the property
+ * bundle when this pattern converter has the option set.
  */
- @Plugin(name = "MdcPatternConverter", category = PatternConverter.CATEGORY)
+@Plugin(name = "MdcPatternConverter", category = PatternConverter.CATEGORY)
 @ConverterKeys({ "X", "mdc", "MDC" })
 public final class MdcPatternConverter extends LogEventPatternConverter {
     /**
@@ -43,8 +43,8 @@ public final class MdcPatternConverter extends LogEventPatternConverter {
      *
      * @param options options, may be null.
      */
-    private MdcPatternConverter(final String[] options) {
-        super(options != null && options.length > 0 ? "MDC{" + options[0] + '}' : "MDC", "mdc");
+    private MdcPatternConverter(final String[] options, final FormattingInfo formattingInfo) {
+        super((options != null && options.length > 0) ? "MDC{" + options[0] + '}' : "MDC", "mdc", formattingInfo);
         key = options != null && options.length > 0 ? options[0] : null;
     }
 
@@ -54,42 +54,44 @@ public final class MdcPatternConverter extends LogEventPatternConverter {
      * @param options options, may be null or first element contains name of property to format.
      * @return instance of PropertiesPatternConverter.
      */
-    public static MdcPatternConverter newInstance(final String[] options) {
-        return new MdcPatternConverter(options);
+    public static MdcPatternConverter newInstance(final String[] options, final FormattingInfo formattingInfo) {
+        return new MdcPatternConverter(options, formattingInfo);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder toAppendTo) {
-        final Map<String, String> contextMap = event.getContextMap();
+    public void format(final LogEvent event, final TextBuffer toAppendTo) {
+        final Map<String, String> map = event.getContextMap();
         // if there is no additional options, we output every single
-        // Key/Value pair for the MDC in a similar format to Hashtable.toString()
+        // Key/Value pair for the Map in a similar format to Hashtable.toString()
         if (key == null) {
-
-
-            if (contextMap == null || contextMap.isEmpty()) {
-                toAppendTo.append("{}");
-                return;
+            toAppendTo.append(getCachedFormattedString(map));
+        } else {
+            // otherwise they just want a single key output
+            final String val = map.get(key);
+            if (val != null) {
+                toAppendTo.append(getCachedFormattedString(val));
             }
-            final StringBuilder sb = new StringBuilder("{");
-            final Set<String> keys = new TreeSet<String>(contextMap.keySet());
-            for (final String key : keys) {
-                if (sb.length() > 1) {
-                    sb.append(", ");
-                }
-                sb.append(key).append('=').append(contextMap.get(key));
+        }
+    }
 
-            }
-            sb.append('}');
-            toAppendTo.append(sb);
-        } else if (contextMap != null) {
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
+        final Map<String, String> map = event.getContextMap();
+        // if there is no additional options, we output every single
+        // Key/Value pair for the Map in a similar format to Hashtable.toString()
+        if (key == null) {
+            toAppendTo.append(getCachedFormattedBytes(map, charset));
+        } else {
             // otherwise they just want a single key output
-            final Object val = contextMap.get(key);
-
+            final String val = map.get(key);
             if (val != null) {
-                toAppendTo.append(val);
+                toAppendTo.append(getCachedFormattedBytes(val, charset));
             }
         }
     }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MessagePatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MessagePatternConverter.java
index d0adc7f179..33531eea9f 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MessagePatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MessagePatternConverter.java
@@ -16,9 +16,12 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
+
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.util.Charsets;
 import org.apache.logging.log4j.message.Message;
 import org.apache.logging.log4j.message.MultiformatMessage;
 
@@ -35,10 +38,12 @@ public final class MessagePatternConverter extends LogEventPatternConverter {
 
     /**
      * Private constructor.
+     * 
      * @param options options, may be null.
      */
-    private MessagePatternConverter(final Configuration config, final String[] options) {
-        super("Message", "message");
+    private MessagePatternConverter(final Configuration config, final String[] options,
+            final FormattingInfo formattingInfo) {
+        super("Message", "message", formattingInfo);
         formats = options;
         this.config = config;
     }
@@ -50,29 +55,49 @@ public final class MessagePatternConverter extends LogEventPatternConverter {
      * @param options options, may be null.
      * @return instance of pattern converter.
      */
-    public static MessagePatternConverter newInstance(final Configuration config, final String[] options) {
-        return new MessagePatternConverter(config, options);
+    public static MessagePatternConverter newInstance(final Configuration config, final String[] options,
+            final FormattingInfo formattingInfo) {
+        return new MessagePatternConverter(config, options, formattingInfo);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder toAppendTo) {
+    public void format(final LogEvent event, final TextBuffer toAppendTo) {
+        final Message msg = event.getMessage();
+        if (msg != null) {
+            final String msgString = messageString(event, msg);
+            toAppendTo.append(msgString, getFormattingInfo());
+        }
+    }
+
+    /**
+     * Format a logging event.
+     *
+     * @param event event to format.
+     * @param toAppendTo buffer to which class name will be appended.
+     */
+    @Override
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
         final Message msg = event.getMessage();
         if (msg != null) {
-            String result;
-            if (msg instanceof MultiformatMessage) {
-                result = ((MultiformatMessage) msg).getFormattedMessage(formats);
-            } else {
-                result = msg.getFormattedMessage();
-            }
-            if (result != null) {
-                toAppendTo.append(config != null && result.contains("${") ?
-                    config.getStrSubstitutor().replace(event, result) : result);
-            } else {
-                toAppendTo.append("null");
-            }
+            final String msgString = messageString(event, msg);
+            final String formatted = applyFormattingInfo(msgString);
+            toAppendTo.append(Charsets.getBytes(formatted, charset));
+        }
+    }
+
+    private String messageString(final LogEvent event, final Message msg) {
+        String result;
+        if (msg instanceof MultiformatMessage) {
+            result = ((MultiformatMessage) msg).getFormattedMessage(formats);
+        } else {
+            result = msg.getFormattedMessage();
+        }
+        if (result == null) {
+            return "null";
         }
+        return (config != null && result.contains("${")) ? config.getStrSubstitutor().replace(event, result) : result;
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MethodLocationPatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MethodLocationPatternConverter.java
index 457b7a68b6..9c2e6ff86d 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MethodLocationPatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MethodLocationPatternConverter.java
@@ -16,6 +16,8 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
+
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
 
@@ -26,17 +28,12 @@ import org.apache.logging.log4j.core.config.plugins.Plugin;
 @Plugin(name = "MethodLocationPatternConverter", category = PatternConverter.CATEGORY)
 @ConverterKeys({ "M", "method" })
 public final class MethodLocationPatternConverter extends LogEventPatternConverter {
-    /**
-     * Singleton.
-     */
-    private static final MethodLocationPatternConverter INSTANCE =
-        new MethodLocationPatternConverter();
 
     /**
      * Private constructor.
      */
-    private MethodLocationPatternConverter() {
-        super("Method", "method");
+    private MethodLocationPatternConverter(final FormattingInfo formattingInfo) {
+        super("Method", "method", formattingInfo);
     }
 
     /**
@@ -45,19 +42,29 @@ public final class MethodLocationPatternConverter extends LogEventPatternConvert
      * @param options options, may be null.
      * @return instance of MethodLocationPatternConverter.
      */
-    public static MethodLocationPatternConverter newInstance(final String[] options) {
-        return INSTANCE;
+    public static MethodLocationPatternConverter newInstance(final String[] options, final FormattingInfo formattingInfo) {
+        return new MethodLocationPatternConverter(formattingInfo);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder toAppendTo) {
+    public void format(final LogEvent event, final TextBuffer toAppendTo) {
         final StackTraceElement element = event.getSource();
+        if (element != null) {
+            toAppendTo.append(getCachedFormattedString(element.getMethodName()));
+        }
+    }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
+        final StackTraceElement element = event.getSource();
         if (element != null) {
-            toAppendTo.append(element.getMethodName());
+            toAppendTo.append(getCachedFormattedBytes(element.getMethodName(), charset));
         }
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/NamePatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/NamePatternConverter.java
index b35c787a21..635ea4ce9a 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/NamePatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/NamePatternConverter.java
@@ -16,6 +16,8 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
+
 
 /**
  * Abstract base class for other pattern converters which can return only parts of their name.
@@ -33,8 +35,8 @@ public abstract class NamePatternConverter extends LogEventPatternConverter {
      * @param style   style name for associated output.
      * @param options options, may be null, first element will be interpreted as an abbreviation pattern.
      */
-    protected NamePatternConverter(final String name, final String style, final String[] options) {
-        super(name, style);
+    protected NamePatternConverter(final String name, final String style, final String[] options, final FormattingInfo formattingInfo) {
+        super(name, style, formattingInfo);
 
         if (options != null && options.length > 0) {
             abbreviator = NameAbbreviator.getAbbreviator(options[0]);
@@ -42,14 +44,39 @@ public abstract class NamePatternConverter extends LogEventPatternConverter {
             abbreviator = NameAbbreviator.getDefaultAbbreviator();
         }
     }
+    
+    /**
+     * Abbreviates specified name.
+     *
+     * @param name the name to abbreviate.
+     * @return The abbreviated name.
+     */
+    @Override
+    protected String convert(final Object name) {
+        return abbreviator.abbreviate(name.toString());
+    }
 
     /**
-     * Abbreviate name in string buffer.
+     * Abbreviates specified name.
      *
-     * @param buf       string buffer containing name.
+     * @param name the name to abbreviate.
      * @return The abbreviated name.
      */
-    protected final String abbreviate(final String buf) {
-        return abbreviator.abbreviate(buf);
+    protected final String abbreviate(final String name) {
+        if (abbreviator == NameAbbreviator.getDefaultAbbreviator() && !hasFormattingInfo) {
+            return name; // no formatting to do
+        }
+        return getCachedFormattedString(name);
+    }
+
+    /**
+     * Abbreviates specified name and returns the encoded result.
+     *
+     * @param name the name to abbreviate.
+     * @param charset the charset to use when converting text to bytes
+     * @return The abbreviated name as a byte array.
+     */
+    protected final byte[] abbreviateToBinary(final String name, final Charset charset) {
+        return getCachedFormattedBytes(name, charset);
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/NdcPatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/NdcPatternConverter.java
index 9a3b97217f..ec3b0a40a6 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/NdcPatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/NdcPatternConverter.java
@@ -16,43 +16,48 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
+
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
 
-
 /**
  * Returns the event's NDC in a StringBuilder.
  */
 @Plugin(name = "NdcPatternConverter", category = PatternConverter.CATEGORY)
 @ConverterKeys({ "x", "NDC" })
 public final class NdcPatternConverter extends LogEventPatternConverter {
-  /**
-   *   Singleton.
-   */
-  private static final NdcPatternConverter INSTANCE =
-    new NdcPatternConverter();
 
-  /**
-   * Private constructor.
-   */
-  private NdcPatternConverter() {
-    super("NDC", "ndc");
-  }
+    /**
+     * Private constructor.
+     */
+    private NdcPatternConverter(final FormattingInfo formattingInfo) {
+        super("NDC", "ndc", formattingInfo);
+    }
+
+    /**
+     * Obtains an instance of NdcPatternConverter.
+     * 
+     * @param options options, may be null.
+     * @return instance of NdcPatternConverter.
+     */
+    public static NdcPatternConverter newInstance(final String[] options, final FormattingInfo formattingInfo) {
+        return new NdcPatternConverter(formattingInfo);
+    }
 
-  /**
-   * Obtains an instance of NdcPatternConverter.
-   * @param options options, may be null.
-   * @return instance of NdcPatternConverter.
-   */
-  public static NdcPatternConverter newInstance(final String[] options) {
-    return INSTANCE;
-  }
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final TextBuffer toAppendTo) {
+        toAppendTo.append(event.getContextStack());
+    }
 
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-public void format(final LogEvent event, final StringBuilder toAppendTo) {
-    toAppendTo.append(event.getContextStack());
-  }
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
+        toAppendTo.append(event.getContextStack());
+    }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/RegexReplacementConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/RegexReplacementConverter.java
index f7c4697794..9049072802 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/RegexReplacementConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/RegexReplacementConverter.java
@@ -16,6 +16,7 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
 import java.util.List;
 import java.util.regex.Pattern;
 
@@ -39,13 +40,14 @@ public final class RegexReplacementConverter extends LogEventPatternConverter {
 
     /**
      * Construct the converter.
+     * 
      * @param formatters The PatternFormatters to generate the text to manipulate.
      * @param pattern The regular expression Pattern.
      * @param substitution The substitution string.
      */
-    private RegexReplacementConverter(final List<PatternFormatter> formatters,
-                                      final Pattern pattern, final String substitution) {
-        super("replace", "replace");
+    private RegexReplacementConverter(final List<PatternFormatter> formatters, final Pattern pattern,
+            final String substitution, final FormattingInfo formattingInfo) {
+        super("replace", "replace", formattingInfo);
         this.pattern = pattern;
         this.substitution = substitution;
         this.formatters = formatters;
@@ -55,11 +57,12 @@ public final class RegexReplacementConverter extends LogEventPatternConverter {
      * Gets an instance of the class.
      *
      * @param config The current Configuration.
-     * @param options pattern options, may be null.  If first element is "short",
-     *                only the first line of the throwable will be formatted.
+     * @param options pattern options, may be null. If first element is "short", only the first line of the throwable
+     *            will be formatted.
      * @return instance of class.
      */
-    public static RegexReplacementConverter newInstance(final Configuration config, final String[] options) {
+    public static RegexReplacementConverter newInstance(final Configuration config, final String[] options,
+            final FormattingInfo formattingInfo) {
         if (options.length != 3) {
             LOGGER.error("Incorrect number of options on replace. Expected 3 received " + options.length);
             return null;
@@ -79,19 +82,38 @@ public final class RegexReplacementConverter extends LogEventPatternConverter {
         final Pattern p = Pattern.compile(options[1]);
         final PatternParser parser = PatternLayout.createPatternParser(config);
         final List<PatternFormatter> formatters = parser.parse(options[0]);
-        return new RegexReplacementConverter(formatters, p, options[2]);
+        return new RegexReplacementConverter(formatters, p, options[2], formattingInfo);
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final TextBuffer toAppendTo) {
+        format0(event, toAppendTo);
+    }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder toAppendTo) {
-        final StringBuilder buf = new StringBuilder();
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
+        format0(event, toAppendTo);
+    }
+
+    private void format0(final LogEvent event, final Buffer toAppendTo) {
+        final TextBuffer buf = new TextBuffer();
         for (final PatternFormatter formatter : formatters) {
             formatter.format(event, buf);
         }
         toAppendTo.append(pattern.matcher(buf.toString()).replaceAll(substitution));
     }
+    
+    @Override
+    public void setCharset(final Charset charset) {
+        super.setCharset(charset);
+        for (PatternFormatter paf : formatters) {
+            paf.getConverter().setCharset(charset);
+        }
+    }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/RelativeTimePatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/RelativeTimePatternConverter.java
index 9767be1c0f..2b9c8f8654 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/RelativeTimePatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/RelativeTimePatternConverter.java
@@ -17,6 +17,7 @@
 package org.apache.logging.log4j.core.pattern;
 
 import java.lang.management.ManagementFactory;
+import java.nio.charset.Charset;
 
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
@@ -27,18 +28,13 @@ import org.apache.logging.log4j.core.config.plugins.Plugin;
 @Plugin(name = "RelativeTimePatternConverter", category = PatternConverter.CATEGORY)
 @ConverterKeys({ "r", "relative" })
 public class RelativeTimePatternConverter extends LogEventPatternConverter {
-    /**
-     * Cached formatted timestamp.
-     */
-    private long lastTimestamp = Long.MIN_VALUE;
     private final long startTime = ManagementFactory.getRuntimeMXBean().getStartTime();
-    private String relative;
 
     /**
      * Private constructor.
      */
-    public RelativeTimePatternConverter() {
-        super("Time", "time");
+    public RelativeTimePatternConverter(final FormattingInfo formattingInfo) {
+        super("Time", "time", formattingInfo);
     }
 
     /**
@@ -48,23 +44,31 @@ public class RelativeTimePatternConverter extends LogEventPatternConverter {
      * @return instance of RelativeTimePatternConverter.
      */
     public static RelativeTimePatternConverter newInstance(
-        final String[] options) {
-        return new RelativeTimePatternConverter();
+        final String[] options, final FormattingInfo formattingInfo) {
+        return new RelativeTimePatternConverter(formattingInfo);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder toAppendTo) {
-        final long timestamp = event.getTimeMillis();
+    public void format(final LogEvent event, final TextBuffer toAppendTo) {
+        handle(event, toAppendTo);
+    }
 
-        synchronized (this) {
-            if (timestamp != lastTimestamp) {
-                lastTimestamp = timestamp;
-                relative = Long.toString(timestamp - startTime);
-            }
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
+        handle(event, toAppendTo);
+    }
+    
+    private void handle(final LogEvent event, final Buffer toAppendTo) {
+        if (hasFormattingInfo) {
+            toAppendTo.append(event.getTimeMillis() - startTime, getFormattingInfo());
+        } else {
+            toAppendTo.append(event.getTimeMillis() - startTime);
         }
-        toAppendTo.append(relative);
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/RootThrowablePatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/RootThrowablePatternConverter.java
index e87d0783ef..ba89397f47 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/RootThrowablePatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/RootThrowablePatternConverter.java
@@ -16,6 +16,8 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
+
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
 import org.apache.logging.log4j.core.impl.Log4jLogEvent;
@@ -23,12 +25,11 @@ import org.apache.logging.log4j.core.impl.ThrowableProxy;
 import org.apache.logging.log4j.core.util.Constants;
 
 /**
- * Outputs the Throwable portion of the LoggingEvent as a full stacktrace
- * unless this converter's option is 'short', where it just outputs the first line of the trace, or if
- * the number of lines to print is explicitly specified.
+ * Outputs the Throwable portion of the LoggingEvent as a full stacktrace unless this converter's option is 'short',
+ * where it just outputs the first line of the trace, or if the number of lines to print is explicitly specified.
  * <p>
- * The extended stack trace will also include the location of where the class was loaded from and the
- * version of the jar if available.
+ * The extended stack trace will also include the location of where the class was loaded from and the version of the jar
+ * if available.
  */
 @Plugin(name = "RootThrowablePatternConverter", category = PatternConverter.CATEGORY)
 @ConverterKeys({ "rEx", "rThrowable", "rException" })
@@ -39,26 +40,38 @@ public final class RootThrowablePatternConverter extends ThrowablePatternConvert
      *
      * @param options options, may be null.
      */
-    private RootThrowablePatternConverter(final String[] options) {
-        super("RootThrowable", "throwable", options);
+    private RootThrowablePatternConverter(final String[] options, final FormattingInfo formattingInfo) {
+        super("RootThrowable", "throwable", options, formattingInfo);
     }
 
     /**
      * Gets an instance of the class.
      *
-     * @param options pattern options, may be null.  If first element is "short",
-     *                only the first line of the throwable will be formatted.
+     * @param options pattern options, may be null. If first element is "short", only the first line of the throwable
+     *            will be formatted.
      * @return instance of class.
      */
-    public static RootThrowablePatternConverter newInstance(final String[] options) {
-        return new RootThrowablePatternConverter(options);
+    public static RootThrowablePatternConverter newInstance(final String[] options, final FormattingInfo formattingInfo) {
+        return new RootThrowablePatternConverter(options, formattingInfo);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder toAppendTo) {
+    public void format(final LogEvent event, final TextBuffer toAppendTo) {
+        handle(event, toAppendTo);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
+        handle(event, toAppendTo);
+    }
+
+    private void handle(final LogEvent event, final Buffer toAppendTo) {
         ThrowableProxy proxy = null;
         if (event instanceof Log4jLogEvent) {
             proxy = ((Log4jLogEvent) event).getThrownProxy();
@@ -70,13 +83,12 @@ public final class RootThrowablePatternConverter extends ThrowablePatternConvert
                 return;
             }
             final String trace = proxy.getCauseStackTraceAsString(options.getPackages());
-            final int len = toAppendTo.length();
-            if (len > 0 && !Character.isWhitespace(toAppendTo.charAt(len - 1))) {
+            if (!toAppendTo.hasTrailingWhitespace()) {
                 toAppendTo.append(' ');
             }
             if (!options.allLines() || !Constants.LINE_SEPARATOR.equals(options.getSeparator())) {
-                final StringBuilder sb = new StringBuilder();
                 final String[] array = trace.split(Constants.LINE_SEPARATOR);
+                final StringBuilder sb = new StringBuilder(array.length << 7); // *128: estimate 128 chars per line
                 final int limit = options.minLines(array.length) - 1;
                 for (int i = 0; i <= limit; ++i) {
                     sb.append(array[i]);
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/SequenceNumberPatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/SequenceNumberPatternConverter.java
index 9e14c78d63..75d7d343aa 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/SequenceNumberPatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/SequenceNumberPatternConverter.java
@@ -16,12 +16,12 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
 import java.util.concurrent.atomic.AtomicLong;
 
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
 
-
 /**
  * Formats the event sequence number.
  */
@@ -31,17 +31,11 @@ public final class SequenceNumberPatternConverter extends LogEventPatternConvert
 
     private static final AtomicLong SEQUENCE = new AtomicLong();
 
-    /**
-     * Singleton.
-     */
-    private static final SequenceNumberPatternConverter INSTANCE =
-        new SequenceNumberPatternConverter();
-
     /**
      * Private constructor.
      */
-    private SequenceNumberPatternConverter() {
-        super("Sequence Number", "sn");
+    private SequenceNumberPatternConverter(final FormattingInfo formattingInfo) {
+        super("Sequence Number", "sn", formattingInfo);
     }
 
     /**
@@ -50,16 +44,31 @@ public final class SequenceNumberPatternConverter extends LogEventPatternConvert
      * @param options options, currently ignored, may be null.
      * @return instance of SequencePatternConverter.
      */
-    public static SequenceNumberPatternConverter newInstance(
-        final String[] options) {
-        return INSTANCE;
+    public static SequenceNumberPatternConverter newInstance(final String[] options, final FormattingInfo formattingInfo) {
+        return new SequenceNumberPatternConverter(formattingInfo);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final TextBuffer toAppendTo) {
+        if (hasFormattingInfo) {
+            toAppendTo.append(SEQUENCE.incrementAndGet(), getFormattingInfo());
+        } else {
+            toAppendTo.append(SEQUENCE.incrementAndGet());
+        }
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder toAppendTo) {
-        toAppendTo.append(Long.toString(SEQUENCE.incrementAndGet()));
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
+        if (hasFormattingInfo) {
+            toAppendTo.append(SEQUENCE.incrementAndGet(), getFormattingInfo());
+        } else {
+            toAppendTo.append(SEQUENCE.incrementAndGet());
+        }
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/StyleConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/StyleConverter.java
index 583be290f9..6cdc2a36b4 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/StyleConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/StyleConverter.java
@@ -16,6 +16,7 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
 import java.util.List;
 
 import org.apache.logging.log4j.core.LogEvent;
@@ -34,14 +35,13 @@ public final class StyleConverter extends LogEventPatternConverter implements An
     /**
      * Gets an instance of the class.
      *
-     * @param config
-     *            The current Configuration.
-     * @param options
-     *            pattern options, may be null. If first element is "short", only the first line of the throwable will
-     *            be formatted.
+     * @param config The current Configuration.
+     * @param options pattern options, may be null. If first element is "short", only the first line of the throwable
+     *            will be formatted.
      * @return instance of class.
      */
-    public static StyleConverter newInstance(final Configuration config, final String[] options) {
+    public static StyleConverter newInstance(final Configuration config, final String[] options,
+            final FormattingInfo formattingInfo) {
         if (options.length < 1) {
             LOGGER.error("Incorrect number of options on style. Expected at least 1, received " + options.length);
             return null;
@@ -60,7 +60,7 @@ public final class StyleConverter extends LogEventPatternConverter implements An
         final boolean noConsoleNoAnsi = options.length > 2
                 && (PatternParser.NO_CONSOLE_NO_ANSI + "=true").equals(options[2]);
         final boolean hideAnsi = noConsoleNoAnsi && System.console() == null;
-        return new StyleConverter(formatters, style, hideAnsi);
+        return new StyleConverter(formatters, style, hideAnsi, formattingInfo);
     }
 
     private final List<PatternFormatter> patternFormatters;
@@ -74,15 +74,13 @@ public final class StyleConverter extends LogEventPatternConverter implements An
     /**
      * Constructs the converter.
      *
-     * @param patternFormatters
-     *            The PatternFormatters to generate the text to manipulate.
-     * @param style
-     *            The style that should encapsulate the pattern.
-     * @param noAnsi
-     *            If true, do not output ANSI escape codes.
+     * @param patternFormatters The PatternFormatters to generate the text to manipulate.
+     * @param style The style that should encapsulate the pattern.
+     * @param noAnsi If true, do not output ANSI escape codes.
      */
-    private StyleConverter(final List<PatternFormatter> patternFormatters, final String style, final boolean noAnsi) {
-        super("style", "style");
+    private StyleConverter(final List<PatternFormatter> patternFormatters, final String style, final boolean noAnsi,
+            final FormattingInfo formattingInfo) {
+        super("style", "style", formattingInfo);
         this.patternFormatters = patternFormatters;
         this.style = style;
         this.defaultStyle = AnsiEscape.getDefaultStyle();
@@ -93,18 +91,50 @@ public final class StyleConverter extends LogEventPatternConverter implements An
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder toAppendTo) {
-        final StringBuilder buf = new StringBuilder();
-        for (final PatternFormatter formatter : patternFormatters) {
-            formatter.format(event, buf);
+    public void format(final LogEvent event, final TextBuffer toAppendTo) {
+        format0(event, toAppendTo, null);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
+        format0(event, toAppendTo, charset);
+    }
+
+    private void format0(final LogEvent event, final Buffer toAppendTo, final Charset charset) {
+        formatNested(event, toAppendTo, charset, noAnsi, patternFormatters, style, defaultStyle);
+    }
+
+    public static void formatNested(final LogEvent event, final Buffer toAppendTo, final Charset charset,
+            final boolean noAnsi, final List<PatternFormatter> nested, final String style, final String defaultStyle) {
+        if (noAnsi) {
+            appendNested(nested, event, toAppendTo, charset);
+            return;
+        }
+        final int startLengthForUndo = toAppendTo.length();
+        toAppendTo.append(style);
+        final int beforeAddingNested = toAppendTo.length();
+
+        appendNested(nested, event, toAppendTo, charset);
+
+        if (toAppendTo.length() == beforeAddingNested) {
+            toAppendTo.setLength(startLengthForUndo); // remove style
+        } else {
+            toAppendTo.append(defaultStyle);
         }
+    }
 
-        if (buf.length() > 0) {
-            if (noAnsi) {
-                // faster to test and do this than setting style and defaultStyle to empty strings.
-                toAppendTo.append(buf.toString());
-            } else {
-                toAppendTo.append(style).append(buf.toString()).append(defaultStyle);
+    private static void appendNested(final List<PatternFormatter> formatters, final LogEvent event,
+            final Buffer toAppendTo, final Charset charset) {
+        if (toAppendTo instanceof TextBuffer) {
+            for (final PatternFormatter formatter : formatters) {
+                formatter.format(event, (TextBuffer) toAppendTo);
+            }
+        } else {
+            for (final PatternFormatter formatter : formatters) {
+                formatter.format(event, (BinaryBuffer) toAppendTo, charset);
             }
         }
     }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ThreadPatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ThreadPatternConverter.java
index c763e515c4..0bf3df0917 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ThreadPatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ThreadPatternConverter.java
@@ -16,8 +16,11 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
+
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
 
 /**
  * Formats the event thread name.
@@ -25,17 +28,12 @@ import org.apache.logging.log4j.core.config.plugins.Plugin;
 @Plugin(name = "ThreadPatternConverter", category = PatternConverter.CATEGORY)
 @ConverterKeys({ "t", "thread" })
 public final class ThreadPatternConverter extends LogEventPatternConverter {
-    /**
-     * Singleton.
-     */
-    private static final ThreadPatternConverter INSTANCE =
-        new ThreadPatternConverter();
 
     /**
      * Private constructor.
      */
-    private ThreadPatternConverter() {
-        super("Thread", "thread");
+    private ThreadPatternConverter(final FormattingInfo formattingInfo) {
+        super("Thread", "thread", formattingInfo);
     }
 
     /**
@@ -44,16 +42,36 @@ public final class ThreadPatternConverter extends LogEventPatternConverter {
      * @param options options, currently ignored, may be null.
      * @return instance of ThreadPatternConverter.
      */
-    public static ThreadPatternConverter newInstance(
-        final String[] options) {
-        return INSTANCE;
+    public static ThreadPatternConverter newInstance(final String[] options, final FormattingInfo formattingInfo) {
+        return new ThreadPatternConverter(formattingInfo);
+    }
+    
+    @Override
+    protected String convert(final Object thread) {
+        if (thread instanceof Thread) {
+            return ((Thread) thread).getName();
+        }
+        return thread.toString();
+    }
+    
+    private Object extractThreadOrThreadName(final LogEvent event) {
+        final boolean useEvent = event instanceof Log4jLogEvent && ((Log4jLogEvent) event).hasThreadName();
+        return useEvent ? event.getThreadName() : Thread.currentThread();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final TextBuffer toAppendTo) {
+        toAppendTo.append(getCachedFormattedString(extractThreadOrThreadName(event)));
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder toAppendTo) {
-        toAppendTo.append(event.getThreadName());
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
+        toAppendTo.append(getCachedFormattedBytes(extractThreadOrThreadName(event), charset));
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ThrowablePatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ThrowablePatternConverter.java
index b73d904c9e..b2869f072a 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ThrowablePatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ThrowablePatternConverter.java
@@ -18,6 +18,7 @@ package org.apache.logging.log4j.core.pattern;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.nio.charset.Charset;
 
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
@@ -25,11 +26,9 @@ import org.apache.logging.log4j.core.impl.ThrowableFormatOptions;
 import org.apache.logging.log4j.core.util.Constants;
 import org.apache.logging.log4j.util.Strings;
 
-
 /**
- * Outputs the Throwable portion of the LoggingEvent as a full stacktrace
- * unless this converter's option is 'short', where it just outputs the first line of the trace, or if
- * the number of lines to print is explicitly specified.
+ * Outputs the Throwable portion of the LoggingEvent as a full stacktrace unless this converter's option is 'short',
+ * where it just outputs the first line of the trace, or if the number of lines to print is explicitly specified.
  */
 @Plugin(name = "ThrowablePatternConverter", category = PatternConverter.CATEGORY)
 @ConverterKeys({ "ex", "throwable", "exception" })
@@ -44,12 +43,14 @@ public class ThrowablePatternConverter extends LogEventPatternConverter {
 
     /**
      * Constructor.
+     * 
      * @param name Name of converter.
      * @param style CSS style for output.
      * @param options options, may be null.
      */
-    protected ThrowablePatternConverter(final String name, final String style, final String[] options) {
-        super(name, style);
+    protected ThrowablePatternConverter(final String name, final String style, final String[] options,
+            final FormattingInfo formattingInfo) {
+        super(name, style, formattingInfo);
         this.options = ThrowableFormatOptions.newInstance(options);
         if (options != null && options.length > 0) {
             rawOption = options[0];
@@ -59,46 +60,56 @@ public class ThrowablePatternConverter extends LogEventPatternConverter {
     /**
      * Gets an instance of the class.
      *
-     * @param options pattern options, may be null.  If first element is "short",
-     *                only the first line of the throwable will be formatted.
+     * @param options pattern options, may be null. If first element is "short", only the first line of the throwable
+     *            will be formatted.
      * @return instance of class.
      */
-    public static ThrowablePatternConverter newInstance(final String[] options) {
-        return new ThrowablePatternConverter("Throwable", "throwable", options);
+    public static ThrowablePatternConverter newInstance(final String[] options, final FormattingInfo formattingInfo) {
+        return new ThrowablePatternConverter("Throwable", "throwable", options, formattingInfo);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final TextBuffer buffer) {
+        handle(event, buffer);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder buffer) {
+    public void format(final LogEvent event, final BinaryBuffer buffer, final Charset charset) {
+        handle(event, buffer);
+    }
+    
+    private void handle(final LogEvent event, final Buffer buffer) {
         final Throwable t = event.getThrown();
 
         if (isSubShortOption()) {
             formatSubShortOption(t, buffer);
-        }
-        else if (t != null && options.anyLines()) {
+        } else if (t != null && options.anyLines()) {
             formatOption(t, buffer);
         }
     }
 
     private boolean isSubShortOption() {
-        return ThrowableFormatOptions.MESSAGE.equalsIgnoreCase(rawOption) ||
-                ThrowableFormatOptions.LOCALIZED_MESSAGE.equalsIgnoreCase(rawOption) ||
-                ThrowableFormatOptions.FILE_NAME.equalsIgnoreCase(rawOption) ||
-                ThrowableFormatOptions.LINE_NUMBER.equalsIgnoreCase(rawOption) ||
-                ThrowableFormatOptions.METHOD_NAME.equalsIgnoreCase(rawOption) ||
-                ThrowableFormatOptions.CLASS_NAME.equalsIgnoreCase(rawOption);
+        return ThrowableFormatOptions.MESSAGE.equalsIgnoreCase(rawOption)
+                || ThrowableFormatOptions.LOCALIZED_MESSAGE.equalsIgnoreCase(rawOption)
+                || ThrowableFormatOptions.FILE_NAME.equalsIgnoreCase(rawOption)
+                || ThrowableFormatOptions.LINE_NUMBER.equalsIgnoreCase(rawOption)
+                || ThrowableFormatOptions.METHOD_NAME.equalsIgnoreCase(rawOption)
+                || ThrowableFormatOptions.CLASS_NAME.equalsIgnoreCase(rawOption);
     }
 
-    private void formatSubShortOption(final Throwable t, final StringBuilder buffer) {
+    private void formatSubShortOption(final Throwable t, final Buffer buffer) {
         StackTraceElement[] trace;
         StackTraceElement throwingMethod = null;
-        int len;
 
         if (t != null) {
             trace = t.getStackTrace();
-            if (trace !=null && trace.length > 0) {
+            if (trace != null && trace.length > 0) {
                 throwingMethod = trace[0];
             }
         }
@@ -108,42 +119,35 @@ public class ThrowablePatternConverter extends LogEventPatternConverter {
 
             if (ThrowableFormatOptions.CLASS_NAME.equalsIgnoreCase(rawOption)) {
                 toAppend = throwingMethod.getClassName();
-            }
-            else if (ThrowableFormatOptions.METHOD_NAME.equalsIgnoreCase(rawOption)) {
+            } else if (ThrowableFormatOptions.METHOD_NAME.equalsIgnoreCase(rawOption)) {
                 toAppend = throwingMethod.getMethodName();
-            }
-            else if (ThrowableFormatOptions.LINE_NUMBER.equalsIgnoreCase(rawOption)) {
+            } else if (ThrowableFormatOptions.LINE_NUMBER.equalsIgnoreCase(rawOption)) {
                 toAppend = String.valueOf(throwingMethod.getLineNumber());
-            }
-            else if (ThrowableFormatOptions.MESSAGE.equalsIgnoreCase(rawOption)) {
+            } else if (ThrowableFormatOptions.MESSAGE.equalsIgnoreCase(rawOption)) {
                 toAppend = t.getMessage();
-            }
-            else if (ThrowableFormatOptions.LOCALIZED_MESSAGE.equalsIgnoreCase(rawOption)) {
+            } else if (ThrowableFormatOptions.LOCALIZED_MESSAGE.equalsIgnoreCase(rawOption)) {
                 toAppend = t.getLocalizedMessage();
-            }
-            else if (ThrowableFormatOptions.FILE_NAME.equalsIgnoreCase(rawOption)) {
+            } else if (ThrowableFormatOptions.FILE_NAME.equalsIgnoreCase(rawOption)) {
                 toAppend = throwingMethod.getFileName();
             }
 
-            len = buffer.length();
-            if (len > 0 && !Character.isWhitespace(buffer.charAt(len - 1))) {
+            if (!buffer.hasTrailingWhitespace()) {
                 buffer.append(' ');
             }
             buffer.append(toAppend);
         }
     }
 
-    private void formatOption(final Throwable throwable, final StringBuilder buffer) {
+    private void formatOption(final Throwable throwable, final Buffer buffer) {
         final StringWriter w = new StringWriter();
 
         throwable.printStackTrace(new PrintWriter(w));
-        final int len = buffer.length();
-        if (len > 0 && !Character.isWhitespace(buffer.charAt(len - 1))) {
+        if (!buffer.hasTrailingWhitespace()) {
             buffer.append(' ');
         }
         if (!options.allLines() || !Constants.LINE_SEPARATOR.equals(options.getSeparator())) {
-            final StringBuilder sb = new StringBuilder();
             final String[] array = w.toString().split(Constants.LINE_SEPARATOR);
+            final StringBuilder sb = new StringBuilder(array.length << 7); // *128: estimate 128 chars per line
             final int limit = options.minLines(array.length) - 1;
             for (int i = 0; i <= limit; ++i) {
                 sb.append(array[i]);
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/UuidPatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/UuidPatternConverter.java
index 439935bedb..46ed031ccc 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/UuidPatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/UuidPatternConverter.java
@@ -16,6 +16,7 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
 import java.util.UUID;
 
 import org.apache.logging.log4j.core.LogEvent;
@@ -34,8 +35,8 @@ public final class UuidPatternConverter extends LogEventPatternConverter {
     /**
      * Private constructor.
      */
-    private UuidPatternConverter(final boolean isRandom) {
-        super("u", "uuid");
+    private UuidPatternConverter(final boolean isRandom, final FormattingInfo formattingInfo) {
+        super("u", "uuid", formattingInfo);
         this.isRandom = isRandom;
     }
 
@@ -45,23 +46,34 @@ public final class UuidPatternConverter extends LogEventPatternConverter {
      * @param options options, currently ignored, may be null.
      * @return instance of SequencePatternConverter.
      */
-    public static UuidPatternConverter newInstance(final String[] options) {
+    public static UuidPatternConverter newInstance(final String[] options, final FormattingInfo formattingInfo) {
         if (options.length == 0) {
-            return new UuidPatternConverter(false);
+            return new UuidPatternConverter(false, formattingInfo);
         }
 
         if (options.length > 1 || (!options[0].equalsIgnoreCase("RANDOM") && !options[0].equalsIgnoreCase("Time"))) {
             LOGGER.error("UUID Pattern Converter only accepts a single option with the value \"RANDOM\" or \"TIME\"");
         }
-        return new UuidPatternConverter(options[0].equalsIgnoreCase("RANDOM"));
+        return new UuidPatternConverter(options[0].equalsIgnoreCase("RANDOM"), formattingInfo);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void format(final LogEvent event, final StringBuilder toAppendTo) {
-        final UUID uuid = isRandom ? UUID.randomUUID() : UuidUtil.getTimeBasedUuid();
-        toAppendTo.append(uuid.toString());
+    public void format(final LogEvent event, final TextBuffer toAppendTo) {
+        toAppendTo.append(getCachedFormattedString(nextUUID()));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final BinaryBuffer toAppendTo, final Charset charset) {
+        toAppendTo.append(getCachedFormattedBytes(nextUUID(), charset));
+    }
+    
+    private UUID nextUUID() {
+        return isRandom ? UUID.randomUUID() : UuidUtil.getTimeBasedUuid();
     }
 }
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/DatePatternConverterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/DatePatternConverterTest.java
index 2e75928c6e..27b02dcbaf 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/DatePatternConverterTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/DatePatternConverterTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.logging.log4j.core.pattern;
 
+import java.nio.charset.Charset;
 import java.util.Calendar;
 import java.util.Date;
 
@@ -31,29 +32,37 @@ public class DatePatternConverterTest {
 
     @Test
     public void testNewInstanceAllowsNullParameter() {
-        DatePatternConverter.newInstance(null); // no errors
+        DatePatternConverter.newInstance(null, FormattingInfo.getDefault()); // no errors
     }
 
     @Test
     public void testFormatLogEventStringBuilderDefaultPattern() {
         final LogEvent event = new MyLogEvent();
-        final DatePatternConverter converter = DatePatternConverter.newInstance(null);
-        final StringBuilder sb = new StringBuilder();
+        final DatePatternConverter converter = DatePatternConverter.newInstance(null, FormattingInfo.getDefault());
+        final TextBuffer sb = new TextBuffer();
         converter.format(event, sb);
 
         final String expected = "2011-12-30 10:56:35,987";
         assertEquals(expected, sb.toString());
+        
+        final BinaryBuffer bin = new BinaryBuffer(Charset.defaultCharset());
+        converter.format(event, bin, Charset.defaultCharset());
+        assertEquals(expected, bin.toString());
     }
 
     @Test
     public void testFormatLogEventStringBuilderIso8601() {
         final LogEvent event = new MyLogEvent();
-        final DatePatternConverter converter = DatePatternConverter.newInstance(ISO8601_FORMAT);
-        final StringBuilder sb = new StringBuilder();
+        final DatePatternConverter converter = DatePatternConverter.newInstance(ISO8601_FORMAT, FormattingInfo.getDefault());
+        final TextBuffer sb = new TextBuffer();
         converter.format(event, sb);
 
         final String expected = "2011-12-30T10:56:35,987";
         assertEquals(expected, sb.toString());
+        
+        final BinaryBuffer bin = new BinaryBuffer(Charset.defaultCharset());
+        converter.format(event, bin, Charset.defaultCharset());
+        assertEquals(expected, bin.toString());
     }
 
     private class MyLogEvent extends AbstractLogEvent {
@@ -70,8 +79,8 @@ public class DatePatternConverterTest {
 
     @Test
     public void testFormatObjectStringBuilderDefaultPattern() {
-        final DatePatternConverter converter = DatePatternConverter.newInstance(null);
-        final StringBuilder sb = new StringBuilder();
+        final DatePatternConverter converter = DatePatternConverter.newInstance(null, FormattingInfo.getDefault());
+        final TextBuffer sb = new TextBuffer();
         converter.format("nondate", sb);
 
         final String expected = ""; // only process dates
@@ -80,8 +89,8 @@ public class DatePatternConverterTest {
 
     @Test
     public void testFormatDateStringBuilderDefaultPattern() {
-        final DatePatternConverter converter = DatePatternConverter.newInstance(null);
-        final StringBuilder sb = new StringBuilder();
+        final DatePatternConverter converter = DatePatternConverter.newInstance(null, FormattingInfo.getDefault());
+        final TextBuffer sb = new TextBuffer();
         converter.format(date(2001, 1, 1), sb);
 
         final String expected = "2001-02-01 14:15:16,123";
@@ -90,8 +99,8 @@ public class DatePatternConverterTest {
 
     @Test
     public void testFormatDateStringBuilderIso8601() {
-        final DatePatternConverter converter = DatePatternConverter.newInstance(ISO8601_FORMAT);
-        final StringBuilder sb = new StringBuilder();
+        final DatePatternConverter converter = DatePatternConverter.newInstance(ISO8601_FORMAT, FormattingInfo.getDefault());
+        final TextBuffer sb = new TextBuffer();
         converter.format(date(2001, 1, 1), sb);
 
         final String expected = "2001-02-01T14:15:16,123";
@@ -100,8 +109,8 @@ public class DatePatternConverterTest {
 
     @Test
     public void testFormatStringBuilderObjectArrayDefaultPattern() {
-        final DatePatternConverter converter = DatePatternConverter.newInstance(null);
-        final StringBuilder sb = new StringBuilder();
+        final DatePatternConverter converter = DatePatternConverter.newInstance(null, FormattingInfo.getDefault());
+        final TextBuffer sb = new TextBuffer();
         converter.format(sb, date(2001, 1, 1), date(2002, 2, 2), date(2003, 3, 3));
 
         final String expected = "2001-02-01 14:15:16,123"; // only process first date
@@ -110,8 +119,8 @@ public class DatePatternConverterTest {
 
     @Test
     public void testFormatStringBuilderObjectArrayIso8601() {
-        final DatePatternConverter converter = DatePatternConverter.newInstance(ISO8601_FORMAT);
-        final StringBuilder sb = new StringBuilder();
+        final DatePatternConverter converter = DatePatternConverter.newInstance(ISO8601_FORMAT, FormattingInfo.getDefault());
+        final TextBuffer sb = new TextBuffer();
         converter.format(sb, date(2001, 1, 1), date(2002, 2, 2), date(2003, 3, 3));
 
         final String expected = "2001-02-01T14:15:16,123"; // only process first date
@@ -127,7 +136,7 @@ public class DatePatternConverterTest {
 
     @Test
     public void testGetPatternReturnsCorrectDefault() {
-        assertEquals(DatePatternConverter.DEFAULT_PATTERN, DatePatternConverter.newInstance(null).getPattern());
+        assertEquals(DatePatternConverter.DEFAULT_PATTERN, DatePatternConverter.newInstance(null, FormattingInfo.getDefault()).getPattern());
     }
 
 }
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/EncodingPatternConverterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/EncodingPatternConverterTest.java
index be1e2adcb6..ae5c66f9cb 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/EncodingPatternConverterTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/EncodingPatternConverterTest.java
@@ -35,11 +35,11 @@ public class EncodingPatternConverterTest {
     public void testReplacement() {
         final LogEvent event = new Log4jLogEvent(EncodingPatternConverterTest.class.getName(), null, null, Level.DEBUG,
             new SimpleMessage("Test \r\n<div class=\"test\">this</div> & <div class='test'>that</div>"), null);
-        final StringBuilder sb = new StringBuilder();
         final LoggerContext ctx = (LoggerContext) LogManager.getContext();
         final String[] options = new String[]{"%msg"};
         final EncodingPatternConverter converter = EncodingPatternConverter
-            .newInstance(ctx.getConfiguration(), options);
+            .newInstance(ctx.getConfiguration(), options, FormattingInfo.getDefault());
+        final TextBuffer sb = new TextBuffer();
         converter.format(event, sb);
         assertEquals(
             "Test \\r\\n&lt;div class=&quot;test&quot;&gt;this&lt;&#x2F;div&gt; &amp; &lt;div class=&apos;test&apos;&gt;that&lt;&#x2F;div&gt;",
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/ExtendedThrowablePatternConverterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/ExtendedThrowablePatternConverterTest.java
index 9edc98ab76..f2d2c76868 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/ExtendedThrowablePatternConverterTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/ExtendedThrowablePatternConverterTest.java
@@ -18,6 +18,7 @@ package org.apache.logging.log4j.core.pattern;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.nio.charset.Charset;
 
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.core.LogEvent;
@@ -35,34 +36,47 @@ public class ExtendedThrowablePatternConverterTest {
 
     @Test
     public void testFull() {
-        final ExtendedThrowablePatternConverter converter = ExtendedThrowablePatternConverter.newInstance(null);
+        final ExtendedThrowablePatternConverter converter = ExtendedThrowablePatternConverter.newInstance(null,
+                FormattingInfo.getDefault());
         final Throwable cause = new NullPointerException("null pointer");
         final Throwable parent = new IllegalArgumentException("IllegalArgument", cause);
         final LogEvent event = new Log4jLogEvent("testLogger", null, this.getClass().getName(), Level.DEBUG,
-            new SimpleMessage("test exception"), parent);
-        final StringBuilder sb = new StringBuilder();
-        converter.format(event, sb);
+                new SimpleMessage("test exception"), parent);
         final StringWriter sw = new StringWriter();
         final PrintWriter pw = new PrintWriter(sw);
         parent.printStackTrace(pw);
+        final String expected = sw.toString().replaceAll("\r", Strings.EMPTY);
+
+        final TextBuffer sb = new TextBuffer();
+        converter.format(event, sb);
         String result = sb.toString();
         result = result.replaceAll(" ~?\\[.*\\]", Strings.EMPTY);
-        final String expected = sw.toString().replaceAll("\r", Strings.EMPTY);
+        assertEquals(expected, result);
+        
+        final BinaryBuffer bin = new BinaryBuffer(Charset.defaultCharset());
+        converter.format(event, bin);
+        result = bin.toString();
+        result = result.replaceAll(" ~?\\[.*\\]", Strings.EMPTY);
         assertEquals(expected, result);
     }
 
     @Test
     public void testFiltering() {
         final String packages = "filters(org.junit, org.apache.maven, sun.reflect, java.lang.reflect)";
-        final String[] options = {packages};
-        final ExtendedThrowablePatternConverter converter = ExtendedThrowablePatternConverter.newInstance(options);
+        final String[] options = { packages };
+        final ExtendedThrowablePatternConverter converter = ExtendedThrowablePatternConverter.newInstance(options,
+                FormattingInfo.getDefault());
         final Throwable cause = new NullPointerException("null pointer");
         final Throwable parent = new IllegalArgumentException("IllegalArgument", cause);
         final LogEvent event = new Log4jLogEvent("testLogger", null, this.getClass().getName(), Level.DEBUG,
-            new SimpleMessage("test exception"), parent);
-        final StringBuilder sb = new StringBuilder();
+                new SimpleMessage("test exception"), parent);
+        final TextBuffer sb = new TextBuffer();
         converter.format(event, sb);
         final String result = sb.toString();
         assertTrue("No suppressed lines", result.contains(" suppressed "));
+        
+        final BinaryBuffer bin = new BinaryBuffer(Charset.defaultCharset());
+        converter.format(event, bin);
+        assertTrue("No suppressed lines", bin.toString().contains(" suppressed "));
     }
 }
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/FormattingInfoTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/FormattingInfoTest.java
new file mode 100644
index 0000000000..945d503073
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/FormattingInfoTest.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.core.pattern;
+
+import java.nio.ByteBuffer;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests the FormattingInfo class.
+ */
+public class FormattingInfoTest {
+
+    @Test
+    public void testFormattingInfoValidConstructorParams() {
+        int[][] valid = new int[][] { //
+        //
+                new int[] { 0, 10 }, //
+                new int[] { 0, Integer.MAX_VALUE }, //
+                new int[] { 10, 10 }, // min=max
+                new int[] { 10, 11 }, //
+        };
+        for (int i = 0; i < valid.length; i++) {
+            new FormattingInfo(true, valid[i][0], valid[i][1]);
+        }
+        for (int i = 0; i < valid.length; i++) {
+            new FormattingInfo(false, valid[i][0], valid[i][1]);
+        }
+    }
+
+    @Test
+    public void testFormattingInfoInvalidConstructorParams() {
+        int[][] invalid = new int[][] { //
+        //
+                new int[] { -1, 10 }, // negative min
+                new int[] { 10, -1 }, // negative max
+                new int[] { -1, -1 }, // negative max
+                new int[] { Integer.MIN_VALUE, Integer.MIN_VALUE }, // negative max
+                // new int[] { 11, 10 }, // min > max
+        };
+        for (int i = 0; i < invalid.length; i++) {
+            try {
+                new FormattingInfo(true, invalid[i][0], invalid[i][1]);
+            } catch (IllegalArgumentException ok) {
+                // constructor validation worked as expected
+                continue;
+            }
+            fail("Should have failed at index " + i);
+        }
+        for (int i = 0; i < invalid.length; i++) {
+            try {
+                new FormattingInfo(false, invalid[i][0], invalid[i][1]);
+            } catch (IllegalArgumentException ok) {
+                // constructor validation worked as expected
+                continue;
+            }
+            fail("Should have failed at index " + i);
+        }
+    }
+
+    @Test
+    public void testDefaultValues() {
+        assertFalse(FormattingInfo.getDefault().isLeftAligned());
+        assertEquals("min", 0, FormattingInfo.getDefault().getMinLength());
+        assertEquals("max", Integer.MAX_VALUE, FormattingInfo.getDefault().getMaxLength());
+    }
+
+    @Test
+    public void testGetDefaultIsSingleton() {
+        assertSame(FormattingInfo.getDefault(), FormattingInfo.getDefault());
+    }
+
+    @Test
+    public void testIsLeftAlignedReturnsConstructorValue() {
+        assertFalse(new FormattingInfo(false, 0, 1).isLeftAligned());
+        assertTrue(new FormattingInfo(true, 0, 1).isLeftAligned());
+    }
+
+    @Test
+    public void testGetMinLengthReturnsConstructorValue() {
+        assertEquals(0, new FormattingInfo(false, 0, 1).getMinLength());
+        assertEquals(11, new FormattingInfo(false, 11, 12).getMinLength());
+    }
+
+    @Test
+    public void testGetMaxLengthReturnsConstructorValue() {
+        assertEquals(1, new FormattingInfo(false, 0, 1).getMaxLength());
+        assertEquals(12, new FormattingInfo(false, 11, 12).getMaxLength());
+    }
+
+    @Test
+    public void testFormatIntStringBuilder() {
+        assertText(2, "12XYZ", true, 7, 12, "12XYZ    ");
+        assertText(2, "12XYZ", false, 7, 12, "12    XYZ");
+        assertText(2, "12XYZABC", true, 0, 4, "12ZABC");
+        assertText(2, "12XYZABC", false, 0, 4, "12ZABC");
+        assertText(2, "12XYZABC", false, 0, Integer.MAX_VALUE, "12XYZABC");
+    }
+
+    private void assertText(int offset, String input, boolean leftAlign, int min, int max, String expected) {
+        StringBuilder sb = new StringBuilder(input);
+        FormattingInfo info = new FormattingInfo(leftAlign, min, max);
+        info.format(offset, sb);
+        assertEquals(info.toString(), expected, sb.toString());
+    }
+
+    @Test
+    public void testFormatIntByteBuffer() {
+        assertBinary(2, "12XYZ", true, 7, 12, "12XYZ    ");
+        assertBinary(2, "12XYZ", false, 7, 12, "12    XYZ");
+        assertBinary(2, "12XYZABC", true, 0, 4, "12ZABC");
+        assertBinary(2, "12XYZABC", false, 0, 4, "12ZABC");
+        assertBinary(2, "12XYZABC", false, 0, Integer.MAX_VALUE, "12XYZABC");
+    }
+
+    private void assertBinary(int offset, String input, boolean leftAlign, int min, int max, String expected) {
+        ByteBuffer buff = ByteBuffer.allocate(512);
+        buff.put(input.getBytes());
+        FormattingInfo info = new FormattingInfo(leftAlign, min, max);
+        info.format(offset, buff);
+        assertEquals(expected.length(), buff.position()); // positioned at end of buffer
+        String actual = new String(buff.array(), 0, buff.position());
+        assertEquals(info.toString(), expected, actual);
+    }
+}
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/LevelPatternConverterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/LevelPatternConverterTest.java
index 4a0e365233..488926d126 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/LevelPatternConverterTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/LevelPatternConverterTest.java
@@ -33,12 +33,12 @@ public class LevelPatternConverterTest {
     private void testLevelLength(final int length, final String debug, final String warn) {
         final Message msg = new SimpleMessage("Hello");
         LogEvent event = new Log4jLogEvent("MyLogger", null, null, Level.DEBUG, msg, null);
-        final StringBuilder sb = new StringBuilder();
-        LevelPatternConverter converter = LevelPatternConverter.newInstance(null);
+        final TextBuffer sb = new TextBuffer();
+        LevelPatternConverter converter = LevelPatternConverter.newInstance(null, FormattingInfo.getDefault());
         converter.format(event, sb);
         assertEquals(Level.DEBUG.toString(), sb.toString());
         final String[] opts = new String[] { "length=" + length };
-        converter = LevelPatternConverter.newInstance(opts);
+        converter = LevelPatternConverter.newInstance(opts, FormattingInfo.getDefault());
         sb.setLength(0);
         converter.format(event, sb);
         assertEquals(debug, sb.toString());
@@ -72,12 +72,12 @@ public class LevelPatternConverterTest {
     public void testLevelLowerCase() {
         final Message msg = new SimpleMessage("Hello");
         LogEvent event = new Log4jLogEvent("MyLogger", null, null, Level.DEBUG, msg, null);
-        final StringBuilder sb = new StringBuilder();
-        LevelPatternConverter converter = LevelPatternConverter.newInstance(null);
+        final TextBuffer sb = new TextBuffer();
+        LevelPatternConverter converter = LevelPatternConverter.newInstance(null, FormattingInfo.getDefault());
         converter.format(event, sb);
         assertEquals(Level.DEBUG.toString(), sb.toString());
         final String[] opts = new String[] { "lowerCase=true" };
-        converter = LevelPatternConverter.newInstance(opts);
+        converter = LevelPatternConverter.newInstance(opts, FormattingInfo.getDefault());
         sb.setLength(0);
         converter.format(event, sb);
         assertEquals("debug", sb.toString());
@@ -91,12 +91,12 @@ public class LevelPatternConverterTest {
     public void testLevelMap() {
         final Message msg = new SimpleMessage("Hello");
         LogEvent event = new Log4jLogEvent("MyLogger", null, null, Level.DEBUG, msg, null);
-        final StringBuilder sb = new StringBuilder();
-        LevelPatternConverter converter = LevelPatternConverter.newInstance(null);
+        final TextBuffer sb = new TextBuffer();
+        LevelPatternConverter converter = LevelPatternConverter.newInstance(null, FormattingInfo.getDefault());
         converter.format(event, sb);
         assertEquals(Level.DEBUG.toString(), sb.toString());
         final String[] opts = new String[] { "WARN=Warning, DEBUG=Debug, ERROR=Error, TRACE=Trace, INFO=Info" };
-        converter = LevelPatternConverter.newInstance(opts);
+        converter = LevelPatternConverter.newInstance(opts, FormattingInfo.getDefault());
         sb.setLength(0);
         converter.format(event, sb);
         assertEquals("Debug", sb.toString());
@@ -110,12 +110,12 @@ public class LevelPatternConverterTest {
     public void testLevelMapWithLength() {
         final Message msg = new SimpleMessage("Hello");
         LogEvent event = new Log4jLogEvent("MyLogger", null, null, Level.DEBUG, msg, null);
-        final StringBuilder sb = new StringBuilder();
-        LevelPatternConverter converter = LevelPatternConverter.newInstance(null);
+        final TextBuffer sb = new TextBuffer();
+        LevelPatternConverter converter = LevelPatternConverter.newInstance(null, FormattingInfo.getDefault());
         converter.format(event, sb);
         assertEquals(Level.DEBUG.toString(), sb.toString());
         final String[] opts = new String[] { "WARN=Warning, length=2" };
-        converter = LevelPatternConverter.newInstance(opts);
+        converter = LevelPatternConverter.newInstance(opts, FormattingInfo.getDefault());
         sb.setLength(0);
         converter.format(event, sb);
         assertEquals("DE", sb.toString());
@@ -129,12 +129,12 @@ public class LevelPatternConverterTest {
     public void testLevelMapWithLengthAndLowerCase() {
         final Message msg = new SimpleMessage("Hello");
         LogEvent event = new Log4jLogEvent("MyLogger", null, null, Level.DEBUG, msg, null);
-        final StringBuilder sb = new StringBuilder();
-        LevelPatternConverter converter = LevelPatternConverter.newInstance(null);
+        final TextBuffer sb = new TextBuffer();
+        LevelPatternConverter converter = LevelPatternConverter.newInstance(null, FormattingInfo.getDefault());
         converter.format(event, sb);
         assertEquals(Level.DEBUG.toString(), sb.toString());
         final String[] opts = new String[] { "WARN=Warning, length=2, lowerCase=true" };
-        converter = LevelPatternConverter.newInstance(opts);
+        converter = LevelPatternConverter.newInstance(opts, FormattingInfo.getDefault());
         sb.setLength(0);
         converter.format(event, sb);
         assertEquals("de", sb.toString());
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/MapPatternConverterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/MapPatternConverterTest.java
index 78480266c0..6b0401d95d 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/MapPatternConverterTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/MapPatternConverterTest.java
@@ -36,9 +36,9 @@ public class MapPatternConverterTest {
         msg.put("subject", "I");
         msg.put("verb", "love");
         msg.put("object", "Log4j");
-        final MapPatternConverter converter = MapPatternConverter.newInstance(null);
+        final MapPatternConverter converter = MapPatternConverter.newInstance(null, FormattingInfo.getDefault());
         final LogEvent event = new Log4jLogEvent("MyLogger", null, null, Level.DEBUG, msg, null);
-        final StringBuilder sb = new StringBuilder();
+        final TextBuffer sb = new TextBuffer();;
         converter.format(event, sb);
         final String str = sb.toString();
         String expected = "subject=I";
@@ -56,9 +56,9 @@ public class MapPatternConverterTest {
         msg.put("subject", "I");
         msg.put("verb", "love");
         msg.put("object", "Log4j");
-        final MapPatternConverter converter = MapPatternConverter.newInstance(new String[] {"object"});
+        final MapPatternConverter converter = MapPatternConverter.newInstance(new String[] {"object"}, FormattingInfo.getDefault());
         final LogEvent event = new Log4jLogEvent("MyLogger", null, null, Level.DEBUG, msg, null);
-        final StringBuilder sb = new StringBuilder();
+        final TextBuffer sb = new TextBuffer();;
         converter.format(event, sb);
         final String str = sb.toString();
         final String expected = "Log4j";
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/MarkerPatternConverterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/MarkerPatternConverterTest.java
index 83621d01df..09c37d9a4c 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/MarkerPatternConverterTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/MarkerPatternConverterTest.java
@@ -39,8 +39,8 @@ public class MarkerPatternConverterTest {
         final Marker eventMarker = MarkerManager.getMarker("EVENT");
         final Marker auditMarker = MarkerManager.getMarker("AUDIT").setParents(eventMarker);
         final LogEvent event = new Log4jLogEvent("MyLogger", auditMarker, null, Level.DEBUG, msg, null);
-        final StringBuilder sb = new StringBuilder();
-        final MarkerPatternConverter converter = MarkerPatternConverter.newInstance(null);
+        final TextBuffer sb = new TextBuffer();
+        final MarkerPatternConverter converter = MarkerPatternConverter.newInstance(null, FormattingInfo.getDefault());
         converter.format(event, sb);
         assertEquals(auditMarker.toString(), sb.toString());
     }
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/MessagePatternConverterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/MessagePatternConverterTest.java
index 7e3f0ffc4a..53fb865e33 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/MessagePatternConverterTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/MessagePatternConverterTest.java
@@ -34,19 +34,19 @@ public class MessagePatternConverterTest {
 
     @Test
     public void testPattern() throws Exception {
-        final MessagePatternConverter converter = MessagePatternConverter.newInstance(null, null);
+        final MessagePatternConverter converter = MessagePatternConverter.newInstance(null, null, FormattingInfo.getDefault());
         Message msg = new SimpleMessage("Hello!");
         LogEvent event = new Log4jLogEvent("MyLogger", null, null, Level.DEBUG, msg, null);
-        StringBuilder sb = new StringBuilder();
+        TextBuffer sb = new TextBuffer();
         converter.format(event, sb);
         assertEquals("Unexpected result", "Hello!", sb.toString());
         event = new Log4jLogEvent("MyLogger", null, null, Level.DEBUG, null, null);
-        sb = new StringBuilder();
+        sb = new TextBuffer();
         converter.format(event, sb);
         assertEquals("Incorrect length: " + sb.length(), 0, sb.length());
         msg = new SimpleMessage(null);
         event = new Log4jLogEvent("MyLogger", null, null, Level.DEBUG, msg, null);
-        sb = new StringBuilder();
+        sb = new TextBuffer();
         converter.format(event, sb);
         assertEquals("Incorrect length: " + sb.length(), 4, sb.length());
 
@@ -55,19 +55,19 @@ public class MessagePatternConverterTest {
     @Test
     public void testPatternWithConfiguration() throws Exception {
         final Configuration config = new DefaultConfiguration();
-        final MessagePatternConverter converter = MessagePatternConverter.newInstance(config, null);
+        final MessagePatternConverter converter = MessagePatternConverter.newInstance(config, null, FormattingInfo.getDefault());
         Message msg = new SimpleMessage("Hello!");
         LogEvent event = new Log4jLogEvent("MyLogger", null, null, Level.DEBUG, msg, null);
-        StringBuilder sb = new StringBuilder();
+        TextBuffer sb = new TextBuffer();
         converter.format(event, sb);
         assertEquals("Unexpected result", "Hello!", sb.toString());
         event = new Log4jLogEvent("MyLogger", null, null, Level.DEBUG, null, null);
-        sb = new StringBuilder();
+        sb = new TextBuffer();
         converter.format(event, sb);
         assertEquals("Incorrect length: " + sb.length(), 0, sb.length());
         msg = new SimpleMessage(null);
         event = new Log4jLogEvent("MyLogger", null, null, Level.DEBUG, msg, null);
-        sb = new StringBuilder();
+        sb = new TextBuffer();
         converter.format(event, sb);
         assertEquals("Incorrect length: " + sb.length(), 4, sb.length());
     }
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/PatternParserTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/PatternParserTest.java
index 075bb193df..fcff4e5424 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/PatternParserTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/PatternParserTest.java
@@ -97,7 +97,7 @@ public class PatternParserTest {
         final LogEvent event = new Log4jLogEvent("org.apache.logging.log4j.PatternParserTest", MarkerManager.getMarker("TEST"),
             Logger.class.getName(), Level.INFO, new SimpleMessage("Hello, world"), null,
             mdc, null, "Thread1", elements[0], System.currentTimeMillis());
-        final StringBuilder buf = new StringBuilder();
+        final TextBuffer buf = new TextBuffer();
         for (final PatternFormatter formatter : formatters) {
             formatter.format(event, buf);
         }
@@ -120,7 +120,7 @@ public class PatternParserTest {
         final LogEvent event = new Log4jLogEvent("a.b.c", null,
             Logger.class.getName(), Level.INFO, new SimpleMessage("Hello, world"), null,
             null, null, "Thread1", elements[0], timestamp);
-        final StringBuilder buf = new StringBuilder();
+        final TextBuffer buf = new TextBuffer();
         for (final PatternFormatter formatter : formatters) {
             formatter.format(event, buf);
         }
@@ -149,7 +149,7 @@ public class PatternParserTest {
         final LogEvent event = new Log4jLogEvent("org.apache.logging.log4j.PatternParserTest",
                 MarkerManager.getMarker("TEST"), Logger.class.getName(), level, new SimpleMessage("Hello, world"),
                 null, null, null, "Thread1", /*stackTraceElement[0]*/null, System.currentTimeMillis());
-        final StringBuilder buf = new StringBuilder();
+        final TextBuffer buf = new TextBuffer();
         for (final PatternFormatter formatter : formatters) {
             formatter.format(event, buf);
         }
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/PatternParserTest2.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/PatternParserTest2.java
index 44e3e47d16..9cbbb9b82b 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/PatternParserTest2.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/PatternParserTest2.java
@@ -33,7 +33,7 @@ public class PatternParserTest2 {
     @Test
     public void testParseConvertBackslashes() {
         final boolean convert = true;
-        final StringBuilder buf = new StringBuilder();
+        final TextBuffer buf = new TextBuffer();
         final String pattern = "%d{HH-mm-ss} \\t ...";
 
         final Calendar cal = Calendar.getInstance();
@@ -48,7 +48,7 @@ public class PatternParserTest2 {
     @Test
     public void testParseDontConvertBackslashes() {
         final boolean convert = false;
-        final StringBuilder buf = new StringBuilder();
+        final TextBuffer buf = new TextBuffer();
         final String pattern = "%d{HH-mm-ss} \\t---";
 
         final Calendar cal = Calendar.getInstance();
@@ -60,7 +60,7 @@ public class PatternParserTest2 {
         assertEquals("13-24-59 \\t---", buf.toString());
     }
 
-    private void parse(final String pattern, final boolean convert, final StringBuilder buf, final Date date, final int i) {
+    private void parse(final String pattern, final boolean convert, final Buffer buf, final Date date, final int i) {
         final PatternParser parser0 = new PatternParser(null, "Converter", null);
         final List<PatternConverter> converters = new ArrayList<PatternConverter>();
         final List<FormattingInfo> fields = new ArrayList<FormattingInfo>();
@@ -79,14 +79,14 @@ public class PatternParserTest2 {
      * @param objects objects to be evaluated in formatting, may not be null.
      */
     protected final void formatFileName(final ArrayPatternConverter[] patternConverters,
-            final FormattingInfo[] patternFields, final StringBuilder buf, final Object... objects) {
+            final FormattingInfo[] patternFields, final Buffer buf, final Object... objects) {
         for (int i = 0; i < patternConverters.length; i++) {
-            final int fieldStart = buf.length();
+//            final int fieldStart = buf.length();
             patternConverters[i].format(buf, objects);
 
-            if (patternFields[i] != null) {
-                patternFields[i].format(fieldStart, buf);
-            }
+//            if (patternFields[i] != null) {
+//                patternFields[i].format(fieldStart, buf);
+//            }
         }
     }
 }
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/RegexReplacementConverterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/RegexReplacementConverterTest.java
index 17f6fd4b5c..7ca46d5b90 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/RegexReplacementConverterTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/RegexReplacementConverterTest.java
@@ -38,13 +38,13 @@ public class RegexReplacementConverterTest {
         ThreadContext.put("MyKey", "Apache");
         final LogEvent event = new Log4jLogEvent(RegexReplacementConverterTest.class.getName(), null, null,
             Level.DEBUG, new SimpleMessage("This is a test"), null);
-        final StringBuilder sb = new StringBuilder();
+        final TextBuffer sb = new TextBuffer();
         final LoggerContext ctx = (LoggerContext) LogManager.getContext();
         final String[] options = new String[] {
             "%logger %msg%n", "\\.", "/"
         };
         final RegexReplacementConverter converter = RegexReplacementConverter.newInstance(ctx.getConfiguration(),
-            options);
+            options, FormattingInfo.getDefault());
         converter.format(event, sb);
         assertEquals("org/apache/logging/log4j/core/pattern/RegexReplacementConverterTest This is a test" +
             Constants.LINE_SEPARATOR, sb.toString());
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/RootThrowablePatternConverterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/RootThrowablePatternConverterTest.java
index 1b142964fa..477ddc898b 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/RootThrowablePatternConverterTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/RootThrowablePatternConverterTest.java
@@ -31,12 +31,12 @@ public class RootThrowablePatternConverterTest {
 
     @Test
     public void testFull1() {
-        final RootThrowablePatternConverter converter = RootThrowablePatternConverter.newInstance(null);
+        final RootThrowablePatternConverter converter = RootThrowablePatternConverter.newInstance(null, FormattingInfo.getDefault());
         final Throwable cause = new NullPointerException("null pointer");
         final Throwable parent = new IllegalArgumentException("IllegalArgument", cause);
         final LogEvent event = new Log4jLogEvent("testLogger", null, this.getClass().getName(), Level.DEBUG,
                 new SimpleMessage("test exception"), parent);
-        final StringBuilder sb = new StringBuilder();
+        final TextBuffer sb = new TextBuffer();
         converter.format(event, sb);
         final String result = sb.toString();
         // System.out.print(result);
@@ -51,7 +51,7 @@ public class RootThrowablePatternConverterTest {
      */
     @Test
     public void testFull2() {
-        final RootThrowablePatternConverter converter = RootThrowablePatternConverter.newInstance(null);
+        final RootThrowablePatternConverter converter = RootThrowablePatternConverter.newInstance(null, FormattingInfo.getDefault());
         Throwable parent;
         try {
             try {
@@ -64,7 +64,7 @@ public class RootThrowablePatternConverterTest {
         }
         final LogEvent event = new Log4jLogEvent("testLogger", null, this.getClass().getName(), Level.DEBUG,
                 new SimpleMessage("test exception"), parent);
-        final StringBuilder sb = new StringBuilder();
+        final TextBuffer sb = new TextBuffer();
         converter.format(event, sb);
         final String result = sb.toString();
         // System.out.print(result);
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/ThrowablePatternConverterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/ThrowablePatternConverterTest.java
index 8b9fce6368..9c705f2d73 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/ThrowablePatternConverterTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/ThrowablePatternConverterTest.java
@@ -42,13 +42,14 @@ public class ThrowablePatternConverterTest {
     @Test(expected = Exception.class)
     public void testBadShortOption() {
         final String[] options = { "short.UNKNOWN" };
-        ThrowablePatternConverter.newInstance(options);
+        ThrowablePatternConverter.newInstance(options, FormattingInfo.getDefault());
     }
 
     @Test
     public void testFull() {
         final String[] options = { "full" };
-        final ThrowablePatternConverter converter = ThrowablePatternConverter.newInstance(options);
+        final ThrowablePatternConverter converter = ThrowablePatternConverter.newInstance(options,
+                FormattingInfo.getDefault());
         Throwable parent;
         try {
             try {
@@ -61,7 +62,7 @@ public class ThrowablePatternConverterTest {
         }
         final LogEvent event = new Log4jLogEvent("testLogger", null, this.getClass().getName(), Level.DEBUG,
                 new SimpleMessage("test exception"), parent);
-        final StringBuilder sb = new StringBuilder();
+        final TextBuffer sb = new TextBuffer();
         converter.format(event, sb);
         final String result = sb.toString();
         // System.out.print(result);
@@ -73,12 +74,13 @@ public class ThrowablePatternConverterTest {
     public void testShortClassName() {
         final String packageName = "org.apache.logging.log4j.core.pattern.";
         final String[] options = { "short.className" };
-        final ThrowablePatternConverter converter = ThrowablePatternConverter.newInstance(options);
+        final ThrowablePatternConverter converter = ThrowablePatternConverter.newInstance(options,
+                FormattingInfo.getDefault());
         final Throwable cause = new NullPointerException("null pointer");
         final Throwable parent = new IllegalArgumentException("IllegalArgument", cause);
         final LogEvent event = new Log4jLogEvent("testLogger", null, this.getClass().getName(), Level.DEBUG,
                 new SimpleMessage("test exception"), parent);
-        final StringBuilder sb = new StringBuilder();
+        final TextBuffer sb = new TextBuffer();
         converter.format(event, sb);
         final String result = sb.toString();
         assertEquals("The class names should be same", packageName + "ThrowablePatternConverterTest", result);
@@ -87,12 +89,13 @@ public class ThrowablePatternConverterTest {
     @Test
     public void testShortFileName() {
         final String[] options = { "short.fileName" };
-        final ThrowablePatternConverter converter = ThrowablePatternConverter.newInstance(options);
+        final ThrowablePatternConverter converter = ThrowablePatternConverter.newInstance(options,
+                FormattingInfo.getDefault());
         final Throwable cause = new NullPointerException("null pointer");
         final Throwable parent = new IllegalArgumentException("IllegalArgument", cause);
         final LogEvent event = new Log4jLogEvent("testLogger", null, this.getClass().getName(), Level.DEBUG,
                 new SimpleMessage("test exception"), parent);
-        final StringBuilder sb = new StringBuilder();
+        final TextBuffer sb = new TextBuffer();
         converter.format(event, sb);
         final String result = sb.toString();
         assertEquals("The file names should be same", "ThrowablePatternConverterTest.java", result);
@@ -101,7 +104,8 @@ public class ThrowablePatternConverterTest {
     @Test
     public void testShortLineNumber() {
         final String[] options = { "short.lineNumber" };
-        final ThrowablePatternConverter converter = ThrowablePatternConverter.newInstance(options);
+        final ThrowablePatternConverter converter = ThrowablePatternConverter.newInstance(options,
+                FormattingInfo.getDefault());
         final Throwable cause = new NullPointerException("null pointer");
         final Throwable parent = new IllegalArgumentException("IllegalArgument", cause);
         final StackTraceElement top = parent.getStackTrace()[0];
@@ -109,7 +113,7 @@ public class ThrowablePatternConverterTest {
 
         final LogEvent event = new Log4jLogEvent("testLogger", null, this.getClass().getName(), Level.DEBUG,
                 new SimpleMessage("test exception"), parent);
-        final StringBuilder sb = new StringBuilder();
+        final TextBuffer sb = new TextBuffer();
         converter.format(event, sb);
         final String result = sb.toString();
         assertTrue("The line numbers should be same", expectedLineNumber == Integer.parseInt(result));
@@ -118,11 +122,12 @@ public class ThrowablePatternConverterTest {
     @Test
     public void testShortLocalizedMessage() {
         final String[] options = { "short.localizedMessage" };
-        final ThrowablePatternConverter converter = ThrowablePatternConverter.newInstance(options);
+        final ThrowablePatternConverter converter = ThrowablePatternConverter.newInstance(options,
+                FormattingInfo.getDefault());
         final Throwable parent = new LocalizedException();
         final LogEvent event = new Log4jLogEvent("testLogger", null, this.getClass().getName(), Level.DEBUG,
                 new SimpleMessage("test exception"), parent);
-        final StringBuilder sb = new StringBuilder();
+        final TextBuffer sb = new TextBuffer();
         converter.format(event, sb);
         final String result = sb.toString();
         assertEquals("The messages should be same", "I am localized.", result);
@@ -131,12 +136,13 @@ public class ThrowablePatternConverterTest {
     @Test
     public void testShortMessage() {
         final String[] options = { "short.message" };
-        final ThrowablePatternConverter converter = ThrowablePatternConverter.newInstance(options);
+        final ThrowablePatternConverter converter = ThrowablePatternConverter.newInstance(options,
+                FormattingInfo.getDefault());
         final Throwable cause = new NullPointerException("null pointer");
         final Throwable parent = new IllegalArgumentException("IllegalArgument", cause);
         final LogEvent event = new Log4jLogEvent("testLogger", null, this.getClass().getName(), Level.DEBUG,
                 new SimpleMessage("test exception"), parent);
-        final StringBuilder sb = new StringBuilder();
+        final TextBuffer sb = new TextBuffer();
         converter.format(event, sb);
         final String result = sb.toString();
         assertEquals("The messages should be same", "IllegalArgument", result);
@@ -145,12 +151,13 @@ public class ThrowablePatternConverterTest {
     @Test
     public void testShortMethodName() {
         final String[] options = { "short.methodName" };
-        final ThrowablePatternConverter converter = ThrowablePatternConverter.newInstance(options);
+        final ThrowablePatternConverter converter = ThrowablePatternConverter.newInstance(options,
+                FormattingInfo.getDefault());
         final Throwable cause = new NullPointerException("null pointer");
         final Throwable parent = new IllegalArgumentException("IllegalArgument", cause);
         final LogEvent event = new Log4jLogEvent("testLogger", null, this.getClass().getName(), Level.DEBUG,
                 new SimpleMessage("test exception"), parent);
-        final StringBuilder sb = new StringBuilder();
+        final TextBuffer sb = new TextBuffer();
         converter.format(event, sb);
         final String result = sb.toString();
         assertEquals("The method names should be same", "testShortMethodName", result);