Repository: logging-log4j2 Updated Branches: refs/heads/master 694413432 -> c3d8ce824
[LOG4J2-1237] Make PatternLayout header and footer accept a pattern. Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/c3d8ce82 Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/c3d8ce82 Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/c3d8ce82 Branch: refs/heads/master Commit: c3d8ce8240202ff92da0c239092b13aa347bae36 Parents: 6944134 Author: ggregory <[email protected]> Authored: Sun Jan 3 22:14:20 2016 -0800 Committer: ggregory <[email protected]> Committed: Sun Jan 3 22:14:20 2016 -0800 ---------------------------------------------------------------------- .../log4j/core/layout/AbstractStringLayout.java | 30 ++-- .../log4j/core/layout/PatternLayout.java | 163 +++++++++++-------- .../log4j/core/layout/PatternLayoutTest.java | 4 +- src/changes/changes.xml | 3 + 4 files changed, 112 insertions(+), 88 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c3d8ce82/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractStringLayout.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractStringLayout.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractStringLayout.java index 68b9fb6..96a80a5 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractStringLayout.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractStringLayout.java @@ -75,6 +75,21 @@ public abstract class AbstractStringLayout extends AbstractLayout<String> implem && (StandardCharsets.ISO_8859_1.equals(charset) || StandardCharsets.US_ASCII.equals(charset)); } + /** + * Returns a {@code StringBuilder} that this Layout implementation can use to write the formatted log event to. + * + * @return a {@code StringBuilder} + */ + protected static StringBuilder getStringBuilder() { + StringBuilder result = threadLocal.get(); + if (result == null) { + result = new StringBuilder(DEFAULT_STRING_BUILDER_SIZE); + threadLocal.set(result); + } + result.setLength(0); + return result; + } + // LOG4J2-1151: If the built-in JDK 8 encoders are available we should use them. private static boolean isPreJava8() { final String version = System.getProperty("java.version"); @@ -98,21 +113,6 @@ public abstract class AbstractStringLayout extends AbstractLayout<String> implem charset = Charset.forName(csName); } - /** - * Returns a {@code StringBuilder} that this Layout implementation can use to write the formatted log event to. - * - * @return a {@code StringBuilder} - */ - protected StringBuilder getStringBuilder() { - StringBuilder result = threadLocal.get(); - if (result == null) { - result = new StringBuilder(DEFAULT_STRING_BUILDER_SIZE); - threadLocal.set(result); - } - result.setLength(0); - return result; - } - protected byte[] getBytes(final String s) { if (useCustomEncoding) { // rely on branch prediction to eliminate this check if false return StringEncoder.encodeSingleByteChars(s); http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c3d8ce82/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java ---------------------------------------------------------------------- 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 cda7915..63c44fd 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 @@ -25,6 +25,7 @@ import org.apache.logging.log4j.core.Layout; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.DefaultConfiguration; +import org.apache.logging.log4j.core.config.LoggerConfig; 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; @@ -38,6 +39,7 @@ 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.util.StringEncoder; +import org.apache.logging.log4j.util.Strings; /** * A flexible layout configurable with pattern string. @@ -57,25 +59,20 @@ import org.apache.logging.log4j.core.util.StringEncoder; public final class PatternLayout extends AbstractStringLayout { /** - * 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"; @@ -83,87 +80,92 @@ public final class PatternLayout extends AbstractStringLayout { private static final long serialVersionUID = 1L; /** - * Initial converter for pattern. - */ - private final PatternFormatter[] formatters; - - /** * Conversion pattern. */ private final String conversionPattern; private final PatternSelector patternSelector; - private final Serializer serializer; + private final Serializer eventSerializer; + + private final Serializer headerSerializer; + private final Serializer footerSerializer; /** * The current Configuration. */ private final Configuration config; - private final RegexReplacement replace; - - private final boolean alwaysWriteExceptions; - - private final boolean noConsoleNoAnsi; - /** * Constructs a EnhancedPatternLayout using the supplied conversion pattern. * * @param config The Configuration. * @param replace The regular expression to match. - * @param pattern conversion pattern. + * @param eventPattern conversion pattern. * @param patternSelector The PatternSelector. * @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 - * @param header + * @param headerPattern header conversion pattern. + * @param footerPattern footer conversion pattern. */ - private PatternLayout(final Configuration config, final RegexReplacement replace, final String pattern, - final PatternSelector patternSelector, final Charset charset, - final boolean alwaysWriteExceptions, final boolean noConsoleNoAnsi, - final String header, final String footer) { - super(charset, StringEncoder.toBytes(header, charset), StringEncoder.toBytes(footer, charset)); - this.replace = replace; - this.conversionPattern = pattern; + private PatternLayout(final Configuration config, final RegexReplacement replace, final String eventPattern, + final PatternSelector patternSelector, final Charset charset, final boolean alwaysWriteExceptions, + final boolean noConsoleNoAnsi, final String headerPattern, final String footerPattern) { + super(charset, StringEncoder.toBytes(headerPattern, charset), StringEncoder.toBytes(footerPattern, charset)); + this.conversionPattern = eventPattern; this.patternSelector = patternSelector; this.config = config; - this.alwaysWriteExceptions = alwaysWriteExceptions; - this.noConsoleNoAnsi = noConsoleNoAnsi; + this.eventSerializer = createSerializer(config, replace, eventPattern, DEFAULT_CONVERSION_PATTERN, patternSelector, + alwaysWriteExceptions, noConsoleNoAnsi); + this.headerSerializer = createSerializer(config, replace, headerPattern, null, patternSelector, + alwaysWriteExceptions, noConsoleNoAnsi); + this.footerSerializer = createSerializer(config, replace, footerPattern, null, patternSelector, + alwaysWriteExceptions, noConsoleNoAnsi); + } + + private Serializer createSerializer(final Configuration configuration, final RegexReplacement replace, + final String pattern, final String defaultPattern, final PatternSelector patternSelector, + final boolean alwaysWriteExceptions, final boolean noConsoleNoAnsi) { + if (Strings.isEmpty(pattern) && Strings.isEmpty(defaultPattern)) { + return null; + } if (patternSelector == null) { - serializer = new PatternSerializer(); - final PatternParser parser = createPatternParser(config); + final PatternParser parser = createPatternParser(configuration); try { - List<PatternFormatter> list = parser.parse(pattern == null ? DEFAULT_CONVERSION_PATTERN : pattern, - this.alwaysWriteExceptions, this.noConsoleNoAnsi); - this.formatters = list.toArray(new PatternFormatter[0]); - } catch (RuntimeException ex) { + final List<PatternFormatter> list = parser.parse(pattern == null ? defaultPattern : pattern, + alwaysWriteExceptions, noConsoleNoAnsi); + final PatternFormatter[] formatters = list.toArray(new PatternFormatter[0]); + return new PatternSerializer(formatters, replace); + } catch (final RuntimeException ex) { throw new IllegalArgumentException("Cannot parse pattern '" + pattern + "'", ex); } - } else { - this.formatters = null; - serializer = new PatternSelectorSerializer(); } - } - - private byte[] strSubstitutorReplace(final byte... b) { - if (b != null && config != null) { - return getBytes(config.getStrSubstitutor().replace(new String(b, getCharset()))); - } - return b; + return new PatternSelectorSerializer(patternSelector, replace); } @Override public byte[] getHeader() { - return strSubstitutorReplace(super.getHeader()); + return serializeHeaderFooter(headerSerializer); } @Override public byte[] getFooter() { - return strSubstitutorReplace(super.getFooter()); + return serializeHeaderFooter(footerSerializer); + } + + private byte[] serializeHeaderFooter(final Serializer serializer) { + if (serializer == null) { + return null; + } + final LoggerConfig rootLogger = config.getRootLogger(); + // Using "" for the FQCN, does it matter? + final LogEvent logEvent = rootLogger.getLogEventFactory().createEvent(rootLogger.getName(), null, Strings.EMPTY, + rootLogger.getLevel(), null, null, null); + return StringEncoder.toBytes(serializer.toSerializable(logEvent), getCharset()); } /** @@ -186,8 +188,7 @@ 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<>(); result.put("structured", "false"); result.put("formatType", "conversion"); @@ -203,7 +204,7 @@ public final class PatternLayout extends AbstractStringLayout { */ @Override public String toSerializable(final LogEvent event) { - return serializer.toSerializable(event); + return eventSerializer.toSerializable(event); } /** @@ -246,9 +247,9 @@ public final class PatternLayout extends AbstractStringLayout { * 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 + * @param headerPattern * The footer to place at the top of the document, once. - * @param footer + * @param footerPattern * The footer to place at the bottom of the document, once. * @return The PatternLayout. */ @@ -262,8 +263,8 @@ public final class PatternLayout extends AbstractStringLayout { @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) { + @PluginAttribute("header") final String headerPattern, + @PluginAttribute("footer") final String footerPattern) { return newBuilder() .withPattern(pattern) .withPatternSelector(patternSelector) @@ -272,18 +273,27 @@ public final class PatternLayout extends AbstractStringLayout { .withCharset(charset) .withAlwaysWriteExceptions(alwaysWriteExceptions) .withNoConsoleNoAnsi(noConsoleNoAnsi) - .withHeader(header) - .withFooter(footer) + .withHeader(headerPattern) + .withFooter(footerPattern) .build(); } - private interface Serializer { - - String toSerializable(final LogEvent event); + + String toSerializable(final LogEvent event); } - private class PatternSerializer implements Serializer { + private static class PatternSerializer implements Serializer { + + private final PatternFormatter[] formatters; + private final RegexReplacement replace; + + private PatternSerializer(final PatternFormatter[] formatters, final RegexReplacement replace) { + super(); + this.formatters = formatters; + this.replace = replace; + } + @Override public String toSerializable(final LogEvent event) { final StringBuilder buf = getStringBuilder(); @@ -299,11 +309,21 @@ public final class PatternLayout extends AbstractStringLayout { } } - private class PatternSelectorSerializer implements Serializer { + private static class PatternSelectorSerializer implements Serializer { + + private final PatternSelector patternSelector; + private final RegexReplacement replace; + + private PatternSelectorSerializer(final PatternSelector patternSelector, final RegexReplacement replace) { + super(); + this.patternSelector = patternSelector; + this.replace = replace; + } + @Override public String toSerializable(final LogEvent event) { final StringBuilder buf = getStringBuilder(); - PatternFormatter[] formatters = patternSelector.getFormatters(event); + final PatternFormatter[] formatters = patternSelector.getFormatters(event); final int len = formatters.length; for (int i = 0; i < len; i++) { formatters[i].format(event, buf); @@ -328,21 +348,21 @@ public final class PatternLayout extends AbstractStringLayout { } /** - * Creates a PatternLayout using the default options and the given - * configuration. These options include using UTF-8, the default conversion - * pattern, exceptions being written, and with ANSI escape codes. + * Creates a PatternLayout using the default options and the given configuration. These options include using UTF-8, + * the default conversion pattern, exceptions being written, and with ANSI escape codes. * * @param configuration The Configuration. * * @return the PatternLayout. * @see #DEFAULT_CONVERSION_PATTERN Default conversion pattern */ - public static PatternLayout createDefaultLayout(Configuration configuration) { + public static PatternLayout createDefaultLayout(final Configuration configuration) { return newBuilder().withConfiguration(configuration).build(); } /** * Creates a builder for a custom PatternLayout. + * * @return a PatternLayout builder. */ @PluginBuilderFactory @@ -398,7 +418,6 @@ public final class PatternLayout extends AbstractStringLayout { return this; } - public Builder withConfiguration(final Configuration configuration) { this.configuration = configuration; return this; http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c3d8ce82/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/PatternLayoutTest.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/PatternLayoutTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/PatternLayoutTest.java index 53335d6..b10c8f5 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/PatternLayoutTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/PatternLayoutTest.java @@ -82,6 +82,7 @@ public class PatternLayoutTest { assertTrue(headerStr, headerStr.contains("(build ")); assertTrue(headerStr, headerStr.contains(" from ")); assertTrue(headerStr, headerStr.contains(" architecture: ")); + assertFalse(headerStr, headerStr.contains("%d{UNIX}")); // final byte[] footer = layout.getFooter(); assertNotNull("No footer", footer); @@ -91,6 +92,7 @@ public class PatternLayoutTest { assertTrue(footerStr, footerStr.contains("(build ")); assertTrue(footerStr, footerStr.contains(" from ")); assertTrue(footerStr, footerStr.contains(" architecture: ")); + assertFalse(footerStr, footerStr.contains("%d{UNIX}")); } /** @@ -323,7 +325,7 @@ public class PatternLayoutTest { .setIncludeLocation(true) .setMessage(new SimpleMessage("entry")).build(); final String result1 = new FauxLogger().formatEvent(event1, layout); - final String expectSuffix1 = String.format("====== PatternLayoutTest.testPatternSelector:325 entry ======%n"); + final String expectSuffix1 = String.format("====== PatternLayoutTest.testPatternSelector:327 entry ======%n"); assertTrue("Unexpected result: " + result1, result1.endsWith(expectSuffix1)); final LogEvent event2 = Log4jLogEvent.newBuilder() // .setLoggerName(this.getClass().getName()).setLoggerFqcn("org.apache.logging.log4j.core.Logger") // http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c3d8ce82/src/changes/changes.xml ---------------------------------------------------------------------- diff --git a/src/changes/changes.xml b/src/changes/changes.xml index a4b6798..6c36be1 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -28,6 +28,9 @@ Added option to discard events below a certain log level if the async logger ring buffer or async appender queue remaining capacity falls below a certain ratio. </action> + <action issue="LOG4J2-1237" dev="ggregory" type="add" due-to="Mike Calmus, Gary Gregory"> + Make PatternLayout header and footer accept a pattern. + </action> <action issue="LOG4J2-908" dev="ggregory" type="fix" due-to="Konstantinos Liakos, Patrick Flaherty, Robin Coe, Gary Gregory"> JSONLayout doesn't add a comma between log events. </action>
