Add some new filters and make the debug messages for configuration more concise

git-svn-id: https://svn.apache.org/repos/asf/logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers@1029218 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/log4j2-api/src/main/java/org/apache/logging/log4j/Level.java b/log4j2-api/src/main/java/org/apache/logging/log4j/Level.java
index d987aa8..da075dc 100644
--- a/log4j2-api/src/main/java/org/apache/logging/log4j/Level.java
+++ b/log4j2-api/src/main/java/org/apache/logging/log4j/Level.java
@@ -18,13 +18,14 @@
 
 /**
  * Levels used for identifying the severity of an event. Levels are organized from most specific to least:<br>
- * OFF<br>
+ * OFF<br>    (most specific)
  * FATAL<br>
  * ERROR<br>
  * WARN<br>
  * INFO<br>
  * DEBUG<br>
  * TRACE<br>
+ * ALL<br>     (least specific)
  *
  * Typically, configuring a level in a filter or on a logger will cause logging events of that level and those
  * that are more specific to pass through the filter.
@@ -65,24 +66,24 @@
     }
 
     /**
-     * @doubt I really dislike the "greaterOrEqual" and "lessOrEqual" methods. I can never remember whether
-     * the test compares this level to the passed in level or the other way around. As it stands, this
-     * method is not intuitive as the name is greaterOrEqual but the test it does is <=.
+     * Compares this level against the level passed as an argument and returns true if this
+     * level is the same or more specific.
      *
-     * Compares the specified Level against this one.
      * @param level The level to check.
-     * @return True if the passed Level is more general or the same as this Level.
+     * @return True if the passed Level is more specific or the same as this Level.
      */
-    public boolean greaterOrEqual(Level level) {
+    public boolean isAtLeastAsSpecificAs(Level level) {
         return (intLevel <= level.intLevel);
     }
 
     /**
-     * Compares the specified Level against this one.
+     * Compares this level against the level passed as an argument and returns true if this
+     * level is the same or more specific.
+     *
      * @param level The level to check.
-     * @return True if the passed Level is more general or the same as this Level.
+     * @return True if the passed Level is more specific or the same as this Level.
      */
-    public boolean greaterOrEqual(int level) {
+    public boolean isAtLeastAsSpecificAs(int level) {
         return (intLevel <= level);
     }
 
diff --git a/log4j2-api/src/main/java/org/apache/logging/log4j/Marker.java b/log4j2-api/src/main/java/org/apache/logging/log4j/Marker.java
index c2ddf02..66e8c0a 100644
--- a/log4j2-api/src/main/java/org/apache/logging/log4j/Marker.java
+++ b/log4j2-api/src/main/java/org/apache/logging/log4j/Marker.java
@@ -24,42 +24,80 @@
  */
 public class Marker implements Serializable {
 
-  private static ConcurrentMap<String, Marker> markerMap = new ConcurrentHashMap<String, Marker>();
+    private static ConcurrentMap<String, Marker> markerMap = new ConcurrentHashMap<String, Marker>();
 
-  public static Marker getMarker(String name) {
-    return markerMap.putIfAbsent(name, new Marker(name));
-  }
-
-  public static Marker getMarker(String name, String parent) {
-    Marker parentMarker = markerMap.get(parent);
-    if (parentMarker == null) {
-      throw new IllegalArgumentException("Parent Marker " + parent + " has not been defined");
+    public static Marker getMarker(String name) {
+        markerMap.putIfAbsent(name, new Marker(name));
+        return markerMap.get(name);
     }
-    return getMarker(name, parentMarker);
-  }
 
-  public static Marker getMarker(String name, Marker parent) {
-    return markerMap.putIfAbsent(name, new Marker(name, parent));
-  }
+    public static Marker getMarker(String name, String parent) {
+        Marker parentMarker = markerMap.get(parent);
+        if (parentMarker == null) {
+            throw new IllegalArgumentException("Parent Marker " + parent + " has not been defined");
+        }
+        return getMarker(name, parentMarker);
+    }
 
-  private String name;
-  private Marker parent;
+    public static Marker getMarker(String name, Marker parent) {
+        markerMap.putIfAbsent(name, new Marker(name, parent));
+        return markerMap.get(name);
+    }
 
-  private Marker(String name) {
-    this.name = name;
-  }
+    private String name;
+    private Marker parent;
 
-  private Marker(String name, Marker parent) {
-    this.name = name;
-    this.parent = parent;
-  }
+    private Marker(String name) {
+        this.name = name;
+    }
 
-  public String getName() {
-    return this.name;
-  }
+    private Marker(String name, Marker parent) {
+        this.name = name;
+        this.parent = parent;
+    }
 
-  public Marker getParent() {
-    return this.parent;
-  }
+    public String getName() {
+        return this.name;
+    }
 
+    public Marker getParent() {
+        return this.parent;
+    }
+
+    public boolean isInstanceOf(Marker m) {
+        if (m == null) {
+            throw new IllegalArgumentException("A marker parameter is required");
+        }
+        Marker test = this;
+        do {
+            if (test == m) {
+                return true;
+            }
+            test = test.getParent();
+        } while (test != null);
+        return false;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        Marker marker = (Marker) o;
+
+        if (name != null ? !name.equals(marker.name) : marker.name != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return name != null ? name.hashCode() : 0;
+    }
 }
diff --git a/log4j2-api/src/main/java/org/apache/logging/log4j/internal/StatusConsoleListener.java b/log4j2-api/src/main/java/org/apache/logging/log4j/internal/StatusConsoleListener.java
index e9398fc..c5072dc 100644
--- a/log4j2-api/src/main/java/org/apache/logging/log4j/internal/StatusConsoleListener.java
+++ b/log4j2-api/src/main/java/org/apache/logging/log4j/internal/StatusConsoleListener.java
@@ -45,7 +45,7 @@
     }
 
     public void log(StatusData data) {
-        if (data.getLevel().greaterOrEqual(level) && !filtered(data)) {
+        if (data.getLevel().isAtLeastAsSpecificAs(level) && !filtered(data)) {
             System.out.println(data.getFormattedStatus());
         }
     }
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/Log4jLogEvent.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/Log4jLogEvent.java
index e43e3d2..342cf99 100644
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/Log4jLogEvent.java
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/Log4jLogEvent.java
@@ -100,6 +100,10 @@
         return throwable;
     }
 
+    public Marker getMarker() {
+        return marker;
+    }
+
     /**
      * @doubt Allows direct access to the map passed into the constructor, would allow appender
      * or layout to manipulate event as seen by other appenders.
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java
index e6e4cc3..442e87d 100644
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java
@@ -1,6 +1,7 @@
 package org.apache.logging.log4j.core;
 
 import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
 import org.apache.logging.log4j.message.Message;
 
 import java.util.Map;
@@ -37,6 +38,12 @@
     Message getMessage();
 
     /**
+     * Get the Marker associated with the event.
+     * @return Marker
+     */
+    Marker getMarker();
+
+    /**
      * Get thread name.
      * @return thread name, may be null.
      * @doubt guess this could go into a thread context object too.
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/AppenderBase.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/AppenderBase.java
index bc1bb1e..c09ab3d 100644
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/AppenderBase.java
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/AppenderBase.java
@@ -141,4 +141,8 @@
         return started;
     }
 
+    public String toString() {
+        return name;
+    }
+
 }
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java
index 4489cfc..db7768d 100644
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java
@@ -34,7 +34,7 @@
  * and then call getBytes which may use a configured encoding or the system
  * default. OTOH, a Writer cannot print byte streams.
  */
-@Plugin(name="Console",type="Core",elementType="appender")
+@Plugin(name="Console",type="Core",elementType="appender",printObject=true)
 public class ConsoleAppender extends OutputStreamAppender {
 
     public static final String LAYOUT = "layout";
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/FileAppender.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/FileAppender.java
index 96b49b5..c2d8daa 100644
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/FileAppender.java
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/FileAppender.java
@@ -31,7 +31,7 @@
 /**
  *
  */
-@Plugin(name="File",type="Core",elementType="appender")
+@Plugin(name="File",type="Core",elementType="appender",printObject=true)
 public class FileAppender extends OutputStreamAppender {
 
     public static final String FILE_NAME = "fileName";
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/ListAppender.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/ListAppender.java
index c1416de..21e0e0a 100644
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/ListAppender.java
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/ListAppender.java
@@ -32,7 +32,7 @@
  * This appender is primarily used for testing. Use in a real environment is discouraged as the
  * List could eventually grow to cause an OutOfMemoryError.
  */
- @Plugin(name="List",type="Core",elementType="appender")
+ @Plugin(name="List",type="Core",elementType="appender",printObject=true)
 public class ListAppender extends AppenderBase {
 
     private List<LogEvent> events = new ArrayList<LogEvent>();
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/config/BaseConfiguration.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/config/BaseConfiguration.java
index dbdeabd..977e0fe 100644
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/config/BaseConfiguration.java
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/config/BaseConfiguration.java
@@ -422,17 +422,22 @@
                         }
                         parms[index] = array;
                     } else {
-                        Class parmClass = parmClasses[index];                       
+                        Class parmClass = parmClasses[index];
+                        boolean present = false;
                         for (Node child : children) {
-                            sb.append(child.toString());
                             PluginType childType = child.getType();
                             if (elem.value().equals(childType.getElementName()) ||
                                 parmClass.isAssignableFrom(childType.getPluginClass())) {
+                                sb.append(child.toString());
+                                present = true;
                                 used.add(child);
                                 parms[index] = child.getObject();
                                 break;
                             }
                         }
+                        if (!present) {
+                            sb.append("null");
+                        }
                     }
                 }
             }
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java
index dc73f91..32ee25a 100644
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java
@@ -46,7 +46,7 @@
 /**
  *
  */
-@Plugin(name="logger",type="Core")
+@Plugin(name="logger",type="Core", printObject=true)
 public class LoggerConfig extends Filterable implements LogEventFactory {
 
     private List<String> appenderRefs = new ArrayList<String>();
@@ -175,6 +175,10 @@
         return new Log4jLogEvent(loggerName, marker, fqcn, level, data, t);
     }
 
+    public String toString() {
+        return name == null || name.length() == 0 ? "root" : name;
+    }
+
     @PluginFactory
     public static LoggerConfig createLogger(@PluginAttr("additivity") String additivity,
                                             @PluginAttr("level") String loggerLevel,
@@ -194,7 +198,7 @@
         return new LoggerConfig(name, appenderRefs, filters, level, additive);
     }
 
-    @Plugin(name = "root", type = "Core")
+    @Plugin(name = "root", type = "Core", printObject=true)
     public static class RootLogger extends LoggerConfig {
 
         @PluginFactory
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/DynamicThresholdFilter.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/DynamicThresholdFilter.java
new file mode 100644
index 0000000..e7e7ea0
--- /dev/null
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/DynamicThresholdFilter.java
@@ -0,0 +1,101 @@
+/*
+ * 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.filter;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttr;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.message.Message;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *
+ */
+@Plugin(name="StructuredData", type="Core", elementType="filter")
+public class DynamicThresholdFilter extends FilterBase {
+    private Map<String, Level> levelMap = new HashMap<String, Level>();
+    private Level defaultThreshold = Level.ERROR;
+    private String key;
+
+    private DynamicThresholdFilter(String key, Map<String, Level> pairs, Level defaultLevel,
+                                   Result onMatch, Result onMismatch) {
+        super(onMatch, onMismatch);
+        if (key == null) {
+            throw new NullPointerException("key cannot be null");
+        }
+        this.key = key;
+        this.levelMap = pairs;
+        this.defaultThreshold = defaultLevel;
+    }
+
+    public String getKey() {
+        return this.key;
+    }
+
+    public Result filter(Logger logger, Level level, Marker marker, String msg, Object[] params) {
+        return filter(level);
+    }
+
+    public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable t) {
+        return filter(level);
+    }
+
+    public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) {
+        return filter(level);
+    }
+
+    @Override
+    public Result filter(LogEvent event) {
+        return filter(event.getLevel());
+    }
+
+    private Result filter(Level level) {
+        Object value = ThreadContext.get(key);
+        if (value != null) {
+            Level ctxLevel = levelMap.get(value);
+            if (ctxLevel == null) {
+                ctxLevel = defaultThreshold;
+            }
+            return level.isAtLeastAsSpecificAs(ctxLevel) ? onMatch : onMismatch;
+        }
+        return Result.NEUTRAL;
+
+    }
+
+    @PluginFactory
+    public static DynamicThresholdFilter createFilter(@PluginAttr("key") String key,
+                                                      @PluginAttr("pairs") ValueLevelPair[] pairs,
+                                                      @PluginAttr("defaultThreshold") String level,
+                                                      @PluginAttr("onmatch") String match,
+                                                      @PluginAttr("onmismatch") String mismatch) {
+        Result onMatch = match == null ? null : Result.valueOf(match);
+        Result onMismatch = mismatch == null ? null : Result.valueOf(mismatch);
+        Map<String, Level> map = new HashMap<String, Level>();
+        for (ValueLevelPair pair : pairs) {
+            map.put(pair.getKey(), pair.getLevel());
+        }
+        Level l = Level.toLevel(level, Level.ERROR);
+        return new DynamicThresholdFilter(key, map, l, onMatch, onMismatch);
+    }
+}
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/FilterBase.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/FilterBase.java
index ed93863..c439fcb 100644
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/FilterBase.java
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/FilterBase.java
@@ -40,7 +40,7 @@
 
     protected final Result onMismatch;
 
-    protected static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger();
+    protected static final org.apache.logging.log4j.Logger logger = StatusLogger.getLogger();
 
     protected static final String ON_MATCH = "onmatch";
     protected static final String ON_MISMATCH = "onmismatch";
@@ -50,8 +50,8 @@
     }
 
     protected FilterBase(Result onMatch, Result onMismatch) {
-        this.onMatch = onMatch == null ? Result.ACCEPT : onMatch;
-        this.onMismatch = onMismatch == null ? Result.NEUTRAL : onMismatch;
+        this.onMatch = onMatch == null ? Result.NEUTRAL : onMatch;
+        this.onMismatch = onMismatch == null ? Result.DENY : onMismatch;
     }
 
     public void start() {
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/Filters.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/Filters.java
index 00652f0..a434293 100644
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/Filters.java
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/Filters.java
@@ -36,12 +36,7 @@
     private final List<Filter> filters;
     private final boolean hasFilters;
 
-    public Filters(Filter[] filters) {
-        this.filters = filters == null || filters.length == 0 ? new ArrayList<Filter>() : Arrays.asList(filters);
-        hasFilters = filters != null && filters.length > 0;
-    }
-
-    private Filters(List<Filter> filters) {
+    public Filters(List<Filter> filters) {
         if (filters == null) {
             this.filters = new ArrayList<Filter>();
             this.hasFilters = false;
@@ -97,7 +92,8 @@
 
     @PluginFactory
     public static Filters createFilters(@PluginElement("filters") Filter[] filters) {
-        return new Filters(filters);
+        List<Filter> f = filters == null || filters.length == 0 ? new ArrayList<Filter>() : Arrays.asList(filters);
+        return new Filters(f);
     }
 
 }
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/KeyValuePair.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/KeyValuePair.java
new file mode 100644
index 0000000..31b420f
--- /dev/null
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/KeyValuePair.java
@@ -0,0 +1,56 @@
+/*
+ * 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.filter;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttr;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+
+/**
+ *
+ */
+@Plugin(name="KeyValuePair", type="Core", printObject=true)
+public class KeyValuePair {
+
+    private final String key;
+    private final String value;
+
+    public KeyValuePair(String key, String value) {
+        this.key = key;
+        this.value = value;
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public String toString() {
+        return key + "=" + value;
+    }
+
+    @PluginFactory
+    public static KeyValuePair createPair(@PluginAttr("key") String key,
+                                          @PluginAttr("value")  String value) {
+
+        return new KeyValuePair(key, value);
+    }
+}
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/MDCFilter.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/MDCFilter.java
deleted file mode 100644
index 59e30a7..0000000
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/MDCFilter.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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.filter;
-
-import org.apache.logging.log4j.Level;
-import org.apache.logging.log4j.ThreadContext;
-import org.apache.logging.log4j.Marker;
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.Logger;
-import org.apache.logging.log4j.core.config.plugins.Plugin;
-import org.apache.logging.log4j.core.config.plugins.PluginAttr;
-import org.apache.logging.log4j.core.config.plugins.PluginFactory;
-import org.apache.logging.log4j.message.Message;
-
-/**
- *
- */
-@Plugin(name="MDC", type="Core", elementType="filter")
-public class MDCFilter extends FilterBase {
-    private final String key;
-    private final String value;
-
-    private static final String KEY = "key";
-    private static final String VALUE = "value";
-
-    public MDCFilter(String key, String value, Result onMatch, Result onMismatch) {
-        super(onMatch, onMismatch);
-        if (key == null) {
-            throw new NullPointerException("key cannot be null");
-        }
-        if (value == null) {
-            throw new NullPointerException("value cannot be null");
-        }
-        this.key = key;
-        this.value = value;
-    }
-
-    public String getKey() {
-        return this.key;
-    }
-
-    public String getValue() {
-        return this.value;
-    }
-     public Result filter(Logger logger, Level level, Marker marker, String msg, Object[] params) {
-        return filter(ThreadContext.get(key));
-    }
-
-    public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable t) {
-        return filter(ThreadContext.get(key));
-    }
-
-    public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) {
-        return filter(ThreadContext.get(key));
-    }
-
-    @Override
-    public Result filter(LogEvent event) {
-        return filter(event.getContextMap().get(key));
-    }
-
-    private Result filter(Object val) {
-        return this.value.equals(val) ? onMatch : onMismatch;
-    }
-
-    @PluginFactory
-    public static MDCFilter createFilter(@PluginAttr("key") String key,
-                                         @PluginAttr("value") String value,
-                                         @PluginAttr("onmatch") String match,
-                                         @PluginAttr("onmismatch") String mismatch) {
-        Result onMatch = match == null ? null : Result.valueOf(match);
-        Result onMismatch = mismatch == null ? null : Result.valueOf(mismatch);
-        return new MDCFilter(key, value, onMatch, onMismatch);
-    }
-}
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/MarkerFilter.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/MarkerFilter.java
new file mode 100644
index 0000000..023c79b
--- /dev/null
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/MarkerFilter.java
@@ -0,0 +1,82 @@
+/*
+ * 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.filter;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttr;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.message.Message;
+
+/**
+ * This filter returns the onMatch result if the marker in the LogEvent is the same as or has the
+ * configured marker as a parent.
+ *
+ */
+@Plugin(name="Marker", type="Core", elementType="filter")
+public class MarkerFilter extends FilterBase {
+
+    private static final String LEVEL = "level";
+
+    private final Marker marker;
+
+    private MarkerFilter(Marker marker, Result onMatch, Result onMismatch) {
+        super(onMatch, onMismatch);
+        this.marker = marker;
+    }
+
+    public Result filter(Logger logger, Level level, Marker marker, String msg, Object[] params) {
+        return filter(marker);
+    }
+
+    public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable t) {
+        return filter(marker);
+    }
+
+    public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) {
+        return filter(marker);
+    }
+
+    @Override
+    public Result filter(LogEvent event) {
+        return filter(event.getMarker());
+    }
+
+    private Result filter(Marker marker) {
+        return marker != null && marker.isInstanceOf(this.marker) ? onMatch : onMismatch;
+    }
+
+    @PluginFactory
+    public static MarkerFilter createFilter(@PluginAttr("marker") String marker,
+                                            @PluginAttr("onMatch") String match,
+                                            @PluginAttr("onMismatch") String mismatch) {
+
+        if (marker == null) {
+            logger.error("A marker must be provided for MarkerFilter");
+            return null;
+        }
+        Marker m = Marker.getMarker(marker);
+        Result onMatch = match == null ? null : Result.valueOf(match);
+        Result onMismatch = mismatch == null ? null : Result.valueOf(mismatch);
+
+        return new MarkerFilter(m, onMatch, onMismatch);
+    }
+
+}
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/RegexFilter.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/RegexFilter.java
new file mode 100644
index 0000000..dc7c76d
--- /dev/null
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/RegexFilter.java
@@ -0,0 +1,96 @@
+/*
+ * 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.filter;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttr;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.message.Message;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * This filter returns the onMatch result if the message matches the regular expression.
+ *
+ * The "useRawMsg" attribute can be used to indicate whether the regular expression should be
+ * applied to the result of calling Message.getMessageFormat (true) or Message.getFormattedMessage()
+ * (false). The default is false.
+ *
+ */
+@Plugin(name="Regex", type="Core", elementType="filter")
+public class RegexFilter extends FilterBase {
+
+    private final Pattern pattern;
+    private final boolean useRawMessage;
+
+    private RegexFilter(boolean raw, Pattern pattern, Result onMatch, Result onMismatch) {
+        super(onMatch, onMismatch);
+        this.pattern = pattern;
+        this.useRawMessage = raw;
+    }
+
+    public Result filter(Logger logger, Level level, Marker marker, String msg, Object[] params) {
+        return filter(msg);
+    }
+
+    public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable t) {
+        return filter(msg.toString());
+    }
+
+    public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) {
+        String text = useRawMessage ? msg.getMessageFormat() : msg.getFormattedMessage();
+        return filter(text);
+    }
+
+    @Override
+    public Result filter(LogEvent event) {
+        String text = useRawMessage ? event.getMessage().getMessageFormat() : event.getMessage().getFormattedMessage();
+        return filter(text);
+    }
+
+    private Result filter(String msg) {
+        if (msg == null) {
+            return onMismatch;
+        }
+        Matcher m = pattern.matcher(msg);
+        return m.matches() ? onMatch : onMismatch;
+    }
+
+    @PluginFactory
+    public static RegexFilter createFilter(@PluginAttr("regex") String regex,
+                                           @PluginAttr("useRawMsg") String useRawMsg,
+                                            @PluginAttr("onMatch") String match,
+                                            @PluginAttr("onMismatch") String mismatch) {
+
+        if (regex == null) {
+            logger.error("A regular expression must be provided for RegexFilter");
+            return null;
+        }
+        boolean raw = useRawMsg == null ? false : Boolean.parseBoolean(useRawMsg);
+        Pattern pattern = Pattern.compile(regex);
+        Result onMatch = match == null ? null : Result.valueOf(match);
+        Result onMismatch = mismatch == null ? null : Result.valueOf(mismatch);
+
+        return new RegexFilter(raw, pattern, onMatch, onMismatch);
+    }
+
+}
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/StructuredDataFilter.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/StructuredDataFilter.java
new file mode 100644
index 0000000..fe187fb
--- /dev/null
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/StructuredDataFilter.java
@@ -0,0 +1,120 @@
+/*
+ * 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.filter;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttr;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.StructuredDataMessage;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *
+ */
+@Plugin(name="StructuredData", type="Core", elementType="filter")
+public class StructuredDataFilter extends FilterBase {
+    private final Map<String, Object> map;
+
+    private final boolean isAnd;
+
+    private StructuredDataFilter(Map<String, Object> map, boolean oper, Result onMatch, Result onMismatch) {
+        super(onMatch, onMismatch);
+        if (map == null) {
+            throw new NullPointerException("key cannot be null");
+        }
+        this.isAnd = oper;
+        this.map = map;
+    }
+
+    @Override
+    public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) {
+        if (msg instanceof StructuredDataMessage) {
+            return filter((StructuredDataMessage)msg);
+        }
+        return Result.NEUTRAL;
+    }
+
+    @Override
+    public Result filter(LogEvent event) {
+        Message msg = event.getMessage();
+        if (msg instanceof StructuredDataMessage) {
+            return filter((StructuredDataMessage) msg);
+        }
+        return Result.NEUTRAL;
+    }
+
+    private Result filter(StructuredDataMessage msg) {
+        boolean match = false;
+        for (String key : map.keySet()) {
+            if (key.equalsIgnoreCase("id")) {
+                match = map.get(key).equals(msg.getId().toString());
+            } else if (key.equalsIgnoreCase("id.name")) {
+                match = map.get(key).equals(msg.getId().getName());
+            } else if (key.equalsIgnoreCase("type")) {
+                match = map.get(key).equals(msg.getType());
+            } else if (key.equalsIgnoreCase("message")) {
+                match = map.get(key).equals(msg.getFormattedMessage().toString());
+            } else {
+                String data = msg.getData().get(key).toString();
+                match = map.get(key).equals(data);
+            }
+            if ((!isAnd && match) || (isAnd && !match)) {
+                break;
+            }
+        }
+        return match ? onMatch : onMismatch;
+    }
+
+    @PluginFactory
+    public static StructuredDataFilter createFilter(@PluginAttr("pairs") KeyValuePair[] pairs,
+                                                    @PluginAttr("operator") String oper,
+                                                    @PluginAttr("onmatch") String match,
+                                                    @PluginAttr("onmismatch") String mismatch) {
+        if (pairs == null || pairs.length == 0) {
+            logger.error("keys and values must be specified for the ThreadContextMapFilter");
+        }
+        Map<String, Object> map = new HashMap<String, Object>();
+        for (KeyValuePair pair : pairs) {
+            String key = pair.getKey();
+            if (key == null) {
+                logger.error("A null key is not valid in StructuredDataFilter");
+                continue;
+            }
+            String value = pair.getValue();
+            if (value == null) {
+                logger.error("A null value for key " + key + " is not allowed in StructuredDataFilter");
+                continue;
+            }
+            map.put(pair.getKey(), pair.getValue());
+        }
+        if (map.size() == 0) {
+            logger.error("StructuredDataFilter is not configured with any valid key value pairs");
+            return null;
+        }
+        boolean isAnd = oper == null || !oper.equalsIgnoreCase("or");
+        Result onMatch = match == null ? null : Result.valueOf(match);
+        Result onMismatch = mismatch == null ? null : Result.valueOf(mismatch);
+        return new StructuredDataFilter(map, isAnd, onMatch, onMismatch);
+    }
+}
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/ThreadContextMapFilter.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/ThreadContextMapFilter.java
new file mode 100644
index 0000000..c7ab503
--- /dev/null
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/ThreadContextMapFilter.java
@@ -0,0 +1,119 @@
+/*
+ * 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.filter;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttr;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.message.Message;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *
+ */
+@Plugin(name="ThreadContextMap", type="Core", elementType="filter")
+public class ThreadContextMapFilter extends FilterBase {
+    private final Map<String, Object> map;
+
+    private final boolean isAnd;
+
+    public ThreadContextMapFilter(Map<String, Object> pairs, boolean oper, Result onMatch, Result onMismatch) {
+        super(onMatch, onMismatch);
+        this.map = pairs;
+        this.isAnd = oper;
+    }
+
+    @Override
+    public Result filter(Logger logger, Level level, Marker marker, String msg, Object[] params) {
+        return filter();
+    }
+
+    @Override
+    public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable t) {
+        return filter();
+    }
+
+    @Override
+    public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) {
+        return filter();
+    }
+
+    private Result filter() {
+        boolean match = false;
+        for (String key : map.keySet()) {
+            match = map.get(key).equals(ThreadContext.get(key));
+            if ((!isAnd && match) || (isAnd && !match)) {
+                break;
+            }
+        }
+        return match ? onMatch : onMismatch;
+    }
+
+    @Override
+    public Result filter(LogEvent event) {
+        Map<String, Object> ctx = event.getContextMap();
+        boolean match = false;
+        for (String key : map.keySet()) {
+            match = map.get(key).equals(ctx.get(key));
+            if ((!isAnd && match) || (isAnd && !match)) {
+                break;
+            }
+        }
+        return match ? onMatch : onMismatch;
+    }
+
+    @PluginFactory
+    public static ThreadContextMapFilter createFilter(@PluginElement("pairs") KeyValuePair[] pairs,
+                                                      @PluginAttr("operator") String oper,
+                                                      @PluginAttr("onmatch") String match,
+                                                      @PluginAttr("onmismatch") String mismatch) {
+        if (pairs == null || pairs.length == 0) {
+            logger.error("key and value pairs must be specified for the ThreadContextMapFilter");
+            return null;
+        }
+        Map<String, Object> map = new HashMap<String, Object>();
+        for (KeyValuePair pair : pairs) {
+            String key = pair.getKey();
+            if (key == null) {
+                logger.error("A null key is not valid in ThreadContextMapFilter");
+                continue;
+            }
+            String value = pair.getValue();
+            if (value == null) {
+                logger.error("A null value for key " + key + " is not allowed in ThreadContextMapFilter");
+                continue;
+            }
+            map.put(pair.getKey(), pair.getValue());
+        }
+        if (map.size() == 0) {
+            logger.error("ThreadContextMapFilter is not configured with any valid key value pairs");
+            return null;
+        }
+        boolean isAnd = oper == null || !oper.equalsIgnoreCase("or");
+        Result onMatch = match == null ? null : Result.valueOf(match);
+        Result onMismatch = mismatch == null ? null : Result.valueOf(mismatch);
+        return new ThreadContextMapFilter(map, isAnd, onMatch, onMismatch);
+    }
+}
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/ThresholdFilter.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/ThresholdFilter.java
index 54f9766..835e8aa 100644
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/ThresholdFilter.java
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/ThresholdFilter.java
@@ -40,7 +40,7 @@
 
     private final Level level;
 
-    public ThresholdFilter(Level level, Result onMatch, Result onMismatch) {
+    private ThresholdFilter(Level level, Result onMatch, Result onMismatch) {
         super(onMatch, onMismatch);
         this.level = level;
     }
@@ -63,7 +63,7 @@
     }
 
     private Result filter(Level level) {
-        return this.level.greaterOrEqual(level) ? onMatch : onMismatch;
+        return level.isAtLeastAsSpecificAs(this.level) ? onMatch : onMismatch;
     }
 
     @PluginFactory
@@ -71,8 +71,8 @@
                                                @PluginAttr("onMatch") String match,
                                                @PluginAttr("onMismatch") String mismatch) {
         Level level = loggerLevel == null ? Level.ERROR : Level.toLevel(loggerLevel.toUpperCase());
-        Result onMatch = match == null ? null : Result.valueOf(match);
-        Result onMismatch = mismatch == null ? null : Result.valueOf(mismatch);
+        Result onMatch = match == null ? Result.NEUTRAL : Result.valueOf(match);
+        Result onMismatch = mismatch == null ? Result.DENY : Result.valueOf(mismatch);
 
         return new ThresholdFilter(level, onMatch, onMismatch);
     }
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/ValueLevelPair.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/ValueLevelPair.java
new file mode 100644
index 0000000..4c9fc89
--- /dev/null
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/filter/ValueLevelPair.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.filter;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttr;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+
+/**
+ *
+ */
+@Plugin(name="ValueLevelPair", type="Core")
+public class ValueLevelPair {
+
+    private String key;
+    private Level  level;
+
+    public ValueLevelPair(String key, Level level) {
+        this.key = key;
+        this.level = level;
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public Level getLevel() {
+        return level;
+    }
+
+    @PluginFactory
+    public static ValueLevelPair createPair(@PluginAttr("key") String key,
+                                          @PluginAttr("threshold")  String value) {
+
+        Level level = Level.toLevel(value);
+        return new ValueLevelPair(key, level);
+    }
+}
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java
index 0adc984..372b580 100644
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java
@@ -386,7 +386,7 @@
  * Philip E. Margolis' highly recommended book "C -- a Software
  * Engineering Approach", ISBN 0-387-97389-3.
  */
-@Plugin(name="PatternLayout",type="Core",elementType="layout")
+@Plugin(name="PatternLayout",type="Core",elementType="layout",printObject=true)
 public class PatternLayout extends LayoutBase {
     /**
      * Default pattern string for log output. Currently set to the
@@ -492,6 +492,10 @@
         return new PatternParser(KEY);
     }
 
+    public String toString() {
+        return "PatternLayout(" + conversionPattern + ")";
+    }
+
     @PluginFactory
     public static PatternLayout createLayout(@PluginAttr("pattern") String pattern,
                                              @PluginAttr("charset") String charset) {
diff --git a/log4j2-core/src/test/java/org/apache/logging/log4j/core/config/XMLConfigurationTest.java b/log4j2-core/src/test/java/org/apache/logging/log4j/core/config/XMLConfigurationTest.java
index 09a9257..530063f 100644
--- a/log4j2-core/src/test/java/org/apache/logging/log4j/core/config/XMLConfigurationTest.java
+++ b/log4j2-core/src/test/java/org/apache/logging/log4j/core/config/XMLConfigurationTest.java
@@ -22,15 +22,13 @@
 import org.apache.logging.log4j.core.Appender;
 import org.apache.logging.log4j.core.Filter;
 import org.apache.logging.log4j.core.LoggerContext;
-import org.apache.logging.log4j.core.filter.MDCFilter;
-import org.apache.logging.log4j.internal.StatusData;
+import org.apache.logging.log4j.core.filter.ThreadContextMapFilter;
 import org.apache.logging.log4j.internal.StatusLogger;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
 import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 
 import static org.junit.Assert.assertTrue;
@@ -77,7 +75,7 @@
         assertTrue("number of filters - " + filterCount, filterCount == 1);
         Iterator<Filter> iter = l.getFilters();
         Filter filter = iter.next();
-        assertTrue(filter instanceof MDCFilter);
+        assertTrue(filter instanceof ThreadContextMapFilter);
         Map<String, Appender> appenders = l.getAppenders();
         assertNotNull(appenders);
         assertTrue("number of appenders = " + appenders.size(), appenders.size() == 1);
diff --git a/log4j2-core/src/test/java/org/apache/logging/log4j/core/filter/DynamicThresholdFilterTest.java b/log4j2-core/src/test/java/org/apache/logging/log4j/core/filter/DynamicThresholdFilterTest.java
new file mode 100644
index 0000000..22db330
--- /dev/null
+++ b/log4j2-core/src/test/java/org/apache/logging/log4j/core/filter/DynamicThresholdFilterTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.filter;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Log4jLogEvent;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ *
+ */
+public class DynamicThresholdFilterTest {
+
+    @Test
+    public void testFilter() {
+        ThreadContext.put("userid", "testuser");
+        ThreadContext.put("organization", "apache");
+        ValueLevelPair[] pairs = new ValueLevelPair[] { new ValueLevelPair("testuser", Level.DEBUG),
+                                                         new ValueLevelPair("JohnDoe", Level.WARN)};
+        DynamicThresholdFilter filter = DynamicThresholdFilter.createFilter("userid", pairs, "ERROR", null, null);
+        filter.start();
+        assertTrue(filter.isStarted());
+        assertTrue(filter.filter(null, Level.DEBUG, null, null, (Throwable)null) == Filter.Result.NEUTRAL);
+        assertTrue(filter.filter(null, Level.ERROR, null, null, (Throwable)null) == Filter.Result.NEUTRAL);
+        ThreadContext.clear();
+        ThreadContext.put("userid", "JohnDoe");
+        ThreadContext.put("organization", "apache");
+        LogEvent event = new Log4jLogEvent(null, null, null, Level.DEBUG, new SimpleMessage("Test"), null);
+        assertTrue(filter.filter(event) == Filter.Result.DENY);
+        event = new Log4jLogEvent(null, null, null, Level.ERROR, new SimpleMessage("Test"), null);
+        assertTrue(filter.filter(event) == Filter.Result.NEUTRAL);
+        ThreadContext.clear();
+    }
+}
diff --git a/log4j2-core/src/test/java/org/apache/logging/log4j/core/filter/MarkerFilterTest.java b/log4j2-core/src/test/java/org/apache/logging/log4j/core/filter/MarkerFilterTest.java
new file mode 100644
index 0000000..153eb53
--- /dev/null
+++ b/log4j2-core/src/test/java/org/apache/logging/log4j/core/filter/MarkerFilterTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.filter;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Log4jLogEvent;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ *
+ */
+public class MarkerFilterTest {
+
+    @Test
+    public void testMarkers() {
+        Marker parent = Marker.getMarker("Parent");
+        Marker child = Marker.getMarker("Child", parent);
+        Marker grandChild = Marker.getMarker("GrandChild", child);
+        Marker sibling = Marker.getMarker("Sibling", parent);
+        Marker stranger = Marker.getMarker("Stranger");
+        MarkerFilter filter = MarkerFilter.createFilter("Parent", null, null);
+        filter.start();
+        assertTrue(filter.isStarted());
+        assertTrue(filter.filter(null, null, stranger, null, (Throwable)null) == Filter.Result.DENY);
+        assertTrue(filter.filter(null, null, child, null, (Throwable)null) == Filter.Result.NEUTRAL);
+        LogEvent event = new Log4jLogEvent(null, grandChild, null, Level.DEBUG, new SimpleMessage("Test"), null);
+        filter = MarkerFilter.createFilter("Child", null, null);
+        assertTrue(filter.filter(event) == Filter.Result.NEUTRAL);
+        event = new Log4jLogEvent(null, sibling, null, Level.DEBUG, new SimpleMessage("Test"), null);
+        assertTrue(filter.filter(event) == Filter.Result.DENY);
+    }
+}
diff --git a/log4j2-core/src/test/java/org/apache/logging/log4j/core/filter/StructuredDataFilterTest.java b/log4j2-core/src/test/java/org/apache/logging/log4j/core/filter/StructuredDataFilterTest.java
new file mode 100644
index 0000000..365222d
--- /dev/null
+++ b/log4j2-core/src/test/java/org/apache/logging/log4j/core/filter/StructuredDataFilterTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.filter;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.message.StructuredDataMessage;
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ *
+ */
+public class StructuredDataFilterTest {
+
+
+    @Test
+    public void testFilter() {
+        KeyValuePair[] pairs = new KeyValuePair[] { new KeyValuePair("id.name", "AccountTransfer"),
+                                                    new KeyValuePair("ToAccount", "123456")};
+        StructuredDataFilter filter = StructuredDataFilter.createFilter(pairs, "and", null, null);
+        filter.start();
+        StructuredDataMessage msg = new StructuredDataMessage("AccountTransfer@18060", "Transfer Successful", "Audit");
+        msg.put("ToAccount", "123456");
+        msg.put("FromAccount", "211000");
+        msg.put("Amount", "1000.00");
+        assertTrue(filter.isStarted());
+        assertTrue(filter.filter(null, Level.DEBUG, null, msg, (Throwable)null) == Filter.Result.NEUTRAL);
+        msg.put("ToAccount", "111111");
+        assertTrue(filter.filter(null, Level.ERROR, null, msg, (Throwable)null) == Filter.Result.DENY);
+        filter = StructuredDataFilter.createFilter(pairs, "or", null, null);
+        filter.start();
+        msg = new StructuredDataMessage("AccountTransfer@18060", "Transfer Successful", "Audit");
+        msg.put("ToAccount", "123456");
+        msg.put("FromAccount", "211000");
+        msg.put("Amount", "1000.00");
+        assertTrue(filter.isStarted());
+        assertTrue(filter.filter(null, Level.DEBUG, null, msg, (Throwable)null) == Filter.Result.NEUTRAL);
+        msg.put("ToAccount", "111111");
+        assertTrue(filter.filter(null, Level.ERROR, null, msg, (Throwable)null) == Filter.Result.NEUTRAL);
+    }
+}
diff --git a/log4j2-core/src/test/java/org/apache/logging/log4j/core/filter/ThreadContextMapFilterTest.java b/log4j2-core/src/test/java/org/apache/logging/log4j/core/filter/ThreadContextMapFilterTest.java
new file mode 100644
index 0000000..5031629
--- /dev/null
+++ b/log4j2-core/src/test/java/org/apache/logging/log4j/core/filter/ThreadContextMapFilterTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.filter;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.core.Filter;
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ *
+ */
+public class ThreadContextMapFilterTest {
+
+    @Test
+    public void testFilter() {
+        ThreadContext.put("userid", "testuser");
+        ThreadContext.put("organization", "Apache");
+        KeyValuePair[] pairs = new KeyValuePair[] { new KeyValuePair("userid", "JohnDoe"),
+                                                    new KeyValuePair("organization", "Apache")};
+        ThreadContextMapFilter filter = ThreadContextMapFilter.createFilter(pairs, "and", null, null);
+        filter.start();
+        assertTrue(filter.isStarted());
+        assertTrue(filter.filter(null, Level.DEBUG, null, null, (Throwable)null) == Filter.Result.DENY);
+        ThreadContext.remove("userid");
+        assertTrue(filter.filter(null, Level.DEBUG, null, null, (Throwable)null) == Filter.Result.DENY);
+        ThreadContext.put("userid", "JohnDoe");
+        assertTrue(filter.filter(null, Level.ERROR, null, null, (Throwable)null) == Filter.Result.NEUTRAL);
+        ThreadContext.put("organization", "ASF");
+        assertTrue(filter.filter(null, Level.DEBUG, null, null, (Throwable)null) == Filter.Result.DENY);
+        ThreadContext.clear();
+        filter = ThreadContextMapFilter.createFilter(pairs, "or", null, null);
+        filter.start();
+        assertTrue(filter.isStarted());
+        ThreadContext.put("userid", "testuser");
+        ThreadContext.put("organization", "Apache");
+        assertTrue(filter.filter(null, Level.DEBUG, null, null, (Throwable)null) == Filter.Result.NEUTRAL);
+        ThreadContext.put("organization", "ASF");
+        assertTrue(filter.filter(null, Level.DEBUG, null, null, (Throwable)null) == Filter.Result.DENY);
+        ThreadContext.remove("organization");
+        assertTrue(filter.filter(null, Level.DEBUG, null, null, (Throwable)null) == Filter.Result.DENY);
+        ThreadContext.clear();
+    }
+}
diff --git a/log4j2-core/src/test/java/org/apache/logging/log4j/core/filter/ThresholdFilterTest.java b/log4j2-core/src/test/java/org/apache/logging/log4j/core/filter/ThresholdFilterTest.java
new file mode 100644
index 0000000..5192acb
--- /dev/null
+++ b/log4j2-core/src/test/java/org/apache/logging/log4j/core/filter/ThresholdFilterTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.filter;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Log4jLogEvent;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+
+/**
+ *
+ */
+public class ThresholdFilterTest {
+
+    @Test
+    public void testThresholds() {
+        ThresholdFilter filter = ThresholdFilter.createFilter("ERROR", null, null);
+        filter.start();
+        assertTrue(filter.isStarted());
+        assertTrue(filter.filter(null, Level.DEBUG, null, null, (Throwable)null) == Filter.Result.DENY);
+        assertTrue(filter.filter(null, Level.ERROR, null, null, (Throwable)null) == Filter.Result.NEUTRAL);
+        LogEvent event = new Log4jLogEvent(null, null, null, Level.DEBUG, new SimpleMessage("Test"), null);
+        assertTrue(filter.filter(event) == Filter.Result.DENY);
+        event = new Log4jLogEvent(null, null, null, Level.ERROR, new SimpleMessage("Test"), null);        
+        assertTrue(filter.filter(event) == Filter.Result.NEUTRAL);
+    }
+}
diff --git a/log4j2-core/src/test/resources/log4j-test1.xml b/log4j2-core/src/test/resources/log4j-test1.xml
index fd41b7f..9f03d6c 100644
--- a/log4j2-core/src/test/resources/log4j-test1.xml
+++ b/log4j2-core/src/test/resources/log4j-test1.xml
@@ -9,9 +9,7 @@
 
   <appenders>
     <Console name="STDOUT">
-      <PatternLayout>
-        <pattern>%m%n</pattern>
-      </PatternLayout>
+      <PatternLayout pattern="%m%n"/>
     </Console>
     <File name="File" fileName="${filename}">
       <PatternLayout>
@@ -28,7 +26,9 @@
   <loggers>
     <logger name="org.apache.logging.log4j.test1" level="debug" additivity="false">
       <filters>
-        <MDC key="test" value="123"/>
+        <ThreadContextMap>
+          <KeyValuePair key="test" value="123"/>
+        </ThreadContextMap>
       </filters>
       <appender-ref ref="STDOUT"/>
     </logger>>