You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by mi...@apache.org on 2016/01/12 17:50:42 UTC

[34/50] logging-log4j2 git commit: [LOG4J2-1237] Make PatternLayout header and footer accept a pattern.

[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/LOG4J-1181
Commit: c3d8ce8240202ff92da0c239092b13aa347bae36
Parents: 6944134
Author: ggregory <gg...@apache.org>
Authored: Sun Jan 3 22:14:20 2016 -0800
Committer: ggregory <gg...@apache.org>
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>