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 2021/04/11 22:54:04 UTC

[logging-log4j2] branch master updated: LOG4J2-3064 - Add Arbiters and SpringProfile

This is an automated email from the ASF dual-hosted git repository.

rgoers pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git


The following commit(s) were added to refs/heads/master by this push:
     new 83e2806  LOG4J2-3064 - Add Arbiters and SpringProfile
83e2806 is described below

commit 83e28061318d37ef946a1fa08064394e7b2e8015
Author: Ralph Goers <rg...@apache.org>
AuthorDate: Sun Apr 11 15:53:47 2021 -0700

    LOG4J2-3064 - Add Arbiters and SpringProfile
---
 .../log4j/core/config/AbstractConfiguration.java   | 109 ++++++++++++++
 .../log4j/core/config/arbiters/Arbiter.java        |  27 ++++
 .../log4j/core/config/arbiters/ClassArbiter.java   |  75 ++++++++++
 .../log4j/core/config/arbiters/DefaultArbiter.java |  53 +++++++
 .../log4j/core/config/arbiters/ScriptArbiter.java  | 121 ++++++++++++++++
 .../log4j/core/config/arbiters/SelectArbiter.java  |  64 +++++++++
 .../config/arbiters/SystemPropertyArbiter.java     |  93 ++++++++++++
 .../core/config/arbiters/BasicArbiterTest.java     |  63 ++++++++
 .../core/config/arbiters/ScriptArbiterTest.java    |  63 ++++++++
 .../core/config/arbiters/SelectArbiterTest.java    |  63 ++++++++
 .../log4j/core/config/xml/XmlSchemaTest.java       |   3 +
 log4j-core/src/test/resources/log4j2-arbiters.xml  |  41 ++++++
 .../src/test/resources/log4j2-scriptArbiters.xml   |  51 +++++++
 .../src/test/resources/log4j2-selectArbiters.xml   |  43 ++++++
 .../org/apache/logging/log4j/plugins/Node.java     |   6 +-
 .../log4j/spring/boot/SpringProfileArbiter.java    |  97 +++++++++++++
 .../log4j/spring/boot/SpringProfileTest.java       |  69 +++++++++
 .../src/test/resources/log4j2-springProfile.xml    |  41 ++++++
 src/changes/changes.xml                            |   3 +
 src/site/asciidoc/manual/configuration.adoc        | 159 +++++++++++++++++++++
 src/site/site.xml                                  |   2 +
 21 files changed, 1245 insertions(+), 1 deletion(-)

diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
index 3240332..1b0f608 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
@@ -49,6 +49,8 @@ import org.apache.logging.log4j.core.appender.ConsoleAppender;
 import org.apache.logging.log4j.core.async.AsyncLoggerConfig;
 import org.apache.logging.log4j.core.async.AsyncLoggerConfigDelegate;
 import org.apache.logging.log4j.core.async.AsyncLoggerConfigDisruptor;
+import org.apache.logging.log4j.core.config.arbiters.Arbiter;
+import org.apache.logging.log4j.core.config.arbiters.SelectArbiter;
 import org.apache.logging.log4j.core.config.plugins.util.PluginBuilder;
 import org.apache.logging.log4j.core.filter.AbstractFilterable;
 import org.apache.logging.log4j.core.layout.PatternLayout;
@@ -518,7 +520,99 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
         }
     }
 
+
+    /**
+     * Process conditions by evaluating them and including the children of conditions that are true
+     * and discarding those that are not.
+     * @param node The node to evaluate.
+     */
+    protected void processConditionals(final Node node) {
+        try {
+            List<Node> addList = new ArrayList<>();
+            List<Node> removeList = new ArrayList<>();
+            for (final Node child : node.getChildren()) {
+                final PluginType<?> type = child.getType();
+                if (type != null && Arbiter.ELEMENT_TYPE.equals(type.getElementName())) {
+                    final Class<?> clazz = type.getPluginClass();
+                    if (SelectArbiter.class.isAssignableFrom(clazz)) {
+                        removeList.add(child);
+                        addList.addAll(processSelect(child, type));
+                    } else if (Arbiter.class.isAssignableFrom(clazz)) {
+                        removeList.add(child);
+                        try {
+                            Arbiter condition = (Arbiter) createPluginObject(type, child, null);
+                            if (condition.isCondition()) {
+                                addList.addAll(child.getChildren());
+                                processConditionals(child);
+                            }
+                        } catch (final Exception inner) {
+                            LOGGER.error("Exception processing {}: Ignoring and including children",
+                                    type.getPluginClass());
+                            processConditionals(child);
+                        }
+                    } else {
+                        LOGGER.error("Encountered Condition Plugin that does not implement Condition: {}",
+                                child.getName());
+                        processConditionals(child);
+                    }
+                } else {
+                    processConditionals(child);
+                }
+            }
+            if (!removeList.isEmpty()) {
+                List<Node> children = node.getChildren();
+                children.removeAll(removeList);
+                children.addAll(addList);
+                for (Node grandChild : addList) {
+                    grandChild.setParent(node);
+                }
+            }
+        } catch (final Exception ex) {
+            LOGGER.error("Error capturing node data for node " + node.getName(), ex);
+        }
+    }
+
+    /**
+     * Handle Select nodes. This finds the first child condition that returns true and attaches its children
+     * to the parent of the Select Node. Other Nodes are discarded.
+     * @param selectNode The Select Node.
+     * @param type The PluginType of the Select Node.
+     * @return The list of Nodes to be added to the parent.
+     */
+    protected List<Node> processSelect(Node selectNode, PluginType<?> type) {
+        List<Node> addList = new ArrayList<>();
+        SelectArbiter select = (SelectArbiter) createPluginObject(type, selectNode, null);
+        List<Arbiter> conditions = new ArrayList<>();
+        for (Node child : selectNode.getChildren()) {
+            PluginType<?> nodeType = child.getType();
+            if (nodeType != null) {
+                if (Arbiter.class.isAssignableFrom(nodeType.getPluginClass())) {
+                    Arbiter condition = (Arbiter) createPluginObject(nodeType, child, null);
+                    conditions.add(condition);
+                    child.setObject(condition);
+                } else {
+                    LOGGER.error("Invalid Node {} for Select. Must be a Condition",
+                            child.getName());
+                }
+            } else {
+                LOGGER.error("No PluginType for node {}", child.getName());
+            }
+        }
+        Arbiter condition = select.evaluateConditions(conditions);
+        if (condition != null) {
+            for (Node child : selectNode.getChildren()) {
+                if (condition == child.getObject()) {
+                    addList.addAll(child.getChildren());
+                    processConditionals(child);
+                }
+            }
+        }
+        return addList;
+    }
+
+
     protected void doConfigure() {
+        processConditionals(rootNode);
         preConfigure(rootNode);
         configurationScheduler.start();
         if (rootNode.hasChildren() && rootNode.getChildren().get(0).getName().equalsIgnoreCase("Properties")) {
@@ -938,6 +1032,21 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
     }
 
     /**
+     * This method is used by Arbiters to create specific children.
+     * @param type The PluginType.
+     * @param node The Node.
+     * @return The created object or null;
+     */
+    public Object createPluginObject(final PluginType<?> type, final Node node) {
+        if (this.getState().equals(State.INITIALIZING)) {
+            return createPluginObject(type, node, null);
+        } else {
+            LOGGER.warn("Plugin Object creation is not allowed after initialization");
+            return null;
+        }
+    }
+
+    /**
      * Invokes a static factory method to either create the desired object or to create a builder object that creates
      * the desired object. In the case of a factory method, it should be annotated with
      * {@link org.apache.logging.log4j.plugins.PluginFactory}, and each parameter should be annotated with
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/arbiters/Arbiter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/arbiters/Arbiter.java
new file mode 100644
index 0000000..ceb24ea
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/arbiters/Arbiter.java
@@ -0,0 +1,27 @@
+/*
+ * 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.config.arbiters;
+
+/**
+ * Interface used to check for portions of the configuration that may be optionally included.
+ */
+public interface Arbiter {
+
+    String ELEMENT_TYPE = "Arbiter";
+
+    boolean isCondition();
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/arbiters/ClassArbiter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/arbiters/ClassArbiter.java
new file mode 100644
index 0000000..0de1568
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/arbiters/ClassArbiter.java
@@ -0,0 +1,75 @@
+/*
+ * 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.config.arbiters;
+
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
+import org.apache.logging.log4j.plugins.Node;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.util.LoaderUtil;
+
+/**
+ * Conditional that determines if the specified class is present.
+ */
+@Plugin(name = "ClassArbiter", category = Node.CATEGORY, elementType = Arbiter.ELEMENT_TYPE,
+        printObject = true, deferChildren = true)
+public class ClassArbiter implements Arbiter {
+
+    private final String className;
+
+    private ClassArbiter(final String className) {
+        this.className = className;
+    }
+
+    @Override
+    public boolean isCondition() {
+        return LoaderUtil.isClassAvailable(className);
+    }
+
+    @PluginBuilderFactory
+    public static SystemPropertyArbiter.Builder newBuilder() {
+        return new SystemPropertyArbiter.Builder();
+    }
+
+    public static class Builder implements org.apache.logging.log4j.core.util.Builder<ClassArbiter> {
+
+        public static final String ATTR_CLASS_NAME = "className";
+
+        @PluginBuilderAttribute(ATTR_CLASS_NAME)
+        private String className;
+
+
+        /**
+         * Sets the Class name.
+         * @param className the class name.
+         * @return this
+         */
+        public Builder setClassName(final String className) {
+            this.className = className;
+            return asBuilder();
+        }
+
+        public Builder asBuilder() {
+            return this;
+        }
+
+        public ClassArbiter build() {
+            return new ClassArbiter(className);
+        }
+
+    }
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/arbiters/DefaultArbiter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/arbiters/DefaultArbiter.java
new file mode 100644
index 0000000..732f5d1
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/arbiters/DefaultArbiter.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.config.arbiters;
+
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
+import org.apache.logging.log4j.plugins.Node;
+import org.apache.logging.log4j.plugins.Plugin;
+
+/**
+ * Default Condition for a Select Condition.
+ */
+@Plugin(name = "DefaultArbiter", category = Node.CATEGORY, elementType = Arbiter.ELEMENT_TYPE,
+        deferChildren = true, printObject = true)
+public class DefaultArbiter implements Arbiter {
+
+    /**
+     * Always returns true since it is the default.
+     */
+    @Override
+    public boolean isCondition() {
+        return true;
+    }
+
+    @PluginBuilderFactory
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    public static class Builder implements org.apache.logging.log4j.core.util.Builder<DefaultArbiter> {
+
+        public Builder asBuilder() {
+            return this;
+        }
+
+        public DefaultArbiter build() {
+            return new DefaultArbiter();
+        }
+    }
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/arbiters/ScriptArbiter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/arbiters/ScriptArbiter.java
new file mode 100644
index 0000000..4fda875
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/arbiters/ScriptArbiter.java
@@ -0,0 +1,121 @@
+/*
+ * 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.config.arbiters;
+
+import javax.script.SimpleBindings;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.AbstractConfiguration;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
+import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
+import org.apache.logging.log4j.core.config.plugins.PluginNode;
+import org.apache.logging.log4j.core.script.AbstractScript;
+import org.apache.logging.log4j.core.script.ScriptRef;
+import org.apache.logging.log4j.plugins.Node;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.plugins.util.PluginType;
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * Condition that evaluates a script.
+ */
+@Plugin(name = "ScriptArbiter", category = Node.CATEGORY, elementType = Arbiter.ELEMENT_TYPE,
+        deferChildren = true, printObject = true)
+public class ScriptArbiter implements Arbiter {
+
+    private final AbstractScript script;
+    private final Configuration configuration;
+
+    private ScriptArbiter(final Configuration configuration, final AbstractScript script) {
+        this.configuration = configuration;
+        this.script = script;
+        if (!(script instanceof ScriptRef)) {
+            configuration.getScriptManager().addScript(script);
+        }
+    }
+
+    /**
+     * Returns the boolean result of the Script.
+     */
+    @Override
+    public boolean isCondition() {
+        final SimpleBindings bindings = new SimpleBindings();
+        bindings.putAll(configuration.getProperties());
+        bindings.put("substitutor", configuration.getStrSubstitutor());
+        final Object object = configuration.getScriptManager().execute(script.getName(), bindings);
+        return Boolean.parseBoolean(object.toString());
+    }
+
+    @PluginBuilderFactory
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    public static class Builder implements org.apache.logging.log4j.core.util.Builder<ScriptArbiter> {
+
+        private static final Logger LOGGER = StatusLogger.getLogger();
+
+        @PluginConfiguration
+        private AbstractConfiguration configuration;
+
+        @PluginNode
+        private Node node;
+
+        public Builder setConfiguration(final AbstractConfiguration configuration) {
+            this.configuration = configuration;
+            return asBuilder();
+        }
+
+        public Builder setNode(final Node node) {
+            this.node = node;
+            return asBuilder();
+        }
+
+        public Builder asBuilder() {
+            return this;
+        }
+
+        public ScriptArbiter build() {
+            AbstractScript script = null;
+            for (Node child : node.getChildren()) {
+                PluginType<?> type = child.getType();
+                if (type == null) {
+                    LOGGER.error("Node {} is missing a Plugintype", child.getName());
+                    continue;
+                }
+                if (AbstractScript.class.isAssignableFrom(type.getPluginClass())) {
+                    script = (AbstractScript) configuration.createPluginObject(type, child);
+                    node.getChildren().remove(child);
+                    break;
+                }
+            }
+
+            if (script == null) {
+                LOGGER.error("A Script, ScriptFile or ScriptRef element must be provided for this ScriptFilter");
+                return null;
+            }
+            if (script instanceof ScriptRef) {
+                if (configuration.getScriptManager().getScript(script.getName()) == null) {
+                    LOGGER.error("No script with name {} has been declared.", script.getName());
+                    return null;
+                }
+            }
+            return new ScriptArbiter(configuration, script);
+        }
+    }
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/arbiters/SelectArbiter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/arbiters/SelectArbiter.java
new file mode 100644
index 0000000..2fb39f0
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/arbiters/SelectArbiter.java
@@ -0,0 +1,64 @@
+/*
+ * 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.config.arbiters;
+
+import java.util.List;
+import java.util.Optional;
+
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
+import org.apache.logging.log4j.plugins.Node;
+import org.apache.logging.log4j.plugins.Plugin;
+
+/**
+ * Class Description goes here.
+ */
+@Plugin(name = "Select", category = Node.CATEGORY, elementType = Arbiter.ELEMENT_TYPE, deferChildren = true,
+        printObject = true)
+public class SelectArbiter {
+
+    public Arbiter evaluateConditions(List<Arbiter> conditions) {
+        Optional<Arbiter> opt = conditions.stream().filter((c) -> c instanceof DefaultArbiter)
+                .reduce((a, b) -> {
+                    throw new IllegalStateException("Multiple elements: " + a + ", " + b);
+                });
+        for (Arbiter condition : conditions) {
+            if (condition instanceof DefaultArbiter) {
+                continue;
+            }
+            if (condition.isCondition()) {
+                return condition;
+            }
+        }
+        return opt.orElse(null);
+    }
+
+    @PluginBuilderFactory
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    public static class Builder implements org.apache.logging.log4j.core.util.Builder<SelectArbiter> {
+
+        public Builder asBuilder() {
+            return this;
+        }
+
+        public SelectArbiter build() {
+            return new SelectArbiter();
+        }
+    }
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/arbiters/SystemPropertyArbiter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/arbiters/SystemPropertyArbiter.java
new file mode 100644
index 0000000..078ca32
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/arbiters/SystemPropertyArbiter.java
@@ -0,0 +1,93 @@
+/*
+ * 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.config.arbiters;
+
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
+import org.apache.logging.log4j.plugins.Node;
+import org.apache.logging.log4j.plugins.Plugin;
+
+/**
+ * Condition that determines if the specified property is set.
+ */
+@Plugin(name = "SystemPropertyArbiter", category = Node.CATEGORY, elementType = Arbiter.ELEMENT_TYPE,
+        deferChildren = true, printObject = true)
+public class SystemPropertyArbiter implements Arbiter {
+
+    private final String propertyName;
+    private final String propertyValue;
+
+    private SystemPropertyArbiter(final String propertyName, final String propertyValue) {
+        this.propertyName = propertyName;
+        this.propertyValue = propertyValue;
+    }
+
+
+    /**
+     * Returns true if either the property name is defined (it has any value) or the property value
+     * matches the requested value.
+     */
+    @Override
+    public boolean isCondition() {
+        String value = System.getProperty(propertyName);
+        return value != null && (propertyValue == null || value.equals(propertyValue));
+    }
+
+    @PluginBuilderFactory
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    public static class Builder implements org.apache.logging.log4j.core.util.Builder<SystemPropertyArbiter> {
+
+        public static final String ATTR_PROPERTY_NAME = "propertyName";
+        public static final String ATTR_PROPERTY_VALUE = "propertyValue";
+
+        @PluginBuilderAttribute(ATTR_PROPERTY_NAME)
+        private String propertyName;
+
+        @PluginBuilderAttribute(ATTR_PROPERTY_VALUE)
+        private String propertyValue;
+        /**
+         * Sets the Property Name.
+         * @param propertyName the property name.
+         * @return this
+         */
+        public Builder setPropertyName(final String propertyName) {
+            this.propertyName = propertyName;
+            return asBuilder();
+        }
+
+        /**
+         * Sets the Property Value.
+         * @param propertyValue the property name.
+         * @return this
+         */
+        public Builder setPropertyValue(final String propertyValue) {
+            this.propertyName = propertyValue;
+            return asBuilder();
+        }
+
+        public Builder asBuilder() {
+            return this;
+        }
+
+        public SystemPropertyArbiter build() {
+            return new SystemPropertyArbiter(propertyName, propertyValue);
+        }
+    }
+}
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/arbiters/BasicArbiterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/arbiters/BasicArbiterTest.java
new file mode 100644
index 0000000..5ec1b3e
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/arbiters/BasicArbiterTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.config.arbiters;
+
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.apache.logging.log4j.test.appender.ListAppender;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests basic condition processing.
+ */
+public class BasicArbiterTest {
+
+    static final String CONFIG = "log4j2-arbiters.xml";
+    static LoggerContext loggerContext = null;
+
+    @AfterEach
+    public void after() {
+        loggerContext.stop();
+        loggerContext = null;
+    }
+
+    @Test
+    public void prodTest() {
+        System.setProperty("env", "prod");
+        loggerContext = Configurator.initialize(null, CONFIG);
+        assertNotNull(loggerContext);
+        Appender app = loggerContext.getConfiguration().getAppender("Out");
+        assertNotNull(app);
+        assertTrue(app instanceof ListAppender);
+    }
+
+    @Test
+    public void devTest() {
+        System.setProperty("env", "dev");
+        loggerContext = Configurator.initialize(null, CONFIG);
+        assertNotNull(loggerContext);
+        Appender app = loggerContext.getConfiguration().getAppender("Out");
+        assertNotNull(app);
+        assertTrue(app instanceof ConsoleAppender);
+    }
+}
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/arbiters/ScriptArbiterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/arbiters/ScriptArbiterTest.java
new file mode 100644
index 0000000..55763fe
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/arbiters/ScriptArbiterTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.config.arbiters;
+
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.apache.logging.log4j.test.appender.ListAppender;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests basic condition processing.
+ */
+public class ScriptArbiterTest {
+
+    static final String CONFIG = "log4j2-scriptArbiters.xml";
+    static LoggerContext loggerContext = null;
+
+    @AfterEach
+    public void after() {
+        loggerContext.stop();
+        loggerContext = null;
+    }
+
+    @Test
+    public void prodTest() {
+        System.setProperty("env", "prod");
+        loggerContext = Configurator.initialize(null, CONFIG);
+        assertNotNull(loggerContext);
+        Appender app = loggerContext.getConfiguration().getAppender("Out");
+        assertNotNull(app);
+        assertTrue(app instanceof ListAppender);
+    }
+
+    @Test
+    public void devTest() {
+        System.setProperty("env", "dev");
+        loggerContext = Configurator.initialize(null, CONFIG);
+        assertNotNull(loggerContext);
+        Appender app = loggerContext.getConfiguration().getAppender("Out");
+        assertNotNull(app);
+        assertTrue(app instanceof ConsoleAppender);
+    }
+}
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/arbiters/SelectArbiterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/arbiters/SelectArbiterTest.java
new file mode 100644
index 0000000..1b7ea9d
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/arbiters/SelectArbiterTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.config.arbiters;
+
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.apache.logging.log4j.test.appender.ListAppender;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests basic condition processing.
+ */
+public class SelectArbiterTest {
+
+    static final String CONFIG = "log4j2-selectArbiters.xml";
+    static LoggerContext loggerContext = null;
+
+    @AfterEach
+    public void after() {
+        loggerContext.stop();
+        loggerContext = null;
+    }
+
+    @Test
+    public void prodTest() {
+        System.setProperty("env", "prod");
+        loggerContext = Configurator.initialize(null, CONFIG);
+        assertNotNull(loggerContext);
+        Appender app = loggerContext.getConfiguration().getAppender("Out");
+        assertNotNull(app);
+        assertTrue(app instanceof ListAppender);
+    }
+
+    @Test
+    public void devTest() {
+        System.setProperty("env", "dev");
+        loggerContext = Configurator.initialize(null, CONFIG);
+        assertNotNull(loggerContext);
+        Appender app = loggerContext.getConfiguration().getAppender("Out");
+        assertNotNull(app);
+        assertTrue(app instanceof ConsoleAppender);
+    }
+}
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/xml/XmlSchemaTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/xml/XmlSchemaTest.java
index 21a997d..4521fdd 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/xml/XmlSchemaTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/xml/XmlSchemaTest.java
@@ -53,6 +53,9 @@ public class XmlSchemaTest {
     private static final String TARGET_NAMESPACE = "http://logging.apache.org/log4j/2.0/config";
 
     private static final List<String> IGNORE_CONFIGS = Arrays.asList( //
+            "log4j2-arbiters.xml", // Arbiters violate XML schema as they can appear anywhere
+            "log4j2-scriptArbiters.xml",
+            "log4j2-selectArbiters.xml",
             "gcFreeLogging.xml", // has 2 <Pattern> tags defined
             "legacy-plugins.xml", //
             "logback", // logback configs
diff --git a/log4j-core/src/test/resources/log4j2-arbiters.xml b/log4j-core/src/test/resources/log4j2-arbiters.xml
new file mode 100644
index 0000000..3cca438
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j2-arbiters.xml
@@ -0,0 +1,41 @@
+<?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 name="ConfigTest" status="ERROR" monitorInterval="5">
+  <Appenders>
+
+    <SystemPropertyArbiter propertyName="env" propertyValue="dev">
+      <Console name="Out">
+        <PatternLayout pattern="%m%n"/>
+      </Console>
+    </SystemPropertyArbiter>
+    <SystemPropertyArbiter propertyName="env" propertyValue="prod">
+      <List name="Out">
+      </List>
+    </SystemPropertyArbiter>
+
+  </Appenders>
+  <Loggers>
+    <Logger name="org.apache.test" level="trace" additivity="false">
+      <AppenderRef ref="Out"/>
+    </Logger>
+    <Root level="error">
+      <AppenderRef ref="Out"/>
+    </Root>
+  </Loggers>
+</Configuration>
\ No newline at end of file
diff --git a/log4j-core/src/test/resources/log4j2-scriptArbiters.xml b/log4j-core/src/test/resources/log4j2-scriptArbiters.xml
new file mode 100644
index 0000000..0f34235
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j2-scriptArbiters.xml
@@ -0,0 +1,51 @@
+<?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 name="ConfigTest" status="ERROR" monitorInterval="5">
+  <Appenders>
+
+    <ScriptArbiter>
+      <Script name="DevEnv" language="JavaScript"><![CDATA[
+            var System = Java.type("java.lang.System");
+            result = System.getProperty("env") == "dev";
+            ]]>
+      </Script>
+      <Console name="Out">
+        <PatternLayout pattern="%m%n"/>
+      </Console>
+    </ScriptArbiter>
+    <ScriptArbiter>
+      <Script name="ProdEnv" language="JavaScript"><![CDATA[
+            var System = Java.type("java.lang.System");
+            result = System.getProperty("env") == "prod";
+            ]]>
+      </Script>
+      <List name="Out">
+      </List>
+    </ScriptArbiter>
+
+  </Appenders>
+  <Loggers>
+    <Logger name="org.apache.test" level="trace" additivity="false">
+      <AppenderRef ref="Out"/>
+    </Logger>
+    <Root level="error">
+      <AppenderRef ref="Out"/>
+    </Root>
+  </Loggers>
+</Configuration>
\ No newline at end of file
diff --git a/log4j-core/src/test/resources/log4j2-selectArbiters.xml b/log4j-core/src/test/resources/log4j2-selectArbiters.xml
new file mode 100644
index 0000000..b86d6d1
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j2-selectArbiters.xml
@@ -0,0 +1,43 @@
+<?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 name="ConfigTest" status="ERROR" monitorInterval="5">
+  <Appenders>
+
+    <Select>
+      <SystemPropertyArbiter propertyName="env" propertyValue="dev">
+        <Console name="Out">
+          <PatternLayout pattern="%m%n"/>
+        </Console>
+      </SystemPropertyArbiter>
+      <DefaultArbiter>
+        <List name="Out">
+        </List>
+      </DefaultArbiter>
+    </Select>
+
+  </Appenders>
+  <Loggers>
+    <Logger name="org.apache.test" level="trace" additivity="false">
+      <AppenderRef ref="Out"/>
+    </Logger>
+    <Root level="error">
+      <AppenderRef ref="Out"/>
+    </Root>
+  </Loggers>
+</Configuration>
\ No newline at end of file
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/Node.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/Node.java
index 22fd9bf..acf1b63 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/Node.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/Node.java
@@ -36,7 +36,7 @@ public class Node {
      */
     public static final String CATEGORY = "Core";
 
-    private final Node parent;
+    private Node parent;
     private final String name;
     private String value;
     private final PluginType<?> type;
@@ -77,6 +77,10 @@ public class Node {
         this.object = node.object;
     }
 
+    public void setParent(Node parent) {
+        this.parent = parent;
+    }
+
     public Map<String, String> getAttributes() {
         return attributes;
     }
diff --git a/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringProfileArbiter.java b/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringProfileArbiter.java
new file mode 100644
index 0000000..1eabd59
--- /dev/null
+++ b/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringProfileArbiter.java
@@ -0,0 +1,97 @@
+/*
+ * 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.spring.boot;
+
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.arbiters.Arbiter;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
+import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
+import org.apache.logging.log4j.plugins.Node;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.springframework.core.env.Environment;
+import org.springframework.core.env.Profiles;
+import org.springframework.util.StringUtils;
+
+/**
+ * An Aribter that uses the active Spring profile to determine if configuration should be included.
+ */
+@Plugin(name = "SpringProfile", category = Node.CATEGORY, elementType = Arbiter.ELEMENT_TYPE,
+        deferChildren = true, printObject = true)
+public class SpringProfileArbiter extends SpringEnvironmentHolder implements Arbiter {
+
+    private final String[] profileNames;
+
+    private SpringProfileArbiter(final String[] profiles) {
+        this.profileNames = profiles;
+
+    }
+
+    @Override
+    public boolean isCondition() {
+        Environment environment = getEnvironment();
+        if (environment == null) {
+            return false;
+        }
+
+        if (profileNames.length == 0) {
+            return false;
+        }
+        return environment.acceptsProfiles(Profiles.of(profileNames));
+    }
+
+    @PluginBuilderFactory
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    public static class Builder implements org.apache.logging.log4j.core.util.Builder<SpringProfileArbiter> {
+
+        public static final String ATTR_NAME = "name";
+
+        @PluginBuilderAttribute(ATTR_NAME)
+        private String name;
+
+        @PluginConfiguration
+        private Configuration configuration;;
+
+        /**
+         * Sets the Profile Name or Names.
+         * @param name the profile name(s).
+         * @return this
+         */
+        public Builder setName(final String name) {
+            this.name = name;
+            return asBuilder();
+        }
+
+        public Builder setConfiguration(final Configuration configuration) {
+            this.configuration = configuration;
+            return asBuilder();
+        }
+
+        private Builder asBuilder() {
+            return this;
+        }
+
+        public SpringProfileArbiter build() {
+            String[] profileNames = StringUtils.trimArrayElements(
+                    StringUtils.commaDelimitedListToStringArray(configuration.getStrSubstitutor().replace(name)));
+            return new SpringProfileArbiter(profileNames);
+        }
+    }
+}
diff --git a/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/SpringProfileTest.java b/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/SpringProfileTest.java
new file mode 100644
index 0000000..3182ae3
--- /dev/null
+++ b/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/SpringProfileTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.spring.boot;
+
+import java.io.File;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.apache.logging.log4j.test.appender.ListAppender;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.springframework.mock.env.MockEnvironment;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests basic condition processing.
+ */
+public class SpringProfileTest {
+
+    static final String CONFIG = "target/test-classes/log4j2-springProfile.xml";
+    static LoggerContext loggerContext;
+    static MockEnvironment env;
+
+    @BeforeClass
+    public static void before() {
+        loggerContext = (LoggerContext) LogManager.getContext(false);
+        env = new MockEnvironment();
+        loggerContext.putObject(Log4j2CloudConfigLoggingSystem.ENVIRONMENT_KEY, env);
+    }
+
+
+    @Test
+    public void prodTest() {
+        env.setActiveProfiles("prod");
+        loggerContext.setConfigLocation(new File(CONFIG).toURI());
+        assertNotNull(loggerContext);
+        Appender app = loggerContext.getConfiguration().getAppender("Out");
+        assertNotNull(app);
+        assertTrue(app instanceof ListAppender);
+    }
+
+    @Test
+    public void devTest() {
+        env.setActiveProfiles("dev");
+        loggerContext.setConfigLocation(new File(CONFIG).toURI());
+        assertNotNull(loggerContext);
+        Appender app = loggerContext.getConfiguration().getAppender("Out");
+        assertNotNull(app);
+        assertTrue(app instanceof ConsoleAppender);
+    }
+}
diff --git a/log4j-spring-boot/src/test/resources/log4j2-springProfile.xml b/log4j-spring-boot/src/test/resources/log4j2-springProfile.xml
new file mode 100644
index 0000000..c3a3783
--- /dev/null
+++ b/log4j-spring-boot/src/test/resources/log4j2-springProfile.xml
@@ -0,0 +1,41 @@
+<?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 name="ConfigTest" status="ERROR" monitorInterval="5">
+  <Appenders>
+
+    <SpringProfile name="dev | staging">
+      <Console name="Out">
+        <PatternLayout pattern="%m%n"/>
+      </Console>
+    </SpringProfile>
+    <SpringProfile name="prod">
+      <List name="Out">
+      </List>
+    </SpringProfile>
+
+  </Appenders>
+  <Loggers>
+    <Logger name="org.apache.test" level="trace" additivity="false">
+      <AppenderRef ref="Out"/>
+    </Logger>
+    <Root level="error">
+      <AppenderRef ref="Out"/>
+    </Root>
+  </Loggers>
+</Configuration>
\ No newline at end of file
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 56f48ce..833d98e 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -170,6 +170,9 @@
     </release>
     <release version="2.15.0" date="2021-MM-DD" description="GA Release 2.15.0">
       <!-- ADDS -->
+      <action issue="LOG4J2-3064" dev="rgoers" type="add">
+        Add Arbiters and SpringProfile plugin.
+      </action>
       <action issue="LOG4J2-3056" dev="vy" type="add" due-to="Marcono1234">
         Refactor MD5 usage for sharing sensitive information.
       </action>
diff --git a/src/site/asciidoc/manual/configuration.adoc b/src/site/asciidoc/manual/configuration.adoc
index d0fed9e..4806419 100644
--- a/src/site/asciidoc/manual/configuration.adoc
+++ b/src/site/asciidoc/manual/configuration.adoc
@@ -43,6 +43,165 @@ Note that unlike Log4j 1.x, the public Log4j 2 API does not expose
 methods to add, modify or remove appenders and filters or manipulate the
 configuration in any way.
 
+[#Architecture]
+== Architecture
+
+In part because support for XML was added first, Log4j's configuration is reflected as a tree structure.
+In fact every configuration dialect, including the ConfigurationBuilder, generates a Node for every
+configuration element. A node is a fairly simple structure that contains a set of attributes, a set of
+child nodes and a PluginType. It is important to note that every Node must have a corresponding plugn,
+as the plugin is the component that actually performs the work represented by the node.
+
+Every document type supported by Log4j has a ConfigurationFactory. The factory itself is a Log4j plugin
+that declares what file extensions it supports and what its priority is. Properties have the highest
+precedence with a value of 8, followed by yaml, json and xml. When autoconfiguration is performed Log4j
+will call each of these factories in order to determine which, if any, support the specified configuration
+file format. If one is found that factory will create the corresponding Configuratoin object and pass the
+reference to the configuration data to it.
+
+Every configuration implementation, such as XMLConfiguration, YamlConfiguration, JsonConfiguration, etc.
+has the primary task of converting the configuration text into the Node tree, typically by parsing the
+text with whatever tool is available for that document type. It should be noted that while most of the
+supported document types are inherintly tree structured, the Java properties syntax is not. Because of the
+need to convert the syntax into a Node tree the Java properties syntax used by Log4j required all properties
+follow a naming pattern that made the tree structure clear. As a consequence, the Java Properties format
+tends to be more verbose than using a different document type.
+
+Once the Node tree is created control is delegated to AbstractConfiguration, which convertes the Nodes into
+their respective Java objects using Log4j's Plugin system and provides all the common functionality.
+
+[#Arbiters]
+== Arbiters
+
+In some situations it is desirable to have a single logging configuration that can be used in any
+deployment environment. For example, it may be necessary to have a different default logging level in
+production then in development. Another case might be where one type of appender is used when running
+natively but another is used when deployed to a docker container. One way to handle that is to use
+a tool such as Spring Cloud Config Server that can be environment aware and serve a different file for
+each environment. Another option is to include Arbiters in the configuration.
+
+An Arbiter is a Log4j plugin that has the job of determining whether other configured elements should be
+included in the generated configuration. While all other "Core" plugins are designed to execute as part of
+Log4j's runtime logic Arbiters execute after the Node tree has been constructed but before the tree is
+converted to a configuration. An Arbiter is a Node itself which is always removed from the Node tree
+before it the tree is processed. All an arbiter really does is provide a method that returns a boolean
+result that determines whether the child nodes of the arbiter should remain in the configuration or be
+pruned.
+
+Arbiters may occur anywhere an element is allowed in the configuration. So an Aribiter could encapsulate
+something as simple as a single property declaration or a whole set of Appenders or Loggers. Arbiters
+may also be nested although Arbiters that are the descendant of another arbiter will only be evalued if the
+ancestor returned true. The child elements of an Arbiter must be valid elements for whatever element is
+the parent of the Arbiter.
+
+This example shows two Arbiters configured that will include either a Console Appender or a List Appender
+depending on whether the value of the env System Property is "dev" or "prod".
+
+[source,xml]
+----
+<Configuration name="ConfigTest" status="ERROR" monitorInterval="5">
+  <Appenders>
+
+    <SystemPropertyArbiter propertyName="env" propertyValue="dev">
+      <Console name="Out">
+        <PatternLayout pattern="%m%n"/>
+      </Console>
+    </SystemPropertyArbiter>
+    <SystemPropertyArbiter propertyName="env" propertyValue="prod">
+      <List name="Out">
+      </List>
+    </SystemPropertyArbiter>
+
+  </Appenders>
+  <Loggers>
+    <Logger name="org.apache.test" level="trace" additivity="false">
+      <AppenderRef ref="Out"/>
+    </Logger>
+    <Root level="error">
+      <AppenderRef ref="Out"/>
+    </Root>
+  </Loggers>
+</Configuration>
+----
+
+Normally Arbiters act in isolation from other Arbiters. That is, the outcome of one Arbiter will not
+impact any other Arbiters. This can be cumbersome when you simply want to use one of a set of choices. A
+special plugin named "Select" can be used in this case. Each element under the Select is required to be
+an Arbiter. The first Arbiter that returns a true value will be the one used while others are ignored.
+If no Arbiter returns true a DefaultAtrbiter may be configured with the default configuration elements.
+The DefaultArbiter is an Arbiter that always returns true, so using it outside of a Select would result in
+its configured elements always being included just as if it hadn't been present.
+
+This example shows an Arbiter that uses Javascript residing in a separate file to determine whether to
+include the Console Appender. If the result is false then a List Appender will be included.
+
+[source,xml]
+----
+<Configuration name="ConfigTest" status="ERROR" monitorInterval="5">
+  <Appenders>
+    <Select>
+      <ScriptArbiter>
+        <ScriptFile language="JavaScript" path="src/test/resources/scripts/prodtest.js" charset="UTF-8" />
+        <Console name="Out">
+          <PatternLayout pattern="%m%n"/>
+        </Console>
+      </ScriptArbiter>
+      <DefaultArbiter>
+        <List name="Out">
+        </List>
+      </DefaultArbiter>
+    </Select>
+  </Appenders>
+  <Loggers>
+    <Logger name="org.apache.test" level="trace" additivity="false">
+      <AppenderRef ref="Out"/>
+    </Logger>
+    <Root level="error">
+      <AppenderRef ref="Out"/>
+    </Root>
+  </Loggers>
+</Configuration>
+----
+
+Natively Log4j contains the SystemProperty Arbister that can evaluate whether to include elements based on
+whether a SystemProperty is non-null or has a specific value, a ClassArbiter that makes its decision
+based on whether the specified class is present, and a ScriptArbiter that makes its decision based
+on the result of the script configured with it.
+
+For Spring Boot users an Arbiter named <code>SpringProfile</code> has been provided. The specified profiles
+are evaluated by Spring's <code>Environment.acceptsProfiles()</code> method, so any expressions it supports
+may be used as the name attribute.
+
+This example will use a Console Appender when the Spring profile is "dev" or "staging" and a List
+Appender when the active profile is "prod".
+
+[source,xml]
+----
+<Configuration name="ConfigTest" status="ERROR" monitorInterval="5">
+  <Appenders>
+
+    <SpringProfile name="dev | staging">
+      <Console name="Out">
+        <PatternLayout pattern="%m%n"/>
+      </Console>
+    </SpringProfile>
+    <SpringProfile name="prod">
+      <List name="Out">
+      </List>
+    </SpringProfile>
+
+  </Appenders>
+  <Loggers>
+    <Logger name="org.apache.test" level="trace" additivity="false">
+      <AppenderRef ref="Out"/>
+    </Logger>
+    <Root level="error">
+      <AppenderRef ref="Out"/>
+    </Root>
+  </Loggers>
+</Configuration>
+----
+
 [#AutomaticConfiguration]
 == Automatic Configuration
 
diff --git a/src/site/site.xml b/src/site/site.xml
index 48a6d3e..ef83f81 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -103,6 +103,8 @@
       <item name="Scala API" href="/manual/scala-api.html"/>
 
       <item name="Configuration" href="/manual/configuration.html" collapse="true">
+        <item name="Configuration Archtecture" href="/manual/configuration.html#Architecture"/>
+        <item name="Arbiters" href="/manual/configuration.html#Arbiters"/>
         <item name="Automatic Configuration" href="/manual/configuration.html#AutomaticConfiguration"/>
         <item name="Additivity" href="/manual/configuration.html#Additivity"/>
         <item name="Automatic Reconfiguration" href="/manual/configuration.html#AutomaticReconfiguration"/>