You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rg...@apache.org on 2015/09/23 20:42:24 UTC

logging-log4j2 git commit: LOG4J2-1129 - Allow PatternLayout to select a pattern to use based on some selection criteria

Repository: logging-log4j2
Updated Branches:
  refs/heads/master fee199f66 -> fe021d94b


LOG4J2-1129 - Allow PatternLayout to select a pattern to use based on some selection criteria


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/fe021d94
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/fe021d94
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/fe021d94

Branch: refs/heads/master
Commit: fe021d94b385c52eb91b486bd26120630efef53d
Parents: fee199f
Author: Ralph Goers <rg...@nextiva.com>
Authored: Wed Sep 23 11:42:02 2015 -0700
Committer: Ralph Goers <rg...@nextiva.com>
Committed: Wed Sep 23 11:42:02 2015 -0700

----------------------------------------------------------------------
 .../core/layout/MarkerPatternSelector.java      | 125 +++++++++++++++
 .../log4j/core/layout/PatternLayout.java        |  97 +++++++++---
 .../logging/log4j/core/layout/PatternMatch.java | 154 +++++++++++++++++++
 .../log4j/core/layout/PatternSelector.java      |  30 ++++
 .../logging/log4j/core/PatternSelectorTest.java |  53 +++++++
 .../core/appender/ConsoleAppenderTest.java      |   2 +-
 .../log4j/core/layout/PatternLayoutTest.java    |  37 ++++-
 .../test/resources/log4j-patternSelector.xml    |  33 ++++
 .../log4j/perf/jmh/PatternLayoutBenchmark.java  |  16 +-
 .../jmh/PatternLayoutComparisonBenchmark.java   |   2 +-
 src/changes/changes.xml                         |   3 +
 11 files changed, 517 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/fe021d94/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/MarkerPatternSelector.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/MarkerPatternSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/MarkerPatternSelector.java
new file mode 100644
index 0000000..f54cf3a
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/MarkerPatternSelector.java
@@ -0,0 +1,125 @@
+/*
+ * 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 org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.core.LogEvent;
+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.pattern.PatternFormatter;
+import org.apache.logging.log4j.core.pattern.PatternParser;
+import org.apache.logging.log4j.core.util.KeyValuePair;
+import org.apache.logging.log4j.status.StatusLogger;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Selects the pattern to use based on the Marker in the LogEvent.
+ */
+@Plugin(name = "MarkerPatternSelector", category = Node.CATEGORY, elementType = PatternSelector.ELEMENT_TYPE, printObject = true)
+public class MarkerPatternSelector implements PatternSelector {
+
+    private final Map<String, PatternFormatter[]> formatterMap = new HashMap<>();
+
+    private final Map<String, String> patternMap = new HashMap<>();
+
+    private final PatternFormatter[] defaultFormatters;
+
+    private final String defaultPattern;
+
+    private static Logger LOGGER = StatusLogger.getLogger();
+
+
+    public MarkerPatternSelector(final PatternMatch[] properties, final String defaultPattern,
+                                 final boolean alwaysWriteExceptions, final boolean noConsoleNoAnsi,
+                                 final Configuration config) {
+        final PatternParser parser = PatternLayout.createPatternParser(config);
+        for (PatternMatch property : properties) {
+            try {
+                List<PatternFormatter> list = parser.parse(property.getPattern(), alwaysWriteExceptions, noConsoleNoAnsi);
+                formatterMap.put(property.getKey(), list.toArray(new PatternFormatter[list.size()]));
+                patternMap.put(property.getKey(), property.getPattern());
+            } catch (RuntimeException ex) {
+                throw new IllegalArgumentException("Cannot parse pattern '" + property.getPattern() + "'", ex);
+            }
+        }
+        try {
+            List<PatternFormatter> list = parser.parse(defaultPattern, alwaysWriteExceptions, noConsoleNoAnsi);
+            defaultFormatters = list.toArray(new PatternFormatter[list.size()]);
+            this.defaultPattern = defaultPattern;
+        } catch (RuntimeException ex) {
+            throw new IllegalArgumentException("Cannot parse pattern '" + defaultPattern + "'", ex);
+        }
+    }
+
+    @Override
+    public PatternFormatter[] getFormatters(LogEvent event) {
+        Marker marker = event.getMarker();
+        if (marker == null) {
+            return defaultFormatters;
+        }
+        for (String key : formatterMap.keySet()) {
+            if (marker.isInstanceOf(key)) {
+                return formatterMap.get(key);
+            }
+        }
+        return defaultFormatters;
+    }
+
+
+    @PluginFactory
+    public static MarkerPatternSelector createSelector(@PluginElement("PatternMatch") final PatternMatch[] properties,
+                                                       @PluginAttribute("defaultPattern") String defaultPattern,
+                                                       @PluginAttribute(value = "alwaysWriteExceptions", defaultBoolean = true) final boolean alwaysWriteExceptions,
+                                                       @PluginAttribute(value = "noConsoleNoAnsi", defaultBoolean = false) final boolean noConsoleNoAnsi,
+                                                       @PluginConfiguration final Configuration config) {
+        if (defaultPattern == null) {
+            defaultPattern = PatternLayout.DEFAULT_CONVERSION_PATTERN;
+        }
+        if (properties == null || properties.length == 0) {
+            LOGGER.warn("No marker patterns were provided");
+        }
+        return new MarkerPatternSelector(properties, defaultPattern, alwaysWriteExceptions,
+                noConsoleNoAnsi, config);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        boolean first = true;
+        for (Map.Entry<String, String> entry : patternMap.entrySet()) {
+            if (!first) {
+                sb.append(", ");
+            }
+            sb.append("key=\"").append(entry.getKey()).append("\", pattern=\"").append(entry.getValue()).append("\"");
+            first = false;
+        }
+        if (!first) {
+            sb.append(", ");
+        }
+        sb.append("default=\"").append(defaultPattern).append("\"");
+        return sb.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/fe021d94/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 4ea5627..e5b78d3 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
@@ -93,6 +93,10 @@ public final class PatternLayout extends AbstractStringLayout {
      */
     private final String conversionPattern;
 
+    private final PatternSelector patternSelector;
+
+    private final Serializer serializer;
+
 
     /**
      * The current Configuration.
@@ -111,6 +115,7 @@ public final class PatternLayout extends AbstractStringLayout {
      * @param config The Configuration.
      * @param replace The regular expression to match.
      * @param pattern 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).
@@ -119,21 +124,29 @@ public final class PatternLayout extends AbstractStringLayout {
      * @param header
      */
     private PatternLayout(final Configuration config, final RegexReplacement replace, final String pattern,
-                          final Charset charset, final boolean alwaysWriteExceptions, final boolean noConsoleNoAnsi,
+                          final PatternSelector patternSelector, 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;
+        this.patternSelector = patternSelector;
         this.config = config;
         this.alwaysWriteExceptions = alwaysWriteExceptions;
         this.noConsoleNoAnsi = noConsoleNoAnsi;
-        final PatternParser parser = createPatternParser(config);
-        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) {
-            throw new IllegalArgumentException("Cannot parse pattern '" + pattern + "'", ex);
+        if (patternSelector == null) {
+            serializer = new PatternSerializer();
+            final PatternParser parser = createPatternParser(config);
+            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) {
+                throw new IllegalArgumentException("Cannot parse pattern '" + pattern + "'", ex);
+            }
+        } else {
+            this.formatters = null;
+            serializer = new PatternSelectorSerializer();
         }
     }
 
@@ -191,16 +204,7 @@ public final class PatternLayout extends AbstractStringLayout {
      */
     @Override
     public String toSerializable(final LogEvent event) {
-        final StringBuilder buf = prepareStringBuilder(strBuilder);
-        final int len = formatters.length;
-        for (int i = 0; i < len; i++) {
-            formatters[i].format(event, buf);
-        }
-        String str = buf.toString();
-        if (replace != null) {
-            str = replace.format(str);
-        }
-        return str;
+        return serializer.toSerializable(event);
     }
 
     /**
@@ -223,7 +227,7 @@ public final class PatternLayout extends AbstractStringLayout {
 
     @Override
     public String toString() {
-        return conversionPattern;
+        return patternSelector == null ? conversionPattern : patternSelector.toString();
     }
 
     /**
@@ -250,6 +254,7 @@ public final class PatternLayout extends AbstractStringLayout {
     @PluginFactory
     public static PatternLayout createLayout(
             @PluginAttribute(value = "pattern", defaultString = DEFAULT_CONVERSION_PATTERN) final String pattern,
+            @PluginElement("PatternSelector") final PatternSelector patternSelector,
             @PluginConfiguration final Configuration config,
             @PluginElement("Replace") final RegexReplacement replace,
             @PluginAttribute(value = "charset", defaultString = "UTF-8") final Charset charset,
@@ -259,6 +264,7 @@ public final class PatternLayout extends AbstractStringLayout {
             @PluginAttribute("footer") final String footer) {
         return newBuilder()
             .withPattern(pattern)
+            .withPatternSelector(patternSelector)
             .withConfiguration(config)
             .withRegexReplacement(replace)
             .withCharset(charset)
@@ -269,6 +275,47 @@ public final class PatternLayout extends AbstractStringLayout {
             .build();
     }
 
+
+    private interface Serializer {
+
+        String toSerializable(final LogEvent event);
+    }
+
+    private class PatternSerializer implements Serializer {
+        @Override
+        public String toSerializable(final LogEvent event) {
+            final StringBuilder buf = strBuilder.get();
+            buf.setLength(0);
+            final int len = formatters.length;
+            for (int i = 0; i < len; i++) {
+                formatters[i].format(event, buf);
+            }
+            String str = buf.toString();
+            if (replace != null) {
+                str = replace.format(str);
+            }
+            return str;
+        }
+    }
+
+    private class PatternSelectorSerializer implements Serializer {
+        @Override
+        public String toSerializable(final LogEvent event) {
+            final StringBuilder buf = strBuilder.get();
+            buf.setLength(0);
+            PatternFormatter[] formatters = patternSelector.getFormatters(event);
+            final int len = formatters.length;
+            for (int i = 0; i < len; i++) {
+                formatters[i].format(event, buf);
+            }
+            String str = buf.toString();
+            if (replace != null) {
+                str = replace.format(str);
+            }
+            return str;
+        }
+    }
+
     /**
      * Creates a PatternLayout using the default options. These options include using UTF-8, the default conversion
      * pattern, exceptions being written, and with ANSI escape codes.
@@ -300,6 +347,9 @@ public final class PatternLayout extends AbstractStringLayout {
         @PluginBuilderAttribute
         private String pattern = PatternLayout.DEFAULT_CONVERSION_PATTERN;
 
+        @PluginElement("PatternSelector")
+        private PatternSelector patternSelector = null;
+
         @PluginConfiguration
         private Configuration configuration = null;
 
@@ -332,6 +382,11 @@ public final class PatternLayout extends AbstractStringLayout {
             return this;
         }
 
+        public Builder withPatternSelector(final PatternSelector patternSelector) {
+            this.patternSelector = patternSelector;
+            return this;
+        }
+
 
         public Builder withConfiguration(final Configuration configuration) {
             this.configuration = configuration;
@@ -374,8 +429,8 @@ public final class PatternLayout extends AbstractStringLayout {
             if (configuration == null) {
                 configuration = new DefaultConfiguration();
             }
-            return new PatternLayout(configuration, regexReplacement, pattern, charset, alwaysWriteExceptions,
-                noConsoleNoAnsi, header, footer);
+            return new PatternLayout(configuration, regexReplacement, pattern, patternSelector, charset,
+                alwaysWriteExceptions, noConsoleNoAnsi, header, footer);
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/fe021d94/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternMatch.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternMatch.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternMatch.java
new file mode 100644
index 0000000..2633c54
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternMatch.java
@@ -0,0 +1,154 @@
+/*
+ * 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 org.apache.logging.log4j.core.config.Node;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
+
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+
+/**
+ * PatternMatch configuration item.
+ *
+ * @since 2.4.1 implements {@link Serializable}
+ */
+@Plugin(name = "PatternMatch", category = Node.CATEGORY, printObject = true)
+public final class PatternMatch implements Serializable {
+
+    private static final long serialVersionUID = 4331228262821046877L;
+
+    private final String key;
+    private final String pattern;
+
+    /**
+     * Constructs a key/value pair. The constructor should only be called from test classes.
+     * @param key The key.
+     * @param pattern The value.
+     */
+    public PatternMatch(final String key, final String pattern) {
+        this.key = key;
+        this.pattern = pattern;
+    }
+
+    /**
+     * Returns the key.
+     * @return the key.
+     */
+    public String getKey() {
+        return key;
+    }
+
+    /**
+     * Returns the pattern.
+     * @return The pattern.
+     */
+    public String getPattern() {
+        return pattern;
+    }
+
+    @Override
+    public String toString() {
+        return key + '=' + pattern;
+    }
+
+    @PluginBuilderFactory
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    protected Object writeReplace() throws ObjectStreamException {
+        return newBuilder().setKey(this.key).setPattern(this.pattern);
+    }
+
+    private void readObject(final ObjectInputStream stream) throws InvalidObjectException {
+        throw new InvalidObjectException("Builder proxy required");
+    }
+
+    public static class Builder implements org.apache.logging.log4j.core.util.Builder<PatternMatch>, Serializable {
+
+        private static final long serialVersionUID = 1L;
+
+        @PluginBuilderAttribute
+        private String key;
+
+        @PluginBuilderAttribute
+        private String pattern;
+
+        public Builder setKey(final String key) {
+            this.key = key;
+            return this;
+        }
+
+        public Builder setPattern(final String pattern) {
+            this.pattern = pattern;
+            return this;
+        }
+
+        @Override
+        public PatternMatch build() {
+            return new PatternMatch(key, pattern);
+        }
+
+        protected Object readResolve() throws ObjectStreamException {
+            return new PatternMatch(key, pattern);
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((key == null) ? 0 : key.hashCode());
+        result = prime * result + ((pattern == null) ? 0 : pattern.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final PatternMatch other = (PatternMatch) obj;
+        if (key == null) {
+            if (other.key != null) {
+                return false;
+            }
+        } else if (!key.equals(other.key)) {
+            return false;
+        }
+        if (pattern == null) {
+            if (other.pattern != null) {
+                return false;
+            }
+        } else if (!pattern.equals(other.pattern)) {
+            return false;
+        }
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/fe021d94/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternSelector.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternSelector.java
new file mode 100644
index 0000000..78c9b49
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternSelector.java
@@ -0,0 +1,30 @@
+/*
+ * 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 org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.pattern.PatternFormatter;
+
+/**
+ * Allows different patterns to be used with the PatternLayout based on some selection criteria.
+ */
+public interface PatternSelector {
+
+    String ELEMENT_TYPE = "patternSelector";
+
+    PatternFormatter[] getFormatters(LogEvent event);
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/fe021d94/log4j-core/src/test/java/org/apache/logging/log4j/core/PatternSelectorTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/PatternSelectorTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/PatternSelectorTest.java
new file mode 100644
index 0000000..06a5ef3
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/PatternSelectorTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.junit.LoggerContextRule;
+import org.apache.logging.log4j.test.appender.ListAppender;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.junit.Assert.*;
+/**
+ *
+ */
+public class PatternSelectorTest {
+
+
+    private static final String CONFIG = "log4j-patternSelector.xml";
+
+    @ClassRule
+    public static LoggerContextRule context = new LoggerContextRule(CONFIG);
+
+    @Test
+    public void testPatternSelector() throws Exception {
+        org.apache.logging.log4j.Logger logger = LogManager.getLogger("TestPatternSelector");
+        logger.entry();
+        logger.info("Hello World");
+        logger.exit();
+        final ListAppender app = (ListAppender) context.getRequiredAppender("List");
+        assertNotNull("No ListAppender", app);
+        List<String> messages = app.getMessages();
+        assertNotNull("No Messages", messages);
+        assertTrue("Incorrect number of messages. Expected 3, Actual " + messages.size(), messages.size() == 3);
+        assertEquals("[TRACE] TestPatternSelector ====== o.a.l.l.c.PatternSelectorTest.testPatternSelector:42 entry ======\n", messages.get(0));
+        assertEquals("[INFO ] TestPatternSelector Hello World\n", messages.get(1));
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/fe021d94/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ConsoleAppenderTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ConsoleAppenderTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ConsoleAppenderTest.java
index 0c002a0..16417b9 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ConsoleAppenderTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ConsoleAppenderTest.java
@@ -98,7 +98,7 @@ public class ConsoleAppenderTest {
 
             mocks.replayAll();
             systemSetter.systemSet(psMock);
-            final Layout<String> layout = PatternLayout.createLayout(null, null, null, null, false, false, null, null);
+            final Layout<String> layout = PatternLayout.createLayout(null, null, null, null, null, false, false, null, null);
             final ConsoleAppender app = ConsoleAppender.createAppender(layout, null, targetName, "Console", "false",
                     "false");
             app.start();

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/fe021d94/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 bac234c..296492b 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
@@ -24,14 +24,14 @@ import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 
 import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.MarkerManager;
 import org.apache.logging.log4j.ThreadContext;
-import org.apache.logging.log4j.core.BasicConfigurationFactory;
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.Logger;
-import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.*;
 import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.config.Property;
 import org.apache.logging.log4j.core.impl.Log4jLogEvent;
 import org.apache.logging.log4j.core.lookup.MainMapLookup;
+import org.apache.logging.log4j.core.util.KeyValuePair;
 import org.apache.logging.log4j.message.SimpleMessage;
 import org.apache.logging.log4j.util.Strings;
 import org.junit.After;
@@ -263,4 +263,33 @@ public class PatternLayoutTest {
                 .withConfiguration(ctx.getConfiguration()).withCharset(StandardCharsets.UTF_8).build();
         assertEquals(StandardCharsets.UTF_8, layout.getCharset());
     }
+
+    @Test
+    public void testPatternSelector() throws Exception {
+        PatternMatch[] patterns = new PatternMatch[1];
+        patterns[0] = new PatternMatch("FLOW", "%d %-5p [%t]: ====== %C{1}.%M:%L %m ======%n");
+        PatternSelector selector = MarkerPatternSelector.createSelector(patterns, "%d %-5p [%t]: %m%n", true, true, ctx.getConfiguration());
+        final PatternLayout layout = PatternLayout.newBuilder().withPatternSelector(selector)
+                .withConfiguration(ctx.getConfiguration()).build();
+        final LogEvent event1 = Log4jLogEvent.newBuilder() //
+                .setLoggerName(this.getClass().getName()).setLoggerFqcn("org.apache.logging.log4j.core.layout.PatternLayoutTest$FauxLogger")
+                .setMarker(MarkerManager.getMarker("FLOW"))
+                .setLevel(Level.TRACE) //
+                .setIncludeLocation(true)
+                .setMessage(new SimpleMessage("entry")).build();
+        final String result1 = new FauxLogger().formatEvent(event1, layout);
+        assertTrue("Unexpected result: " + result1, result1.endsWith("====== PatternLayoutTest.testPatternSelector:280 entry ======\n"));
+        final LogEvent event2 = Log4jLogEvent.newBuilder() //
+                .setLoggerName(this.getClass().getName()).setLoggerFqcn("org.apache.logging.log4j.core.Logger") //
+                .setLevel(Level.INFO) //
+                .setMessage(new SimpleMessage("Hello, world 1!")).build();
+        final String result2 = new String(layout.toByteArray(event2));
+        assertTrue("Unexpected result: " + result2, result2.endsWith("Hello, world 1!\n"));
+    }
+
+    public class FauxLogger {
+        public String formatEvent(LogEvent event, Layout layout) {
+            return new String(layout.toByteArray(event));
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/fe021d94/log4j-core/src/test/resources/log4j-patternSelector.xml
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/log4j-patternSelector.xml b/log4j-core/src/test/resources/log4j-patternSelector.xml
new file mode 100644
index 0000000..124aaf8
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j-patternSelector.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<Configuration status="WARN">
+  <Appenders>
+    <List name="List">
+      <PatternLayout>
+        <MarkerPatternSelector defaultPattern="[%-5level] %c{1.} %msg%n">
+          <PatternMatch key="FLOW" pattern="[%-5level] %c{1.} ====== %C{1.}.%M:%L %msg ======%n"/>
+        </MarkerPatternSelector>
+      </PatternLayout>
+    </List>
+  </Appenders>
+  <Loggers>
+    <Root level="trace">
+      <AppenderRef ref="List" />
+    </Root>
+  </Loggers>
+</Configuration>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/fe021d94/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/PatternLayoutBenchmark.java
----------------------------------------------------------------------
diff --git a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/PatternLayoutBenchmark.java b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/PatternLayoutBenchmark.java
index 70ee7d1..f66637a 100644
--- a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/PatternLayoutBenchmark.java
+++ b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/PatternLayoutBenchmark.java
@@ -58,14 +58,14 @@ public class PatternLayoutBenchmark {
     private static final String DEFAULT_ENCODING = CHARSET_DEFAULT.name();
     private static final String STRING_SHIFT_JIS = "SHIFT_JIS";
     private static final Charset CHARSET_SHIFT_JIS = Charset.forName(STRING_SHIFT_JIS);
-    private final PatternLayout PATTERN_M = PatternLayout.createLayout("%m%n", null, null, CHARSET_DEFAULT, false, true, null, null);
-    private final PatternLayout PATTERN_SPACE = PatternLayout.createLayout(" ", null, null, CHARSET_DEFAULT, false, true, null, null);
-    private final PatternLayout PATTERN_M_C = PatternLayout.createLayout("%c %m%n", null, null, CHARSET_DEFAULT, false, true, null, null);
-    private final PatternLayout PATTERN_M_C_D = PatternLayout.createLayout("%d %c %m%n", null, null, CHARSET_DEFAULT, false, true, null, null);
-    private final PatternLayout PATTERN_M_D = PatternLayout.createLayout("%d %m%n", null, null, CHARSET_DEFAULT, false, true, null, null);
-    private final PatternLayout PATTERN_M_EX = PatternLayout.createLayout("%m %ex%n", null, null, CHARSET_DEFAULT, false, true, null, null);
-    private final PatternLayout PATTERN_M_D_EX = PatternLayout.createLayout("%d %m%ex%n", null, null, CHARSET_DEFAULT, false, true, null, null);
-    private final PatternLayout PATTERN_M_C_D_EX = PatternLayout.createLayout("%d %c %m%ex%n", null, null, CHARSET_DEFAULT, false, true, null, null);
+    private final PatternLayout PATTERN_M = PatternLayout.createLayout("%m%n", null, null, null, CHARSET_DEFAULT, false, true, null, null);
+    private final PatternLayout PATTERN_SPACE = PatternLayout.createLayout(" ", null, null, null, CHARSET_DEFAULT, false, true, null, null);
+    private final PatternLayout PATTERN_M_C = PatternLayout.createLayout("%c %m%n", null, null, null, CHARSET_DEFAULT, false, true, null, null);
+    private final PatternLayout PATTERN_M_C_D = PatternLayout.createLayout("%d %c %m%n", null, null, null, CHARSET_DEFAULT, false, true, null, null);
+    private final PatternLayout PATTERN_M_D = PatternLayout.createLayout("%d %m%n", null, null, null, CHARSET_DEFAULT, false, true, null, null);
+    private final PatternLayout PATTERN_M_EX = PatternLayout.createLayout("%m %ex%n", null, null, null, CHARSET_DEFAULT, false, true, null, null);
+    private final PatternLayout PATTERN_M_D_EX = PatternLayout.createLayout("%d %m%ex%n", null, null, null, CHARSET_DEFAULT, false, true, null, null);
+    private final PatternLayout PATTERN_M_C_D_EX = PatternLayout.createLayout("%d %c %m%ex%n", null, null, null, CHARSET_DEFAULT, false, true, null, null);
 
     private static LogEvent createLogEvent() {
         final Marker marker = null;

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/fe021d94/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/PatternLayoutComparisonBenchmark.java
----------------------------------------------------------------------
diff --git a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/PatternLayoutComparisonBenchmark.java b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/PatternLayoutComparisonBenchmark.java
index 3acd892..0919835 100644
--- a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/PatternLayoutComparisonBenchmark.java
+++ b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/PatternLayoutComparisonBenchmark.java
@@ -59,7 +59,7 @@ public class PatternLayoutComparisonBenchmark {
     final static LogEvent LOG4J2EVENT = createLog4j2Event();
     private static final Charset CHARSET_DEFAULT = Charset.defaultCharset();
     private static final String LOG4JPATTERN = "%d %5p [%t] %c{1} %X{transactionId} - %m%n";
-    private final PatternLayout LOG4J2_PATTERN_LAYOUT = PatternLayout.createLayout(LOG4JPATTERN,
+    private final PatternLayout LOG4J2_PATTERN_LAYOUT = PatternLayout.createLayout(LOG4JPATTERN, null,
             null, null, CHARSET_DEFAULT, false, true, null, null);
 
     private static LogEvent createLog4j2Event() {

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/fe021d94/src/changes/changes.xml
----------------------------------------------------------------------
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 93ba5f1..a8a0c8c 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -24,6 +24,9 @@
   </properties>
   <body>
     <release version="2.4.1" date="2015-MM-DD" description="GA Release 2.4.1">
+      <action issue="LOG4J2-1129" dev="rgoers" type="add">
+        Allow PatternLayout to select a pattern to use based on some selection criteria"
+      </action>
       <action issue="LOG4J2-1127" dev="ggregory" type="fix">
         log4j2.xml cannot be parsed on Oracle Weblogic 12c</action>
       <action issue="LOG4J2-1128" dev="ggregory" type="update">