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"/>