Merge branch 'master' of https://github.com/apache/logging-log4j2 into LOG4J2-2707
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderWrapper.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderWrapper.java
index f06f909..eb39fe7 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderWrapper.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderWrapper.java
@@ -18,16 +18,26 @@
import org.apache.log4j.Appender;
import org.apache.log4j.Layout;
+import org.apache.log4j.helpers.AppenderAttachableImpl;
+import org.apache.log4j.spi.AppenderAttachable;
import org.apache.log4j.spi.ErrorHandler;
import org.apache.log4j.spi.Filter;
import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.filter.AbstractFilterable;
+import org.apache.logging.log4j.status.StatusLogger;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
/**
* Holds a Log4j 2 Appender in an empty Log4j 1 Appender so it can be extracted when constructing the configuration.
* Allows a Log4j 1 Appender to reference a Log4j 2 Appender.
*/
-public class AppenderWrapper implements Appender {
+public class AppenderWrapper extends AppenderAttachableImpl implements Appender {
+ private static final Logger LOGGER = StatusLogger.getLogger();
private final org.apache.logging.log4j.core.Appender appender;
public AppenderWrapper(org.apache.logging.log4j.core.Appender appender) {
@@ -40,6 +50,15 @@
@Override
public void addFilter(Filter newFilter) {
+ if (appender instanceof AbstractFilterable) {
+ if (newFilter instanceof FilterWrapper) {
+ ((AbstractFilterable) appender).addFilter(((FilterWrapper) newFilter).getFilter());
+ } else {
+ ((AbstractFilterable) appender).addFilter(new FilterAdapter(newFilter));
+ }
+ } else {
+ LOGGER.warn("Unable to add filter to appender {}, it does not support filters", appender.getName());
+ }
}
@Override
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/AbstractBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/AbstractBuilder.java
new file mode 100644
index 0000000..0448ed2
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/AbstractBuilder.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.log4j.builders;
+
+import org.apache.log4j.bridge.FilterAdapter;
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.Filter;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.filter.CompositeFilter;
+import org.apache.logging.log4j.core.filter.ThresholdFilter;
+import org.apache.logging.log4j.status.StatusLogger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * Base class for Log4j 1 component builders.
+ */
+public abstract class AbstractBuilder {
+
+ private static Logger LOGGER = StatusLogger.getLogger();
+ protected static final String FILE_PARAM = "File";
+ protected static final String APPEND_PARAM = "Append";
+ protected static final String BUFFERED_IO_PARAM = "BufferedIO";
+ protected static final String BUFFER_SIZE_PARAM = "BufferSize";
+ protected static final String MAX_SIZE_PARAM = "MaxFileSize";
+ protected static final String MAX_BACKUP_INDEX = "MaxBackupIndex";
+ protected static final String RELATIVE = "RELATIVE";
+
+ private final String prefix;
+ private final Properties props;
+
+ public AbstractBuilder() {
+ this.prefix = null;
+ this.props = new Properties();
+ }
+
+ public AbstractBuilder(String prefix, Properties props) {
+ this.prefix = prefix + ".";
+ this.props = props;
+ }
+
+ public String getProperty(String key) {
+ return props.getProperty(prefix + key);
+ }
+
+ public String getProperty(String key, String defaultValue) {
+ return props.getProperty(prefix + key, defaultValue);
+ }
+
+ public boolean getBooleanProperty(String key) {
+ return Boolean.parseBoolean(props.getProperty(prefix + key, Boolean.FALSE.toString()));
+ }
+
+ public int getIntegerProperty(String key, int defaultValue) {
+ String value = props.getProperty(key);
+ try {
+ if (value != null) {
+ return Integer.parseInt(value);
+ }
+ } catch (Exception ex) {
+ LOGGER.warn("Error converting value {} of {} to an integer: {}", value, key, ex.getMessage());
+ }
+ return defaultValue;
+ }
+
+ public Properties getProperties() {
+ return props;
+ }
+
+
+ protected org.apache.logging.log4j.core.Filter buildFilters(String level, Filter filter) {
+ if (level != null && filter != null) {
+ List<org.apache.logging.log4j.core.Filter> filterList = new ArrayList<>();
+ org.apache.logging.log4j.core.Filter thresholdFilter =
+ ThresholdFilter.createFilter(OptionConverter.convertLevel(level, Level.TRACE),
+ org.apache.logging.log4j.core.Filter.Result.NEUTRAL,
+ org.apache.logging.log4j.core.Filter.Result.DENY);
+ filterList.add(thresholdFilter);
+ Filter f = filter;
+ while (f != null) {
+ if (filter instanceof FilterWrapper) {
+ filterList.add(((FilterWrapper) f).getFilter());
+ } else {
+ filterList.add(new FilterAdapter(f));
+ }
+ f = f.next;
+ }
+ return CompositeFilter.createFilters(filterList.toArray(new org.apache.logging.log4j.core.Filter[0]));
+ } else if (level != null) {
+ return ThresholdFilter.createFilter(OptionConverter.convertLevel(level, Level.TRACE),
+ org.apache.logging.log4j.core.Filter.Result.NEUTRAL,
+ org.apache.logging.log4j.core.Filter.Result.DENY);
+ } else if (filter != null) {
+ if (filter instanceof FilterWrapper) {
+ return ((FilterWrapper) filter).getFilter();
+ } else {
+ return new FilterAdapter(filter);
+ }
+ }
+ return null;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BuilderManager.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BuilderManager.java
index 9a6b7b8..d6465f0 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BuilderManager.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BuilderManager.java
@@ -21,8 +21,9 @@
import org.apache.log4j.builders.appender.AppenderBuilder;
import org.apache.log4j.builders.filter.FilterBuilder;
import org.apache.log4j.builders.layout.LayoutBuilder;
+import org.apache.log4j.config.PropertiesConfiguration;
import org.apache.log4j.spi.Filter;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.xml.XmlConfiguration;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.plugins.util.PluginManager;
import org.apache.logging.log4j.plugins.util.PluginType;
@@ -30,8 +31,10 @@
import org.apache.logging.log4j.util.LoaderUtil;
import org.w3c.dom.Element;
+import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
+import java.util.Properties;
/**
*
@@ -41,6 +44,7 @@
public static final String CATEGORY = "Log4j Builder";
private static final Logger LOGGER = StatusLogger.getLogger();
private final Map<String, PluginType<?>> plugins;
+ private static Class<?>[] constructorParams = new Class[] { String.class, Properties.class};
public BuilderManager() {
final PluginManager manager = new PluginManager(CATEGORY);
@@ -48,13 +52,13 @@
plugins = manager.getPlugins();
}
- public Appender parseAppender(String className, Element appenderElement, XmlConfigurationFactory factory) {
+ public Appender parseAppender(String className, Element appenderElement, XmlConfiguration config) {
PluginType<?> plugin = plugins.get(className.toLowerCase());
if (plugin != null) {
try {
@SuppressWarnings("unchecked")
AppenderBuilder builder = (AppenderBuilder) LoaderUtil.newInstanceOf(plugin.getPluginClass());
- return builder.parseAppender(appenderElement, factory);
+ return builder.parseAppender(appenderElement, config);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
}
@@ -62,13 +66,25 @@
return null;
}
- public Filter parseFilter(String className, Element filterElement, XmlConfigurationFactory factory) {
+ public Appender parseAppender(String name, String className, String prefix, String layoutPrefix,
+ String filterPrefix, Properties props, PropertiesConfiguration config) {
+ PluginType<?> plugin = plugins.get(className.toLowerCase());
+ if (plugin != null) {
+ AppenderBuilder builder = createBuilder(plugin, prefix, props);
+ if (builder != null) {
+ return builder.parseAppender(name, layoutPrefix, filterPrefix, props, config);
+ }
+ }
+ return null;
+ }
+
+ public Filter parseFilter(String className, Element filterElement, XmlConfiguration config) {
PluginType<?> plugin = plugins.get(className.toLowerCase());
if (plugin != null) {
try {
@SuppressWarnings("unchecked")
FilterBuilder builder = (FilterBuilder) LoaderUtil.newInstanceOf(plugin.getPluginClass());
- return builder.parseFilter(filterElement, factory);
+ return builder.parseFilter(filterElement, config);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
}
@@ -76,19 +92,58 @@
return null;
}
+ public Filter parseFilter(String className, String filterPrefix, Properties props, PropertiesConfiguration config) {
+ PluginType<?> plugin = plugins.get(className.toLowerCase());
+ if (plugin != null) {
+ FilterBuilder builder = createBuilder(plugin, filterPrefix, props);
+ if (builder != null) {
+ return builder.parseFilter(config);
+ }
+ }
+ return null;
+ }
- public Layout parseLayout(String className, Element layoutElement, XmlConfigurationFactory factory) {
+ public Layout parseLayout(String className, Element layoutElement, XmlConfiguration config) {
PluginType<?> plugin = plugins.get(className.toLowerCase());
if (plugin != null) {
try {
@SuppressWarnings("unchecked")
LayoutBuilder builder = (LayoutBuilder) LoaderUtil.newInstanceOf(plugin.getPluginClass());
- return builder.parseLayout(layoutElement, factory);
+ return builder.parseLayout(layoutElement, config);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
}
}
return null;
}
+ public Layout parseLayout(String className, String layoutPrefix, Properties props, PropertiesConfiguration config) {
+ PluginType<?> plugin = plugins.get(className.toLowerCase());
+ if (plugin != null) {
+ LayoutBuilder builder = createBuilder(plugin, layoutPrefix, props);
+ if (builder != null) {
+ return builder.parseLayout(config);
+ }
+ }
+ return null;
+ }
+
+ private <T extends AbstractBuilder> T createBuilder(PluginType<?> plugin, String prefix, Properties props) {
+ try {
+ Class<?> clazz = plugin.getPluginClass();
+ if (AbstractBuilder.class.isAssignableFrom(clazz)) {
+ @SuppressWarnings("unchecked")
+ Constructor<T> constructor =
+ (Constructor<T>) clazz.getConstructor(constructorParams);
+ return constructor.newInstance(prefix, props);
+ } else {
+ @SuppressWarnings("unchecked")
+ T builder = (T) LoaderUtil.newInstanceOf(clazz);
+ return builder;
+ }
+ } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException ex) {
+ LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
+ return null;
+ }
+ }
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AppenderBuilder.java
index 2b72efd..0572b86 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AppenderBuilder.java
@@ -17,17 +17,20 @@
package org.apache.log4j.builders.appender;
import org.apache.log4j.Appender;
-import org.apache.log4j.xml.XmlConfigurationFactory;
-import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
import org.w3c.dom.Element;
-import java.util.function.BiFunction;
+import java.util.Properties;
/**
* Define an Appender Builder.
*/
public interface AppenderBuilder {
- Appender parseAppender(Element element, XmlConfigurationFactory factory);
+ Appender parseAppender(Element element, XmlConfiguration configuration);
+
+ Appender parseAppender(String name, String layoutPrefix, String filterPrefix, Properties props,
+ PropertiesConfiguration configuration);
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AsyncAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AsyncAppenderBuilder.java
new file mode 100644
index 0000000..86969ed
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AsyncAppenderBuilder.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.log4j.builders.appender;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.builders.BooleanHolder;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.AsyncAppender;
+import org.apache.logging.log4j.core.config.AppenderRef;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfiguration.NAME_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+import static org.apache.log4j.config.Log4j1Configuration.APPENDER_REF_TAG;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+
+
+/**
+ * Build an Asynch Appender
+ */
+@Plugin(name = "org.apache.log4j.AsyncAppender", category = CATEGORY)
+public class AsyncAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ private static final String BLOCKING_PARAM = "Blocking";
+ private static final String INCLUDE_LOCATION_PARAM = "IncludeLocation";
+
+ public AsyncAppenderBuilder() {
+ }
+
+ public AsyncAppenderBuilder(String prefix, Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+ String name = appenderElement.getAttribute(NAME_ATTR);
+ Holder<List<String>> appenderRefs = new Holder<>(new ArrayList<>());
+ Holder<Boolean> blocking = new BooleanHolder();
+ Holder<Boolean> includeLocation = new BooleanHolder();
+ Holder<String> level = new Holder<>("trace");
+ Holder<Integer> bufferSize = new Holder<>(1024);
+ forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
+ switch (currentElement.getTagName()) {
+ case APPENDER_REF_TAG:
+ Appender appender = config.findAppenderByReference(currentElement);
+ if (appender != null) {
+ appenderRefs.get().add(appender.getName());
+ }
+ break;
+ case PARAM_TAG: {
+ switch (currentElement.getAttribute(NAME_ATTR)) {
+ case BUFFER_SIZE_PARAM: {
+ String value = currentElement.getAttribute(VALUE_ATTR);
+ if (value == null) {
+ LOGGER.warn("No value supplied for BufferSize parameter. Defaulting to 1024.");
+ } else {
+ bufferSize.set(Integer.parseInt(value));
+ }
+ break;
+ }
+ case BLOCKING_PARAM: {
+ String value = currentElement.getAttribute(VALUE_ATTR);
+ if (value == null) {
+ LOGGER.warn("No value supplied for Blocking parameter. Defaulting to false.");
+ } else {
+ blocking.set(Boolean.parseBoolean(value));
+ }
+ break;
+ }
+ case INCLUDE_LOCATION_PARAM: {
+ String value = currentElement.getAttribute(VALUE_ATTR);
+ if (value == null) {
+ LOGGER.warn("No value supplied for IncludeLocation parameter. Defaulting to false.");
+ } else {
+ includeLocation.set(Boolean.parseBoolean(value));
+ }
+ break;
+ }
+ case THRESHOLD_PARAM: {
+ String value = currentElement.getAttribute(VALUE_ATTR);
+ if (value == null) {
+ LOGGER.warn("No value supplied for Threshold parameter, ignoring.");
+ } else {
+ level.set(value);
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+ });
+ return createAppender(name, level.get(), appenderRefs.get().toArray(new String[0]), blocking.get(),
+ bufferSize.get(), includeLocation.get(), config);
+ }
+
+ @Override
+ public Appender parseAppender(final String name, final String layoutPrefix,
+ final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
+ String appenderRef = getProperty(APPENDER_REF_TAG);
+ boolean blocking = getBooleanProperty(BLOCKING_PARAM);
+ boolean includeLocation = getBooleanProperty(INCLUDE_LOCATION_PARAM);
+ String level = getProperty(THRESHOLD_PARAM);
+ int bufferSize = getIntegerProperty(BUFFER_SIZE_PARAM, 1024);
+ if (appenderRef == null) {
+ LOGGER.warn("No appender references configured for AsyncAppender {}", name);
+ return null;
+ }
+ Appender appender = configuration.parseAppender(props, appenderRef);
+ if (appender == null) {
+ LOGGER.warn("Cannot locate Appender {}", appenderRef);
+ return null;
+ }
+ return createAppender(name, level, new String[] {appenderRef}, blocking, bufferSize, includeLocation,
+ configuration);
+ }
+
+ private <T extends Log4j1Configuration> Appender createAppender(String name, String level,
+ String[] appenderRefs, boolean blocking, int bufferSize, boolean includeLocation,
+ T configuration) {
+ org.apache.logging.log4j.Level logLevel = OptionConverter.convertLevel(level,
+ org.apache.logging.log4j.Level.TRACE);
+ AppenderRef[] refs = new AppenderRef[appenderRefs.length];
+ int index = 0;
+ for (String appenderRef : appenderRefs) {
+ refs[index++] = AppenderRef.createAppenderRef(appenderRef, logLevel, null);
+ }
+ return new AppenderWrapper(AsyncAppender.newBuilder()
+ .setName(name)
+ .setAppenderRefs(refs)
+ .setBlocking(blocking)
+ .setBufferSize(bufferSize)
+ .setIncludeLocation(includeLocation)
+ .setConfiguration(configuration)
+ .build());
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.java
index d9b7905..423fab7 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.java
@@ -19,93 +19,143 @@
import org.apache.log4j.Appender;
import org.apache.log4j.Layout;
import org.apache.log4j.bridge.AppenderWrapper;
-import org.apache.log4j.bridge.FilterAdapter;
-import org.apache.log4j.bridge.FilterWrapper;
import org.apache.log4j.bridge.LayoutAdapter;
import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
import org.apache.log4j.builders.Holder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
import org.apache.log4j.spi.Filter;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.xml.XmlConfiguration;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.apache.logging.log4j.plugins.Plugin;
import org.apache.logging.log4j.status.StatusLogger;
import org.w3c.dom.Element;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
import static org.apache.log4j.builders.BuilderManager.CATEGORY;
-import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.LAYOUT_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.NAME_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
/**
* Build a Console Appender
*/
@Plugin(name = "org.apache.log4j.ConsoleAppender", category = CATEGORY)
-public class ConsoleAppenderBuilder implements AppenderBuilder {
+public class ConsoleAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
private static final String SYSTEM_OUT = "System.out";
private static final String SYSTEM_ERR = "System.err";
private static final String TARGET = "target";
private static final Logger LOGGER = StatusLogger.getLogger();
+ public ConsoleAppenderBuilder() {
+ }
+
+ public ConsoleAppenderBuilder(String prefix, Properties props) {
+ super(prefix, props);
+ }
+
@Override
- public Appender parseAppender(Element appenderElement, XmlConfigurationFactory factory) {
- String name = appenderElement.getAttribute(XmlConfigurationFactory.NAME_ATTR);
+ public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+ String name = appenderElement.getAttribute(NAME_ATTR);
Holder<String> target = new Holder<>(SYSTEM_OUT);
Holder<Layout> layout = new Holder<>();
- Holder<Filter> filter = new Holder<>();
+ Holder<List<Filter>> filters = new Holder<>(new ArrayList<>());
+ Holder<String> level = new Holder<>();
forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
switch (currentElement.getTagName()) {
case LAYOUT_TAG:
- layout.set(factory.parseLayout(currentElement));
+ layout.set(config.parseLayout(currentElement));
break;
case FILTER_TAG:
- filter.set(factory.parseFilters(currentElement));
+ filters.get().add(config.parseFilters(currentElement));
break;
case PARAM_TAG: {
- if (currentElement.getAttribute(NAME_ATTR).equalsIgnoreCase(TARGET)) {
- String value = currentElement.getAttribute(VALUE_ATTR);
- if (value == null) {
- LOGGER.warn("No value supplied for target parameter. Defaulting to System.out.");
- } else {
- switch (value) {
- case SYSTEM_OUT:
- target.set(SYSTEM_OUT);
- break;
- case SYSTEM_ERR:
- target.set(SYSTEM_ERR);
- break;
- default:
- LOGGER.warn("Invalid value \"{}\" for target parameter. Using default of System.out",
- value);
+ switch (currentElement.getAttribute(NAME_ATTR)) {
+ case TARGET: {
+ String value = currentElement.getAttribute(VALUE_ATTR);
+ if (value == null) {
+ LOGGER.warn("No value supplied for target parameter. Defaulting to System.out.");
+ } else {
+ switch (value) {
+ case SYSTEM_OUT:
+ target.set(SYSTEM_OUT);
+ break;
+ case SYSTEM_ERR:
+ target.set(SYSTEM_ERR);
+ break;
+ default:
+ LOGGER.warn("Invalid value \"{}\" for target parameter. Using default of System.out",
+ value);
+ }
}
+ break;
+ }
+ case THRESHOLD_PARAM: {
+ String value = currentElement.getAttribute(VALUE_ATTR);
+ if (value == null) {
+ LOGGER.warn("No value supplied for Threshold parameter, ignoring.");
+ } else {
+ level.set(value);
+ }
+ break;
}
}
break;
}
}
});
- org.apache.logging.log4j.core.Layout<?> consoleLayout = null;
- org.apache.logging.log4j.core.Filter consoleFilter = null;
-
- if (layout.get() instanceof LayoutWrapper) {
- consoleLayout = ((LayoutWrapper) layout.get()).getLayout();
- } else if (layout.get() != null) {
- consoleLayout = new LayoutAdapter(layout.get());
- }
- if (filter.get() != null) {
- if (filter.get() instanceof FilterWrapper) {
- consoleFilter = ((FilterWrapper) filter.get()).getFilter();
+ Filter head = null;
+ Filter current = null;
+ for (Filter f : filters.get()) {
+ if (head == null) {
+ head = f;
+ current = f;
} else {
- consoleFilter = new FilterAdapter(filter.get());
+ current.next = f;
+ current = f;
}
}
- ConsoleAppender.Target consoleTarget = SYSTEM_ERR.equals(target.get())
+ return createAppender(name, layout.get(), head, level.get(), target.get(), config);
+ }
+
+ @Override
+ public Appender parseAppender(final String name, final String layoutPrefix,
+ final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
+ Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+ Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+ String level = getProperty(THRESHOLD_PARAM);
+ String target = getProperty(TARGET);
+ return createAppender(name, layout, filter, level, target, configuration);
+ }
+
+ private <T extends Log4j1Configuration> Appender createAppender(String name, Layout layout, Filter filter,
+ String level, String target, T configuration) {
+ org.apache.logging.log4j.core.Layout<?> consoleLayout = null;
+
+ if (layout instanceof LayoutWrapper) {
+ consoleLayout = ((LayoutWrapper) layout).getLayout();
+ } else if (layout != null) {
+ consoleLayout = new LayoutAdapter(layout);
+ }
+ org.apache.logging.log4j.core.Filter consoleFilter = buildFilters(level, filter);
+ ConsoleAppender.Target consoleTarget = SYSTEM_ERR.equals(target)
? ConsoleAppender.Target.SYSTEM_ERR : ConsoleAppender.Target.SYSTEM_OUT;
return new AppenderWrapper(ConsoleAppender.newBuilder()
.setName(name)
.setTarget(consoleTarget)
.setLayout(consoleLayout)
.setFilter(consoleFilter)
- .setConfiguration(factory.getConfiguration())
+ .setConfiguration(configuration)
.build());
}
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java
index e37d385..1dd1885 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java
@@ -19,14 +19,15 @@
import org.apache.log4j.Appender;
import org.apache.log4j.Layout;
import org.apache.log4j.bridge.AppenderWrapper;
-import org.apache.log4j.bridge.FilterAdapter;
-import org.apache.log4j.bridge.FilterWrapper;
import org.apache.log4j.bridge.LayoutAdapter;
import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
import org.apache.log4j.builders.BooleanHolder;
import org.apache.log4j.builders.Holder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
import org.apache.log4j.spi.Filter;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.xml.XmlConfiguration;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.appender.RollingFileAppender;
import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
@@ -37,23 +38,41 @@
import org.apache.logging.log4j.status.StatusLogger;
import org.w3c.dom.Element;
+import java.util.Properties;
+
import static org.apache.log4j.builders.BuilderManager.CATEGORY;
-import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.LAYOUT_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.NAME_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
/**
* Build a Daily Rolling File Appender
*/
@Plugin(name = "org.apache.log4j.DailyRollingFileAppender", category = CATEGORY)
-public class DailyRollingFileAppenderBuilder implements AppenderBuilder {
+public class DailyRollingFileAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
private static final Logger LOGGER = StatusLogger.getLogger();
+ public DailyRollingFileAppenderBuilder() {
+ }
+
+ public DailyRollingFileAppenderBuilder(String prefix, Properties props) {
+ super(prefix, props);
+ }
+
+
@Override
- public Appender parseAppender(Element appenderElement, XmlConfigurationFactory factory) {
+ public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
String name = appenderElement.getAttribute(NAME_ATTR);
Holder<Layout> layout = new Holder<>();
Holder<Filter> filter = new Holder<>();
Holder<String> fileName = new Holder<>();
+ Holder<String> level = new Holder<>();
Holder<Boolean> immediateFlush = new BooleanHolder();
Holder<Boolean> append = new BooleanHolder();
Holder<Boolean> bufferedIo = new BooleanHolder();
@@ -61,13 +80,13 @@
forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
switch (currentElement.getTagName()) {
case LAYOUT_TAG:
- layout.set(factory.parseLayout(currentElement));
+ layout.set(config.parseLayout(currentElement));
break;
case FILTER_TAG:
- filter.set(factory.parseFilters(currentElement));
+ filter.set(config.parseFilters(currentElement));
break;
case PARAM_TAG: {
- switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+ switch (currentElement.getAttribute(NAME_ATTR)) {
case FILE_PARAM:
fileName.set(currentElement.getAttribute(VALUE_ATTR));
break;
@@ -89,7 +108,7 @@
}
break;
}
- case BUFFER_SIZE_PARAM:
+ case BUFFER_SIZE_PARAM: {
String size = currentElement.getAttribute(VALUE_ATTR);
if (size != null) {
bufferSize.set(Integer.parseInt(size));
@@ -97,46 +116,72 @@
LOGGER.warn("No value provide for bufferSize parameter");
}
break;
+ }
+ case THRESHOLD_PARAM: {
+ String value = currentElement.getAttribute(VALUE_ATTR);
+ if (value == null) {
+ LOGGER.warn("No value supplied for Threshold parameter, ignoring.");
+ } else {
+ level.set(value);
+ }
+ break;
+ }
}
break;
}
}
});
+ return createAppender(name, layout.get(), filter.get(), fileName.get(), append.get(), immediateFlush.get(),
+ level.get(), bufferedIo.get(), bufferSize.get(), config);
+ }
+
+ @Override
+ public Appender parseAppender(final String name, final String layoutPrefix, final String filterPrefix,
+ final Properties props, final PropertiesConfiguration configuration) {
+ Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+ Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+ String fileName = getProperty(FILE_PARAM);
+ String level = getProperty(THRESHOLD_PARAM);
+ boolean append = getBooleanProperty(APPEND_PARAM);
+ boolean immediateFlush = false;
+ boolean bufferedIo = getBooleanProperty(BUFFERED_IO_PARAM);
+ int bufferSize = Integer.parseInt(getProperty(BUFFER_SIZE_PARAM, "8192"));
+ return createAppender(name, layout, filter, fileName, append, immediateFlush,
+ level, bufferedIo, bufferSize, configuration);
+ }
+
+ private <T extends Log4j1Configuration> Appender createAppender(final String name, final Layout layout,
+ final Filter filter, final String fileName, final boolean append, boolean immediateFlush,
+ final String level, final boolean bufferedIo, final int bufferSize, final T configuration) {
org.apache.logging.log4j.core.Layout<?> fileLayout = null;
- org.apache.logging.log4j.core.Filter fileFilter = null;
- if (bufferedIo.get()) {
- immediateFlush.set(Boolean.TRUE);
+ if (bufferedIo) {
+ immediateFlush = true;
}
- if (layout.get() instanceof LayoutWrapper) {
- fileLayout = ((LayoutWrapper) layout.get()).getLayout();
- } else if (layout.get() != null) {
- fileLayout = new LayoutAdapter(layout.get());
+ if (layout instanceof LayoutWrapper) {
+ fileLayout = ((LayoutWrapper) layout).getLayout();
+ } else if (layout != null) {
+ fileLayout = new LayoutAdapter(layout);
}
- if (filter.get() != null) {
- if (filter.get() instanceof FilterWrapper) {
- fileFilter = ((FilterWrapper) filter.get()).getFilter();
- } else {
- fileFilter = new FilterAdapter(filter.get());
- }
- }
- if (fileName.get() == null) {
+ org.apache.logging.log4j.core.Filter fileFilter = buildFilters(level, filter);
+ if (fileName == null) {
LOGGER.warn("Unable to create File Appender, no file name provided");
return null;
}
- String filePattern = fileName.get() +"%d{yyy-MM-dd}";
+ String filePattern = fileName +"%d{yyy-MM-dd}";
TriggeringPolicy policy = TimeBasedTriggeringPolicy.newBuilder().setModulate(true).build();
RolloverStrategy strategy = DefaultRolloverStrategy.newBuilder()
- .setConfig(factory.getConfiguration())
+ .setConfig(configuration)
.setMax(Integer.toString(Integer.MAX_VALUE))
.build();
return new AppenderWrapper(RollingFileAppender.newBuilder()
.setName(name)
- .setConfiguration(factory.getConfiguration())
+ .setConfiguration(configuration)
.setLayout(fileLayout)
.setFilter(fileFilter)
- .setFileName(fileName.get())
- .setImmediateFlush(immediateFlush.get())
+ .setFileName(fileName)
+ .setBufferSize(bufferSize)
+ .setImmediateFlush(immediateFlush)
.setFilePattern(filePattern)
.setPolicy(policy)
.setStrategy(strategy)
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java
index 732581e..9d108c2 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java
@@ -19,37 +19,54 @@
import org.apache.log4j.Appender;
import org.apache.log4j.Layout;
import org.apache.log4j.bridge.AppenderWrapper;
-import org.apache.log4j.bridge.FilterAdapter;
-import org.apache.log4j.bridge.FilterWrapper;
import org.apache.log4j.bridge.LayoutAdapter;
import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
import org.apache.log4j.builders.BooleanHolder;
import org.apache.log4j.builders.Holder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
import org.apache.log4j.spi.Filter;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.xml.XmlConfiguration;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.appender.FileAppender;
import org.apache.logging.log4j.plugins.Plugin;
import org.apache.logging.log4j.status.StatusLogger;
import org.w3c.dom.Element;
+import java.util.Properties;
+
import static org.apache.log4j.builders.BuilderManager.CATEGORY;
-import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.LAYOUT_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+import static org.apache.log4j.xml.XmlConfiguration.NAME_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR;
/**
* Build a File Appender
*/
@Plugin(name = "org.apache.log4j.FileAppender", category = CATEGORY)
-public class FileAppenderBuilder implements AppenderBuilder {
+public class FileAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
private static final Logger LOGGER = StatusLogger.getLogger();
+ public FileAppenderBuilder() {
+ }
+
+ public FileAppenderBuilder(String prefix, Properties props) {
+ super(prefix, props);
+ }
+
@Override
- public Appender parseAppender(Element appenderElement, XmlConfigurationFactory factory) {
+ public Appender parseAppender(Element appenderElement, XmlConfiguration config) {
String name = appenderElement.getAttribute(NAME_ATTR);
Holder<Layout> layout = new Holder<>();
Holder<Filter> filter = new Holder<>();
Holder<String> fileName = new Holder<>();
+ Holder<String> level = new Holder<>();
Holder<Boolean> immediateFlush = new BooleanHolder();
Holder<Boolean> append = new BooleanHolder();
Holder<Boolean> bufferedIo = new BooleanHolder();
@@ -57,13 +74,13 @@
forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
switch (currentElement.getTagName()) {
case LAYOUT_TAG:
- layout.set(factory.parseLayout(currentElement));
+ layout.set(config.parseLayout(currentElement));
break;
case FILTER_TAG:
- filter.set(factory.parseFilters(currentElement));
+ filter.set(config.parseFilters(currentElement));
break;
case PARAM_TAG: {
- switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+ switch (currentElement.getAttribute(NAME_ATTR)) {
case FILE_PARAM:
fileName.set(currentElement.getAttribute(VALUE_ATTR));
break;
@@ -85,7 +102,7 @@
}
break;
}
- case BUFFER_SIZE_PARAM:
+ case BUFFER_SIZE_PARAM: {
String size = currentElement.getAttribute(VALUE_ATTR);
if (size != null) {
bufferSize.set(Integer.parseInt(size));
@@ -93,43 +110,69 @@
LOGGER.warn("No value provide for bufferSize parameter");
}
break;
+ }
+ case THRESHOLD_PARAM: {
+ String value = currentElement.getAttribute(VALUE_ATTR);
+ if (value == null) {
+ LOGGER.warn("No value supplied for Threshold parameter, ignoring.");
+ } else {
+ level.set(value);
+ }
+ break;
+ }
}
break;
}
}
});
+ return createAppender(name, config, layout.get(), filter.get(), fileName.get(), level.get(),
+ immediateFlush.get(), append.get(), bufferedIo.get(), bufferSize.get());
+ }
+
+
+ @Override
+ public Appender parseAppender(final String name, final String layoutPrefix, final String filterPrefix,
+ final Properties props, final PropertiesConfiguration configuration) {
+ Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+ Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+ String level = getProperty(THRESHOLD_PARAM);
+ String fileName = getProperty(FILE_PARAM);
+ boolean append = getBooleanProperty(APPEND_PARAM);
+ boolean immediateFlush = false;
+ boolean bufferedIo = getBooleanProperty(BUFFERED_IO_PARAM);
+ int bufferSize = Integer.parseInt(getProperty(BUFFER_SIZE_PARAM, "8192"));
+ return createAppender(name, configuration, layout, filter, fileName, level, immediateFlush,
+ append, bufferedIo, bufferSize);
+ }
+
+ private Appender createAppender(final String name, final Log4j1Configuration configuration, final Layout layout,
+ final Filter filter, final String fileName, String level, boolean immediateFlush, final boolean append,
+ final boolean bufferedIo, final int bufferSize) {
org.apache.logging.log4j.core.Layout<?> fileLayout = null;
- org.apache.logging.log4j.core.Filter fileFilter = null;
- if (bufferedIo.get()) {
- immediateFlush.set(Boolean.TRUE);
+ if (bufferedIo) {
+ immediateFlush = true;
}
- if (layout.get() instanceof LayoutWrapper) {
- fileLayout = ((LayoutWrapper) layout.get()).getLayout();
- } else if (layout.get() != null) {
- fileLayout = new LayoutAdapter(layout.get());
+ if (layout instanceof LayoutWrapper) {
+ fileLayout = ((LayoutWrapper) layout).getLayout();
+ } else if (layout != null) {
+ fileLayout = new LayoutAdapter(layout);
}
- if (filter.get() != null) {
- if (filter.get() instanceof FilterWrapper) {
- fileFilter = ((FilterWrapper) filter.get()).getFilter();
- } else {
- fileFilter = new FilterAdapter(filter.get());
- }
- }
- if (fileName.get() == null) {
+ org.apache.logging.log4j.core.Filter fileFilter = buildFilters(level, filter);
+ if (fileName == null) {
LOGGER.warn("Unable to create File Appender, no file name provided");
return null;
}
return new AppenderWrapper(FileAppender.newBuilder()
.setName(name)
- .setConfiguration(factory.getConfiguration())
+ .setConfiguration(configuration)
.setLayout(fileLayout)
.setFilter(fileFilter)
- .setFileName(fileName.get())
- .setImmediateFlush(immediateFlush.get())
- .setAppend(append.get())
- .setBufferedIo(bufferedIo.get())
- .setBufferSize(bufferSize.get())
+ .setFileName(fileName)
+ .setImmediateFlush(immediateFlush)
+ .setAppend(append)
+ .setBufferedIo(bufferedIo)
+ .setBufferSize(bufferSize)
.build());
}
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/NullAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/NullAppenderBuilder.java
index b972d6b..8b69d39 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/NullAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/NullAppenderBuilder.java
@@ -18,13 +18,16 @@
import org.apache.log4j.Appender;
import org.apache.log4j.bridge.AppenderWrapper;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.appender.NullAppender;
import org.apache.logging.log4j.plugins.Plugin;
import org.apache.logging.log4j.status.StatusLogger;
import org.w3c.dom.Element;
+import java.util.Properties;
+
import static org.apache.log4j.builders.BuilderManager.CATEGORY;
/**
@@ -36,8 +39,15 @@
private static final Logger LOGGER = StatusLogger.getLogger();
@Override
- public Appender parseAppender(Element appenderElement, XmlConfigurationFactory factory) {
+ public Appender parseAppender(Element appenderElement, XmlConfiguration config) {
String name = appenderElement.getAttribute("name");
return new AppenderWrapper(NullAppender.createAppender(name));
}
+
+
+ @Override
+ public Appender parseAppender(final String name, final String layoutPrefix,
+ final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
+ return new AppenderWrapper(NullAppender.createAppender(name));
+ }
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java
index 81c3901..ccc8099 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java
@@ -19,14 +19,15 @@
import org.apache.log4j.Appender;
import org.apache.log4j.Layout;
import org.apache.log4j.bridge.AppenderWrapper;
-import org.apache.log4j.bridge.FilterAdapter;
-import org.apache.log4j.bridge.FilterWrapper;
import org.apache.log4j.bridge.LayoutAdapter;
import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
import org.apache.log4j.builders.BooleanHolder;
import org.apache.log4j.builders.Holder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
import org.apache.log4j.spi.Filter;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.xml.XmlConfiguration;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.appender.RollingFileAppender;
import org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy;
@@ -39,19 +40,35 @@
import org.apache.logging.log4j.status.StatusLogger;
import org.w3c.dom.Element;
+import java.util.Properties;
+
import static org.apache.log4j.builders.BuilderManager.CATEGORY;
-import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.LAYOUT_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.NAME_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
/**
* Build a File Appender
*/
@Plugin(name = "org.apache.log4j.RollingFileAppender", category = CATEGORY)
-public class RollingFileAppenderBuilder implements AppenderBuilder {
+public class RollingFileAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
private static final Logger LOGGER = StatusLogger.getLogger();
+ public RollingFileAppenderBuilder() {
+ }
+
+ public RollingFileAppenderBuilder(String prefix, Properties props) {
+ super(prefix, props);
+ }
+
@Override
- public Appender parseAppender(Element appenderElement, XmlConfigurationFactory factory) {
+ public Appender parseAppender(Element appenderElement, XmlConfiguration config) {
String name = appenderElement.getAttribute(NAME_ATTR);
Holder<Layout> layout = new Holder<>();
Holder<Filter> filter = new Holder<>();
@@ -62,16 +79,17 @@
Holder<Integer> bufferSize = new Holder<>(8192);
Holder<String> maxSize = new Holder<>();
Holder<String> maxBackups = new Holder<>();
+ Holder<String> level = new Holder<>();
forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
switch (currentElement.getTagName()) {
case LAYOUT_TAG:
- layout.set(factory.parseLayout(currentElement));
+ layout.set(config.parseLayout(currentElement));
break;
case FILTER_TAG:
- filter.set(factory.parseFilters(currentElement));
+ filter.set(config.parseFilters(currentElement));
break;
case PARAM_TAG: {
- switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+ switch (currentElement.getAttribute(NAME_ATTR)) {
case FILE_PARAM:
fileName.set(currentElement.getAttribute(VALUE_ATTR));
break;
@@ -120,48 +138,73 @@
}
break;
}
+ case THRESHOLD_PARAM: {
+ String value = currentElement.getAttribute(VALUE_ATTR);
+ if (value == null) {
+ LOGGER.warn("No value supplied for Threshold parameter, ignoring.");
+ } else {
+ level.set(value);
+ }
+ break;
+ }
}
break;
}
}
});
+ return createAppender(name, config, layout.get(), filter.get(), bufferedIo.get(), immediateFlush.get(),
+ fileName.get(), level.get(), maxSize.get(), maxBackups.get());
+ }
+
+ @Override
+ public Appender parseAppender(final String name, final String layoutPrefix,
+ final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
+ Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+ Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+ String fileName = getProperty(FILE_PARAM);
+ String level = getProperty(THRESHOLD_PARAM);
+ boolean immediateFlush = false;
+ boolean bufferedIo = getBooleanProperty(BUFFERED_IO_PARAM);
+ String maxSize = getProperty(MAX_SIZE_PARAM);
+ String maxBackups = getProperty(MAX_BACKUP_INDEX);
+ return createAppender(name, configuration, layout, filter, bufferedIo, immediateFlush, fileName, level, maxSize,
+ maxBackups);
+ }
+
+ private Appender createAppender(final String name, final Log4j1Configuration config, final Layout layout,
+ final Filter filter, final boolean bufferedIo, boolean immediateFlush, final String fileName,
+ final String level, final String maxSize, final String maxBackups) {
org.apache.logging.log4j.core.Layout<?> fileLayout = null;
- org.apache.logging.log4j.core.Filter fileFilter = null;
- if (bufferedIo.get()) {
- immediateFlush.set(Boolean.TRUE);
+ if (bufferedIo) {
+ immediateFlush = true;
}
- if (layout.get() instanceof LayoutWrapper) {
- fileLayout = ((LayoutWrapper) layout.get()).getLayout();
- } else if (layout.get() != null) {
- fileLayout = new LayoutAdapter(layout.get());
+ if (layout instanceof LayoutWrapper) {
+ fileLayout = ((LayoutWrapper) layout).getLayout();
+ } else if (layout != null) {
+ fileLayout = new LayoutAdapter(layout);
}
- if (filter.get() != null) {
- if (filter.get() instanceof FilterWrapper) {
- fileFilter = ((FilterWrapper) filter.get()).getFilter();
- } else {
- fileFilter = new FilterAdapter(filter.get());
- }
- }
- if (fileName.get() == null) {
+ org.apache.logging.log4j.core.Filter fileFilter = buildFilters(level, filter);
+ if (fileName == null) {
LOGGER.warn("Unable to create File Appender, no file name provided");
return null;
}
- String filePattern = fileName.get() +"%d{yyy-MM-dd}";
+ String filePattern = fileName +"%d{yyy-MM-dd}";
TriggeringPolicy timePolicy = TimeBasedTriggeringPolicy.newBuilder().setModulate(true).build();
- SizeBasedTriggeringPolicy sizePolicy = SizeBasedTriggeringPolicy.createPolicy(maxSize.get());
+ SizeBasedTriggeringPolicy sizePolicy = SizeBasedTriggeringPolicy.createPolicy(maxSize);
CompositeTriggeringPolicy policy = CompositeTriggeringPolicy.createPolicy(sizePolicy, timePolicy);
RolloverStrategy strategy = DefaultRolloverStrategy.newBuilder()
- .setConfig(factory.getConfiguration())
- .setMax(maxBackups.get())
+ .setConfig(config)
+ .setMax(maxBackups)
.build();
return new AppenderWrapper(RollingFileAppender.newBuilder()
.setName(name)
- .setConfiguration(factory.getConfiguration())
+ .setConfiguration(config)
.setLayout(fileLayout)
.setFilter(fileFilter)
- .setImmediateFlush(immediateFlush.get())
- .setFileName(fileName.get())
+ .setBufferedIo(bufferedIo)
+ .setImmediateFlush(immediateFlush)
+ .setFileName(fileName)
.setFilePattern(filePattern)
.setPolicy(policy)
.setStrategy(strategy)
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/DenyAllFilterBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/DenyAllFilterBuilder.java
index b449ca0..f15d6e5 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/DenyAllFilterBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/DenyAllFilterBuilder.java
@@ -17,8 +17,9 @@
package org.apache.log4j.builders.filter;
import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.config.PropertiesConfiguration;
import org.apache.log4j.spi.Filter;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.xml.XmlConfiguration;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.plugins.Plugin;
import org.apache.logging.log4j.core.filter.DenyAllFilter;
@@ -36,7 +37,12 @@
private static final Logger LOGGER = StatusLogger.getLogger();
@Override
- public Filter parseFilter(Element filterElement, XmlConfigurationFactory factory) {
+ public Filter parseFilter(Element filterElement, XmlConfiguration config) {
+ return new FilterWrapper(DenyAllFilter.newBuilder().build());
+ }
+
+ @Override
+ public Filter parseFilter(PropertiesConfiguration config) {
return new FilterWrapper(DenyAllFilter.newBuilder().build());
}
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/FilterBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/FilterBuilder.java
index fbeaefb..f26e7cb 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/FilterBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/FilterBuilder.java
@@ -16,8 +16,9 @@
*/
package org.apache.log4j.builders.filter;
+import org.apache.log4j.config.PropertiesConfiguration;
import org.apache.log4j.spi.Filter;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.xml.XmlConfiguration;
import org.w3c.dom.Element;
/**
@@ -25,6 +26,8 @@
*/
public interface FilterBuilder {
- Filter parseFilter(Element element, XmlConfigurationFactory factory);
+ Filter parseFilter(Element element, XmlConfiguration config);
+
+ Filter parseFilter(PropertiesConfiguration config);
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelMatchFilterBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelMatchFilterBuilder.java
index 0d13e3f..5b1e435 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelMatchFilterBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelMatchFilterBuilder.java
@@ -17,10 +17,12 @@
package org.apache.log4j.builders.filter;
import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
import org.apache.log4j.builders.BooleanHolder;
import org.apache.log4j.builders.Holder;
+import org.apache.log4j.config.PropertiesConfiguration;
import org.apache.log4j.spi.Filter;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.xml.XmlConfiguration;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.plugins.Plugin;
@@ -28,27 +30,36 @@
import org.apache.logging.log4j.status.StatusLogger;
import org.w3c.dom.Element;
+import java.util.Properties;
+
import static org.apache.log4j.builders.BuilderManager.CATEGORY;
-import static org.apache.log4j.xml.XmlConfigurationFactory.*;
-import static org.apache.log4j.xml.XmlConfigurationFactory.VALUE_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.*;
+import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR;
/**
* Build a Level match failter.
*/
@Plugin(name = "org.apache.log4j.varia.LevelMatchFilter", category = CATEGORY)
-public class LevelMatchFilterBuilder implements FilterBuilder {
+public class LevelMatchFilterBuilder extends AbstractBuilder implements FilterBuilder {
private static final Logger LOGGER = StatusLogger.getLogger();
- private static final String LEVEL = "level";
- private static final String ACCEPT_ON_MATCH = "acceptonmatch";
+ private static final String LEVEL = "LevelToMatch";
+ private static final String ACCEPT_ON_MATCH = "AcceptOnMatch";
+
+ public LevelMatchFilterBuilder() {
+ }
+
+ public LevelMatchFilterBuilder(String prefix, Properties props) {
+ super(prefix, props);
+ }
@Override
- public Filter parseFilter(Element filterElement, XmlConfigurationFactory factory) {
+ public Filter parseFilter(Element filterElement, XmlConfiguration config) {
final Holder<String> level = new Holder<>();
final Holder<Boolean> acceptOnMatch = new BooleanHolder();
forEachElement(filterElement.getElementsByTagName("param"), (currentElement) -> {
if (currentElement.getTagName().equals("param")) {
- switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+ switch (currentElement.getAttribute(NAME_ATTR)) {
case LEVEL:
level.set(currentElement.getAttribute(VALUE_ATTR));
break;
@@ -58,11 +69,22 @@
}
}
});
+ return createFilter(level.get(), acceptOnMatch.get());
+ }
+
+ @Override
+ public Filter parseFilter(PropertiesConfiguration config) {
+ String level = getProperty(LEVEL);
+ boolean acceptOnMatch = getBooleanProperty(ACCEPT_ON_MATCH);
+ return createFilter(level, acceptOnMatch);
+ }
+
+ private Filter createFilter(String level, boolean acceptOnMatch) {
Level lvl = Level.ERROR;
- if (level.get() != null) {
- lvl = Level.toLevel(level.get(), Level.ERROR);
+ if (level != null) {
+ lvl = Level.toLevel(level, Level.ERROR);
}
- org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch.get() != null && acceptOnMatch.get()
+ org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch
? org.apache.logging.log4j.core.Filter.Result.ACCEPT
: org.apache.logging.log4j.core.Filter.Result.DENY;
return new FilterWrapper(LevelMatchFilter.newBuilder()
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.java
index 838aafdc..ca2c70f 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.java
@@ -17,10 +17,12 @@
package org.apache.log4j.builders.filter;
import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
import org.apache.log4j.builders.BooleanHolder;
import org.apache.log4j.builders.Holder;
+import org.apache.log4j.config.PropertiesConfiguration;
import org.apache.log4j.spi.Filter;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.xml.XmlConfiguration;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.plugins.Plugin;
@@ -29,28 +31,37 @@
import org.w3c.dom.Element;
+import java.util.Properties;
+
import static org.apache.log4j.builders.BuilderManager.CATEGORY;
-import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+import static org.apache.log4j.xml.XmlConfiguration.*;
/**
* Build a Level match failter.
*/
@Plugin(name = "org.apache.log4j.varia.LevelRangeFilter", category = CATEGORY)
-public class LevelRangeFilterBuilder implements FilterBuilder {
+public class LevelRangeFilterBuilder extends AbstractBuilder implements FilterBuilder {
private static final Logger LOGGER = StatusLogger.getLogger();
- private static final String LEVEL_MAX = "levelmax";
- private static final String LEVEL_MIN = "levelmin";
- private static final String ACCEPT_ON_MATCH = "acceptonmatch";
+ private static final String LEVEL_MAX = "LevelMax";
+ private static final String LEVEL_MIN = "LevelMin";
+ private static final String ACCEPT_ON_MATCH = "AcceptOnMatch";
+
+ public LevelRangeFilterBuilder() {
+ }
+
+ public LevelRangeFilterBuilder(String prefix, Properties props) {
+ super(prefix, props);
+ }
@Override
- public Filter parseFilter(Element filterElement, XmlConfigurationFactory factory) {
+ public Filter parseFilter(Element filterElement, XmlConfiguration config) {
final Holder<String> levelMax = new Holder<>();
final Holder<String> levelMin = new Holder<>();
final Holder<Boolean> acceptOnMatch = new BooleanHolder();
forEachElement(filterElement.getElementsByTagName("param"), (currentElement) -> {
if (currentElement.getTagName().equals("param")) {
- switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+ switch (currentElement.getAttribute(NAME_ATTR)) {
case LEVEL_MAX:
levelMax.set(currentElement.getAttribute(VALUE_ATTR));
break;
@@ -63,15 +74,27 @@
}
}
});
+ return createFilter(levelMax.get(), levelMin.get(), acceptOnMatch.get());
+ }
+
+ @Override
+ public Filter parseFilter(PropertiesConfiguration config) {
+ String levelMax = getProperty(LEVEL_MAX);
+ String levelMin = getProperty(LEVEL_MIN);
+ boolean acceptOnMatch = getBooleanProperty(ACCEPT_ON_MATCH);
+ return createFilter(levelMax, levelMin, acceptOnMatch);
+ }
+
+ private Filter createFilter(String levelMax, String levelMin, boolean acceptOnMatch) {
Level max = Level.FATAL;
Level min = Level.TRACE;
- if (levelMax.get() != null) {
- max = Level.toLevel(levelMax.get(), Level.FATAL);
+ if (levelMax != null) {
+ max = Level.toLevel(levelMax, Level.FATAL);
}
- if (levelMin.get() != null) {
- min = Level.toLevel(levelMin.get(), Level.DEBUG);
+ if (levelMin != null) {
+ min = Level.toLevel(levelMin, Level.DEBUG);
}
- org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch.get() != null && acceptOnMatch.get()
+ org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch
? org.apache.logging.log4j.core.Filter.Result.ACCEPT
: org.apache.logging.log4j.core.Filter.Result.NEUTRAL;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/StringMatchFilterBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/StringMatchFilterBuilder.java
index a8ec3bf..f81dbdc 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/StringMatchFilterBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/StringMatchFilterBuilder.java
@@ -17,36 +17,40 @@
package org.apache.log4j.builders.filter;
import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
import org.apache.log4j.builders.BooleanHolder;
import org.apache.log4j.builders.Holder;
+import org.apache.log4j.config.PropertiesConfiguration;
import org.apache.log4j.spi.Filter;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.xml.XmlConfiguration;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.plugins.Plugin;
import org.apache.logging.log4j.core.filter.StringMatchFilter;
import org.apache.logging.log4j.status.StatusLogger;
import org.w3c.dom.Element;
+import java.util.Properties;
+
import static org.apache.log4j.builders.BuilderManager.CATEGORY;
-import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+import static org.apache.log4j.xml.XmlConfiguration.*;
/**
* Build a String match filter.
*/
@Plugin(name = "org.apache.log4j.varia.StringMatchFilter", category = CATEGORY)
-public class StringMatchFilterBuilder implements FilterBuilder {
+public class StringMatchFilterBuilder extends AbstractBuilder implements FilterBuilder {
private static final Logger LOGGER = StatusLogger.getLogger();
- private static final String STRING_TO_MATCH = "stringtomatch";
- private static final String ACCEPT_ON_MATCH = "acceptonmatch";
+ private static final String STRING_TO_MATCH = "StringToMatch";
+ private static final String ACCEPT_ON_MATCH = "AcceptOnMatch";
@Override
- public Filter parseFilter(Element filterElement, XmlConfigurationFactory factory) {
+ public Filter parseFilter(Element filterElement, XmlConfiguration config) {
final Holder<Boolean> acceptOnMatch = new BooleanHolder();
final Holder<String> text = new Holder<>();
forEachElement(filterElement.getElementsByTagName("param"), (currentElement) -> {
if (currentElement.getTagName().equals("param")) {
- switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+ switch (currentElement.getAttribute(NAME_ATTR)) {
case STRING_TO_MATCH:
text.set(currentElement.getAttribute(VALUE_ATTR));
break;
@@ -57,15 +61,26 @@
}
}
});
- if (text.get() == null) {
+ return createFilter(text.get(), acceptOnMatch.get());
+ }
+
+ @Override
+ public Filter parseFilter(PropertiesConfiguration config) {
+ String text = getProperty(STRING_TO_MATCH);
+ boolean acceptOnMatch = getBooleanProperty(ACCEPT_ON_MATCH);
+ return createFilter(text, acceptOnMatch);
+ }
+
+ private Filter createFilter(String text, boolean acceptOnMatch) {
+ if (text == null) {
LOGGER.warn("No text provided for StringMatchFilter");
return null;
}
- org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch.get() != null && acceptOnMatch.get()
+ org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch
? org.apache.logging.log4j.core.Filter.Result.ACCEPT
: org.apache.logging.log4j.core.Filter.Result.DENY;
return new FilterWrapper(StringMatchFilter.newBuilder()
- .setMatchString(text.get())
+ .setMatchString(text)
.setOnMatch(onMatch)
.setOnMismatch(org.apache.logging.log4j.core.Filter.Result.NEUTRAL)
.build());
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/HtmlLayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/HtmlLayoutBuilder.java
index 0cbbf70..423350e 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/HtmlLayoutBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/HtmlLayoutBuilder.java
@@ -18,43 +18,68 @@
import org.apache.log4j.Layout;
import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
import org.apache.log4j.builders.BooleanHolder;
import org.apache.log4j.builders.Holder;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.plugins.Plugin;
import org.apache.logging.log4j.core.layout.HtmlLayout;
import org.apache.logging.log4j.status.StatusLogger;
import org.w3c.dom.Element;
+import java.util.Properties;
+
import static org.apache.log4j.builders.BuilderManager.CATEGORY;
-import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+import static org.apache.log4j.xml.XmlConfiguration.*;
/**
* Build a Pattern Layout
*/
@Plugin(name = "org.apache.log4j.HTMLLayout", category = CATEGORY)
-public class HtmlLayoutBuilder implements LayoutBuilder {
+public class HtmlLayoutBuilder extends AbstractBuilder implements LayoutBuilder {
private static final Logger LOGGER = StatusLogger.getLogger();
+ private static final String TITLE = "Title";
+ private static final String LOCATION_INFO = "LocationInfo";
+
+ public HtmlLayoutBuilder() {
+ }
+
+ public HtmlLayoutBuilder(String prefix, Properties props) {
+ super(prefix, props);
+ }
+
@Override
- public Layout parseLayout(Element layoutElement, XmlConfigurationFactory factory) {
+ public Layout parseLayout(Element layoutElement, XmlConfiguration config) {
final Holder<String> title = new Holder<>();
final Holder<Boolean> locationInfo = new BooleanHolder();
forEachElement(layoutElement.getElementsByTagName("param"), (currentElement) -> {
- if (currentElement.getTagName().equals("param")) {
- if ("title".equalsIgnoreCase(currentElement.getAttribute("name"))) {
+ if (currentElement.getTagName().equals(PARAM_TAG)) {
+ if (TITLE.equalsIgnoreCase(currentElement.getAttribute("name"))) {
title.set(currentElement.getAttribute("value"));
- } else if ("locationInfo".equalsIgnoreCase(currentElement.getAttribute("name"))) {
+ } else if (LOCATION_INFO.equalsIgnoreCase(currentElement.getAttribute("name"))) {
locationInfo.set(Boolean.parseBoolean(currentElement.getAttribute("value")));
}
}
});
+ return createLayout(title.get(), locationInfo.get());
+ }
+
+ @Override
+ public Layout parseLayout(PropertiesConfiguration config) {
+ String title = getProperty(TITLE);
+ boolean locationInfo = getBooleanProperty(LOCATION_INFO);
+ return createLayout(title, locationInfo);
+ }
+
+ private Layout createLayout(String title, boolean locationInfo) {
return new LayoutWrapper(HtmlLayout.newBuilder()
- .setTitle(title.get())
- .setLocationInfo(locationInfo.get())
+ .setTitle(title)
+ .setLocationInfo(locationInfo)
.build());
}
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/LayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/LayoutBuilder.java
index 9dc88f3..4f28284 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/LayoutBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/LayoutBuilder.java
@@ -17,7 +17,8 @@
package org.apache.log4j.builders.layout;
import org.apache.log4j.Layout;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
import org.w3c.dom.Element;
/**
@@ -25,6 +26,7 @@
*/
public interface LayoutBuilder {
- Layout parseLayout(Element element, XmlConfigurationFactory factory);
+ Layout parseLayout(Element element, XmlConfiguration config);
+ Layout parseLayout(PropertiesConfiguration config);
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/PatternLayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/PatternLayoutBuilder.java
index e066778..1849768 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/PatternLayoutBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/PatternLayoutBuilder.java
@@ -18,29 +18,43 @@
import org.apache.log4j.Layout;
import org.apache.log4j.bridge.LayoutWrapper;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.plugins.PluginAliases;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.plugins.Plugin;
-import org.apache.logging.log4j.plugins.PluginAliases;
import org.apache.logging.log4j.status.StatusLogger;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
+import java.util.Properties;
+
import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
/**
* Build a Pattern Layout
*/
@Plugin(name = "org.apache.log4j.PatternLayout", category = CATEGORY)
@PluginAliases("org.apache.log4j.EnhancedPatternLayout")
-public class PatternLayoutBuilder implements LayoutBuilder {
+public class PatternLayoutBuilder extends AbstractBuilder implements LayoutBuilder {
private static final Logger LOGGER = StatusLogger.getLogger();
+ private static final String PATTERN = "ConversionPattern";
+
+ public PatternLayoutBuilder() {
+ }
+
+ public PatternLayoutBuilder(String prefix, Properties props) {
+ super(prefix, props);
+ }
@Override
- public Layout parseLayout(Element layoutElement, XmlConfigurationFactory factory) {
+ public Layout parseLayout(final Element layoutElement, final XmlConfiguration config) {
NodeList params = layoutElement.getElementsByTagName("param");
final int length = params.getLength();
String pattern = null;
@@ -48,30 +62,44 @@
Node currentNode = params.item(index);
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
Element currentElement = (Element) currentNode;
- if (currentElement.getTagName().equals("param")) {
- if ("conversionPattern".equalsIgnoreCase(currentElement.getAttribute("name"))) {
- pattern = currentElement.getAttribute("value")
- // Log4j 2's %x (NDC) is not compatible with Log4j 1's
- // %x
- // Log4j 1: "foo bar baz"
- // Log4j 2: "[foo, bar, baz]"
- // Use %ndc to get the Log4j 1 format
- .replace("%x", "%ndc")
-
- // Log4j 2's %X (MDC) is not compatible with Log4j 1's
- // %X
- // Log4j 1: "{{foo,bar}{hoo,boo}}"
- // Log4j 2: "{foo=bar,hoo=boo}"
- // Use %properties to get the Log4j 1 format
- .replace("%X", "%properties");
+ if (currentElement.getTagName().equals(PARAM_TAG)) {
+ if (PATTERN.equalsIgnoreCase(currentElement.getAttribute("name"))) {
+ pattern = currentElement.getAttribute("value");
break;
}
}
}
}
+ return createLayout(pattern, config);
+ }
+
+ @Override
+ public Layout parseLayout(final PropertiesConfiguration config) {
+ String pattern = getProperty(PATTERN);
+ return createLayout(pattern, config);
+ }
+
+ private Layout createLayout(String pattern, final Log4j1Configuration config) {
+ if (pattern == null) {
+ LOGGER.info("No pattern provided for pattern layout, using default pattern");
+ pattern = PatternLayout.DEFAULT_CONVERSION_PATTERN;
+ }
return new LayoutWrapper(PatternLayout.newBuilder()
- .setPattern(pattern)
- .setConfiguration(factory.getConfiguration())
+ .setPattern(pattern
+ // Log4j 2's %x (NDC) is not compatible with Log4j 1's
+ // %x
+ // Log4j 1: "foo bar baz"
+ // Log4j 2: "[foo, bar, baz]"
+ // Use %ndc to get the Log4j 1 format
+ .replace("%x", "%ndc")
+
+ // Log4j 2's %X (MDC) is not compatible with Log4j 1's
+ // %X
+ // Log4j 1: "{{foo,bar}{hoo,boo}}"
+ // Log4j 2: "{foo=bar,hoo=boo}"
+ // Use %properties to get the Log4j 1 format
+ .replace("%X", "%properties"))
+ .setConfiguration(config)
.build());
}
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java
index 411b2ef..d35beba 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java
@@ -18,13 +18,16 @@
import org.apache.log4j.Layout;
import org.apache.log4j.bridge.LayoutWrapper;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.status.StatusLogger;
import org.w3c.dom.Element;
+import java.util.Properties;
+
import static org.apache.log4j.builders.BuilderManager.CATEGORY;
/**
@@ -36,10 +39,18 @@
private static final Logger LOGGER = StatusLogger.getLogger();
@Override
- public Layout parseLayout(Element layoutElement, XmlConfigurationFactory factory) {
+ public Layout parseLayout(Element layoutElement, XmlConfiguration config) {
return new LayoutWrapper(PatternLayout.newBuilder()
.setPattern("%level - %m%n")
- .setConfiguration(factory.getConfiguration())
+ .setConfiguration(config)
+ .build());
+ }
+
+ @Override
+ public Layout parseLayout(PropertiesConfiguration config) {
+ return new LayoutWrapper(PatternLayout.newBuilder()
+ .setPattern("%level - %m%n")
+ .setConfiguration(config)
.build());
}
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/TTCCLayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/TTCCLayoutBuilder.java
index a2e85de..2641ea2 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/TTCCLayoutBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/TTCCLayoutBuilder.java
@@ -18,42 +18,54 @@
import org.apache.log4j.Layout;
import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
import org.apache.log4j.builders.BooleanHolder;
import org.apache.log4j.builders.Holder;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.plugins.Plugin;
import org.apache.logging.log4j.status.StatusLogger;
import org.w3c.dom.Element;
+import java.util.Properties;
+
import static org.apache.log4j.builders.BuilderManager.CATEGORY;
-import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+import static org.apache.log4j.xml.XmlConfiguration.*;
/**
* Build a Pattern Layout
*/
@Plugin(name = "org.apache.log4j.TTCCLayout", category = CATEGORY)
-public class TTCCLayoutBuilder implements LayoutBuilder {
+public class TTCCLayoutBuilder extends AbstractBuilder implements LayoutBuilder {
private static final Logger LOGGER = StatusLogger.getLogger();
- private static final String THREAD_PRINTING_PARAM = "threadprinting";
- private static final String CATEGORY_PREFIXING_PARAM = "categoryprefixing";
- private static final String CONTEXT_PRINTING_PARAM = "contextprinting";
- private static final String DATE_FORMAT_PARAM = "dateformat";
- private static final String TIMEZONE_FORMAT = "timezone";
+ private static final String THREAD_PRINTING_PARAM = "ThreadPrinting";
+ private static final String CATEGORY_PREFIXING_PARAM = "CategoryPrefixing";
+ private static final String CONTEXT_PRINTING_PARAM = "ContextPrinting";
+ private static final String DATE_FORMAT_PARAM = "DateFormat";
+ private static final String TIMEZONE_FORMAT = "TimeZone";
+
+ public TTCCLayoutBuilder() {
+ }
+
+ public TTCCLayoutBuilder(String prefix, Properties props) {
+ super(prefix, props);
+ }
@Override
- public Layout parseLayout(Element layoutElement, XmlConfigurationFactory factory) {
+ public Layout parseLayout(Element layoutElement, XmlConfiguration config) {
final Holder<Boolean> threadPrinting = new BooleanHolder();
final Holder<Boolean> categoryPrefixing = new BooleanHolder();
final Holder<Boolean> contextPrinting = new BooleanHolder();
final Holder<String> dateFormat = new Holder<>();
final Holder<String> timezone = new Holder<>();
forEachElement(layoutElement.getElementsByTagName("param"), (currentElement) -> {
- if (currentElement.getTagName().equals("param")) {
- switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+ if (currentElement.getTagName().equals(PARAM_TAG)) {
+ switch (currentElement.getAttribute(NAME_ATTR)) {
case THREAD_PRINTING_PARAM:
threadPrinting.set(Boolean.parseBoolean(currentElement.getAttribute(VALUE_ATTR)));
break;
@@ -72,32 +84,50 @@
}
}
});
+ return createLayout(threadPrinting.get(), categoryPrefixing.get(), contextPrinting.get(),
+ dateFormat.get(), timezone.get(), config);
+ }
+
+ @Override
+ public Layout parseLayout(PropertiesConfiguration config) {
+ boolean threadPrinting = getBooleanProperty(THREAD_PRINTING_PARAM);
+ boolean categoryPrefixing = getBooleanProperty(CATEGORY_PREFIXING_PARAM);
+ boolean contextPrinting = getBooleanProperty(CONTEXT_PRINTING_PARAM);
+ String dateFormat = getProperty(DATE_FORMAT_PARAM);
+ String timezone = getProperty(TIMEZONE_FORMAT);
+
+ return createLayout(threadPrinting, categoryPrefixing, contextPrinting,
+ dateFormat, timezone, config);
+ }
+
+ private Layout createLayout(boolean threadPrinting, boolean categoryPrefixing, boolean contextPrinting,
+ String dateFormat, String timezone, Log4j1Configuration config) {
StringBuilder sb = new StringBuilder();
- if (dateFormat.get() != null) {
- if (RELATIVE.equalsIgnoreCase(dateFormat.get())) {
+ if (dateFormat != null) {
+ if (RELATIVE.equalsIgnoreCase(dateFormat)) {
sb.append("%r ");
} else {
- sb.append("%d{").append(dateFormat.get()).append("}");
- if (timezone.get() != null) {
- sb.append("{").append(timezone.get()).append("}");
+ sb.append("%d{").append(dateFormat).append("}");
+ if (timezone != null) {
+ sb.append("{").append(timezone).append("}");
}
sb.append(" ");
}
}
- if (threadPrinting.get()) {
+ if (threadPrinting) {
sb.append("[%t] ");
}
sb.append("%p ");
- if (categoryPrefixing.get()) {
+ if (categoryPrefixing) {
sb.append("%c ");
}
- if (contextPrinting.get()) {
+ if (contextPrinting) {
sb.append("%notEmpty{%ndc }");
}
sb.append("- %m%n");
return new LayoutWrapper(PatternLayout.newBuilder()
.setPattern(sb.toString())
- .setConfiguration(factory.getConfiguration())
+ .setConfiguration(config)
.build());
}
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1Configuration.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1Configuration.java
index c0717cb..021afa3 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1Configuration.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1Configuration.java
@@ -23,27 +23,31 @@
import org.apache.logging.log4j.core.config.Reconfigurable;
/**
- * Class Description goes here.
+ * Base Configuration for Log4j 1.
*/
public class Log4j1Configuration extends AbstractConfiguration implements Reconfigurable {
+ public static final String MONITOR_INTERVAL = "log4j1.monitorInterval";
+ public static final String APPENDER_REF_TAG = "appender-ref";
+ public static final String THRESHOLD_PARAM = "Threshold";
+
+ public static final String INHERITED = "inherited";
+
+ public static final String NULL = "null";
+
public Log4j1Configuration(final LoggerContext loggerContext, final ConfigurationSource source,
int monitorIntervalSeconds) {
super(loggerContext, source);
initializeWatchers(this, source, monitorIntervalSeconds);
}
- @Override
- protected void doConfigure() {
- super.getScheduler().start();
-
- }
-
/**
* Initialize the configuration.
*/
@Override
public void initialize() {
+ getStrSubstitutor().setConfiguration(this);
+ super.getScheduler().start();
doConfigure();
setState(State.INITIALIZED);
LOGGER.debug("Configuration {} initialized", this);
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java
new file mode 100644
index 0000000..c3e995d
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java
@@ -0,0 +1,592 @@
+/*
+ * 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.log4j.config;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.PatternLayout;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.builders.BuilderManager;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.config.status.StatusConfiguration;
+import org.apache.logging.log4j.core.filter.AbstractFilterable;
+import org.apache.logging.log4j.util.LoaderUtil;
+
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.SortedMap;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+
+/**
+ * Construct a configuration based on Log4j 1 properties.
+ */
+public class PropertiesConfiguration extends Log4j1Configuration {
+
+ private static final String CATEGORY_PREFIX = "log4j.category.";
+ private static final String LOGGER_PREFIX = "log4j.logger.";
+ private static final String ADDITIVITY_PREFIX = "log4j.additivity.";
+ private static final String ROOT_CATEGORY_PREFIX = "log4j.rootCategory";
+ private static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger";
+ private static final String APPENDER_PREFIX = "log4j.appender.";
+ private static final String LOGGER_REF = "logger-ref";
+ private static final String ROOT_REF = "root-ref";
+ private static final String APPENDER_REF_TAG = "appender-ref";
+ public static final long DEFAULT_DELAY = 60000;
+ public static final String DEBUG_KEY="log4j.debug";
+
+ private static final String INTERNAL_ROOT_NAME = "root";
+
+ private final Map<String, Appender> registry;
+
+ private final BuilderManager manager;
+
+ /**
+ * No argument constructor.
+ */
+ public PropertiesConfiguration(final LoggerContext loggerContext, final ConfigurationSource source,
+ int monitorIntervalSeconds) {
+ super(loggerContext, source, monitorIntervalSeconds);
+ registry = new HashMap<>();
+ manager = new BuilderManager();
+ }
+
+ public void doConfigure() {
+ InputStream is = getConfigurationSource().getInputStream();
+ Properties props = new Properties();
+ try {
+ props.load(is);
+ } catch (Exception e) {
+ LOGGER.error("Could not read configuration file [{}].", getConfigurationSource().toString(), e);
+ return;
+ }
+ // If we reach here, then the config file is alright.
+ doConfigure(props);
+ }
+
+ /**
+ * Read configuration from a file. <b>The existing configuration is
+ * not cleared nor reset.</b> If you require a different behavior,
+ * then call {@link LogManager#resetConfiguration
+ * resetConfiguration} method before calling
+ * <code>doConfigure</code>.
+ *
+ * <p>The configuration file consists of statements in the format
+ * <code>key=value</code>. The syntax of different configuration
+ * elements are discussed below.
+ *
+ * <p>The level value can consist of the string values OFF, FATAL,
+ * ERROR, WARN, INFO, DEBUG, ALL or a <em>custom level</em> value. A
+ * custom level value can be specified in the form
+ * level#classname. By default the repository-wide threshold is set
+ * to the lowest possible value, namely the level <code>ALL</code>.
+ * </p>
+ *
+ *
+ * <h3>Appender configuration</h3>
+ *
+ * <p>Appender configuration syntax is:
+ * <pre>
+ * # For appender named <i>appenderName</i>, set its class.
+ * # Note: The appender name can contain dots.
+ * log4j.appender.appenderName=fully.qualified.name.of.appender.class
+ *
+ * # Set appender specific options.
+ * log4j.appender.appenderName.option1=value1
+ * ...
+ * log4j.appender.appenderName.optionN=valueN
+ * </pre>
+ * <p>
+ * For each named appender you can configure its {@link Layout}. The
+ * syntax for configuring an appender's layout is:
+ * <pre>
+ * log4j.appender.appenderName.layout=fully.qualified.name.of.layout.class
+ * log4j.appender.appenderName.layout.option1=value1
+ * ....
+ * log4j.appender.appenderName.layout.optionN=valueN
+ * </pre>
+ * <p>
+ * The syntax for adding {@link Filter}s to an appender is:
+ * <pre>
+ * log4j.appender.appenderName.filter.ID=fully.qualified.name.of.filter.class
+ * log4j.appender.appenderName.filter.ID.option1=value1
+ * ...
+ * log4j.appender.appenderName.filter.ID.optionN=valueN
+ * </pre>
+ * The first line defines the class name of the filter identified by ID;
+ * subsequent lines with the same ID specify filter option - value
+ * pairs. Multiple filters are added to the appender in the lexicographic
+ * order of IDs.
+ * <p>
+ * The syntax for adding an {@link ErrorHandler} to an appender is:
+ * <pre>
+ * log4j.appender.appenderName.errorhandler=fully.qualified.name.of.errorhandler.class
+ * log4j.appender.appenderName.errorhandler.appender-ref=appenderName
+ * log4j.appender.appenderName.errorhandler.option1=value1
+ * ...
+ * log4j.appender.appenderName.errorhandler.optionN=valueN
+ * </pre>
+ *
+ * <h3>Configuring loggers</h3>
+ *
+ * <p>The syntax for configuring the root logger is:
+ * <pre>
+ * log4j.rootLogger=[level], appenderName, appenderName, ...
+ * </pre>
+ *
+ * <p>This syntax means that an optional <em>level</em> can be
+ * supplied followed by appender names separated by commas.
+ *
+ * <p>The level value can consist of the string values OFF, FATAL,
+ * ERROR, WARN, INFO, DEBUG, ALL or a <em>custom level</em> value. A
+ * custom level value can be specified in the form
+ * <code>level#classname</code>.
+ *
+ * <p>If a level value is specified, then the root level is set
+ * to the corresponding level. If no level value is specified,
+ * then the root level remains untouched.
+ *
+ * <p>The root logger can be assigned multiple appenders.
+ *
+ * <p>Each <i>appenderName</i> (separated by commas) will be added to
+ * the root logger. The named appender is defined using the
+ * appender syntax defined above.
+ *
+ * <p>For non-root categories the syntax is almost the same:
+ * <pre>
+ * log4j.logger.logger_name=[level|INHERITED|NULL], appenderName, appenderName, ...
+ * </pre>
+ *
+ * <p>The meaning of the optional level value is discussed above
+ * in relation to the root logger. In addition however, the value
+ * INHERITED can be specified meaning that the named logger should
+ * inherit its level from the logger hierarchy.
+ *
+ * <p>If no level value is supplied, then the level of the
+ * named logger remains untouched.
+ *
+ * <p>By default categories inherit their level from the
+ * hierarchy. However, if you set the level of a logger and later
+ * decide that that logger should inherit its level, then you should
+ * specify INHERITED as the value for the level value. NULL is a
+ * synonym for INHERITED.
+ *
+ * <p>Similar to the root logger syntax, each <i>appenderName</i>
+ * (separated by commas) will be attached to the named logger.
+ *
+ * <p>See the <a href="../../../../manual.html#additivity">appender
+ * additivity rule</a> in the user manual for the meaning of the
+ * <code>additivity</code> flag.
+ *
+ *
+ * # Set options for appender named "A1".
+ * # Appender "A1" will be a SyslogAppender
+ * log4j.appender.A1=org.apache.log4j.net.SyslogAppender
+ *
+ * # The syslog daemon resides on www.abc.net
+ * log4j.appender.A1.SyslogHost=www.abc.net
+ *
+ * # A1's layout is a PatternLayout, using the conversion pattern
+ * # <b>%r %-5p %c{2} %M.%L %x - %m\n</b>. Thus, the log output will
+ * # include # the relative time since the start of the application in
+ * # milliseconds, followed by the level of the log request,
+ * # followed by the two rightmost components of the logger name,
+ * # followed by the callers method name, followed by the line number,
+ * # the nested diagnostic context and finally the message itself.
+ * # Refer to the documentation of {@link PatternLayout} for further information
+ * # on the syntax of the ConversionPattern key.
+ * log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+ * log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %c{2} %M.%L %x - %m\n
+ *
+ * # Set options for appender named "A2"
+ * # A2 should be a RollingFileAppender, with maximum file size of 10 MB
+ * # using at most one backup file. A2's layout is TTCC, using the
+ * # ISO8061 date format with context printing enabled.
+ * log4j.appender.A2=org.apache.log4j.RollingFileAppender
+ * log4j.appender.A2.MaxFileSize=10MB
+ * log4j.appender.A2.MaxBackupIndex=1
+ * log4j.appender.A2.layout=org.apache.log4j.TTCCLayout
+ * log4j.appender.A2.layout.ContextPrinting=enabled
+ * log4j.appender.A2.layout.DateFormat=ISO8601
+ *
+ * # Root logger set to DEBUG using the A2 appender defined above.
+ * log4j.rootLogger=DEBUG, A2
+ *
+ * # Logger definitions:
+ * # The SECURITY logger inherits is level from root. However, it's output
+ * # will go to A1 appender defined above. It's additivity is non-cumulative.
+ * log4j.logger.SECURITY=INHERIT, A1
+ * log4j.additivity.SECURITY=false
+ *
+ * # Only warnings or above will be logged for the logger "SECURITY.access".
+ * # Output will go to A1.
+ * log4j.logger.SECURITY.access=WARN
+ *
+ *
+ * # The logger "class.of.the.day" inherits its level from the
+ * # logger hierarchy. Output will go to the appender's of the root
+ * # logger, A2 in this case.
+ * log4j.logger.class.of.the.day=INHERIT
+ * </pre>
+ *
+ * <p>Refer to the <b>setOption</b> method in each Appender and
+ * Layout for class specific options.
+ *
+ * <p>Use the <code>#</code> or <code>!</code> characters at the
+ * beginning of a line for comments.
+ *
+ * <p>
+ */
+ private void doConfigure(Properties properties) {
+ String status = "error";
+ String value = properties.getProperty(DEBUG_KEY);
+ if (value == null) {
+ value = properties.getProperty("log4j.configDebug");
+ if (value != null) {
+ LOGGER.warn("[log4j.configDebug] is deprecated. Use [log4j.debug] instead.");
+ }
+ }
+
+ if (value != null) {
+ status = OptionConverter.toBoolean(value, false) ? "debug" : "error";
+ }
+
+ final StatusConfiguration statusConfig = new StatusConfiguration().setStatus(status);
+ statusConfig.initialize();
+
+ configureRoot(properties);
+ parseLoggers(properties);
+
+ LOGGER.debug("Finished configuring.");
+ }
+
+ // --------------------------------------------------------------------------
+ // Internal stuff
+ // --------------------------------------------------------------------------
+
+ private void configureRoot(Properties props) {
+ String effectiveFrefix = ROOT_LOGGER_PREFIX;
+ String value = OptionConverter.findAndSubst(ROOT_LOGGER_PREFIX, props);
+
+ if (value == null) {
+ value = OptionConverter.findAndSubst(ROOT_CATEGORY_PREFIX, props);
+ effectiveFrefix = ROOT_CATEGORY_PREFIX;
+ }
+
+ if (value == null) {
+ LOGGER.debug("Could not find root logger information. Is this OK?");
+ } else {
+ LoggerConfig root = getRootLogger();
+ parseLogger(props, root, effectiveFrefix, INTERNAL_ROOT_NAME, value);
+ }
+ }
+
+ /**
+ * Parse non-root elements, such non-root categories and renderers.
+ */
+ private void parseLoggers(Properties props) {
+ Enumeration enumeration = props.propertyNames();
+ while (enumeration.hasMoreElements()) {
+ String key = (String) enumeration.nextElement();
+ if (key.startsWith(CATEGORY_PREFIX) || key.startsWith(LOGGER_PREFIX)) {
+ String loggerName = null;
+ if (key.startsWith(CATEGORY_PREFIX)) {
+ loggerName = key.substring(CATEGORY_PREFIX.length());
+ } else if (key.startsWith(LOGGER_PREFIX)) {
+ loggerName = key.substring(LOGGER_PREFIX.length());
+ }
+ String value = OptionConverter.findAndSubst(key, props);
+ LoggerConfig loggerConfig = getLogger(loggerName);
+ if (loggerConfig == null) {
+ boolean additivity = getAdditivityForLogger(props, loggerName);
+ loggerConfig = new LoggerConfig(loggerName, org.apache.logging.log4j.Level.ERROR, additivity);
+ addLogger(loggerName, loggerConfig);
+ }
+ parseLogger(props, loggerConfig, key, loggerName, value);
+ }
+ }
+ }
+
+ /**
+ * Parse the additivity option for a non-root category.
+ */
+ private boolean getAdditivityForLogger(Properties props, String loggerName) {
+ boolean additivity = true;
+ String key = ADDITIVITY_PREFIX + loggerName;
+ String value = OptionConverter.findAndSubst(key, props);
+ LOGGER.debug("Handling {}=[{}]", key, value);
+ // touch additivity only if necessary
+ if ((value != null) && (!value.equals(""))) {
+ additivity = OptionConverter.toBoolean(value, true);
+ }
+ return additivity;
+ }
+
+ /**
+ * This method must work for the root category as well.
+ */
+ private void parseLogger(Properties props, LoggerConfig logger, String optionKey, String loggerName, String value) {
+
+ LOGGER.debug("Parsing for [{}] with value=[{}].", loggerName, value);
+ // We must skip over ',' but not white space
+ StringTokenizer st = new StringTokenizer(value, ",");
+
+ // If value is not in the form ", appender.." or "", then we should set the level of the logger.
+
+ if (!(value.startsWith(",") || value.equals(""))) {
+
+ // just to be on the safe side...
+ if (!st.hasMoreTokens()) {
+ return;
+ }
+
+ String levelStr = st.nextToken();
+ LOGGER.debug("Level token is [{}].", levelStr);
+
+ org.apache.logging.log4j.Level level = levelStr == null ? org.apache.logging.log4j.Level.ERROR :
+ OptionConverter.convertLevel(levelStr, org.apache.logging.log4j.Level.DEBUG);
+ logger.setLevel(level);
+ LOGGER.debug("Logger {} level set to {}", loggerName, level);
+ }
+
+ Appender appender;
+ String appenderName;
+ while (st.hasMoreTokens()) {
+ appenderName = st.nextToken().trim();
+ if (appenderName == null || appenderName.equals(",")) {
+ continue;
+ }
+ LOGGER.debug("Parsing appender named \"{}\".", appenderName);
+ appender = parseAppender(props, appenderName);
+ if (appender != null) {
+ LOGGER.debug("Adding appender named [{}] to loggerConfig [{}].", appenderName,
+ logger.getName());
+ logger.addAppender(getAppender(appenderName), null, null);
+ } else {
+ LOGGER.debug("Appender named [{}}] not found.", appenderName);
+ }
+ }
+ }
+
+ public Appender parseAppender(Properties props, String appenderName) {
+ Appender appender = registry.get(appenderName);
+ if ((appender != null)) {
+ LOGGER.debug("Appender \"" + appenderName + "\" was already parsed.");
+ return appender;
+ }
+ // Appender was not previously initialized.
+ final String prefix = APPENDER_PREFIX + appenderName;
+ final String layoutPrefix = prefix + ".layout";
+ final String filterPrefix = APPENDER_PREFIX + appenderName + ".filter.";
+ String className = OptionConverter.findAndSubst(prefix, props);
+ appender = manager.parseAppender(appenderName, className, prefix, layoutPrefix, filterPrefix, props, this);
+ if (appender == null) {
+ appender = buildAppender(appenderName, className, prefix, layoutPrefix, filterPrefix, props);
+ } else {
+ registry.put(appenderName, appender);
+ if (appender instanceof AppenderWrapper) {
+ addAppender(((AppenderWrapper) appender).getAppender());
+ } else {
+ addAppender(new AppenderAdapter(appender).getAdapter());
+ }
+ }
+ return appender;
+ }
+
+ private Appender buildAppender(final String appenderName, final String className, final String prefix,
+ final String layoutPrefix, final String filterPrefix, final Properties props) {
+ Appender appender = newInstanceOf(className, "Appender");
+ if (appender == null) {
+ return null;
+ }
+ appender.setName(appenderName);
+ appender.setLayout(parseLayout(layoutPrefix, appenderName, props));
+ final String errorHandlerPrefix = prefix + ".errorhandler";
+ String errorHandlerClass = OptionConverter.findAndSubst(errorHandlerPrefix, props);
+ if (errorHandlerClass != null) {
+ ErrorHandler eh = parseErrorHandler(props, errorHandlerPrefix, errorHandlerClass, appender);
+ if (eh != null) {
+ appender.setErrorHandler(eh);
+ }
+ }
+ parseAppenderFilters(props, filterPrefix, appenderName);
+ String[] keys = new String[] {
+ layoutPrefix,
+ };
+ addProperties(appender, keys, props, prefix);
+ if (appender instanceof AppenderWrapper) {
+ addAppender(((AppenderWrapper) appender).getAppender());
+ } else {
+ addAppender(new AppenderAdapter(appender).getAdapter());
+ }
+ registry.put(appenderName, appender);
+ return appender;
+ }
+
+ public Layout parseLayout(String layoutPrefix, String appenderName, Properties props) {
+ String layoutClass = OptionConverter.findAndSubst(layoutPrefix, props);
+ if (layoutClass == null) {
+ return null;
+ }
+ Layout layout = manager.parseLayout(layoutClass, layoutPrefix, props, this);
+ if (layout == null) {
+ layout = buildLayout(layoutPrefix, layoutClass, appenderName, props);
+ }
+ return layout;
+ }
+
+ private Layout buildLayout(String layoutPrefix, String className, String appenderName, Properties props) {
+ Layout layout = newInstanceOf(className, "Layout");
+ if (layout == null) {
+ return null;
+ }
+ LOGGER.debug("Parsing layout options for \"{}\".", appenderName);
+ PropertySetter.setProperties(layout, props, layoutPrefix + ".");
+ LOGGER.debug("End of parsing for \"{}\".", appenderName);
+ return layout;
+ }
+
+ public ErrorHandler parseErrorHandler(final Properties props, final String errorHandlerPrefix,
+ final String errorHandlerClass, final Appender appender) {
+ ErrorHandler eh = newInstanceOf(errorHandlerClass, "ErrorHandler");
+ final String[] keys = new String[] {
+ errorHandlerPrefix + "." + ROOT_REF,
+ errorHandlerPrefix + "." + LOGGER_REF,
+ errorHandlerPrefix + "." + APPENDER_REF_TAG
+ };
+ addProperties(eh, keys, props, errorHandlerPrefix);
+ return eh;
+ }
+
+ public void addProperties(final Object obj, final String[] keys, final Properties props, final String prefix) {
+ final Properties edited = new Properties();
+ props.stringPropertyNames().stream().filter((name) -> {
+ if (name.startsWith(prefix)) {
+ for (String key : keys) {
+ if (name.equals(key)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }).forEach((name) -> edited.put(name, props.getProperty(name)));
+ PropertySetter.setProperties(obj, edited, prefix + ".");
+ }
+
+
+ public Filter parseAppenderFilters(Properties props, String filterPrefix, String appenderName) {
+ // extract filters and filter options from props into a hashtable mapping
+ // the property name defining the filter class to a list of pre-parsed
+ // name-value pairs associated to that filter
+ int fIdx = filterPrefix.length();
+ SortedMap<String, List<NameValue>> filters = new TreeMap<>();
+ Enumeration e = props.keys();
+ String name = "";
+ while (e.hasMoreElements()) {
+ String key = (String) e.nextElement();
+ if (key.startsWith(filterPrefix)) {
+ int dotIdx = key.indexOf('.', fIdx);
+ String filterKey = key;
+ if (dotIdx != -1) {
+ filterKey = key.substring(0, dotIdx);
+ name = key.substring(dotIdx + 1);
+ }
+ List<NameValue> filterOpts = filters.computeIfAbsent(filterKey, k -> new ArrayList<>());
+ if (dotIdx != -1) {
+ String value = OptionConverter.findAndSubst(key, props);
+ filterOpts.add(new NameValue(name, value));
+ }
+ }
+ }
+
+ Filter head = null;
+ Filter next = null;
+ for (Map.Entry<String, List<NameValue>> entry : filters.entrySet()) {
+ String clazz = props.getProperty(entry.getKey());
+ Filter filter = null;
+ if (clazz != null) {
+ filter = manager.parseFilter(clazz, filterPrefix, props, this);
+ if (filter == null) {
+ LOGGER.debug("Filter key: [{}] class: [{}] props: {}", entry.getKey(), clazz, entry.getValue());
+ filter = buildFilter(clazz, appenderName, entry.getValue());
+ }
+ }
+ if (filter != null) {
+ if (head != null) {
+ head = filter;
+ next = filter;
+ } else {
+ next.setNext(filter);
+ next = filter;
+ }
+ }
+ }
+ return head;
+ }
+
+ private Filter buildFilter(String className, String appenderName, List<NameValue> props) {
+ Filter filter = newInstanceOf(className, "Filter");
+ if (filter != null) {
+ PropertySetter propSetter = new PropertySetter(filter);
+ for (NameValue property : props) {
+ propSetter.setProperty(property.key, property.value);
+ }
+ propSetter.activate();
+ }
+ return filter;
+ }
+
+
+ private static <T> T newInstanceOf(String className, String type) {
+ try {
+ return LoaderUtil.newInstanceOf(className);
+ } catch (ClassNotFoundException | IllegalAccessException | NoSuchMethodException |
+ InstantiationException | InvocationTargetException ex) {
+ LOGGER.error("Unable to create {} {} due to {}:{}", type, className,
+ ex.getClass().getSimpleName(), ex.getMessage());
+ return null;
+ }
+ }
+
+ private static class NameValue {
+ String key, value;
+
+ NameValue(String key, String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public String toString() {
+ return key + "=" + value;
+ }
+ }
+
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfigurationFactory.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfigurationFactory.java
new file mode 100644
index 0000000..465eaa1
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfigurationFactory.java
@@ -0,0 +1,74 @@
+/*
+ * 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.log4j.config;
+
+import org.apache.logging.log4j.core.LoggerContext;
+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.Order;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.util.PropertiesUtil;
+
+/**
+ * Configures Log4j from a log4j 1 format properties file.
+ */
+@Plugin(name = "Log4j1PropertiesConfigurationFactory", category = ConfigurationFactory.CATEGORY)
+@Order(2)
+public class PropertiesConfigurationFactory extends ConfigurationFactory {
+
+
+ /**
+ * File name prefix for test configurations.
+ */
+ protected static final String TEST_PREFIX = "log4j-test";
+
+ /**
+ * File name prefix for standard configurations.
+ */
+ protected static final String DEFAULT_PREFIX = "log4j";
+
+ @Override
+ protected String[] getSupportedTypes() {
+ if (!PropertiesUtil.getProperties().getBooleanProperty(ConfigurationFactory.LOG4J1_EXPERIMENTAL, Boolean.FALSE)) {
+ return null;
+ }
+ return new String[] {".properties"};
+ }
+
+ @Override
+ public Configuration getConfiguration(LoggerContext loggerContext, ConfigurationSource source) {
+ int interval = PropertiesUtil.getProperties().getIntegerProperty(Log4j1Configuration.MONITOR_INTERVAL, 0);
+ return new PropertiesConfiguration(loggerContext, source, interval);
+ }
+
+ @Override
+ protected String getTestPrefix() {
+ return TEST_PREFIX;
+ }
+
+ @Override
+ protected String getDefaultPrefix() {
+ return DEFAULT_PREFIX;
+ }
+
+ @Override
+ protected String getVersion() {
+ return LOG4J1_VERSION;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl.java
new file mode 100644
index 0000000..fe9530d
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl.java
@@ -0,0 +1,70 @@
+/*
+ * 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.log4j.helpers;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.spi.AppenderAttachable;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Allows Classes to attach Appenders.
+ */
+public class AppenderAttachableImpl implements AppenderAttachable {
+
+ private final ConcurrentMap<String, Appender> appenders = new ConcurrentHashMap<>();
+
+ @Override
+ public void addAppender(Appender newAppender) {
+ if (newAppender != null) {
+ appenders.put(newAppender.getName(), newAppender);
+ }
+ }
+
+ @Override
+ public Enumeration getAllAppenders() {
+ return Collections.enumeration(appenders.values());
+ }
+
+ @Override
+ public Appender getAppender(String name) {
+ return appenders.get(name);
+ }
+
+ @Override
+ public boolean isAttached(Appender appender) {
+ return appenders.containsValue(appender);
+ }
+
+ @Override
+ public void removeAllAppenders() {
+ appenders.clear();
+ }
+
+ @Override
+ public void removeAppender(Appender appender) {
+ appenders.remove(appender.getName(), appender);
+ }
+
+ @Override
+ public void removeAppender(String name) {
+ appenders.remove(name);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java
index 8ee0ea5..e09e26b 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java
@@ -18,7 +18,6 @@
package org.apache.log4j.helpers;
import org.apache.log4j.Level;
-
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.util.LoaderUtil;
@@ -332,6 +331,51 @@
}
}
}
+
+ public static org.apache.logging.log4j.Level convertLevel(String level,
+ org.apache.logging.log4j.Level defaultLevel) {
+ Level l = toLevel(level, null);
+ return l != null ? convertLevel(l) : defaultLevel;
+ }
+
+ public static org.apache.logging.log4j.Level convertLevel(Level level) {
+ if (level == null) {
+ return org.apache.logging.log4j.Level.ERROR;
+ }
+ if (level.isGreaterOrEqual(Level.FATAL)) {
+ return org.apache.logging.log4j.Level.FATAL;
+ } else if (level.isGreaterOrEqual(Level.ERROR)) {
+ return org.apache.logging.log4j.Level.ERROR;
+ } else if (level.isGreaterOrEqual(Level.WARN)) {
+ return org.apache.logging.log4j.Level.WARN;
+ } else if (level.isGreaterOrEqual(Level.INFO)) {
+ return org.apache.logging.log4j.Level.INFO;
+ } else if (level.isGreaterOrEqual(Level.DEBUG)) {
+ return org.apache.logging.log4j.Level.DEBUG;
+ } else if (level.isGreaterOrEqual(Level.TRACE)) {
+ return org.apache.logging.log4j.Level.TRACE;
+ }
+ return org.apache.logging.log4j.Level.ALL;
+ }
+
+ /**
+ * Find the value corresponding to <code>key</code> in
+ * <code>props</code>. Then perform variable substitution on the
+ * found value.
+ */
+ public static String findAndSubst(String key, Properties props) {
+ String value = props.getProperty(key);
+ if (value == null) {
+ return null;
+ }
+
+ try {
+ return substVars(value, props);
+ } catch (IllegalArgumentException e) {
+ LOGGER.error("Bad option value [{}].", value, e);
+ return value;
+ }
+ }
private static class CharMap {
final char key;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java
new file mode 100644
index 0000000..c96c26d
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java
@@ -0,0 +1,744 @@
+/*
+ * 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.log4j.xml;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.Level;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.builders.BuilderManager;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertySetter;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.AppenderAttachable;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.config.status.StatusConfiguration;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.LoaderUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.FactoryConfigurationError;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.function.Consumer;
+
+/**
+ * Class Description goes here.
+ */
+public class XmlConfiguration extends Log4j1Configuration {
+
+ private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger();
+
+ private static final String CONFIGURATION_TAG = "log4j:configuration";
+ private static final String OLD_CONFIGURATION_TAG = "configuration";
+ private static final String RENDERER_TAG = "renderer";
+ private static final String APPENDER_TAG = "appender";
+ public static final String PARAM_TAG = "param";
+ public static final String LAYOUT_TAG = "layout";
+ private static final String CATEGORY = "category";
+ private static final String LOGGER_ELEMENT = "logger";
+ private static final String CATEGORY_FACTORY_TAG = "categoryFactory";
+ private static final String LOGGER_FACTORY_TAG = "loggerFactory";
+ public static final String NAME_ATTR = "name";
+ private static final String CLASS_ATTR = "class";
+ public static final String VALUE_ATTR = "value";
+ private static final String ROOT_TAG = "root";
+ private static final String LEVEL_TAG = "level";
+ private static final String PRIORITY_TAG = "priority";
+ public static final String FILTER_TAG = "filter";
+ private static final String ERROR_HANDLER_TAG = "errorHandler";
+ public static final String REF_ATTR = "ref";
+ private static final String ADDITIVITY_ATTR = "additivity";
+ private static final String CONFIG_DEBUG_ATTR = "configDebug";
+ private static final String INTERNAL_DEBUG_ATTR = "debug";
+ private static final String EMPTY_STR = "";
+ private static final Class[] ONE_STRING_PARAM = new Class[]{String.class};
+ private static final String dbfKey = "javax.xml.parsers.DocumentBuilderFactory";
+ private static final String THROWABLE_RENDERER_TAG = "throwableRenderer";
+
+ public static final long DEFAULT_DELAY = 60000;
+ /**
+ * File name prefix for test configurations.
+ */
+ protected static final String TEST_PREFIX = "log4j-test";
+
+ /**
+ * File name prefix for standard configurations.
+ */
+ protected static final String DEFAULT_PREFIX = "log4j";
+
+ private final BuilderManager manager;
+
+ // key: appenderName, value: appender
+ private Map<String, Appender> appenderMap;
+
+ private Properties props = null;
+
+ public XmlConfiguration(final LoggerContext loggerContext, final ConfigurationSource source,
+ int monitorIntervalSeconds) {
+ super(loggerContext, source, monitorIntervalSeconds);
+ appenderMap = new HashMap<>();
+ manager = new BuilderManager();
+ }
+
+ public void addAppenderIfAbsent(Appender appender) {
+ appenderMap.putIfAbsent(appender.getName(), appender);
+ }
+
+ /**
+ * Configure log4j by reading in a log4j.dtd compliant XML
+ * configuration file.
+ */
+ @Override
+ public void doConfigure() throws FactoryConfigurationError {
+ ConfigurationSource source = getConfigurationSource();
+ ParseAction action = new ParseAction() {
+ public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
+ InputSource inputSource = new InputSource(source.getInputStream());
+ inputSource.setSystemId("dummy://log4j.dtd");
+ return parser.parse(inputSource);
+ }
+
+ public String toString() {
+ return getConfigurationSource().getLocation();
+ }
+ };
+ doConfigure(action);
+ }
+
+ private void doConfigure(final ParseAction action) throws FactoryConfigurationError {
+ DocumentBuilderFactory dbf;
+ try {
+ LOGGER.debug("System property is : {}", OptionConverter.getSystemProperty(dbfKey, null));
+ dbf = DocumentBuilderFactory.newInstance();
+ LOGGER.debug("Standard DocumentBuilderFactory search succeded.");
+ LOGGER.debug("DocumentBuilderFactory is: " + dbf.getClass().getName());
+ } catch (FactoryConfigurationError fce) {
+ Exception e = fce.getException();
+ LOGGER.debug("Could not instantiate a DocumentBuilderFactory.", e);
+ throw fce;
+ }
+
+ try {
+ dbf.setValidating(true);
+
+ DocumentBuilder docBuilder = dbf.newDocumentBuilder();
+
+ docBuilder.setErrorHandler(new SAXErrorHandler());
+ docBuilder.setEntityResolver(new Log4jEntityResolver());
+
+ Document doc = action.parse(docBuilder);
+ parse(doc.getDocumentElement());
+ } catch (Exception e) {
+ if (e instanceof InterruptedException || e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ // I know this is miserable...
+ LOGGER.error("Could not parse " + action.toString() + ".", e);
+ }
+ }
+
+ /**
+ * Delegates unrecognized content to created instance if it supports UnrecognizedElementParser.
+ *
+ * @param instance instance, may be null.
+ * @param element element, may not be null.
+ * @param props properties
+ * @throws IOException thrown if configuration of owner object should be abandoned.
+ */
+ private void parseUnrecognizedElement(final Object instance, final Element element,
+ final Properties props) throws Exception {
+ boolean recognized = false;
+ if (instance instanceof UnrecognizedElementHandler) {
+ recognized = ((UnrecognizedElementHandler) instance).parseUnrecognizedElement(
+ element, props);
+ }
+ if (!recognized) {
+ LOGGER.warn("Unrecognized element {}", element.getNodeName());
+ }
+ }
+
+ /**
+ * Delegates unrecognized content to created instance if
+ * it supports UnrecognizedElementParser and catches and
+ * logs any exception.
+ *
+ * @param instance instance, may be null.
+ * @param element element, may not be null.
+ * @param props properties
+ * @since 1.2.15
+ */
+ private void quietParseUnrecognizedElement(final Object instance,
+ final Element element,
+ final Properties props) {
+ try {
+ parseUnrecognizedElement(instance, element, props);
+ } catch (Exception ex) {
+ if (ex instanceof InterruptedException || ex instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.error("Error in extension content: ", ex);
+ }
+ }
+
+ /**
+ * Substitutes property value for any references in expression.
+ *
+ * @param value value from configuration file, may contain
+ * literal text, property references or both
+ * @param props properties.
+ * @return evaluated expression, may still contain expressions
+ * if unable to expand.
+ */
+ public String subst(final String value, final Properties props) {
+ try {
+ return OptionConverter.substVars(value, props);
+ } catch (IllegalArgumentException e) {
+ LOGGER.warn("Could not perform variable substitution.", e);
+ return value;
+ }
+ }
+
+ /**
+ * Sets a parameter based from configuration file content.
+ *
+ * @param elem param element, may not be null.
+ * @param propSetter property setter, may not be null.
+ * @param props properties
+ * @since 1.2.15
+ */
+ public void setParameter(final Element elem, final PropertySetter propSetter, final Properties props) {
+ String name = subst(elem.getAttribute("name"), props);
+ String value = (elem.getAttribute("value"));
+ value = subst(OptionConverter.convertSpecialChars(value), props);
+ propSetter.setProperty(name, value);
+ }
+
+ /**
+ * Creates an object and processes any nested param elements
+ * but does not call activateOptions. If the class also supports
+ * UnrecognizedElementParser, the parseUnrecognizedElement method
+ * will be call for any child elements other than param.
+ *
+ * @param element element, may not be null.
+ * @param props properties
+ * @param expectedClass interface or class expected to be implemented
+ * by created class
+ * @return created class or null.
+ * @throws Exception thrown if the contain object should be abandoned.
+ * @since 1.2.15
+ */
+ public Object parseElement(final Element element, final Properties props,
+ @SuppressWarnings("rawtypes") final Class expectedClass) throws Exception {
+ String clazz = subst(element.getAttribute("class"), props);
+ Object instance = OptionConverter.instantiateByClassName(clazz,
+ expectedClass, null);
+
+ if (instance != null) {
+ PropertySetter propSetter = new PropertySetter(instance);
+ NodeList children = element.getChildNodes();
+ final int length = children.getLength();
+
+ for (int loop = 0; loop < length; loop++) {
+ Node currentNode = children.item(loop);
+ if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+ Element currentElement = (Element) currentNode;
+ String tagName = currentElement.getTagName();
+ if (tagName.equals("param")) {
+ setParameter(currentElement, propSetter, props);
+ } else {
+ parseUnrecognizedElement(instance, currentElement, props);
+ }
+ }
+ }
+ return instance;
+ }
+ return null;
+ }
+
+ /**
+ * Used internally to parse appenders by IDREF name.
+ */
+ private Appender findAppenderByName(Document doc, String appenderName) {
+ Appender appender = appenderMap.get(appenderName);
+
+ if (appender != null) {
+ return appender;
+ } else {
+ // Doesn't work on DOM Level 1 :
+ // Element element = doc.getElementById(appenderName);
+
+ // Endre's hack:
+ Element element = null;
+ NodeList list = doc.getElementsByTagName("appender");
+ for (int t = 0; t < list.getLength(); t++) {
+ Node node = list.item(t);
+ NamedNodeMap map = node.getAttributes();
+ Node attrNode = map.getNamedItem("name");
+ if (appenderName.equals(attrNode.getNodeValue())) {
+ element = (Element) node;
+ break;
+ }
+ }
+ // Hack finished.
+
+ if (element == null) {
+
+ LOGGER.error("No appender named [{}] could be found.", appenderName);
+ return null;
+ } else {
+ appender = parseAppender(element);
+ if (appender != null) {
+ appenderMap.put(appenderName, appender);
+ }
+ return appender;
+ }
+ }
+ }
+
+ /**
+ * Used internally to parse appenders by IDREF element.
+ */
+ public Appender findAppenderByReference(Element appenderRef) {
+ String appenderName = subst(appenderRef.getAttribute(REF_ATTR));
+ Document doc = appenderRef.getOwnerDocument();
+ return findAppenderByName(doc, appenderName);
+ }
+
+ /**
+ * Used internally to parse an appender element.
+ */
+ public Appender parseAppender(Element appenderElement) {
+ String className = subst(appenderElement.getAttribute(CLASS_ATTR));
+ LOGGER.debug("Class name: [" + className + ']');
+ Appender appender = manager.parseAppender(className, appenderElement, this);
+ if (appender == null) {
+ appender = buildAppender(className, appenderElement);
+ }
+ return appender;
+ }
+
+ private Appender buildAppender(String className, Element appenderElement) {
+ try {
+ Appender appender = LoaderUtil.newInstanceOf(className);
+ PropertySetter propSetter = new PropertySetter(appender);
+
+ appender.setName(subst(appenderElement.getAttribute(NAME_ATTR)));
+ forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
+ // Parse appender parameters
+ switch (currentElement.getTagName()) {
+ case PARAM_TAG:
+ setParameter(currentElement, propSetter);
+ break;
+ case LAYOUT_TAG:
+ appender.setLayout(parseLayout(currentElement));
+ break;
+ case FILTER_TAG:
+ Filter filter = parseFilters(currentElement);
+ if (filter != null) {
+ LOGGER.debug("Adding filter of type [{}] to appender named [{}]",
+ filter.getClass(), appender.getName());
+ appender.addFilter(filter);
+ }
+ break;
+ case ERROR_HANDLER_TAG:
+ parseErrorHandler(currentElement, appender);
+ break;
+ case APPENDER_REF_TAG:
+ String refName = subst(currentElement.getAttribute(REF_ATTR));
+ if (appender instanceof AppenderAttachable) {
+ AppenderAttachable aa = (AppenderAttachable) appender;
+ Appender child = findAppenderByReference(currentElement);
+ LOGGER.debug("Attaching appender named [{}] to appender named [{}].", refName,
+ appender.getName());
+ aa.addAppender(child);
+ } else {
+ LOGGER.error("Requesting attachment of appender named [{}] to appender named [{}}]"
+ + "which does not implement org.apache.log4j.spi.AppenderAttachable.",
+ refName, appender.getName());
+ }
+ break;
+ default:
+ try {
+ parseUnrecognizedElement(appender, currentElement, props);
+ } catch (Exception ex) {
+ throw new ConsumerException(ex);
+ }
+ }
+ });
+ propSetter.activate();
+ return appender;
+ } catch (ConsumerException ex) {
+ Throwable t = ex.getCause();
+ if (t instanceof InterruptedException || t instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.error("Could not create an Appender. Reported error follows.", t);
+ } catch (Exception oops) {
+ if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.error("Could not create an Appender. Reported error follows.", oops);
+ }
+ return null;
+ }
+
+ /**
+ * Used internally to parse an {@link ErrorHandler} element.
+ */
+ private void parseErrorHandler(Element element, Appender appender) {
+ ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByClassName(
+ subst(element.getAttribute(CLASS_ATTR)),
+ ErrorHandler.class,
+ null);
+
+ if (eh != null) {
+ eh.setAppender(appender);
+
+ PropertySetter propSetter = new PropertySetter(eh);
+ forEachElement(element.getChildNodes(), (currentElement) -> {
+ String tagName = currentElement.getTagName();
+ if (tagName.equals(PARAM_TAG)) {
+ setParameter(currentElement, propSetter);
+ }
+ });
+ propSetter.activate();
+ appender.setErrorHandler(eh);
+ }
+ }
+
+ /**
+ * Used internally to parse a filter element.
+ */
+ public Filter parseFilters(Element filterElement) {
+ String className = subst(filterElement.getAttribute(CLASS_ATTR));
+ LOGGER.debug("Class name: [" + className + ']');
+ Filter filter = manager.parseFilter(className, filterElement, this);
+ if (filter == null) {
+ PropertySetter propSetter = new PropertySetter(filter);
+ forEachElement(filterElement.getChildNodes(), (currentElement) -> {
+ String tagName = currentElement.getTagName();
+ if (tagName.equals(PARAM_TAG)) {
+ setParameter(currentElement, propSetter);
+ } else {
+ quietParseUnrecognizedElement(filter, currentElement, props);
+ }
+ });
+ propSetter.activate();
+ }
+ return filter;
+ }
+
+ /**
+ * Used internally to parse an category element.
+ */
+ private void parseCategory(Element loggerElement) {
+ // Create a new org.apache.log4j.Category object from the <category> element.
+ String catName = subst(loggerElement.getAttribute(NAME_ATTR));
+ boolean additivity = OptionConverter.toBoolean(subst(loggerElement.getAttribute(ADDITIVITY_ATTR)), true);
+ LoggerConfig loggerConfig = getLogger(catName);
+ if (loggerConfig == null) {
+ loggerConfig = new LoggerConfig(catName, org.apache.logging.log4j.Level.ERROR, additivity);
+ addLogger(catName, loggerConfig);
+ } else {
+ loggerConfig.setAdditive(additivity);
+ }
+ parseChildrenOfLoggerElement(loggerElement, loggerConfig, false);
+ }
+
+ /**
+ * Used internally to parse the roor category element.
+ */
+ private void parseRoot(Element rootElement) {
+ LoggerConfig root = getRootLogger();
+ parseChildrenOfLoggerElement(rootElement, root, true);
+ }
+
+ /**
+ * Used internally to parse the children of a LoggerConfig element.
+ */
+ private void parseChildrenOfLoggerElement(Element catElement, LoggerConfig loggerConfig, boolean isRoot) {
+
+ final PropertySetter propSetter = new PropertySetter(loggerConfig);
+ loggerConfig.getAppenderRefs().clear();
+ forEachElement(catElement.getChildNodes(), (currentElement) -> {
+ switch (currentElement.getTagName()) {
+ case APPENDER_REF_TAG: {
+ Appender appender = findAppenderByReference(currentElement);
+ String refName = subst(currentElement.getAttribute(REF_ATTR));
+ if (appender != null) {
+ LOGGER.debug("Adding appender named [{}] to loggerConfig [{}].", refName,
+ loggerConfig.getName());
+ loggerConfig.addAppender(getAppender(refName), null, null);
+ } else {
+ LOGGER.debug("Appender named [{}}] not found.", refName);
+ }
+ break;
+ }
+ case LEVEL_TAG: case PRIORITY_TAG: {
+ parseLevel(currentElement, loggerConfig, isRoot);
+ break;
+ }
+ case PARAM_TAG: {
+ setParameter(currentElement, propSetter);
+ break;
+ }
+ default: {
+ quietParseUnrecognizedElement(loggerConfig, currentElement, props);
+ }
+ }
+ });
+ propSetter.activate();
+ }
+
+ /**
+ * Used internally to parse a layout element.
+ */
+ public Layout parseLayout(Element layoutElement) {
+ String className = subst(layoutElement.getAttribute(CLASS_ATTR));
+ LOGGER.debug("Parsing layout of class: \"{}\"", className);
+ Layout layout = manager.parseLayout(className, layoutElement, this);
+ if (layout == null) {
+ layout = buildLayout(className, layoutElement);
+ }
+ return layout;
+ }
+
+ private Layout buildLayout(String className, Element layout_element) {
+ try {
+ Layout layout = LoaderUtil.newInstanceOf(className);
+ PropertySetter propSetter = new PropertySetter(layout);
+ forEachElement(layout_element.getChildNodes(), (currentElement) -> {
+ String tagName = currentElement.getTagName();
+ if (tagName.equals(PARAM_TAG)) {
+ setParameter(currentElement, propSetter);
+ } else {
+ try {
+ parseUnrecognizedElement(layout, currentElement, props);
+ } catch (Exception ex) {
+ throw new ConsumerException(ex);
+ }
+ }
+ });
+
+ propSetter.activate();
+ return layout;
+ } catch (ConsumerException ce) {
+ Throwable cause = ce.getCause();
+ if (cause instanceof InterruptedException || cause instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.error("Could not create the Layout. Reported error follows.", cause);
+ } catch (Exception oops) {
+ if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.error("Could not create the Layout. Reported error follows.", oops);
+ }
+ return null;
+ }
+
+ /**
+ * Used internally to parse a level element.
+ */
+ private void parseLevel(Element element, LoggerConfig logger, boolean isRoot) {
+ String catName = logger.getName();
+ if (isRoot) {
+ catName = "root";
+ }
+
+ String priStr = subst(element.getAttribute(VALUE_ATTR));
+ LOGGER.debug("Level value for {} is [{}}].", catName, priStr);
+
+ if (INHERITED.equalsIgnoreCase(priStr) || NULL.equalsIgnoreCase(priStr)) {
+ if (isRoot) {
+ LOGGER.error("Root level cannot be inherited. Ignoring directive.");
+ } else {
+ logger.setLevel(null);
+ }
+ } else {
+ String className = subst(element.getAttribute(CLASS_ATTR));
+ if (EMPTY_STR.equals(className)) {
+ logger.setLevel(OptionConverter.convertLevel(priStr, org.apache.logging.log4j.Level.DEBUG));
+ } else {
+ LOGGER.debug("Desired Level sub-class: [{}]", className);
+ try {
+ Class<?> clazz = LoaderUtil.loadClass(className);
+ Method toLevelMethod = clazz.getMethod("toLevel", ONE_STRING_PARAM);
+ Level pri = (Level) toLevelMethod.invoke(null, new Object[]{priStr});
+ logger.setLevel(OptionConverter.convertLevel(pri));
+ } catch (Exception oops) {
+ if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.error("Could not create level [" + priStr +
+ "]. Reported error follows.", oops);
+ return;
+ }
+ }
+ }
+ LOGGER.debug("{} level set to {}", catName, logger.getLevel());
+ }
+
+ private void setParameter(Element elem, PropertySetter propSetter) {
+ String name = subst(elem.getAttribute(NAME_ATTR));
+ String value = (elem.getAttribute(VALUE_ATTR));
+ value = subst(OptionConverter.convertSpecialChars(value));
+ propSetter.setProperty(name, value);
+ }
+
+ /**
+ * Used internally to configure the log4j framework by parsing a DOM
+ * tree of XML elements based on <a
+ * href="doc-files/log4j.dtd">log4j.dtd</a>.
+ */
+ private void parse(Element element) {
+ String rootElementName = element.getTagName();
+
+ if (!rootElementName.equals(CONFIGURATION_TAG)) {
+ if (rootElementName.equals(OLD_CONFIGURATION_TAG)) {
+ LOGGER.warn("The <" + OLD_CONFIGURATION_TAG +
+ "> element has been deprecated.");
+ LOGGER.warn("Use the <" + CONFIGURATION_TAG + "> element instead.");
+ } else {
+ LOGGER.error("DOM element is - not a <" + CONFIGURATION_TAG + "> element.");
+ return;
+ }
+ }
+
+
+ String debugAttrib = subst(element.getAttribute(INTERNAL_DEBUG_ATTR));
+
+ LOGGER.debug("debug attribute= \"" + debugAttrib + "\".");
+ // if the log4j.dtd is not specified in the XML file, then the
+ // "debug" attribute is returned as the empty string.
+ String status = "error";
+ if (!debugAttrib.equals("") && !debugAttrib.equals("null")) {
+ status = OptionConverter.toBoolean(debugAttrib, true) ? "debug" : "error";
+
+ } else {
+ LOGGER.debug("Ignoring " + INTERNAL_DEBUG_ATTR + " attribute.");
+ }
+
+ String confDebug = subst(element.getAttribute(CONFIG_DEBUG_ATTR));
+ if (!confDebug.equals("") && !confDebug.equals("null")) {
+ LOGGER.warn("The \"" + CONFIG_DEBUG_ATTR + "\" attribute is deprecated.");
+ LOGGER.warn("Use the \"" + INTERNAL_DEBUG_ATTR + "\" attribute instead.");
+ status = OptionConverter.toBoolean(confDebug, true) ? "debug" : "error";
+ }
+
+ final StatusConfiguration statusConfig = new StatusConfiguration().setStatus(status);
+ statusConfig.initialize();
+
+ forEachElement(element.getChildNodes(), (currentElement) -> {
+ switch (currentElement.getTagName()) {
+ case CATEGORY: case LOGGER_ELEMENT:
+ parseCategory(currentElement);
+ break;
+ case ROOT_TAG:
+ parseRoot(currentElement);
+ break;
+ case RENDERER_TAG:
+ LOGGER.warn("Renderers are not supported by Log4j 2 and will be ignored.");
+ break;
+ case THROWABLE_RENDERER_TAG:
+ LOGGER.warn("Throwable Renderers are not supported by Log4j 2 and will be ignored.");
+ break;
+ case CATEGORY_FACTORY_TAG: case LOGGER_FACTORY_TAG:
+ LOGGER.warn("Log4j 1 Logger factories are not supported by Log4j 2 and will be ignored.");
+ break;
+ case APPENDER_TAG:
+ Appender appender = parseAppender(currentElement);
+ appenderMap.put(appender.getName(), appender);
+ if (appender instanceof AppenderWrapper) {
+ addAppender(((AppenderWrapper) appender).getAppender());
+ } else {
+ addAppender(new AppenderAdapter(appender).getAdapter());
+ }
+ break;
+ default:
+ quietParseUnrecognizedElement(null, currentElement, props);
+ }
+ });
+ }
+
+ private String subst(final String value) {
+ return getStrSubstitutor().replace(value);
+ }
+
+ public static void forEachElement(NodeList list, Consumer<Element> consumer) {
+ final int length = list.getLength();
+ for (int loop = 0; loop < length; loop++) {
+ Node currentNode = list.item(loop);
+
+ if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+ Element currentElement = (Element) currentNode;
+ consumer.accept(currentElement);
+ }
+ }
+ }
+
+ private interface ParseAction {
+ Document parse(final DocumentBuilder parser) throws SAXException, IOException;
+ }
+
+ private static class SAXErrorHandler implements org.xml.sax.ErrorHandler {
+ private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger();
+
+ public void error(final SAXParseException ex) {
+ emitMessage("Continuable parsing error ", ex);
+ }
+
+ public void fatalError(final SAXParseException ex) {
+ emitMessage("Fatal parsing error ", ex);
+ }
+
+ public void warning(final SAXParseException ex) {
+ emitMessage("Parsing warning ", ex);
+ }
+
+ private static void emitMessage(final String msg, final SAXParseException ex) {
+ LOGGER.warn("{} {} and column {}", msg, ex.getLineNumber(), ex.getColumnNumber());
+ LOGGER.warn(ex.getMessage(), ex.getException());
+ }
+ }
+
+ private static class ConsumerException extends RuntimeException {
+
+ ConsumerException(Exception ex) {
+ super(ex);
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java
index 17235a4..04b596e 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java
@@ -17,105 +17,24 @@
package org.apache.log4j.xml;
-import org.apache.log4j.Appender;
-import org.apache.log4j.Layout;
-import org.apache.log4j.Level;
-import org.apache.log4j.bridge.AppenderAdapter;
-import org.apache.log4j.bridge.AppenderWrapper;
-import org.apache.log4j.builders.BuilderManager;
import org.apache.log4j.config.Log4j1Configuration;
-import org.apache.log4j.config.PropertySetter;
-import org.apache.log4j.helpers.OptionConverter;
-import org.apache.log4j.spi.AppenderAttachable;
-import org.apache.log4j.spi.Configurator;
-import org.apache.log4j.spi.ErrorHandler;
-import org.apache.log4j.spi.Filter;
import org.apache.logging.log4j.core.LoggerContext;
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.LoggerConfig;
import org.apache.logging.log4j.core.config.Order;
-import org.apache.logging.log4j.core.config.status.StatusConfiguration;
import org.apache.logging.log4j.plugins.Plugin;
import org.apache.logging.log4j.status.StatusLogger;
-import org.apache.logging.log4j.util.LoaderUtil;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.FactoryConfigurationError;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InterruptedIOException;
-import java.io.Reader;
-import java.lang.reflect.Method;
-import java.net.URL;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Consumer;
+import org.apache.logging.log4j.util.PropertiesUtil;
/**
* Constructs a Configuration usable in Log4j 2 from a Log4j 1 configuration file.
*/
@Plugin(name = "Log4j1XmlConfigurationFactory", category = ConfigurationFactory.CATEGORY)
@Order(2)
-public class XmlConfigurationFactory extends ConfigurationFactory implements Configurator {
+public class XmlConfigurationFactory extends ConfigurationFactory {
private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger();
- private static final String CONFIGURATION_TAG = "log4j:configuration";
- private static final String OLD_CONFIGURATION_TAG = "configuration";
- private static final String RENDERER_TAG = "renderer";
- private static final String APPENDER_TAG = "appender";
- private static final String APPENDER_REF_TAG = "appender-ref";
- public static final String PARAM_TAG = "param";
- public static final String LAYOUT_TAG = "layout";
- private static final String CATEGORY = "category";
- private static final String LOGGER_ELEMENT = "logger";
- private static final String CATEGORY_FACTORY_TAG = "categoryFactory";
- private static final String LOGGER_FACTORY_TAG = "loggerFactory";
- public static final String NAME_ATTR = "name";
- private static final String CLASS_ATTR = "class";
- public static final String VALUE_ATTR = "value";
- private static final String ROOT_TAG = "root";
- private static final String LEVEL_TAG = "level";
- private static final String PRIORITY_TAG = "priority";
- public static final String FILTER_TAG = "filter";
- private static final String ERROR_HANDLER_TAG = "errorHandler";
- private static final String REF_ATTR = "ref";
- private static final String ADDITIVITY_ATTR = "additivity";
- private static final String CONFIG_DEBUG_ATTR = "configDebug";
- private static final String INTERNAL_DEBUG_ATTR = "debug";
- private static final String EMPTY_STR = "";
- private static final Class[] ONE_STRING_PARAM = new Class[]{String.class};
- private static final String dbfKey = "javax.xml.parsers.DocumentBuilderFactory";
- private static final String THROWABLE_RENDERER_TAG = "throwableRenderer";
- private static final String SYSTEM_OUT = "System.out";
- private static final String SYSTEM_ERR = "System.err";
- private static final String THREAD_PRINTING_PARAM = "threadprinting";
- private static final String CATEGORY_PREFIXING_PARAM = "categoryprefixing";
- private static final String CONTEXT_PRINTING_PARAM = "contextprinting";
- private static final String DATE_FORMAT_PARAM = "dateformat";
- private static final String TIMEZONE_FORMAT = "timezone";
- public static final String FILE_PARAM = "file";
- public static final String APPEND_PARAM = "append";
- public static final String BUFFERED_IO_PARAM = "bufferedio";
- public static final String BUFFER_SIZE_PARAM = "buffersize";
- public static final String MAX_SIZE_PARAM = "maxfileSize";
- public static final String MAX_BACKUP_INDEX = "maxbackupindex";
- public static final String RELATIVE = "RELATIVE";
- public static final long DEFAULT_DELAY = 60000;
/**
* File name prefix for test configurations.
*/
@@ -126,47 +45,18 @@
*/
protected static final String DEFAULT_PREFIX = "log4j";
- private final BuilderManager manager;
-
- // key: appenderName, value: appender
- private Map<String, Appender> appenderBag;
-
- private Properties props = null;
-
- private final LoggerContext loggerContext;
- private Log4j1Configuration configuration;
-
- /**
- * No argument constructor.
- */
- public XmlConfigurationFactory() {
- appenderBag = new HashMap<>();
- loggerContext = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
- manager = new BuilderManager();
- }
-
-
- private XmlConfigurationFactory(ConfigurationSource source, int monitorIntervalSeconds) {
- appenderBag = new HashMap<>();
- loggerContext = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
- configuration = new Log4j1Configuration(loggerContext, source, monitorIntervalSeconds);
- manager = new BuilderManager();
- }
-
@Override
protected String[] getSupportedTypes() {
+ if (!PropertiesUtil.getProperties().getBooleanProperty(ConfigurationFactory.LOG4J1_EXPERIMENTAL, Boolean.FALSE)) {
+ return null;
+ }
return new String[] {".xml"};
}
@Override
public Configuration getConfiguration(LoggerContext loggerContext, ConfigurationSource source) {
- configuration = new Log4j1Configuration(loggerContext, source, 0);
- doConfigure();
- return configuration;
- }
-
- public Configuration getConfiguration() {
- return configuration;
+ int interval = PropertiesUtil.getProperties().getIntegerProperty(Log4j1Configuration.MONITOR_INTERVAL, 0);
+ return new XmlConfiguration(loggerContext, source, interval);
}
@Override
@@ -179,731 +69,8 @@
return DEFAULT_PREFIX;
}
- /**
- * Delegates unrecognized content to created instance if
- * it supports UnrecognizedElementParser.
- *
- * @param instance instance, may be null.
- * @param element element, may not be null.
- * @param props properties
- * @throws IOException thrown if configuration of owner object
- * should be abandoned.
- * @since 1.2.15
- */
- private static void parseUnrecognizedElement(final Object instance, final Element element,
- final Properties props) throws Exception {
- boolean recognized = false;
- if (instance instanceof UnrecognizedElementHandler) {
- recognized = ((UnrecognizedElementHandler) instance).parseUnrecognizedElement(
- element, props);
- }
- if (!recognized) {
- LOGGER.warn("Unrecognized element {}", element.getNodeName());
- }
- }
-
- /**
- * Delegates unrecognized content to created instance if
- * it supports UnrecognizedElementParser and catches and
- * logs any exception.
- *
- * @param instance instance, may be null.
- * @param element element, may not be null.
- * @param props properties
- * @since 1.2.15
- */
- private static void quietParseUnrecognizedElement(final Object instance,
- final Element element,
- final Properties props) {
- try {
- parseUnrecognizedElement(instance, element, props);
- } catch (Exception ex) {
- if (ex instanceof InterruptedException || ex instanceof InterruptedIOException) {
- Thread.currentThread().interrupt();
- }
- LOGGER.error("Error in extension content: ", ex);
- }
- }
-
- /**
- * Like {@link #configureAndWatch(String, long)} except that the
- * default delay is used.
- *
- * @param configFilename A log4j configuration file in XML format.
- */
- public static void configureAndWatch(final String configFilename) {
- configureAndWatch(configFilename, DEFAULT_DELAY);
- }
-
- /**
- * Read the configuration file <code>configFilename</code> if it
- * exists. Moreover, a thread will be created that will periodically
- * check if <code>configFilename</code> has been created or
- * modified. The period is determined by the <code>delay</code>
- * argument. If a change or file creation is detected, then
- * <code>configFilename</code> is read to configure log4j.
- *
- * @param configFilename A log4j configuration file in XML format.
- * @param delay The delay in milliseconds to wait between each check.
- */
- public static void configureAndWatch(final String configFilename, final long delay) {
- try {
- File file = new File(configFilename);
- InputStream is = new FileInputStream(file);
- ConfigurationSource source = new ConfigurationSource(is, file);
- int seconds = (int) TimeUnit.MILLISECONDS.toSeconds(delay);
- XmlConfigurationFactory factory = new XmlConfigurationFactory(source, seconds);
- factory.doConfigure();
- org.apache.logging.log4j.core.config.Configurator.reconfigure(factory.getConfiguration());
-
- } catch (IOException ioe) {
- LOGGER.error("Unable to process configuration file {} due to {}", configFilename, ioe.getMessage());
- }
- }
-
- /**
- * A static version of doConfigure(String).
- */
- public static void configure(final String filename) throws FactoryConfigurationError {
- configureAndWatch(filename, 0);
- }
-
- /**
- * A static version of doConfigure(URL).
- */
- public static void configure(final URL url) throws FactoryConfigurationError {
- try {
- InputStream is = url.openStream();
- ConfigurationSource source = new ConfigurationSource(is, url);
- XmlConfigurationFactory factory = new XmlConfigurationFactory(source, 0);
- factory.doConfigure();
- org.apache.logging.log4j.core.config.Configurator.reconfigure(factory.getConfiguration());
- } catch (IOException ioe) {
- LOGGER.error("Unable to process configuration {} due to {}", url.toString(), ioe.getMessage());
- }
- }
-
- /**
- * Substitutes property value for any references in expression.
- *
- * @param value value from configuration file, may contain
- * literal text, property references or both
- * @param props properties.
- * @return evaluated expression, may still contain expressions
- * if unable to expand.
- */
- public static String subst(final String value, final Properties props) {
- try {
- return OptionConverter.substVars(value, props);
- } catch (IllegalArgumentException e) {
- LOGGER.warn("Could not perform variable substitution.", e);
- return value;
- }
- }
-
- /**
- * Sets a parameter based from configuration file content.
- *
- * @param elem param element, may not be null.
- * @param propSetter property setter, may not be null.
- * @param props properties
- * @since 1.2.15
- */
- public static void setParameter(final Element elem, final PropertySetter propSetter, final Properties props) {
- String name = subst(elem.getAttribute("name"), props);
- String value = (elem.getAttribute("value"));
- value = subst(OptionConverter.convertSpecialChars(value), props);
- propSetter.setProperty(name, value);
- }
-
- /**
- * Creates an object and processes any nested param elements
- * but does not call activateOptions. If the class also supports
- * UnrecognizedElementParser, the parseUnrecognizedElement method
- * will be call for any child elements other than param.
- *
- * @param element element, may not be null.
- * @param props properties
- * @param expectedClass interface or class expected to be implemented
- * by created class
- * @return created class or null.
- * @throws Exception thrown if the contain object should be abandoned.
- * @since 1.2.15
- */
- public static Object parseElement(final Element element, final Properties props,
- @SuppressWarnings("rawtypes") final Class expectedClass) throws Exception {
- String clazz = subst(element.getAttribute("class"), props);
- Object instance = OptionConverter.instantiateByClassName(clazz,
- expectedClass, null);
-
- if (instance != null) {
- PropertySetter propSetter = new PropertySetter(instance);
- NodeList children = element.getChildNodes();
- final int length = children.getLength();
-
- for (int loop = 0; loop < length; loop++) {
- Node currentNode = children.item(loop);
- if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
- Element currentElement = (Element) currentNode;
- String tagName = currentElement.getTagName();
- if (tagName.equals("param")) {
- setParameter(currentElement, propSetter, props);
- } else {
- parseUnrecognizedElement(instance, currentElement, props);
- }
- }
- }
- return instance;
- }
- return null;
- }
-
- /**
- * Used internally to parse appenders by IDREF name.
- */
- private Appender findAppenderByName(Document doc, String appenderName) {
- Appender appender = appenderBag.get(appenderName);
-
- if (appender != null) {
- return appender;
- } else {
- // Doesn't work on DOM Level 1 :
- // Element element = doc.getElementById(appenderName);
-
- // Endre's hack:
- Element element = null;
- NodeList list = doc.getElementsByTagName("appender");
- for (int t = 0; t < list.getLength(); t++) {
- Node node = list.item(t);
- NamedNodeMap map = node.getAttributes();
- Node attrNode = map.getNamedItem("name");
- if (appenderName.equals(attrNode.getNodeValue())) {
- element = (Element) node;
- break;
- }
- }
- // Hack finished.
-
- if (element == null) {
-
- LOGGER.error("No appender named [{}] could be found.", appenderName);
- return null;
- } else {
- appender = parseAppender(element);
- if (appender != null) {
- appenderBag.put(appenderName, appender);
- }
- return appender;
- }
- }
- }
-
- /**
- * Used internally to parse appenders by IDREF element.
- */
- private Appender findAppenderByReference(Element appenderRef) {
- String appenderName = subst(appenderRef.getAttribute(REF_ATTR));
- Document doc = appenderRef.getOwnerDocument();
- return findAppenderByName(doc, appenderName);
- }
-
- /**
- * Used internally to parse an appender element.
- */
- private Appender parseAppender(Element appenderElement) {
- String className = subst(appenderElement.getAttribute(CLASS_ATTR));
- LOGGER.debug("Class name: [" + className + ']');
- Appender appender = manager.parseAppender(className, appenderElement, this);
- if (appender == null) {
- appender = buildAppender(className, appenderElement);
- }
- return appender;
- }
-
- private Appender buildAppender(String className, Element appenderElement) {
- try {
- Appender appender = LoaderUtil.newInstanceOf(className);
- PropertySetter propSetter = new PropertySetter(appender);
-
- appender.setName(subst(appenderElement.getAttribute(NAME_ATTR)));
- forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
- // Parse appender parameters
- switch (currentElement.getTagName()) {
- case PARAM_TAG:
- setParameter(currentElement, propSetter);
- break;
- case LAYOUT_TAG:
- appender.setLayout(parseLayout(currentElement));
- break;
- case FILTER_TAG:
- Filter filter = parseFilters(currentElement);
- if (filter != null) {
- LOGGER.debug("Adding filter of type [{}] to appender named [{}]",
- filter.getClass(), appender.getName());
- appender.addFilter(filter);
- }
- break;
- case ERROR_HANDLER_TAG:
- parseErrorHandler(currentElement, appender);
- break;
- case APPENDER_REF_TAG:
- String refName = subst(currentElement.getAttribute(REF_ATTR));
- if (appender instanceof AppenderAttachable) {
- AppenderAttachable aa = (AppenderAttachable) appender;
- Appender child = findAppenderByReference(currentElement);
- LOGGER.debug("Attaching appender named [{}] to appender named [{}].", refName,
- appender.getName());
- aa.addAppender(child);
- } else {
- LOGGER.error("Requesting attachment of appender named [{}] to appender named [{}}]"
- + "which does not implement org.apache.log4j.spi.AppenderAttachable.",
- refName, appender.getName());
- }
- break;
- default:
- try {
- parseUnrecognizedElement(appender, currentElement, props);
- } catch (Exception ex) {
- throw new ConsumerException(ex);
- }
- }
- });
- propSetter.activate();
- return appender;
- } catch (ConsumerException ex) {
- Throwable t = ex.getCause();
- if (t instanceof InterruptedException || t instanceof InterruptedIOException) {
- Thread.currentThread().interrupt();
- }
- LOGGER.error("Could not create an Appender. Reported error follows.", t);
- } catch (Exception oops) {
- if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
- Thread.currentThread().interrupt();
- }
- LOGGER.error("Could not create an Appender. Reported error follows.", oops);
- }
- return null;
- }
-
- /**
- * Used internally to parse an {@link ErrorHandler} element.
- */
- private void parseErrorHandler(Element element, Appender appender) {
- ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByClassName(
- subst(element.getAttribute(CLASS_ATTR)),
- ErrorHandler.class,
- null);
-
- if (eh != null) {
- eh.setAppender(appender);
-
- PropertySetter propSetter = new PropertySetter(eh);
- forEachElement(element.getChildNodes(), (currentElement) -> {
- String tagName = currentElement.getTagName();
- if (tagName.equals(PARAM_TAG)) {
- setParameter(currentElement, propSetter);
- }
- });
- propSetter.activate();
- appender.setErrorHandler(eh);
- }
- }
-
- /**
- * Used internally to parse a filter element.
- */
- public Filter parseFilters(Element filterElement) {
- String className = subst(filterElement.getAttribute(CLASS_ATTR));
- LOGGER.debug("Class name: [" + className + ']');
- Filter filter = manager.parseFilter(className, filterElement, this);
- if (filter == null) {
- PropertySetter propSetter = new PropertySetter(filter);
- forEachElement(filterElement.getChildNodes(), (currentElement) -> {
- String tagName = currentElement.getTagName();
- if (tagName.equals(PARAM_TAG)) {
- setParameter(currentElement, propSetter);
- } else {
- quietParseUnrecognizedElement(filter, currentElement, props);
- }
- });
- propSetter.activate();
- }
- return filter;
- }
-
- /**
- * Used internally to parse an category element.
- */
- private void parseCategory(Element loggerElement) {
- // Create a new org.apache.log4j.Category object from the <category> element.
- String catName = subst(loggerElement.getAttribute(NAME_ATTR));
- boolean additivity = OptionConverter.toBoolean(subst(loggerElement.getAttribute(ADDITIVITY_ATTR)), true);
- LoggerConfig loggerConfig = configuration.getLogger(catName);
- if (loggerConfig == null) {
- loggerConfig = new LoggerConfig(catName, org.apache.logging.log4j.Level.ERROR, additivity);
- configuration.addLogger(catName, loggerConfig);
- } else {
- loggerConfig.setAdditive(additivity);
- }
- parseChildrenOfLoggerElement(loggerElement, loggerConfig, false);
- }
-
- /**
- * Used internally to parse the roor category element.
- */
- private void parseRoot(Element rootElement) {
- LoggerConfig root = configuration.getRootLogger();
- parseChildrenOfLoggerElement(rootElement, root, true);
- }
-
- /**
- * Used internally to parse the children of a LoggerConfig element.
- */
- private void parseChildrenOfLoggerElement(Element catElement, LoggerConfig loggerConfig, boolean isRoot) {
-
- final PropertySetter propSetter = new PropertySetter(loggerConfig);
- loggerConfig.getAppenderRefs().clear();
- forEachElement(catElement.getChildNodes(), (currentElement) -> {
- switch (currentElement.getTagName()) {
- case APPENDER_REF_TAG: {
- Appender appender = findAppenderByReference(currentElement);
- String refName = subst(currentElement.getAttribute(REF_ATTR));
- if (appender != null) {
- LOGGER.debug("Adding appender named [{}] to loggerConfig [{}].", refName,
- loggerConfig.getName());
- loggerConfig.addAppender(configuration.getAppender(refName), null, null);
- } else {
- LOGGER.debug("Appender named [{}}] not found.", refName);
- }
- break;
- }
- case LEVEL_TAG: case PRIORITY_TAG: {
- parseLevel(currentElement, loggerConfig, isRoot);
- break;
- }
- case PARAM_TAG: {
- setParameter(currentElement, propSetter);
- break;
- }
- default: {
- quietParseUnrecognizedElement(loggerConfig, currentElement, props);
- }
- }
- });
- propSetter.activate();
- }
-
- /**
- * Used internally to parse a layout element.
- */
- public Layout parseLayout(Element layoutElement) {
- String className = subst(layoutElement.getAttribute(CLASS_ATTR));
- LOGGER.debug("Parsing layout of class: \"{}\"", className);
- Layout layout = manager.parseLayout(className, layoutElement, this);
- if (layout == null) {
- layout = buildLayout(className, layoutElement);
- }
- return layout;
- }
-
- private Layout buildLayout(String className, Element layout_element) {
- try {
- Layout layout = LoaderUtil.newInstanceOf(className);
- PropertySetter propSetter = new PropertySetter(layout);
- forEachElement(layout_element.getChildNodes(), (currentElement) -> {
- String tagName = currentElement.getTagName();
- if (tagName.equals(PARAM_TAG)) {
- setParameter(currentElement, propSetter);
- } else {
- try {
- parseUnrecognizedElement(layout, currentElement, props);
- } catch (Exception ex) {
- throw new ConsumerException(ex);
- }
- }
- });
-
- propSetter.activate();
- return layout;
- } catch (ConsumerException ce) {
- Throwable cause = ce.getCause();
- if (cause instanceof InterruptedException || cause instanceof InterruptedIOException) {
- Thread.currentThread().interrupt();
- }
- LOGGER.error("Could not create the Layout. Reported error follows.", cause);
- } catch (Exception oops) {
- if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
- Thread.currentThread().interrupt();
- }
- LOGGER.error("Could not create the Layout. Reported error follows.", oops);
- }
- return null;
- }
-
- /**
- * Used internally to parse a level element.
- */
- private void parseLevel(Element element, LoggerConfig logger, boolean isRoot) {
- String catName = logger.getName();
- if (isRoot) {
- catName = "root";
- }
-
- String priStr = subst(element.getAttribute(VALUE_ATTR));
- LOGGER.debug("Level value for {} is [{}}].", catName, priStr);
-
- if (INHERITED.equalsIgnoreCase(priStr) || NULL.equalsIgnoreCase(priStr)) {
- if (isRoot) {
- LOGGER.error("Root level cannot be inherited. Ignoring directive.");
- } else {
- logger.setLevel(null);
- }
- } else {
- String className = subst(element.getAttribute(CLASS_ATTR));
- if (EMPTY_STR.equals(className)) {
- logger.setLevel(convertLevel(OptionConverter.toLevel(priStr, Level.DEBUG)));
- } else {
- LOGGER.debug("Desired Level sub-class: [{}]", className);
- try {
- Class<?> clazz = LoaderUtil.loadClass(className);
- Method toLevelMethod = clazz.getMethod("toLevel", ONE_STRING_PARAM);
- Level pri = (Level) toLevelMethod.invoke(null, new Object[]{priStr});
- logger.setLevel(convertLevel(pri));
- } catch (Exception oops) {
- if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
- Thread.currentThread().interrupt();
- }
- LOGGER.error("Could not create level [" + priStr +
- "]. Reported error follows.", oops);
- return;
- }
- }
- }
- LOGGER.debug("{} level set to {}", catName, logger.getLevel());
- }
-
- private void setParameter(Element elem, PropertySetter propSetter) {
- String name = subst(elem.getAttribute(NAME_ATTR));
- String value = (elem.getAttribute(VALUE_ATTR));
- value = subst(OptionConverter.convertSpecialChars(value));
- propSetter.setProperty(name, value);
- }
-
- /**
- * Configure log4j by reading in a log4j.dtd compliant XML
- * configuration file.
- */
- private void doConfigure() throws FactoryConfigurationError {
- ConfigurationSource source = configuration.getConfigurationSource();
- ParseAction action = new ParseAction() {
- public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
- InputSource inputSource = new InputSource(source.getInputStream());
- inputSource.setSystemId("dummy://log4j.dtd");
- return parser.parse(inputSource);
- }
-
- public String toString() {
- return configuration.getConfigurationSource().getLocation();
- }
- };
- doConfigure(action);
- }
-
- private void doConfigure(final ParseAction action) throws FactoryConfigurationError {
- DocumentBuilderFactory dbf;
- try {
- LOGGER.debug("System property is : {}", OptionConverter.getSystemProperty(dbfKey, null));
- dbf = DocumentBuilderFactory.newInstance();
- LOGGER.debug("Standard DocumentBuilderFactory search succeded.");
- LOGGER.debug("DocumentBuilderFactory is: " + dbf.getClass().getName());
- } catch (FactoryConfigurationError fce) {
- Exception e = fce.getException();
- LOGGER.debug("Could not instantiate a DocumentBuilderFactory.", e);
- throw fce;
- }
-
- try {
- dbf.setValidating(true);
-
- DocumentBuilder docBuilder = dbf.newDocumentBuilder();
-
- docBuilder.setErrorHandler(new SAXErrorHandler());
- docBuilder.setEntityResolver(new Log4jEntityResolver());
-
- Document doc = action.parse(docBuilder);
- parse(doc.getDocumentElement());
- } catch (Exception e) {
- if (e instanceof InterruptedException || e instanceof InterruptedIOException) {
- Thread.currentThread().interrupt();
- }
- // I know this is miserable...
- LOGGER.error("Could not parse " + action.toString() + ".", e);
- }
- }
-
@Override
- public void doConfigure(InputStream inputStream, LoggerContext loggerContext) {
- try {
- ConfigurationSource source = new ConfigurationSource(inputStream);
- configuration = new Log4j1Configuration(loggerContext, source, 0);
- doConfigure();
- } catch (IOException ioe) {
- LOGGER.error("Unable to process configuration due to {}", ioe.getMessage());
- }
- }
-
- @Override
- public void doConfigure(URL url, LoggerContext loggerContext) {
- try {
- ConfigurationSource source = new ConfigurationSource(url.openStream(), url);
- configuration = new Log4j1Configuration(loggerContext, source, 0);
- doConfigure();
- } catch (IOException ioe) {
- LOGGER.error("Unable to process configuration due to {}", ioe.getMessage());
- }
- }
-
- /**
- * Used internally to configure the log4j framework by parsing a DOM
- * tree of XML elements based on <a
- * href="doc-files/log4j.dtd">log4j.dtd</a>.
- */
- private void parse(Element element) {
- String rootElementName = element.getTagName();
-
- if (!rootElementName.equals(CONFIGURATION_TAG)) {
- if (rootElementName.equals(OLD_CONFIGURATION_TAG)) {
- LOGGER.warn("The <" + OLD_CONFIGURATION_TAG +
- "> element has been deprecated.");
- LOGGER.warn("Use the <" + CONFIGURATION_TAG + "> element instead.");
- } else {
- LOGGER.error("DOM element is - not a <" + CONFIGURATION_TAG + "> element.");
- return;
- }
- }
-
-
- String debugAttrib = subst(element.getAttribute(INTERNAL_DEBUG_ATTR));
-
- LOGGER.debug("debug attribute= \"" + debugAttrib + "\".");
- // if the log4j.dtd is not specified in the XML file, then the
- // "debug" attribute is returned as the empty string.
- String status = "error";
- if (!debugAttrib.equals("") && !debugAttrib.equals("null")) {
- status = OptionConverter.toBoolean(debugAttrib, true) ? "debug" : "error";
-
- } else {
- LOGGER.debug("Ignoring " + INTERNAL_DEBUG_ATTR + " attribute.");
- }
-
- String confDebug = subst(element.getAttribute(CONFIG_DEBUG_ATTR));
- if (!confDebug.equals("") && !confDebug.equals("null")) {
- LOGGER.warn("The \"" + CONFIG_DEBUG_ATTR + "\" attribute is deprecated.");
- LOGGER.warn("Use the \"" + INTERNAL_DEBUG_ATTR + "\" attribute instead.");
- status = OptionConverter.toBoolean(confDebug, true) ? "debug" : "error";
- }
-
- final StatusConfiguration statusConfig = new StatusConfiguration().setStatus(status);
- statusConfig.initialize();
-
- forEachElement(element.getChildNodes(), (currentElement) -> {
- switch (currentElement.getTagName()) {
- case CATEGORY: case LOGGER_ELEMENT:
- parseCategory(currentElement);
- break;
- case ROOT_TAG:
- parseRoot(currentElement);
- break;
- case RENDERER_TAG:
- LOGGER.warn("Renderers are not supported by Log4j 2 and will be ignored.");
- break;
- case THROWABLE_RENDERER_TAG:
- LOGGER.warn("Throwable Renderers are not supported by Log4j 2 and will be ignored.");
- break;
- case CATEGORY_FACTORY_TAG: case LOGGER_FACTORY_TAG:
- LOGGER.warn("Log4j 1 Logger factories are not supported by Log4j 2 and will be ignored.");
- break;
- case APPENDER_TAG:
- Appender appender = parseAppender(currentElement);
- appenderBag.put(appender.getName(), appender);
- if (appender instanceof AppenderWrapper) {
- configuration.addAppender(((AppenderWrapper) appender).getAppender());
- } else {
- configuration.addAppender(new AppenderAdapter(appender).getAdapter());
- }
- break;
- default:
- quietParseUnrecognizedElement(null, currentElement, props);
- }
- });
- }
-
- private org.apache.logging.log4j.Level convertLevel(Level level) {
- if (level == null) {
- return org.apache.logging.log4j.Level.ERROR;
- }
- if (level.isGreaterOrEqual(Level.FATAL)) {
- return org.apache.logging.log4j.Level.FATAL;
- } else if (level.isGreaterOrEqual(Level.ERROR)) {
- return org.apache.logging.log4j.Level.ERROR;
- } else if (level.isGreaterOrEqual(Level.WARN)) {
- return org.apache.logging.log4j.Level.WARN;
- } else if (level.isGreaterOrEqual(Level.INFO)) {
- return org.apache.logging.log4j.Level.INFO;
- } else if (level.isGreaterOrEqual(Level.DEBUG)) {
- return org.apache.logging.log4j.Level.DEBUG;
- } else if (level.isGreaterOrEqual(Level.TRACE)) {
- return org.apache.logging.log4j.Level.TRACE;
- }
- return org.apache.logging.log4j.Level.ALL;
- }
-
- private String subst(final String value) {
- return configuration.getStrSubstitutor().replace(value);
- }
-
- public static void forEachElement(NodeList list, Consumer<Element> consumer) {
- final int length = list.getLength();
- for (int loop = 0; loop < length; loop++) {
- Node currentNode = list.item(loop);
-
- if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
- Element currentElement = (Element) currentNode;
- consumer.accept(currentElement);
- }
- }
- }
-
- private interface ParseAction {
- Document parse(final DocumentBuilder parser) throws SAXException, IOException;
- }
-
- private static class SAXErrorHandler implements org.xml.sax.ErrorHandler {
- private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger();
-
- public void error(final SAXParseException ex) {
- emitMessage("Continuable parsing error ", ex);
- }
-
- public void fatalError(final SAXParseException ex) {
- emitMessage("Fatal parsing error ", ex);
- }
-
- public void warning(final SAXParseException ex) {
- emitMessage("Parsing warning ", ex);
- }
-
- private static void emitMessage(final String msg, final SAXParseException ex) {
- LOGGER.warn("{} {} and column {}", msg, ex.getLineNumber(), ex.getColumnNumber());
- LOGGER.warn(ex.getMessage(), ex.getException());
- }
- }
-
- private static class ConsumerException extends RuntimeException {
-
- ConsumerException(Exception ex) {
- super(ex);
- }
+ protected String getVersion() {
+ return LOG4J1_VERSION;
}
}
-
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/AsyncAppenderTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AsyncAppenderTest.java
new file mode 100644
index 0000000..bac1338
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AsyncAppenderTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.log4j.config;
+
+import org.apache.log4j.ListAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.apache.logging.log4j.spi.LoggerContextFactory;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test configuration from XML.
+ */
+public class AsyncAppenderTest {
+
+ @Test
+ public void testAsyncXml() throws Exception {
+ LoggerContext loggerContext = configure("target/test-classes/log4j1-async.xml");
+ Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ Thread.sleep(50);
+ Configuration configuration = loggerContext.getConfiguration();
+ Map<String, Appender> appenders = configuration.getAppenders();
+ ListAppender messageAppender = null;
+ for (Map.Entry<String, Appender> entry : appenders.entrySet()) {
+ if (entry.getKey().equals("list")) {
+ messageAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+ }
+ }
+ assertNotNull("No Message Appender", messageAppender);
+ List<String> messages = messageAppender.getMessages();
+ assertTrue("No messages", messages != null && messages.size() > 0);
+ }
+
+ @Test
+ public void testAsyncProperties() throws Exception {
+ LoggerContext loggerContext = configure("target/test-classes/log4j1-async.properties");
+ Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ Thread.sleep(50);
+ Configuration configuration = loggerContext.getConfiguration();
+ Map<String, Appender> appenders = configuration.getAppenders();
+ ListAppender messageAppender = null;
+ for (Map.Entry<String, Appender> entry : appenders.entrySet()) {
+ if (entry.getKey().equals("list")) {
+ messageAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+ }
+ }
+ assertNotNull("No Message Appender", messageAppender);
+ List<String> messages = messageAppender.getMessages();
+ assertTrue("No messages", messages != null && messages.size() > 0);
+ }
+
+
+ private LoggerContext configure(String configLocation) throws Exception {
+ File file = new File(configLocation);
+ InputStream is = new FileInputStream(file);
+ ConfigurationSource source = new ConfigurationSource(is, file);
+ LoggerContextFactory factory = org.apache.logging.log4j.LogManager.getFactory();
+ LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+ Configuration configuration;
+ if (configLocation.endsWith(".xml")) {
+ configuration = new XmlConfigurationFactory().getConfiguration(context, source);
+ } else {
+ configuration = new PropertiesConfigurationFactory().getConfiguration(context, source);
+ }
+ assertNotNull("No configuration created", configuration);
+ Configurator.reconfigure(configuration);
+ return context;
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/AutoConfigTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AutoConfigTest.java
index 764f612..5b4412d 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/AutoConfigTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AutoConfigTest.java
@@ -21,10 +21,11 @@
import org.apache.log4j.Logger;
import org.apache.log4j.bridge.AppenderAdapter;
import org.apache.log4j.spi.LoggingEvent;
-import org.apache.log4j.xml.XmlConfigurationFactory;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
import org.apache.logging.log4j.spi.LoggerContext;
+import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
@@ -39,6 +40,11 @@
*/
public class AutoConfigTest {
+ @BeforeClass
+ public static void beforeClass() {
+ System.setProperty(ConfigurationFactory.LOG4J1_EXPERIMENTAL, "true");
+ }
+
@Test
public void testListAppender() {
Logger logger = LogManager.getLogger("test");
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationFactoryTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationFactoryTest.java
new file mode 100644
index 0000000..de81b63
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationFactoryTest.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.log4j.config;
+
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.File;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test configuration from Properties.
+ */
+public class PropertiesConfigurationFactoryTest {
+
+ @BeforeClass
+ public static void beforeClass() {
+ System.setProperty(ConfigurationFactory.LOG4J1_CONFIGURATION_FILE_PROPERTY, "target/test-classes/log4j1-file.properties");
+ }
+
+ @Test
+ public void testProperties() throws Exception {
+ Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ File file = new File("target/temp.A1");
+ assertTrue("File A1 was not created", file.exists());
+ assertTrue("File A1 is empty", file.length() > 0);
+ file = new File("target/temp.A2");
+ assertTrue("File A2 was not created", file.exists());
+ assertTrue("File A2 is empty", file.length() > 0);
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationTest.java
new file mode 100644
index 0000000..72ff293
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationTest.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.log4j.config;
+
+import org.apache.log4j.ListAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test configuration from Properties.
+ */
+public class PropertiesConfigurationTest {
+
+ @Test
+ public void testProperties() throws Exception {
+ configure("target/test-classes/log4j1-file.properties");
+ Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ File file = new File("target/temp.A1");
+ assertTrue("File A1 was not created", file.exists());
+ assertTrue("File A1 is empty", file.length() > 0);
+ file = new File("target/temp.A2");
+ assertTrue("File A2 was not created", file.exists());
+ assertTrue("File A2 is empty", file.length() > 0);
+ }
+
+ @Test
+ public void testListAppender() throws Exception {
+ LoggerContext loggerContext = configure("target/test-classes/log4j1-list.properties");
+ Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ Configuration configuration = loggerContext.getConfiguration();
+ Map<String, Appender> appenders = configuration.getAppenders();
+ ListAppender eventAppender = null;
+ ListAppender messageAppender = null;
+ for (Map.Entry<String, Appender> entry : appenders.entrySet()) {
+ if (entry.getKey().equals("list")) {
+ messageAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+ } else if (entry.getKey().equals("events")) {
+ eventAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+ }
+ }
+ assertNotNull("No Event Appender", eventAppender);
+ assertNotNull("No Message Appender", messageAppender);
+ List<LoggingEvent> events = eventAppender.getEvents();
+ assertTrue("No events", events != null && events.size() > 0);
+ List<String> messages = messageAppender.getMessages();
+ assertTrue("No messages", messages != null && messages.size() > 0);
+ }
+
+ private LoggerContext configure(String configLocation) throws Exception {
+ File file = new File(configLocation);
+ InputStream is = new FileInputStream(file);
+ ConfigurationSource source = new ConfigurationSource(is, file);
+ LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+ Configuration configuration = new PropertiesConfigurationFactory().getConfiguration(context, source);
+ assertNotNull("No configuration created", configuration);
+ Configurator.reconfigure(configuration);
+ return context;
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationFactoryTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationFactoryTest.java
index d522a18..65b8d47 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationFactoryTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationFactoryTest.java
@@ -16,23 +16,18 @@
*/
package org.apache.log4j.config;
-import org.apache.log4j.ListAppender;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
-import org.apache.log4j.bridge.AppenderAdapter;
-import org.apache.log4j.spi.LoggingEvent;
-import org.apache.log4j.xml.DOMConfigurator;
-import org.apache.log4j.xml.XmlConfigurationFactory;
-import org.apache.logging.log4j.core.Appender;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.spi.LoggerContext;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
import java.util.List;
import java.util.Map;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -41,9 +36,12 @@
*/
public class XmlConfigurationFactoryTest {
+ @BeforeClass
+ public static void beforeClass() {
+ System.setProperty(ConfigurationFactory.LOG4J1_CONFIGURATION_FILE_PROPERTY, "target/test-classes/log4j1-file.xml");
+ }
@Test
public void testXML() throws Exception {
- XmlConfigurationFactory.configure("target/test-classes/log4j1-file.xml");
Logger logger = LogManager.getLogger("test");
logger.debug("This is a test of the root logger");
File file = new File("target/temp.A1");
@@ -54,29 +52,4 @@
assertTrue("File A2 is empty", file.length() > 0);
}
- @Test
- public void testListAppender() {
- XmlConfigurationFactory.configure("target/test-classes/log4j1-list.xml");
- Logger logger = LogManager.getLogger("test");
- logger.debug("This is a test of the root logger");
- LoggerContext loggerContext = org.apache.logging.log4j.LogManager.getContext(false);
- Configuration configuration = ((org.apache.logging.log4j.core.LoggerContext) loggerContext).getConfiguration();
- Map<String, Appender> appenders = configuration.getAppenders();
- ListAppender eventAppender = null;
- ListAppender messageAppender = null;
- for (Map.Entry<String, Appender> entry : appenders.entrySet()) {
- if (entry.getKey().equals("list")) {
- messageAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
- } else if (entry.getKey().equals("events")) {
- eventAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
- }
- }
- assertNotNull("No Event Appender", eventAppender);
- assertNotNull("No Message Appender", messageAppender);
- List<LoggingEvent> events = eventAppender.getEvents();
- assertTrue("No events", events != null && events.size() > 0);
- List<String> messages = messageAppender.getMessages();
- assertTrue("No messages", messages != null && messages.size() > 0);
- }
-
}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.java
new file mode 100644
index 0000000..c4cc360
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.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.log4j.config;
+
+import org.apache.log4j.ListAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.spi.LoggerContextFactory;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test configuration from XML.
+ */
+public class XmlConfigurationTest {
+
+ @Test
+ public void testXML() throws Exception {
+ configure("target/test-classes/log4j1-file.xml");
+ Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ File file = new File("target/temp.A1");
+ assertTrue("File A1 was not created", file.exists());
+ assertTrue("File A1 is empty", file.length() > 0);
+ file = new File("target/temp.A2");
+ assertTrue("File A2 was not created", file.exists());
+ assertTrue("File A2 is empty", file.length() > 0);
+ }
+
+ @Test
+ public void testListAppender() throws Exception {
+ LoggerContext loggerContext = configure("target/test-classes/log4j1-list.xml");
+ Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ Configuration configuration = loggerContext.getConfiguration();
+ Map<String, Appender> appenders = configuration.getAppenders();
+ ListAppender eventAppender = null;
+ ListAppender messageAppender = null;
+ for (Map.Entry<String, Appender> entry : appenders.entrySet()) {
+ if (entry.getKey().equals("list")) {
+ messageAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+ } else if (entry.getKey().equals("events")) {
+ eventAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+ }
+ }
+ assertNotNull("No Event Appender", eventAppender);
+ assertNotNull("No Message Appender", messageAppender);
+ List<LoggingEvent> events = eventAppender.getEvents();
+ assertTrue("No events", events != null && events.size() > 0);
+ List<String> messages = messageAppender.getMessages();
+ assertTrue("No messages", messages != null && messages.size() > 0);
+ }
+
+ private LoggerContext configure(String configLocation) throws Exception {
+ File file = new File(configLocation);
+ InputStream is = new FileInputStream(file);
+ ConfigurationSource source = new ConfigurationSource(is, file);
+ LoggerContextFactory factory = org.apache.logging.log4j.LogManager.getFactory();
+ LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+ Configuration configuration = new XmlConfigurationFactory().getConfiguration(context, source);
+ assertNotNull("No configuration created", configuration);
+ Configurator.reconfigure(configuration);
+ return context;
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/resources/log4j1-async.properties b/log4j-1.2-api/src/test/resources/log4j1-async.properties
new file mode 100644
index 0000000..8e80b46
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-async.properties
@@ -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.
+
+log4j.appender.list=org.apache.log4j.ListAppender
+log4j.appender.list.layout=org.apache.log4j.PatternLayout
+log4j.appender.list.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+log4j.appender.async=org.apache.log4j.AsyncAppender
+log4j.appender.async.appender-ref=list
+log4j.rootLogger=trace, async
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-async.xml b/log4j-1.2-api/src/test/resources/log4j1-async.xml
new file mode 100644
index 0000000..a0cb7f6
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-async.xml
@@ -0,0 +1,35 @@
+<?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.
+-->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+ <appender name="list" class="org.apache.log4j.ListAppender">
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
+ </layout>
+ </appender>
+ <appender name="async" class="org.apache.log4j.AsyncAppender">
+ <appender-ref ref="list"/>
+ </appender>
+
+ <root>
+ <priority value ="trace" />
+ <appender-ref ref="async" />
+ </root>
+
+</log4j:configuration>
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-file.properties b/log4j-1.2-api/src/test/resources/log4j1-file.properties
new file mode 100644
index 0000000..ee870df
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-file.properties
@@ -0,0 +1,31 @@
+# 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.
+
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.Target=System.out
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+log4j.appender.A1=org.apache.log4j.FileAppender
+log4j.appender.A1.File=target/temp.A1
+log4j.appender.A1.Append=false
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+log4j.appender.A1.layout.ConversionPattern=%-5p %c{2} - %m%n
+log4j.appender.A2=org.apache.log4j.FileAppender
+log4j.appender.A2.File=target/temp.A2
+log4j.appender.A2.Append=false
+log4j.appender.A2.layout=org.apache.log4j.TTCCLayout
+log4j.appender.A2.layout.DateFormat=ISO8601
+log4j.logger.org.apache.log4j.xml=trace, A1
+log4j.rootLogger=trace, A1, A2
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-list.properties b/log4j-1.2-api/src/test/resources/log4j1-list.properties
new file mode 100644
index 0000000..43d6208
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-list.properties
@@ -0,0 +1,20 @@
+# 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.
+
+log4j.appender.list=org.apache.log4j.ListAppender
+log4j.appender.list.layout=org.apache.log4j.PatternLayout
+log4j.appender.list.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+log4j.appender.events=org.apache.log4j.ListAppender
+log4j.rootLogger=trace, list, events
\ No newline at end of file
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 17cbbed..6757f04 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
@@ -45,10 +45,10 @@
import org.apache.logging.log4j.core.util.NetUtils;
import org.apache.logging.log4j.plugins.util.PluginManager;
import org.apache.logging.log4j.plugins.util.PluginType;
-import org.apache.logging.log4j.util.ReflectionUtil;
import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.LoaderUtil;
import org.apache.logging.log4j.util.PropertiesUtil;
+import org.apache.logging.log4j.util.ReflectionUtil;
import org.apache.logging.log4j.util.Strings;
/**
@@ -91,6 +91,10 @@
*/
public static final String CONFIGURATION_FILE_PROPERTY = "log4j.configurationFile";
+ public static final String LOG4J1_CONFIGURATION_FILE_PROPERTY = "log4j.configuration";
+
+ public static final String LOG4J1_EXPERIMENTAL = "log4j1.compatibility";
+
public static final String AUTHORIZATION_PROVIDER = "log4j2.authorizationProvider";
/**
@@ -116,6 +120,9 @@
*/
protected static final String DEFAULT_PREFIX = "log4j2";
+ protected static final String LOG4J1_VERSION = "1";
+ protected static final String LOG4J2_VERSION = "2";
+
/**
* The name of the classloader URI scheme.
*/
@@ -137,7 +144,7 @@
private static final String HTTPS = "https";
private static final String HTTP = "http";
- private static AuthorizationProvider authorizationProvider = null;
+ private static volatile AuthorizationProvider authorizationProvider = null;
/**
* Returns the ConfigurationFactory.
@@ -175,7 +182,6 @@
//noinspection NonThreadSafeLazyInitialization
factories = Collections.unmodifiableList(list);
authorizationProvider = authorizationProvider(props);
-
}
} finally {
LOCK.unlock();
@@ -207,7 +213,6 @@
return provider;
}
-
public static AuthorizationProvider getAuthorizationProvider() {
return authorizationProvider;
}
@@ -265,6 +270,10 @@
return DEFAULT_PREFIX;
}
+ protected String getVersion() {
+ return LOG4J2_VERSION;
+ }
+
protected boolean isActive() {
return true;
}
@@ -399,6 +408,13 @@
return new CompositeConfiguration(configs);
}
return getConfiguration(loggerContext, configLocationStr);
+ } else {
+ final String log4j1ConfigStr = this.substitutor.replace(PropertiesUtil.getProperties()
+ .getStringProperty(LOG4J1_CONFIGURATION_FILE_PROPERTY));
+ if (log4j1ConfigStr != null) {
+ System.setProperty(LOG4J1_EXPERIMENTAL, "true");
+ return getConfiguration(LOG4J1_VERSION, loggerContext, log4j1ConfigStr);
+ }
}
for (final ConfigurationFactory factory : getFactories()) {
final String[] types = factory.getSupportedTypes();
@@ -444,7 +460,7 @@
if (config != null) {
return config;
}
- LOGGER.error("No Log4j 2 configuration file found. " +
+ LOGGER.warn("No Log4j 2 configuration file found. " +
"Using default configuration (logging only errors to the console), " +
"or user programmatically provided configurations. " +
"Set system property 'log4j2.debug' " +
@@ -454,6 +470,11 @@
}
private Configuration getConfiguration(final LoggerContext loggerContext, final String configLocationStr) {
+ return getConfiguration(null, loggerContext, configLocationStr);
+ }
+
+ private Configuration getConfiguration(String requiredVersion, final LoggerContext loggerContext,
+ final String configLocationStr) {
ConfigurationSource source = null;
try {
source = ConfigurationSource.fromUri(NetUtils.toURI(configLocationStr));
@@ -467,6 +488,9 @@
}
if (source != null) {
for (final ConfigurationFactory factory : getFactories()) {
+ if (requiredVersion != null && !factory.getVersion().equals(requiredVersion)) {
+ continue;
+ }
final String[] types = factory.getSupportedTypes();
if (types != null) {
for (final String type : types) {
diff --git a/log4j-layout-jackson-xml/src/main/java/org/apache/logging/log4j/jackson/xml/builders/layout/XmlLayoutBuilder.java b/log4j-layout-jackson-xml/src/main/java/org/apache/logging/log4j/jackson/xml/builders/layout/XmlLayoutBuilder.java
index 3175ed2..28f0474 100644
--- a/log4j-layout-jackson-xml/src/main/java/org/apache/logging/log4j/jackson/xml/builders/layout/XmlLayoutBuilder.java
+++ b/log4j-layout-jackson-xml/src/main/java/org/apache/logging/log4j/jackson/xml/builders/layout/XmlLayoutBuilder.java
@@ -18,40 +18,68 @@
import org.apache.log4j.Layout;
import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
import org.apache.log4j.builders.BooleanHolder;
-import org.apache.log4j.builders.BuilderManager;
import org.apache.log4j.builders.Holder;
import org.apache.log4j.builders.layout.LayoutBuilder;
-import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.jackson.xml.layout.XmlLayout;
import org.apache.logging.log4j.plugins.Plugin;
import org.apache.logging.log4j.status.StatusLogger;
import org.w3c.dom.Element;
+import java.util.Properties;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
/**
- * Build a Pattern Layout
+ * Build an XML Layout
*/
-@Plugin(name = "org.apache.log4j.xml.XMLLayout", category = BuilderManager.CATEGORY)
-public class XmlLayoutBuilder implements LayoutBuilder {
+@Plugin(name = "org.apache.log4j.xml.XMLLayout", category = CATEGORY)
+public class XmlLayoutBuilder extends AbstractBuilder implements LayoutBuilder {
private static final Logger LOGGER = StatusLogger.getLogger();
+ private static final String LOCATION_INFO = "LocationInfo";
+ private static final String PROPERTIES = "Properties";
+
+ public XmlLayoutBuilder() {
+ }
+
+ public XmlLayoutBuilder(String prefix, Properties props) {
+ super(prefix, props);
+ }
+
@Override
- public Layout parseLayout(Element layoutElement, XmlConfigurationFactory factory) {
+ public Layout parseLayout(Element layoutElement, XmlConfiguration config) {
final Holder<Boolean> properties = new BooleanHolder();
final Holder<Boolean> locationInfo = new BooleanHolder();
- XmlConfigurationFactory.forEachElement(layoutElement.getElementsByTagName("param"), (currentElement) -> {
- if ("properties".equalsIgnoreCase(currentElement.getAttribute("name"))) {
+ forEachElement(layoutElement.getElementsByTagName(PARAM_TAG), (currentElement) -> {
+ if (PROPERTIES.equalsIgnoreCase(currentElement.getAttribute("name"))) {
properties.set(Boolean.parseBoolean(currentElement.getAttribute("value")));
- } else if ("locationInfo".equalsIgnoreCase(currentElement.getAttribute("name"))) {
+ } else if (LOCATION_INFO.equalsIgnoreCase(currentElement.getAttribute("name"))) {
locationInfo.set(Boolean.parseBoolean(currentElement.getAttribute("value")));
}
});
+ return createLayout(properties.get(), locationInfo.get());
+ }
+
+ @Override
+ public Layout parseLayout(PropertiesConfiguration config) {
+ boolean properties = getBooleanProperty(PROPERTIES);
+ boolean locationInfo = getBooleanProperty(LOCATION_INFO);
+ return createLayout(properties, locationInfo);
+ }
+
+ private Layout createLayout(boolean properties, boolean locationInfo) {
return new LayoutWrapper(XmlLayout.newBuilder()
- .setLocationInfo(locationInfo.get())
- .setProperties(properties.get())
+ .setLocationInfo(locationInfo)
+ .setProperties(properties)
.build());
}
}
diff --git a/src/site/asciidoc/index.adoc b/src/site/asciidoc/index.adoc
index ed6a03c..c2ef444 100644
--- a/src/site/asciidoc/index.adoc
+++ b/src/site/asciidoc/index.adoc
@@ -111,8 +111,9 @@
=== Cloud Enabled
Version 2.12.0 introduces support for accessing Docker container information via a Lookup and for accessing
-and updating the Log4j configuration through Spring Cloud Configuration. See [Logging in the Cloud](manual/cloud.html)
-for details.
+and updating the Log4j configuration through Spring Cloud Configuration. This support was enhanced in
+version 2.13.0 to add support for accessing Spring Boot properties as well as Kubernetes information.
+See [Logging in the Cloud](manual/cloud.html) for details.
== Documentation
diff --git a/src/site/markdown/manual/cloud.md b/src/site/markdown/manual/cloud.md
index 0dd0d17..69020fd 100644
--- a/src/site/markdown/manual/cloud.md
+++ b/src/site/markdown/manual/cloud.md
@@ -224,6 +224,15 @@
Further information regarding integration of the log4j-spring-cloud-config-client can be found at
[Log4j Spring Cloud Config Client](../log4j-spring-cloud-config/log4j-spring-cloud-config-client/index.html).
+## Integration with Spring Boot
+
+Log4j integrates with Spring Boot in 2 ways:
+
+1. A Spring Lookup can be used to access the Spring application configuration from Log4j configuration files.
+2. Log4j will access the Spring configuration when it is trying to resolve log4j system properties.
+
+Both of these require that the log4j-spring-cloud-client jar is included in the application.
+
## Integration with Docker
Applications within a Docker container that log using a Docker logging driver can include special
diff --git a/src/site/markdown/manual/compatibility.md b/src/site/markdown/manual/compatibility.md
new file mode 100644
index 0000000..df0d728
--- /dev/null
+++ b/src/site/markdown/manual/compatibility.md
@@ -0,0 +1,44 @@
+<!-- vim: set syn=markdown : -->
+<!--
+ 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.
+-->
+
+# Log4j 2 Compatibility with Log4j 1
+
+## API Compatibility
+
+Log4j 2 provides support for the Log4j 1 logging methods by providing alternate implementations
+of the classes containing those methods. These classes may be found in the log4j-1.2-api jar
+distributed with the project. All calls to perform logging will result in the data passed to the logging methods
+to be forwarded to the Log4j2 API where they can be processed by implementations of the Log4j 2 API.
+
+## Configuration Compatibility
+
+Log4j 2 provides experimental support for Log4j 1 configuration files. Configuration of the Appenders, Layouts
+and Filters that were provided in the Log4j 1 distribution will be redirected to their Log4j 2 counterparts.
+This means that although the while the behavior of these components will be similar they may not be exactly the
+same. For example, the XML generated by the XMLLayout may not exactly match the XML generated by the Log4j 1
+XMLLayout.
+
+In addition, Log4j 2 supports custom Log4j 1 Appenders, Filters, and Layouts with some constraints. Since the
+original Log4j 1 components are not present in Log4j 2 custom components that extend them will fail.
+
+As support for Log4j 1 is an experimental feature one of the following steps must be taken to enable it:
+
+1. Set the system property "log4j1.compatibility" to a value of "true". Log4j 2 will then add log4j.properties,
+log4j-test.properties, log4j.xml and log4j-test.xml to the configuration files it searches for on the class path.
+1. Set the Log4j 1 system property "log4j.configuration" to the location of the log4j 1 configuration file. The
+files must have a file extension of either ".properties" or ".xml".
\ No newline at end of file
diff --git a/src/site/site.xml b/src/site/site.xml
index 74b047e..bd86e7f 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -86,6 +86,7 @@
<menu name="Manual" inherit="top" img="img/glyphicons/book.png">
<item name="Introduction" href="/manual/index.html"/>
<item name="Architecture" href="/manual/architecture.html"/>
+ <item name="Log4j 1.x Compatibility" href="manual/compatibility.html"/>
<item name="Log4j 1.x Migration" href="manual/migration.html"/>
<item name="Java API" href="/manual/api.html" collapse="true">