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 2016/04/24 08:33:11 UTC
logging-log4j2 git commit: LOG4J2-494 working CompositeConfiguration
Repository: logging-log4j2
Updated Branches:
refs/heads/LOG4j2-494 [created] bb64f4577
LOG4J2-494 working CompositeConfiguration
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/bb64f457
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/bb64f457
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/bb64f457
Branch: refs/heads/LOG4j2-494
Commit: bb64f457730864b70e3b932c2156c5522245d909
Parents: 4195c7b
Author: Ralph Goers <rg...@nextiva.com>
Authored: Sat Apr 23 23:29:57 2016 -0700
Committer: Ralph Goers <rg...@nextiva.com>
Committed: Sat Apr 23 23:29:57 2016 -0700
----------------------------------------------------------------------
.../apache/logging/log4j/util/LoaderUtil.java | 8 +-
log4j-core/${filename} | 0
.../core/config/AbstractConfiguration.java | 26 +-
.../log4j/core/config/ConfigurationFactory.java | 66 +++--
.../log4j/core/config/ConfigurationSource.java | 51 +++-
.../core/config/ConfiguratonFileWatcher.java | 4 +
.../logging/log4j/core/config/Configurator.java | 46 +++-
.../composite/CompositeConfiguration.java | 168 +++++++++++++
.../config/composite/DefaultMergeStrategy.java | 252 +++++++++++++++++++
.../core/config/composite/MergeStrategy.java | 33 +++
.../core/config/composite/package-info.java | 21 ++
.../log4j/core/impl/Log4jContextFactory.java | 43 +++-
.../logging/log4j/core/util/WatchManager.java | 9 +
.../core/config/CompositeConfigurationTest.java | 149 +++++++++++
.../src/test/resources/log4j-comp-appender.json | 34 +++
.../src/test/resources/log4j-comp-appender.xml | 39 +++
.../src/test/resources/log4j-comp-filter.json | 9 +
.../src/test/resources/log4j-comp-filter.xml | 34 +++
.../src/test/resources/log4j-comp-logger.json | 36 +++
.../src/test/resources/log4j-comp-logger.xml | 41 +++
.../test/resources/log4j-comp-properties.json | 16 ++
.../test/resources/log4j-comp-properties.xml | 34 +++
22 files changed, 1072 insertions(+), 47 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java
index 096d945..c2d55a2 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java
@@ -28,7 +28,7 @@ import java.util.Objects;
/**
* <em>Consider this class private.</em> Utility class for ClassLoaders.
- *
+ *
* @see ClassLoader
* @see RuntimePermission
* @see Thread#getContextClassLoader()
@@ -135,14 +135,14 @@ public final class LoaderUtil {
* @throws InvocationTargetException if there was an exception whilst constructing the class
* @since 2.1
*/
- public static Object newInstanceOf(final String className) throws ClassNotFoundException, IllegalAccessException,
+ public static <T> T newInstanceOf(final String className) throws ClassNotFoundException, IllegalAccessException,
InstantiationException, NoSuchMethodException, InvocationTargetException {
final Class<?> clazz = loadClass(className);
try {
- return clazz.getConstructor().newInstance();
+ return (T) clazz.getConstructor().newInstance();
} catch (final NoSuchMethodException ignored) {
// FIXME: looking at the code for Class.newInstance(), this seems to do the same thing as above
- return clazz.newInstance();
+ return (T) clazz.newInstance();
}
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/${filename}
----------------------------------------------------------------------
diff --git a/log4j-core/${filename} b/log4j-core/${filename}
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
----------------------------------------------------------------------
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 00cd397..316381a 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
@@ -98,6 +98,11 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
protected boolean isShutdownHookEnabled = true;
/**
+ * The Script manager.
+ */
+ protected ScriptManager scriptManager;
+
+ /**
* The Advertiser which exposes appender configurations to external systems.
*/
private Advertiser advertiser = new DefaultAdvertiser();
@@ -113,7 +118,6 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
private LoggerConfig root = new LoggerConfig();
private final ConcurrentMap<String, Object> componentMap = new ConcurrentHashMap<>();
private final ConfigurationSource configurationSource;
- private ScriptManager scriptManager;
private final ConfigurationScheduler configurationScheduler = new ConfigurationScheduler();
private final WatchManager watchManager = new WatchManager(configurationScheduler);
private AsyncLoggerConfigDisruptor asyncLoggerConfigDisruptor;
@@ -151,6 +155,18 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
return scriptManager;
}
+ public void setScriptManager(ScriptManager scriptManager) {
+ this.scriptManager = scriptManager;
+ }
+
+ public PluginManager getPluginManager() {
+ return pluginManager;
+ }
+
+ public void setPluginManager(PluginManager pluginManager) {
+ this.pluginManager = pluginManager;
+ }
+
@Override
public WatchManager getWatchManager() {
return watchManager;
@@ -161,7 +177,11 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
return configurationScheduler;
}
- @Override
+ public Node getRootNode() {
+ return rootNode;
+ }
+
+ @Override
public AsyncLoggerConfigDelegate getAsyncLoggerConfigDelegate() {
// lazily instantiate only when requested by AsyncLoggers:
// loading AsyncLoggerConfigDisruptor requires LMAX Disruptor jar on classpath
@@ -357,7 +377,7 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
return isShutdownHookEnabled;
}
- protected void setup() {
+ public void setup() {
}
protected Level getDefaultStatus() {
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java
index 8f3d764..41f3571 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java
@@ -36,6 +36,7 @@ import java.util.concurrent.locks.ReentrantLock;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory;
+import org.apache.logging.log4j.core.config.composite.CompositeConfiguration;
import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
import org.apache.logging.log4j.core.config.plugins.util.PluginType;
import org.apache.logging.log4j.core.lookup.Interpolator;
@@ -392,32 +393,21 @@ public abstract class ConfigurationFactory extends ConfigurationBuilderFactory {
final String configLocationStr = this.substitutor.replace(PropertiesUtil.getProperties()
.getStringProperty(CONFIGURATION_FILE_PROPERTY));
if (configLocationStr != null) {
- ConfigurationSource source = null;
- try {
- source = getInputFromUri(NetUtils.toURI(configLocationStr));
- } catch (final Exception ex) {
- // Ignore the error and try as a String.
- LOGGER.catching(Level.DEBUG, ex);
- }
- if (source == null) {
- final ClassLoader loader = LoaderUtil.getThreadContextClassLoader();
- source = getInputFromString(configLocationStr, loader);
- }
- if (source != null) {
- for (final ConfigurationFactory factory : getFactories()) {
- final String[] types = factory.getSupportedTypes();
- if (types != null) {
- for (final String type : types) {
- if (type.equals("*") || configLocationStr.endsWith(type)) {
- final Configuration config = factory.getConfiguration(source);
- if (config != null) {
- return config;
- }
- }
- }
+ String[] sources = configLocationStr.split(",");
+ if (sources.length > 1) {
+ List<AbstractConfiguration> configs = new ArrayList<>();
+ for (String sourceLocation : sources) {
+ Configuration config = getConfiguration(sourceLocation);
+ if (config != null && config instanceof AbstractConfiguration) {
+ configs.add((AbstractConfiguration) config);
+ } else {
+ LOGGER.error("Failed to created configuration at {}", sourceLocation);
+ return null;
}
}
+ return new CompositeConfiguration(configs);
}
+ return getConfiguration(configLocationStr);
} else {
for (final ConfigurationFactory factory : getFactories()) {
final String[] types = factory.getSupportedTypes();
@@ -468,6 +458,36 @@ public abstract class ConfigurationFactory extends ConfigurationBuilderFactory {
return new DefaultConfiguration();
}
+ private Configuration getConfiguration(String configLocationStr) {
+ ConfigurationSource source = null;
+ try {
+ source = getInputFromUri(NetUtils.toURI(configLocationStr));
+ } catch (final Exception ex) {
+ // Ignore the error and try as a String.
+ LOGGER.catching(Level.DEBUG, ex);
+ }
+ if (source == null) {
+ final ClassLoader loader = LoaderUtil.getThreadContextClassLoader();
+ source = getInputFromString(configLocationStr, loader);
+ }
+ if (source != null) {
+ for (final ConfigurationFactory factory : getFactories()) {
+ final String[] types = factory.getSupportedTypes();
+ if (types != null) {
+ for (final String type : types) {
+ if (type.equals("*") || configLocationStr.endsWith(type)) {
+ final Configuration config = factory.getConfiguration(source);
+ if (config != null) {
+ return config;
+ }
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
private Configuration getConfiguration(final boolean isTest, final String name) {
final boolean named = Strings.isNotEmpty(name);
final ClassLoader loader = LoaderUtil.getThreadContextClassLoader();
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationSource.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationSource.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationSource.java
index edb2d75..f1627bb 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationSource.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationSource.java
@@ -23,6 +23,8 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.net.URL;
import java.util.Objects;
@@ -44,7 +46,7 @@ public class ConfigurationSource {
/**
* Constructs a new {@code ConfigurationSource} with the specified input stream that originated from the specified
* file.
- *
+ *
* @param stream the input stream
* @param file the file where the input stream originated
*/
@@ -59,7 +61,7 @@ public class ConfigurationSource {
/**
* Constructs a new {@code ConfigurationSource} with the specified input stream that originated from the specified
* url.
- *
+ *
* @param stream the input stream
* @param url the URL where the input stream originated
*/
@@ -74,7 +76,7 @@ public class ConfigurationSource {
/**
* Constructs a new {@code ConfigurationSource} with the specified input stream. Since the stream is the only source
* of data, this constructor makes a copy of the stream contents.
- *
+ *
* @param stream the input stream
* @throws IOException if an exception occurred reading from the specified stream
*/
@@ -92,7 +94,7 @@ public class ConfigurationSource {
/**
* Returns the contents of the specified {@code InputStream} as a byte array.
- *
+ *
* @param inputStream the stream to read
* @return the contents of the specified stream
* @throws IOException if a problem occurred reading from the stream
@@ -113,7 +115,7 @@ public class ConfigurationSource {
/**
* Returns the file configuration source, or {@code null} if this configuration source is based on an URL or has
* neither a file nor an URL.
- *
+ *
* @return the configuration source file, or {@code null}
*/
public File getFile() {
@@ -123,7 +125,7 @@ public class ConfigurationSource {
/**
* Returns the configuration source URL, or {@code null} if this configuration source is based on a file or has
* neither a file nor an URL.
- *
+ *
* @return the configuration source URL, or {@code null}
*/
public URL getURL() {
@@ -131,9 +133,40 @@ public class ConfigurationSource {
}
/**
+ * Returns a URI representing the configuration resource or null if it cannot be determined.
+ * @return The URI.
+ */
+ public URI getURI() {
+ URI sourceURI = null;
+ if (url != null) {
+ try {
+ sourceURI = url.toURI();
+ } catch (URISyntaxException ex) {
+ /* Ignore the exception */
+ }
+ }
+ if (sourceURI == null && file != null) {
+ sourceURI = file.toURI();
+ }
+ if (sourceURI == null && location != null) {
+ try {
+ sourceURI = new URI(location);
+ } catch (URISyntaxException ex) {
+ // Assume the scheme was missing.
+ try {
+ sourceURI = new URI("file://" + location);
+ } catch (URISyntaxException uriEx) {
+ /* Ignore the exception */
+ }
+ }
+ }
+ return sourceURI;
+ }
+
+ /**
* Returns a string describing the configuration source file or URL, or {@code null} if this configuration source
* has neither a file nor an URL.
- *
+ *
* @return a string describing the configuration source file or URL, or {@code null}
*/
public String getLocation() {
@@ -142,7 +175,7 @@ public class ConfigurationSource {
/**
* Returns the input stream that this configuration source was constructed with.
- *
+ *
* @return the input stream that this configuration source was constructed with.
*/
public InputStream getInputStream() {
@@ -151,7 +184,7 @@ public class ConfigurationSource {
/**
* Returns a new {@code ConfigurationSource} whose input stream is reset to the beginning.
- *
+ *
* @return a new {@code ConfigurationSource}
* @throws IOException if a problem occurred while opening the new input stream
*/
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfiguratonFileWatcher.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfiguratonFileWatcher.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfiguratonFileWatcher.java
index e268c54..82d06ab 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfiguratonFileWatcher.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfiguratonFileWatcher.java
@@ -35,6 +35,10 @@ public class ConfiguratonFileWatcher implements FileWatcher {
this.listeners = listeners;
}
+ public List<ConfigurationListener> getListeners() {
+ return listeners;
+ }
+
@Override
public void fileModified(File file) {
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configurator.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configurator.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configurator.java
index fe6a5d5..f0e462e 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configurator.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configurator.java
@@ -17,6 +17,8 @@
package org.apache.logging.log4j.core.config;
import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.Level;
@@ -109,8 +111,24 @@ public final class Configurator {
*/
public static LoggerContext initialize(final String name, final ClassLoader loader, final String configLocation,
final Object externalContext) {
- final URI uri = Strings.isBlank(configLocation) ? null : NetUtils.toURI(configLocation);
- return initialize(name, loader, uri, externalContext);
+ if (Strings.isBlank(configLocation)) {
+ return initialize(name, loader, (URI) null, externalContext);
+ }
+ if (configLocation.contains(",")) {
+ String[] parts = configLocation.split(",");
+ String scheme = null;
+ List<URI> uris = new ArrayList<>(parts.length);
+ for (String part : parts) {
+ URI uri = NetUtils.toURI(scheme != null ? scheme + ":" + part : part);
+ if (scheme == null && uri.getScheme() != null) {
+ scheme = uri.getScheme();
+ }
+ uris.add(uri);
+ }
+ return initialize(name, loader, uris, externalContext);
+ } else {
+ return initialize(name, loader, NetUtils.toURI(configLocation), externalContext);
+ }
}
/**
@@ -146,6 +164,20 @@ public final class Configurator {
return null;
}
+ public static LoggerContext initialize(final String name, final ClassLoader loader, final List<URI> configLocations,
+ final Object externalContext) {
+ try {
+ final Log4jContextFactory factory = getFactory();
+ return factory == null ?
+ null :
+ factory.getContext(FQCN, loader, externalContext, false, configLocations, name);
+ } catch (final Exception ex) {
+ LOGGER.error("There was a problem initializing the LoggerContext [{}] using configurations at [{}].", name,
+ configLocations, ex);
+ }
+ return null;
+ }
+
/**
* Initializes the Logging Context.
* @param name The Context name.
@@ -202,7 +234,7 @@ public final class Configurator {
public static void setAllLevels(final String parentLogger, final Level level) {
// 1) get logger config
// 2) if exact match, use it, if not, create it.
- // 3) set level on logger config
+ // 3) set level on logger config
// 4) update child logger configs with level
// 5) update loggers
final LoggerContext loggerContext = LoggerContext.getContext(false);
@@ -225,10 +257,10 @@ public final class Configurator {
}
return set;
}
-
+
/**
* Sets logger levels.
- *
+ *
* @param levelMap
* a levelMap where keys are level names and values are new
* Levels.
@@ -249,7 +281,7 @@ public final class Configurator {
/**
* Sets a logger's level.
- *
+ *
* @param loggerName
* the logger name
* @param level
@@ -283,7 +315,7 @@ public final class Configurator {
/**
* Sets the root logger's level.
- *
+ *
* @param level
* the new level
*/
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/CompositeConfiguration.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/CompositeConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/CompositeConfiguration.java
new file mode 100644
index 0000000..8b416b3
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/CompositeConfiguration.java
@@ -0,0 +1,168 @@
+/*
+ * 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.composite;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.config.AbstractConfiguration;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.ConfiguratonFileWatcher;
+import org.apache.logging.log4j.core.config.Node;
+import org.apache.logging.log4j.core.config.Reconfigurable;
+import org.apache.logging.log4j.core.config.plugins.util.ResolverUtil;
+import org.apache.logging.log4j.core.config.status.StatusConfiguration;
+import org.apache.logging.log4j.core.util.FileWatcher;
+import org.apache.logging.log4j.core.util.Patterns;
+import org.apache.logging.log4j.core.util.WatchManager;
+import org.apache.logging.log4j.util.LoaderUtil;
+import org.apache.logging.log4j.util.PropertiesUtil;
+
+/**
+ * A Composite Configuration.
+ */
+public class CompositeConfiguration extends AbstractConfiguration implements Reconfigurable {
+
+ /**
+ * Allow the ConfigurationFactory class to be specified as a system property.
+ */
+ public static final String MERGE_STRATEGY_PROPERTY = "log4j.mergeStrategy";
+
+ private static final String[] VERBOSE_CLASSES = new String[] {ResolverUtil.class.getName()};
+
+ private List<? extends AbstractConfiguration> configurations;
+
+ private MergeStrategy mergeStrategy;
+
+ /**
+ * Construct the ComponsiteConfiguration.
+ *
+ * @param configurations The List of Configurations to merge.
+ */
+ public CompositeConfiguration(List<? extends AbstractConfiguration> configurations) {
+ super(ConfigurationSource.NULL_SOURCE);
+ this.configurations = configurations;
+ String mergeStrategyClassName = PropertiesUtil.getProperties().getStringProperty(MERGE_STRATEGY_PROPERTY,
+ DefaultMergeStrategy.class.getName());
+ try {
+ mergeStrategy = LoaderUtil.newInstanceOf(mergeStrategyClassName);
+ } catch (ClassNotFoundException | IllegalAccessException | NoSuchMethodException | InvocationTargetException |
+ InstantiationException ex) {
+ mergeStrategy = new DefaultMergeStrategy();
+ }
+ }
+
+ @Override
+ public void setup() {
+ AbstractConfiguration targetConfiguration = configurations.get(0);
+ staffChildConfiguration(targetConfiguration);
+ WatchManager watchManager = targetConfiguration.getWatchManager();
+ rootNode = targetConfiguration.getRootNode();
+ for (AbstractConfiguration sourceConfiguration : configurations.subList(1, configurations.size())) {
+ staffChildConfiguration(sourceConfiguration);
+ Node sourceRoot = sourceConfiguration.getRootNode();
+ mergeStrategy.mergConfigurations(rootNode, sourceRoot, getPluginManager());
+ if (LOGGER.isEnabled(Level.ALL)) {
+ StringBuilder sb = new StringBuilder();
+ printNodes("", rootNode, sb);
+ System.out.println(sb.toString());
+ }
+ int monitorInterval = sourceConfiguration.getWatchManager().getIntervalSeconds();
+ if (monitorInterval > 0) {
+ if (monitorInterval < watchManager.getIntervalSeconds()) {
+ watchManager.setIntervalSeconds(monitorInterval);
+ }
+ WatchManager sourceWatchManager = sourceConfiguration.getWatchManager();
+ Map<File, FileWatcher> watchers = sourceWatchManager.getWatchers();
+ FileWatcher fileWatcher = new ConfiguratonFileWatcher(this, listeners);
+ for (Map.Entry<File, FileWatcher> entry : watchers.entrySet()) {
+ if (entry.getValue() instanceof ConfiguratonFileWatcher) {
+ watchManager.watchFile(entry.getKey(), fileWatcher);
+ }
+ }
+ }
+ }
+ final StatusConfiguration statusConfig = new StatusConfiguration().withVerboseClasses(VERBOSE_CLASSES)
+ .withStatus(getDefaultStatus());
+ for (final Map.Entry<String, String> entry : rootNode.getAttributes().entrySet()) {
+ final String key = entry.getKey();
+ final String value = getStrSubstitutor().replace(entry.getValue());
+ if ("status".equalsIgnoreCase(key)) {
+ statusConfig.withStatus(value);
+ } else if ("dest".equalsIgnoreCase(key)) {
+ statusConfig.withDestination(value);
+ } else if ("shutdownHook".equalsIgnoreCase(key)) {
+ isShutdownHookEnabled = !"disable".equalsIgnoreCase(value);
+ } else if ("verbose".equalsIgnoreCase(key)) {
+ statusConfig.withVerbosity(value);
+ } else if ("packages".equalsIgnoreCase(key)) {
+ pluginPackages.addAll(Arrays.asList(value.split(Patterns.COMMA_SEPARATOR)));
+ } else if ("name".equalsIgnoreCase(key)) {
+ setName(value);
+ }
+ }
+ statusConfig.initialize();
+ }
+
+ @Override
+ public Configuration reconfigure() {
+ List<AbstractConfiguration> configs = new ArrayList<>();
+ ConfigurationFactory factory = ConfigurationFactory.getInstance();
+ for (AbstractConfiguration config : configurations) {
+ ConfigurationSource source = config.getConfigurationSource();
+ URI sourceURI = source.getURI();
+ Configuration currentConfig;
+ if (sourceURI != null) {
+ LOGGER.warn("Unable to determine URI for configuration {}, changes to it will be ignored",
+ config.getName());
+ currentConfig = factory.getConfiguration(config.getName(), sourceURI);
+ if (currentConfig == null) {
+ LOGGER.warn("Unable to reload configuration {}, changes to it will be ignored", config.getName());
+ currentConfig = config;
+ }
+ } else {
+ currentConfig = config;
+ }
+ configs.add((AbstractConfiguration) currentConfig);
+
+ }
+
+ return new CompositeConfiguration(configs);
+ }
+
+ private void staffChildConfiguration(AbstractConfiguration childConfiguration) {
+ childConfiguration.setPluginManager(pluginManager);
+ childConfiguration.setScriptManager(scriptManager);
+ childConfiguration.setup();
+ }
+
+ private void printNodes(String indent, Node node, StringBuilder sb) {
+ sb.append(indent).append(node.getName()).append(" type: ").append(node.getType()).append("\n");
+ sb.append(indent).append(node.getAttributes().toString()).append("\n");
+ for (Node child : node.getChildren()) {
+ printNodes(indent + " ", child, sb);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/DefaultMergeStrategy.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/DefaultMergeStrategy.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/DefaultMergeStrategy.java
new file mode 100644
index 0000000..7b123a0
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/DefaultMergeStrategy.java
@@ -0,0 +1,252 @@
+/*
+ * 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.composite;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.config.Node;
+import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
+import org.apache.logging.log4j.core.config.plugins.util.PluginType;
+import org.apache.logging.log4j.core.filter.CompositeFilter;
+
+/**
+ * The default merge strategy for composite configurations.
+ * <p>
+ * The default merge strategy performs the merge according to the following rules:
+ * <ol>
+ * <li>Aggregates the global configuration attributes with those in later configurations replacing those in previous
+ * configurations with the exception that the highest status level and the lowest monitorInterval greater than 0 will
+ * be used.</li>
+ * <li>Properties from all configurations are aggregated. Duplicate properties replace those in previous
+ * configurations.</li>
+ * <li>Filters are aggregated under a CompositeFilter if more than one Filter is defined. Since Filters are not named
+ * duplicates may be present.</li>
+ * <li>Scripts and ScriptFile references are aggregated. Duplicate definiations replace those in previous
+ * configurations.</li>
+ * <li>Appenders are aggregated. Appenders with the same name are replaced by those in later configurations, including
+ * all of the Appender's subcomponents.</li>
+ * <li>Loggers are all aggregated. Logger attributes are individually merged with duplicates being replaced by those
+ * in later configurations. Appender references on a Logger are aggregated with duplicates being replaced by those in
+ * later configurations. Filters on a Logger are aggregated under a CompositeFilter if more than one Filter is defined.
+ * Since Filters are not named duplicates may be present. Filters under Appender references included or discarded
+ * depending on whether their parent Appender reference is kept or discarded.</li>
+ * </ol>
+ */
+public class DefaultMergeStrategy implements MergeStrategy {
+
+ private static final String APPENDERS = "appenders";
+ private static final String PROPERTIES = "properties";
+ private static final String LOGGERS = "loggers";
+ private static final String SCRIPTS = "scripts";
+ private static final String FILTERS = "filters";
+ private static final String STATUS = "status";
+ private static final String NAME = "name";
+ private static final String REF = "ref";
+
+ /**
+ * Merge the source Configuration into the target Configuration.
+ *
+ * @param target The target node to merge into.
+ * @param source The source node.
+ * @param pluginManager The PluginManager.
+ */
+ public void mergConfigurations(Node target, Node source, PluginManager pluginManager) {
+ for (Map.Entry<String, String> attribute : source.getAttributes().entrySet()) {
+ boolean isFound = false;
+ for (Map.Entry<String, String> targetAttribute : target.getAttributes().entrySet()) {
+ if (targetAttribute.getKey().equalsIgnoreCase(attribute.getKey())) {
+ if (attribute.getKey().equalsIgnoreCase(STATUS)) {
+ Level targetLevel = Level.getLevel(targetAttribute.getValue());
+ Level sourceLevel = Level.getLevel(attribute.getValue());
+ if (targetLevel != null && sourceLevel != null) {
+ if (sourceLevel.isLessSpecificThan(targetLevel)) {
+ targetAttribute.setValue(attribute.getValue());
+ }
+ }
+ } else {
+ if (attribute.getKey().equalsIgnoreCase("monitorInterval")) {
+ int sourceInterval = Integer.parseInt(attribute.getValue());
+ int targetInterval = Integer.parseInt(targetAttribute.getValue());
+ if (targetInterval == 0 || sourceInterval < targetInterval) {
+ targetAttribute.setValue(attribute.getValue());
+ }
+ } else {
+ targetAttribute.setValue(attribute.getValue());
+ }
+ }
+ isFound = true;
+ }
+ }
+ if (!isFound) {
+ target.getAttributes().put(attribute.getKey(), attribute.getValue());
+ }
+ }
+ for (Node sourceChildNode : source.getChildren()) {
+ boolean isFilter = isFilterNode(sourceChildNode);
+ boolean isMerged = false;
+ for (Node targetChildNode : target.getChildren()) {
+ if (isFilter) {
+ if (isFilterNode(targetChildNode)) {
+ updateFilterNode(target, targetChildNode, sourceChildNode, pluginManager);
+ isMerged = true;
+ break;
+ } else {
+ continue;
+ }
+ }
+
+ if (!targetChildNode.getName().equalsIgnoreCase(sourceChildNode.getName())) {
+ continue;
+ }
+
+ switch (targetChildNode.getName().toLowerCase()) {
+ case PROPERTIES:
+ case SCRIPTS:
+ case APPENDERS: {
+ for (Node node : sourceChildNode.getChildren()) {
+ for (Node targetNode : targetChildNode.getChildren()) {
+ if (targetNode.getAttributes().get(NAME).equals(node.getAttributes().get(NAME))) {
+ targetChildNode.getChildren().remove(targetNode);
+ break;
+ }
+ }
+ targetChildNode.getChildren().add(node);
+ }
+ isMerged = true;
+ break;
+ }
+ case LOGGERS: {
+ Map<String, Node> targetLoggers = new HashMap<>();
+ for (Node node : targetChildNode.getChildren()) {
+ targetLoggers.put(node.getName(), node);
+ }
+ for (Node node : sourceChildNode.getChildren()) {
+ Node targetNode = getLoggerNode(targetChildNode, node.getAttributes().get(NAME));
+ Node loggerNode = new Node(targetChildNode, node.getName(), node.getType());
+ if (targetNode != null) {
+ for (Node sourceLoggerChild : node.getChildren()) {
+ if (isFilterNode(sourceLoggerChild)) {
+ boolean foundFilter = false;
+ for (Node targetChild : targetNode.getChildren()) {
+ if (isFilterNode(targetChild)) {
+ updateFilterNode(loggerNode, targetChild, sourceLoggerChild,
+ pluginManager);
+ foundFilter = true;
+ break;
+ }
+ }
+ if (!foundFilter) {
+ Node childNode = new Node(loggerNode, sourceLoggerChild.getName(),
+ sourceLoggerChild.getType());
+ targetNode.getChildren().add(childNode);
+ }
+ } else {
+ Node childNode = new Node(loggerNode, sourceLoggerChild.getName(),
+ sourceLoggerChild.getType());
+ childNode.getAttributes().putAll(sourceLoggerChild.getAttributes());
+ if (childNode.getName().equalsIgnoreCase("AppenderRef")) {
+ for (Node targetChild : targetNode.getChildren()) {
+ if (isSameReference(targetChild, childNode)) {
+ targetNode.getChildren().remove(targetChild);
+ break;
+ }
+ }
+ } else {
+ for (Node targetChild : targetNode.getChildren()) {
+ if (isSameName(targetChild, childNode)) {
+ targetNode.getChildren().remove(targetChild);
+ break;
+ }
+ }
+ }
+
+ targetNode.getChildren().add(childNode);
+ }
+ }
+ } else {
+ loggerNode.getAttributes().putAll(node.getAttributes());
+ loggerNode.getChildren().addAll(node.getChildren());
+ targetChildNode.getChildren().add(loggerNode);
+ }
+ }
+ isMerged = true;
+ break;
+ }
+ default: {
+ targetChildNode.getChildren().addAll(sourceChildNode.getChildren());
+ isMerged = true;
+ break;
+ }
+
+ }
+ }
+ if (!isMerged) {
+ target.getChildren().add(sourceChildNode);
+ }
+ }
+ }
+
+ private Node getLoggerNode(Node parentNode, String name) {
+ for (Node node : parentNode.getChildren()) {
+ String nodeName = node.getAttributes().get(NAME);
+ if (name == null && nodeName == null) {
+ return node;
+ }
+ if (nodeName != null && nodeName.equals(name)) {
+ return node;
+ }
+ }
+ return null;
+ }
+
+ private void updateFilterNode(Node target, Node targetChildNode, Node sourceChildNode,
+ PluginManager pluginManager) {
+ if (CompositeFilter.class.isAssignableFrom(targetChildNode.getType().getPluginClass())) {
+ Node node = new Node(targetChildNode, sourceChildNode.getName(), sourceChildNode.getType());
+ node.getChildren().addAll(sourceChildNode.getChildren());
+ node.getAttributes().putAll(sourceChildNode.getAttributes());
+ targetChildNode.getChildren().add(node);
+ } else {
+ PluginType pluginType = pluginManager.getPluginType(FILTERS);
+ Node filtersNode = new Node(targetChildNode, FILTERS, pluginType);
+ Node node = new Node(filtersNode, sourceChildNode.getName(), sourceChildNode.getType());
+ node.getAttributes().putAll(sourceChildNode.getAttributes());
+ List<Node> children = filtersNode.getChildren();
+ children.add(targetChildNode);
+ children.add(node);
+ List<Node> nodes = target.getChildren();
+ nodes.remove(targetChildNode);
+ nodes.add(filtersNode);
+ }
+ }
+
+ private boolean isFilterNode(Node node) {
+ return Filter.class.isAssignableFrom(node.getType().getPluginClass());
+ }
+
+ private boolean isSameName(Node node1, Node node2) {
+ return node1.getAttributes().get(NAME).toLowerCase().equals(node2.getAttributes().get(NAME).toLowerCase());
+ }
+
+ private boolean isSameReference(Node node1, Node node2) {
+ return node1.getAttributes().get(REF).toLowerCase().equals(node2.getAttributes().get(REF).toLowerCase());
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/MergeStrategy.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/MergeStrategy.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/MergeStrategy.java
new file mode 100644
index 0000000..130a6f0
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/MergeStrategy.java
@@ -0,0 +1,33 @@
+/*
+ * 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.composite;
+
+import org.apache.logging.log4j.core.config.Node;
+import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
+
+/**
+ * Merges two configurations together
+ */
+public interface MergeStrategy {
+
+ /**
+ * Merge the soure node tree into the target node tree.
+ * @param target The target Node tree.
+ * @param source The source Node tree.
+ */
+ void mergConfigurations(Node target, Node source, PluginManager pluginManager);
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/package-info.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/package-info.java
new file mode 100644
index 0000000..fd920fa
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Support for composite configurations.
+ */
+package org.apache.logging.log4j.core.config.composite;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java
index c65a64f..b504838 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jContextFactory.java
@@ -17,10 +17,14 @@
package org.apache.logging.log4j.core.impl;
import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
import org.apache.logging.log4j.core.LifeCycle;
import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.AbstractConfiguration;
+import org.apache.logging.log4j.core.config.composite.CompositeConfiguration;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.ConfigurationFactory;
import org.apache.logging.log4j.core.config.ConfigurationSource;
@@ -241,6 +245,43 @@ public class Log4jContextFactory implements LoggerContextFactory, ShutdownCallba
return ctx;
}
+ public LoggerContext getContext(final String fqcn, final ClassLoader loader, final Object externalContext,
+ final boolean currentContext, final List<URI> configLocations, final String name) {
+ final LoggerContext ctx = selector
+ .getContext(fqcn, loader, currentContext, null/*this probably needs to change*/);
+ if (externalContext != null && ctx.getExternalContext() == null) {
+ ctx.setExternalContext(externalContext);
+ }
+ if (name != null) {
+ ctx.setName(name);
+ }
+ if (ctx.getState() == LifeCycle.State.INITIALIZED) {
+ if ((configLocations != null && !configLocations.isEmpty())) {
+ ContextAnchor.THREAD_CONTEXT.set(ctx);
+ List<AbstractConfiguration> configurations = new ArrayList<>(configLocations.size());
+ for (URI configLocation : configLocations) {
+ Configuration currentReadConfiguration = ConfigurationFactory.getInstance()
+ .getConfiguration(name, configLocation);
+ if (currentReadConfiguration instanceof AbstractConfiguration) {
+ configurations.add((AbstractConfiguration) currentReadConfiguration);
+ } else {
+ LOGGER.error(
+ "Found configuration {}, which is not an AbstractConfiguration and can't be handled by CompositeConfiguration",
+ configLocation);
+ }
+ }
+ CompositeConfiguration compositeConfiguration = new CompositeConfiguration(configurations);
+ LOGGER.debug("Starting LoggerContext[name={}] from configurations at {}", ctx.getName(),
+ configLocations);
+ ctx.start(compositeConfiguration);
+ ContextAnchor.THREAD_CONTEXT.remove();
+ } else {
+ ctx.start();
+ }
+ }
+ return ctx;
+ }
+
/**
* Returns the ContextSelector.
* @return The ContextSelector.
@@ -251,7 +292,7 @@ public class Log4jContextFactory implements LoggerContextFactory, ShutdownCallba
/**
* Returns the ShutdownCallbackRegistry
- *
+ *
* @return the ShutdownCallbackRegistry
* @since 2.4
*/
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java
index 3750462..146bff7 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java
@@ -17,6 +17,7 @@
package org.apache.logging.log4j.core.util;
import java.io.File;
+import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -78,6 +79,14 @@ public class WatchManager extends AbstractLifeCycle {
}
+ public Map<File, FileWatcher> getWatchers() {
+ Map<File, FileWatcher> map = new HashMap<>();
+ for (Map.Entry<File, FileMonitor> entry : watchers.entrySet()) {
+ map.put(entry.getKey(), entry.getValue().fileWatcher);
+ }
+ return map;
+ }
+
private class WatchWorker implements Runnable {
@Override
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/test/java/org/apache/logging/log4j/core/config/CompositeConfigurationTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/CompositeConfigurationTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/CompositeConfigurationTest.java
new file mode 100644
index 0000000..f17c941
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/CompositeConfigurationTest.java
@@ -0,0 +1,149 @@
+package org.apache.logging.log4j.core.config;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Map;
+
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.apache.logging.log4j.core.appender.FileAppender;
+import org.apache.logging.log4j.core.appender.RollingFileAppender;
+import org.apache.logging.log4j.core.config.composite.CompositeConfiguration;
+import org.apache.logging.log4j.core.filter.CompositeFilter;
+import org.apache.logging.log4j.core.filter.ThresholdFilter;
+import org.apache.logging.log4j.junit.LoggerContextRule;
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+public class CompositeConfigurationTest
+{
+
+ @Test
+ public void compositeConfigurationUsed()
+ {
+ final LoggerContextRule lcr =
+ new LoggerContextRule( "classpath:log4j-comp-appender.xml,log4j-comp-appender.json" );
+ Statement test = new Statement()
+ {
+ @Override
+ public void evaluate()
+ throws Throwable
+ {
+ assertTrue( lcr.getConfiguration() instanceof CompositeConfiguration);
+ }
+ };
+ runTest( lcr, test );
+ }
+
+ @Test
+ public void compositeProperties()
+ {
+ final LoggerContextRule lcr =
+ new LoggerContextRule( "classpath:log4j-comp-properties.xml,log4j-comp-properties.json" );
+ Statement test = new Statement()
+ {
+ @Override
+ public void evaluate()
+ throws Throwable
+ {
+ CompositeConfiguration config = (CompositeConfiguration) lcr.getConfiguration();
+ assertEquals( "json", config.getStrSubstitutor().replace( "${propertyShared}" ) );
+ assertEquals( "xml", config.getStrSubstitutor().replace( "${propertyXml}" ) );
+ assertEquals( "json", config.getStrSubstitutor().replace( "${propertyJson}" ) );
+ }
+ };
+ runTest( lcr, test );
+ }
+
+ @Test
+ public void compositeAppenders()
+ {
+ final LoggerContextRule lcr =
+ new LoggerContextRule( "classpath:log4j-comp-appender.xml,log4j-comp-appender.json" );
+ Statement test = new Statement()
+ {
+ @Override
+ public void evaluate()
+ throws Throwable
+ {
+ CompositeConfiguration config = (CompositeConfiguration) lcr.getConfiguration();
+ Map<String, Appender> appender = config.getAppenders();
+ assertEquals( 3, appender.size() );
+ assertTrue( appender.get( "STDOUT" ) instanceof ConsoleAppender );
+ assertTrue( appender.get( "File" ) instanceof FileAppender );
+ assertTrue( appender.get( "Override" ) instanceof RollingFileAppender );
+ }
+ };
+ runTest( lcr, test );
+ }
+
+ @Test
+ public void compositeLogger()
+ {
+ final LoggerContextRule lcr = new LoggerContextRule( "classpath:log4j-comp-logger.xml,log4j-comp-logger.json" );
+ Statement test = new Statement()
+ {
+ @Override
+ public void evaluate()
+ throws Throwable
+ {
+ CompositeConfiguration config = (CompositeConfiguration) lcr.getConfiguration();
+ Map<String, Appender> appendersMap = config.getLogger( "cat1" ).getAppenders();
+ assertEquals("Expected 2 Appender references for cat1 but got " + appendersMap.size(), 2,
+ appendersMap.size() );
+ assertTrue( appendersMap.get( "STDOUT" ) instanceof ConsoleAppender );
+
+ appendersMap = config.getLogger( "cat2" ).getAppenders();
+ assertEquals("Expected 1 Appender reference for cat2 but got " + appendersMap.size(), 1,
+ appendersMap.size() );
+ assertTrue( appendersMap.get( "File" ) instanceof FileAppender );
+
+ appendersMap = config.getLogger( "cat3" ).getAppenders();
+ assertEquals("Expected 1 Appender reference for cat3 but got " + appendersMap.size(), 1,
+ appendersMap.size() );
+ assertTrue( appendersMap.get( "File" ) instanceof FileAppender );
+
+ appendersMap = config.getRootLogger().getAppenders();
+ assertEquals("Expected 2 Appender references for the root logger but got " + appendersMap.size(), 2,
+ appendersMap.size() );
+ assertTrue( appendersMap.get( "File" ) instanceof FileAppender );
+ assertTrue( appendersMap.get( "STDOUT" ) instanceof ConsoleAppender );
+ }
+ };
+ runTest( lcr, test );
+ }
+
+ @Test
+ public void overrideFilter()
+ {
+ final LoggerContextRule lcr = new LoggerContextRule( "classpath:log4j-comp-filter.xml,log4j-comp-filter.json" );
+ Statement test = new Statement()
+ {
+ @Override
+ public void evaluate()
+ throws Throwable
+ {
+ CompositeConfiguration config = (CompositeConfiguration) lcr.getConfiguration();
+ assertTrue( config.getFilter() instanceof CompositeFilter);
+ CompositeFilter filter = (CompositeFilter) config.getFilter();
+ assertTrue( filter.getFiltersArray().length == 2);
+ }
+ };
+ runTest( lcr, test );
+ }
+
+ private void runTest( LoggerContextRule rule, Statement statement )
+ {
+ try
+ {
+ rule.apply( statement, Description.createTestDescription( getClass(),
+ Thread.currentThread().getStackTrace()[1].getMethodName() ) ).evaluate();
+ }
+ catch ( Throwable throwable )
+ {
+ throw new RuntimeException( throwable );
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/test/resources/log4j-comp-appender.json
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/log4j-comp-appender.json b/log4j-core/src/test/resources/log4j-comp-appender.json
new file mode 100644
index 0000000..1f0b02d
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j-comp-appender.json
@@ -0,0 +1,34 @@
+{
+ "Configuration" : {
+ "status": "warn",
+ "name": "YAMLConfigTest",
+ "appenders": {
+ "Console": {
+ "name": "STDOUT",
+ "PatternLayout": {
+ "Pattern": "%m%n"
+ }
+ },
+ "RollingFile": {
+ "name": "Override",
+ "fileName": "target/log4j-appender-override.log",
+ "filePattern": "target/log4j-appender-override-$${date:yyyy-MM}.log",
+ "PatternLayout": {
+ "Pattern": "%m%n"
+ },
+ "SizeBasedTriggeringPolicy": {
+ "size": "500"
+ }
+ }
+ },
+ "Loggers" : {
+ "Root" : {
+ "level" : "error",
+ "AppenderRef" : {
+ "ref" : "STDOUT"
+ }
+
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/test/resources/log4j-comp-appender.xml
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/log4j-comp-appender.xml b/log4j-core/src/test/resources/log4j-comp-appender.xml
new file mode 100644
index 0000000..7cd71a2
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j-comp-appender.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<Configuration status="ERROR" name="XMLConfigTest">
+ <Appenders>
+ <File name="File" fileName="target/log4j-comp-appender.log" bufferedIO="false">
+ <PatternLayout>
+ <Pattern>%d %p %C{1.} [%t] %m%n</Pattern>
+ </PatternLayout>
+ </File>
+ <File name="Override" fileName="target/log4j-comp-appender.log" bufferedIO="false">
+ <PatternLayout>
+ <Pattern>%d %p %C{1.} [%t] %m%n</Pattern>
+ </PatternLayout>
+ </File>
+ </Appenders>
+
+ <Loggers>
+ <Root level="ERROR">
+ <AppenderRef ref="File"/>
+ </Root>
+ </Loggers>
+
+</Configuration>
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/test/resources/log4j-comp-filter.json
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/log4j-comp-filter.json b/log4j-core/src/test/resources/log4j-comp-filter.json
new file mode 100644
index 0000000..c010298
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j-comp-filter.json
@@ -0,0 +1,9 @@
+{
+ "Configuration" : {
+ "status": "warn",
+ "name": "YAMLConfigTest",
+ "thresholdFilter" : {
+ "level": "debug"
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/test/resources/log4j-comp-filter.xml
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/log4j-comp-filter.xml b/log4j-core/src/test/resources/log4j-comp-filter.xml
new file mode 100644
index 0000000..553d448
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j-comp-filter.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<Configuration status="ERROR" name="XMLConfigTest">
+ <BurstFilter level="INFO" rate="16" maxBurst="100"/>
+
+ <Appenders>
+ <Console name="STDOUT">
+ <PatternLayout pattern="%m%n"/>
+ </Console>
+ </Appenders>
+
+ <Loggers>
+ <Root level="error">
+ <AppenderRef ref="STDOUT"/>
+ </Root>
+ </Loggers>
+
+</Configuration>
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/test/resources/log4j-comp-logger.json
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/log4j-comp-logger.json b/log4j-core/src/test/resources/log4j-comp-logger.json
new file mode 100644
index 0000000..b2b08d7
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j-comp-logger.json
@@ -0,0 +1,36 @@
+{
+ "Configuration" : {
+ "status": "warn",
+ "name": "LoggerConfigTest",
+ "Loggers" : {
+ "logger" : [
+ {
+ "name" : "cat1",
+ "level" : "debug",
+ "additivity" : false,
+ "AppenderRef" : {
+ "ref" : "STDOUT"
+ }
+ },
+ {
+ "name" : "cat2",
+ "level" : "debug",
+ "additivity" : false,
+ "AppenderRef" : {
+ "ref" : "File"
+ }
+
+ }
+ ],
+ "Root" : {
+ "level" : "error",
+ "AppenderRef" : [{
+ "ref" : "STDOUT"
+ },
+ {
+ "ref" : "File"
+ }]
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/test/resources/log4j-comp-logger.xml
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/log4j-comp-logger.xml b/log4j-core/src/test/resources/log4j-comp-logger.xml
new file mode 100644
index 0000000..0934e94
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j-comp-logger.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 status="ERROR" name="LoggerConfigTest">
+ <Appenders>
+ <Console name="STDOUT">
+ <PatternLayout pattern="%m%n"/>
+ </Console>
+ <File name="File" fileName="${filename}" bufferedIO="false">
+ <PatternLayout>
+ <Pattern>%d %p %C{1.} [%t] %m%n</Pattern>
+ </PatternLayout>
+ </File>
+ </Appenders>
+
+ <Loggers>
+ <Logger name="cat1" level="debug" additivity="false">
+ <AppenderRef ref="File"/>
+ </Logger>
+
+ <Logger name="cat3" level="debug" additivity="false">
+ <AppenderRef ref="File"/>
+ </Logger>
+ </Loggers>
+
+</Configuration>
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/test/resources/log4j-comp-properties.json
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/log4j-comp-properties.json b/log4j-core/src/test/resources/log4j-comp-properties.json
new file mode 100644
index 0000000..83c2d09
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j-comp-properties.json
@@ -0,0 +1,16 @@
+{
+ "Configuration" : {
+ "status": "warn",
+ "name": "YAMLConfigTest",
+
+ "properties" : {
+ "property" : [{
+ "name" : "propertyShared",
+ "value": "json"
+ },{
+ "name" : "propertyJson",
+ "value": "json"
+ }]
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bb64f457/log4j-core/src/test/resources/log4j-comp-properties.xml
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/log4j-comp-properties.xml b/log4j-core/src/test/resources/log4j-comp-properties.xml
new file mode 100644
index 0000000..6fa9c6b
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j-comp-properties.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<Configuration status="OFF" name="XMLConfigTest">
+ <Properties>
+ <Property name="propertyShared">xml</Property>
+ <Property name="propertyXml">xml</Property>
+ </Properties>
+ <Appenders>
+ <Console name="STDOUT">
+ <PatternLayout pattern="%m%n"/>
+ </Console>
+ </Appenders>
+ <Loggers>
+ <Root level="error">
+ <AppenderRef ref="STDOUT"/>
+ </Root>
+ </Loggers>
+</Configuration>