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("&");
- break;
- case '<':
- toAppendTo.append("<");
- break;
- case '>':
- toAppendTo.append(">");
- break;
- case '"':
- toAppendTo.append(""");
- break;
- case '\'':
- toAppendTo.append("'");
- break;
- case '/':
- toAppendTo.append("/");
- 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("&");
+ break;
+ case '<':
+ toAppendTo.append("<");
+ break;
+ case '>':
+ toAppendTo.append(">");
+ break;
+ case '"':
+ toAppendTo.append(""");
+ break;
+ case '\'':
+ toAppendTo.append("'");
+ break;
+ case '/':
+ toAppendTo.append("/");
+ 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<div class="test">this</div> & <div class='test'>that</div>",
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);