]]>
+ and the Apache Log4j logo are trademarks of The Apache Software Foundation.</p>
falsetrue
+ 8
@@ -169,22 +194,9 @@
-
- org.codehaus.mojo
- findbugs-maven-plugin
- ${findbugs.plugin.version}
-
- true
- -Duser.language=en
- Normal
- Default
- ${log4jParentDir}/findbugs-exclude-filter.xml
-
- org.apache.maven.pluginsmaven-jxr-plugin
- ${jxr.plugin.version}non-aggregate
@@ -203,12 +215,14 @@
org.apache.maven.pluginsmaven-pmd-plugin
- ${pmd.plugin.version}${maven.compiler.target}
+
+ com.github.spotbugs
+ spotbugs-maven-plugin
+
-
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/AppenderSkeleton.java b/log4j-1.2-api/src/main/java/org/apache/log4j/AppenderSkeleton.java
index a4c52310c2e..2800107687f 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/AppenderSkeleton.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/AppenderSkeleton.java
@@ -44,11 +44,9 @@ public abstract class AppenderSkeleton implements Appender, OptionHandler {
* Create new instance.
*/
public AppenderSkeleton() {
- super();
}
protected AppenderSkeleton(final boolean isActive) {
- super();
}
@Override
@@ -56,7 +54,7 @@ public void activateOptions() {
}
@Override
- public void addFilter(Filter newFilter) {
+ public void addFilter(final Filter newFilter) {
if(headFilter == null) {
headFilter = tailFilter = newFilter;
} else {
@@ -104,72 +102,70 @@ public Priority getThreshold() {
return threshold;
}
- public boolean isAsSevereAsThreshold(Priority priority) {
+ public boolean isAsSevereAsThreshold(final Priority priority) {
return ((threshold == null) || priority.isGreaterOrEqual(threshold));
}
- /**
- * This method is never going to be called in Log4j 2 so there isn't much point in having any code in it.
- * @param event The LoggingEvent.
- */
@Override
- public void doAppend(LoggingEvent event) {
+ public synchronized void doAppend(final LoggingEvent event) {
+ // Threshold checks and filtering is performed by the AppenderWrapper.
+ append(event);
}
/**
- * Set the {@link ErrorHandler} for this Appender.
+ * Sets the {@link ErrorHandler} for this Appender.
*
* @since 0.9.0
*/
@Override
- public synchronized void setErrorHandler(ErrorHandler eh) {
+ public synchronized void setErrorHandler(final ErrorHandler eh) {
if (eh != null) {
this.errorHandler = eh;
}
}
@Override
- public void setLayout(Layout layout) {
+ public void setLayout(final Layout layout) {
this.layout = layout;
}
@Override
- public void setName(String name) {
+ public void setName(final String name) {
this.name = name;
}
- public void setThreshold(Priority threshold) {
+ public void setThreshold(final Priority threshold) {
this.threshold = threshold;
}
public static class NoOpErrorHandler implements ErrorHandler {
@Override
- public void setLogger(Logger logger) {
+ public void setLogger(final Logger logger) {
}
@Override
- public void error(String message, Exception e, int errorCode) {
+ public void error(final String message, final Exception e, final int errorCode) {
}
@Override
- public void error(String message) {
+ public void error(final String message) {
}
@Override
- public void error(String message, Exception e, int errorCode, LoggingEvent event) {
+ public void error(final String message, final Exception e, final int errorCode, final LoggingEvent event) {
}
@Override
- public void setAppender(Appender appender) {
+ public void setAppender(final Appender appender) {
}
@Override
- public void setBackupAppender(Appender appender) {
+ public void setBackupAppender(final Appender appender) {
}
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/BasicConfigurator.java b/log4j-1.2-api/src/main/java/org/apache/log4j/BasicConfigurator.java
index 2b7ec7fa57b..de422174213 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/BasicConfigurator.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/BasicConfigurator.java
@@ -16,30 +16,50 @@
*/
package org.apache.log4j;
+import org.apache.logging.log4j.util.StackLocatorUtil;
+
/**
- * Provided for compatibility with Log4j 1.x.
+ * Configures the package.
+ *
+ *
+ * For file based configuration, see {@link PropertyConfigurator}. For XML based configuration, see
+ * {@link org.apache.log4j.xml.DOMConfigurator DOMConfigurator}.
+ *
+ *
+ * @since 0.8.1
*/
public class BasicConfigurator {
- protected BasicConfigurator() {
- }
-
+ /**
+ * Adds a {@link ConsoleAppender} that uses {@link PatternLayout} using the
+ * {@link PatternLayout#TTCC_CONVERSION_PATTERN} and prints to System.out to the root category.
+ */
public static void configure() {
- LogManager.reconfigure();
+ LogManager.reconfigure(StackLocatorUtil.getCallerClassLoader(2));
}
/**
- * No-op implementation.
- * @param appender The appender.
+ * Adds appender to the root category.
+ *
+ * @param appender The appender to add to the root category.
*/
public static void configure(final Appender appender) {
- // no-op
+ LogManager.getRootLogger(StackLocatorUtil.getCallerClassLoader(2)).addAppender(appender);
}
/**
- * No-op implementation.
+ * Resets the default hierarchy to its default. It is equivalent to calling
+ * Category.getDefaultHierarchy().resetConfiguration().
+ *
+ * See {@link Hierarchy#resetConfiguration()} for more details.
*/
public static void resetConfiguration() {
- // no-op
+ LogManager.resetConfiguration(StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ /**
+ * Constructs a new instance.
+ */
+ protected BasicConfigurator() {
}
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java b/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java
index 873aa0e3c1a..c35a2cc74e0 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java
@@ -16,157 +16,397 @@
*/
package org.apache.log4j;
+import java.util.Collection;
+import java.util.Collections;
import java.util.Enumeration;
import java.util.Map;
import java.util.ResourceBundle;
-import java.util.WeakHashMap;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.Vector;
import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Collectors;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.LogEventWrapper;
+import org.apache.log4j.helpers.AppenderAttachableImpl;
import org.apache.log4j.helpers.NullEnumeration;
-import org.apache.log4j.spi.LoggerFactory;
+import org.apache.log4j.legacy.core.CategoryUtil;
+import org.apache.log4j.or.ObjectRenderer;
+import org.apache.log4j.or.RendererMap;
+import org.apache.log4j.spi.AppenderAttachable;
+import org.apache.log4j.spi.HierarchyEventListener;
+import org.apache.log4j.spi.LoggerRepository;
import org.apache.log4j.spi.LoggingEvent;
-import org.apache.logging.log4j.core.LoggerContext;
-import org.apache.logging.log4j.core.util.NameUtil;
import org.apache.logging.log4j.message.LocalizedMessage;
+import org.apache.logging.log4j.message.MapMessage;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.ObjectMessage;
-import org.apache.logging.log4j.spi.AbstractLoggerAdapter;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.spi.ExtendedLogger;
+import org.apache.logging.log4j.spi.LoggerContext;
+import org.apache.logging.log4j.util.StackLocatorUtil;
import org.apache.logging.log4j.util.Strings;
-
/**
* Implementation of the Category class for compatibility, despite it having been deprecated a long, long time ago.
*/
-public class Category {
+public class Category implements AppenderAttachable {
- private static PrivateAdapter adapter = new PrivateAdapter();
+ private static final String FQCN = Category.class.getName();
- private static final Map> CONTEXT_MAP =
- new WeakHashMap<>();
+ /**
+ * Tests if the named category exists (in the default hierarchy).
+ *
+ * @param name The name to test.
+ * @return Whether the name exists.
+ *
+ * @deprecated Please use {@link LogManager#exists(String)} instead.
+ * @since 0.8.5
+ */
+ @Deprecated
+ public static Logger exists(final String name) {
+ return LogManager.exists(name, StackLocatorUtil.getCallerClassLoader(2));
+ }
- private static final String FQCN = Category.class.getName();
+ /**
+ * Returns all the currently defined categories in the default hierarchy as an {@link java.util.Enumeration
+ * Enumeration}.
+ *
+ *
+ * The root category is not included in the returned {@link Enumeration}.
+ *
+ *
+ * @return and Enumeration of the Categories.
+ *
+ * @deprecated Please use {@link LogManager#getCurrentLoggers()} instead.
+ */
+ @SuppressWarnings("rawtypes")
+ @Deprecated
+ public static Enumeration getCurrentCategories() {
+ return LogManager.getCurrentLoggers(StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ /**
+ * Gets the default LoggerRepository instance.
+ *
+ * @return the default LoggerRepository instance.
+ * @deprecated Please use {@link LogManager#getLoggerRepository()} instead.
+ * @since 1.0
+ */
+ @Deprecated
+ public static LoggerRepository getDefaultHierarchy() {
+ return LogManager.getLoggerRepository();
+ }
+
+ public static Category getInstance(@SuppressWarnings("rawtypes") final Class clazz) {
+ return LogManager.getLogger(clazz.getName(), StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ public static Category getInstance(final String name) {
+ return LogManager.getLogger(name, StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ public static Category getRoot() {
+ return LogManager.getRootLogger(StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ private static String getSubName(final String name) {
+ if (Strings.isEmpty(name)) {
+ return null;
+ }
+ final int i = name.lastIndexOf('.');
+ return i > 0 ? name.substring(0, i) : Strings.EMPTY;
+ }
+
+ /**
+ * Shuts down the current configuration.
+ */
+ public static void shutdown() {
+ // Depth 2 gets the call site of this method.
+ LogManager.shutdown(StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ /**
+ * The name of this category.
+ */
+ protected String name;
+
+ /**
+ * Additivity is set to true by default, that is children inherit the appenders of their ancestors by default. If this
+ * variable is set to false then the appenders found in the ancestors of this category are not used.
+ * However, the children of this category will inherit its appenders, unless the children have their additivity flag set
+ * to false too. See the user manual for more details.
+ */
+ protected boolean additive = true;
+
+ /**
+ * The assigned level of this category. The level variable need not be assigned a value in which case it is
+ * inherited form the hierarchy.
+ */
+ volatile protected Level level;
+
+ private RendererMap rendererMap;
+
+ /**
+ * The parent of this category. All categories have at least one ancestor which is the root category.
+ */
+ volatile protected Category parent;
/**
* Resource bundle for localized messages.
*/
- protected ResourceBundle bundle = null;
+ protected ResourceBundle bundle;
- private final org.apache.logging.log4j.core.Logger logger;
+ private final org.apache.logging.log4j.Logger logger;
+
+ /** Categories need to know what Hierarchy they are in. */
+ protected LoggerRepository repository;
+
+ AppenderAttachableImpl aai;
/**
* Constructor used by Logger to specify a LoggerContext.
+ *
* @param context The LoggerContext.
* @param name The name of the Logger.
*/
protected Category(final LoggerContext context, final String name) {
+ this.name = name;
this.logger = context.getLogger(name);
+ this.repository = LogManager.getLoggerRepository();
+ // this.rendererMap = ((RendererSupport) repository).getRendererMap();
+ }
+
+ Category(final org.apache.logging.log4j.Logger logger) {
+ this.logger = logger;
+ // rendererMap = ((RendererSupport) LogManager.getLoggerRepository()).getRendererMap();
}
/**
* Constructor exposed by Log4j 1.2.
+ *
* @param name The name of the Logger.
*/
protected Category(final String name) {
- this(PrivateManager.getContext(), name);
+ this(Hierarchy.getContext(), name);
}
- private Category(final org.apache.logging.log4j.core.Logger logger) {
- this.logger = logger;
+ /**
+ * Add newAppender to the list of appenders of this Category instance.
+ *
+ * If newAppender is already in the list of appenders, then it won't be added again.
+ *
+ */
+ @Override
+ public void addAppender(final Appender appender) {
+ if (appender != null) {
+ if (LogManager.isLog4jCorePresent()) {
+ CategoryUtil.addAppender(logger, AppenderAdapter.adapt(appender));
+ } else {
+ synchronized (this) {
+ if (aai == null) {
+ aai = new AppenderAttachableImpl();
+ }
+ aai.addAppender(appender);
+ }
+ }
+ repository.fireAddAppenderEvent(this, appender);
+ }
}
- public static Category getInstance(final String name) {
- return getInstance(PrivateManager.getContext(), name, adapter);
+ /**
+ * If assertion parameter is {@code false}, then logs msg as an {@link #error(Object) error}
+ * statement.
+ *
+ *
+ * The assert method has been renamed to assertLog because assert is a language
+ * reserved word in JDK 1.4.
+ *
+ *
+ * @param assertion The assertion.
+ * @param msg The message to print if assertion is false.
+ *
+ * @since 1.2
+ */
+ public void assertLog(final boolean assertion, final String msg) {
+ if (!assertion) {
+ this.error(msg);
+ }
}
- static Logger getInstance(final LoggerContext context, final String name) {
- return getInstance(context, name, adapter);
+ /**
+ * Call the appenders in the hierrachy starting at this. If no appenders could be found, emit a warning.
+ *
+ * This method calls all the appenders inherited from the hierarchy circumventing any evaluation of whether to log or
+ * not to log the particular log request.
+ *
+ *
+ * @param event the event to log.
+ */
+ public void callAppenders(final LoggingEvent event) {
+ if (LogManager.isLog4jCorePresent()) {
+ CategoryUtil.log(logger, new LogEventWrapper(event));
+ return;
+ }
+ int writes = 0;
+ for (Category c = this; c != null; c = c.parent) {
+ // Protected against simultaneous call to addAppender, removeAppender,...
+ synchronized (c) {
+ if (c.aai != null) {
+ writes += c.aai.appendLoopOnAppenders(event);
+ }
+ if (!c.additive) {
+ break;
+ }
+ }
+ }
+ if (writes == 0) {
+ repository.emitNoAppenderWarning(this);
+ }
}
- static Logger getInstance(final LoggerContext context, final String name, final LoggerFactory factory) {
- final ConcurrentMap loggers = getLoggersMap(context);
- Logger logger = loggers.get(name);
- if (logger != null) {
- return logger;
+ /**
+ * Closes all attached appenders implementing the AppenderAttachable interface.
+ *
+ * @since 1.0
+ */
+ synchronized void closeNestedAppenders() {
+ final Enumeration enumeration = this.getAllAppenders();
+ if (enumeration != null) {
+ while (enumeration.hasMoreElements()) {
+ final Appender a = (Appender) enumeration.nextElement();
+ if (a instanceof AppenderAttachable) {
+ a.close();
+ }
+ }
}
- logger = factory.makeNewLoggerInstance(name);
- final Logger prev = loggers.putIfAbsent(name, logger);
- return prev == null ? logger : prev;
}
- static Logger getInstance(final LoggerContext context, final String name, final PrivateAdapter factory) {
- final ConcurrentMap loggers = getLoggersMap(context);
- Logger logger = loggers.get(name);
- if (logger != null) {
- return logger;
- }
- logger = factory.newLogger(name, context);
- final Logger prev = loggers.putIfAbsent(name, logger);
- return prev == null ? logger : prev;
+ public void debug(final Object message) {
+ maybeLog(FQCN, org.apache.logging.log4j.Level.DEBUG, message, null);
}
- public static Category getInstance(@SuppressWarnings("rawtypes") final Class clazz) {
- return getInstance(clazz.getName());
+ public void debug(final Object message, final Throwable t) {
+ maybeLog(FQCN, org.apache.logging.log4j.Level.DEBUG, message, t);
}
- static Logger getInstance(final LoggerContext context, @SuppressWarnings("rawtypes") final Class clazz) {
- return getInstance(context, clazz.getName());
+ public void error(final Object message) {
+ maybeLog(FQCN, org.apache.logging.log4j.Level.ERROR, message, null);
}
- public final String getName() {
- return logger.getName();
+ public void error(final Object message, final Throwable t) {
+ maybeLog(FQCN, org.apache.logging.log4j.Level.ERROR, message, t);
}
- org.apache.logging.log4j.core.Logger getLogger() {
- return logger;
+ public void fatal(final Object message) {
+ maybeLog(FQCN, org.apache.logging.log4j.Level.FATAL, message, null);
}
- public final Category getParent() {
- final org.apache.logging.log4j.core.Logger parent = logger.getParent();
- if (parent == null) {
- return null;
+ public void fatal(final Object message, final Throwable t) {
+ maybeLog(FQCN, org.apache.logging.log4j.Level.FATAL, message, t);
+ }
+
+ /**
+ * LoggerRepository forgot the fireRemoveAppenderEvent method, if using the stock Hierarchy implementation, then call
+ * its fireRemove. Custom repositories can implement HierarchyEventListener if they want remove notifications.
+ *
+ * @param appender appender, may be null.
+ */
+ private void fireRemoveAppenderEvent(final Appender appender) {
+ if (appender != null) {
+ if (repository instanceof Hierarchy) {
+ ((Hierarchy) repository).fireRemoveAppenderEvent(this, appender);
+ } else if (repository instanceof HierarchyEventListener) {
+ ((HierarchyEventListener) repository).removeAppenderEvent(this, appender);
+ }
}
- final ConcurrentMap loggers = getLoggersMap(logger.getContext());
- final Logger l = loggers.get(parent.getName());
- return l == null ? new Category(parent) : l;
}
- public static Category getRoot() {
- return getInstance(Strings.EMPTY);
+ private static Message createMessage(Object message) {
+ if (message instanceof String) {
+ return new SimpleMessage((String) message);
+ }
+ if (message instanceof CharSequence) {
+ return new SimpleMessage((CharSequence) message);
+ }
+ if (message instanceof Map) {
+ return new MapMessage<>((Map) message);
+ }
+ if (message instanceof Message) {
+ return (Message) message;
+ }
+ return new ObjectMessage(message);
}
- static Logger getRoot(final LoggerContext context) {
- return getInstance(context, Strings.EMPTY);
+ public void forcedLog(final String fqcn, final Priority level, final Object message, final Throwable t) {
+ final org.apache.logging.log4j.Level lvl = level.getVersion2Level();
+ final Message msg = createMessage(message);
+ if (logger instanceof ExtendedLogger) {
+ ((ExtendedLogger) logger).logMessage(fqcn, lvl, null, msg, t);
+ } else {
+ logger.log(lvl, msg, t);
+ }
}
- private static ConcurrentMap getLoggersMap(final LoggerContext context) {
- synchronized (CONTEXT_MAP) {
- ConcurrentMap map = CONTEXT_MAP.get(context);
- if (map == null) {
- map = new ConcurrentHashMap<>();
- CONTEXT_MAP.put(context, map);
+ private ObjectRenderer get(final Class clazz) {
+ ObjectRenderer renderer = null;
+ for (Class super T> c = clazz; c != null; c = c.getSuperclass()) {
+ renderer = rendererMap.get(c);
+ if (renderer != null) {
+ return renderer;
+ }
+ renderer = searchInterfaces(c);
+ if (renderer != null) {
+ return renderer;
}
- return map;
}
+ return null;
}
- /**
- Returns all the currently defined categories in the default
- hierarchy as an {@link java.util.Enumeration Enumeration}.
+ public boolean getAdditivity() {
+ return LogManager.isLog4jCorePresent() ? CategoryUtil.isAdditive(logger) : false;
+ }
-
The root category is not included in the returned
- {@link Enumeration}.
- @return and Enumeration of the Categories.
+ /**
+ * Get all the Log4j 1.x appenders contained in this category as an
+ * {@link Enumeration}. Log4j 2.x appenders are omitted.
+ *
+ * @return Enumeration An enumeration of the appenders in this category.
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public Enumeration getAllAppenders() {
+ if (LogManager.isLog4jCorePresent()) {
+ final Collection appenders = CategoryUtil.getAppenders(logger)
+ .values();
+ return Collections.enumeration(appenders.stream()
+ // omit native Log4j 2.x appenders
+ .filter(AppenderAdapter.Adapter.class::isInstance)
+ .map(AppenderWrapper::adapt)
+ .collect(Collectors.toSet()));
+ }
+ return aai == null ? NullEnumeration.getInstance() : aai.getAllAppenders();
+ }
- @deprecated Please use {@link LogManager#getCurrentLoggers()} instead.
+ /**
+ * Look for the appender named as name.
+ *
+ * Return the appender with that name if in the list. Return null otherwise.
+ *
*/
- @SuppressWarnings("rawtypes")
- @Deprecated
- public static Enumeration getCurrentCategories() {
- return LogManager.getCurrentLoggers();
+ @Override
+ public Appender getAppender(final String name) {
+ if (LogManager.isLog4jCorePresent()) {
+ return AppenderWrapper.adapt(CategoryUtil.getAppenders(logger).get(name));
+ }
+ return aai != null ? aai.getAppender(name) : null;
}
- public final Level getEffectiveLevel() {
+ public Priority getChainedPriority() {
+ return getEffectiveLevel();
+ }
+
+ public Level getEffectiveLevel() {
switch (logger.getLevel().getStandardLevel()) {
case ALL:
return Level.ALL;
@@ -182,307 +422,288 @@ public final Level getEffectiveLevel() {
return Level.ERROR;
case FATAL:
return Level.FATAL;
- case OFF:
- return Level.OFF;
default:
// TODO Should this be an IllegalStateException?
return Level.OFF;
}
}
- public final Priority getChainedPriority() {
- return getEffectiveLevel();
+ /**
+ * Gets the {@link LoggerRepository} where this Category instance is attached.
+ *
+ * @deprecated Please use {@link #getLoggerRepository()} instead.
+ * @since 1.1
+ */
+ @Deprecated
+ public LoggerRepository getHierarchy() {
+ return repository;
}
public final Level getLevel() {
return getEffectiveLevel();
}
- public void setLevel(final Level level) {
- logger.setLevel(org.apache.logging.log4j.Level.toLevel(level.levelStr));
+ private String getLevelStr(final Priority priority) {
+ return priority == null ? null : priority.levelStr;
}
- public final Level getPriority() {
- return getEffectiveLevel();
+ org.apache.logging.log4j.Logger getLogger() {
+ return logger;
}
- public void setPriority(final Priority priority) {
- logger.setLevel(org.apache.logging.log4j.Level.toLevel(priority.levelStr));
+ /**
+ * Gets the {@link LoggerRepository} where this Category is attached.
+ *
+ * @since 1.2
+ */
+ public LoggerRepository getLoggerRepository() {
+ return repository;
}
- public void debug(final Object message) {
- maybeLog(FQCN, org.apache.logging.log4j.Level.DEBUG, message, null);
+ public final String getName() {
+ return logger.getName();
}
- public void debug(final Object message, final Throwable t) {
- maybeLog(FQCN, org.apache.logging.log4j.Level.DEBUG, message, t);
+ public final Category getParent() {
+ if (!LogManager.isLog4jCorePresent()) {
+ return null;
+ }
+ final org.apache.logging.log4j.Logger parent = CategoryUtil.getParent(logger);
+ final LoggerContext loggerContext = CategoryUtil.getLoggerContext(logger);
+ if (parent == null || loggerContext == null) {
+ return null;
+ }
+ final ConcurrentMap loggers = Hierarchy.getLoggersMap(loggerContext);
+ final Logger parentLogger = loggers.get(parent.getName());
+ return parentLogger == null ? new Category(parent) : parentLogger;
}
- public boolean isDebugEnabled() {
- return logger.isDebugEnabled();
+ public final Level getPriority() {
+ return getEffectiveLevel();
}
- public void error(final Object message) {
- maybeLog(FQCN, org.apache.logging.log4j.Level.ERROR, message, null);
+ public ResourceBundle getResourceBundle() {
+ if (bundle != null) {
+ return bundle;
+ }
+ String name = logger.getName();
+ if (LogManager.isLog4jCorePresent()) {
+ final LoggerContext ctx = CategoryUtil.getLoggerContext(logger);
+ if (ctx != null) {
+ final ConcurrentMap loggers = Hierarchy.getLoggersMap(ctx);
+ while ((name = getSubName(name)) != null) {
+ final Logger subLogger = loggers.get(name);
+ if (subLogger != null) {
+ final ResourceBundle rb = subLogger.bundle;
+ if (rb != null) {
+ return rb;
+ }
+ }
+ }
+ }
+ }
+ return null;
}
- public void error(final Object message, final Throwable t) {
- maybeLog(FQCN, org.apache.logging.log4j.Level.ERROR, message, t);
+ public void info(final Object message) {
+ maybeLog(FQCN, org.apache.logging.log4j.Level.INFO, message, null);
}
- public boolean isErrorEnabled() {
- return logger.isErrorEnabled();
+ public void info(final Object message, final Throwable t) {
+ maybeLog(FQCN, org.apache.logging.log4j.Level.INFO, message, t);
}
- public void warn(final Object message) {
- maybeLog(FQCN, org.apache.logging.log4j.Level.WARN, message, null);
+ /**
+ * Is the appender passed as parameter attached to this category?
+ *
+ * @param appender The Appender to add.
+ * @return true if the appender is attached.
+ */
+ @Override
+ public boolean isAttached(final Appender appender) {
+ return aai == null ? false : aai.isAttached(appender);
}
- public void warn(final Object message, final Throwable t) {
- maybeLog(FQCN, org.apache.logging.log4j.Level.WARN, message, t);
+ public boolean isDebugEnabled() {
+ return logger.isDebugEnabled();
}
- public boolean isWarnEnabled() {
- return logger.isWarnEnabled();
+ private boolean isEnabledFor(final org.apache.logging.log4j.Level level) {
+ return logger.isEnabled(level);
}
- public void fatal(final Object message) {
- maybeLog(FQCN, org.apache.logging.log4j.Level.FATAL, message, null);
+ public boolean isEnabledFor(final Priority level) {
+ return isEnabledFor(level.getVersion2Level());
}
- public void fatal(final Object message, final Throwable t) {
- maybeLog(FQCN, org.apache.logging.log4j.Level.FATAL, message, t);
+ public boolean isErrorEnabled() {
+ return logger.isErrorEnabled();
}
public boolean isFatalEnabled() {
return logger.isFatalEnabled();
}
- public void info(final Object message) {
- maybeLog(FQCN, org.apache.logging.log4j.Level.INFO, message, null);
- }
-
- public void info(final Object message, final Throwable t) {
- maybeLog(FQCN, org.apache.logging.log4j.Level.INFO, message, t);
- }
-
public boolean isInfoEnabled() {
return logger.isInfoEnabled();
}
- public void trace(final Object message) {
- maybeLog(FQCN, org.apache.logging.log4j.Level.TRACE, message, null);
- }
-
- public void trace(final Object message, final Throwable t) {
- maybeLog(FQCN, org.apache.logging.log4j.Level.TRACE, message, t);
- }
-
- public boolean isTraceEnabled() {
- return logger.isTraceEnabled();
+ public boolean isWarnEnabled() {
+ return logger.isWarnEnabled();
}
- public boolean isEnabledFor(final Priority level) {
- final org.apache.logging.log4j.Level lvl = org.apache.logging.log4j.Level.toLevel(level.toString());
- return isEnabledFor(lvl);
+ public void l7dlog(final Priority priority, final String key, final Object[] params, final Throwable t) {
+ if (isEnabledFor(priority)) {
+ final Message msg = new LocalizedMessage(bundle, key, params);
+ forcedLog(FQCN, priority, msg, t);
+ }
}
- /**
- * No-op implementation.
- * @param appender The Appender to add.
- */
- public void addAppender(final Appender appender) {
+ public void l7dlog(final Priority priority, final String key, final Throwable t) {
+ if (isEnabledFor(priority)) {
+ final Message msg = new LocalizedMessage(bundle, key, null);
+ forcedLog(FQCN, priority, msg, t);
+ }
}
- /**
- * No-op implementation.
- * @param event The logging event.
- */
- public void callAppenders(final LoggingEvent event) {
+ public void log(final Priority priority, final Object message) {
+ if (isEnabledFor(priority)) {
+ forcedLog(FQCN, priority, message, null);
+ }
}
- @SuppressWarnings("rawtypes")
- public Enumeration getAllAppenders() {
- return NullEnumeration.getInstance();
+ public void log(final Priority priority, final Object message, final Throwable t) {
+ if (isEnabledFor(priority)) {
+ forcedLog(FQCN, priority, message, t);
+ }
}
- /**
- * No-op implementation.
- * @param name The name of the Appender.
- * @return null.
- */
- public Appender getAppender(final String name) {
- return null;
+ public void log(final String fqcn, final Priority priority, final Object message, final Throwable t) {
+ if (isEnabledFor(priority)) {
+ forcedLog(fqcn, priority, message, t);
+ }
}
- /**
- Is the appender passed as parameter attached to this category?
- * @param appender The Appender to add.
- * @return true if the appender is attached.
- */
- public boolean isAttached(final Appender appender) {
- return false;
+ void maybeLog(final String fqcn, final org.apache.logging.log4j.Level level, final Object message, final Throwable throwable) {
+ if (logger.isEnabled(level)) {
+ final Message msg = createMessage(message);
+ if (logger instanceof ExtendedLogger) {
+ ((ExtendedLogger) logger).logMessage(fqcn, level, null, msg, throwable);
+ } else {
+ logger.log(level, msg, throwable);
+ }
+ }
}
/**
- * No-op implementation.
+ * Removes all previously added appenders from this Category instance.
+ *
+ * This is useful when re-reading configuration information.
+ *
*/
+ @Override
public void removeAllAppenders() {
+ if (aai != null) {
+ final Vector appenders = new Vector();
+ for (final Enumeration iter = aai.getAllAppenders(); iter != null && iter.hasMoreElements();) {
+ appenders.add(iter.nextElement());
+ }
+ aai.removeAllAppenders();
+ for (final Object appender : appenders) {
+ fireRemoveAppenderEvent((Appender) appender);
+ }
+ aai = null;
+ }
}
/**
- * No-op implementation.
+ * Removes the appender passed as parameter form the list of appenders.
+ *
* @param appender The Appender to remove.
+ * @since 0.8.2
*/
+ @Override
public void removeAppender(final Appender appender) {
+ if (appender == null || aai == null) {
+ return;
+ }
+ final boolean wasAttached = aai.isAttached(appender);
+ aai.removeAppender(appender);
+ if (wasAttached) {
+ fireRemoveAppenderEvent(appender);
+ }
}
/**
- * No-op implementation.
+ * Removes the appender with the name passed as parameter form the list of appenders.
+ *
* @param name The Appender to remove.
+ * @since 0.8.2
*/
+ @Override
public void removeAppender(final String name) {
- }
-
- /**
- * No-op implementation.
- */
- public static void shutdown() {
- }
-
-
- public void forcedLog(final String fqcn, final Priority level, final Object message, final Throwable t) {
- final org.apache.logging.log4j.Level lvl = org.apache.logging.log4j.Level.toLevel(level.toString());
- final Message msg = message instanceof Message ? (Message) message : new ObjectMessage(message);
- logger.logMessage(fqcn, lvl, null, msg, t);
- }
-
- public boolean exists(final String name) {
- return PrivateManager.getContext().hasLogger(name);
- }
-
- public boolean getAdditivity() {
- return logger.isAdditive();
- }
-
- public void setAdditivity(final boolean additivity) {
- logger.setAdditive(additivity);
- }
-
- public void setResourceBundle(final ResourceBundle bundle) {
- this.bundle = bundle;
- }
-
- public ResourceBundle getResourceBundle() {
- if (bundle != null) {
- return bundle;
+ if (name == null || aai == null) {
+ return;
}
- String name = logger.getName();
- final ConcurrentMap loggers = getLoggersMap(logger.getContext());
- while ((name = NameUtil.getSubName(name)) != null) {
- final Logger subLogger = loggers.get(name);
- if (subLogger != null) {
- final ResourceBundle rb = subLogger.bundle;
- if (rb != null) {
- return rb;
- }
- }
+ final Appender appender = aai.getAppender(name);
+ aai.removeAppender(name);
+ if (appender != null) {
+ fireRemoveAppenderEvent(appender);
}
- return null;
}
- /**
- If assertion parameter is {@code false}, then
- logs msg as an {@link #error(Object) error} statement.
-
-
The assert method has been renamed to
- assertLog because assert is a language
- reserved word in JDK 1.4.
-
- @param assertion The assertion.
- @param msg The message to print if assertion is
- false.
-
- @since 1.2
- */
- public void assertLog(final boolean assertion, final String msg) {
- if (!assertion) {
- this.error(msg);
+ ObjectRenderer searchInterfaces(final Class> c) {
+ ObjectRenderer renderer = rendererMap.get(c);
+ if (renderer != null) {
+ return renderer;
}
- }
-
- public void l7dlog(final Priority priority, final String key, final Throwable t) {
- if (isEnabledFor(priority)) {
- final Message msg = new LocalizedMessage(bundle, key, null);
- forcedLog(FQCN, priority, msg, t);
+ final Class>[] ia = c.getInterfaces();
+ for (final Class> clazz : ia) {
+ renderer = searchInterfaces(clazz);
+ if (renderer != null) {
+ return renderer;
+ }
}
+ return null;
}
- public void l7dlog(final Priority priority, final String key, final Object[] params, final Throwable t) {
- if (isEnabledFor(priority)) {
- final Message msg = new LocalizedMessage(bundle, key, params);
- forcedLog(FQCN, priority, msg, t);
+ public void setAdditivity(final boolean additivity) {
+ if (LogManager.isLog4jCorePresent()) {
+ CategoryUtil.setAdditivity(logger, additivity);
}
}
- public void log(final Priority priority, final Object message, final Throwable t) {
- if (isEnabledFor(priority)) {
- final Message msg = new ObjectMessage(message);
- forcedLog(FQCN, priority, msg, t);
- }
+ /**
+ * Only the Hiearchy class can set the hiearchy of a category. Default package access is MANDATORY here.
+ */
+ final void setHierarchy(final LoggerRepository repository) {
+ this.repository = repository;
}
- public void log(final Priority priority, final Object message) {
- if (isEnabledFor(priority)) {
- final Message msg = new ObjectMessage(message);
- forcedLog(FQCN, priority, msg, null);
- }
+ public void setLevel(final Level level) {
+ setLevel(level != null ? level.getVersion2Level() : null);
}
- public void log(final String fqcn, final Priority priority, final Object message, final Throwable t) {
- if (isEnabledFor(priority)) {
- final Message msg = new ObjectMessage(message);
- forcedLog(fqcn, priority, msg, t);
+ private void setLevel(final org.apache.logging.log4j.Level level) {
+ if (LogManager.isLog4jCorePresent()) {
+ CategoryUtil.setLevel(logger, level);
}
}
- private void maybeLog(final String fqcn, final org.apache.logging.log4j.Level level,
- final Object message, final Throwable throwable) {
- if (logger.isEnabled(level, null, message, throwable)) {
- logger.logMessage(FQCN, level, null, new ObjectMessage(message), throwable);
- }
+ public void setPriority(final Priority priority) {
+ setLevel(priority != null ? priority.getVersion2Level() : null);
}
- private static class PrivateAdapter extends AbstractLoggerAdapter {
-
- @Override
- protected Logger newLogger(final String name, final org.apache.logging.log4j.spi.LoggerContext context) {
- return new Logger((LoggerContext) context, name);
- }
-
- @Override
- protected org.apache.logging.log4j.spi.LoggerContext getContext() {
- return PrivateManager.getContext();
- }
+ public void setResourceBundle(final ResourceBundle bundle) {
+ this.bundle = bundle;
}
- /**
- * Private LogManager.
- */
- private static class PrivateManager extends org.apache.logging.log4j.LogManager {
- private static final String FQCN = Category.class.getName();
-
- public static LoggerContext getContext() {
- return (LoggerContext) getContext(FQCN, false);
- }
-
- public static org.apache.logging.log4j.Logger getLogger(final String name) {
- return getLogger(FQCN, name);
- }
+ public void warn(final Object message) {
+ maybeLog(FQCN, org.apache.logging.log4j.Level.WARN, message, null);
}
- private boolean isEnabledFor(final org.apache.logging.log4j.Level level) {
- return logger.isEnabled(level, null, null);
+ public void warn(final Object message, final Throwable t) {
+ maybeLog(FQCN, org.apache.logging.log4j.Level.WARN, message, t);
}
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/CategoryKey.java b/log4j-1.2-api/src/main/java/org/apache/log4j/CategoryKey.java
new file mode 100644
index 00000000000..048c6b1ca9d
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/CategoryKey.java
@@ -0,0 +1,50 @@
+/*
+ * 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;
+
+/**
+ * CategoryKey is a wrapper for String that apparently accelerated hash table lookup in early JVM's.
+ */
+class CategoryKey {
+
+ String name;
+ int hashCache;
+
+ CategoryKey(final String name) {
+ this.name = name;
+ this.hashCache = name.hashCode();
+ }
+
+ @Override
+ final public int hashCode() {
+ return hashCache;
+ }
+
+ @Override
+ final public boolean equals(final Object rArg) {
+ if (this == rArg) {
+ return true;
+ }
+
+ if (rArg != null && CategoryKey.class == rArg.getClass()) {
+ return name.equals(((CategoryKey) rArg).name);
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/ConsoleAppender.java b/log4j-1.2-api/src/main/java/org/apache/log4j/ConsoleAppender.java
new file mode 100644
index 00000000000..fb9c48ce938
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/ConsoleAppender.java
@@ -0,0 +1,140 @@
+/*
+ * 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;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * Placeholder for Log4j 1.2 Console Appender.
+ */
+public class ConsoleAppender extends WriterAppender {
+
+ public static final String SYSTEM_OUT = "System.out";
+ public static final String SYSTEM_ERR = "System.err";
+
+ protected String target = SYSTEM_OUT;
+
+ /**
+ * Determines if the appender honors reassignments of System.out or System.err made after configuration.
+ */
+ private boolean follow;
+
+ /**
+ * Constructs a non-configured appender.
+ */
+ public ConsoleAppender() {
+ }
+
+ /**
+ * Constructs a configured appender.
+ *
+ * @param layout layout, may not be null.
+ */
+ public ConsoleAppender(final Layout layout) {
+ this(layout, SYSTEM_OUT);
+ }
+
+ /**
+ * Constructs a configured appender.
+ *
+ * @param layout layout, may not be null.
+ * @param target target, either "System.err" or "System.out".
+ */
+ public ConsoleAppender(final Layout layout, final String target) {
+ setLayout(layout);
+ setTarget(target);
+ activateOptions();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void append(final LoggingEvent theEvent) {
+ // NOOP
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void close() {
+ // NOOP
+ }
+
+ /**
+ * Gets whether the appender honors reassignments of System.out or System.err made after configuration.
+ *
+ * @return true if appender will use value of System.out or System.err in force at the time when logging events are
+ * appended.
+ * @since 1.2.13
+ */
+ public boolean getFollow() {
+ return follow;
+ }
+
+ /**
+ * Gets the current value of the Target property. The default value of the option is "System.out".
+ *
+ * See also {@link #setTarget}.
+ */
+ public String getTarget() {
+ return target;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean requiresLayout() {
+ return false;
+ }
+
+ /**
+ * Sets whether the appender honors reassignments of System.out or System.err made after configuration.
+ *
+ * @param follow if true, appender will use value of System.out or System.err in force at the time when logging events
+ * are appended.
+ * @since 1.2.13
+ */
+ public void setFollow(boolean follow) {
+ this.follow = follow;
+ }
+
+ /**
+ * Sets the value of the Target option. Recognized values are "System.out" and "System.err". Any other value will
+ * be ignored.
+ */
+ public void setTarget(final String value) {
+ final String v = value.trim();
+
+ if (SYSTEM_OUT.equalsIgnoreCase(v)) {
+ target = SYSTEM_OUT;
+ } else if (SYSTEM_ERR.equalsIgnoreCase(v)) {
+ target = SYSTEM_ERR;
+ } else {
+ targetWarn(value);
+ }
+ }
+
+ void targetWarn(final String val) {
+ StatusLogger.getLogger().warn("[" + val + "] should be System.out or System.err.");
+ StatusLogger.getLogger().warn("Using previously set target, System.out by default.");
+ }
+
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/DefaultCategoryFactory.java b/log4j-1.2-api/src/main/java/org/apache/log4j/DefaultCategoryFactory.java
new file mode 100644
index 00000000000..cb443eadbd5
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/DefaultCategoryFactory.java
@@ -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.
+ */
+
+package org.apache.log4j;
+
+import org.apache.log4j.spi.LoggerFactory;
+
+class DefaultCategoryFactory implements LoggerFactory {
+
+ DefaultCategoryFactory() {
+ }
+
+ @Override
+ public Logger makeNewLoggerInstance(final String name) {
+ return new Logger(name);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/DefaultThrowableRenderer.java b/log4j-1.2-api/src/main/java/org/apache/log4j/DefaultThrowableRenderer.java
new file mode 100644
index 00000000000..1913f9c34db
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/DefaultThrowableRenderer.java
@@ -0,0 +1,84 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.LineNumberReader;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
+
+import org.apache.log4j.spi.ThrowableRenderer;
+
+/**
+ * Default implementation of {@link ThrowableRenderer} using {@link Throwable#printStackTrace(PrintWriter)}.
+ *
+ * @since 1.2.16
+ */
+public final class DefaultThrowableRenderer implements ThrowableRenderer {
+
+ /**
+ * Render throwable using Throwable.printStackTrace.
+ *
+ * @param throwable throwable, may not be null.
+ * @return string representation.
+ */
+ public static String[] render(final Throwable throwable) {
+ final StringWriter sw = new StringWriter();
+ final PrintWriter pw = new PrintWriter(sw);
+ try {
+ throwable.printStackTrace(pw);
+ } catch (final RuntimeException ex) {
+ // ignore
+ }
+ pw.flush();
+ final LineNumberReader reader = new LineNumberReader(new StringReader(sw.toString()));
+ final ArrayList lines = new ArrayList<>();
+ try {
+ String line = reader.readLine();
+ while (line != null) {
+ lines.add(line);
+ line = reader.readLine();
+ }
+ } catch (final IOException ex) {
+ if (ex instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ lines.add(ex.toString());
+ }
+ final String[] tempRep = new String[lines.size()];
+ lines.toArray(tempRep);
+ return tempRep;
+ }
+
+ /**
+ * Construct new instance.
+ */
+ public DefaultThrowableRenderer() {
+ // empty
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String[] doRender(final Throwable throwable) {
+ return render(throwable);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/FileAppender.java b/log4j-1.2-api/src/main/java/org/apache/log4j/FileAppender.java
new file mode 100644
index 00000000000..40f46a9b779
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/FileAppender.java
@@ -0,0 +1,306 @@
+/*
+ * 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;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.Writer;
+
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.helpers.QuietWriter;
+import org.apache.log4j.spi.ErrorCode;
+
+/**
+ * FileAppender appends log events to a file.
+ *
+ * Support for java.io.Writer and console appending has been deprecated and then removed. See the
+ * replacement solutions: {@link WriterAppender} and {@link ConsoleAppender}.
+ *
+ */
+public class FileAppender extends WriterAppender {
+
+ /**
+ * Controls file truncatation. The default value for this variable is true, meaning that by default a
+ * FileAppender will append to an existing file and not truncate it.
+ *
+ * This option is meaningful only if the FileAppender opens the file.
+ *
+ */
+ protected boolean fileAppend = true;
+
+ /**
+ * The name of the log file.
+ */
+ protected String fileName = null;
+
+ /**
+ * Do we do bufferedIO?
+ */
+ protected boolean bufferedIO = false;
+
+ /**
+ * Determines the size of IO buffer be. Default is 8K.
+ */
+ protected int bufferSize = 8 * 1024;
+
+ /**
+ * The default constructor does not do anything.
+ */
+ public FileAppender() {
+ }
+
+ /**
+ * Constructs a FileAppender and open the file designated by filename. The opened filename will become the
+ * output destination for this appender.
+ *
+ * The file will be appended to.
+ *
+ */
+ public FileAppender(Layout layout, String filename) throws IOException {
+ this(layout, filename, true);
+ }
+
+ /**
+ * Constructs a FileAppender and open the file designated by filename. The opened filename will become the
+ * output destination for this appender.
+ *
+ * If the append parameter is true, the file will be appended to. Otherwise, the file designated by
+ * filename will be truncated before being opened.
+ *
+ */
+ public FileAppender(Layout layout, String filename, boolean append) throws IOException {
+ this.layout = layout;
+ this.setFile(filename, append, false, bufferSize);
+ }
+
+ /**
+ * Constructs a FileAppender and open the file designated by filename. The opened filename
+ * will become the output destination for this appender.
+ *
+ * If the append parameter is true, the file will be appended to. Otherwise, the file designated by
+ * filename will be truncated before being opened.
+ *
+ *
+ * If the bufferedIO parameter is true, then buffered IO will be used to write to the output
+ * file.
+ *
+ */
+ public FileAppender(Layout layout, String filename, boolean append, boolean bufferedIO, int bufferSize) throws IOException {
+ this.layout = layout;
+ this.setFile(filename, append, bufferedIO, bufferSize);
+ }
+
+ /**
+ * If the value of File is not null, then {@link #setFile} is called with the values of File
+ * and Append properties.
+ *
+ * @since 0.8.1
+ */
+ public void activateOptions() {
+ if (fileName != null) {
+ try {
+ setFile(fileName, fileAppend, bufferedIO, bufferSize);
+ } catch (java.io.IOException e) {
+ errorHandler.error("setFile(" + fileName + "," + fileAppend + ") call failed.", e, ErrorCode.FILE_OPEN_FAILURE);
+ }
+ } else {
+ // LogLog.error("File option not set for appender ["+name+"].");
+ LogLog.warn("File option not set for appender [" + name + "].");
+ LogLog.warn("Are you using FileAppender instead of ConsoleAppender?");
+ }
+ }
+
+ /**
+ * Closes the previously opened file.
+ */
+ protected void closeFile() {
+ if (this.qw != null) {
+ try {
+ this.qw.close();
+ } catch (java.io.IOException e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ // Exceptionally, it does not make sense to delegate to an
+ // ErrorHandler. Since a closed appender is basically dead.
+ LogLog.error("Could not close " + qw, e);
+ }
+ }
+ }
+
+ /**
+ * Returns the value of the Append option.
+ */
+ public boolean getAppend() {
+ return fileAppend;
+ }
+
+ /**
+ * Get the value of the BufferedIO option.
+ *
+ *
+ * BufferedIO will significatnly increase performance on heavily loaded systems.
+ *
+ */
+ public boolean getBufferedIO() {
+ return this.bufferedIO;
+ }
+
+ /**
+ * Get the size of the IO buffer.
+ */
+ public int getBufferSize() {
+ return this.bufferSize;
+ }
+
+ /** Returns the value of the File option. */
+ public String getFile() {
+ return fileName;
+ }
+
+ /**
+ * Close any previously opened file and call the parent's reset.
+ */
+ protected void reset() {
+ closeFile();
+ this.fileName = null;
+ super.reset();
+ }
+
+ /**
+ * The Append option takes a boolean value. It is set to true by default. If true, then
+ * File will be opened in append mode by {@link #setFile setFile} (see above). Otherwise, {@link #setFile
+ * setFile} will open File in truncate mode.
+ *
+ *
+ * Note: Actual opening of the file is made when {@link #activateOptions} is called, not when the options are set.
+ *
+ */
+ public void setAppend(boolean flag) {
+ fileAppend = flag;
+ }
+
+ /**
+ * The BufferedIO option takes a boolean value. It is set to false by default. If true, then
+ * File will be opened and the resulting {@link java.io.Writer} wrapped around a {@link BufferedWriter}.
+ *
+ * BufferedIO will significatnly increase performance on heavily loaded systems.
+ *
+ */
+ public void setBufferedIO(boolean bufferedIO) {
+ this.bufferedIO = bufferedIO;
+ if (bufferedIO) {
+ immediateFlush = false;
+ }
+ }
+
+ /**
+ * Set the size of the IO buffer.
+ */
+ public void setBufferSize(int bufferSize) {
+ this.bufferSize = bufferSize;
+ }
+
+ /**
+ * The File property takes a string value which should be the name of the file to append to.
+ *
+ * Note that the special values "System.out" or "System.err" are no longer honored.
+ *
+ *
+ * Note: Actual opening of the file is made when {@link #activateOptions} is called, not when the options are set.
+ *
+ */
+ public void setFile(String file) {
+ // Trim spaces from both ends. The users probably does not want
+ // trailing spaces in file names.
+ String val = file.trim();
+ fileName = val;
+ }
+
+ /**
+ * Sets and opens the file where the log output will go. The specified file must be writable.
+ *
+ * If there was already an opened file, then the previous file is closed first.
+ *
+ *
+ * Do not use this method directly. To configure a FileAppender or one of its subclasses, set its properties one by
+ * one and then call activateOptions.
+ *
+ *
+ * @param fileName The path to the log file.
+ * @param append If true will append to fileName. Otherwise will truncate fileName.
+ */
+ public synchronized void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize) throws IOException {
+ LogLog.debug("setFile called: " + fileName + ", " + append);
+
+ // It does not make sense to have immediate flush and bufferedIO.
+ if (bufferedIO) {
+ setImmediateFlush(false);
+ }
+
+ reset();
+ FileOutputStream ostream = null;
+ try {
+ //
+ // attempt to create file
+ //
+ ostream = new FileOutputStream(fileName, append);
+ } catch (FileNotFoundException ex) {
+ //
+ // if parent directory does not exist then
+ // attempt to create it and try to create file
+ // see bug 9150
+ //
+ String parentName = new File(fileName).getParent();
+ if (parentName != null) {
+ File parentDir = new File(parentName);
+ if (!parentDir.exists() && parentDir.mkdirs()) {
+ ostream = new FileOutputStream(fileName, append);
+ } else {
+ throw ex;
+ }
+ } else {
+ throw ex;
+ }
+ }
+ Writer fw = createWriter(ostream);
+ if (bufferedIO) {
+ fw = new BufferedWriter(fw, bufferSize);
+ }
+ this.setQWForFiles(fw);
+ this.fileName = fileName;
+ this.fileAppend = append;
+ this.bufferedIO = bufferedIO;
+ this.bufferSize = bufferSize;
+ writeHeader();
+ LogLog.debug("setFile ended");
+ }
+
+ /**
+ * Sets the quiet writer being used.
+ *
+ * This method is overriden by {@link RollingFileAppender}.
+ */
+ protected void setQWForFiles(Writer writer) {
+ this.qw = new QuietWriter(writer, errorHandler);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/Hierarchy.java b/log4j-1.2-api/src/main/java/org/apache/log4j/Hierarchy.java
new file mode 100644
index 00000000000..a40b33bd539
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/Hierarchy.java
@@ -0,0 +1,606 @@
+/*
+ * 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;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.legacy.core.ContextUtil;
+import org.apache.log4j.or.ObjectRenderer;
+import org.apache.log4j.or.RendererMap;
+import org.apache.log4j.spi.HierarchyEventListener;
+import org.apache.log4j.spi.LoggerFactory;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.RendererSupport;
+import org.apache.log4j.spi.ThrowableRenderer;
+import org.apache.log4j.spi.ThrowableRendererSupport;
+import org.apache.logging.log4j.core.appender.AsyncAppender;
+import org.apache.logging.log4j.spi.AbstractLoggerAdapter;
+import org.apache.logging.log4j.spi.LoggerContext;
+import org.apache.logging.log4j.util.StackLocatorUtil;
+
+/**
+ * This class is specialized in retrieving loggers by name and also maintaining the logger hierarchy.
+ *
+ *
+ * The casual user does not have to deal with this class directly.
+ *
+ *
+ * The structure of the logger hierarchy is maintained by the {@link #getLogger} method. The hierarchy is such that
+ * children link to their parent but parents do not have any pointers to their children. Moreover, loggers can be
+ * instantiated in any order, in particular descendant before ancestor.
+ *
+ *
+ * In case a descendant is created before a particular ancestor, then it creates a provision node for the ancestor and
+ * adds itself to the provision node. Other descendants of the same ancestor add themselves to the previously created
+ * provision node.
+ *
+ */
+public class Hierarchy implements LoggerRepository, RendererSupport, ThrowableRendererSupport {
+
+ private static class PrivateLoggerAdapter extends AbstractLoggerAdapter {
+
+ @Override
+ protected org.apache.logging.log4j.spi.LoggerContext getContext() {
+ return PrivateLogManager.getContext();
+ }
+
+ @Override
+ protected Logger newLogger(final String name, final org.apache.logging.log4j.spi.LoggerContext context) {
+ return new Logger(context, name);
+ }
+ }
+
+ /**
+ * Private LogManager.
+ */
+ private static class PrivateLogManager extends org.apache.logging.log4j.LogManager {
+ private static final String FQCN = Hierarchy.class.getName();
+
+ public static LoggerContext getContext() {
+ return getContext(FQCN, false);
+ }
+
+ public static org.apache.logging.log4j.Logger getLogger(final String name) {
+ return getLogger(FQCN, name);
+ }
+ }
+
+ private static final PrivateLoggerAdapter LOGGER_ADAPTER = new PrivateLoggerAdapter();
+
+ private static final WeakHashMap> CONTEXT_MAP = new WeakHashMap<>();
+
+ static LoggerContext getContext() {
+ return PrivateLogManager.getContext();
+ }
+
+ static Logger getInstance(final LoggerContext context, final String name) {
+ return getInstance(context, name, LOGGER_ADAPTER);
+ }
+
+ static Logger getInstance(final LoggerContext context, final String name, final LoggerFactory factory) {
+ return getLoggersMap(context).computeIfAbsent(name, k -> factory.makeNewLoggerInstance(name));
+ }
+
+ static Logger getInstance(final LoggerContext context, final String name, final PrivateLoggerAdapter factory) {
+ return getLoggersMap(context).computeIfAbsent(name, k -> factory.newLogger(name, context));
+ }
+
+ static ConcurrentMap getLoggersMap(final LoggerContext context) {
+ synchronized (CONTEXT_MAP) {
+ return CONTEXT_MAP.computeIfAbsent(context, k -> new ConcurrentHashMap<>());
+ }
+ }
+
+ static Logger getRootLogger(final LoggerContext context) {
+ return getInstance(context, org.apache.logging.log4j.LogManager.ROOT_LOGGER_NAME);
+ }
+
+ private final LoggerFactory defaultFactory;
+ private final Vector listeners;
+ Hashtable ht;
+ Logger root;
+ RendererMap rendererMap;
+ int thresholdInt;
+ Level threshold;
+ boolean emittedNoAppenderWarning;
+
+ boolean emittedNoResourceBundleWarning;
+
+ private ThrowableRenderer throwableRenderer;
+
+ /**
+ * Creates a new logger hierarchy.
+ *
+ * @param root The root of the new hierarchy.
+ *
+ */
+ public Hierarchy(final Logger root) {
+ ht = new Hashtable();
+ listeners = new Vector(1);
+ this.root = root;
+ // Enable all level levels by default.
+ setThreshold(Level.ALL);
+ this.root.setHierarchy(this);
+ rendererMap = new RendererMap();
+ defaultFactory = new DefaultCategoryFactory();
+ }
+
+ @Override
+ public void addHierarchyEventListener(final HierarchyEventListener listener) {
+ if (listeners.contains(listener)) {
+ LogLog.warn("Ignoring attempt to add an existent listener.");
+ } else {
+ listeners.addElement(listener);
+ }
+ }
+
+ /**
+ * Adds an object renderer for a specific class.
+ */
+ public void addRenderer(final Class classToRender, final ObjectRenderer or) {
+ rendererMap.put(classToRender, or);
+ }
+
+ /**
+ * This call will clear all logger definitions from the internal hashtable. Invoking this method will irrevocably mess
+ * up the logger hierarchy.
+ *
+ *
+ * You should really know what you are doing before invoking this method.
+ *
+ *
+ * @since 0.9.0
+ */
+ public void clear() {
+ // System.out.println("\n\nAbout to clear internal hash table.");
+ ht.clear();
+ getLoggersMap(getContext()).clear();
+ }
+
+ @Override
+ public void emitNoAppenderWarning(final Category cat) {
+ // No appenders in hierarchy, warn user only once.
+ if (!this.emittedNoAppenderWarning) {
+ LogLog.warn("No appenders could be found for logger (" + cat.getName() + ").");
+ LogLog.warn("Please initialize the log4j system properly.");
+ LogLog.warn("See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.");
+ this.emittedNoAppenderWarning = true;
+ }
+ }
+
+ /**
+ * Tests if the named logger exists in the hierarchy. If so return its reference, otherwise returns null.
+ *
+ * @param name The name of the logger to search for.
+ *
+ */
+ @Override
+ public Logger exists(final String name) {
+ return exists(name, getContext());
+ }
+
+ Logger exists(final String name, final ClassLoader classLoader) {
+ return exists(name, getContext(classLoader));
+ }
+
+ Logger exists(final String name, final LoggerContext loggerContext) {
+ if (!loggerContext.hasLogger(name)) {
+ return null;
+ }
+ return Logger.getLogger(name);
+ }
+
+ @Override
+ public void fireAddAppenderEvent(final Category logger, final Appender appender) {
+ if (listeners != null) {
+ final int size = listeners.size();
+ HierarchyEventListener listener;
+ for (int i = 0; i < size; i++) {
+ listener = (HierarchyEventListener) listeners.elementAt(i);
+ listener.addAppenderEvent(logger, appender);
+ }
+ }
+ }
+
+ void fireRemoveAppenderEvent(final Category logger, final Appender appender) {
+ if (listeners != null) {
+ final int size = listeners.size();
+ HierarchyEventListener listener;
+ for (int i = 0; i < size; i++) {
+ listener = (HierarchyEventListener) listeners.elementAt(i);
+ listener.removeAppenderEvent(logger, appender);
+ }
+ }
+ }
+
+ LoggerContext getContext(final ClassLoader classLoader) {
+ return LogManager.getContext(classLoader);
+ }
+
+ /**
+ * @deprecated Please use {@link #getCurrentLoggers} instead.
+ */
+ @Deprecated
+ @Override
+ public Enumeration getCurrentCategories() {
+ return getCurrentLoggers();
+ }
+
+ /**
+ * Gets all the currently defined categories in this hierarchy as an {@link java.util.Enumeration Enumeration}.
+ *
+ *
+ * The root logger is not included in the returned {@link Enumeration}.
+ *
+ */
+ @Override
+ public Enumeration getCurrentLoggers() {
+ // The accumlation in v is necessary because not all elements in
+ // ht are Logger objects as there might be some ProvisionNodes
+ // as well.
+// final Vector v = new Vector(ht.size());
+//
+// final Enumeration elems = ht.elements();
+// while (elems.hasMoreElements()) {
+// final Object o = elems.nextElement();
+// if (o instanceof Logger) {
+// v.addElement(o);
+// }
+// }
+// return v.elements();
+
+ return LogManager.getCurrentLoggers(StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ /**
+ * Gets a new logger instance named as the first parameter using the default factory.
+ *
+ *
+ * If a logger of that name already exists, then it will be returned. Otherwise, a new logger will be instantiated and
+ * then linked with its existing ancestors as well as children.
+ *
+ *
+ * @param name The name of the logger to retrieve.
+ *
+ */
+ @Override
+ public Logger getLogger(final String name) {
+ return getInstance(getContext(), name);
+ }
+
+ Logger getLogger(final String name, final ClassLoader classLoader) {
+ return getInstance(getContext(classLoader), name);
+ }
+
+ /**
+ * Gets a new logger instance named as the first parameter using factory.
+ *
+ *
+ * If a logger of that name already exists, then it will be returned. Otherwise, a new logger will be instantiated by
+ * the factory parameter and linked with its existing ancestors as well as children.
+ *
+ *
+ * @param name The name of the logger to retrieve.
+ * @param factory The factory that will make the new logger instance.
+ *
+ */
+ @Override
+ public Logger getLogger(final String name, final LoggerFactory factory) {
+ return getInstance(getContext(), name, factory);
+ }
+
+ Logger getLogger(final String name, final LoggerFactory factory, final ClassLoader classLoader) {
+ return getInstance(getContext(classLoader), name, factory);
+ }
+
+ /**
+ * Gets the renderer map for this hierarchy.
+ */
+ @Override
+ public RendererMap getRendererMap() {
+ return rendererMap;
+ }
+
+ /**
+ * Gets the root of this hierarchy.
+ *
+ * @since 0.9.0
+ */
+ @Override
+ public Logger getRootLogger() {
+ return getInstance(getContext(), org.apache.logging.log4j.LogManager.ROOT_LOGGER_NAME);
+ }
+
+ Logger getRootLogger(final ClassLoader classLoader) {
+ return getInstance(getContext(classLoader), org.apache.logging.log4j.LogManager.ROOT_LOGGER_NAME);
+ }
+
+ /**
+ * Gets a {@link Level} representation of the enable state.
+ *
+ * @since 1.2
+ */
+ @Override
+ public Level getThreshold() {
+ return threshold;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ThrowableRenderer getThrowableRenderer() {
+ return throwableRenderer;
+ }
+
+ /**
+ * This method will return true if this repository is disabled for level object passed as
+ * parameter and false otherwise. See also the {@link #setThreshold(Level) threshold} emthod.
+ */
+ @Override
+ public boolean isDisabled(final int level) {
+ return thresholdInt > level;
+ }
+
+ /**
+ * @deprecated Deprecated with no replacement.
+ */
+ @Deprecated
+ public void overrideAsNeeded(final String override) {
+ LogLog.warn("The Hiearchy.overrideAsNeeded method has been deprecated.");
+ }
+
+ /**
+ * Resets all values contained in this hierarchy instance to their default. This removes all appenders from all
+ * categories, sets the level of all non-root categories to null, sets their additivity flag to
+ * true and sets the level of the root logger to {@link Level#DEBUG DEBUG}. Moreover, message disabling is
+ * set its default "off" value.
+ *
+ *
+ * Existing categories are not removed. They are just reset.
+ *
+ *
+ *
+ * This method should be used sparingly and with care as it will block all logging until it is completed.
+ *
+ *
+ * @since 0.8.5
+ */
+ @Override
+ public void resetConfiguration() {
+ resetConfiguration(getContext());
+ }
+
+ void resetConfiguration(final ClassLoader classLoader) {
+ resetConfiguration(getContext(classLoader));
+ }
+
+ void resetConfiguration(final LoggerContext loggerContext) {
+ getLoggersMap(loggerContext).clear();
+
+ getRootLogger().setLevel(Level.DEBUG);
+ root.setResourceBundle(null);
+ setThreshold(Level.ALL);
+
+ // the synchronization is needed to prevent JDK 1.2.x hashtable
+ // surprises
+ synchronized (ht) {
+ shutdown(); // nested locks are OK
+
+ final Enumeration cats = getCurrentLoggers();
+ while (cats.hasMoreElements()) {
+ final Logger c = (Logger) cats.nextElement();
+ c.setLevel(null);
+ c.setAdditivity(true);
+ c.setResourceBundle(null);
+ }
+ }
+ rendererMap.clear();
+ throwableRenderer = null;
+ }
+
+ /**
+ * Does nothing.
+ *
+ * @deprecated Deprecated with no replacement.
+ */
+ @Deprecated
+ public void setDisableOverride(final String override) {
+ LogLog.warn("The Hiearchy.setDisableOverride method has been deprecated.");
+ }
+
+ /**
+ * Used by subclasses to add a renderer to the hierarchy passed as parameter.
+ */
+ @Override
+ public void setRenderer(final Class renderedClass, final ObjectRenderer renderer) {
+ rendererMap.put(renderedClass, renderer);
+ }
+
+ /**
+ * Enable logging for logging requests with level l or higher. By default all levels are enabled.
+ *
+ * @param level The minimum level for which logging requests are sent to their appenders.
+ */
+ @Override
+ public void setThreshold(final Level level) {
+ if (level != null) {
+ thresholdInt = level.level;
+ threshold = level;
+ }
+ }
+
+ /**
+ * The string form of {@link #setThreshold(Level)}.
+ */
+ @Override
+ public void setThreshold(final String levelStr) {
+ final Level level = OptionConverter.toLevel(levelStr, null);
+ if (level != null) {
+ setThreshold(level);
+ } else {
+ LogLog.warn("Could not convert [" + levelStr + "] to Level.");
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setThrowableRenderer(final ThrowableRenderer throwableRenderer) {
+ this.throwableRenderer = throwableRenderer;
+ }
+
+ /**
+ * Shutting down a hierarchy will safely close and remove all appenders in all categories including the root
+ * logger.
+ *
+ *
+ * Some appenders such as {@link org.apache.log4j.net.SocketAppender} and {@link AsyncAppender} need to be closed before
+ * the application exists. Otherwise, pending logging events might be lost.
+ *
+ *
+ * The shutdown method is careful to close nested appenders before closing regular appenders. This is
+ * allows configurations where a regular appender is attached to a logger and again to a nested appender.
+ *
+ *
+ * @since 1.0
+ */
+ @Override
+ public void shutdown() {
+ shutdown(getContext());
+ }
+
+ public void shutdown(final ClassLoader classLoader) {
+ shutdown(org.apache.logging.log4j.LogManager.getContext(classLoader, false));
+ }
+
+ void shutdown(final LoggerContext context) {
+// final Logger root = getRootLogger();
+// // begin by closing nested appenders
+// root.closeNestedAppenders();
+//
+// synchronized (ht) {
+// Enumeration cats = this.getCurrentLoggers();
+// while (cats.hasMoreElements()) {
+// final Logger c = (Logger) cats.nextElement();
+// c.closeNestedAppenders();
+// }
+//
+// // then, remove all appenders
+// root.removeAllAppenders();
+// cats = this.getCurrentLoggers();
+// while (cats.hasMoreElements()) {
+// final Logger c = (Logger) cats.nextElement();
+// c.removeAllAppenders();
+// }
+// }
+ getLoggersMap(context).clear();
+ if (LogManager.isLog4jCorePresent()) {
+ ContextUtil.shutdown(context);
+ }
+ }
+
+ /**
+ * We update the links for all the children that placed themselves in the provision node 'pn'. The second argument 'cat'
+ * is a reference for the newly created Logger, parent of all the children in 'pn'
+ *
+ * We loop on all the children 'c' in 'pn':
+ *
+ * If the child 'c' has been already linked to a child of 'cat' then there is no need to update 'c'.
+ *
+ * Otherwise, we set cat's parent field to c's parent and set c's parent field to cat.
+ *
+ */
+ final private void updateChildren(final ProvisionNode pn, final Logger logger) {
+ // System.out.println("updateChildren called for " + logger.name);
+ final int last = pn.size();
+
+ for (int i = 0; i < last; i++) {
+ final Logger l = (Logger) pn.elementAt(i);
+ // System.out.println("Updating child " +p.name);
+
+ // Unless this child already points to a correct (lower) parent,
+ // make cat.parent point to l.parent and l.parent to cat.
+ if (!l.parent.name.startsWith(logger.name)) {
+ logger.parent = l.parent;
+ l.parent = logger;
+ }
+ }
+ }
+
+ /**
+ * This method loops through all the *potential* parents of 'cat'. There 3 possible cases:
+ *
+ * 1) No entry for the potential parent of 'cat' exists
+ *
+ * We create a ProvisionNode for this potential parent and insert 'cat' in that provision node.
+ *
+ * 2) There entry is of type Logger for the potential parent.
+ *
+ * The entry is 'cat's nearest existing parent. We update cat's parent field with this entry. We also break from the
+ * loop because updating our parent's parent is our parent's responsibility.
+ *
+ * 3) There entry is of type ProvisionNode for this potential parent.
+ *
+ * We add 'cat' to the list of children for this potential parent.
+ */
+ final private void updateParents(final Logger cat) {
+ final String name = cat.name;
+ final int length = name.length();
+ boolean parentFound = false;
+
+ // System.out.println("UpdateParents called for " + name);
+
+ // if name = "w.x.y.z", loop thourgh "w.x.y", "w.x" and "w", but not "w.x.y.z"
+ for (int i = name.lastIndexOf('.', length - 1); i >= 0; i = name.lastIndexOf('.', i - 1)) {
+ final String substr = name.substring(0, i);
+
+ // System.out.println("Updating parent : " + substr);
+ final CategoryKey key = new CategoryKey(substr); // simple constructor
+ final Object o = ht.get(key);
+ // Create a provision node for a future parent.
+ if (o == null) {
+ // System.out.println("No parent "+substr+" found. Creating ProvisionNode.");
+ final ProvisionNode pn = new ProvisionNode(cat);
+ ht.put(key, pn);
+ } else if (o instanceof Category) {
+ parentFound = true;
+ cat.parent = (Category) o;
+ // System.out.println("Linking " + cat.name + " -> " + ((Category) o).name);
+ break; // no need to update the ancestors of the closest ancestor
+ } else if (o instanceof ProvisionNode) {
+ ((ProvisionNode) o).addElement(cat);
+ } else {
+ final Exception e = new IllegalStateException("unexpected object type " + o.getClass() + " in ht.");
+ e.printStackTrace();
+ }
+ }
+ // If we could not find any existing parents, then link with root.
+ if (!parentFound) {
+ cat.parent = root;
+ }
+ }
+
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/Layout.java b/log4j-1.2-api/src/main/java/org/apache/log4j/Layout.java
index 2ef4b1c7eaf..dbd22916a21 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/Layout.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/Layout.java
@@ -24,6 +24,8 @@
*/
public abstract class Layout {
+ public final static String LINE_SEP = Strings.LINE_SEPARATOR;
+
/** Note that the line.separator property can be looked up even by applets. */
public static final int LINE_SEP_LEN = Strings.LINE_SEPARATOR.length();
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/Level.java b/log4j-1.2-api/src/main/java/org/apache/log4j/Level.java
index af5315417bd..2da318529b3 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/Level.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/Level.java
@@ -23,6 +23,7 @@
import java.io.Serializable;
import java.util.Locale;
+import org.apache.log4j.helpers.OptionConverter;
import org.apache.logging.log4j.util.Strings;
/**
@@ -48,50 +49,50 @@ public class Level extends Priority implements Serializable {
* The OFF has the highest possible rank and is
* intended to turn off logging.
*/
- public static final Level OFF = new Level(OFF_INT, "OFF", 0);
+ public static final Level OFF = new Level(OFF_INT, "OFF", 0, org.apache.logging.log4j.Level.OFF);
/**
* The FATAL level designates very severe error
* events that will presumably lead the application to abort.
*/
- public static final Level FATAL = new Level(FATAL_INT, "FATAL", 0);
+ public static final Level FATAL = new Level(FATAL_INT, "FATAL", 0, org.apache.logging.log4j.Level.FATAL);
/**
* The ERROR level designates error events that
* might still allow the application to continue running.
*/
- public static final Level ERROR = new Level(ERROR_INT, "ERROR", 3);
+ public static final Level ERROR = new Level(ERROR_INT, "ERROR", 3, org.apache.logging.log4j.Level.ERROR);
/**
* The WARN level designates potentially harmful situations.
*/
- public static final Level WARN = new Level(WARN_INT, "WARN", 4);
+ public static final Level WARN = new Level(WARN_INT, "WARN", 4, org.apache.logging.log4j.Level.WARN);
/**
* The INFO level designates informational messages
* that highlight the progress of the application at coarse-grained
* level.
*/
- public static final Level INFO = new Level(INFO_INT, "INFO", 6);
+ public static final Level INFO = new Level(INFO_INT, "INFO", 6, org.apache.logging.log4j.Level.INFO);
/**
* The DEBUG Level designates fine-grained
* informational events that are most useful to debug an
* application.
*/
- public static final Level DEBUG = new Level(DEBUG_INT, "DEBUG", 7);
+ public static final Level DEBUG = new Level(DEBUG_INT, "DEBUG", 7, org.apache.logging.log4j.Level.DEBUG);
/**
* The TRACE Level designates finer-grained
* informational events than the DEBUG level.
*/
- public static final Level TRACE = new Level(TRACE_INT, "TRACE", 7);
+ public static final Level TRACE = new Level(TRACE_INT, "TRACE", 7, org.apache.logging.log4j.Level.TRACE);
/**
* The ALL has the lowest possible rank and is intended to
* turn on all logging.
*/
- public static final Level ALL = new Level(ALL_INT, "ALL", 7);
+ public static final Level ALL = new Level(ALL_INT, "ALL", 7, org.apache.logging.log4j.Level.ALL);
/**
* Serialization version id.
@@ -99,16 +100,21 @@ public class Level extends Priority implements Serializable {
private static final long serialVersionUID = 3491141966387921974L;
/**
- * Instantiate a Level object.
+ * Instantiate a Level object. A corresponding Log4j 2.x level is also created.
*
* @param level The logging level.
* @param levelStr The level name.
* @param syslogEquivalent The matching syslog level.
*/
protected Level(final int level, final String levelStr, final int syslogEquivalent) {
- super(level, levelStr, syslogEquivalent);
+ this(level, levelStr, syslogEquivalent, null);
}
+ protected Level(final int level, final String levelStr, final int syslogEquivalent,
+ final org.apache.logging.log4j.Level version2Equivalent) {
+ super(level, levelStr, syslogEquivalent);
+ this.version2Level = version2Equivalent != null ? version2Equivalent : OptionConverter.createLevel(this);
+ }
/**
* Convert the string passed as argument to a level. If the
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/LogManager.java b/log4j-1.2-api/src/main/java/org/apache/log4j/LogManager.java
index 751cb86972d..26c19bd3de7 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/LogManager.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/LogManager.java
@@ -1,222 +1,224 @@
-/*
- * 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;
-
-import java.util.Enumeration;
-
-import org.apache.log4j.helpers.NullEnumeration;
-import org.apache.log4j.spi.HierarchyEventListener;
-import org.apache.log4j.spi.LoggerFactory;
-import org.apache.log4j.spi.LoggerRepository;
-import org.apache.log4j.spi.RepositorySelector;
-import org.apache.logging.log4j.core.LoggerContext;
-import org.apache.logging.log4j.util.Strings;
-
-/**
- *
- */
-public final class LogManager {
-
- /**
- * @deprecated This variable is for internal use only. It will
- * become package protected in future versions.
- * */
- @Deprecated
- public static final String DEFAULT_CONFIGURATION_FILE = "log4j.properties";
-
- /**
- * @deprecated This variable is for internal use only. It will
- * become private in future versions.
- * */
- @Deprecated
- public static final String DEFAULT_CONFIGURATION_KEY = "log4j.configuration";
-
- /**
- * @deprecated This variable is for internal use only. It will
- * become private in future versions.
- * */
- @Deprecated
- public static final String CONFIGURATOR_CLASS_KEY = "log4j.configuratorClass";
-
- /**
- * @deprecated This variable is for internal use only. It will
- * become private in future versions.
- */
- @Deprecated
- public static final String DEFAULT_INIT_OVERRIDE_KEY = "log4j.defaultInitOverride";
-
- static final String DEFAULT_XML_CONFIGURATION_FILE = "log4j.xml";
-
- private static final LoggerRepository REPOSITORY = new Repository();
-
- private LogManager() {
- }
-
- public static Logger getRootLogger() {
- return Category.getInstance(PrivateManager.getContext(), Strings.EMPTY);
- }
-
- public static Logger getLogger(final String name) {
- return Category.getInstance(PrivateManager.getContext(), name);
- }
-
- public static Logger getLogger(final Class> clazz) {
- return Category.getInstance(PrivateManager.getContext(), clazz.getName());
- }
-
- public static Logger getLogger(final String name, final LoggerFactory factory) {
- return Category.getInstance(PrivateManager.getContext(), name);
- }
-
- public static Logger exists(final String name) {
- final LoggerContext ctx = PrivateManager.getContext();
- if (!ctx.hasLogger(name)) {
- return null;
- }
- return Logger.getLogger(name);
- }
-
- @SuppressWarnings("rawtypes")
- public static Enumeration getCurrentLoggers() {
- return NullEnumeration.getInstance();
- }
-
- static void reconfigure() {
- final LoggerContext ctx = PrivateManager.getContext();
- ctx.reconfigure();
- }
-
- /**
- * No-op implementation.
- */
- public static void shutdown() {
- }
-
- /**
- * No-op implementation.
- */
- public static void resetConfiguration() {
- }
-
- /**
- * No-op implementation.
- * @param selector The RepositorySelector.
- * @param guard prevents calls at the incorrect time.
- * @throws IllegalArgumentException if a parameter is invalid.
- */
- public static void setRepositorySelector(final RepositorySelector selector, final Object guard)
- throws IllegalArgumentException {
- }
-
- public static LoggerRepository getLoggerRepository() {
- return REPOSITORY;
- }
-
- /**
- * The Repository.
- */
- private static class Repository implements LoggerRepository {
- @Override
- public void addHierarchyEventListener(final HierarchyEventListener listener) {
-
- }
-
- @Override
- public boolean isDisabled(final int level) {
- return false;
- }
-
- @Override
- public void setThreshold(final Level level) {
-
- }
-
- @Override
- public void setThreshold(final String val) {
-
- }
-
- @Override
- public void emitNoAppenderWarning(final Category cat) {
-
- }
-
- @Override
- public Level getThreshold() {
- return Level.OFF;
- }
-
- @Override
- public Logger getLogger(final String name) {
- return Category.getInstance(PrivateManager.getContext(), name);
- }
-
- @Override
- public Logger getLogger(final String name, final LoggerFactory factory) {
- return Category.getInstance(PrivateManager.getContext(), name);
- }
-
- @Override
- public Logger getRootLogger() {
- return Category.getRoot(PrivateManager.getContext());
- }
-
- @Override
- public Logger exists(final String name) {
- return LogManager.exists(name);
- }
-
- @Override
- public void shutdown() {
- }
-
- @Override
- @SuppressWarnings("rawtypes")
- public Enumeration getCurrentLoggers() {
- return NullEnumeration.getInstance();
- }
-
- @Override
- @SuppressWarnings("rawtypes")
- public Enumeration getCurrentCategories() {
- return NullEnumeration.getInstance();
- }
-
- @Override
- public void fireAddAppenderEvent(final Category logger, final Appender appender) {
- }
-
- @Override
- public void resetConfiguration() {
- }
- }
-
- /**
- * Internal LogManager.
- */
- private static class PrivateManager extends org.apache.logging.log4j.LogManager {
- private static final String FQCN = LogManager.class.getName();
-
- public static LoggerContext getContext() {
- return (LoggerContext) getContext(FQCN, false);
- }
-
- public static org.apache.logging.log4j.Logger getLogger(final String name) {
- return getLogger(FQCN, name);
- }
- }
-}
+/*
+ * 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;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.stream.Collectors;
+
+import org.apache.log4j.legacy.core.ContextUtil;
+import org.apache.log4j.spi.DefaultRepositorySelector;
+import org.apache.log4j.spi.LoggerFactory;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.NOPLoggerRepository;
+import org.apache.log4j.spi.RepositorySelector;
+import org.apache.log4j.spi.RootLogger;
+import org.apache.logging.log4j.spi.LoggerContext;
+import org.apache.logging.log4j.util.LoaderUtil;
+import org.apache.logging.log4j.util.StackLocatorUtil;
+
+/**
+ * The main entry point to Log4j 1.
+ */
+public final class LogManager {
+
+ /**
+ * @deprecated This variable is for internal use only. It will become package protected in future versions.
+ */
+ @Deprecated
+ public static final String DEFAULT_CONFIGURATION_FILE = "log4j.properties";
+
+ /**
+ * @deprecated This variable is for internal use only. It will become private in future versions.
+ */
+ @Deprecated
+ public static final String DEFAULT_CONFIGURATION_KEY = "log4j.configuration";
+
+ /**
+ * @deprecated This variable is for internal use only. It will become private in future versions.
+ */
+ @Deprecated
+ public static final String CONFIGURATOR_CLASS_KEY = "log4j.configuratorClass";
+
+ /**
+ * @deprecated This variable is for internal use only. It will become private in future versions.
+ */
+ @Deprecated
+ public static final String DEFAULT_INIT_OVERRIDE_KEY = "log4j.defaultInitOverride";
+
+ static final String DEFAULT_XML_CONFIGURATION_FILE = "log4j.xml";
+
+ static private RepositorySelector repositorySelector;
+
+ private static final boolean LOG4J_CORE_PRESENT;
+
+ static {
+ LOG4J_CORE_PRESENT = LoaderUtil.isClassAvailable("org.apache.logging.log4j.core.LoggerContext");
+ // By default, we use a DefaultRepositorySelector which always returns 'hierarchy'.
+ final Hierarchy hierarchy = new Hierarchy(new RootLogger(Level.DEBUG));
+ repositorySelector = new DefaultRepositorySelector(hierarchy);
+ }
+
+ /**
+ * Tests if a logger for the given name exists.
+ *
+ * @param name logger name to test.
+ * @return whether a logger for the given name exists.
+ */
+ public static Logger exists(final String name) {
+ return exists(name, StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ static Logger exists(final String name, final ClassLoader classLoader) {
+ return getHierarchy().exists(name, classLoader);
+ }
+
+ /**
+ * Gets a LoggerContext.
+ *
+ * @param classLoader The ClassLoader for the context. If null the context will attempt to determine the appropriate
+ * ClassLoader.
+ * @return a LoggerContext.
+ */
+ static LoggerContext getContext(final ClassLoader classLoader) {
+ return org.apache.logging.log4j.LogManager.getContext(classLoader, false);
+ }
+
+ /**
+ * Gets an enumeration of the current loggers.
+ *
+ * @return an enumeration of the current loggers.
+ */
+ @SuppressWarnings("rawtypes")
+ public static Enumeration getCurrentLoggers() {
+ return getCurrentLoggers(StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ @SuppressWarnings("rawtypes")
+ static Enumeration getCurrentLoggers(final ClassLoader classLoader) {
+ // @formatter:off
+ return Collections.enumeration(
+ LogManager.getContext(classLoader).getLoggerRegistry()
+ .getLoggers().stream().map(e -> LogManager.getLogger(e.getName(), classLoader))
+ .collect(Collectors.toList()));
+ // @formatter:on
+ }
+
+ static Hierarchy getHierarchy() {
+ final LoggerRepository loggerRepository = getLoggerRepository();
+ return loggerRepository instanceof Hierarchy ? (Hierarchy) loggerRepository : null;
+ }
+
+ /**
+ * Gets the logger for the given class.
+ */
+ public static Logger getLogger(final Class> clazz) {
+ final Hierarchy hierarchy = getHierarchy();
+ return hierarchy != null ? hierarchy.getLogger(clazz.getName(), StackLocatorUtil.getCallerClassLoader(2))
+ : getLoggerRepository().getLogger(clazz.getName());
+ }
+
+ /**
+ * Gets the logger for the given name.
+ */
+ public static Logger getLogger(final String name) {
+ final Hierarchy hierarchy = getHierarchy();
+ return hierarchy != null ? hierarchy.getLogger(name, StackLocatorUtil.getCallerClassLoader(2)) : getLoggerRepository().getLogger(name);
+ }
+
+ static Logger getLogger(final String name, final ClassLoader classLoader) {
+ final Hierarchy hierarchy = getHierarchy();
+ return hierarchy != null ? hierarchy.getLogger(name, classLoader) : getLoggerRepository().getLogger(name);
+ }
+
+ public static Logger getLogger(final String name, final LoggerFactory factory) {
+ final Hierarchy hierarchy = getHierarchy();
+ return hierarchy != null ? hierarchy.getLogger(name, factory, StackLocatorUtil.getCallerClassLoader(2))
+ : getLoggerRepository().getLogger(name, factory);
+ }
+
+ static Logger getLogger(final String name, final LoggerFactory factory, final ClassLoader classLoader) {
+ final Hierarchy hierarchy = getHierarchy();
+ return hierarchy != null ? hierarchy.getLogger(name, factory, classLoader) : getLoggerRepository().getLogger(name, factory);
+ }
+
+ public static LoggerRepository getLoggerRepository() {
+ if (repositorySelector == null) {
+ repositorySelector = new DefaultRepositorySelector(new NOPLoggerRepository());
+ }
+ return repositorySelector.getLoggerRepository();
+ }
+
+ /**
+ * Gets the root logger.
+ */
+ public static Logger getRootLogger() {
+ return getRootLogger(StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ static Logger getRootLogger(final ClassLoader classLoader) {
+ final Hierarchy hierarchy = getHierarchy();
+ return hierarchy != null ? hierarchy.getRootLogger(classLoader) : getLoggerRepository().getRootLogger();
+ }
+
+ static boolean isLog4jCorePresent() {
+ return LOG4J_CORE_PRESENT;
+ }
+
+ static void reconfigure(final ClassLoader classLoader) {
+ if (isLog4jCorePresent()) {
+ ContextUtil.reconfigure(LogManager.getContext(classLoader));
+ }
+ }
+
+ public static void resetConfiguration() {
+ resetConfiguration(StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ static void resetConfiguration(final ClassLoader classLoader) {
+ final Hierarchy hierarchy = getHierarchy();
+ if (hierarchy != null) {
+ hierarchy.resetConfiguration(classLoader);
+ } else {
+ getLoggerRepository().resetConfiguration();
+ }
+ }
+
+ public static void setRepositorySelector(final RepositorySelector selector, final Object guard) throws IllegalArgumentException {
+ if (selector == null) {
+ throw new IllegalArgumentException("RepositorySelector must be non-null.");
+ }
+ LogManager.repositorySelector = selector;
+ }
+
+ /**
+ * Shuts down the current configuration.
+ */
+ public static void shutdown() {
+ shutdown(StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ static void shutdown(final ClassLoader classLoader) {
+ final Hierarchy hierarchy = getHierarchy();
+ if (hierarchy != null) {
+ hierarchy.shutdown(classLoader);
+ } else {
+ getLoggerRepository().shutdown();
+ }
+ }
+
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/Logger.java b/log4j-1.2-api/src/main/java/org/apache/log4j/Logger.java
index fb7277b5012..b152e2b757f 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/Logger.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/Logger.java
@@ -16,51 +16,58 @@
*/
package org.apache.log4j;
-
import org.apache.log4j.spi.LoggerFactory;
-import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.spi.LoggerContext;
+import org.apache.logging.log4j.util.StackLocatorUtil;
/**
*
*/
public class Logger extends Category {
- protected Logger(final String name) {
- super(PrivateManager.getContext(), name);
- }
+ /**
+ * The fully qualified name of the Logger class.
+ */
+ private static final String FQCN = Logger.class.getName();
- Logger(final LoggerContext context, final String name) {
- super(context, name);
+ public static Logger getLogger(final Class> clazz) {
+ // Depth 2 gets the call site of this method.
+ return LogManager.getLogger(clazz.getName(), StackLocatorUtil.getCallerClassLoader(2));
}
public static Logger getLogger(final String name) {
- return Category.getInstance(PrivateManager.getContext(), name);
+ // Depth 2 gets the call site of this method.
+ return LogManager.getLogger(name, StackLocatorUtil.getCallerClassLoader(2));
}
- public static Logger getLogger(final Class> clazz) {
- return Category.getInstance(PrivateManager.getContext(), clazz);
+ public static Logger getLogger(final String name, final LoggerFactory factory) {
+ // Depth 2 gets the call site of this method.
+ return LogManager.getLogger(name, factory, StackLocatorUtil.getCallerClassLoader(2));
}
public static Logger getRootLogger() {
- return Category.getRoot(PrivateManager.getContext());
+ return LogManager.getRootLogger();
}
- public static Logger getLogger(final String name, final LoggerFactory factory) {
- return Category.getInstance(PrivateManager.getContext(), name, factory);
+ Logger(final LoggerContext context, final String name) {
+ super(context, name);
}
- /**
- * Internal Log Manager.
- */
- private static class PrivateManager extends org.apache.logging.log4j.LogManager {
- private static final String FQCN = Logger.class.getName();
+ protected Logger(final String name) {
+ super(name);
+ }
- public static LoggerContext getContext() {
- return (LoggerContext) getContext(FQCN, false);
- }
+ public boolean isTraceEnabled() {
+ return getLogger().isTraceEnabled();
+ }
+
+ public void trace(final Object message) {
+ maybeLog(FQCN, org.apache.logging.log4j.Level.TRACE, message, null);
+ }
- public static org.apache.logging.log4j.Logger getLogger(final String name) {
- return getLogger(FQCN, name);
- }
+ public void trace(final Object message, final Throwable t) {
+ maybeLog(FQCN, org.apache.logging.log4j.Level.TRACE, message, t);
}
+
+
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/MDC.java b/log4j-1.2-api/src/main/java/org/apache/log4j/MDC.java
index ee7631a6994..ad902d21e69 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/MDC.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/MDC.java
@@ -28,8 +28,7 @@
*/
public final class MDC {
-
- private static ThreadLocal
- * @return Stack A clone of the current thread's diagnostic context.
+ * @return Stack A clone of the current thread's diagnostic context, Stack of Strings.
*/
@SuppressWarnings("rawtypes")
public static Stack cloneStack() {
@@ -65,7 +65,7 @@ public static Stack cloneStack() {
/**
- * Inherit the diagnostic context of another thread.
+ * Inherit the diagnostic context of another thread, a Stack of Strings.
*
* The parent thread can obtain a reference to its diagnostic
* context using the {@link #cloneStack} method. It should
@@ -83,9 +83,10 @@ public static Stack cloneStack() {
* there is no client-transparent way of inheriting diagnostic
* contexts. Do you know any solution to this problem?
*
- * @param stack The diagnostic context of the parent thread.
+ * @param stack The diagnostic context of the parent thread, a Stack of Strings.
*/
- public static void inherit(final Stack stack) {
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public static void inherit(final Stack stack) {
org.apache.logging.log4j.ThreadContext.setStack(stack);
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/PatternLayout.java b/log4j-1.2-api/src/main/java/org/apache/log4j/PatternLayout.java
index c2e1251ee8c..da6f87f6547 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/PatternLayout.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/PatternLayout.java
@@ -16,25 +16,117 @@
*/
package org.apache.log4j;
+import org.apache.log4j.helpers.PatternConverter;
+import org.apache.log4j.helpers.PatternParser;
import org.apache.log4j.spi.LoggingEvent;
-import org.apache.logging.log4j.util.Strings;
/**
*
*/
public class PatternLayout extends Layout {
+ /**
+ * Default pattern string for log output. Currently set to the string {@value #DEFAULT_CONVERSION_PATTERN} which
+ * just prints the application supplied message.
+ */
+ public final static String DEFAULT_CONVERSION_PATTERN = "%m%n";
+
+ /**
+ * A conversion pattern equivalent to the TTCCCLayout. Current value is {@value #TTCC_CONVERSION_PATTERN}
+ */
+ public final static String TTCC_CONVERSION_PATTERN = "%r [%t] %p %c %x - %m%n";
+
+ protected final int BUF_SIZE = 256;
+
+ protected final int MAX_CAPACITY = 1024;
+
+ // output buffer appended to when format() is invoked
+ private StringBuffer sbuf = new StringBuffer(BUF_SIZE);
+
+ private String pattern;
+
+ private PatternConverter head;
+
+ /**
+ * Constructs a PatternLayout using the DEFAULT_LAYOUT_PATTERN.
+ *
+ * The default pattern just produces the application supplied message.
+ */
+ public PatternLayout() {
+ this(DEFAULT_CONVERSION_PATTERN);
+ }
+
+ /**
+ * Constructs a PatternLayout using the supplied conversion pattern.
+ */
public PatternLayout(final String pattern) {
+ this.pattern = pattern;
+ head = createPatternParser((pattern == null) ? DEFAULT_CONVERSION_PATTERN : pattern).parse();
+ }
+ /**
+ * Does not do anything as options become effective
+ */
+ public void activateOptions() {
+ // nothing to do.
}
+ /**
+ * Returns PatternParser used to parse the conversion string. Subclasses may override this to return a subclass of
+ * PatternParser which recognize custom conversion characters.
+ *
+ * @since 0.9.0
+ */
+ protected PatternParser createPatternParser(final String pattern) {
+ return new PatternParser(pattern);
+ }
+
+ /**
+ * Produces a formatted string as specified by the conversion pattern.
+ */
@Override
public String format(final LoggingEvent event) {
- return Strings.EMPTY;
+ // Reset working stringbuffer
+ if (sbuf.capacity() > MAX_CAPACITY) {
+ sbuf = new StringBuffer(BUF_SIZE);
+ } else {
+ sbuf.setLength(0);
+ }
+
+ PatternConverter c = head;
+
+ while (c != null) {
+ c.format(sbuf, event);
+ c = c.next;
+ }
+ return sbuf.toString();
}
+ /**
+ * Returns the value of the ConversionPattern option.
+ */
+ public String getConversionPattern() {
+ return pattern;
+ }
+
+ /**
+ * The PatternLayout does not handle the throwable contained within {@link LoggingEvent LoggingEvents}. Thus, it returns
+ * true.
+ *
+ * @since 0.8.4
+ */
@Override
public boolean ignoresThrowable() {
return true;
}
+
+ /**
+ * Set the ConversionPattern option. This is the string which controls formatting and consists of a mix of
+ * literal content and conversion specifiers.
+ */
+ public void setConversionPattern(final String conversionPattern) {
+ pattern = conversionPattern;
+ head = createPatternParser(conversionPattern).parse();
+ }
+
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/Priority.java b/log4j-1.2-api/src/main/java/org/apache/log4j/Priority.java
index 8f6eee9b519..33a78915ef2 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/Priority.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/Priority.java
@@ -96,6 +96,7 @@ public class Priority {
transient int level;
transient String levelStr;
transient int syslogEquivalent;
+ transient org.apache.logging.log4j.Level version2Level;
/**
* Default constructor for deserialization.
@@ -148,6 +149,14 @@ final int getSyslogEquivalent() {
return syslogEquivalent;
}
+ /**
+ * Gets the Log4j 2.x level associated with this priority
+ *
+ * @return a Log4j 2.x level.
+ */
+ public org.apache.logging.log4j.Level getVersion2Level() {
+ return version2Level;
+ }
/**
* Returns {@code true} if this level has a higher or equal
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/PropertyConfigurator.java b/log4j-1.2-api/src/main/java/org/apache/log4j/PropertyConfigurator.java
index 0fe1fe0e60c..7069f5f8cec 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/PropertyConfigurator.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/PropertyConfigurator.java
@@ -16,111 +16,663 @@
*/
package org.apache.log4j;
+import java.io.IOException;
import java.io.InputStream;
+import java.io.InterruptedIOException;
import java.net.URL;
+import java.net.URLConnection;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Map;
import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import org.apache.log4j.bridge.FilterAdapter;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.config.PropertySetter;
+import org.apache.log4j.helpers.FileWatchdog;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.or.RendererMap;
+import org.apache.log4j.spi.Configurator;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggerFactory;
import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.OptionHandler;
+import org.apache.log4j.spi.RendererSupport;
+import org.apache.log4j.spi.ThrowableRenderer;
+import org.apache.log4j.spi.ThrowableRendererSupport;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.net.UrlConnectionFactory;
+import org.apache.logging.log4j.util.StackLocatorUtil;
/**
- * A configurator for properties.
+ * Configures Log4j from properties.
*/
-public class PropertyConfigurator {
+public class PropertyConfigurator implements Configurator {
+
+ static class NameValue {
+ String key, value;
+
+ public NameValue(final String key, final String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return key + "=" + value;
+ }
+ }
+
+ static class PropertyWatchdog extends FileWatchdog {
+
+ private final ClassLoader classLoader;
+
+ PropertyWatchdog(final String fileName, final ClassLoader classLoader) {
+ super(fileName);
+ this.classLoader = classLoader;
+ }
+
+ /**
+ * Call {@link PropertyConfigurator#configure(String)} with the filename to reconfigure log4j.
+ */
+ @Override
+ public void doOnChange() {
+ new PropertyConfigurator().doConfigure(filename, LogManager.getLoggerRepository(), classLoader);
+ }
+ }
+
+ class SortedKeyEnumeration implements Enumeration {
+
+ private final Enumeration e;
+
+ public SortedKeyEnumeration(final Hashtable ht) {
+ final Enumeration f = ht.keys();
+ final Vector keys = new Vector(ht.size());
+ for (int i, last = 0; f.hasMoreElements(); ++last) {
+ final String key = (String) f.nextElement();
+ for (i = 0; i < last; ++i) {
+ final String s = (String) keys.get(i);
+ if (key.compareTo(s) <= 0) {
+ break;
+ }
+ }
+ keys.add(i, key);
+ }
+ e = keys.elements();
+ }
+
+ @Override
+ public boolean hasMoreElements() {
+ return e.hasMoreElements();
+ }
+
+ @Override
+ public Object nextElement() {
+ return e.nextElement();
+ }
+ }
+
+ static final String CATEGORY_PREFIX = "log4j.category.";
+ static final String LOGGER_PREFIX = "log4j.logger.";
+ static final String FACTORY_PREFIX = "log4j.factory";
+ static final String ADDITIVITY_PREFIX = "log4j.additivity.";
+ static final String ROOT_CATEGORY_PREFIX = "log4j.rootCategory";
+ static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger";
+ static final String APPENDER_PREFIX = "log4j.appender.";
+ static final String RENDERER_PREFIX = "log4j.renderer.";
+ static final String THRESHOLD_PREFIX = "log4j.threshold";
+
+ private static final String THROWABLE_RENDERER_PREFIX = "log4j.throwableRenderer";
+ 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";
/**
- * Read configuration options from configuration file.
- *
- * @param configFileName The configuration file
- * @param hierarchy The hierarchy
+ * Key for specifying the {@link org.apache.log4j.spi.LoggerFactory LoggerFactory}. Currently set to
+ * "log4j.loggerFactory".
+ */
+ public static final String LOGGER_FACTORY_KEY = "log4j.loggerFactory";
+
+ /**
+ * If property set to true, then hierarchy will be reset before configuration.
*/
- public void doConfigure(final String configFileName, final LoggerRepository hierarchy) {
+ private static final String RESET_KEY = "log4j.reset";
+ static final private String INTERNAL_ROOT_NAME = "root";
+
+ /**
+ * Reads configuration options from an InputStream.
+ *
+ * @param inputStream The input stream
+ */
+ public static void configure(final InputStream inputStream) {
+ new PropertyConfigurator().doConfigure(inputStream, LogManager.getLoggerRepository(), StackLocatorUtil.getCallerClassLoader(2));
}
/**
- * Read configuration options from properties.
+ * Reads configuration options from properties.
*
* See {@link #doConfigure(String, LoggerRepository)} for the expected format.
*
* @param properties The properties
- * @param hierarchy The hierarchy
*/
- public void doConfigure(final Properties properties, final LoggerRepository hierarchy) {
+ public static void configure(final Properties properties) {
+ new PropertyConfigurator().doConfigure(properties, LogManager.getLoggerRepository(), StackLocatorUtil.getCallerClassLoader(2));
}
/**
- * Read configuration options from an InputStream.
+ * Reads configuration options from configuration file.
*
- * @param inputStream The input stream
- * @param hierarchy The hierarchy
+ * @param fileName The configuration file.
*/
- public void doConfigure(final InputStream inputStream, final LoggerRepository hierarchy) {
+ public static void configure(final String fileName) {
+ new PropertyConfigurator().doConfigure(fileName, LogManager.getLoggerRepository(), StackLocatorUtil.getCallerClassLoader(2));
}
/**
- * Read configuration options from url configURL.
+ * Reads configuration options from url configURL.
*
* @param configURL The configuration URL
- * @param hierarchy The hierarchy
*/
- public void doConfigure(final URL configURL, final LoggerRepository hierarchy) {
+ public static void configure(final URL configURL) {
+ new PropertyConfigurator().doConfigure(configURL, LogManager.getLoggerRepository(), StackLocatorUtil.getCallerClassLoader(2));
}
/**
- * Read configuration options from configuration file.
+ * Like {@link #configureAndWatch(String, long)} except that the default delay as defined by FileWatchdog.DEFAULT_DELAY
+ * is used.
*
- * @param configFileName The configuration file.
+ * @param configFilename A file in key=value format.
*/
- public static void configure(final String configFileName) {
+ public static void configureAndWatch(final String configFilename) {
+ configureAndWatch(configFilename, FileWatchdog.DEFAULT_DELAY, StackLocatorUtil.getCallerClassLoader(2));
}
/**
- * Read configuration options from url configURL.
+ * Reads the configuration file configFilename if it exists. Moreover, a thread will be created that will
+ * periodically check if configFilename has been created or modified. The period is determined by the
+ * delay argument. If a change or file creation is detected, then configFilename is read to
+ * configure log4j.
*
- * @param configURL The configuration URL
+ * @param configFilename A file in key=value format.
+ * @param delayMillis The delay in milliseconds to wait between each check.
*/
- public static void configure(final URL configURL) {
+ public static void configureAndWatch(final String configFilename, final long delayMillis) {
+ configureAndWatch(configFilename, delayMillis, StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ static void configureAndWatch(final String configFilename, final long delay, final ClassLoader classLoader) {
+ final PropertyWatchdog watchdog = new PropertyWatchdog(configFilename, classLoader);
+ watchdog.setDelay(delay);
+ watchdog.start();
+ }
+
+ private static Configuration reconfigure(final Configuration configuration) {
+ org.apache.logging.log4j.core.config.Configurator.reconfigure(configuration);
+ return configuration;
+ }
+
+ /**
+ * Used internally to keep track of configured appenders.
+ */
+ protected Hashtable registry = new Hashtable(11);
+
+ private LoggerRepository repository;
+
+ protected LoggerFactory loggerFactory = new DefaultCategoryFactory();
+
+ /**
+ * Checks the provided Properties object for a {@link org.apache.log4j.spi.LoggerFactory LoggerFactory}
+ * entry specified by {@link #LOGGER_FACTORY_KEY}. If such an entry exists, an attempt is made to create an instance
+ * using the default constructor. This instance is used for subsequent Category creations within this configurator.
+ *
+ * @see #parseCatsAndRenderers
+ */
+ protected void configureLoggerFactory(final Properties properties) {
+ final String factoryClassName = OptionConverter.findAndSubst(LOGGER_FACTORY_KEY, properties);
+ if (factoryClassName != null) {
+ LogLog.debug("Setting category factory to [" + factoryClassName + "].");
+ loggerFactory = (LoggerFactory) OptionConverter.instantiateByClassName(factoryClassName, LoggerFactory.class, loggerFactory);
+ PropertySetter.setProperties(loggerFactory, properties, FACTORY_PREFIX + ".");
+ }
+ }
+
+ void configureRootCategory(final Properties properties, final LoggerRepository loggerRepository) {
+ String effectiveFrefix = ROOT_LOGGER_PREFIX;
+ String value = OptionConverter.findAndSubst(ROOT_LOGGER_PREFIX, properties);
+
+ if (value == null) {
+ value = OptionConverter.findAndSubst(ROOT_CATEGORY_PREFIX, properties);
+ effectiveFrefix = ROOT_CATEGORY_PREFIX;
+ }
+
+ if (value == null) {
+ LogLog.debug("Could not find root logger information. Is this OK?");
+ } else {
+ final Logger root = loggerRepository.getRootLogger();
+ synchronized (root) {
+ parseCategory(properties, root, effectiveFrefix, INTERNAL_ROOT_NAME, value);
+ }
+ }
}
/**
* Reads configuration options from an InputStream.
*
* @param inputStream The input stream
+ * @param loggerRepository The hierarchy
*/
- public static void configure(final InputStream inputStream) {
+ @Override
+ public void doConfigure(final InputStream inputStream, final LoggerRepository loggerRepository) {
+ doConfigure(inputStream, loggerRepository, StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ Configuration doConfigure(final InputStream inputStream, final LoggerRepository loggerRepository, final ClassLoader classLoader) {
+ return doConfigure(loadProperties(inputStream), loggerRepository, classLoader);
}
/**
- * Read configuration options from properties.
+ * Reads configuration options from properties.
*
* See {@link #doConfigure(String, LoggerRepository)} for the expected format.
*
* @param properties The properties
+ * @param loggerRepository The hierarchy
*/
- public static void configure(final Properties properties) {
+ public void doConfigure(final Properties properties, final LoggerRepository loggerRepository) {
+ doConfigure(properties, loggerRepository, StackLocatorUtil.getCallerClassLoader(2));
}
/**
- * Like {@link #configureAndWatch(String, long)} except that the
- * default delay as defined by FileWatchdog.DEFAULT_DELAY is
- * used.
+ * Reads configuration options from properties.
*
- * @param configFilename A file in key=value format.
+ * See {@link #doConfigure(String, LoggerRepository)} for the expected format.
+ *
+ * @param properties The properties
+ * @param loggerRepository The hierarchy
*/
- public static void configureAndWatch(final String configFilename) {
+ Configuration doConfigure(final Properties properties, final LoggerRepository loggerRepository, final ClassLoader classLoader) {
+ final PropertiesConfiguration configuration = new PropertiesConfiguration(LogManager.getContext(classLoader), properties);
+ configuration.doConfigure();
+
+ repository = loggerRepository;
+// String value = properties.getProperty(LogLog.DEBUG_KEY);
+// if (value == null) {
+// value = properties.getProperty("log4j.configDebug");
+// if (value != null) {
+// LogLog.warn("[log4j.configDebug] is deprecated. Use [log4j.debug] instead.");
+// }
+// }
+//
+// if (value != null) {
+// LogLog.setInternalDebugging(OptionConverter.toBoolean(value, true));
+// }
+//
+// //
+// // if log4j.reset=true then
+// // reset hierarchy
+// final String reset = properties.getProperty(RESET_KEY);
+// if (reset != null && OptionConverter.toBoolean(reset, false)) {
+// hierarchy.resetConfiguration();
+// }
+//
+// final String thresholdStr = OptionConverter.findAndSubst(THRESHOLD_PREFIX, properties);
+// if (thresholdStr != null) {
+// hierarchy.setThreshold(OptionConverter.toLevel(thresholdStr, (Level) Level.ALL));
+// LogLog.debug("Hierarchy threshold set to [" + hierarchy.getThreshold() + "].");
+// }
+//
+// configureRootCategory(properties, hierarchy);
+// configureLoggerFactory(properties);
+// parseCatsAndRenderers(properties, hierarchy);
+//
+ // We don't want to hold references to appenders preventing their
+ // garbage collection.
+ registry.clear();
+
+ return reconfigure(configuration);
}
/**
- * Read the configuration file configFilename if it
- * exists. Moreover, a thread will be created that will periodically
- * check if configFilename has been created or
- * modified. The period is determined by the delay
- * argument. If a change or file creation is detected, then
- * configFilename is read to configure log4j.
+ * Reads configuration options from configuration file.
*
- * @param configFilename A file in key=value format.
- * @param delay The delay in milliseconds to wait between each check.
+ * @param fileName The configuration file
+ * @param loggerRepository The hierarchy
+ */
+ public void doConfigure(final String fileName, final LoggerRepository loggerRepository) {
+ doConfigure(fileName, loggerRepository, StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ /**
+ * Reads configuration options from configuration file.
+ *
+ * @param fileName The configuration file
+ * @param loggerRepository The hierarchy
+ */
+ Configuration doConfigure(final String fileName, final LoggerRepository loggerRepository, final ClassLoader classLoader) {
+ try (InputStream inputStream = Files.newInputStream(Paths.get(fileName))) {
+ return doConfigure(inputStream, loggerRepository, classLoader);
+ } catch (final Exception e) {
+ if (e instanceof InterruptedIOException || e instanceof InterruptedException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.error("Could not read configuration file [" + fileName + "].", e);
+ LogLog.error("Ignoring configuration file [" + fileName + "].");
+ return null;
+ }
+ }
+
+ /**
+ * Read configuration options from url configURL.
+ *
+ * @param url The configuration URL
+ * @param loggerRepository The hierarchy
*/
- public static void configureAndWatch(final String configFilename, final long delay) {
+ @Override
+ public void doConfigure(final URL url, final LoggerRepository loggerRepository) {
+ doConfigure(url, loggerRepository, StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ Configuration doConfigure(final URL url, final LoggerRepository loggerRepository, final ClassLoader classLoader) {
+ LogLog.debug("Reading configuration from URL " + url);
+ try {
+ final URLConnection urlConnection = UrlConnectionFactory.createConnection(url);
+ try (InputStream inputStream = urlConnection.getInputStream()) {
+ return doConfigure(inputStream, loggerRepository, classLoader);
+ }
+ } catch (final IOException e) {
+ LogLog.error("Could not read configuration file from URL [" + url + "].", e);
+ LogLog.error("Ignoring configuration file [" + url + "].");
+ return null;
+ }
+
+ }
+
+ private Properties loadProperties(final InputStream inputStream) {
+ final Properties loaded = new Properties();
+ try {
+ loaded.load(inputStream);
+ } catch (final IOException | IllegalArgumentException e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.error("Could not read configuration file from InputStream [" + inputStream + "].", e);
+ LogLog.error("Ignoring configuration InputStream [" + inputStream + "].");
+ return null;
+ }
+ return loaded;
+ }
+
+ /**
+ * Parse the additivity option for a non-root category.
+ */
+ void parseAdditivityForLogger(final Properties properties, final Logger logger, final String loggerName) {
+ final String value = OptionConverter.findAndSubst(ADDITIVITY_PREFIX + loggerName, properties);
+ LogLog.debug("Handling " + ADDITIVITY_PREFIX + loggerName + "=[" + value + "]");
+ // touch additivity only if necessary
+ if ((value != null) && (!value.equals(""))) {
+ final boolean additivity = OptionConverter.toBoolean(value, true);
+ LogLog.debug("Setting additivity for \"" + loggerName + "\" to " + additivity);
+ logger.setAdditivity(additivity);
+ }
+ }
+
+ Appender parseAppender(final Properties properties, final String appenderName) {
+ Appender appender = registryGet(appenderName);
+ if ((appender != null)) {
+ LogLog.debug("Appender \"" + appenderName + "\" was already parsed.");
+ return appender;
+ }
+ // Appender was not previously initialized.
+ final String prefix = APPENDER_PREFIX + appenderName;
+ final String layoutPrefix = prefix + ".layout";
+
+ appender = (Appender) OptionConverter.instantiateByKey(properties, prefix, org.apache.log4j.Appender.class, null);
+ if (appender == null) {
+ LogLog.error("Could not instantiate appender named \"" + appenderName + "\".");
+ return null;
+ }
+ appender.setName(appenderName);
+
+ if (appender instanceof OptionHandler) {
+ if (appender.requiresLayout()) {
+ final Layout layout = (Layout) OptionConverter.instantiateByKey(properties, layoutPrefix, Layout.class, null);
+ if (layout != null) {
+ appender.setLayout(layout);
+ LogLog.debug("Parsing layout options for \"" + appenderName + "\".");
+ // configureOptionHandler(layout, layoutPrefix + ".", props);
+ PropertySetter.setProperties(layout, properties, layoutPrefix + ".");
+ LogLog.debug("End of parsing for \"" + appenderName + "\".");
+ }
+ }
+ final String errorHandlerPrefix = prefix + ".errorhandler";
+ final String errorHandlerClass = OptionConverter.findAndSubst(errorHandlerPrefix, properties);
+ if (errorHandlerClass != null) {
+ final ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByKey(properties, errorHandlerPrefix, ErrorHandler.class, null);
+ if (eh != null) {
+ appender.setErrorHandler(eh);
+ LogLog.debug("Parsing errorhandler options for \"" + appenderName + "\".");
+ parseErrorHandler(eh, errorHandlerPrefix, properties, repository);
+ final Properties edited = new Properties();
+ final String[] keys = new String[] {errorHandlerPrefix + "." + ROOT_REF, errorHandlerPrefix + "." + LOGGER_REF,
+ errorHandlerPrefix + "." + APPENDER_REF_TAG};
+ for (final Object element : properties.entrySet()) {
+ final Map.Entry entry = (Map.Entry) element;
+ int i = 0;
+ for (; i < keys.length; i++) {
+ if (keys[i].equals(entry.getKey())) {
+ break;
+ }
+ }
+ if (i == keys.length) {
+ edited.put(entry.getKey(), entry.getValue());
+ }
+ }
+ PropertySetter.setProperties(eh, edited, errorHandlerPrefix + ".");
+ LogLog.debug("End of errorhandler parsing for \"" + appenderName + "\".");
+ }
+
+ }
+ // configureOptionHandler((OptionHandler) appender, prefix + ".", props);
+ PropertySetter.setProperties(appender, properties, prefix + ".");
+ LogLog.debug("Parsed \"" + appenderName + "\" options.");
+ }
+ parseAppenderFilters(properties, appenderName, appender);
+ registryPut(appender);
+ return appender;
+ }
+
+ void parseAppenderFilters(final Properties properties, final String appenderName, final Appender appender) {
+ // 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
+ final String filterPrefix = APPENDER_PREFIX + appenderName + ".filter.";
+ final int fIdx = filterPrefix.length();
+ final Hashtable filters = new Hashtable();
+ final Enumeration e = properties.keys();
+ String name = "";
+ while (e.hasMoreElements()) {
+ final String key = (String) e.nextElement();
+ if (key.startsWith(filterPrefix)) {
+ final int dotIdx = key.indexOf('.', fIdx);
+ String filterKey = key;
+ if (dotIdx != -1) {
+ filterKey = key.substring(0, dotIdx);
+ name = key.substring(dotIdx + 1);
+ }
+ Vector filterOpts = (Vector) filters.get(filterKey);
+ if (filterOpts == null) {
+ filterOpts = new Vector();
+ filters.put(filterKey, filterOpts);
+ }
+ if (dotIdx != -1) {
+ final String value = OptionConverter.findAndSubst(key, properties);
+ filterOpts.add(new NameValue(name, value));
+ }
+ }
+ }
+
+ // sort filters by IDs, insantiate filters, set filter options,
+ // add filters to the appender
+ final Enumeration g = new SortedKeyEnumeration(filters);
+ Filter head = null;
+ while (g.hasMoreElements()) {
+ final String key = (String) g.nextElement();
+ final String clazz = properties.getProperty(key);
+ if (clazz != null) {
+ LogLog.debug("Filter key: [" + key + "] class: [" + properties.getProperty(key) + "] props: " + filters.get(key));
+ final Filter filter = (Filter) OptionConverter.instantiateByClassName(clazz, Filter.class, null);
+ if (filter != null) {
+ final PropertySetter propSetter = new PropertySetter(filter);
+ final Vector v = (Vector) filters.get(key);
+ final Enumeration filterProps = v.elements();
+ while (filterProps.hasMoreElements()) {
+ final NameValue kv = (NameValue) filterProps.nextElement();
+ propSetter.setProperty(kv.key, kv.value);
+ }
+ propSetter.activate();
+ LogLog.debug("Adding filter of type [" + filter.getClass() + "] to appender named [" + appender.getName() + "].");
+ head = FilterAdapter.addFilter(head, filter);
+ }
+ } else {
+ LogLog.warn("Missing class definition for filter: [" + key + "]");
+ }
+ }
+ appender.addFilter(head);
+ }
+
+ /**
+ * This method must work for the root category as well.
+ */
+ void parseCategory(final Properties properties, final Logger logger, final String optionKey, final String loggerName, final String value) {
+
+ LogLog.debug("Parsing for [" + loggerName + "] with value=[" + value + "].");
+ // We must skip over ',' but not white space
+ final StringTokenizer st = new StringTokenizer(value, ",");
+
+ // If value is not in the form ", appender.." or "", then we should set
+ // the level of the loggeregory.
+
+ if (!(value.startsWith(",") || value.equals(""))) {
+
+ // just to be on the safe side...
+ if (!st.hasMoreTokens()) {
+ return;
+ }
+
+ final String levelStr = st.nextToken();
+ LogLog.debug("Level token is [" + levelStr + "].");
+
+ // If the level value is inherited, set category level value to
+ // null. We also check that the user has not specified inherited for the
+ // root category.
+ if (INHERITED.equalsIgnoreCase(levelStr) || NULL.equalsIgnoreCase(levelStr)) {
+ if (loggerName.equals(INTERNAL_ROOT_NAME)) {
+ LogLog.warn("The root logger cannot be set to null.");
+ } else {
+ logger.setLevel(null);
+ }
+ } else {
+ logger.setLevel(OptionConverter.toLevel(levelStr, Log4j1Configuration.DEFAULT_LEVEL));
+ }
+ LogLog.debug("Category " + loggerName + " set to " + logger.getLevel());
+ }
+
+ // Begin by removing all existing appenders.
+ logger.removeAllAppenders();
+
+ Appender appender;
+ String appenderName;
+ while (st.hasMoreTokens()) {
+ appenderName = st.nextToken().trim();
+ if (appenderName == null || appenderName.equals(",")) {
+ continue;
+ }
+ LogLog.debug("Parsing appender named \"" + appenderName + "\".");
+ appender = parseAppender(properties, appenderName);
+ if (appender != null) {
+ logger.addAppender(appender);
+ }
+ }
+ }
+
+ /**
+ * Parse non-root elements, such non-root categories and renderers.
+ */
+ protected void parseCatsAndRenderers(final Properties properties, final LoggerRepository loggerRepository) {
+ final Enumeration enumeration = properties.propertyNames();
+ while (enumeration.hasMoreElements()) {
+ final 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());
+ }
+ final String value = OptionConverter.findAndSubst(key, properties);
+ final Logger logger = loggerRepository.getLogger(loggerName, loggerFactory);
+ synchronized (logger) {
+ parseCategory(properties, logger, key, loggerName, value);
+ parseAdditivityForLogger(properties, logger, loggerName);
+ }
+ } else if (key.startsWith(RENDERER_PREFIX)) {
+ final String renderedClass = key.substring(RENDERER_PREFIX.length());
+ final String renderingClass = OptionConverter.findAndSubst(key, properties);
+ if (loggerRepository instanceof RendererSupport) {
+ RendererMap.addRenderer((RendererSupport) loggerRepository, renderedClass, renderingClass);
+ }
+ } else if (key.equals(THROWABLE_RENDERER_PREFIX)) {
+ if (loggerRepository instanceof ThrowableRendererSupport) {
+ final ThrowableRenderer tr = (ThrowableRenderer) OptionConverter.instantiateByKey(properties, THROWABLE_RENDERER_PREFIX,
+ org.apache.log4j.spi.ThrowableRenderer.class, null);
+ if (tr == null) {
+ LogLog.error("Could not instantiate throwableRenderer.");
+ } else {
+ final PropertySetter setter = new PropertySetter(tr);
+ setter.setProperties(properties, THROWABLE_RENDERER_PREFIX + ".");
+ ((ThrowableRendererSupport) loggerRepository).setThrowableRenderer(tr);
+
+ }
+ }
+ }
+ }
+ }
+
+ private void parseErrorHandler(final ErrorHandler errorHandler, final String errorHandlerPrefix, final Properties props,
+ final LoggerRepository loggerRepository) {
+ if (errorHandler != null && loggerRepository != null) {
+ final boolean rootRef = OptionConverter.toBoolean(OptionConverter.findAndSubst(errorHandlerPrefix + ROOT_REF, props), false);
+ if (rootRef) {
+ errorHandler.setLogger(loggerRepository.getRootLogger());
+ }
+ final String loggerName = OptionConverter.findAndSubst(errorHandlerPrefix + LOGGER_REF, props);
+ if (loggerName != null) {
+ final Logger logger = loggerFactory == null ? loggerRepository.getLogger(loggerName) : loggerRepository.getLogger(loggerName, loggerFactory);
+ errorHandler.setLogger(logger);
+ }
+ final String appenderName = OptionConverter.findAndSubst(errorHandlerPrefix + APPENDER_REF_TAG, props);
+ if (appenderName != null) {
+ final Appender backup = parseAppender(props, appenderName);
+ if (backup != null) {
+ errorHandler.setBackupAppender(backup);
+ }
+ }
+ }
+ }
+
+ Appender registryGet(final String name) {
+ return (Appender) registry.get(name);
+ }
+
+ void registryPut(final Appender appender) {
+ registry.put(appender.getName(), appender);
}
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/ProvisionNode.java b/log4j-1.2-api/src/main/java/org/apache/log4j/ProvisionNode.java
new file mode 100644
index 00000000000..31cfb825d6f
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/ProvisionNode.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+import java.util.Vector;
+
+class ProvisionNode extends Vector {
+ private static final long serialVersionUID = -4479121426311014469L;
+
+ ProvisionNode(final Logger logger) {
+ super();
+ this.addElement(logger);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/RenderedMessage.java b/log4j-1.2-api/src/main/java/org/apache/log4j/RenderedMessage.java
new file mode 100644
index 00000000000..422a565a398
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/RenderedMessage.java
@@ -0,0 +1,60 @@
+/*
+ * 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;
+
+import org.apache.log4j.or.ObjectRenderer;
+import org.apache.logging.log4j.message.Message;
+
+/**
+ * Implements object rendering for Log4j 1.x compatibility.
+ */
+public class RenderedMessage implements Message {
+
+ private final ObjectRenderer renderer;
+ private final Object object;
+ private String rendered = null;
+
+ public RenderedMessage(ObjectRenderer renderer, Object object) {
+ this.renderer = renderer;
+ this.object = object;
+ }
+
+
+ @Override
+ public String getFormattedMessage() {
+ if (rendered == null) {
+ rendered = renderer.doRender(object);
+ }
+
+ return rendered;
+ }
+
+ @Override
+ public String getFormat() {
+ return getFormattedMessage();
+ }
+
+ @Override
+ public Object[] getParameters() {
+ return null;
+ }
+
+ @Override
+ public Throwable getThrowable() {
+ return null;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/RollingFileAppender.java b/log4j-1.2-api/src/main/java/org/apache/log4j/RollingFileAppender.java
new file mode 100644
index 00000000000..2577b9a271b
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/RollingFileAppender.java
@@ -0,0 +1,253 @@
+/*
+ * 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;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.Writer;
+
+import org.apache.log4j.helpers.CountingQuietWriter;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * RollingFileAppender extends FileAppender to backup the log files when they reach a certain size.
+ *
+ * The log4j extras companion includes alternatives which should be considered for new deployments and which are
+ * discussed in the documentation for org.apache.log4j.rolling.RollingFileAppender.
+ */
+public class RollingFileAppender extends FileAppender {
+
+ /**
+ * The default maximum file size is 10MB.
+ */
+ protected long maxFileSize = 10 * 1024 * 1024;
+
+ /**
+ * There is one backup file by default.
+ */
+ protected int maxBackupIndex = 1;
+
+ private long nextRollover = 0;
+
+ /**
+ * The default constructor simply calls its {@link FileAppender#FileAppender parents constructor}.
+ */
+ public RollingFileAppender() {
+ super();
+ }
+
+ /**
+ * Constructs a RollingFileAppender and open the file designated by filename. The opened filename will
+ * become the ouput destination for this appender.
+ *
+ *
+ * If the append parameter is true, the file will be appended to. Otherwise, the file desginated by
+ * filename will be truncated before being opened.
+ *
+ */
+ public RollingFileAppender(Layout layout, String filename, boolean append) throws IOException {
+ super(layout, filename, append);
+ }
+
+ /**
+ * Constructs a FileAppender and open the file designated by filename. The opened filename will become the
+ * output destination for this appender.
+ *
+ *
+ * The file will be appended to.
+ *
+ */
+ public RollingFileAppender(Layout layout, String filename) throws IOException {
+ super(layout, filename);
+ }
+
+ /**
+ * Gets the value of the MaxBackupIndex option.
+ */
+ public int getMaxBackupIndex() {
+ return maxBackupIndex;
+ }
+
+ /**
+ * Gets the maximum size that the output file is allowed to reach before being rolled over to backup files.
+ *
+ * @since 1.1
+ */
+ public long getMaximumFileSize() {
+ return maxFileSize;
+ }
+
+ /**
+ * Implements the usual roll over behaviour.
+ *
+ * If MaxBackupIndex is positive, then files {File.1, ...,
+ * File.MaxBackupIndex -1} are renamed to {File.2, ..., File.MaxBackupIndex}.
+ * Moreover, File is renamed File.1 and closed. A new File is created to receive
+ * further log output.
+ *
+ *
+ * If MaxBackupIndex is equal to zero, then the File is truncated with no backup files
+ * created.
+ *
+ */
+ public // synchronization not necessary since doAppend is alreasy synched
+ void rollOver() {
+ File target;
+ File file;
+
+ if (qw != null) {
+ long size = ((CountingQuietWriter) qw).getCount();
+ LogLog.debug("rolling over count=" + size);
+ // if operation fails, do not roll again until
+ // maxFileSize more bytes are written
+ nextRollover = size + maxFileSize;
+ }
+ LogLog.debug("maxBackupIndex=" + maxBackupIndex);
+
+ boolean renameSucceeded = true;
+ // If maxBackups <= 0, then there is no file renaming to be done.
+ if (maxBackupIndex > 0) {
+ // Delete the oldest file, to keep Windows happy.
+ file = new File(fileName + '.' + maxBackupIndex);
+ if (file.exists())
+ renameSucceeded = file.delete();
+
+ // Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3, 2}
+ for (int i = maxBackupIndex - 1; i >= 1 && renameSucceeded; i--) {
+ file = new File(fileName + "." + i);
+ if (file.exists()) {
+ target = new File(fileName + '.' + (i + 1));
+ LogLog.debug("Renaming file " + file + " to " + target);
+ renameSucceeded = file.renameTo(target);
+ }
+ }
+
+ if (renameSucceeded) {
+ // Rename fileName to fileName.1
+ target = new File(fileName + "." + 1);
+
+ this.closeFile(); // keep windows happy.
+
+ file = new File(fileName);
+ LogLog.debug("Renaming file " + file + " to " + target);
+ renameSucceeded = file.renameTo(target);
+ //
+ // if file rename failed, reopen file with append = true
+ //
+ if (!renameSucceeded) {
+ try {
+ this.setFile(fileName, true, bufferedIO, bufferSize);
+ } catch (IOException e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.error("setFile(" + fileName + ", true) call failed.", e);
+ }
+ }
+ }
+ }
+
+ //
+ // if all renames were successful, then
+ //
+ if (renameSucceeded) {
+ try {
+ // This will also close the file. This is OK since multiple
+ // close operations are safe.
+ this.setFile(fileName, false, bufferedIO, bufferSize);
+ nextRollover = 0;
+ } catch (IOException e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.error("setFile(" + fileName + ", false) call failed.", e);
+ }
+ }
+ }
+
+ public synchronized void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize) throws IOException {
+ super.setFile(fileName, append, this.bufferedIO, this.bufferSize);
+ if (append) {
+ File f = new File(fileName);
+ ((CountingQuietWriter) qw).setCount(f.length());
+ }
+ }
+
+ /**
+ * Sets the maximum number of backup files to keep around.
+ *
+ *
+ * The MaxBackupIndex option determines how many backup files are kept before the oldest is erased. This option
+ * takes a positive integer value. If set to zero, then there will be no backup files and the log file will be truncated
+ * when it reaches MaxFileSize.
+ *
+ */
+ public void setMaxBackupIndex(int maxBackups) {
+ this.maxBackupIndex = maxBackups;
+ }
+
+ /**
+ * Sets the maximum size that the output file is allowed to reach before being rolled over to backup files.
+ *
+ *
+ * This method is equivalent to {@link #setMaxFileSize} except that it is required for differentiating the setter taking
+ * a long argument from the setter taking a String argument by the JavaBeans
+ * {@link java.beans.Introspector Introspector}.
+ *
+ *
+ * @see #setMaxFileSize(String)
+ */
+ public void setMaximumFileSize(long maxFileSize) {
+ this.maxFileSize = maxFileSize;
+ }
+
+ /**
+ * Sets the maximum size that the output file is allowed to reach before being rolled over to backup files.
+ *
+ *
+ * In configuration files, the MaxFileSize option takes an long integer in the range 0 - 2^63. You can specify
+ * the value with the suffixes "KB", "MB" or "GB" so that the integer is interpreted being expressed respectively in
+ * kilobytes, megabytes or gigabytes. For example, the value "10KB" will be interpreted as 10240.
+ *
+ */
+ public void setMaxFileSize(String value) {
+ maxFileSize = OptionConverter.toFileSize(value, maxFileSize + 1);
+ }
+
+ protected void setQWForFiles(Writer writer) {
+ this.qw = new CountingQuietWriter(writer, errorHandler);
+ }
+
+ /**
+ * This method differentiates RollingFileAppender from its super class.
+ *
+ * @since 0.9.0
+ */
+ protected void subAppend(LoggingEvent event) {
+ super.subAppend(event);
+ if (fileName != null && qw != null) {
+ long size = ((CountingQuietWriter) qw).getCount();
+ if (size >= maxFileSize && size >= nextRollover) {
+ rollOver();
+ }
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/SimpleLayout.java b/log4j-1.2-api/src/main/java/org/apache/log4j/SimpleLayout.java
new file mode 100644
index 00000000000..2ed850dc360
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/SimpleLayout.java
@@ -0,0 +1,43 @@
+/*
+ * 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;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.util.Strings;
+
+/**
+ * Simple-layout.
+ */
+public class SimpleLayout extends Layout {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String format(final LoggingEvent theEvent) {
+ return Strings.EMPTY;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean ignoresThrowable() {
+ return true;
+ }
+
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/VectorAppender.java b/log4j-1.2-api/src/main/java/org/apache/log4j/VectorAppender.java
new file mode 100644
index 00000000000..97af6055bf9
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/VectorAppender.java
@@ -0,0 +1,79 @@
+/*
+ * 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;
+
+import java.util.Vector;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Appends logging events to a vector.
+ */
+public class VectorAppender extends AppenderSkeleton {
+
+ public Vector vector;
+
+ public VectorAppender() {
+ vector = new Vector();
+ }
+
+ /**
+ * Does nothing.
+ */
+ @Override
+ public void activateOptions() {
+ // noop
+ }
+
+ /**
+ * This method is called by the {@link AppenderSkeleton#doAppend} method.
+ *
+ */
+ @Override
+ public void append(final LoggingEvent event) {
+ // System.out.println("---Vector appender called with message ["+event.getRenderedMessage()+"].");
+ // System.out.flush();
+ try {
+ Thread.sleep(100);
+ } catch (final Exception e) {
+ // ignore
+ }
+ vector.addElement(event);
+ }
+
+ @Override
+ public synchronized void close() {
+ if (this.closed) {
+ return;
+ }
+ this.closed = true;
+ }
+
+ public Vector getVector() {
+ return vector;
+ }
+
+ public boolean isClosed() {
+ return closed;
+ }
+
+ @Override
+ public boolean requiresLayout() {
+ return false;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/WriterAppender.java b/log4j-1.2-api/src/main/java/org/apache/log4j/WriterAppender.java
new file mode 100644
index 00000000000..760449c2374
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/WriterAppender.java
@@ -0,0 +1,396 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+import org.apache.log4j.helpers.QuietWriter;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.status.StatusLogger;
+
+
+/**
+ * WriterAppender appends log events to a {@link Writer} or an
+ * {@link OutputStream} depending on the user's choice.
+ */
+public class WriterAppender extends AppenderSkeleton {
+ private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger();
+
+ /**
+ * Immediate flush means that the underlying writer or output stream
+ * will be flushed at the end of each append operation unless shouldFlush()
+ * is overridden. Immediate
+ * flush is slower but ensures that each append request is actually
+ * written. If immediateFlush is set to
+ * false, then there is a good chance that the last few
+ * logs events are not actually written to persistent media if and
+ * when the application crashes.
+ *
+ *
The immediateFlush variable is set to
+ * true by default.
+ */
+ protected boolean immediateFlush = true;
+
+ /**
+ * The encoding to use when writing.
The
+ * encoding variable is set to null by
+ * default which results in the utilization of the system's default
+ * encoding.
+ */
+ protected String encoding;
+
+ /**
+ * This is the {@link QuietWriter quietWriter} where we will write
+ * to.
+ */
+ protected QuietWriter qw;
+
+
+ /**
+ * This default constructor does nothing.
+ */
+ public WriterAppender() {
+ }
+
+ /**
+ * Instantiate a WriterAppender and set the output destination to a
+ * new {@link OutputStreamWriter} initialized with os
+ * as its {@link OutputStream}.
+ * @param layout The Layout.
+ * @param os The OutputStream.
+ */
+ public WriterAppender(Layout layout, OutputStream os) {
+ this(layout, new OutputStreamWriter(os));
+ }
+
+ /**
+ * Instantiate a WriterAppender and set the output destination to
+ * writer.
+ *
+ *
The writer must have been previously opened by
+ * the user.
+ *
+ * @param layout The Layout.
+ * @param writer The Writer.
+ */
+ public WriterAppender(Layout layout, Writer writer) {
+ this.layout = layout;
+ this.setWriter(writer);
+ }
+
+ /**
+ * Returns value of the ImmediateFlush option.
+ * @return the value of the immediate flush setting.
+ */
+ public boolean getImmediateFlush() {
+ return immediateFlush;
+ }
+
+ /**
+ * If the ImmediateFlush option is set to
+ * true, the appender will flush at the end of each
+ * write. This is the default behavior. If the option is set to
+ * false, then the underlying stream can defer writing
+ * to physical medium to a later time.
+ *
+ *
Avoiding the flush operation at the end of each append results in
+ * a performance gain of 10 to 20 percent. However, there is safety
+ * tradeoff involved in skipping flushing. Indeed, when flushing is
+ * skipped, then it is likely that the last few log events will not
+ * be recorded on disk when the application exits. This is a high
+ * price to pay even for a 20% performance gain.
+ *
+ * @param value the value to set the immediate flush setting to.
+ */
+ public void setImmediateFlush(boolean value) {
+ immediateFlush = value;
+ }
+
+ /**
+ * Does nothing.
+ */
+ @Override
+ public void activateOptions() {
+ }
+
+
+ /**
+ * This method is called by the {@link AppenderSkeleton#doAppend}
+ * method.
+ *
+ *
If the output stream exists and is writable then write a log
+ * statement to the output stream. Otherwise, write a single warning
+ * message to System.err.
+ *
+ *
The format of the output will depend on this appender's
+ * layout.
+ */
+ @Override
+ public void append(LoggingEvent event) {
+
+ // Reminder: the nesting of calls is:
+ //
+ // doAppend()
+ // - check threshold
+ // - filter
+ // - append();
+ // - checkEntryConditions();
+ // - subAppend();
+
+ if (!checkEntryConditions()) {
+ return;
+ }
+ subAppend(event);
+ }
+
+ /**
+ * This method determines if there is a sense in attempting to append.
+ *
+ *
It checks whether there is a set output target and also if
+ * there is a set layout. If these checks fail, then the boolean
+ * value false is returned.
+ * @return true if appending is allowed, false otherwise.
+ */
+ protected boolean checkEntryConditions() {
+ if (this.closed) {
+ LOGGER.warn("Not allowed to write to a closed appender.");
+ return false;
+ }
+
+ if (this.qw == null) {
+ errorHandler.error("No output stream or file set for the appender named [" + name + "].");
+ return false;
+ }
+
+ if (this.layout == null) {
+ errorHandler.error("No layout set for the appender named [" + name + "].");
+ return false;
+ }
+ return true;
+ }
+
+
+ /**
+ * Close this appender instance. The underlying stream or writer is
+ * also closed.
+ *
+ *
Closed appenders cannot be reused.
+ *
+ * @see #setWriter
+ * @since 0.8.4
+ */
+ @Override
+ public synchronized void close() {
+ if (this.closed) {
+ return;
+ }
+ this.closed = true;
+ writeFooter();
+ reset();
+ }
+
+ /**
+ * Close the underlying {@link Writer}.
+ */
+ protected void closeWriter() {
+ if (qw != null) {
+ try {
+ qw.close();
+ } catch (IOException e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ // There is do need to invoke an error handler at this late
+ // stage.
+ LOGGER.error("Could not close " + qw, e);
+ }
+ }
+ }
+
+ /**
+ * Returns an OutputStreamWriter when passed an OutputStream. The
+ * encoding used will depend on the value of the
+ * encoding property. If the encoding value is
+ * specified incorrectly the writer will be opened using the default
+ * system encoding (an error message will be printed to the LOGGER.
+ * @param os The OutputStream.
+ * @return The OutputStreamWriter.
+ */
+ protected OutputStreamWriter createWriter(OutputStream os) {
+ OutputStreamWriter retval = null;
+
+ String enc = getEncoding();
+ if (enc != null) {
+ try {
+ retval = new OutputStreamWriter(os, enc);
+ } catch (IOException e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.warn("Error initializing output writer.");
+ LOGGER.warn("Unsupported encoding?");
+ }
+ }
+ if (retval == null) {
+ retval = new OutputStreamWriter(os);
+ }
+ return retval;
+ }
+
+ public String getEncoding() {
+ return encoding;
+ }
+
+ public void setEncoding(String value) {
+ encoding = value;
+ }
+
+
+ /**
+ * Set the {@link ErrorHandler} for this WriterAppender and also the
+ * underlying {@link QuietWriter} if any.
+ */
+ @Override
+ public synchronized void setErrorHandler(ErrorHandler eh) {
+ if (eh == null) {
+ LOGGER.warn("You have tried to set a null error-handler.");
+ } else {
+ this.errorHandler = eh;
+ if (this.qw != null) {
+ this.qw.setErrorHandler(eh);
+ }
+ }
+ }
+
+ /**
+ *
Sets the Writer where the log output will go. The
+ * specified Writer must be opened by the user and be
+ * writable.
+ *
+ *
The java.io.Writer will be closed when the
+ * appender instance is closed.
+ *
+ *
+ *
WARNING: Logging to an unopened Writer will fail.
+ *
+ *
+ * @param writer An already opened Writer.
+ */
+ public synchronized void setWriter(Writer writer) {
+ reset();
+ this.qw = new QuietWriter(writer, errorHandler);
+ //this.tp = new TracerPrintWriter(qw);
+ writeHeader();
+ }
+
+
+ /**
+ * Actual writing occurs here.
+ *
+ *
Most subclasses of WriterAppender will need to
+ * override this method.
+ * @param event The event to log.
+ *
+ * @since 0.9.0
+ */
+ protected void subAppend(LoggingEvent event) {
+ this.qw.write(this.layout.format(event));
+
+ if (layout.ignoresThrowable()) {
+ String[] s = event.getThrowableStrRep();
+ if (s != null) {
+ int len = s.length;
+ for (int i = 0; i < len; i++) {
+ this.qw.write(s[i]);
+ this.qw.write(Layout.LINE_SEP);
+ }
+ }
+ }
+
+ if (shouldFlush(event)) {
+ this.qw.flush();
+ }
+ }
+
+
+ /**
+ * The WriterAppender requires a layout. Hence, this method returns
+ * true.
+ */
+ @Override
+ public boolean requiresLayout() {
+ return true;
+ }
+
+ /**
+ * Clear internal references to the writer and other variables.
+ *
+ * Subclasses can override this method for an alternate closing
+ * behavior.
+ */
+ protected void reset() {
+ closeWriter();
+ this.qw = null;
+ //this.tp = null;
+ }
+
+
+ /**
+ * Write a footer as produced by the embedded layout's {@link
+ * Layout#getFooter} method.
+ */
+ protected void writeFooter() {
+ if (layout != null) {
+ String f = layout.getFooter();
+ if (f != null && this.qw != null) {
+ this.qw.write(f);
+ this.qw.flush();
+ }
+ }
+ }
+
+ /**
+ * Write a header as produced by the embedded layout's {@link
+ * Layout#getHeader} method.
+ */
+ protected void writeHeader() {
+ if (layout != null) {
+ String h = layout.getHeader();
+ if (h != null && this.qw != null) {
+ this.qw.write(h);
+ }
+ }
+ }
+
+ /**
+ * Determines whether the writer should be flushed after
+ * this event is written.
+ * @param event The event to log.
+ * @return true if the writer should be flushed.
+ *
+ * @since 1.2.16
+ */
+ protected boolean shouldFlush(final LoggingEvent event) {
+ return immediateFlush;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderAdapter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderAdapter.java
new file mode 100644
index 00000000000..23931b2ee65
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderAdapter.java
@@ -0,0 +1,98 @@
+/*
+ * 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.bridge;
+
+import java.io.Serializable;
+
+import org.apache.log4j.Appender;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.config.Property;
+import org.apache.logging.log4j.util.Strings;
+
+/**
+ * Binds a Log4j 1.x Appender to Log4j 2.
+ */
+public class AppenderAdapter {
+
+ private final Appender appender;
+ private final Adapter adapter;
+
+ /**
+ * Adapts a Log4j 1.x appender into a Log4j 2.x appender. Applying this method
+ * on the result of
+ * {@link AppenderWrapper#adapt(org.apache.logging.log4j.core.Appender)} should
+ * return the original Log4j 2.x appender.
+ *
+ * @param appender a Log4j 1.x appender
+ * @return a Log4j 2.x appender or {@code null} if the parameter is {@code null}
+ */
+ public static org.apache.logging.log4j.core.Appender adapt(Appender appender) {
+ if (appender instanceof org.apache.logging.log4j.core.Appender) {
+ return (org.apache.logging.log4j.core.Appender) appender;
+ }
+ if (appender instanceof AppenderWrapper) {
+ return ((AppenderWrapper) appender).getAppender();
+ }
+ if (appender != null) {
+ return new AppenderAdapter(appender).getAdapter();
+ }
+ return null;
+ }
+
+ /**
+ * Constructor.
+ * @param appender The Appender to wrap.
+ */
+ private AppenderAdapter(Appender appender) {
+ this.appender = appender;
+ final org.apache.logging.log4j.core.Filter appenderFilter = FilterAdapter.adapt(appender.getFilter());
+ String name = appender.getName();
+ if (Strings.isEmpty(name)) {
+ name = String.format("0x%08x", appender.hashCode());
+ }
+ this.adapter = new Adapter(name, appenderFilter, null, true, null);
+ }
+
+ public Adapter getAdapter() {
+ return adapter;
+ }
+
+ public class Adapter extends AbstractAppender {
+
+ protected Adapter(final String name, final Filter filter, final Layout extends Serializable> layout,
+ final boolean ignoreExceptions, final Property[] properties) {
+ super(name, filter, layout, ignoreExceptions, properties);
+ }
+
+ @Override
+ public void append(LogEvent event) {
+ appender.doAppend(new LogEventAdapter(event));
+ }
+
+ @Override
+ public void stop() {
+ appender.close();
+ }
+
+ public Appender getAppender() {
+ return appender;
+ }
+ }
+}
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
new file mode 100644
index 00000000000..d4b1869cd0a
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderWrapper.java
@@ -0,0 +1,146 @@
+/*
+ * 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.bridge;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderAdapter.Adapter;
+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;
+
+/**
+ * Wraps 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 {
+
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ private final org.apache.logging.log4j.core.Appender appender;
+
+ /**
+ * Adapts a Log4j 2.x appender into a Log4j 1.x appender. Applying this method
+ * on the result of {@link AppenderAdapter#adapt(Appender)} should return the
+ * original Log4j 1.x appender.
+ *
+ * @param appender a Log4j 2.x appender
+ * @return a Log4j 1.x appender or {@code null} if the parameter is {@code null}
+ */
+ public static Appender adapt(org.apache.logging.log4j.core.Appender appender) {
+ if (appender instanceof Appender) {
+ return (Appender) appender;
+ }
+ if (appender instanceof Adapter) {
+ Adapter adapter = (Adapter) appender;
+ // Don't unwrap an appender with filters
+ if (!adapter.hasFilter()) {
+ return adapter.getAppender();
+ }
+ }
+ if (appender != null) {
+ return new AppenderWrapper(appender);
+ }
+ return null;
+ }
+
+ /**
+ * Constructs a new instance for a Core Appender.
+ *
+ * @param appender a Core Appender.
+ */
+ public AppenderWrapper(org.apache.logging.log4j.core.Appender appender) {
+ this.appender = appender;
+ }
+
+ /**
+ * Gets the wrapped Core Appender.
+ *
+ * @return the wrapped Core Appender.
+ */
+ public org.apache.logging.log4j.core.Appender getAppender() {
+ return appender;
+ }
+
+ @Override
+ public void addFilter(Filter newFilter) {
+ if (appender instanceof AbstractFilterable) {
+ ((AbstractFilterable) appender).addFilter(FilterAdapter.adapt(newFilter));
+ } else {
+ LOGGER.warn("Unable to add filter to appender {}, it does not support filters", appender.getName());
+ }
+ }
+
+ @Override
+ public Filter getFilter() {
+ return null;
+ }
+
+ @Override
+ public void clearFilters() {
+ // noop
+ }
+
+ @Override
+ public void close() {
+ // Not supported with Log4j 2.
+ }
+
+ @Override
+ public void doAppend(LoggingEvent event) {
+ if (event instanceof LogEventAdapter) {
+ appender.append(((LogEventAdapter) event).getEvent());
+ }
+ }
+
+ @Override
+ public String getName() {
+ return appender.getName();
+ }
+
+ @Override
+ public void setErrorHandler(ErrorHandler errorHandler) {
+ appender.setHandler(new ErrorHandlerAdapter(errorHandler));
+ }
+
+ @Override
+ public ErrorHandler getErrorHandler() {
+ return ((ErrorHandlerAdapter) appender.getHandler()).getHandler();
+ }
+
+ @Override
+ public void setLayout(Layout layout) {
+ // Log4j 2 doesn't support this.
+ }
+
+ @Override
+ public Layout getLayout() {
+ return new LayoutWrapper(appender.getLayout());
+ }
+
+ @Override
+ public void setName(String name) {
+ // Log4j 2 doesn't support this.
+ }
+
+ @Override
+ public boolean requiresLayout() {
+ return false;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/ErrorHandlerAdapter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/ErrorHandlerAdapter.java
new file mode 100644
index 00000000000..1f166a25b02
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/ErrorHandlerAdapter.java
@@ -0,0 +1,59 @@
+/*
+ * 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.bridge;
+
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.logging.log4j.core.LogEvent;
+
+/**
+ * Makes a Log4j 1 ErrorHandler usable by a Log4j 2 Appender.
+ */
+public class ErrorHandlerAdapter implements org.apache.logging.log4j.core.ErrorHandler {
+
+ private final ErrorHandler errorHandler;
+
+ public ErrorHandlerAdapter(ErrorHandler errorHandler) {
+ this.errorHandler = errorHandler;
+ }
+
+ public ErrorHandler getHandler() {
+ return errorHandler;
+ }
+
+ @Override
+ public void error(String msg) {
+ errorHandler.error(msg);
+ }
+
+ @Override
+ public void error(String msg, Throwable t) {
+ if (t instanceof Exception) {
+ errorHandler.error(msg, (Exception) t, 0);
+ } else {
+ errorHandler.error(msg);
+ }
+ }
+
+ @Override
+ public void error(String msg, LogEvent event, Throwable t) {
+ if (t == null || t instanceof Exception) {
+ errorHandler.error(msg, (Exception) t, 0, new LogEventAdapter(event));
+ } else {
+ errorHandler.error(msg);
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/FilterAdapter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/FilterAdapter.java
new file mode 100644
index 00000000000..71104333bd8
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/FilterAdapter.java
@@ -0,0 +1,112 @@
+/*
+ * 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.bridge;
+
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.filter.AbstractFilter;
+import org.apache.logging.log4j.core.filter.CompositeFilter;
+
+/**
+ * Binds a Log4j 1.x Filter with Log4j 2.
+ */
+public class FilterAdapter extends AbstractFilter {
+
+ private final Filter filter;
+
+ /**
+ * Adapts a Log4j 1.x filter into a Log4j 2.x filter. Applying this method to
+ * the result of
+ * {@link FilterWrapper#adapt(org.apache.logging.log4j.core.Filter)} should
+ * return the original Log4j 2.x filter.
+ *
+ * @param filter a Log4j 1.x filter
+ * @return a Log4j 2.x filter or {@code null} if the parameter is {@code null}
+ */
+ public static org.apache.logging.log4j.core.Filter adapt(Filter filter) {
+ if (filter instanceof org.apache.logging.log4j.core.Filter) {
+ return (org.apache.logging.log4j.core.Filter) filter;
+ }
+ // Don't unwrap the head of a filter chain
+ if (filter instanceof FilterWrapper && filter.getNext() == null) {
+ return ((FilterWrapper) filter).getFilter();
+ }
+ if (filter != null) {
+ return new FilterAdapter(filter);
+ }
+ return null;
+ }
+
+ /**
+ * Appends one filter to another using Log4j 2.x concatenation utilities.
+ * @param first
+ * @param second
+ * @return
+ */
+ public static Filter addFilter(Filter first, Filter second) {
+ if (first == null) {
+ return second;
+ }
+ if (second == null) {
+ return first;
+ }
+ final CompositeFilter composite;
+ if (first instanceof FilterWrapper && ((FilterWrapper) first).getFilter() instanceof CompositeFilter) {
+ composite = (CompositeFilter) ((FilterWrapper) first).getFilter();
+ } else {
+ composite = CompositeFilter.createFilters(new org.apache.logging.log4j.core.Filter[] {adapt(first)});
+ }
+ return FilterWrapper.adapt(composite.addFilter(adapt(second)));
+ }
+
+ private FilterAdapter(Filter filter) {
+ this.filter = filter;
+ }
+
+ @Override
+ public Result filter(LogEvent event) {
+ LoggingEvent loggingEvent = new LogEventAdapter(event);
+ Filter next = filter;
+ while (next != null) {
+ switch (next.decide(loggingEvent)) {
+ case Filter.ACCEPT:
+ return Result.ACCEPT;
+ case Filter.DENY:
+ return Result.DENY;
+ default:
+ }
+ next = next.getNext();
+ }
+ return Result.NEUTRAL;
+ }
+
+ /**
+ * Gets the actual filter.
+ *
+ * @return the actual filter.
+ * @since 2.17.1
+ */
+ public Filter getFilter() {
+ return filter;
+ }
+
+ @Override
+ public void start() {
+ filter.activateOptions();
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/FilterWrapper.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/FilterWrapper.java
new file mode 100644
index 00000000000..9783be38cad
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/FilterWrapper.java
@@ -0,0 +1,68 @@
+/*
+ * 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.bridge;
+
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * This acts as a container for Log4j 2 Filters to be attached to Log4j 1 components. However, the Log4j 2
+ * Filters will always be called directly so this class just acts as a container.
+ */
+public class FilterWrapper extends Filter {
+
+ private final org.apache.logging.log4j.core.Filter filter;
+
+ /**
+ * Adapts a Log4j 2.x filter into a Log4j 1.x filter. Applying this method to
+ * the result of {@link FilterAdapter#adapt(Filter)} should return the original
+ * Log4j 1.x filter.
+ *
+ * @param filter a Log4j 2.x filter
+ * @return a Log4j 1.x filter or {@code null} if the parameter is {@code null}
+ */
+ public static Filter adapt(org.apache.logging.log4j.core.Filter filter) {
+ if (filter instanceof Filter) {
+ return (Filter) filter;
+ }
+ if (filter instanceof FilterAdapter) {
+ return ((FilterAdapter) filter).getFilter();
+ }
+ if (filter != null) {
+ return new FilterWrapper(filter);
+ }
+ return null;
+ }
+
+ public FilterWrapper(org.apache.logging.log4j.core.Filter filter) {
+ this.filter = filter;
+ }
+
+ public org.apache.logging.log4j.core.Filter getFilter() {
+ return filter;
+ }
+
+ /**
+ * This method is never called.
+ * @param event The LoggingEvent to decide upon.
+ * @return 0
+ */
+ @Override
+ public int decide(LoggingEvent event) {
+ return 0;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LayoutAdapter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LayoutAdapter.java
new file mode 100644
index 00000000000..a98b0a86466
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LayoutAdapter.java
@@ -0,0 +1,95 @@
+/*
+ * 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.bridge;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.layout.ByteBufferDestination;
+
+/**
+ * Class Description goes here.
+ */
+public class LayoutAdapter implements org.apache.logging.log4j.core.Layout {
+ private final Layout layout;
+
+ /**
+ * Adapts a Log4j 1.x layout into a Log4j 2.x layout. Applying this method to
+ * the result of
+ * {@link LayoutWrapper#adapt(org.apache.logging.log4j.core.Layout)} should
+ * return the original Log4j 2.x layout.
+ *
+ * @param layout a Log4j 1.x layout
+ * @return a Log4j 2.x layout or {@code null} if the parameter is {@code null}
+ */
+ public static org.apache.logging.log4j.core.Layout> adapt(Layout layout) {
+ if (layout instanceof LayoutWrapper) {
+ return ((LayoutWrapper) layout).getLayout();
+ }
+ if (layout != null) {
+ return new LayoutAdapter(layout);
+ }
+ return null;
+ }
+
+ private LayoutAdapter(Layout layout) {
+ this.layout = layout;
+ }
+
+ public Layout getLayout() {
+ return layout;
+ }
+
+ @Override
+ public byte[] getFooter() {
+ return layout.getFooter() == null ? null : layout.getFooter().getBytes();
+ }
+
+ @Override
+ public byte[] getHeader() {
+ return layout.getHeader() == null ? null : layout.getHeader().getBytes();
+ }
+
+ @Override
+ public byte[] toByteArray(LogEvent event) {
+ String result = layout.format(new LogEventAdapter(event));
+ return result == null ? null : result.getBytes();
+ }
+
+ @Override
+ public String toSerializable(LogEvent event) {
+ return layout.format(new LogEventAdapter(event));
+ }
+
+ @Override
+ public String getContentType() {
+ return layout.getContentType();
+ }
+
+ @Override
+ public Map getContentFormat() {
+ return new HashMap<>();
+ }
+
+ @Override
+ public void encode(LogEvent event, ByteBufferDestination destination) {
+ final byte[] data = toByteArray(event);
+ destination.writeBytes(data, 0, data.length);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LayoutWrapper.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LayoutWrapper.java
new file mode 100644
index 00000000000..76d6d5de6eb
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LayoutWrapper.java
@@ -0,0 +1,79 @@
+/*
+ * 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.bridge;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Bridge between the Log4j 1 Layout and a Log4j 2 Layout.
+ */
+public class LayoutWrapper extends Layout {
+
+ private final org.apache.logging.log4j.core.Layout> layout;
+
+ /**
+ * Adapts a Log4j 2.x layout into a Log4j 1.x layout. Applying this method to
+ * the result of {@link LayoutAdapter#adapt(Layout)} should return the original
+ * Log4j 1.x layout.
+ *
+ * @param layout a Log4j 2.x layout
+ * @return a Log4j 1.x layout or {@code null} if the parameter is {@code null}
+ */
+ public static Layout adapt(org.apache.logging.log4j.core.Layout> layout) {
+ if (layout instanceof LayoutAdapter) {
+ return ((LayoutAdapter) layout).getLayout();
+ }
+ if (layout != null) {
+ return new LayoutWrapper(layout);
+ }
+ return null;
+ }
+
+ /**
+ * Constructs a new instance.
+ *
+ * @param layout The layout to wrap.
+ */
+ public LayoutWrapper(org.apache.logging.log4j.core.Layout> layout) {
+ this.layout = layout;
+ }
+
+ @Override
+ public String format(LoggingEvent event) {
+ return layout.toSerializable(((LogEventAdapter)event).getEvent()).toString();
+ }
+
+ /**
+ * Unwraps.
+ *
+ * @return The wrapped object.
+ */
+ public org.apache.logging.log4j.core.Layout> getLayout() {
+ return this.layout;
+ }
+
+ @Override
+ public boolean ignoresThrowable() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("LayoutWrapper [layout=%s]", layout);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LogEventAdapter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LogEventAdapter.java
new file mode 100644
index 00000000000..a3ce275335f
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LogEventAdapter.java
@@ -0,0 +1,215 @@
+/*
+ * 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.bridge;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.log4j.Category;
+import org.apache.log4j.Level;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.ThrowableInformation;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.util.Loader;
+import org.apache.logging.log4j.core.util.Throwables;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.Strings;
+
+/**
+ * Converts a Log4j 2 LogEvent into the components needed by a Log4j 1.x LoggingEvent.
+ * This class requires Log4j 2.
+ */
+public class LogEventAdapter extends LoggingEvent {
+
+ private static final long JVM_START_TIME = initStartTime();
+
+ private final LogEvent event;
+
+ public LogEventAdapter(LogEvent event) {
+ this.event = event;
+ }
+
+ /**
+ * Returns the time when the application started, in milliseconds
+ * elapsed since 01.01.1970.
+ * @return the time when the JVM started.
+ */
+ public static long getStartTime() {
+ return JVM_START_TIME;
+ }
+
+ /**
+ * Returns the result of {@code ManagementFactory.getRuntimeMXBean().getStartTime()},
+ * or the current system time if JMX is not available.
+ */
+ private static long initStartTime() {
+ // We'd like to call ManagementFactory.getRuntimeMXBean().getStartTime(),
+ // but Google App Engine throws a java.lang.NoClassDefFoundError
+ // "java.lang.management.ManagementFactory is a restricted class".
+ // The reflection is necessary because without it, Google App Engine
+ // will refuse to initialize this class.
+ try {
+ final Class> factoryClass = Loader.loadSystemClass("java.lang.management.ManagementFactory");
+ final Method getRuntimeMXBean = factoryClass.getMethod("getRuntimeMXBean");
+ final Object runtimeMXBean = getRuntimeMXBean.invoke(null);
+
+ final Class> runtimeMXBeanClass = Loader.loadSystemClass("java.lang.management.RuntimeMXBean");
+ final Method getStartTime = runtimeMXBeanClass.getMethod("getStartTime");
+ return (Long) getStartTime.invoke(runtimeMXBean);
+ } catch (final Throwable t) {
+ StatusLogger.getLogger().error("Unable to call ManagementFactory.getRuntimeMXBean().getStartTime(), "
+ + "using system time for OnStartupTriggeringPolicy", t);
+ // We have little option but to declare "now" as the beginning of time.
+ return System.currentTimeMillis();
+ }
+ }
+
+ public LogEvent getEvent() {
+ return this.event;
+ }
+
+ /**
+ * Set the location information for this logging event. The collected
+ * information is cached for future use.
+ */
+ @Override
+ public LocationInfo getLocationInformation() {
+ return new LocationInfo(event.getSource());
+ }
+
+ /**
+ * Return the level of this event. Use this form instead of directly
+ * accessing the level field.
+ */
+ @Override
+ public Level getLevel() {
+ return OptionConverter.convertLevel(event.getLevel());
+ }
+
+ /**
+ * Return the name of the logger. Use this form instead of directly
+ * accessing the categoryName field.
+ */
+ @Override
+ public String getLoggerName() {
+ return event.getLoggerName();
+ }
+
+ @Override
+ public long getTimeStamp() {
+ return event.getTimeMillis();
+ }
+
+ /**
+ * Gets the logger of the event.
+ */
+ @Override
+ public Category getLogger() {
+ return Category.getInstance(event.getLoggerName());
+ }
+
+ /*
+ Return the message for this logging event.
+ */
+ @Override
+ public Object getMessage() {
+ return event.getMessage();
+ }
+
+ /*
+ * This method returns the NDC for this event.
+ */
+ @Override
+ public String getNDC() {
+ return event.getContextStack().toString();
+ }
+
+ /*
+ Returns the context corresponding to the key parameter.
+ */
+ @Override
+ public Object getMDC(String key) {
+ if (event.getContextData() != null) {
+ return event.getContextData().getValue(key);
+ }
+ return null;
+ }
+
+ /**
+ * Obtain a copy of this thread's MDC prior to serialization or
+ * asynchronous logging.
+ */
+ @Override
+ public void getMDCCopy() {
+ }
+
+ @Override
+ public String getRenderedMessage() {
+ return event.getMessage().getFormattedMessage();
+ }
+
+ @Override
+ public String getThreadName() {
+ return event.getThreadName();
+ }
+
+ /**
+ * Returns the throwable information contained within this
+ * event. May be null if there is no such information.
+ *
+ *
Note that the {@link Throwable} object contained within a
+ * {@link ThrowableInformation} does not survive serialization.
+ *
+ * @since 1.1
+ */
+ @Override
+ public ThrowableInformation getThrowableInformation() {
+ if (event.getThrown() != null) {
+ return new ThrowableInformation(event.getThrown());
+ }
+ return null;
+ }
+
+ /**
+ * Return this event's throwable's string[] representaion.
+ */
+ @Override
+ public String[] getThrowableStrRep() {
+ if (event.getThrown() != null) {
+ return Throwables.toStringList(event.getThrown()).toArray(Strings.EMPTY_ARRAY);
+ }
+ return null;
+ }
+
+ @Override
+ public String getProperty(final String key) {
+ return event.getContextData().getValue(key);
+ }
+
+ @Override
+ public Set getPropertyKeySet() {
+ return event.getContextData().toMap().keySet();
+ }
+
+ @Override
+ public Map getProperties() {
+ return event.getContextData().toMap();
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LogEventWrapper.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LogEventWrapper.java
new file mode 100644
index 00000000000..600f69f6d0e
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LogEventWrapper.java
@@ -0,0 +1,217 @@
+/*
+ * 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.bridge;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.log4j.NDC;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.ThrowableInformation;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.impl.ThrowableProxy;
+import org.apache.logging.log4j.core.time.Instant;
+import org.apache.logging.log4j.core.time.MutableInstant;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.spi.MutableThreadContextStack;
+import org.apache.logging.log4j.util.BiConsumer;
+import org.apache.logging.log4j.util.ReadOnlyStringMap;
+import org.apache.logging.log4j.util.TriConsumer;
+
+/**
+ * Exposes a Log4j 1 logging event as a Log4j 2 LogEvent.
+ */
+public class LogEventWrapper implements LogEvent {
+
+ private final LoggingEvent event;
+ private final ContextDataMap contextData;
+ private final MutableThreadContextStack contextStack;
+ private Thread thread;
+
+ public LogEventWrapper(LoggingEvent event) {
+ this.event = event;
+ this.contextData = new ContextDataMap(event.getProperties());
+ this.contextStack = new MutableThreadContextStack(NDC.cloneStack());
+ this.thread = Objects.equals(event.getThreadName(), Thread.currentThread().getName())
+ ? Thread.currentThread() : null;
+ }
+
+ @Override
+ public LogEvent toImmutable() {
+ return this;
+ }
+
+ @Override
+ public ReadOnlyStringMap getContextData() {
+ return contextData;
+ }
+
+ @Override
+ public ThreadContext.ContextStack getContextStack() {
+ return contextStack;
+ }
+
+ @Override
+ public String getLoggerFqcn() {
+ return null;
+ }
+
+ @Override
+ public Level getLevel() {
+ return OptionConverter.convertLevel(event.getLevel());
+ }
+
+ @Override
+ public String getLoggerName() {
+ return event.getLoggerName();
+ }
+
+ @Override
+ public Marker getMarker() {
+ return null;
+ }
+
+ @Override
+ public Message getMessage() {
+ return new SimpleMessage(event.getRenderedMessage());
+ }
+
+ @Override
+ public long getTimeMillis() {
+ return event.getTimeStamp();
+ }
+
+ @Override
+ public Instant getInstant() {
+ MutableInstant mutable = new MutableInstant();
+ mutable.initFromEpochMilli(event.getTimeStamp(), 0);
+ return mutable;
+ }
+
+ @Override
+ public StackTraceElement getSource() {
+ LocationInfo info = event.getLocationInformation();
+ return new StackTraceElement(info.getClassName(), info.getMethodName(), info.getFileName(),
+ Integer.parseInt(info.getLineNumber()));
+ }
+
+ @Override
+ public String getThreadName() {
+ return event.getThreadName();
+ }
+
+ @Override
+ public long getThreadId() {
+ Thread thread = getThread();
+ return thread != null ? thread.getId() : 0;
+ }
+
+ @Override
+ public int getThreadPriority() {
+ Thread thread = getThread();
+ return thread != null ? thread.getPriority() : 0;
+ }
+
+ private Thread getThread() {
+ if (thread == null && event.getThreadName() != null) {
+ for (Thread thread : Thread.getAllStackTraces().keySet()) {
+ if (thread.getName().equals(event.getThreadName())) {
+ this.thread = thread;
+ return thread;
+ }
+ }
+ }
+ return thread;
+ }
+
+ @Override
+ public Throwable getThrown() {
+ ThrowableInformation throwableInformation = event.getThrowableInformation();
+ return throwableInformation == null ? null : throwableInformation.getThrowable();
+ }
+
+ @Override
+ public ThrowableProxy getThrownProxy() {
+ return null;
+ }
+
+ @Override
+ public boolean isEndOfBatch() {
+ return false;
+ }
+
+ @Override
+ public boolean isIncludeLocation() {
+ return false;
+ }
+
+ @Override
+ public void setEndOfBatch(boolean endOfBatch) {
+
+ }
+
+ @Override
+ public void setIncludeLocation(boolean locationRequired) {
+
+ }
+
+ @Override
+ public long getNanoTime() {
+ return 0;
+ }
+
+ private static class ContextDataMap extends HashMap implements ReadOnlyStringMap {
+
+ ContextDataMap(Map map) {
+ if (map != null) {
+ super.putAll(map);
+ }
+ }
+
+ @Override
+ public Map toMap() {
+ return this;
+ }
+
+ @Override
+ public boolean containsKey(String key) {
+ return super.containsKey(key);
+ }
+
+ @Override
+ public void forEach(BiConsumer action) {
+ super.forEach((k,v) -> action.accept(k, (V) v));
+ }
+
+ @Override
+ public void forEach(TriConsumer action, S state) {
+ super.forEach((k,v) -> action.accept(k, (V) v, state));
+ }
+
+ @Override
+ public V getValue(String key) {
+ return (V) super.get(key);
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/RewritePolicyAdapter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/RewritePolicyAdapter.java
new file mode 100644
index 00000000000..cd037548452
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/RewritePolicyAdapter.java
@@ -0,0 +1,49 @@
+/*
+ * 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.bridge;
+
+import org.apache.log4j.rewrite.RewritePolicy;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.core.LogEvent;
+
+
+/**
+ * Binds a Log4j 1.x RewritePolicy to Log4j 2.
+ */
+public class RewritePolicyAdapter implements org.apache.logging.log4j.core.appender.rewrite.RewritePolicy {
+
+ private final RewritePolicy policy;
+
+ /**
+ * Constructor.
+ * @param policy The Rewrite policy.
+ */
+ public RewritePolicyAdapter(RewritePolicy policy) {
+ this.policy = policy;
+ }
+
+ @Override
+ public LogEvent rewrite(LogEvent source) {
+ LoggingEvent event = policy.rewrite(new LogEventAdapter(source));
+ return event instanceof LogEventAdapter ? ((LogEventAdapter) event).getEvent() : new LogEventWrapper(event);
+ }
+
+ public RewritePolicy getPolicy() {
+ return this.policy;
+ }
+
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/RewritePolicyWrapper.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/RewritePolicyWrapper.java
new file mode 100644
index 00000000000..2a944816a48
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/RewritePolicyWrapper.java
@@ -0,0 +1,44 @@
+/*
+ * 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.bridge;
+
+import org.apache.log4j.rewrite.RewritePolicy;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.core.LogEvent;
+
+/**
+ * Binds a Log4j 2 RewritePolicy to Log4j 1.
+ */
+public class RewritePolicyWrapper implements RewritePolicy {
+
+ private final org.apache.logging.log4j.core.appender.rewrite.RewritePolicy policy;
+
+ public RewritePolicyWrapper(org.apache.logging.log4j.core.appender.rewrite.RewritePolicy policy) {
+ this.policy = policy;
+ }
+
+ @Override
+ public LoggingEvent rewrite(LoggingEvent source) {
+ LogEvent event = source instanceof LogEventAdapter ? ((LogEventAdapter) source).getEvent() :
+ new LogEventWrapper(source);
+ return new LogEventAdapter(policy.rewrite(event));
+ }
+
+ public org.apache.logging.log4j.core.appender.rewrite.RewritePolicy getPolicy() {
+ return policy;
+ }
+}
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 00000000000..0969ef04cf9
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/AbstractBuilder.java
@@ -0,0 +1,206 @@
+/*
+ * 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 static org.apache.log4j.xml.XmlConfiguration.NAME_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+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.ThresholdFilter;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.Strings;
+import org.w3c.dom.Element;
+
+/**
+ * Base class for Log4j 1 component builders.
+ *
+ * @param The type to build.
+ */
+public abstract class AbstractBuilder implements Builder {
+
+ private static final 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 IMMEDIATE_FLUSH_PARAM = "ImmediateFlush";
+ protected static final String MAX_SIZE_PARAM = "MaxFileSize";
+ protected static final String MAX_BACKUP_INDEX = "MaxBackupIndex";
+ protected static final String RELATIVE = "RELATIVE";
+ protected static final String NULL = "NULL";
+
+ private final String prefix;
+ private final Properties properties;
+
+ public AbstractBuilder() {
+ this(null, new Properties());
+ }
+
+ public AbstractBuilder(final String prefix, final Properties props) {
+ this.prefix = prefix != null ? prefix + "." : null;
+ this.properties = (Properties) props.clone();
+ final Map map = new HashMap<>();
+ System.getProperties().forEach((k, v) -> map.put(k.toString(), v.toString()));
+ props.forEach((k, v) -> map.put(k.toString(), v.toString()));
+ // normalize keys to lower case for case-insensitive access.
+ props.forEach((k, v) -> map.put(toBeanKey(k.toString()), v.toString()));
+ props.entrySet().forEach(e -> this.properties.put(toBeanKey(e.getKey().toString()), e.getValue()));
+ }
+
+ protected static org.apache.logging.log4j.core.Filter buildFilters(final String level, final Filter filter) {
+ Filter head = null;
+ if (level != null) {
+ final 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);
+ head = new FilterWrapper(thresholdFilter);
+ }
+ if (filter != null) {
+ head = FilterAdapter.addFilter(head, filter);
+ }
+ return FilterAdapter.adapt(head);
+ }
+
+ private String capitalize(final String value) {
+ if (Strings.isEmpty(value) || Character.isUpperCase(value.charAt(0))) {
+ return value;
+ }
+ final char[] chars = value.toCharArray();
+ chars[0] = Character.toUpperCase(chars[0]);
+ return new String(chars);
+ }
+
+ public boolean getBooleanProperty(final String key, final boolean defaultValue) {
+ return Boolean.parseBoolean(getProperty(key, Boolean.toString(defaultValue)));
+ }
+
+ public boolean getBooleanProperty(final String key) {
+ return getBooleanProperty(key, false);
+ }
+
+ protected boolean getBooleanValueAttribute(final Element element) {
+ return Boolean.parseBoolean(getValueAttribute(element));
+ }
+
+ public int getIntegerProperty(final String key, final int defaultValue) {
+ String value = null;
+ try {
+ value = getProperty(key);
+ if (value != null) {
+ return Integer.parseInt(value);
+ }
+ } catch (final Exception ex) {
+ LOGGER.warn("Error converting value {} of {} to an integer: {}", value, key, ex.getMessage());
+ }
+ return defaultValue;
+ }
+
+ protected String getNameAttribute(final Element element) {
+ return element.getAttribute(NAME_ATTR);
+ }
+
+ protected String getNameAttributeKey(final Element element) {
+ return toBeanKey(element.getAttribute(NAME_ATTR));
+ }
+
+ public Properties getProperties() {
+ return properties;
+ }
+
+ public String getProperty(final String key) {
+ return getProperty(key, null);
+ }
+
+ public String getProperty(final String key, final String defaultValue) {
+ String value = properties.getProperty(prefix + toJavaKey(key));
+ value = value != null ? value : properties.getProperty(prefix + toBeanKey(key), defaultValue);
+ value = value != null ? substVars(value) : defaultValue;
+ return value != null ? value.trim() : defaultValue;
+ }
+
+ protected String getValueAttribute(final Element element) {
+ return getValueAttribute(element, null);
+ }
+
+ protected String getValueAttribute(final Element element, final String defaultValue) {
+ final String attribute = element.getAttribute(VALUE_ATTR);
+ return substVars(attribute != null ? attribute.trim() : defaultValue);
+ }
+
+ protected String substVars(final String value) {
+ return OptionConverter.substVars(value, properties);
+ }
+
+ String toBeanKey(final String value) {
+ return capitalize(value);
+ }
+
+ String toJavaKey(final String value) {
+ return uncapitalize(value);
+ }
+
+ private String uncapitalize(final String value) {
+ if (Strings.isEmpty(value) || Character.isLowerCase(value.charAt(0))) {
+ return value;
+ }
+ final char[] chars = value.toCharArray();
+ chars[0] = Character.toLowerCase(chars[0]);
+ return new String(chars);
+ }
+
+ protected void set(final String name, final Element element, AtomicBoolean ref) {
+ final String value = getValueAttribute(element);
+ if (value == null) {
+ LOGGER.warn("No value for {} parameter, using default {}", name, ref);
+ } else {
+ ref.set(Boolean.parseBoolean(value));
+ }
+ }
+
+ protected void set(final String name, final Element element, AtomicInteger ref) {
+ final String value = getValueAttribute(element);
+ if (value == null) {
+ LOGGER.warn("No value for {} parameter, using default {}", name, ref);
+ } else {
+ try {
+ ref.set(Integer.parseInt(value));
+ } catch (NumberFormatException e) {
+ LOGGER.warn("{} parsing {} parameter, using default {}: {}", e.getClass().getName(), name, ref, e.getMessage(), e);
+ }
+ }
+ }
+
+ protected void set(final String name, final Element element, AtomicReference ref) {
+ final String value = getValueAttribute(element);
+ if (value == null) {
+ LOGGER.warn("No value for {} parameter, using default {}", name, ref);
+ } else {
+ ref.set(value);
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/Builder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/Builder.java
new file mode 100644
index 00000000000..8a9b67036ad
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/Builder.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.log4j.builders;
+
+/**
+ * A marker interface for Log4j 1.x component builders.
+ *
+ * @param The type to build.
+ */
+public interface Builder {
+ // empty
+}
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
new file mode 100644
index 00000000000..8df2cc03980
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BuilderManager.java
@@ -0,0 +1,161 @@
+/*
+ * 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 java.util.Locale;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.function.Function;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.bridge.RewritePolicyWrapper;
+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.builders.rewrite.RewritePolicyBuilder;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.rewrite.RewritePolicy;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.plugins.Inject;
+import org.apache.logging.log4j.plugins.Namespace;
+import org.apache.logging.log4j.plugins.di.Injector;
+import org.apache.logging.log4j.plugins.model.PluginNamespace;
+import org.apache.logging.log4j.plugins.model.PluginType;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.Cast;
+import org.w3c.dom.Element;
+
+/**
+ *
+ */
+public class BuilderManager {
+
+ /** Plugin namespace. */
+ public static final String NAMESPACE = "Log4j Builder";
+ public static final Appender INVALID_APPENDER = new AppenderWrapper(null);
+ public static final Filter INVALID_FILTER = new FilterWrapper(null);
+ public static final Layout INVALID_LAYOUT = new LayoutWrapper(null);
+ public static final RewritePolicy INVALID_REWRITE_POLICY = new RewritePolicyWrapper(null);
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ private static final Class>[] CONSTRUCTOR_PARAMS = new Class[] { String.class, Properties.class };
+ private final Injector injector;
+ private final PluginNamespace plugins;
+
+ /**
+ * Constructs a new instance.
+ */
+ @Inject
+ public BuilderManager(final Injector injector, @Namespace(NAMESPACE) PluginNamespace plugins) {
+ this.injector = injector;
+ this.plugins = plugins;
+ }
+
+ private , U> T createBuilder(final PluginType plugin, final String prefix, final Properties props) {
+ if (plugin == null) {
+ return null;
+ }
+ try {
+ final Class clazz = plugin.getPluginClass();
+ if (AbstractBuilder.class.isAssignableFrom(clazz)) {
+ return clazz.getConstructor(CONSTRUCTOR_PARAMS).newInstance(prefix, props);
+ }
+ final T builder = injector.getInstance(clazz);
+ // Reasonable message instead of `ClassCastException`
+ if (!Builder.class.isAssignableFrom(clazz)) {
+ LOGGER.warn("Unable to load plugin: builder {} does not implement {}", clazz, Builder.class);
+ return null;
+ }
+ return builder;
+ } catch (final ReflectiveOperationException ex) {
+ LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
+ return null;
+ }
+ }
+
+ private PluginType getPlugin(final String className) {
+ Objects.requireNonNull(plugins, "plugins");
+ Objects.requireNonNull(className, "className");
+ final String key = className.toLowerCase(Locale.ROOT).trim();
+ final PluginType> pluginType = plugins.get(key);
+ if (pluginType == null) {
+ LOGGER.warn("Unable to load plugin class name {} with key {}", className, key);
+ }
+ return Cast.cast(pluginType);
+ }
+
+ private , U> U newInstance(final PluginType plugin, final Function consumer,
+ final U invalidValue) {
+ if (plugin != null) {
+ final T builder = injector.getInstance(plugin.getPluginClass());
+ if (builder != null) {
+ final U result = consumer.apply(builder);
+ // returning an empty wrapper is short for "we support this legacy class, but it has validation errors"
+ return result != null ? result : invalidValue;
+ }
+ }
+ return null;
+ }
+
+ public
, T> T parse(final String className, final String prefix, final Properties props,
+ final PropertiesConfiguration config, T invalidValue) {
+ final P parser = createBuilder(getPlugin(className), prefix, props);
+ if (parser != null) {
+ final T value = parser.parse(config);
+ return value != null ? value : invalidValue;
+ }
+ return null;
+ }
+
+ public Appender parseAppender(final String className, final Element appenderElement,
+ final XmlConfiguration config) {
+ return newInstance(this.>getPlugin(className),
+ b -> b.parseAppender(appenderElement, config), INVALID_APPENDER);
+ }
+
+ public Appender parseAppender(final String name, final String className, final String prefix, final String layoutPrefix, final String filterPrefix,
+ final Properties props, final PropertiesConfiguration config) {
+ final AppenderBuilder builder = createBuilder(getPlugin(className), prefix, props);
+ if (builder != null) {
+ final Appender appender = builder.parseAppender(name, prefix, layoutPrefix, filterPrefix, props, config);
+ return appender != null ? appender : INVALID_APPENDER;
+ }
+ return null;
+ }
+
+ public Filter parseFilter(final String className, final Element filterElement, final XmlConfiguration config) {
+ return newInstance(this.getPlugin(className), b -> b.parse(filterElement, config),
+ INVALID_FILTER);
+ }
+
+ public Layout parseLayout(final String className, final Element layoutElement, final XmlConfiguration config) {
+ return newInstance(this.getPlugin(className), b -> b.parse(layoutElement, config),
+ INVALID_LAYOUT);
+ }
+
+ public RewritePolicy parseRewritePolicy(final String className, final Element rewriteElement,
+ final XmlConfiguration config) {
+ return newInstance(this.getPlugin(className), b -> b.parse(rewriteElement, config),
+ INVALID_REWRITE_POLICY);
+ }
+
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/Parser.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/Parser.java
new file mode 100644
index 00000000000..01812ef8e4d
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/Parser.java
@@ -0,0 +1,48 @@
+/*
+ * 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.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.w3c.dom.Element;
+
+/**
+ * Parses DOM and properties.
+ *
+ * @param The type to build.
+ */
+public interface Parser extends Builder {
+
+ /**
+ * Parses a DOM Element.
+ *
+ * @param element the DOM Element.
+ * @param config the XML configuration.
+ * @return parse result.
+ */
+ T parse(Element element, XmlConfiguration config);
+
+ /**
+ * Parses a PropertiesConfigurationt.
+ *
+ * @param element the PropertiesConfiguration.
+ * @return parse result.
+ */
+ T parse(PropertiesConfiguration config);
+
+}
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
new file mode 100644
index 00000000000..aacbc80d750
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AppenderBuilder.java
@@ -0,0 +1,39 @@
+/*
+ * 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 java.util.Properties;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.builders.Builder;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.w3c.dom.Element;
+
+/**
+ * Define an Appender Builder.
+ *
+ * @param The type to build.
+ */
+public interface AppenderBuilder extends Builder {
+
+ Appender parseAppender(Element element, XmlConfiguration configuration);
+
+ Appender parseAppender(String name, String appenderPrefix, 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 00000000000..e69111394aa
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AsyncAppenderBuilder.java
@@ -0,0 +1,158 @@
+/*
+ * 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.bridge.FilterAdapter;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.AsyncAppender;
+import org.apache.logging.log4j.core.appender.AsyncAppender.Builder;
+import org.apache.logging.log4j.core.config.AppenderRef;
+import org.apache.logging.log4j.plugins.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.Strings;
+import org.w3c.dom.Element;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.apache.log4j.builders.BuilderManager.NAMESPACE;
+import static org.apache.log4j.config.Log4j1Configuration.APPENDER_REF_TAG;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.*;
+
+/**
+ * Build an Async Appender
+ */
+@Namespace(NAMESPACE)
+@Plugin("org.apache.log4j.AsyncAppender")
+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(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+ final String name = getNameAttribute(appenderElement);
+ final AtomicReference> appenderRefs = new AtomicReference<>(new ArrayList<>());
+ final AtomicBoolean blocking = new AtomicBoolean();
+ final AtomicBoolean includeLocation = new AtomicBoolean();
+ final AtomicReference level = new AtomicReference<>("trace");
+ final AtomicInteger bufferSize = new AtomicInteger(1024);
+ final AtomicReference filter = new AtomicReference<>();
+ forEachElement(appenderElement.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case APPENDER_REF_TAG:
+ final Appender appender = config.findAppenderByReference(currentElement);
+ if (appender != null) {
+ appenderRefs.get().add(appender.getName());
+ }
+ break;
+ case FILTER_TAG:
+ config.addFilter(filter, currentElement);
+ break;
+ case PARAM_TAG: {
+ switch (getNameAttributeKey(currentElement)) {
+ case BUFFER_SIZE_PARAM:
+ set(BUFFER_SIZE_PARAM, currentElement, bufferSize);
+ break;
+ case BLOCKING_PARAM:
+ set(BLOCKING_PARAM, currentElement, blocking);
+ break;
+ case INCLUDE_LOCATION_PARAM:
+ set(INCLUDE_LOCATION_PARAM, currentElement, includeLocation);
+ break;
+ case THRESHOLD_PARAM:
+ set(THRESHOLD_PARAM, currentElement, level);
+ break;
+ }
+ break;
+ }
+ }
+ });
+ return createAppender(name, level.get(), appenderRefs.get().toArray(Strings.EMPTY_ARRAY), blocking.get(),
+ bufferSize.get(), includeLocation.get(), filter.get(), config);
+ }
+
+ @Override
+ public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix,
+ final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
+ final String appenderRef = getProperty(APPENDER_REF_TAG);
+ final Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+ final boolean blocking = getBooleanProperty(BLOCKING_PARAM);
+ final boolean includeLocation = getBooleanProperty(INCLUDE_LOCATION_PARAM);
+ final String level = getProperty(THRESHOLD_PARAM);
+ final int bufferSize = getIntegerProperty(BUFFER_SIZE_PARAM, 1024);
+ if (appenderRef == null) {
+ LOGGER.error("No appender references configured for AsyncAppender {}", name);
+ return null;
+ }
+ final Appender appender = configuration.parseAppender(props, appenderRef);
+ if (appender == null) {
+ LOGGER.error("Cannot locate Appender {}", appenderRef);
+ return null;
+ }
+ return createAppender(name, level, new String[]{appenderRef}, blocking, bufferSize, includeLocation, filter,
+ configuration);
+ }
+
+ private Appender createAppender(final String name, final String level,
+ final String[] appenderRefs, final boolean blocking, final int bufferSize, final boolean includeLocation,
+ final Filter filter, final T configuration) {
+ if (appenderRefs.length == 0) {
+ LOGGER.error("No appender references configured for AsyncAppender {}", name);
+ return null;
+ }
+ final Level logLevel = OptionConverter.convertLevel(level,
+ Level.TRACE);
+ final AppenderRef[] refs = new AppenderRef[appenderRefs.length];
+ int index = 0;
+ for (final String appenderRef : appenderRefs) {
+ refs[index++] = AppenderRef.createAppenderRef(appenderRef, logLevel, null);
+ }
+ Builder builder = AsyncAppender.newBuilder();
+ builder.setFilter(FilterAdapter.adapt(filter));
+ return AppenderWrapper.adapt(builder.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
new file mode 100644
index 00000000000..8ef424f077f
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.java
@@ -0,0 +1,146 @@
+/*
+ * 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.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.spi.Filter;
+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.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.apache.log4j.builders.BuilderManager.NAMESPACE;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.*;
+
+/**
+ * Build a Console Appender
+ */
+@Namespace(NAMESPACE)
+@Plugin("org.apache.log4j.ConsoleAppender")
+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_PARAM = "Target";
+ private static final String FOLLOW_PARAM = "Follow";
+
+ private static final Logger LOGGER = StatusLogger.getLogger();
+
+ public ConsoleAppenderBuilder() {
+ }
+
+ public ConsoleAppenderBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+ final String name = getNameAttribute(appenderElement);
+ final AtomicReference target = new AtomicReference<>(SYSTEM_OUT);
+ final AtomicReference layout = new AtomicReference<>();
+ final AtomicReference filter = new AtomicReference<>();
+ final AtomicReference level = new AtomicReference<>();
+ final AtomicBoolean follow = new AtomicBoolean();
+ final AtomicBoolean immediateFlush = new AtomicBoolean(true);
+ forEachElement(appenderElement.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case LAYOUT_TAG:
+ layout.set(config.parseLayout(currentElement));
+ break;
+ case FILTER_TAG:
+ config.addFilter(filter, currentElement);
+ break;
+ case PARAM_TAG: {
+ switch (getNameAttributeKey(currentElement)) {
+ case TARGET_PARAM:
+ final String value = getValueAttribute(currentElement);
+ 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 {}", value, SYSTEM_OUT);
+ }
+ }
+ break;
+ case THRESHOLD_PARAM:
+ set(THRESHOLD_PARAM, currentElement, level);
+ break;
+ case FOLLOW_PARAM:
+ set(FOLLOW_PARAM, currentElement, follow);
+ break;
+ case IMMEDIATE_FLUSH_PARAM:
+ set(IMMEDIATE_FLUSH_PARAM, currentElement, immediateFlush);
+ break;
+ }
+ break;
+ }
+ }
+ });
+ return createAppender(name, layout.get(), filter.get(), level.get(), target.get(), immediateFlush.get(), follow.get(), config);
+ }
+
+ @Override
+ public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix,
+ final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
+ final Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+ final Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+ final String level = getProperty(THRESHOLD_PARAM);
+ final String target = getProperty(TARGET_PARAM);
+ final boolean follow = getBooleanProperty(FOLLOW_PARAM);
+ final boolean immediateFlush = getBooleanProperty(IMMEDIATE_FLUSH_PARAM);
+ return createAppender(name, layout, filter, level, target, immediateFlush, follow, configuration);
+ }
+
+ private Appender createAppender(final String name, final Layout layout, final Filter filter,
+ final String level, final String target, final boolean immediateFlush, final boolean follow, final T configuration) {
+ org.apache.logging.log4j.core.Layout> consoleLayout = LayoutAdapter.adapt(layout);
+
+ final org.apache.logging.log4j.core.Filter consoleFilter = buildFilters(level, filter);
+ final ConsoleAppender.Target consoleTarget = SYSTEM_ERR.equals(target)
+ ? ConsoleAppender.Target.SYSTEM_ERR : ConsoleAppender.Target.SYSTEM_OUT;
+ return AppenderWrapper.adapt(ConsoleAppender.newBuilder()
+ .setName(name)
+ .setTarget(consoleTarget)
+ .setFollow(follow)
+ .setLayout(consoleLayout)
+ .setFilter(consoleFilter)
+ .setConfiguration(configuration)
+ .setImmediateFlush(immediateFlush)
+ .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
new file mode 100644
index 00000000000..4c1ea384547
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java
@@ -0,0 +1,173 @@
+/*
+ * 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.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.spi.Filter;
+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;
+import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
+import org.apache.logging.log4j.core.time.Clock;
+import org.apache.logging.log4j.plugins.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.apache.log4j.builders.BuilderManager.NAMESPACE;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.*;
+
+/**
+ * Build a Daily Rolling File Appender
+ */
+@Namespace(NAMESPACE)
+@Plugin("org.apache.log4j.DailyRollingFileAppender")
+public class DailyRollingFileAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+ private static final String DEFAULT_DATE_PATTERN = ".yyyy-MM-dd";
+ private static final String DATE_PATTERN_PARAM = "DatePattern";
+
+ private static final Logger LOGGER = StatusLogger.getLogger();
+
+ public DailyRollingFileAppenderBuilder() {
+ }
+
+ public DailyRollingFileAppenderBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+ final String name = getNameAttribute(appenderElement);
+ final AtomicReference layout = new AtomicReference<>();
+ final AtomicReference filter = new AtomicReference<>();
+ final AtomicReference fileName = new AtomicReference<>();
+ final AtomicReference level = new AtomicReference<>();
+ final AtomicBoolean immediateFlush = new AtomicBoolean(true);
+ final AtomicBoolean append = new AtomicBoolean(true);
+ final AtomicBoolean bufferedIo = new AtomicBoolean();
+ final AtomicInteger bufferSize = new AtomicInteger(8192);
+ final AtomicReference datePattern = new AtomicReference<>(DEFAULT_DATE_PATTERN);
+ forEachElement(appenderElement.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case LAYOUT_TAG:
+ layout.set(config.parseLayout(currentElement));
+ break;
+ case FILTER_TAG:
+ config.addFilter(filter, currentElement);
+ break;
+ case PARAM_TAG:
+ switch (getNameAttributeKey(currentElement)) {
+ case FILE_PARAM:
+ set(FILE_PARAM, currentElement, fileName);
+ break;
+ case APPEND_PARAM:
+ set(APPEND_PARAM, currentElement, append);
+ break;
+ case BUFFERED_IO_PARAM:
+ set(BUFFERED_IO_PARAM, currentElement, bufferedIo);
+ break;
+ case BUFFER_SIZE_PARAM:
+ set(BUFFER_SIZE_PARAM, currentElement, bufferSize);
+ break;
+ case THRESHOLD_PARAM:
+ set(THRESHOLD_PARAM, currentElement, level);
+ break;
+ case DATE_PATTERN_PARAM:
+ set(DATE_PATTERN_PARAM, currentElement, datePattern);
+ break;
+ case IMMEDIATE_FLUSH_PARAM:
+ set(IMMEDIATE_FLUSH_PARAM, currentElement, immediateFlush);
+ break;
+ }
+ break;
+ }
+ });
+ return createAppender(name, layout.get(), filter.get(), fileName.get(), append.get(), immediateFlush.get(),
+ level.get(), bufferedIo.get(), bufferSize.get(), datePattern.get(), config,
+ config.getComponent(Clock.KEY));
+ }
+
+ @Override
+ public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix,
+ final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
+ final Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+ final Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+ final String fileName = getProperty(FILE_PARAM);
+ final String level = getProperty(THRESHOLD_PARAM);
+ final boolean append = getBooleanProperty(APPEND_PARAM, true);
+ final boolean immediateFlush = getBooleanProperty(IMMEDIATE_FLUSH_PARAM, true);
+ final boolean bufferedIo = getBooleanProperty(BUFFERED_IO_PARAM, false);
+ final int bufferSize = getIntegerProperty(BUFFER_SIZE_PARAM, 8192);
+ final String datePattern = getProperty(DATE_PATTERN_PARAM, DEFAULT_DATE_PATTERN);
+ return createAppender(name, layout, filter, fileName, append, immediateFlush, level, bufferedIo, bufferSize,
+ datePattern, configuration, configuration.getComponent(Clock.KEY));
+ }
+
+ private 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 String datePattern,
+ final T configuration, final Clock clock) {
+
+ org.apache.logging.log4j.core.Layout> fileLayout = LayoutAdapter.adapt(layout);
+ if (bufferedIo) {
+ immediateFlush = false;
+ }
+ final org.apache.logging.log4j.core.Filter fileFilter = buildFilters(level, filter);
+ if (fileName == null) {
+ LOGGER.error("Unable to create DailyRollingFileAppender, no file name provided");
+ return null;
+ }
+ final String filePattern = fileName + "%d{" + datePattern + "}";
+ final TriggeringPolicy timePolicy = TimeBasedTriggeringPolicy.newBuilder().setClock(clock).setModulate(true).build();
+ final TriggeringPolicy policy = CompositeTriggeringPolicy.createPolicy(timePolicy);
+ final RolloverStrategy strategy = DefaultRolloverStrategy.newBuilder()
+ .setConfig(configuration)
+ .setMax(Integer.toString(Integer.MAX_VALUE))
+ .build();
+ return AppenderWrapper.adapt(RollingFileAppender.newBuilder()
+ .setName(name)
+ .setConfiguration(configuration)
+ .setLayout(fileLayout)
+ .setFilter(fileFilter)
+ .setFileName(fileName)
+ .setAppend(append)
+ .setBufferedIo(bufferedIo)
+ .setBufferSize(bufferSize)
+ .setImmediateFlush(immediateFlush)
+ .setFilePattern(filePattern)
+ .setPolicy(policy)
+ .setStrategy(strategy)
+ .build());
+ }
+}
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
new file mode 100644
index 00000000000..1c661f87647
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java
@@ -0,0 +1,147 @@
+/*
+ * 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.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.spi.Filter;
+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.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.apache.log4j.builders.BuilderManager.NAMESPACE;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.*;
+
+/**
+ * Build a File Appender
+ */
+@Namespace(NAMESPACE)
+@Plugin("org.apache.log4j.FileAppender")
+public class FileAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+ private static final Logger LOGGER = StatusLogger.getLogger();
+
+ public FileAppenderBuilder() {
+ }
+
+ public FileAppenderBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+ final String name = getNameAttribute(appenderElement);
+ final AtomicReference layout = new AtomicReference<>();
+ final AtomicReference filter = new AtomicReference<>();
+ final AtomicReference fileName = new AtomicReference<>();
+ final AtomicReference level = new AtomicReference<>();
+ final AtomicBoolean immediateFlush = new AtomicBoolean(true);
+ final AtomicBoolean append = new AtomicBoolean(true);
+ final AtomicBoolean bufferedIo = new AtomicBoolean();
+ final AtomicInteger bufferSize = new AtomicInteger(8192);
+ forEachElement(appenderElement.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case LAYOUT_TAG:
+ layout.set(config.parseLayout(currentElement));
+ break;
+ case FILTER_TAG:
+ config.addFilter(filter, currentElement);
+ break;
+ case PARAM_TAG:
+ switch (getNameAttributeKey(currentElement)) {
+ case FILE_PARAM:
+ set(FILE_PARAM, currentElement, fileName);
+ break;
+ case APPEND_PARAM:
+ set(APPEND_PARAM, currentElement, append);
+ break;
+ case BUFFERED_IO_PARAM:
+ set(BUFFERED_IO_PARAM, currentElement, bufferedIo);
+ break;
+ case BUFFER_SIZE_PARAM:
+ set(BUFFER_SIZE_PARAM, currentElement, bufferSize);
+ break;
+ case THRESHOLD_PARAM:
+ set(THRESHOLD_PARAM, currentElement, level);
+ break;
+ case IMMEDIATE_FLUSH_PARAM:
+ set(IMMEDIATE_FLUSH_PARAM, currentElement, immediateFlush);
+ 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 appenderPrefix, final String layoutPrefix,
+ final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
+ final Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+ final Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+ final String level = getProperty(THRESHOLD_PARAM);
+ final String fileName = getProperty(FILE_PARAM);
+ final boolean append = getBooleanProperty(APPEND_PARAM, true);
+ final boolean immediateFlush = getBooleanProperty(IMMEDIATE_FLUSH_PARAM, true);
+ final boolean bufferedIo = getBooleanProperty(BUFFERED_IO_PARAM, false);
+ final 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, final String level, boolean immediateFlush, final boolean append,
+ final boolean bufferedIo, final int bufferSize) {
+ org.apache.logging.log4j.core.Layout> fileLayout = LayoutAdapter.adapt(layout);
+ if (bufferedIo) {
+ immediateFlush = false;
+ }
+ final org.apache.logging.log4j.core.Filter fileFilter = buildFilters(level, filter);
+ if (fileName == null) {
+ LOGGER.error("Unable to create FileAppender, no file name provided");
+ return null;
+ }
+ return AppenderWrapper.adapt(FileAppender.newBuilder()
+ .setName(name)
+ .setConfiguration(configuration)
+ .setLayout(fileLayout)
+ .setFilter(fileFilter)
+ .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
new file mode 100644
index 00000000000..201159fbdf4
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/NullAppenderBuilder.java
@@ -0,0 +1,51 @@
+/*
+ * 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.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.core.appender.NullAppender;
+import org.apache.logging.log4j.plugins.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.w3c.dom.Element;
+
+import java.util.Properties;
+
+import static org.apache.log4j.builders.BuilderManager.NAMESPACE;
+
+/**
+ * Build a Null Appender
+ */
+@Namespace(NAMESPACE)
+@Plugin("org.apache.log4j.varia.NullAppender")
+public class NullAppenderBuilder implements AppenderBuilder {
+
+ @Override
+ public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+ final String name = appenderElement.getAttribute("name");
+ return AppenderWrapper.adapt(NullAppender.createAppender(name));
+ }
+
+
+ @Override
+ public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix,
+ final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
+ return AppenderWrapper.adapt(NullAppender.createAppender(name));
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RewriteAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RewriteAppenderBuilder.java
new file mode 100644
index 00000000000..6d8c8968388
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RewriteAppenderBuilder.java
@@ -0,0 +1,148 @@
+/*
+ * 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.bridge.RewritePolicyAdapter;
+import org.apache.log4j.bridge.RewritePolicyWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.builders.BuilderManager;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.rewrite.RewritePolicy;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.rewrite.RewriteAppender;
+import org.apache.logging.log4j.core.config.AppenderRef;
+import org.apache.logging.log4j.plugins.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.Strings;
+import org.w3c.dom.Element;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.apache.log4j.builders.BuilderManager.NAMESPACE;
+import static org.apache.log4j.config.Log4j1Configuration.APPENDER_REF_TAG;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.*;
+
+/**
+ * Build an Rewrite Appender
+ */
+@Namespace(NAMESPACE)
+@Plugin("org.apache.log4j.rewrite.RewriteAppender")
+public class RewriteAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ private static final String REWRITE_POLICY_TAG = "rewritePolicy";
+
+ public RewriteAppenderBuilder() {
+ }
+
+ public RewriteAppenderBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+ final String name = getNameAttribute(appenderElement);
+ final AtomicReference> appenderRefs = new AtomicReference<>(new ArrayList<>());
+ final AtomicReference rewritePolicyHolder = new AtomicReference<>();
+ final AtomicReference level = new AtomicReference<>();
+ final AtomicReference filter = new AtomicReference<>();
+ forEachElement(appenderElement.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case APPENDER_REF_TAG:
+ final Appender appender = config.findAppenderByReference(currentElement);
+ if (appender != null) {
+ appenderRefs.get().add(appender.getName());
+ }
+ break;
+ case REWRITE_POLICY_TAG:
+ final RewritePolicy policy = config.parseRewritePolicy(currentElement);
+ if (policy != null) {
+ rewritePolicyHolder.set(policy);
+ }
+ break;
+ case FILTER_TAG:
+ config.addFilter(filter, currentElement);
+ break;
+ case PARAM_TAG:
+ if (getNameAttributeKey(currentElement).equalsIgnoreCase(THRESHOLD_PARAM)) {
+ set(THRESHOLD_PARAM, currentElement, level);
+ }
+ break;
+ }
+ });
+ return createAppender(name, level.get(), appenderRefs.get().toArray(Strings.EMPTY_ARRAY), rewritePolicyHolder.get(),
+ filter.get(), config);
+ }
+
+ @Override
+ public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix,
+ final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
+ final String appenderRef = getProperty(APPENDER_REF_TAG);
+ final Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+ final String policyPrefix = appenderPrefix + ".rewritePolicy";
+ final String className = getProperty(policyPrefix);
+ final RewritePolicy policy = configuration.getBuilderManager()
+ .parse(className, policyPrefix, props, configuration, BuilderManager.INVALID_REWRITE_POLICY);
+ final String level = getProperty(THRESHOLD_PARAM);
+ if (appenderRef == null) {
+ LOGGER.error("No appender references configured for RewriteAppender {}", name);
+ return null;
+ }
+ final Appender appender = configuration.parseAppender(props, appenderRef);
+ if (appender == null) {
+ LOGGER.error("Cannot locate Appender {}", appenderRef);
+ return null;
+ }
+ return createAppender(name, level, new String[] {appenderRef}, policy, filter, configuration);
+ }
+
+ private Appender createAppender(final String name, final String level,
+ final String[] appenderRefs, final RewritePolicy policy, final Filter filter, final T configuration) {
+ if (appenderRefs.length == 0) {
+ LOGGER.error("No appender references configured for RewriteAppender {}", name);
+ return null;
+ }
+ final Level logLevel = OptionConverter.convertLevel(level,
+ Level.TRACE);
+ final AppenderRef[] refs = new AppenderRef[appenderRefs.length];
+ int index = 0;
+ for (final String appenderRef : appenderRefs) {
+ refs[index++] = AppenderRef.createAppenderRef(appenderRef, logLevel, null);
+ }
+ final org.apache.logging.log4j.core.Filter rewriteFilter = buildFilters(level, filter);
+ org.apache.logging.log4j.core.appender.rewrite.RewritePolicy rewritePolicy;
+ if (policy instanceof RewritePolicyWrapper) {
+ rewritePolicy = ((RewritePolicyWrapper) policy).getPolicy();
+ } else {
+ rewritePolicy = new RewritePolicyAdapter(policy);
+ }
+ return AppenderWrapper.adapt(RewriteAppender.createAppender(name, true, refs, configuration,
+ rewritePolicy, rewriteFilter));
+ }
+}
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
new file mode 100644
index 00000000000..169191aa061
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java
@@ -0,0 +1,179 @@
+/*
+ * 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.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.spi.Filter;
+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;
+import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
+import org.apache.logging.log4j.core.time.Clock;
+import org.apache.logging.log4j.plugins.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.apache.log4j.builders.BuilderManager.NAMESPACE;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.*;
+
+/**
+ * Build a File Appender
+ */
+@Namespace(NAMESPACE)
+@Plugin("org.apache.log4j.RollingFileAppender")
+public class RollingFileAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+ private static final String DEFAULT_MAX_SIZE = "10 MB";
+ private static final String DEFAULT_MAX_BACKUPS = "1";
+
+ private static final Logger LOGGER = StatusLogger.getLogger();
+
+ public RollingFileAppenderBuilder() {
+ }
+
+ public RollingFileAppenderBuilder(final String prefix, final Properties properties) {
+ super(prefix, properties);
+ }
+
+ @Override
+ public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+ final String name = getNameAttribute(appenderElement);
+ final AtomicReference layout = new AtomicReference<>();
+ final AtomicReference filter = new AtomicReference<>();
+ final AtomicReference fileName = new AtomicReference<>();
+ final AtomicBoolean immediateFlush = new AtomicBoolean(true);
+ final AtomicBoolean append = new AtomicBoolean(true);
+ final AtomicBoolean bufferedIo = new AtomicBoolean();
+ final AtomicInteger bufferSize = new AtomicInteger(8192);
+ final AtomicReference maxSize = new AtomicReference<>(DEFAULT_MAX_SIZE);
+ final AtomicReference maxBackups = new AtomicReference<>(DEFAULT_MAX_BACKUPS);
+ final AtomicReference level = new AtomicReference<>();
+ forEachElement(appenderElement.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case LAYOUT_TAG:
+ layout.set(config.parseLayout(currentElement));
+ break;
+ case FILTER_TAG:
+ config.addFilter(filter, currentElement);
+ break;
+ case PARAM_TAG:
+ switch (getNameAttributeKey(currentElement)) {
+ case FILE_PARAM:
+ set(FILE_PARAM, currentElement, fileName);
+ break;
+ case APPEND_PARAM:
+ set(APPEND_PARAM, currentElement, append);
+ break;
+ case BUFFERED_IO_PARAM:
+ set(BUFFERED_IO_PARAM, currentElement, bufferedIo);
+ break;
+ case BUFFER_SIZE_PARAM:
+ set(BUFFER_SIZE_PARAM, currentElement, bufferSize);
+ break;
+ case MAX_BACKUP_INDEX:
+ set(MAX_BACKUP_INDEX, currentElement, maxBackups);
+ break;
+ case MAX_SIZE_PARAM:
+ set(MAX_SIZE_PARAM, currentElement, maxSize);
+ break;
+ case THRESHOLD_PARAM:
+ set(THRESHOLD_PARAM, currentElement, level);
+ break;
+ case IMMEDIATE_FLUSH_PARAM:
+ set(IMMEDIATE_FLUSH_PARAM, currentElement, immediateFlush);
+ break;
+ }
+ break;
+ }
+ });
+ return createAppender(name, config, layout.get(), filter.get(), append.get(), bufferedIo.get(),
+ bufferSize.get(), immediateFlush.get(), fileName.get(), level.get(), maxSize.get(), maxBackups.get(),
+ config.getComponent(Clock.KEY));
+ }
+
+ @Override
+ public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix,
+ final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
+ final Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+ final Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+ final String fileName = getProperty(FILE_PARAM);
+ final String level = getProperty(THRESHOLD_PARAM);
+ final boolean append = getBooleanProperty(APPEND_PARAM, true);
+ final boolean immediateFlush = getBooleanProperty(IMMEDIATE_FLUSH_PARAM, true);
+ final boolean bufferedIo = getBooleanProperty(BUFFERED_IO_PARAM, false);
+ final int bufferSize = getIntegerProperty(BUFFER_SIZE_PARAM, 8192);
+ final String maxSize = getProperty(MAX_SIZE_PARAM, DEFAULT_MAX_SIZE);
+ final String maxBackups = getProperty(MAX_BACKUP_INDEX, DEFAULT_MAX_BACKUPS);
+ return createAppender(name, configuration, layout, filter, append, bufferedIo, bufferSize, immediateFlush,
+ fileName, level, maxSize, maxBackups, configuration.getComponent(Clock.KEY));
+ }
+
+ private Appender createAppender(final String name, final Log4j1Configuration config, final Layout layout,
+ final Filter filter, final boolean append, final boolean bufferedIo, final int bufferSize,
+ boolean immediateFlush, final String fileName, final String level, final String maxSize,
+ final String maxBackups, final Clock clock) {
+ org.apache.logging.log4j.core.Layout> fileLayout = LayoutAdapter.adapt(layout);
+ if (!bufferedIo) {
+ immediateFlush = false;
+ }
+ final org.apache.logging.log4j.core.Filter fileFilter = buildFilters(level, filter);
+ if (fileName == null) {
+ LOGGER.error("Unable to create RollingFileAppender, no file name provided");
+ return null;
+ }
+ final String filePattern = fileName + ".%i";
+ final TriggeringPolicy timePolicy = TimeBasedTriggeringPolicy.newBuilder().setClock(clock).setModulate(true).build();
+ final SizeBasedTriggeringPolicy sizePolicy = SizeBasedTriggeringPolicy.createPolicy(maxSize);
+ final CompositeTriggeringPolicy policy = CompositeTriggeringPolicy.createPolicy(sizePolicy);
+ final RolloverStrategy strategy = DefaultRolloverStrategy.newBuilder()
+ .setConfig(config)
+ .setMax(maxBackups)
+ .build();
+ return AppenderWrapper.adapt(RollingFileAppender.newBuilder()
+ .setName(name)
+ .setConfiguration(config)
+ .setLayout(fileLayout)
+ .setFilter(fileFilter)
+ .setAppend(append)
+ .setBufferedIo(bufferedIo)
+ .setBufferSize(bufferSize)
+ .setImmediateFlush(immediateFlush)
+ .setFileName(fileName)
+ .setFilePattern(filePattern)
+ .setPolicy(policy)
+ .setStrategy(strategy)
+ .build());
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SocketAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SocketAppenderBuilder.java
new file mode 100644
index 00000000000..4bf4f611856
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SocketAppenderBuilder.java
@@ -0,0 +1,145 @@
+/*
+ * 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.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.SocketAppender;
+import org.apache.logging.log4j.plugins.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.apache.log4j.builders.BuilderManager.NAMESPACE;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.*;
+
+/**
+ * Build a Console Appender
+ */
+@Namespace(NAMESPACE)
+@Plugin("org.apache.log4j.net.SocketAppender")
+public class SocketAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+ private static final String HOST_PARAM = "RemoteHost";
+ private static final String PORT_PARAM = "Port";
+ private static final String RECONNECTION_DELAY_PARAM = "ReconnectionDelay";
+ private static final int DEFAULT_PORT = 4560;
+
+ /**
+ * The default reconnection delay (30000 milliseconds or 30 seconds).
+ */
+ private static final int DEFAULT_RECONNECTION_DELAY = 30_000;
+
+ public static final Logger LOGGER = StatusLogger.getLogger();
+
+ public SocketAppenderBuilder() {
+ }
+
+ public SocketAppenderBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ private Appender createAppender(final String name, final String host, final int port, final Layout layout,
+ final Filter filter, final String level, final boolean immediateFlush, final int reconnectDelayMillis, final T configuration) {
+ org.apache.logging.log4j.core.Layout> actualLayout = LayoutAdapter.adapt(layout);
+ final org.apache.logging.log4j.core.Filter actualFilter = buildFilters(level, filter);
+ // @formatter:off
+ return AppenderWrapper.adapt(SocketAppender.newBuilder()
+ .setHost(host)
+ .setPort(port)
+ .setReconnectDelayMillis(reconnectDelayMillis)
+ .setName(name)
+ .setLayout(actualLayout)
+ .setFilter(actualFilter)
+ .setConfiguration(configuration)
+ .setImmediateFlush(immediateFlush)
+ .build());
+ // @formatter:on
+ }
+
+ @Override
+ public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+ final String name = getNameAttribute(appenderElement);
+ final AtomicReference host = new AtomicReference<>("localhost");
+ final AtomicInteger port = new AtomicInteger(DEFAULT_PORT);
+ final AtomicInteger reconnectDelay = new AtomicInteger(DEFAULT_RECONNECTION_DELAY);
+ final AtomicReference layout = new AtomicReference<>();
+ final AtomicReference filter = new AtomicReference<>();
+ final AtomicReference level = new AtomicReference<>();
+ final AtomicBoolean immediateFlush = new AtomicBoolean(true);
+ forEachElement(appenderElement.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case LAYOUT_TAG:
+ layout.set(config.parseLayout(currentElement));
+ break;
+ case FILTER_TAG:
+ config.addFilter(filter, currentElement);
+ break;
+ case PARAM_TAG:
+ switch (getNameAttributeKey(currentElement)) {
+ case HOST_PARAM:
+ set(HOST_PARAM, currentElement, host);
+ break;
+ case PORT_PARAM:
+ set(PORT_PARAM, currentElement, port);
+ break;
+ case RECONNECTION_DELAY_PARAM:
+ set(RECONNECTION_DELAY_PARAM, currentElement, reconnectDelay);
+ break;
+ case THRESHOLD_PARAM:
+ set(THRESHOLD_PARAM, currentElement, level);
+ break;
+ case IMMEDIATE_FLUSH_PARAM:
+ set(IMMEDIATE_FLUSH_PARAM, currentElement, immediateFlush);
+ break;
+ }
+ break;
+ }
+ });
+ return createAppender(name, host.get(), port.get(), layout.get(), filter.get(), level.get(), immediateFlush.get(), reconnectDelay.get(), config);
+ }
+
+ @Override
+ public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix, final String filterPrefix, final Properties props,
+ final PropertiesConfiguration configuration) {
+ // @formatter:off
+ return createAppender(name,
+ getProperty(HOST_PARAM),
+ getIntegerProperty(PORT_PARAM, DEFAULT_PORT),
+ configuration.parseLayout(layoutPrefix, name, props),
+ configuration.parseAppenderFilters(props, filterPrefix, name),
+ getProperty(THRESHOLD_PARAM),
+ getBooleanProperty(IMMEDIATE_FLUSH_PARAM),
+ getIntegerProperty(RECONNECTION_DELAY_PARAM, DEFAULT_RECONNECTION_DELAY),
+ configuration);
+ // @formatter:on
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SyslogAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SyslogAppenderBuilder.java
new file mode 100644
index 00000000000..2e215e51055
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SyslogAppenderBuilder.java
@@ -0,0 +1,182 @@
+/*
+ * 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.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.layout.Log4j1SyslogLayout;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.SyslogAppender;
+import org.apache.logging.log4j.core.net.Facility;
+import org.apache.logging.log4j.core.net.Protocol;
+import org.apache.logging.log4j.plugins.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.Strings;
+import org.w3c.dom.Element;
+
+import java.io.Serializable;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.apache.log4j.builders.BuilderManager.NAMESPACE;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.*;
+
+/**
+ * Build a File Appender
+ */
+@Namespace(NAMESPACE)
+@Plugin("org.apache.log4j.net.SyslogAppender")
+public class SyslogAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+ private static final String DEFAULT_HOST = "localhost";
+ private static int DEFAULT_PORT = 514;
+ private static final String DEFAULT_FACILITY = "LOCAL0";
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ private static final String FACILITY_PARAM = "Facility";
+ private static final String FACILITY_PRINTING_PARAM = "FacilityPrinting";
+ private static final String HEADER_PARAM = "Header";
+ private static final String PROTOCOL_PARAM = "Protocol";
+ private static final String SYSLOG_HOST_PARAM = "SyslogHost";
+
+ public SyslogAppenderBuilder() {
+ }
+
+ public SyslogAppenderBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+ final String name = getNameAttribute(appenderElement);
+ final AtomicReference layout = new AtomicReference<>();
+ final AtomicReference filter = new AtomicReference<>();
+ final AtomicReference facility = new AtomicReference<>();
+ final AtomicReference level = new AtomicReference<>();
+ final AtomicReference host = new AtomicReference<>();
+ final AtomicReference protocol = new AtomicReference<>(Protocol.TCP);
+ final AtomicBoolean header = new AtomicBoolean(false);
+ final AtomicBoolean facilityPrinting = new AtomicBoolean(false);
+ forEachElement(appenderElement.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case LAYOUT_TAG:
+ layout.set(config.parseLayout(currentElement));
+ break;
+ case FILTER_TAG:
+ config.addFilter(filter, currentElement);
+ break;
+ case PARAM_TAG:
+ switch (getNameAttributeKey(currentElement)) {
+ case FACILITY_PARAM:
+ set(FACILITY_PARAM, currentElement, facility);
+ break;
+ case FACILITY_PRINTING_PARAM:
+ set(FACILITY_PRINTING_PARAM, currentElement, facilityPrinting);
+ break;
+ case HEADER_PARAM:
+ set(HEADER_PARAM, currentElement, header);
+ break;
+ case PROTOCOL_PARAM:
+ protocol.set(Protocol.valueOf(getValueAttribute(currentElement, Protocol.TCP.name())));
+ break;
+ case SYSLOG_HOST_PARAM:
+ set(SYSLOG_HOST_PARAM, currentElement, host);
+ break;
+ case THRESHOLD_PARAM:
+ set(THRESHOLD_PARAM, currentElement, level);
+ break;
+ }
+ break;
+ }
+ });
+
+ return createAppender(name, config, layout.get(), facility.get(), filter.get(), host.get(), level.get(),
+ protocol.get(), header.get(), facilityPrinting.get());
+ }
+
+
+ @Override
+ public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix,
+ final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
+ final Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+ final Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+ final String level = getProperty(THRESHOLD_PARAM);
+ final String facility = getProperty(FACILITY_PARAM, DEFAULT_FACILITY);
+ final boolean facilityPrinting = getBooleanProperty(FACILITY_PRINTING_PARAM, false);
+ final boolean header = getBooleanProperty(HEADER_PARAM, false);
+ final String protocol = getProperty(PROTOCOL_PARAM, Protocol.TCP.name());
+ final String syslogHost = getProperty(SYSLOG_HOST_PARAM, DEFAULT_HOST + ":" + DEFAULT_PORT);
+
+ return createAppender(name, configuration, layout, facility, filter, syslogHost, level,
+ Protocol.valueOf(protocol), header, facilityPrinting);
+ }
+
+ private Appender createAppender(final String name, final Log4j1Configuration configuration, final Layout layout,
+ final String facility, final Filter filter, final String syslogHost, final String level, final Protocol protocol,
+ final boolean header, final boolean facilityPrinting) {
+ final AtomicReference host = new AtomicReference<>();
+ final AtomicInteger port = new AtomicInteger();
+ resolveSyslogHost(syslogHost, host, port);
+ final org.apache.logging.log4j.core.Layout extends Serializable> messageLayout = LayoutAdapter.adapt(layout);
+ final Log4j1SyslogLayout appenderLayout = Log4j1SyslogLayout.newBuilder()
+ .setHeader(header)
+ .setFacility(Facility.toFacility(facility))
+ .setFacilityPrinting(facilityPrinting)
+ .setMessageLayout(messageLayout)
+ .build();
+
+ final org.apache.logging.log4j.core.Filter fileFilter = buildFilters(level, filter);
+ return AppenderWrapper.adapt(SyslogAppender.newSyslogAppenderBuilder()
+ .setName(name)
+ .setConfiguration(configuration)
+ .setLayout(appenderLayout)
+ .setFilter(fileFilter)
+ .setPort(port.get())
+ .setProtocol(protocol)
+ .setHost(host.get())
+ .build());
+ }
+
+ private void resolveSyslogHost(final String syslogHost, final AtomicReference host, final AtomicInteger port) {
+ //
+ // If not an unbracketed IPv6 address then
+ // parse as a URL
+ //
+ final String[] parts = syslogHost != null ? syslogHost.split(":") : Strings.EMPTY_ARRAY;
+ if (parts.length == 1) {
+ host.set(parts[0]);
+ port.set(DEFAULT_PORT);
+ } else if (parts.length == 2) {
+ host.set(parts[0]);
+ port.set(Integer.parseInt(parts[1].trim()));
+ } else {
+ LOGGER.warn("Invalid {} setting: {}. Using default.", SYSLOG_HOST_PARAM, syslogHost);
+ host.set(DEFAULT_HOST);
+ port.set(DEFAULT_PORT);
+ }
+ }
+}
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
new file mode 100644
index 00000000000..0691665a6ea
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/DenyAllFilterBuilder.java
@@ -0,0 +1,46 @@
+/*
+ * 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.filter;
+
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.core.filter.DenyAllFilter;
+import org.apache.logging.log4j.plugins.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.w3c.dom.Element;
+
+import static org.apache.log4j.builders.BuilderManager.NAMESPACE;
+
+/**
+ * Build a Pattern Layout
+ */
+@Namespace(NAMESPACE)
+@Plugin("org.apache.log4j.varia.DenyAllFilter")
+public class DenyAllFilterBuilder implements FilterBuilder {
+
+ @Override
+ public Filter parse(Element filterElement, XmlConfiguration config) {
+ return new FilterWrapper(DenyAllFilter.newBuilder().build());
+ }
+
+ @Override
+ public Filter parse(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
new file mode 100644
index 00000000000..8153bdd7e6b
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/FilterBuilder.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.log4j.builders.filter;
+
+import org.apache.log4j.builders.Parser;
+import org.apache.log4j.spi.Filter;
+
+/**
+ * Define a Filter Builder.
+ */
+public interface FilterBuilder extends Parser {
+ // empty
+}
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
new file mode 100644
index 00000000000..81554124d29
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelMatchFilterBuilder.java
@@ -0,0 +1,95 @@
+/*
+ * 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.filter;
+
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.filter.LevelMatchFilter;
+import org.apache.logging.log4j.plugins.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.w3c.dom.Element;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.apache.log4j.builders.BuilderManager.NAMESPACE;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+/**
+ * Build a Level match filter.
+ */
+@Namespace(NAMESPACE)
+@Plugin("org.apache.log4j.varia.LevelMatchFilter")
+public class LevelMatchFilterBuilder extends AbstractBuilder implements FilterBuilder {
+
+ 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 parse(Element filterElement, XmlConfiguration config) {
+ final AtomicReference level = new AtomicReference<>();
+ final AtomicBoolean acceptOnMatch = new AtomicBoolean();
+ forEachElement(filterElement.getElementsByTagName("param"), currentElement -> {
+ if (currentElement.getTagName().equals("param")) {
+ switch (getNameAttributeKey(currentElement)) {
+ case LEVEL:
+ level.set(getValueAttribute(currentElement));
+ break;
+ case ACCEPT_ON_MATCH:
+ acceptOnMatch.set(getBooleanValueAttribute(currentElement));
+ break;
+ }
+ }
+ });
+ return createFilter(level.get(), acceptOnMatch.get());
+ }
+
+ @Override
+ public Filter parse(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 != null) {
+ lvl = OptionConverter.toLevel(level, org.apache.log4j.Level.ERROR).getVersion2Level();
+ }
+ 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 FilterWrapper.adapt(LevelMatchFilter.newBuilder()
+ .setLevel(lvl)
+ .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/filter/LevelRangeFilterBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.java
new file mode 100644
index 00000000000..7219ed2538a
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.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.builders.filter;
+
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.filter.LevelRangeFilter;
+import org.apache.logging.log4j.plugins.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.w3c.dom.Element;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.apache.log4j.builders.BuilderManager.NAMESPACE;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+/**
+ * Build a Level match filter.
+ */
+@Namespace(NAMESPACE)
+@Plugin("org.apache.log4j.varia.LevelRangeFilter")
+public class LevelRangeFilterBuilder extends AbstractBuilder implements FilterBuilder {
+
+ 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 parse(Element filterElement, XmlConfiguration config) {
+ final AtomicReference levelMax = new AtomicReference<>();
+ final AtomicReference levelMin = new AtomicReference<>();
+ final AtomicBoolean acceptOnMatch = new AtomicBoolean();
+ forEachElement(filterElement.getElementsByTagName("param"), currentElement -> {
+ if (currentElement.getTagName().equals("param")) {
+ switch (getNameAttributeKey(currentElement)) {
+ case LEVEL_MAX:
+ levelMax.set(getValueAttribute(currentElement));
+ break;
+ case LEVEL_MIN:
+ levelMax.set(getValueAttribute(currentElement));
+ break;
+ case ACCEPT_ON_MATCH:
+ acceptOnMatch.set(getBooleanValueAttribute(currentElement));
+ break;
+ }
+ }
+ });
+ return createFilter(levelMax.get(), levelMin.get(), acceptOnMatch.get());
+ }
+
+ @Override
+ public Filter parse(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 != null) {
+ max = OptionConverter.toLevel(levelMax, org.apache.log4j.Level.FATAL).getVersion2Level();
+ }
+ if (levelMin != null) {
+ min = OptionConverter.toLevel(levelMin, org.apache.log4j.Level.DEBUG).getVersion2Level();
+ }
+ 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;
+
+ return FilterWrapper.adapt(LevelRangeFilter.createFilter(min, max, onMatch,
+ org.apache.logging.log4j.core.Filter.Result.DENY));
+ }
+}
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
new file mode 100644
index 00000000000..311a1006133
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/StringMatchFilterBuilder.java
@@ -0,0 +1,98 @@
+/*
+ * 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.filter;
+
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.filter.StringMatchFilter;
+import org.apache.logging.log4j.plugins.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.apache.log4j.builders.BuilderManager.NAMESPACE;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+/**
+ * Build a String match filter.
+ */
+@Namespace(NAMESPACE)
+@Plugin("org.apache.log4j.varia.StringMatchFilter")
+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";
+
+ public StringMatchFilterBuilder() {
+ super();
+ }
+
+ public StringMatchFilterBuilder(String prefix, Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Filter parse(Element filterElement, XmlConfiguration config) {
+ final AtomicBoolean acceptOnMatch = new AtomicBoolean();
+ final AtomicReference text = new AtomicReference<>();
+ forEachElement(filterElement.getElementsByTagName("param"), currentElement -> {
+ if (currentElement.getTagName().equals("param")) {
+ switch (getNameAttributeKey(currentElement)) {
+ case STRING_TO_MATCH:
+ text.set(getValueAttribute(currentElement));
+ break;
+ case ACCEPT_ON_MATCH:
+ acceptOnMatch.set(getBooleanValueAttribute(currentElement));
+ break;
+
+ }
+ }
+ });
+ return createFilter(text.get(), acceptOnMatch.get());
+ }
+
+ @Override
+ public Filter parse(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.error("No text provided for StringMatchFilter");
+ return null;
+ }
+ 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 FilterWrapper.adapt(StringMatchFilter.newBuilder()
+ .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
new file mode 100644
index 00000000000..1c5f1f336a2
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/HtmlLayoutBuilder.java
@@ -0,0 +1,85 @@
+/*
+ * 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.layout;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.core.layout.HtmlLayout;
+import org.apache.logging.log4j.plugins.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.w3c.dom.Element;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.apache.log4j.builders.BuilderManager.NAMESPACE;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+/**
+ * Build a Pattern Layout
+ */
+@Namespace(NAMESPACE)
+@Plugin("org.apache.log4j.HTMLLayout")
+public class HtmlLayoutBuilder extends AbstractBuilder implements LayoutBuilder {
+
+ private static final String DEFAULT_TITLE = "Log4J Log Messages";
+ private static final String TITLE_PARAM = "Title";
+ private static final String LOCATION_INFO_PARAM = "LocationInfo";
+
+ public HtmlLayoutBuilder() {
+ }
+
+ public HtmlLayoutBuilder(String prefix, Properties props) {
+ super(prefix, props);
+ }
+
+
+ @Override
+ public Layout parse(Element layoutElement, XmlConfiguration config) {
+ final AtomicReference title = new AtomicReference<>("Log4J Log Messages");
+ final AtomicBoolean locationInfo = new AtomicBoolean();
+ forEachElement(layoutElement.getElementsByTagName("param"), currentElement -> {
+ if (currentElement.getTagName().equals(PARAM_TAG)) {
+ if (TITLE_PARAM.equalsIgnoreCase(currentElement.getAttribute("name"))) {
+ title.set(currentElement.getAttribute("value"));
+ } else if (LOCATION_INFO_PARAM.equalsIgnoreCase(currentElement.getAttribute("name"))) {
+ locationInfo.set(getBooleanValueAttribute(currentElement));
+ }
+ }
+ });
+ return createLayout(title.get(), locationInfo.get());
+ }
+
+ @Override
+ public Layout parse(PropertiesConfiguration config) {
+ String title = getProperty(TITLE_PARAM, DEFAULT_TITLE);
+ boolean locationInfo = getBooleanProperty(LOCATION_INFO_PARAM);
+ return createLayout(title, locationInfo);
+ }
+
+ private Layout createLayout(String title, boolean locationInfo) {
+ return LayoutWrapper.adapt(HtmlLayout.newBuilder()
+ .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
new file mode 100644
index 00000000000..72c17637bcd
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/LayoutBuilder.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.log4j.builders.layout;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.builders.Parser;
+
+/**
+ * Define a Layout Builder.
+ */
+public interface LayoutBuilder extends Parser {
+ // empty
+}
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
new file mode 100644
index 00000000000..e55b5b00056
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/PatternLayoutBuilder.java
@@ -0,0 +1,109 @@
+/*
+ * 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.layout;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.LayoutWrapper;
+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.layout.PatternLayout;
+import org.apache.logging.log4j.plugins.Namespace;
+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.NAMESPACE;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+
+/**
+ * Build a Pattern Layout
+ */
+@Namespace(NAMESPACE)
+@Plugin("org.apache.log4j.PatternLayout")
+@PluginAliases("org.apache.log4j.EnhancedPatternLayout")
+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 parse(final Element layoutElement, final XmlConfiguration config) {
+ NodeList params = layoutElement.getElementsByTagName("param");
+ final int length = params.getLength();
+ String pattern = null;
+ for (int index = 0; index < length; ++ index) {
+ Node currentNode = params.item(index);
+ if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+ Element currentElement = (Element) currentNode;
+ if (currentElement.getTagName().equals(PARAM_TAG)) {
+ if (PATTERN.equalsIgnoreCase(currentElement.getAttribute("name"))) {
+ pattern = currentElement.getAttribute("value");
+ break;
+ }
+ }
+ }
+ }
+ return createLayout(pattern, config);
+ }
+
+ @Override
+ public Layout parse(final PropertiesConfiguration config) {
+ String pattern = getProperty(PATTERN);
+ return createLayout(pattern, config);
+ }
+
+ 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 LayoutWrapper.adapt(PatternLayout.newBuilder()
+ .setPattern(pattern
+ // Log4j 2 and Log4j 1 level names differ for custom levels
+ .replaceAll("%([-\\.\\d]*)p(?!\\w)", "%$1v1Level")
+ // 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
+ .replaceAll("%([-\\.\\d]*)x(?!\\w)", "%$1ndc")
+
+ // 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
+ .replaceAll("%([-\\.\\d]*)X(?!\\w)", "%$1properties"))
+ .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
new file mode 100644
index 00000000000..85bef1e1b0e
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.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.builders.layout;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.plugins.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.w3c.dom.Element;
+
+import static org.apache.log4j.builders.BuilderManager.NAMESPACE;
+
+/**
+ * Build a Pattern Layout
+ */
+@Namespace(NAMESPACE)
+@Plugin("org.apache.log4j.SimpleLayout")
+public class SimpleLayoutBuilder implements LayoutBuilder {
+
+ @Override
+ public Layout parse(Element layoutElement, XmlConfiguration config) {
+ return new LayoutWrapper(PatternLayout.newBuilder()
+ .setPattern("%v1Level - %m%n")
+ .setConfiguration(config)
+ .build());
+ }
+
+ @Override
+ public Layout parse(PropertiesConfiguration config) {
+ return new LayoutWrapper(PatternLayout.newBuilder()
+ .setPattern("%v1Level - %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
new file mode 100644
index 00000000000..de7f384949f
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/TTCCLayoutBuilder.java
@@ -0,0 +1,132 @@
+/*
+ * 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.layout;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.LayoutWrapper;
+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.core.layout.PatternLayout;
+import org.apache.logging.log4j.plugins.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.w3c.dom.Element;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.apache.log4j.builders.BuilderManager.NAMESPACE;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+/**
+ * Build a Pattern Layout
+ */
+@Namespace(NAMESPACE)
+@Plugin("org.apache.log4j.TTCCLayout")
+public class TTCCLayoutBuilder extends AbstractBuilder implements LayoutBuilder {
+
+ 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 parse(Element layoutElement, XmlConfiguration config) {
+ final AtomicBoolean threadPrinting = new AtomicBoolean(Boolean.TRUE);
+ final AtomicBoolean categoryPrefixing = new AtomicBoolean(Boolean.TRUE);
+ final AtomicBoolean contextPrinting = new AtomicBoolean(Boolean.TRUE);
+ final AtomicReference dateFormat = new AtomicReference<>(RELATIVE);
+ final AtomicReference timezone = new AtomicReference<>();
+ forEachElement(layoutElement.getElementsByTagName("param"), currentElement -> {
+ if (currentElement.getTagName().equals(PARAM_TAG)) {
+ switch (getNameAttributeKey(currentElement)) {
+ case THREAD_PRINTING_PARAM:
+ threadPrinting.set(getBooleanValueAttribute(currentElement));
+ break;
+ case CATEGORY_PREFIXING_PARAM:
+ categoryPrefixing.set(getBooleanValueAttribute(currentElement));
+ break;
+ case CONTEXT_PRINTING_PARAM:
+ contextPrinting.set(getBooleanValueAttribute(currentElement));
+ break;
+ case DATE_FORMAT_PARAM:
+ dateFormat.set(getValueAttribute(currentElement));
+ break;
+ case TIMEZONE_FORMAT:
+ timezone.set(getValueAttribute(currentElement));
+ break;
+ }
+ }
+ });
+ return createLayout(threadPrinting.get(), categoryPrefixing.get(), contextPrinting.get(),
+ dateFormat.get(), timezone.get(), config);
+ }
+
+ @Override
+ public Layout parse(PropertiesConfiguration config) {
+ boolean threadPrinting = getBooleanProperty(THREAD_PRINTING_PARAM, true);
+ boolean categoryPrefixing = getBooleanProperty(CATEGORY_PREFIXING_PARAM, true);
+ boolean contextPrinting = getBooleanProperty(CONTEXT_PRINTING_PARAM, true);
+ String dateFormat = getProperty(DATE_FORMAT_PARAM, RELATIVE);
+ 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 != null) {
+ if (RELATIVE.equalsIgnoreCase(dateFormat)) {
+ sb.append("%r ");
+ } else if (!NULL.equalsIgnoreCase(dateFormat)){
+ sb.append("%d{").append(dateFormat).append("}");
+ if (timezone != null) {
+ sb.append("{").append(timezone).append("}");
+ }
+ sb.append(" ");
+ }
+ }
+ if (threadPrinting) {
+ sb.append("[%t] ");
+ }
+ sb.append("%p ");
+ if (categoryPrefixing) {
+ sb.append("%c ");
+ }
+ if (contextPrinting) {
+ sb.append("%notEmpty{%ndc }");
+ }
+ sb.append("- %m%n");
+ return LayoutWrapper.adapt(PatternLayout.newBuilder()
+ .setPattern(sb.toString())
+ .setConfiguration(config)
+ .build());
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/XmlLayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/XmlLayoutBuilder.java
new file mode 100644
index 00000000000..c0137f4296f
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/XmlLayoutBuilder.java
@@ -0,0 +1,78 @@
+/*
+ * 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.layout;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.layout.Log4j1XmlLayout;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.plugins.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.w3c.dom.Element;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static org.apache.log4j.builders.BuilderManager.NAMESPACE;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+/**
+ * Build an XML Layout
+ */
+@Namespace(NAMESPACE)
+@Plugin("org.apache.log4j.xml.XMLLayout")
+public class XmlLayoutBuilder extends AbstractBuilder implements LayoutBuilder {
+
+ 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 parse(Element layoutElement, XmlConfiguration config) {
+ final AtomicBoolean properties = new AtomicBoolean();
+ final AtomicBoolean locationInfo = new AtomicBoolean();
+ forEachElement(layoutElement.getElementsByTagName(PARAM_TAG), currentElement -> {
+ if (PROPERTIES.equalsIgnoreCase(currentElement.getAttribute("name"))) {
+ properties.set(getBooleanValueAttribute(currentElement));
+ } else if (LOCATION_INFO.equalsIgnoreCase(currentElement.getAttribute("name"))) {
+ locationInfo.set(getBooleanValueAttribute(currentElement));
+ }
+ });
+ return createLayout(properties.get(), locationInfo.get());
+ }
+
+ @Override
+ public Layout parse(PropertiesConfiguration config) {
+ boolean properties = getBooleanProperty(PROPERTIES);
+ boolean locationInfo = getBooleanProperty(LOCATION_INFO);
+ return createLayout(properties, locationInfo);
+ }
+
+ private Layout createLayout(boolean properties, boolean locationInfo) {
+ return LayoutWrapper.adapt(Log4j1XmlLayout.createLayout(locationInfo, properties));
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rewrite/RewritePolicyBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rewrite/RewritePolicyBuilder.java
new file mode 100644
index 00000000000..e83d0338727
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rewrite/RewritePolicyBuilder.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.log4j.builders.rewrite;
+
+import org.apache.log4j.builders.Parser;
+import org.apache.log4j.rewrite.RewritePolicy;
+
+/**
+ * Define a RewritePolicy Builder.
+ */
+public interface RewritePolicyBuilder extends Parser {
+ // empty
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/component/helpers/Constants.java b/log4j-1.2-api/src/main/java/org/apache/log4j/component/helpers/Constants.java
new file mode 100644
index 00000000000..9a41b2cd21a
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/component/helpers/Constants.java
@@ -0,0 +1,126 @@
+/*
+ * 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.component.helpers;
+
+
+/**
+ * Constants used internally throughout log4j.
+ *
+ */
+public interface Constants {
+
+ /**
+ * log4j package name string literal.
+ */
+ String LOG4J_PACKAGE_NAME = "org.apache.log4j";
+
+ /**
+ * The name of the default repository is "default" (without the quotes).
+ */
+ String DEFAULT_REPOSITORY_NAME = "default";
+
+ /**
+ * application string literal.
+ */
+ String APPLICATION_KEY = "application";
+ /**
+ * hostname string literal.
+ */
+ String HOSTNAME_KEY = "hostname";
+ /**
+ * receiver string literal.
+ */
+ String RECEIVER_NAME_KEY = "receiver";
+ /**
+ * log4jid string literal.
+ */
+ String LOG4J_ID_KEY = "log4jid";
+ /**
+ * time stamp pattern string literal.
+ */
+ String TIMESTAMP_RULE_FORMAT = "yyyy/MM/dd HH:mm:ss";
+
+ /**
+ * The default property file name for automatic configuration.
+ */
+ String DEFAULT_CONFIGURATION_FILE = "log4j.properties";
+ /**
+ * The default XML configuration file name for automatic configuration.
+ */
+ String DEFAULT_XML_CONFIGURATION_FILE = "log4j.xml";
+ /**
+ * log4j.configuration string literal.
+ */
+ String DEFAULT_CONFIGURATION_KEY = "log4j.configuration";
+ /**
+ * log4j.configuratorClass string literal.
+ */
+ String CONFIGURATOR_CLASS_KEY = "log4j.configuratorClass";
+
+ /**
+ * JNDI context name string literal.
+ */
+ String JNDI_CONTEXT_NAME = "java:comp/env/log4j/context-name";
+
+ /**
+ * TEMP_LIST_APPENDER string literal.
+ */
+ String TEMP_LIST_APPENDER_NAME = "TEMP_LIST_APPENDER";
+ /**
+ * TEMP_CONSOLE_APPENDER string literal.
+ */
+ String TEMP_CONSOLE_APPENDER_NAME = "TEMP_CONSOLE_APPENDER";
+ /**
+ * Codes URL string literal.
+ */
+ String CODES_HREF =
+ "http://logging.apache.org/log4j/docs/codes.html";
+
+
+ /**
+ * ABSOLUTE string literal.
+ */
+ String ABSOLUTE_FORMAT = "ABSOLUTE";
+ /**
+ * SimpleTimePattern for ABSOLUTE.
+ */
+ String ABSOLUTE_TIME_PATTERN = "HH:mm:ss,SSS";
+
+ /**
+ * SimpleTimePattern for ABSOLUTE.
+ */
+ String SIMPLE_TIME_PATTERN = "HH:mm:ss";
+
+ /**
+ * DATE string literal.
+ */
+ String DATE_AND_TIME_FORMAT = "DATE";
+ /**
+ * SimpleTimePattern for DATE.
+ */
+ String DATE_AND_TIME_PATTERN = "dd MMM yyyy HH:mm:ss,SSS";
+
+ /**
+ * ISO8601 string literal.
+ */
+ String ISO8601_FORMAT = "ISO8601";
+ /**
+ * SimpleTimePattern for ISO8601.
+ */
+ String ISO8601_PATTERN = "yyyy-MM-dd HH:mm:ss,SSS";
+}
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
new file mode 100644
index 00000000000..256f8170c47
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1Configuration.java
@@ -0,0 +1,77 @@
+/*
+ * 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.Level;
+import org.apache.log4j.builders.BuilderManager;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.AbstractConfiguration;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.Reconfigurable;
+
+/**
+ * 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";
+
+ /**
+ * The effective level used, when the configuration uses a non-existent custom
+ * level.
+ */
+ public static final Level DEFAULT_LEVEL = Level.DEBUG;
+
+ protected final BuilderManager manager;
+
+ public Log4j1Configuration(final LoggerContext loggerContext, final ConfigurationSource configurationSource,
+ int monitorIntervalSeconds) {
+ super(loggerContext, configurationSource);
+ initializeWatchers(this, configurationSource, monitorIntervalSeconds);
+ manager = injector.getInstance(BuilderManager.class);
+ }
+
+ public BuilderManager getBuilderManager() {
+ return manager;
+ }
+
+ /**
+ * Initialize the configuration.
+ */
+ @Override
+ public void initialize() {
+ injector.registerBinding(Configuration.KEY, () -> this);
+ getStrSubstitutor().setConfiguration(this);
+ getConfigurationStrSubstitutor().setConfiguration(this);
+ super.getScheduler().start();
+ doConfigure();
+ setState(State.INITIALIZED);
+ LOGGER.debug("Configuration {} initialized", this);
+ }
+
+ @Override
+ public Configuration reconfigure() {
+ return null;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationConverter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationConverter.java
index 4475f6aba7d..caede4e4479 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationConverter.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationConverter.java
@@ -16,6 +16,8 @@
*/
package org.apache.log4j.config;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -26,9 +28,14 @@
import java.nio.file.attribute.BasicFileAttributes;
import java.util.concurrent.atomic.AtomicInteger;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
import org.apache.logging.log4j.core.config.ConfigurationException;
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration;
+import org.apache.logging.log4j.core.config.builder.impl.DefaultConfigurationBuilder;
import org.apache.logging.log4j.core.tools.BasicCommandLineArguments;
import org.apache.logging.log4j.core.tools.picocli.CommandLine;
import org.apache.logging.log4j.core.tools.picocli.CommandLine.Command;
@@ -175,11 +182,16 @@ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attr
final int lastIndex = newFile.lastIndexOf(".");
newFile = lastIndex < 0 ? newFile + FILE_EXT_XML
: newFile.substring(0, lastIndex) + FILE_EXT_XML;
- final Path resolved = file.resolveSibling(newFile);
+ final Path resolvedPath = file.resolveSibling(newFile);
try (final InputStream input = new InputStreamWrapper(Files.newInputStream(file), file.toString());
- final OutputStream output = Files.newOutputStream(resolved)) {
+ final OutputStream output = Files.newOutputStream(resolvedPath)) {
try {
- convert(input, output);
+ final ByteArrayOutputStream tmpOutput = new ByteArrayOutputStream();
+ convert(input, tmpOutput);
+ tmpOutput.close();
+ DefaultConfigurationBuilder.formatXml(
+ new StreamSource(new ByteArrayInputStream(tmpOutput.toByteArray())),
+ new StreamResult(output));
countOKs.incrementAndGet();
} catch (ConfigurationException | IOException e) {
countFails.incrementAndGet();
@@ -187,8 +199,14 @@ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attr
throw e;
}
e.printStackTrace();
+ } catch (TransformerException e) {
+ countFails.incrementAndGet();
+ if (cla.isFailFast()) {
+ throw new IOException(e);
+ }
+ e.printStackTrace();
}
- verbose("Wrote %s", resolved);
+ verbose("Wrote %s", resolvedPath);
}
}
return FileVisitResult.CONTINUE;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationFactory.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationFactory.java
index 83f66753d93..af4de345c0d 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationFactory.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationFactory.java
@@ -16,9 +16,6 @@
*/
package org.apache.log4j.config;
-import java.io.IOException;
-import java.io.InputStream;
-
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.ConfigurationException;
@@ -27,17 +24,21 @@
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration;
+import java.io.IOException;
+import java.io.InputStream;
+
/**
* Experimental ConfigurationFactory for Log4j 1.2 properties configuration files.
*/
// TODO
-// @Plugin(name = "Log4j1ConfigurationFactory", category = ConfigurationFactory.CATEGORY)
+// @Category(ConfigurationFactory.CATEGORY)
+// @Plugin("Log4j1ConfigurationFactory")
//
// Best Value?
// @Order(50)
public class Log4j1ConfigurationFactory extends ConfigurationFactory {
- private static final String[] SUFFIXES = {".properties"};
+ private static final String[] SUFFIXES = { ".properties" };
@Override
public Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) {
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationParser.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationParser.java
index 112ab4247fb..3d364bca02c 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationParser.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationParser.java
@@ -25,7 +25,9 @@
import java.util.Properties;
import java.util.TreeMap;
+import org.apache.log4j.helpers.OptionConverter;
import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Filter.Result;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.apache.logging.log4j.core.appender.FileAppender;
import org.apache.logging.log4j.core.appender.NullAppender;
@@ -39,7 +41,6 @@
import org.apache.logging.log4j.core.config.builder.api.LoggerComponentBuilder;
import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder;
import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration;
-import org.apache.logging.log4j.core.lookup.StrSubstitutor;
import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.Strings;
@@ -47,7 +48,7 @@
* Experimental parser for Log4j 1.2 properties configuration files.
*
* This class is not thread-safe.
- *
+ *
*
* From the Log4j 1.2 Javadocs:
*
@@ -66,10 +67,10 @@ public class Log4j1ConfigurationParser {
private static final String ROOTCATEGORY = "rootCategory";
private static final String TRUE = "true";
private static final String FALSE = "false";
+ private static final String RELATIVE = "RELATIVE";
+ private static final String NULL = "NULL";
private final Properties properties = new Properties();
- private StrSubstitutor strSubstitutorProperties;
- private StrSubstitutor strSubstitutorSystem;
private final ConfigurationBuilder builder = ConfigurationBuilderFactory
.newConfigurationBuilder();
@@ -89,8 +90,6 @@ public ConfigurationBuilder buildConfigurationBuilder(final
throws IOException {
try {
properties.load(input);
- strSubstitutorProperties = new StrSubstitutor(properties);
- strSubstitutorSystem = new StrSubstitutor(System.getProperties());
final String rootCategoryValue = getLog4jValue(ROOTCATEGORY);
final String rootLoggerValue = getLog4jValue(ROOTLOGGER);
if (rootCategoryValue == null && rootLoggerValue == null) {
@@ -102,9 +101,16 @@ public ConfigurationBuilder buildConfigurationBuilder(final
builder.setConfigurationName("Log4j1");
// DEBUG
final String debugValue = getLog4jValue("debug");
- if (Boolean.valueOf(debugValue)) {
+ if (Boolean.parseBoolean(debugValue)) {
builder.setStatusLevel(Level.DEBUG);
}
+ // global threshold
+ final String threshold = OptionConverter.findAndSubst(PropertiesConfiguration.THRESHOLD_KEY, properties);
+ if (threshold != null) {
+ final Level level = OptionConverter.convertLevel(threshold.trim(), Level.ALL);
+ builder.add(builder.newFilter("ThresholdFilter", Result.NEUTRAL, Result.DENY)
+ .addAttribute("level", level));
+ }
// Root
buildRootLogger(getLog4jValue(ROOTCATEGORY));
buildRootLogger(getLog4jValue(ROOTLOGGER));
@@ -145,13 +151,13 @@ private Map buildClassToPropertyPrefixMap() {
for (final Map.Entry
+ *
+ *
Try one last time with ClassLoader.getSystemResource(resource).
+ *
+ *
+ */
+ static public URL getResource(final String resource) {
+ ClassLoader classLoader = null;
+ URL url = null;
+
+ try {
+ if (!ignoreTCL) {
+ classLoader = getTCL();
+ if (classLoader != null) {
+ LogLog.debug("Trying to find [" + resource + "] using context classloader " + classLoader + ".");
+ url = classLoader.getResource(resource);
+ if (url != null) {
+ return url;
+ }
+ }
+ }
+
+ // We could not find resource. Ler us now try with the
+ // ClassLoader that loaded this class.
+ classLoader = Loader.class.getClassLoader();
+ if (classLoader != null) {
+ LogLog.debug("Trying to find [" + resource + "] using " + classLoader + " class loader.");
+ url = classLoader.getResource(resource);
+ if (url != null) {
+ return url;
+ }
+ }
+ } catch (final Throwable t) {
+ // can't be InterruptedException or InterruptedIOException
+ // since not declared, must be error or RuntimeError.
+ LogLog.warn(TSTR, t);
+ }
+
+ // Last ditch attempt: get the resource from the class path. It
+ // may be the case that clazz was loaded by the Extentsion class
+ // loader which the parent of the system class loader. Hence the
+ // code below.
+ LogLog.debug("Trying to find [" + resource + "] using ClassLoader.getSystemResource().");
+ return ClassLoader.getSystemResource(resource);
+ }
+
+ /**
+ * Gets a resource by delegating to getResource(String).
+ *
+ * @param resource resource name
+ * @param clazz class, ignored.
+ * @return URL to resource or null.
+ * @deprecated as of 1.2.
+ */
+ @Deprecated
+ public static URL getResource(final String resource, final Class clazz) {
+ return getResource(resource);
+ }
+
+ /**
+ * Shorthand for {@code Thread.currentThread().getContextClassLoader()}.
+ */
+ private static ClassLoader getTCL() {
+ return Thread.currentThread().getContextClassLoader();
+ }
+
+ /**
+ * Always returns false since Java 1.x support is long gone.
+ *
+ * @return Always false.
+ */
+ public static boolean isJava1() {
+ return false;
+ }
+
+ /**
+ * Loads the specified class using the ThreadcontextClassLoader, if that fails try
+ * Class.forname.
+ *
+ * @param clazz The class to load.
+ * @return The Class.
+ * @throws ClassNotFoundException Never thrown, declared for compatibility.
+ */
+ static public Class loadClass(final String clazz) throws ClassNotFoundException {
+ // Just call Class.forName(clazz) if we are instructed to ignore the TCL.
+ if (ignoreTCL) {
+ return Class.forName(clazz);
+ }
+ try {
+ return getTCL().loadClass(clazz);
+ } catch (final Throwable t) {
+ // ignore
+ }
+ return Class.forName(clazz);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/LogLog.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/LogLog.java
new file mode 100644
index 00000000000..d40773e1082
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/LogLog.java
@@ -0,0 +1,173 @@
+/*
+ * 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.logging.log4j.status.StatusLogger;
+
+/**
+ * Logs statements from within Log4j.
+ *
+ *
+ * Log4j components cannot make Log4j logging calls. However, it is sometimes useful for the user to learn about what
+ * Log4j is doing. You can enable Log4j internal logging by defining the log4j.configDebug variable.
+ *
+ *
+ * All Log4j internal debug calls go to System.out where as internal error messages are sent to
+ * System.err. All internal messages are prepended with the string "log4j: ".
+ *
+ *
+ * @since 0.8.2
+ */
+public class LogLog {
+
+ private static final StatusLogger LOGGER = StatusLogger.getLogger();
+
+ /**
+ * Makes Log4j print log4j-internal debug statements to System.out.
+ *
+ *
+ * The value of this string is {@value #DEBUG_KEY}
+ *
+ *
+ * Note that the search for all option names is case sensitive.
+ *
+ */
+ public static final String DEBUG_KEY = "log4j.debug";
+
+ /**
+ * Makes Log4j components print log4j-internal debug statements to System.out.
+ *
+ *
+ * The value of this string is {@value #CONFIG_DEBUG_KEY}.
+ *
+ *
+ * Note that the search for all option names is case sensitive.
+ *
+ *
+ * @deprecated Use {@link #DEBUG_KEY} instead.
+ */
+ @Deprecated
+ public static final String CONFIG_DEBUG_KEY = "log4j.configDebug";
+
+ /**
+ * Debug enabled Enable or disable.
+ */
+ protected static boolean debugEnabled = false;
+
+ /**
+ * In quietMode not even errors generate any output.
+ */
+ private static boolean quietMode = false;
+
+ static {
+ String key = OptionConverter.getSystemProperty(DEBUG_KEY, null);
+ if (key == null) {
+ key = OptionConverter.getSystemProperty(CONFIG_DEBUG_KEY, null);
+ }
+ if (key != null) {
+ debugEnabled = OptionConverter.toBoolean(key, true);
+ }
+ }
+
+ /**
+ * Logs Log4j internal debug statements.
+ *
+ * @param message the message object to log.
+ */
+ public static void debug(final String message) {
+ if (debugEnabled && !quietMode) {
+ LOGGER.debug(message);
+ }
+ }
+
+ /**
+ * Logs Log4j internal debug statements.
+ *
+ * @param message the message object to log.
+ * @param throwable the {@code Throwable} to log, including its stack trace.
+ */
+ public static void debug(final String message, final Throwable throwable) {
+ if (debugEnabled && !quietMode) {
+ LOGGER.debug(message, throwable);
+ }
+ }
+
+ /**
+ * Logs Log4j internal error statements.
+ *
+ * @param message the message object to log.
+ */
+ public static void error(final String message) {
+ if (!quietMode) {
+ LOGGER.error(message);
+ }
+ }
+
+ /**
+ * Logs Log4j internal error statements.
+ *
+ * @param message the message object to log.
+ * @param throwable the {@code Throwable} to log, including its stack trace.
+ */
+ public static void error(final String message, final Throwable throwable) {
+ if (!quietMode) {
+ LOGGER.error(message, throwable);
+ }
+ }
+
+ /**
+ * Enables and disables Log4j internal logging.
+ *
+ * @param enabled Enable or disable.
+ */
+ static public void setInternalDebugging(final boolean enabled) {
+ debugEnabled = enabled;
+ }
+
+ /**
+ * In quite mode no LogLog generates strictly no output, not even for errors.
+ *
+ * @param quietMode A true for not
+ */
+ public static void setQuietMode(final boolean quietMode) {
+ LogLog.quietMode = quietMode;
+ }
+
+ /**
+ * Logs Log4j internal warning statements.
+ *
+ * @param message the message object to log.
+ */
+ public static void warn(final String message) {
+ if (!quietMode) {
+ LOGGER.warn(message);
+ }
+ }
+
+ /**
+ * Logs Log4j internal warnings.
+ *
+ * @param message the message object to log.
+ * @param throwable the {@code Throwable} to log, including its stack trace.
+ */
+ public static void warn(final String message, final Throwable throwable) {
+ if (!quietMode) {
+ LOGGER.warn(message, throwable);
+ }
+ }
+}
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
new file mode 100644
index 00000000000..ba6e4596bc7
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java
@@ -0,0 +1,690 @@
+/*
+ * 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 java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Priority;
+import org.apache.log4j.PropertyConfigurator;
+import org.apache.log4j.spi.Configurator;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.spi.StandardLevel;
+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.Strings;
+
+/**
+ * A convenience class to convert property values to specific types.
+ */
+public class OptionConverter {
+
+ private static class CharMap {
+ final char key;
+ final char replacement;
+
+ public CharMap(final char key, final char replacement) {
+ this.key = key;
+ this.replacement = replacement;
+ }
+ }
+ static String DELIM_START = "${";
+ static char DELIM_STOP = '}';
+ static int DELIM_START_LEN = 2;
+ static int DELIM_STOP_LEN = 1;
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ /**
+ * A Log4j 1.x level above or equal to this value is considered as OFF.
+ */
+ static final int MAX_CUTOFF_LEVEL = Priority.FATAL_INT
+ + 100 * (StandardLevel.FATAL.intLevel() - StandardLevel.OFF.intLevel() - 1) + 1;
+ /**
+ * A Log4j 1.x level below or equal to this value is considered as ALL.
+ *
+ * Log4j 2.x ALL to TRACE interval is shorter. This is {@link Priority#ALL_INT}
+ * plus the difference.
+ */
+ static final int MIN_CUTOFF_LEVEL = Priority.ALL_INT + Level.TRACE_INT
+ - (Priority.ALL_INT + StandardLevel.ALL.intLevel()) + StandardLevel.TRACE.intLevel();
+ /**
+ * Cache of currently known levels.
+ */
+ static final ConcurrentMap LEVELS = new ConcurrentHashMap<>();
+ /**
+ * Postfix for all Log4j 2.x level names.
+ */
+ private static final String LOG4J2_LEVEL_CLASS = org.apache.logging.log4j.Level.class.getName();
+
+ private static final CharMap[] charMap = new CharMap[] {
+ new CharMap('n', '\n'),
+ new CharMap('r', '\r'),
+ new CharMap('t', '\t'),
+ new CharMap('f', '\f'),
+ new CharMap('\b', '\b'),
+ new CharMap('\"', '\"'),
+ new CharMap('\'', '\''),
+ new CharMap('\\', '\\')
+ };
+
+ public static String[] concatanateArrays(final String[] l, final String[] r) {
+ final int len = l.length + r.length;
+ final String[] a = new String[len];
+
+ System.arraycopy(l, 0, a, 0, l.length);
+ System.arraycopy(r, 0, a, l.length, r.length);
+
+ return a;
+ }
+
+ static int toLog4j2Level(final int v1Level) {
+ // I don't believe anyone uses values much bigger than FATAL
+ if (v1Level >= MAX_CUTOFF_LEVEL) {
+ return StandardLevel.OFF.intLevel();
+ }
+ // Linear transformation up to debug: CUTOFF_LEVEL -> OFF, DEBUG -> DEBUG
+ if (v1Level > Priority.DEBUG_INT) {
+ final int offset = Math.round((v1Level - Priority.DEBUG_INT) / 100.0f);
+ return StandardLevel.DEBUG.intLevel() - offset;
+ }
+ // Steeper linear transformation
+ if (v1Level > Level.TRACE_INT) {
+ final int offset = Math.round((v1Level - Level.TRACE_INT) / 50.0f);
+ return StandardLevel.TRACE.intLevel() - offset;
+ }
+ if (v1Level > MIN_CUTOFF_LEVEL) {
+ final int offset = Level.TRACE_INT - v1Level;
+ return StandardLevel.TRACE.intLevel() + offset;
+ }
+ return StandardLevel.ALL.intLevel();
+ }
+
+ static int toLog4j1Level(int v2Level) {
+ if (v2Level == StandardLevel.ALL.intLevel()) {
+ return Priority.ALL_INT;
+ }
+ if (v2Level > StandardLevel.TRACE.intLevel()) {
+ return MIN_CUTOFF_LEVEL + (StandardLevel.ALL.intLevel() - v2Level);
+ }
+ // Inflating by 50
+ if (v2Level > StandardLevel.DEBUG.intLevel()) {
+ return Level.TRACE_INT + 50 * (StandardLevel.TRACE.intLevel() - v2Level);
+ }
+ // Inflating by 100
+ if (v2Level > StandardLevel.OFF.intLevel()) {
+ return Priority.DEBUG_INT + 100 * (StandardLevel.DEBUG.intLevel() - v2Level);
+ }
+ return Priority.OFF_INT;
+ }
+
+ static int toSyslogLevel(int v2Level) {
+ if (v2Level <= StandardLevel.FATAL.intLevel()) {
+ return 0;
+ }
+ if (v2Level <= StandardLevel.ERROR.intLevel()) {
+ return 3 - (3 * (StandardLevel.ERROR.intLevel() - v2Level))
+ / (StandardLevel.ERROR.intLevel() - StandardLevel.FATAL.intLevel());
+ }
+ if (v2Level <= StandardLevel.WARN.intLevel()) {
+ return 4;
+ }
+ if (v2Level <= StandardLevel.INFO.intLevel()) {
+ return 6 - (2 * (StandardLevel.INFO.intLevel() - v2Level))
+ / (StandardLevel.INFO.intLevel() - StandardLevel.WARN.intLevel());
+ }
+ return 7;
+ }
+
+ public static org.apache.logging.log4j.Level createLevel(final Priority level) {
+ final String name = level.toString().toUpperCase() + "#" + level.getClass().getName();
+ return org.apache.logging.log4j.Level.forName(name, toLog4j2Level(level.toInt()));
+ }
+
+ public static org.apache.logging.log4j.Level convertLevel(final Priority level) {
+ return level != null ? level.getVersion2Level() : org.apache.logging.log4j.Level.ERROR;
+ }
+
+ /**
+ * @param level
+ * @return
+ */
+ public static Level convertLevel(final org.apache.logging.log4j.Level level) {
+ // level is standard or was created by Log4j 1.x custom level
+ Level actualLevel = toLevel(level.name(), null);
+ // level was created by Log4j 2.x
+ if (actualLevel == null) {
+ actualLevel = toLevel(LOG4J2_LEVEL_CLASS, level.name(), null);
+ }
+ return actualLevel != null ? actualLevel : Level.ERROR;
+ }
+
+ public static org.apache.logging.log4j.Level convertLevel(final String level,
+ final org.apache.logging.log4j.Level defaultLevel) {
+ final Level actualLevel = toLevel(level, null);
+ return actualLevel != null ? actualLevel.getVersion2Level() : defaultLevel;
+ }
+
+ public static String convertSpecialChars(final String s) {
+ char c;
+ final int len = s.length();
+ final StringBuilder sbuf = new StringBuilder(len);
+
+ int i = 0;
+ while (i < len) {
+ c = s.charAt(i++);
+ if (c == '\\') {
+ c = s.charAt(i++);
+ for (final CharMap entry : charMap) {
+ if (entry.key == c) {
+ c = entry.replacement;
+ }
+ }
+ }
+ sbuf.append(c);
+ }
+ return sbuf.toString();
+ }
+
+ /**
+ * Find the value corresponding to key in
+ * props. Then perform variable substitution on the
+ * found value.
+ * @param key The key used to locate the substitution string.
+ * @param props The properties to use in the substitution.
+ * @return The substituted string.
+ */
+ public static String findAndSubst(final String key, final Properties props) {
+ final String value = props.getProperty(key);
+ if (value == null) {
+ return null;
+ }
+
+ try {
+ return substVars(value, props);
+ } catch (final IllegalArgumentException e) {
+ LOGGER.error("Bad option value [{}].", value, e);
+ return value;
+ }
+ }
+
+
+ /**
+ * Very similar to System.getProperty except
+ * that the {@link SecurityException} is hidden.
+ *
+ * @param key The key to search for.
+ * @param def The default value to return.
+ * @return the string value of the system property, or the default
+ * value if there is no property with that key.
+ * @since 1.1
+ */
+ public static String getSystemProperty(final String key, final String def) {
+ try {
+ return System.getProperty(key, def);
+ } catch (final Throwable e) { // MS-Java throws com.ms.security.SecurityExceptionEx
+ LOGGER.debug("Was not allowed to read system property \"{}\".", key);
+ return def;
+ }
+ }
+
+ /**
+ * Instantiate an object given a class name. Check that the
+ * className is a subclass of
+ * superClass. If that test fails or the object could
+ * not be instantiated, then defaultValue is returned.
+ *
+ * @param className The fully qualified class name of the object to instantiate.
+ * @param superClass The class to which the new object should belong.
+ * @param defaultValue The object to return in case of non-fulfillment
+ * @return The created object.
+ */
+ public static Object instantiateByClassName(final String className, final Class> superClass,
+ final Object defaultValue) {
+ if (className != null) {
+ try {
+ final Object obj = LoaderUtil.newInstanceOf(className);
+ if (!superClass.isAssignableFrom(obj.getClass())) {
+ LOGGER.error("A \"{}\" object is not assignable to a \"{}\" variable", className,
+ superClass.getName());
+ return defaultValue;
+ }
+ return obj;
+ } catch (final ReflectiveOperationException e) {
+ LOGGER.error("Could not instantiate class [" + className + "].", e);
+ }
+ }
+ return defaultValue;
+ }
+
+ public static Object instantiateByKey(final Properties props, final String key, final Class superClass, final Object defaultValue) {
+
+ // Get the value of the property in string form
+ final String className = findAndSubst(key, props);
+ if (className == null) {
+ LogLog.error("Could not find value for key " + key);
+ return defaultValue;
+ }
+ // Trim className to avoid trailing spaces that cause problems.
+ return OptionConverter.instantiateByClassName(className.trim(), superClass, defaultValue);
+ }
+
+ /**
+ * Configure log4j given an {@link InputStream}.
+ *
+ * The InputStream will be interpreted by a new instance of a log4j configurator.
+ *
+ *
+ * All configurations steps are taken on the hierarchy passed as a parameter.
+ *
+ *
+ * @param inputStream The configuration input stream.
+ * @param clazz The class name, of the log4j configurator which will parse the inputStream. This must be a
+ * subclass of {@link Configurator}, or null. If this value is null then a default configurator of
+ * {@link PropertyConfigurator} is used.
+ * @param hierarchy The {@link LoggerRepository} to act on.
+ * @since 1.2.17
+ */
+ static public void selectAndConfigure(final InputStream inputStream, final String clazz, final LoggerRepository hierarchy) {
+ Configurator configurator = null;
+
+ if (clazz != null) {
+ LOGGER.debug("Preferred configurator class: " + clazz);
+ configurator = (Configurator) instantiateByClassName(clazz, Configurator.class, null);
+ if (configurator == null) {
+ LOGGER.error("Could not instantiate configurator [" + clazz + "].");
+ return;
+ }
+ } else {
+ configurator = new PropertyConfigurator();
+ }
+
+ configurator.doConfigure(inputStream, hierarchy);
+ }
+
+ /**
+ * Configure log4j given a URL.
+ *
+ * The url must point to a file or resource which will be interpreted by a new instance of a log4j configurator.
+ *
+ *
+ * All configurations steps are taken on the hierarchy passed as a parameter.
+ *
+ *
+ * @param url The location of the configuration file or resource.
+ * @param clazz The classname, of the log4j configurator which will parse the file or resource at url. This
+ * must be a subclass of {@link Configurator}, or null. If this value is null then a default configurator of
+ * {@link PropertyConfigurator} is used, unless the filename pointed to by url ends in '.xml', in
+ * which case {@link org.apache.log4j.xml.DOMConfigurator} is used.
+ * @param hierarchy The {@link LoggerRepository} to act on.
+ *
+ * @since 1.1.4
+ */
+ static public void selectAndConfigure(final URL url, String clazz, final LoggerRepository hierarchy) {
+ Configurator configurator = null;
+ final String filename = url.getFile();
+
+ if (clazz == null && filename != null && filename.endsWith(".xml")) {
+ clazz = "org.apache.log4j.xml.DOMConfigurator";
+ }
+
+ if (clazz != null) {
+ LOGGER.debug("Preferred configurator class: " + clazz);
+ configurator = (Configurator) instantiateByClassName(clazz, Configurator.class, null);
+ if (configurator == null) {
+ LOGGER.error("Could not instantiate configurator [" + clazz + "].");
+ return;
+ }
+ } else {
+ configurator = new PropertyConfigurator();
+ }
+
+ configurator.doConfigure(url, hierarchy);
+ }
+
+ /**
+ * Perform variable substitution in string val from the
+ * values of keys found in the system propeties.
+ *
+ *
The variable substitution delimeters are ${ and }.
+ *
+ *
For example, if the System properties contains "key=value", then
+ * the call
+ *
+ * String s = OptionConverter.substituteVars("Value of key is ${key}.");
+ *
+ *
+ * will set the variable s to "Value of key is value.".
+ *
+ *
If no value could be found for the specified key, then the
+ * props parameter is searched, if the value could not
+ * be found there, then substitution defaults to the empty string.
+ *
+ *
For example, if system propeties contains no value for the key
+ * "inexistentKey", then the call
+ *
+ *
+ * String s = OptionConverter.subsVars("Value of inexistentKey is [${inexistentKey}]");
+ *
+ * will set s to "Value of inexistentKey is []"
+ *
+ *
An {@link IllegalArgumentException} is thrown if
+ * val contains a start delimeter "${" which is not
+ * balanced by a stop delimeter "}".
+ *
+ *
Author Avy Sharell
+ *
+ * @param val The string on which variable substitution is performed.
+ * @param props The properties to use for the substitution.
+ * @return The substituted string.
+ * @throws IllegalArgumentException if val is malformed.
+ */
+ public static String substVars(final String val, final Properties props) throws IllegalArgumentException {
+ return substVars(val, props, new ArrayList<>());
+ }
+
+ private static String substVars(final String val, final Properties props, final List keys)
+ throws IllegalArgumentException {
+ if (val == null) {
+ return null;
+ }
+ final StringBuilder sbuf = new StringBuilder();
+
+ int i = 0;
+ int j;
+ int k;
+
+ while (true) {
+ j = val.indexOf(DELIM_START, i);
+ if (j == -1) {
+ // no more variables
+ if (i == 0) { // this is a simple string
+ return val;
+ }
+ // add the tail string which contails no variables and return the result.
+ sbuf.append(val.substring(i));
+ return sbuf.toString();
+ }
+ sbuf.append(val.substring(i, j));
+ k = val.indexOf(DELIM_STOP, j);
+ if (k == -1) {
+ throw new IllegalArgumentException(Strings.dquote(val)
+ + " has no closing brace. Opening brace at position " + j
+ + '.');
+ }
+ j += DELIM_START_LEN;
+ final String key = val.substring(j, k);
+ // first try in System properties
+ String replacement = PropertiesUtil.getProperties().getStringProperty(key, null);
+ // then try props parameter
+ if (replacement == null && props != null) {
+ replacement = props.getProperty(key);
+ }
+
+ if (replacement != null) {
+
+ // Do variable substitution on the replacement string
+ // such that we can solve "Hello ${x2}" as "Hello p1"
+ // the where the properties are
+ // x1=p1
+ // x2=${x1}
+ if (!keys.contains(key)) {
+ final List usedKeys = new ArrayList<>(keys);
+ usedKeys.add(key);
+ final String recursiveReplacement = substVars(replacement, props, usedKeys);
+ sbuf.append(recursiveReplacement);
+ } else {
+ sbuf.append(replacement);
+ }
+
+ }
+ i = k + DELIM_STOP_LEN;
+ }
+ }
+
+ /**
+ * If value is "true", then true is
+ * returned. If value is "false", then
+ * true is returned. Otherwise, default is
+ * returned.
+ *
+ *
Case of value is unimportant.
+ * @param value The value to convert.
+ * @param dEfault The default value.
+ * @return the value of the result.
+ */
+ public static boolean toBoolean(final String value, final boolean dEfault) {
+ if (value == null) {
+ return dEfault;
+ }
+ final String trimmedVal = value.trim();
+ if ("true".equalsIgnoreCase(trimmedVal)) {
+ return true;
+ }
+ if ("false".equalsIgnoreCase(trimmedVal)) {
+ return false;
+ }
+ return dEfault;
+ }
+
+ public static long toFileSize(final String value, final long defaultValue) {
+ if (value == null) {
+ return defaultValue;
+ }
+
+ String s = value.trim().toUpperCase();
+ long multiplier = 1;
+ int index;
+
+ if ((index = s.indexOf("KB")) != -1) {
+ multiplier = 1024;
+ s = s.substring(0, index);
+ } else if ((index = s.indexOf("MB")) != -1) {
+ multiplier = 1024 * 1024;
+ s = s.substring(0, index);
+ } else if ((index = s.indexOf("GB")) != -1) {
+ multiplier = 1024 * 1024 * 1024;
+ s = s.substring(0, index);
+ }
+ if (s != null) {
+ try {
+ return Long.valueOf(s).longValue() * multiplier;
+ } catch (final NumberFormatException e) {
+ LogLog.error("[" + s + "] is not in proper int form.");
+ LogLog.error("[" + value + "] not in expected format.", e);
+ }
+ }
+ return defaultValue;
+ }
+
+ public static int toInt(final String value, final int dEfault) {
+ if (value != null) {
+ final String s = value.trim();
+ try {
+ return Integer.valueOf(s).intValue();
+ } catch (final NumberFormatException e) {
+ LogLog.error("[" + s + "] is not in proper int form.");
+ e.printStackTrace();
+ }
+ }
+ return dEfault;
+ }
+
+ /**
+ * Converts a standard or custom priority level to a Level object.
+ *
+ * If value is of form "level#classname", then the specified class'
+ * toLevel method is called to process the specified level string; if no '#'
+ * character is present, then the default {@link org.apache.log4j.Level} class
+ * is used to process the level value.
+ *
+ *
+ *
+ * As a special case, if the value parameter is equal to the string
+ * "NULL", then the value null will be returned.
+ *
+ *
+ *
+ * As a Log4j 2.x extension, a {@code value}
+ * "level#org.apache.logging.log4j.Level" retrieves the corresponding custom
+ * Log4j 2.x level.
+ *
+ *
+ *
+ * If any error occurs while converting the value to a level, the
+ * defaultValue parameter, which may be null, is
+ * returned.
+ *
+ *
+ *
+ * Case of value is insignificant for the level, but is
+ * significant for the class name part, if present.
+ *
+ *
+ * @param value The value to convert.
+ * @param defaultValue The default value.
+ * @return the value of the result.
+ *
+ * @since 1.1
+ */
+ public static Level toLevel(String value, final Level defaultValue) {
+ if (value == null) {
+ return defaultValue;
+ }
+
+ value = value.trim();
+ final Level cached = LEVELS.get(value);
+ if (cached != null) {
+ return cached;
+ }
+
+ final int hashIndex = value.indexOf('#');
+ if (hashIndex == -1) {
+ if ("NULL".equalsIgnoreCase(value)) {
+ return null;
+ }
+ // no class name specified : use standard Level class
+ final Level standardLevel = Level.toLevel(value, defaultValue);
+ if (standardLevel != null && value.equals(standardLevel.toString())) {
+ LEVELS.putIfAbsent(value, standardLevel);
+ }
+ return standardLevel;
+ }
+
+ final String clazz = value.substring(hashIndex + 1);
+ final String levelName = value.substring(0, hashIndex);
+
+ final Level customLevel = toLevel(clazz, levelName, defaultValue);
+ if (customLevel != null && levelName.equals(customLevel.toString())
+ && clazz.equals(customLevel.getClass().getName())) {
+ LEVELS.putIfAbsent(value, customLevel);
+ }
+ return customLevel;
+ }
+
+ /**
+ * Converts a custom priority level to a Level object.
+ *
+ *
+ * If {@code clazz} has the special value "org.apache.logging.log4j.Level" a
+ * wrapper of the corresponding Log4j 2.x custom level object is returned.
+ *
+ *
+ * @param clazz a custom level class,
+ * @param levelName the name of the level,
+ * @param defaultValue the value to return in case an error occurs,
+ * @return the value of the result.
+ */
+ public static Level toLevel(final String clazz, final String levelName, final Level defaultValue) {
+
+ // This is degenerate case but you never know.
+ if ("NULL".equalsIgnoreCase(levelName)) {
+ return null;
+ }
+
+ LOGGER.debug("toLevel" + ":class=[" + clazz + "]" + ":pri=[" + levelName + "]");
+
+ // Support for levels defined in Log4j2.
+ if (LOG4J2_LEVEL_CLASS.equals(clazz)) {
+ final org.apache.logging.log4j.Level v2Level = org.apache.logging.log4j.Level.getLevel(levelName.toUpperCase());
+ if (v2Level != null) {
+ return new LevelWrapper(v2Level);
+ }
+ return defaultValue;
+ }
+ try {
+ final Class> customLevel = LoaderUtil.loadClass(clazz);
+
+ // get a ref to the specified class' static method
+ // toLevel(String, org.apache.log4j.Level)
+ final Class>[] paramTypes = new Class[] { String.class, org.apache.log4j.Level.class };
+ final java.lang.reflect.Method toLevelMethod =
+ customLevel.getMethod("toLevel", paramTypes);
+
+ // now call the toLevel method, passing level string + default
+ final Object[] params = new Object[]{levelName, defaultValue};
+ final Object o = toLevelMethod.invoke(null, params);
+
+ return (Level) o;
+ } catch (final ClassNotFoundException e) {
+ LOGGER.warn("custom level class [" + clazz + "] not found.");
+ } catch (final NoSuchMethodException e) {
+ LOGGER.warn("custom level class [" + clazz + "]"
+ + " does not have a class function toLevel(String, Level)", e);
+ } catch (final java.lang.reflect.InvocationTargetException e) {
+ if (e.getTargetException() instanceof InterruptedException
+ || e.getTargetException() instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.warn("custom level class [" + clazz + "]"
+ + " could not be instantiated", e);
+ } catch (final ClassCastException e) {
+ LOGGER.warn("class [" + clazz
+ + "] is not a subclass of org.apache.log4j.Level", e);
+ } catch (final IllegalAccessException e) {
+ LOGGER.warn("class [" + clazz +
+ "] cannot be instantiated due to access restrictions", e);
+ } catch (final RuntimeException e) {
+ LOGGER.warn("class [" + clazz + "], level [" + levelName +
+ "] conversion failed.", e);
+ }
+ return defaultValue;
+ }
+
+ /**
+ * OptionConverter is a static class.
+ */
+ private OptionConverter() {
+ }
+
+ private static class LevelWrapper extends Level {
+
+ private static final long serialVersionUID = -7693936267612508528L;
+
+ protected LevelWrapper(org.apache.logging.log4j.Level v2Level) {
+ super(toLog4j1Level(v2Level.intLevel()), v2Level.name(), toSyslogLevel(v2Level.intLevel()), v2Level);
+ }
+
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/PatternConverter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/PatternConverter.java
new file mode 100644
index 00000000000..2b46db7fee6
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/PatternConverter.java
@@ -0,0 +1,111 @@
+/*
+ * 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.spi.LoggingEvent;
+
+/**
+
+
PatternConverter is an abtract class that provides the
+ formatting functionality that derived classes need.
+
+
Conversion specifiers in a conversion patterns are parsed to
+ individual PatternConverters. Each of which is responsible for
+ converting a logging event in a converter specific manner.
+
+ @author James P. Cakalic
+ @author Ceki Gülcü
+
+ @since 0.8.2
+ */
+public abstract class PatternConverter {
+ public PatternConverter next;
+ int min = -1;
+ int max = 0x7FFFFFFF;
+ boolean leftAlign = false;
+
+ protected
+ PatternConverter() { }
+
+ protected
+ PatternConverter(FormattingInfo fi) {
+ min = fi.min;
+ max = fi.max;
+ leftAlign = fi.leftAlign;
+ }
+
+ /**
+ Derived pattern converters must override this method in order to
+ convert conversion specifiers in the correct way.
+ */
+ abstract
+ protected
+ String convert(LoggingEvent event);
+
+ /**
+ A template method for formatting in a converter specific way.
+ */
+ public
+ void format(StringBuffer sbuf, LoggingEvent e) {
+ String s = convert(e);
+
+ if(s == null) {
+ if(0 < min)
+ spacePad(sbuf, min);
+ return;
+ }
+
+ int len = s.length();
+
+ if(len > max)
+ sbuf.append(s.substring(len-max));
+ else if(len < min) {
+ if(leftAlign) {
+ sbuf.append(s);
+ spacePad(sbuf, min-len);
+ }
+ else {
+ spacePad(sbuf, min-len);
+ sbuf.append(s);
+ }
+ }
+ else
+ sbuf.append(s);
+ }
+
+ static String[] SPACES = {" ", " ", " ", " ", //1,2,4,8 spaces
+ " ", // 16 spaces
+ " " }; // 32 spaces
+
+ /**
+ Fast space padding method.
+ */
+ public
+ void spacePad(StringBuffer sbuf, int length) {
+ while(length >= 32) {
+ sbuf.append(SPACES[5]);
+ length -= 32;
+ }
+
+ for(int i = 4; i >= 0; i--) {
+ if((length & (1<
+// Igor E. Poteryaev
+// Reinhard Deschler
+
+/**
+ * Most of the work of the {@link org.apache.log4j.PatternLayout} class is delegated to the PatternParser class.
+ *
+ *
+ * It is this class that parses conversion patterns and creates a chained list of {@link OptionConverter
+ * OptionConverters}.
+ *
+ * @author James P. Cakalic
+ * @author Ceki Gülcü
+ * @author Anders Kristensen
+ *
+ * @since 0.8.2
+ */
+public class PatternParser {
+
+ private static final char ESCAPE_CHAR = '%';
+
+ private static final int LITERAL_STATE = 0;
+ private static final int CONVERTER_STATE = 1;
+ private static final int DOT_STATE = 3;
+ private static final int MIN_STATE = 4;
+ private static final int MAX_STATE = 5;
+
+ static final int FULL_LOCATION_CONVERTER = 1000;
+ static final int METHOD_LOCATION_CONVERTER = 1001;
+ static final int CLASS_LOCATION_CONVERTER = 1002;
+ static final int LINE_LOCATION_CONVERTER = 1003;
+ static final int FILE_LOCATION_CONVERTER = 1004;
+
+ static final int RELATIVE_TIME_CONVERTER = 2000;
+ static final int THREAD_CONVERTER = 2001;
+ static final int LEVEL_CONVERTER = 2002;
+ static final int NDC_CONVERTER = 2003;
+ static final int MESSAGE_CONVERTER = 2004;
+
+ int state;
+ protected StringBuffer currentLiteral = new StringBuffer(32);
+ protected int patternLength;
+ protected int i;
+ PatternConverter head;
+ PatternConverter tail;
+ protected FormattingInfo formattingInfo = new FormattingInfo();
+ protected String pattern;
+
+ public PatternParser(String pattern) {
+ this.pattern = pattern;
+ patternLength = pattern.length();
+ state = LITERAL_STATE;
+ }
+
+ private void addToList(PatternConverter pc) {
+ if (head == null) {
+ head = tail = pc;
+ } else {
+ tail.next = pc;
+ tail = pc;
+ }
+ }
+
+ protected String extractOption() {
+ if ((i < patternLength) && (pattern.charAt(i) == '{')) {
+ int end = pattern.indexOf('}', i);
+ if (end > i) {
+ String r = pattern.substring(i + 1, end);
+ i = end + 1;
+ return r;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * The option is expected to be in decimal and positive. In case of error, zero is returned.
+ */
+ protected int extractPrecisionOption() {
+ String opt = extractOption();
+ int r = 0;
+ if (opt != null) {
+ try {
+ r = Integer.parseInt(opt);
+ if (r <= 0) {
+ LogLog.error("Precision option (" + opt + ") isn't a positive integer.");
+ r = 0;
+ }
+ } catch (NumberFormatException e) {
+ LogLog.error("Category option \"" + opt + "\" not a decimal integer.", e);
+ }
+ }
+ return r;
+ }
+
+ public PatternConverter parse() {
+ char c;
+ i = 0;
+ while (i < patternLength) {
+ c = pattern.charAt(i++);
+ switch (state) {
+ case LITERAL_STATE:
+ // In literal state, the last char is always a literal.
+ if (i == patternLength) {
+ currentLiteral.append(c);
+ continue;
+ }
+ if (c == ESCAPE_CHAR) {
+ // peek at the next char.
+ switch (pattern.charAt(i)) {
+ case ESCAPE_CHAR:
+ currentLiteral.append(c);
+ i++; // move pointer
+ break;
+ case 'n':
+ currentLiteral.append(Layout.LINE_SEP);
+ i++; // move pointer
+ break;
+ default:
+ if (currentLiteral.length() != 0) {
+ addToList(new LiteralPatternConverter(currentLiteral.toString()));
+ // LogLog.debug("Parsed LITERAL converter: \""
+ // +currentLiteral+"\".");
+ }
+ currentLiteral.setLength(0);
+ currentLiteral.append(c); // append %
+ state = CONVERTER_STATE;
+ formattingInfo.reset();
+ }
+ } else {
+ currentLiteral.append(c);
+ }
+ break;
+ case CONVERTER_STATE:
+ currentLiteral.append(c);
+ switch (c) {
+ case '-':
+ formattingInfo.leftAlign = true;
+ break;
+ case '.':
+ state = DOT_STATE;
+ break;
+ default:
+ if (c >= '0' && c <= '9') {
+ formattingInfo.min = c - '0';
+ state = MIN_STATE;
+ } else
+ finalizeConverter(c);
+ } // switch
+ break;
+ case MIN_STATE:
+ currentLiteral.append(c);
+ if (c >= '0' && c <= '9')
+ formattingInfo.min = formattingInfo.min * 10 + (c - '0');
+ else if (c == '.')
+ state = DOT_STATE;
+ else {
+ finalizeConverter(c);
+ }
+ break;
+ case DOT_STATE:
+ currentLiteral.append(c);
+ if (c >= '0' && c <= '9') {
+ formattingInfo.max = c - '0';
+ state = MAX_STATE;
+ } else {
+ LogLog.error("Error occured in position " + i + ".\n Was expecting digit, instead got char \"" + c + "\".");
+ state = LITERAL_STATE;
+ }
+ break;
+ case MAX_STATE:
+ currentLiteral.append(c);
+ if (c >= '0' && c <= '9')
+ formattingInfo.max = formattingInfo.max * 10 + (c - '0');
+ else {
+ finalizeConverter(c);
+ state = LITERAL_STATE;
+ }
+ break;
+ } // switch
+ } // while
+ if (currentLiteral.length() != 0) {
+ addToList(new LiteralPatternConverter(currentLiteral.toString()));
+ // LogLog.debug("Parsed LITERAL converter: \""+currentLiteral+"\".");
+ }
+ return head;
+ }
+
+ protected void finalizeConverter(char c) {
+ PatternConverter pc = null;
+ switch (c) {
+ case 'c':
+ pc = new CategoryPatternConverter(formattingInfo, extractPrecisionOption());
+ // LogLog.debug("CATEGORY converter.");
+ // formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'C':
+ pc = new ClassNamePatternConverter(formattingInfo, extractPrecisionOption());
+ // LogLog.debug("CLASS_NAME converter.");
+ // formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'd':
+ String dateFormatStr = AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT;
+ DateFormat df;
+ String dOpt = extractOption();
+ if (dOpt != null)
+ dateFormatStr = dOpt;
+
+ if (dateFormatStr.equalsIgnoreCase(AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT))
+ df = new ISO8601DateFormat();
+ else if (dateFormatStr.equalsIgnoreCase(AbsoluteTimeDateFormat.ABS_TIME_DATE_FORMAT))
+ df = new AbsoluteTimeDateFormat();
+ else if (dateFormatStr.equalsIgnoreCase(AbsoluteTimeDateFormat.DATE_AND_TIME_DATE_FORMAT))
+ df = new DateTimeDateFormat();
+ else {
+ try {
+ df = new SimpleDateFormat(dateFormatStr);
+ } catch (IllegalArgumentException e) {
+ LogLog.error("Could not instantiate SimpleDateFormat with " + dateFormatStr, e);
+ df = (DateFormat) OptionConverter.instantiateByClassName("org.apache.log4j.helpers.ISO8601DateFormat", DateFormat.class, null);
+ }
+ }
+ pc = new DatePatternConverter(formattingInfo, df);
+ // LogLog.debug("DATE converter {"+dateFormatStr+"}.");
+ // formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'F':
+ pc = new LocationPatternConverter(formattingInfo, FILE_LOCATION_CONVERTER);
+ // LogLog.debug("File name converter.");
+ // formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'l':
+ pc = new LocationPatternConverter(formattingInfo, FULL_LOCATION_CONVERTER);
+ // LogLog.debug("Location converter.");
+ // formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'L':
+ pc = new LocationPatternConverter(formattingInfo, LINE_LOCATION_CONVERTER);
+ // LogLog.debug("LINE NUMBER converter.");
+ // formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'm':
+ pc = new BasicPatternConverter(formattingInfo, MESSAGE_CONVERTER);
+ // LogLog.debug("MESSAGE converter.");
+ // formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'M':
+ pc = new LocationPatternConverter(formattingInfo, METHOD_LOCATION_CONVERTER);
+ // LogLog.debug("METHOD converter.");
+ // formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'p':
+ pc = new BasicPatternConverter(formattingInfo, LEVEL_CONVERTER);
+ // LogLog.debug("LEVEL converter.");
+ // formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'r':
+ pc = new BasicPatternConverter(formattingInfo, RELATIVE_TIME_CONVERTER);
+ // LogLog.debug("RELATIVE time converter.");
+ // formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 't':
+ pc = new BasicPatternConverter(formattingInfo, THREAD_CONVERTER);
+ // LogLog.debug("THREAD converter.");
+ // formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ /*
+ * case 'u': if(i < patternLength) { char cNext = pattern.charAt(i); if(cNext >= '0' && cNext <= '9') { pc = new
+ * UserFieldPatternConverter(formattingInfo, cNext - '0'); LogLog.debug("USER converter ["+cNext+"].");
+ * formattingInfo.dump(); currentLiteral.setLength(0); i++; } else LogLog.error("Unexpected char"
+ * +cNext+" at position "+i); } break;
+ */
+ case 'x':
+ pc = new BasicPatternConverter(formattingInfo, NDC_CONVERTER);
+ // LogLog.debug("NDC converter.");
+ currentLiteral.setLength(0);
+ break;
+ case 'X':
+ String xOpt = extractOption();
+ pc = new MDCPatternConverter(formattingInfo, xOpt);
+ currentLiteral.setLength(0);
+ break;
+ default:
+ LogLog.error("Unexpected char [" + c + "] at position " + i + " in conversion patterrn.");
+ pc = new LiteralPatternConverter(currentLiteral.toString());
+ currentLiteral.setLength(0);
+ }
+
+ addConverter(pc);
+ }
+
+ protected void addConverter(PatternConverter pc) {
+ currentLiteral.setLength(0);
+ // Add the pattern converter to the list.
+ addToList(pc);
+ // Next pattern is assumed to be a literal.
+ state = LITERAL_STATE;
+ // Reset formatting info
+ formattingInfo.reset();
+ }
+
+ // ---------------------------------------------------------------------
+ // PatternConverters
+ // ---------------------------------------------------------------------
+
+ private static class BasicPatternConverter extends PatternConverter {
+ int type;
+
+ BasicPatternConverter(FormattingInfo formattingInfo, int type) {
+ super(formattingInfo);
+ this.type = type;
+ }
+
+ public String convert(LoggingEvent event) {
+ switch (type) {
+ case RELATIVE_TIME_CONVERTER:
+ return (Long.toString(event.timeStamp - LoggingEvent.getStartTime()));
+ case THREAD_CONVERTER:
+ return event.getThreadName();
+ case LEVEL_CONVERTER:
+ return event.getLevel().toString();
+ case NDC_CONVERTER:
+ return event.getNDC();
+ case MESSAGE_CONVERTER: {
+ return event.getRenderedMessage();
+ }
+ default:
+ return null;
+ }
+ }
+ }
+
+ private static class LiteralPatternConverter extends PatternConverter {
+ private String literal;
+
+ LiteralPatternConverter(String value) {
+ literal = value;
+ }
+
+ public final void format(StringBuffer sbuf, LoggingEvent event) {
+ sbuf.append(literal);
+ }
+
+ public String convert(LoggingEvent event) {
+ return literal;
+ }
+ }
+
+ private static class DatePatternConverter extends PatternConverter {
+ private DateFormat df;
+ private Date date;
+
+ DatePatternConverter(FormattingInfo formattingInfo, DateFormat df) {
+ super(formattingInfo);
+ date = new Date();
+ this.df = df;
+ }
+
+ public String convert(LoggingEvent event) {
+ date.setTime(event.timeStamp);
+ String converted = null;
+ try {
+ converted = df.format(date);
+ } catch (Exception ex) {
+ LogLog.error("Error occured while converting date.", ex);
+ }
+ return converted;
+ }
+ }
+
+ private static class MDCPatternConverter extends PatternConverter {
+ private String key;
+
+ MDCPatternConverter(FormattingInfo formattingInfo, String key) {
+ super(formattingInfo);
+ this.key = key;
+ }
+
+ public String convert(LoggingEvent event) {
+ if (key == null) {
+ StringBuffer buf = new StringBuffer("{");
+ Map properties = event.getProperties();
+ if (properties.size() > 0) {
+ Object[] keys = properties.keySet().toArray();
+ Arrays.sort(keys);
+ for (int i = 0; i < keys.length; i++) {
+ buf.append('{');
+ buf.append(keys[i]);
+ buf.append(',');
+ buf.append(properties.get(keys[i]));
+ buf.append('}');
+ }
+ }
+ buf.append('}');
+ return buf.toString();
+ } else {
+ Object val = event.getMDC(key);
+ if (val == null) {
+ return null;
+ } else {
+ return val.toString();
+ }
+ }
+ }
+ }
+
+ private class LocationPatternConverter extends PatternConverter {
+ int type;
+
+ LocationPatternConverter(FormattingInfo formattingInfo, int type) {
+ super(formattingInfo);
+ this.type = type;
+ }
+
+ public String convert(LoggingEvent event) {
+ LocationInfo locationInfo = event.getLocationInformation();
+ switch (type) {
+ case FULL_LOCATION_CONVERTER:
+ return locationInfo.fullInfo;
+ case METHOD_LOCATION_CONVERTER:
+ return locationInfo.getMethodName();
+ case LINE_LOCATION_CONVERTER:
+ return locationInfo.getLineNumber();
+ case FILE_LOCATION_CONVERTER:
+ return locationInfo.getFileName();
+ default:
+ return null;
+ }
+ }
+ }
+
+ private static abstract class NamedPatternConverter extends PatternConverter {
+ int precision;
+
+ NamedPatternConverter(FormattingInfo formattingInfo, int precision) {
+ super(formattingInfo);
+ this.precision = precision;
+ }
+
+ abstract String getFullyQualifiedName(LoggingEvent event);
+
+ public String convert(LoggingEvent event) {
+ String n = getFullyQualifiedName(event);
+ if (precision <= 0)
+ return n;
+ else {
+ int len = n.length();
+
+ // We substract 1 from 'len' when assigning to 'end' to avoid out of
+ // bounds exception in return r.substring(end+1, len). This can happen if
+ // precision is 1 and the category name ends with a dot.
+ int end = len - 1;
+ for (int i = precision; i > 0; i--) {
+ end = n.lastIndexOf('.', end - 1);
+ if (end == -1)
+ return n;
+ }
+ return n.substring(end + 1, len);
+ }
+ }
+ }
+
+ private class ClassNamePatternConverter extends NamedPatternConverter {
+
+ ClassNamePatternConverter(FormattingInfo formattingInfo, int precision) {
+ super(formattingInfo, precision);
+ }
+
+ String getFullyQualifiedName(LoggingEvent event) {
+ return event.getLocationInformation().getClassName();
+ }
+ }
+
+ private class CategoryPatternConverter extends NamedPatternConverter {
+
+ CategoryPatternConverter(FormattingInfo formattingInfo, int precision) {
+ super(formattingInfo, precision);
+ }
+
+ String getFullyQualifiedName(LoggingEvent event) {
+ return event.getLoggerName();
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/QuietWriter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/QuietWriter.java
new file mode 100644
index 00000000000..42120d97774
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/QuietWriter.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 java.io.FilterWriter;
+import java.io.Writer;
+
+import org.apache.log4j.spi.ErrorCode;
+import org.apache.log4j.spi.ErrorHandler;
+
+
+/**
+ * QuietWriter does not throw exceptions when things go
+ * wrong. Instead, it delegates error handling to its {@link ErrorHandler}.
+ */
+public class QuietWriter extends FilterWriter {
+
+ protected ErrorHandler errorHandler;
+
+ public QuietWriter(Writer writer, ErrorHandler errorHandler) {
+ super(writer);
+ setErrorHandler(errorHandler);
+ }
+
+ @Override
+ public void write(String string) {
+ if (string != null) {
+ try {
+ out.write(string);
+ } catch (Exception e) {
+ errorHandler.error("Failed to write [" + string + "].", e,
+ ErrorCode.WRITE_FAILURE);
+ }
+ }
+ }
+
+ @Override
+ public void flush() {
+ try {
+ out.flush();
+ } catch (Exception e) {
+ errorHandler.error("Failed to flush writer,", e,
+ ErrorCode.FLUSH_FAILURE);
+ }
+ }
+
+
+ public void setErrorHandler(ErrorHandler eh) {
+ if (eh == null) {
+ // This is a programming error on the part of the enclosing appender.
+ throw new IllegalArgumentException("Attempted to set null ErrorHandler.");
+ }
+ this.errorHandler = eh;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/RelativeTimeDateFormat.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/RelativeTimeDateFormat.java
new file mode 100644
index 00000000000..a3fbc211d34
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/RelativeTimeDateFormat.java
@@ -0,0 +1,59 @@
+/*
+ * 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 java.text.DateFormat;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.util.Date;
+
+/**
+ * Formats a {@link Date} by printing the number of milliseconds elapsed since construction of the format. This is the
+ * fastest printing DateFormat in the package.
+ *
+ * @since 0.7.5
+ */
+public class RelativeTimeDateFormat extends DateFormat {
+
+ private static final long serialVersionUID = 7055751607085611984L;
+
+ protected final long startTime;
+
+ public RelativeTimeDateFormat() {
+ this.startTime = System.currentTimeMillis();
+ }
+
+ /**
+ * Appends to sbuf the number of milliseconds elapsed since the start of the application.
+ *
+ * @since 0.7.5
+ */
+ @Override
+ public StringBuffer format(final Date date, final StringBuffer sbuf, final FieldPosition fieldPosition) {
+ // System.err.println(":"+ date.getTime() + " - " + startTime);
+ return sbuf.append((date.getTime() - startTime));
+ }
+
+ /**
+ * Always returns null.
+ */
+ @Override
+ public Date parse(final String s, final ParsePosition pos) {
+ return null;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/UtilLoggingLevel.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/UtilLoggingLevel.java
new file mode 100644
index 00000000000..8c1c92eee6a
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/UtilLoggingLevel.java
@@ -0,0 +1,236 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Level;
+
+/**
+ * An extension of the Level class that provides support for java.util.logging Levels.
+ */
+public class UtilLoggingLevel extends Level {
+
+ /**
+ * Serialization version id.
+ */
+ private static final long serialVersionUID = 909301162611820211L;
+
+ /**
+ * Numerical value for SEVERE.
+ */
+ public static final int SEVERE_INT = 22000;
+ /**
+ * Numerical value for WARNING.
+ */
+ public static final int WARNING_INT = 21000;
+
+ // INFO level defined in parent as 20000..no need to redefine here
+
+ /**
+ * Numerical value for CONFIG.
+ */
+ public static final int CONFIG_INT = 14000;
+
+ /**
+ * Numerical value for FINE.
+ */
+ public static final int FINE_INT = 13000;
+
+ /**
+ * Numerical value for FINER.
+ */
+ public static final int FINER_INT = 12000;
+
+ /**
+ * Numerical value for FINEST.
+ */
+ public static final int FINEST_INT = 11000;
+
+ /**
+ * Numerical value for UNKNOWN.
+ */
+ public static final int UNKNOWN_INT = 10000;
+
+ /**
+ * SEVERE.
+ */
+ public static final UtilLoggingLevel SEVERE = new UtilLoggingLevel(SEVERE_INT, "SEVERE", 0);
+
+ /**
+ * WARNING.
+ */
+ public static final UtilLoggingLevel WARNING = new UtilLoggingLevel(WARNING_INT, "WARNING", 4);
+
+ /**
+ * INFO.
+ */
+ // note: we've aligned the int values of the java.util.logging INFO level with log4j's level
+ public static final UtilLoggingLevel INFO = new UtilLoggingLevel(INFO_INT, "INFO", 5);
+
+ /**
+ * CONFIG.
+ */
+ public static final UtilLoggingLevel CONFIG = new UtilLoggingLevel(CONFIG_INT, "CONFIG", 6);
+
+ /**
+ * FINE.
+ */
+ public static final UtilLoggingLevel FINE = new UtilLoggingLevel(FINE_INT, "FINE", 7);
+
+ /**
+ * FINER.
+ */
+ public static final UtilLoggingLevel FINER = new UtilLoggingLevel(FINER_INT, "FINER", 8);
+
+ /**
+ * FINEST.
+ */
+ public static final UtilLoggingLevel FINEST = new UtilLoggingLevel(FINEST_INT, "FINEST", 9);
+
+ /**
+ * Create new instance.
+ *
+ * @param level numeric value for level.
+ * @param levelStr symbolic name for level.
+ * @param syslogEquivalent Equivalent syslog severity.
+ */
+ protected UtilLoggingLevel(final int level, final String levelStr, final int syslogEquivalent) {
+ super(level, levelStr, syslogEquivalent);
+ }
+
+ /**
+ * Convert an integer passed as argument to a level. If the conversion fails, then this method returns the specified
+ * default.
+ *
+ * @param val numeric value.
+ * @param defaultLevel level to be returned if no level matches numeric value.
+ * @return matching level or default level.
+ */
+ public static UtilLoggingLevel toLevel(final int val, final UtilLoggingLevel defaultLevel) {
+ switch (val) {
+ case SEVERE_INT:
+ return SEVERE;
+
+ case WARNING_INT:
+ return WARNING;
+
+ case INFO_INT:
+ return INFO;
+
+ case CONFIG_INT:
+ return CONFIG;
+
+ case FINE_INT:
+ return FINE;
+
+ case FINER_INT:
+ return FINER;
+
+ case FINEST_INT:
+ return FINEST;
+
+ default:
+ return defaultLevel;
+ }
+ }
+
+ /**
+ * Gets level matching numeric value.
+ *
+ * @param val numeric value.
+ * @return matching level or UtilLoggerLevel.FINEST if no match.
+ */
+ public static Level toLevel(final int val) {
+ return toLevel(val, FINEST);
+ }
+
+ /**
+ * Gets list of supported levels.
+ *
+ * @return list of supported levels.
+ */
+ public static List getAllPossibleLevels() {
+ final ArrayList list = new ArrayList<>();
+ list.add(FINE);
+ list.add(FINER);
+ list.add(FINEST);
+ list.add(INFO);
+ list.add(CONFIG);
+ list.add(WARNING);
+ list.add(SEVERE);
+ return list;
+ }
+
+ /**
+ * Get level with specified symbolic name.
+ *
+ * @param s symbolic name.
+ * @return matching level or Level.DEBUG if no match.
+ */
+ public static Level toLevel(final String s) {
+ return toLevel(s, Level.DEBUG);
+ }
+
+ /**
+ * Get level with specified symbolic name.
+ *
+ * @param sArg symbolic name.
+ * @param defaultLevel level to return if no match.
+ * @return matching level or defaultLevel if no match.
+ */
+ public static Level toLevel(final String sArg, final Level defaultLevel) {
+ if (sArg == null) {
+ return defaultLevel;
+ }
+
+ final String s = sArg.toUpperCase();
+
+ if (s.equals("SEVERE")) {
+ return SEVERE;
+ }
+
+ // if(s.equals("FINE")) return Level.FINE;
+ if (s.equals("WARNING")) {
+ return WARNING;
+ }
+
+ if (s.equals("INFO")) {
+ return INFO;
+ }
+
+ if (s.equals("CONFIG")) {
+ return CONFIG;
+ }
+
+ if (s.equals("FINE")) {
+ return FINE;
+ }
+
+ if (s.equals("FINER")) {
+ return FINER;
+ }
+
+ if (s.equals("FINEST")) {
+ return FINEST;
+ }
+ return defaultLevel;
+ }
+
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/AbstractDynamicMBean.java b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/AbstractDynamicMBean.java
new file mode 100644
index 00000000000..f4543fd60ed
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/AbstractDynamicMBean.java
@@ -0,0 +1,177 @@
+/*
+ * 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.jmx;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.DynamicMBean;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.JMException;
+import javax.management.MBeanRegistration;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.NotCompliantMBeanException;
+import javax.management.ObjectName;
+import javax.management.RuntimeOperationsException;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Logger;
+
+public abstract class AbstractDynamicMBean implements DynamicMBean, MBeanRegistration {
+
+ /**
+ * Get MBean name.
+ *
+ * @param appender appender, may not be null.
+ * @return name.
+ * @since 1.2.16
+ */
+ static protected String getAppenderName(final Appender appender) {
+ String name = appender.getName();
+ if (name == null || name.trim().length() == 0) {
+ // try to get some form of a name, because null is not allowed (exception), and empty string certainly isn't useful in
+ // JMX..
+ name = appender.toString();
+ }
+ return name;
+ }
+ String dClassName;
+ MBeanServer server;
+
+ private final Vector mbeanList = new Vector();
+
+ /**
+ * Enables the to get the values of several attributes of the Dynamic MBean.
+ */
+ @Override
+ public AttributeList getAttributes(final String[] attributeNames) {
+
+ // Check attributeNames is not null to avoid NullPointerException later on
+ if (attributeNames == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException("attributeNames[] cannot be null"), "Cannot invoke a getter of " + dClassName);
+ }
+
+ final AttributeList resultList = new AttributeList();
+
+ // if attributeNames is empty, return an empty result list
+ if (attributeNames.length == 0) {
+ return resultList;
+ }
+
+ // build the result attribute list
+ for (final String attributeName : attributeNames) {
+ try {
+ final Object value = getAttribute((String) attributeName);
+ resultList.add(new Attribute(attributeName, value));
+ } catch (final JMException e) {
+ e.printStackTrace();
+ } catch (final RuntimeException e) {
+ e.printStackTrace();
+ }
+ }
+ return (resultList);
+ }
+
+ protected abstract Logger getLogger();
+
+ @Override
+ public void postDeregister() {
+ getLogger().debug("postDeregister is called.");
+ }
+
+ @Override
+ public void postRegister(final java.lang.Boolean registrationDone) {
+ }
+
+ /**
+ * Performs cleanup for deregistering this MBean. Default implementation unregisters MBean instances which are
+ * registered using {@link #registerMBean(Object mbean, ObjectName objectName)}.
+ */
+ @Override
+ public void preDeregister() {
+ getLogger().debug("preDeregister called.");
+
+ final Enumeration iterator = mbeanList.elements();
+ while (iterator.hasMoreElements()) {
+ final ObjectName name = (ObjectName) iterator.nextElement();
+ try {
+ server.unregisterMBean(name);
+ } catch (final InstanceNotFoundException e) {
+ getLogger().warn("Missing MBean " + name.getCanonicalName());
+ } catch (final MBeanRegistrationException e) {
+ getLogger().warn("Failed unregistering " + name.getCanonicalName());
+ }
+ }
+ }
+
+ @Override
+ public ObjectName preRegister(final MBeanServer server, final ObjectName name) {
+ getLogger().debug("preRegister called. Server=" + server + ", name=" + name);
+ this.server = server;
+ return name;
+ }
+
+ /**
+ * Registers MBean instance in the attached server. Must NOT be called before registration of this instance.
+ */
+ protected void registerMBean(final Object mbean, final ObjectName objectName)
+ throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {
+ server.registerMBean(mbean, objectName);
+ mbeanList.add(objectName);
+ }
+
+ /**
+ * Sets the values of several attributes of the Dynamic MBean, and returns the list of attributes that have been set.
+ */
+ @Override
+ public AttributeList setAttributes(final AttributeList attributes) {
+
+ // Check attributes is not null to avoid NullPointerException later on
+ if (attributes == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException("AttributeList attributes cannot be null"),
+ "Cannot invoke a setter of " + dClassName);
+ }
+ final AttributeList resultList = new AttributeList();
+
+ // if attributeNames is empty, nothing more to do
+ if (attributes.isEmpty()) {
+ return resultList;
+ }
+
+ // for each attribute, try to set it and add to the result list if successfull
+ for (final Object attribute : attributes) {
+ final Attribute attr = (Attribute) attribute;
+ try {
+ setAttribute(attr);
+ final String name = attr.getName();
+ final Object value = getAttribute(name);
+ resultList.add(new Attribute(name, value));
+ } catch (final JMException e) {
+ e.printStackTrace();
+ } catch (final RuntimeException e) {
+ e.printStackTrace();
+ }
+ }
+ return (resultList);
+ }
+
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/Agent.java b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/Agent.java
new file mode 100644
index 00000000000..e1cd3586fa2
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/Agent.java
@@ -0,0 +1,131 @@
+/*
+ * 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.jmx;
+
+import java.io.InterruptedIOException;
+import java.lang.reflect.InvocationTargetException;
+
+import javax.management.JMException;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.ObjectName;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Manages an instance of com.sun.jdmk.comm.HtmlAdapterServer which was provided for demonstration purposes in the Java
+ * Management Extensions Reference Implementation 1.2.1. This class is provided to maintain compatibility with earlier
+ * versions of log4j and use in new code is discouraged.
+ *
+ * @deprecated
+ */
+@Deprecated
+public class Agent {
+
+ /**
+ * Diagnostic logger.
+ *
+ * @deprecated
+ */
+ @Deprecated
+ static Logger log = Logger.getLogger(Agent.class);
+
+ /**
+ * Creates a new instance of com.sun.jdmk.comm.HtmlAdapterServer using reflection.
+ *
+ * @since 1.2.16
+ * @return new instance.
+ */
+ private static Object createServer() {
+ Object newInstance = null;
+ try {
+ newInstance = Class.forName("com.sun.jdmk.comm.HtmlAdapterServer").newInstance();
+ } catch (final ClassNotFoundException ex) {
+ throw new RuntimeException(ex.toString());
+ } catch (final InstantiationException ex) {
+ throw new RuntimeException(ex.toString());
+ } catch (final IllegalAccessException ex) {
+ throw new RuntimeException(ex.toString());
+ }
+ return newInstance;
+ }
+
+ /**
+ * Invokes HtmlAdapterServer.start() using reflection.
+ *
+ * @since 1.2.16
+ * @param server instance of com.sun.jdmk.comm.HtmlAdapterServer.
+ */
+ private static void startServer(final Object server) {
+ try {
+ server.getClass().getMethod("start", new Class[0]).invoke(server, new Object[0]);
+ } catch (final InvocationTargetException ex) {
+ final Throwable cause = ex.getTargetException();
+ if (cause instanceof RuntimeException) {
+ throw (RuntimeException) cause;
+ } else if (cause != null) {
+ if (cause instanceof InterruptedException || cause instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ throw new RuntimeException(cause.toString());
+ } else {
+ throw new RuntimeException();
+ }
+ } catch (final NoSuchMethodException ex) {
+ throw new RuntimeException(ex.toString());
+ } catch (final IllegalAccessException ex) {
+ throw new RuntimeException(ex.toString());
+ }
+ }
+
+ /**
+ * Create new instance.
+ *
+ * @deprecated
+ */
+ @Deprecated
+ public Agent() {
+ }
+
+ /**
+ * Starts instance of HtmlAdapterServer.
+ *
+ * @deprecated
+ */
+ @Deprecated
+ public void start() {
+
+ final MBeanServer server = MBeanServerFactory.createMBeanServer();
+ final Object html = createServer();
+
+ try {
+ log.info("Registering HtmlAdaptorServer instance.");
+ server.registerMBean(html, new ObjectName("Adaptor:name=html,port=8082"));
+ log.info("Registering HierarchyDynamicMBean instance.");
+ final HierarchyDynamicMBean hdm = new HierarchyDynamicMBean();
+ server.registerMBean(hdm, new ObjectName("log4j:hiearchy=default"));
+ } catch (final JMException e) {
+ log.error("Problem while registering MBeans instances.", e);
+ return;
+ } catch (final RuntimeException e) {
+ log.error("Problem while registering MBeans instances.", e);
+ return;
+ }
+ startServer(html);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/AppenderDynamicMBean.java b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/AppenderDynamicMBean.java
new file mode 100644
index 00000000000..6374059fd14
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/AppenderDynamicMBean.java
@@ -0,0 +1,280 @@
+/*
+ * 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.jmx;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.InterruptedIOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import javax.management.Attribute;
+import javax.management.AttributeNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanConstructorInfo;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.management.RuntimeOperationsException;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.Priority;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.OptionHandler;
+
+public class AppenderDynamicMBean extends AbstractDynamicMBean {
+
+ // This category instance is for logging.
+ private static Logger cat = Logger.getLogger(AppenderDynamicMBean.class);
+ private final MBeanConstructorInfo[] dConstructors = new MBeanConstructorInfo[1];
+ private final Vector dAttributes = new Vector();
+
+ private final String dClassName = this.getClass().getName();
+ private final Hashtable dynamicProps = new Hashtable(5);
+ private final MBeanOperationInfo[] dOperations = new MBeanOperationInfo[2];
+
+ private final String dDescription = "This MBean acts as a management facade for log4j appenders.";
+
+ // We wrap this appender instance.
+ private final Appender appender;
+
+ public AppenderDynamicMBean(final Appender appender) throws IntrospectionException {
+ this.appender = appender;
+ buildDynamicMBeanInfo();
+ }
+
+ private void buildDynamicMBeanInfo() throws IntrospectionException {
+ final Constructor[] constructors = this.getClass().getConstructors();
+ dConstructors[0] = new MBeanConstructorInfo("AppenderDynamicMBean(): Constructs a AppenderDynamicMBean instance", constructors[0]);
+
+ final BeanInfo bi = Introspector.getBeanInfo(appender.getClass());
+ final PropertyDescriptor[] pd = bi.getPropertyDescriptors();
+
+ final int size = pd.length;
+
+ for (int i = 0; i < size; i++) {
+ final String name = pd[i].getName();
+ final Method readMethod = pd[i].getReadMethod();
+ final Method writeMethod = pd[i].getWriteMethod();
+ if (readMethod != null) {
+ final Class returnClass = readMethod.getReturnType();
+ if (isSupportedType(returnClass)) {
+ String returnClassName;
+ if (returnClass.isAssignableFrom(Priority.class)) {
+ returnClassName = "java.lang.String";
+ } else {
+ returnClassName = returnClass.getName();
+ }
+
+ dAttributes.add(new MBeanAttributeInfo(name, returnClassName, "Dynamic", true, writeMethod != null, false));
+ dynamicProps.put(name, new MethodUnion(readMethod, writeMethod));
+ }
+ }
+ }
+
+ MBeanParameterInfo[] params = new MBeanParameterInfo[0];
+
+ dOperations[0] = new MBeanOperationInfo("activateOptions", "activateOptions(): add an appender", params, "void", MBeanOperationInfo.ACTION);
+
+ params = new MBeanParameterInfo[1];
+ params[0] = new MBeanParameterInfo("layout class", "java.lang.String", "layout class");
+
+ dOperations[1] = new MBeanOperationInfo("setLayout", "setLayout(): add a layout", params, "void", MBeanOperationInfo.ACTION);
+ }
+
+ @Override
+ public Object getAttribute(final String attributeName) throws AttributeNotFoundException, MBeanException, ReflectionException {
+
+ // Check attributeName is not null to avoid NullPointerException later on
+ if (attributeName == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Attribute name cannot be null"),
+ "Cannot invoke a getter of " + dClassName + " with null attribute name");
+ }
+
+ cat.debug("getAttribute called with [" + attributeName + "].");
+ if (attributeName.startsWith("appender=" + appender.getName() + ",layout")) {
+ try {
+ return new ObjectName("log4j:" + attributeName);
+ } catch (final MalformedObjectNameException e) {
+ cat.error("attributeName", e);
+ } catch (final RuntimeException e) {
+ cat.error("attributeName", e);
+ }
+ }
+
+ final MethodUnion mu = (MethodUnion) dynamicProps.get(attributeName);
+
+ // cat.debug("----name="+attributeName+", b="+b);
+
+ if (mu != null && mu.readMethod != null) {
+ try {
+ return mu.readMethod.invoke(appender, null);
+ } catch (final IllegalAccessException e) {
+ return null;
+ } catch (final InvocationTargetException e) {
+ if (e.getTargetException() instanceof InterruptedException || e.getTargetException() instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ return null;
+ } catch (final RuntimeException e) {
+ return null;
+ }
+ }
+
+ // If attributeName has not been recognized throw an AttributeNotFoundException
+ throw (new AttributeNotFoundException("Cannot find " + attributeName + " attribute in " + dClassName));
+
+ }
+
+ @Override
+ protected Logger getLogger() {
+ return cat;
+ }
+
+ @Override
+ public MBeanInfo getMBeanInfo() {
+ cat.debug("getMBeanInfo called.");
+
+ final MBeanAttributeInfo[] attribs = new MBeanAttributeInfo[dAttributes.size()];
+ dAttributes.toArray(attribs);
+
+ return new MBeanInfo(dClassName, dDescription, attribs, dConstructors, dOperations, new MBeanNotificationInfo[0]);
+ }
+
+ @Override
+ public Object invoke(final String operationName, final Object params[], final String signature[]) throws MBeanException, ReflectionException {
+
+ if (operationName.equals("activateOptions") && appender instanceof OptionHandler) {
+ final OptionHandler oh = (OptionHandler) appender;
+ oh.activateOptions();
+ return "Options activated.";
+ } else if (operationName.equals("setLayout")) {
+ final Layout layout = (Layout) OptionConverter.instantiateByClassName((String) params[0], Layout.class, null);
+ appender.setLayout(layout);
+ registerLayoutMBean(layout);
+ }
+ return null;
+ }
+
+ private boolean isSupportedType(final Class clazz) {
+ if (clazz.isPrimitive() || (clazz == String.class) || clazz.isAssignableFrom(Priority.class)) {
+ return true;
+ }
+
+ return false;
+
+ }
+
+ @Override
+ public ObjectName preRegister(final MBeanServer server, final ObjectName name) {
+ cat.debug("preRegister called. Server=" + server + ", name=" + name);
+ this.server = server;
+ registerLayoutMBean(appender.getLayout());
+
+ return name;
+ }
+
+ void registerLayoutMBean(final Layout layout) {
+ if (layout == null) {
+ return;
+ }
+
+ final String name = getAppenderName(appender) + ",layout=" + layout.getClass().getName();
+ cat.debug("Adding LayoutMBean:" + name);
+ ObjectName objectName = null;
+ try {
+ final LayoutDynamicMBean appenderMBean = new LayoutDynamicMBean(layout);
+ objectName = new ObjectName("log4j:appender=" + name);
+ if (!server.isRegistered(objectName)) {
+ registerMBean(appenderMBean, objectName);
+ dAttributes.add(new MBeanAttributeInfo("appender=" + name, "javax.management.ObjectName", "The " + name + " layout.", true, true, false));
+ }
+
+ } catch (final JMException e) {
+ cat.error("Could not add DynamicLayoutMBean for [" + name + "].", e);
+ } catch (final java.beans.IntrospectionException e) {
+ cat.error("Could not add DynamicLayoutMBean for [" + name + "].", e);
+ } catch (final RuntimeException e) {
+ cat.error("Could not add DynamicLayoutMBean for [" + name + "].", e);
+ }
+ }
+
+ @Override
+ public void setAttribute(final Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException {
+
+ // Check attribute is not null to avoid NullPointerException later on
+ if (attribute == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Attribute cannot be null"),
+ "Cannot invoke a setter of " + dClassName + " with null attribute");
+ }
+ final String name = attribute.getName();
+ Object value = attribute.getValue();
+
+ if (name == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Attribute name cannot be null"),
+ "Cannot invoke the setter of " + dClassName + " with null attribute name");
+ }
+
+ final MethodUnion mu = (MethodUnion) dynamicProps.get(name);
+
+ if (mu != null && mu.writeMethod != null) {
+ final Object[] o = new Object[1];
+
+ final Class[] params = mu.writeMethod.getParameterTypes();
+ if (params[0] == org.apache.log4j.Priority.class) {
+ value = OptionConverter.toLevel((String) value, (Level) getAttribute(name));
+ }
+ o[0] = value;
+
+ try {
+ mu.writeMethod.invoke(appender, o);
+
+ } catch (final InvocationTargetException e) {
+ if (e.getTargetException() instanceof InterruptedException || e.getTargetException() instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ cat.error("FIXME", e);
+ } catch (final IllegalAccessException e) {
+ cat.error("FIXME", e);
+ } catch (final RuntimeException e) {
+ cat.error("FIXME", e);
+ }
+ } else if (name.endsWith(".layout")) {
+
+ } else {
+ throw (new AttributeNotFoundException("Attribute " + name + " not found in " + this.getClass().getName()));
+ }
+ }
+
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/HierarchyDynamicMBean.java b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/HierarchyDynamicMBean.java
new file mode 100644
index 00000000000..b29ef8bd648
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/HierarchyDynamicMBean.java
@@ -0,0 +1,252 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.jmx;
+
+import java.lang.reflect.Constructor;
+import java.util.Vector;
+
+import javax.management.Attribute;
+import javax.management.AttributeNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.JMException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanConstructorInfo;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+import javax.management.Notification;
+import javax.management.NotificationBroadcaster;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationFilter;
+import javax.management.NotificationFilterSupport;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.management.RuntimeOperationsException;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Category;
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.HierarchyEventListener;
+import org.apache.log4j.spi.LoggerRepository;
+
+public class HierarchyDynamicMBean extends AbstractDynamicMBean implements HierarchyEventListener, NotificationBroadcaster {
+
+ static final String ADD_APPENDER = "addAppender.";
+ static final String THRESHOLD = "threshold";
+
+ private static Logger log = Logger.getLogger(HierarchyDynamicMBean.class);
+ private final MBeanConstructorInfo[] dConstructors = new MBeanConstructorInfo[1];
+
+ private final MBeanOperationInfo[] dOperations = new MBeanOperationInfo[1];
+ private final Vector vAttributes = new Vector();
+ private final String dClassName = this.getClass().getName();
+
+ private final String dDescription = "This MBean acts as a management facade for org.apache.log4j.Hierarchy.";
+
+ private final NotificationBroadcasterSupport nbs = new NotificationBroadcasterSupport();
+
+ private final LoggerRepository hierarchy;
+
+ public HierarchyDynamicMBean() {
+ hierarchy = LogManager.getLoggerRepository();
+ buildDynamicMBeanInfo();
+ }
+
+ @Override
+ public void addAppenderEvent(final Category logger, final Appender appender) {
+ log.debug("addAppenderEvent called: logger=" + logger.getName() + ", appender=" + appender.getName());
+ final Notification n = new Notification(ADD_APPENDER + logger.getName(), this, 0);
+ n.setUserData(appender);
+ log.debug("sending notification.");
+ nbs.sendNotification(n);
+ }
+
+ ObjectName addLoggerMBean(final Logger logger) {
+ final String name = logger.getName();
+ ObjectName objectName = null;
+ try {
+ final LoggerDynamicMBean loggerMBean = new LoggerDynamicMBean(logger);
+ objectName = new ObjectName("log4j", "logger", name);
+
+ if (!server.isRegistered(objectName)) {
+ registerMBean(loggerMBean, objectName);
+ final NotificationFilterSupport nfs = new NotificationFilterSupport();
+ nfs.enableType(ADD_APPENDER + logger.getName());
+ log.debug("---Adding logger [" + name + "] as listener.");
+ nbs.addNotificationListener(loggerMBean, nfs, null);
+ vAttributes.add(new MBeanAttributeInfo("logger=" + name, "javax.management.ObjectName", "The " + name + " logger.", true, true, // this makes
+ // the object
+ // clickable
+ false));
+
+ }
+
+ } catch (final JMException e) {
+ log.error("Could not add loggerMBean for [" + name + "].", e);
+ } catch (final RuntimeException e) {
+ log.error("Could not add loggerMBean for [" + name + "].", e);
+ }
+ return objectName;
+ }
+
+ public ObjectName addLoggerMBean(final String name) {
+ final Logger cat = LogManager.exists(name);
+
+ if (cat != null) {
+ return addLoggerMBean(cat);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void addNotificationListener(final NotificationListener listener, final NotificationFilter filter, final java.lang.Object handback) {
+ nbs.addNotificationListener(listener, filter, handback);
+ }
+
+ private void buildDynamicMBeanInfo() {
+ final Constructor[] constructors = this.getClass().getConstructors();
+ dConstructors[0] = new MBeanConstructorInfo("HierarchyDynamicMBean(): Constructs a HierarchyDynamicMBean instance", constructors[0]);
+
+ vAttributes.add(new MBeanAttributeInfo(THRESHOLD, "java.lang.String", "The \"threshold\" state of the hiearchy.", true, true, false));
+
+ final MBeanParameterInfo[] params = new MBeanParameterInfo[1];
+ params[0] = new MBeanParameterInfo("name", "java.lang.String", "Create a logger MBean");
+ dOperations[0] = new MBeanOperationInfo("addLoggerMBean", "addLoggerMBean(): add a loggerMBean", params, "javax.management.ObjectName",
+ MBeanOperationInfo.ACTION);
+ }
+
+ @Override
+ public Object getAttribute(final String attributeName) throws AttributeNotFoundException, MBeanException, ReflectionException {
+
+ // Check attributeName is not null to avoid NullPointerException later on
+ if (attributeName == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Attribute name cannot be null"),
+ "Cannot invoke a getter of " + dClassName + " with null attribute name");
+ }
+
+ log.debug("Called getAttribute with [" + attributeName + "].");
+
+ // Check for a recognized attributeName and call the corresponding getter
+ if (attributeName.equals(THRESHOLD)) {
+ return hierarchy.getThreshold();
+ } else if (attributeName.startsWith("logger")) {
+ final int k = attributeName.indexOf("%3D");
+ String val = attributeName;
+ if (k > 0) {
+ val = attributeName.substring(0, k) + '=' + attributeName.substring(k + 3);
+ }
+ try {
+ return new ObjectName("log4j:" + val);
+ } catch (final JMException e) {
+ log.error("Could not create ObjectName" + val);
+ } catch (final RuntimeException e) {
+ log.error("Could not create ObjectName" + val);
+ }
+ }
+
+ // If attributeName has not been recognized throw an AttributeNotFoundException
+ throw (new AttributeNotFoundException("Cannot find " + attributeName + " attribute in " + dClassName));
+
+ }
+
+ @Override
+ protected Logger getLogger() {
+ return log;
+ }
+
+ @Override
+ public MBeanInfo getMBeanInfo() {
+ // cat.debug("getMBeanInfo called.");
+
+ final MBeanAttributeInfo[] attribs = new MBeanAttributeInfo[vAttributes.size()];
+ vAttributes.toArray(attribs);
+
+ return new MBeanInfo(dClassName, dDescription, attribs, dConstructors, dOperations, new MBeanNotificationInfo[0]);
+ }
+
+ @Override
+ public MBeanNotificationInfo[] getNotificationInfo() {
+ return nbs.getNotificationInfo();
+ }
+
+ @Override
+ public Object invoke(final String operationName, final Object params[], final String signature[]) throws MBeanException, ReflectionException {
+
+ if (operationName == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Operation name cannot be null"),
+ "Cannot invoke a null operation in " + dClassName);
+ }
+ // Check for a recognized operation name and call the corresponding operation
+
+ if (operationName.equals("addLoggerMBean")) {
+ return addLoggerMBean((String) params[0]);
+ } else {
+ throw new ReflectionException(new NoSuchMethodException(operationName), "Cannot find the operation " + operationName + " in " + dClassName);
+ }
+
+ }
+
+ @Override
+ public void postRegister(final java.lang.Boolean registrationDone) {
+ log.debug("postRegister is called.");
+ hierarchy.addHierarchyEventListener(this);
+ final Logger root = hierarchy.getRootLogger();
+ addLoggerMBean(root);
+ }
+
+ @Override
+ public void removeAppenderEvent(final Category cat, final Appender appender) {
+ log.debug("removeAppenderCalled: logger=" + cat.getName() + ", appender=" + appender.getName());
+ }
+
+ @Override
+ public void removeNotificationListener(final NotificationListener listener) throws ListenerNotFoundException {
+ nbs.removeNotificationListener(listener);
+ }
+
+ @Override
+ public void setAttribute(final Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException {
+
+ // Check attribute is not null to avoid NullPointerException later on
+ if (attribute == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Attribute cannot be null"),
+ "Cannot invoke a setter of " + dClassName + " with null attribute");
+ }
+ final String name = attribute.getName();
+ final Object value = attribute.getValue();
+
+ if (name == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Attribute name cannot be null"),
+ "Cannot invoke the setter of " + dClassName + " with null attribute name");
+ }
+
+ if (name.equals(THRESHOLD)) {
+ final Level l = OptionConverter.toLevel((String) value, hierarchy.getThreshold());
+ hierarchy.setThreshold(l);
+ }
+
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/LayoutDynamicMBean.java b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/LayoutDynamicMBean.java
new file mode 100644
index 00000000000..68e9ebf4d56
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/LayoutDynamicMBean.java
@@ -0,0 +1,216 @@
+/*
+ * 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.jmx;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.InterruptedIOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import javax.management.Attribute;
+import javax.management.AttributeNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanConstructorInfo;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+import javax.management.ReflectionException;
+import javax.management.RuntimeOperationsException;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.OptionHandler;
+
+public class LayoutDynamicMBean extends AbstractDynamicMBean {
+
+ // This category instance is for logging.
+ private static Logger cat = Logger.getLogger(LayoutDynamicMBean.class);
+ private final MBeanConstructorInfo[] dConstructors = new MBeanConstructorInfo[1];
+ private final Vector dAttributes = new Vector();
+
+ private final String dClassName = this.getClass().getName();
+ private final Hashtable dynamicProps = new Hashtable(5);
+ private final MBeanOperationInfo[] dOperations = new MBeanOperationInfo[1];
+
+ private final String dDescription = "This MBean acts as a management facade for log4j layouts.";
+
+ // We wrap this layout instance.
+ private final Layout layout;
+
+ public LayoutDynamicMBean(final Layout layout) throws IntrospectionException {
+ this.layout = layout;
+ buildDynamicMBeanInfo();
+ }
+
+ private void buildDynamicMBeanInfo() throws IntrospectionException {
+ final Constructor[] constructors = this.getClass().getConstructors();
+ dConstructors[0] = new MBeanConstructorInfo("LayoutDynamicMBean(): Constructs a LayoutDynamicMBean instance", constructors[0]);
+
+ final BeanInfo bi = Introspector.getBeanInfo(layout.getClass());
+ final PropertyDescriptor[] pd = bi.getPropertyDescriptors();
+
+ final int size = pd.length;
+
+ for (int i = 0; i < size; i++) {
+ final String name = pd[i].getName();
+ final Method readMethod = pd[i].getReadMethod();
+ final Method writeMethod = pd[i].getWriteMethod();
+ if (readMethod != null) {
+ final Class returnClass = readMethod.getReturnType();
+ if (isSupportedType(returnClass)) {
+ String returnClassName;
+ if (returnClass.isAssignableFrom(Level.class)) {
+ returnClassName = "java.lang.String";
+ } else {
+ returnClassName = returnClass.getName();
+ }
+
+ dAttributes.add(new MBeanAttributeInfo(name, returnClassName, "Dynamic", true, writeMethod != null, false));
+ dynamicProps.put(name, new MethodUnion(readMethod, writeMethod));
+ }
+ }
+ }
+
+ final MBeanParameterInfo[] params = new MBeanParameterInfo[0];
+
+ dOperations[0] = new MBeanOperationInfo("activateOptions", "activateOptions(): add an layout", params, "void", MBeanOperationInfo.ACTION);
+ }
+
+ @Override
+ public Object getAttribute(final String attributeName) throws AttributeNotFoundException, MBeanException, ReflectionException {
+
+ // Check attributeName is not null to avoid NullPointerException later on
+ if (attributeName == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Attribute name cannot be null"),
+ "Cannot invoke a getter of " + dClassName + " with null attribute name");
+ }
+
+ final MethodUnion mu = (MethodUnion) dynamicProps.get(attributeName);
+
+ cat.debug("----name=" + attributeName + ", mu=" + mu);
+
+ if (mu != null && mu.readMethod != null) {
+ try {
+ return mu.readMethod.invoke(layout, null);
+ } catch (final InvocationTargetException e) {
+ if (e.getTargetException() instanceof InterruptedException || e.getTargetException() instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ return null;
+ } catch (final IllegalAccessException e) {
+ return null;
+ } catch (final RuntimeException e) {
+ return null;
+ }
+ }
+
+ // If attributeName has not been recognized throw an AttributeNotFoundException
+ throw (new AttributeNotFoundException("Cannot find " + attributeName + " attribute in " + dClassName));
+
+ }
+
+ @Override
+ protected Logger getLogger() {
+ return cat;
+ }
+
+ @Override
+ public MBeanInfo getMBeanInfo() {
+ cat.debug("getMBeanInfo called.");
+
+ final MBeanAttributeInfo[] attribs = new MBeanAttributeInfo[dAttributes.size()];
+ dAttributes.toArray(attribs);
+
+ return new MBeanInfo(dClassName, dDescription, attribs, dConstructors, dOperations, new MBeanNotificationInfo[0]);
+ }
+
+ @Override
+ public Object invoke(final String operationName, final Object params[], final String signature[]) throws MBeanException, ReflectionException {
+
+ if (operationName.equals("activateOptions") && layout instanceof OptionHandler) {
+ final OptionHandler oh = (OptionHandler) layout;
+ oh.activateOptions();
+ return "Options activated.";
+ }
+ return null;
+ }
+
+ private boolean isSupportedType(final Class clazz) {
+ if (clazz.isPrimitive() || (clazz == String.class) || clazz.isAssignableFrom(Level.class)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void setAttribute(final Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException {
+
+ // Check attribute is not null to avoid NullPointerException later on
+ if (attribute == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Attribute cannot be null"),
+ "Cannot invoke a setter of " + dClassName + " with null attribute");
+ }
+ final String name = attribute.getName();
+ Object value = attribute.getValue();
+
+ if (name == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Attribute name cannot be null"),
+ "Cannot invoke the setter of " + dClassName + " with null attribute name");
+ }
+
+ final MethodUnion mu = (MethodUnion) dynamicProps.get(name);
+
+ if (mu != null && mu.writeMethod != null) {
+ final Object[] o = new Object[1];
+
+ final Class[] params = mu.writeMethod.getParameterTypes();
+ if (params[0] == org.apache.log4j.Priority.class) {
+ value = OptionConverter.toLevel((String) value, (Level) getAttribute(name));
+ }
+ o[0] = value;
+
+ try {
+ mu.writeMethod.invoke(layout, o);
+
+ } catch (final InvocationTargetException e) {
+ if (e.getTargetException() instanceof InterruptedException || e.getTargetException() instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ cat.error("FIXME", e);
+ } catch (final IllegalAccessException e) {
+ cat.error("FIXME", e);
+ } catch (final RuntimeException e) {
+ cat.error("FIXME", e);
+ }
+ } else {
+ throw (new AttributeNotFoundException("Attribute " + name + " not found in " + this.getClass().getName()));
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/LoggerDynamicMBean.java b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/LoggerDynamicMBean.java
new file mode 100644
index 00000000000..4ae0e569146
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/LoggerDynamicMBean.java
@@ -0,0 +1,227 @@
+/*
+ * 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.jmx;
+
+import java.lang.reflect.Constructor;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import javax.management.Attribute;
+import javax.management.AttributeNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanConstructorInfo;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+import javax.management.MalformedObjectNameException;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.management.RuntimeOperationsException;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.helpers.OptionConverter;
+
+public class LoggerDynamicMBean extends AbstractDynamicMBean implements NotificationListener {
+
+ // This Logger instance is for logging.
+ private static Logger cat = Logger.getLogger(LoggerDynamicMBean.class);
+ private final MBeanConstructorInfo[] dConstructors = new MBeanConstructorInfo[1];
+
+ private final MBeanOperationInfo[] dOperations = new MBeanOperationInfo[1];
+ private final Vector dAttributes = new Vector();
+
+ private final String dClassName = this.getClass().getName();
+
+ private final String dDescription = "This MBean acts as a management facade for a org.apache.log4j.Logger instance.";
+
+ // We wrap this Logger instance.
+ private final Logger logger;
+
+ public LoggerDynamicMBean(final Logger logger) {
+ this.logger = logger;
+ buildDynamicMBeanInfo();
+ }
+
+ void addAppender(final String appenderClass, final String appenderName) {
+ cat.debug("addAppender called with " + appenderClass + ", " + appenderName);
+ final Appender appender = (Appender) OptionConverter.instantiateByClassName(appenderClass, org.apache.log4j.Appender.class, null);
+ appender.setName(appenderName);
+ logger.addAppender(appender);
+
+ // appenderMBeanRegistration();
+
+ }
+
+ void appenderMBeanRegistration() {
+ final Enumeration enumeration = logger.getAllAppenders();
+ while (enumeration.hasMoreElements()) {
+ final Appender appender = (Appender) enumeration.nextElement();
+ registerAppenderMBean(appender);
+ }
+ }
+
+ private void buildDynamicMBeanInfo() {
+ final Constructor[] constructors = this.getClass().getConstructors();
+ dConstructors[0] = new MBeanConstructorInfo("HierarchyDynamicMBean(): Constructs a HierarchyDynamicMBean instance", constructors[0]);
+
+ dAttributes.add(new MBeanAttributeInfo("name", "java.lang.String", "The name of this Logger.", true, false, false));
+
+ dAttributes.add(new MBeanAttributeInfo("priority", "java.lang.String", "The priority of this logger.", true, true, false));
+
+ final MBeanParameterInfo[] params = new MBeanParameterInfo[2];
+ params[0] = new MBeanParameterInfo("class name", "java.lang.String", "add an appender to this logger");
+ params[1] = new MBeanParameterInfo("appender name", "java.lang.String", "name of the appender");
+
+ dOperations[0] = new MBeanOperationInfo("addAppender", "addAppender(): add an appender", params, "void", MBeanOperationInfo.ACTION);
+ }
+
+ @Override
+ public Object getAttribute(final String attributeName) throws AttributeNotFoundException, MBeanException, ReflectionException {
+
+ // Check attributeName is not null to avoid NullPointerException later on
+ if (attributeName == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Attribute name cannot be null"),
+ "Cannot invoke a getter of " + dClassName + " with null attribute name");
+ }
+
+ // Check for a recognized attributeName and call the corresponding getter
+ if (attributeName.equals("name")) {
+ return logger.getName();
+ } else if (attributeName.equals("priority")) {
+ final Level l = logger.getLevel();
+ if (l == null) {
+ return null;
+ } else {
+ return l.toString();
+ }
+ } else if (attributeName.startsWith("appender=")) {
+ try {
+ return new ObjectName("log4j:" + attributeName);
+ } catch (final MalformedObjectNameException e) {
+ cat.error("Could not create ObjectName" + attributeName);
+ } catch (final RuntimeException e) {
+ cat.error("Could not create ObjectName" + attributeName);
+ }
+ }
+
+ // If attributeName has not been recognized throw an AttributeNotFoundException
+ throw (new AttributeNotFoundException("Cannot find " + attributeName + " attribute in " + dClassName));
+
+ }
+
+ @Override
+ protected Logger getLogger() {
+ return logger;
+ }
+
+ @Override
+ public MBeanInfo getMBeanInfo() {
+ // cat.debug("getMBeanInfo called.");
+
+ final MBeanAttributeInfo[] attribs = new MBeanAttributeInfo[dAttributes.size()];
+ dAttributes.toArray(attribs);
+
+ final MBeanInfo mb = new MBeanInfo(dClassName, dDescription, attribs, dConstructors, dOperations, new MBeanNotificationInfo[0]);
+ // cat.debug("getMBeanInfo exit.");
+ return mb;
+ }
+
+ @Override
+ public void handleNotification(final Notification notification, final Object handback) {
+ cat.debug("Received notification: " + notification.getType());
+ registerAppenderMBean((Appender) notification.getUserData());
+
+ }
+
+ @Override
+ public Object invoke(final String operationName, final Object params[], final String signature[]) throws MBeanException, ReflectionException {
+
+ if (operationName.equals("addAppender")) {
+ addAppender((String) params[0], (String) params[1]);
+ return "Hello world.";
+ }
+
+ return null;
+ }
+
+ @Override
+ public void postRegister(final java.lang.Boolean registrationDone) {
+ appenderMBeanRegistration();
+ }
+
+ void registerAppenderMBean(final Appender appender) {
+ final String name = getAppenderName(appender);
+ cat.debug("Adding AppenderMBean for appender named " + name);
+ ObjectName objectName = null;
+ try {
+ final AppenderDynamicMBean appenderMBean = new AppenderDynamicMBean(appender);
+ objectName = new ObjectName("log4j", "appender", name);
+ if (!server.isRegistered(objectName)) {
+ registerMBean(appenderMBean, objectName);
+ dAttributes.add(new MBeanAttributeInfo("appender=" + name, "javax.management.ObjectName", "The " + name + " appender.", true, true, false));
+ }
+
+ } catch (final JMException e) {
+ cat.error("Could not add appenderMBean for [" + name + "].", e);
+ } catch (final java.beans.IntrospectionException e) {
+ cat.error("Could not add appenderMBean for [" + name + "].", e);
+ } catch (final RuntimeException e) {
+ cat.error("Could not add appenderMBean for [" + name + "].", e);
+ }
+ }
+
+ @Override
+ public void setAttribute(final Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException {
+
+ // Check attribute is not null to avoid NullPointerException later on
+ if (attribute == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Attribute cannot be null"),
+ "Cannot invoke a setter of " + dClassName + " with null attribute");
+ }
+ final String name = attribute.getName();
+ final Object value = attribute.getValue();
+
+ if (name == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Attribute name cannot be null"),
+ "Cannot invoke the setter of " + dClassName + " with null attribute name");
+ }
+
+ if (name.equals("priority")) {
+ if (value instanceof String) {
+ final String s = (String) value;
+ Level p = logger.getLevel();
+ if (s.equalsIgnoreCase("NULL")) {
+ p = null;
+ } else {
+ p = OptionConverter.toLevel(s, p);
+ }
+ logger.setLevel(p);
+ }
+ } else {
+ throw (new AttributeNotFoundException("Attribute " + name + " not found in " + this.getClass().getName()));
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/MethodUnion.java b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/MethodUnion.java
new file mode 100644
index 00000000000..4e57aec8ccc
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/MethodUnion.java
@@ -0,0 +1,32 @@
+/*
+ * 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.jmx;
+
+import java.lang.reflect.Method;
+
+class MethodUnion {
+
+ Method readMethod;
+ Method writeMethod;
+
+ MethodUnion(final Method readMethod, final Method writeMethod) {
+ this.readMethod = readMethod;
+ this.writeMethod = writeMethod;
+ }
+
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/package-info.java b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/package-info.java
new file mode 100644
index 00000000000..6eec1006861
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/package-info.java
@@ -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.
+ */
+/**
+ * This package lets you manage log4j settings using JMX. It is unfortunately not of production quality.
+ */
+package org.apache.log4j.jmx;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1SyslogLayout.java b/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1SyslogLayout.java
new file mode 100644
index 00000000000..1a504492b5a
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1SyslogLayout.java
@@ -0,0 +1,224 @@
+/*
+ * 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.layout;
+
+import java.io.Serializable;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.StringLayout;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
+import org.apache.logging.log4j.core.layout.AbstractStringLayout;
+import org.apache.logging.log4j.core.net.Facility;
+import org.apache.logging.log4j.core.net.Priority;
+import org.apache.logging.log4j.core.pattern.DatePatternConverter;
+import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
+import org.apache.logging.log4j.core.util.NetUtils;
+import org.apache.logging.log4j.plugins.Configurable;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.plugins.PluginElement;
+import org.apache.logging.log4j.util.Chars;
+
+/**
+ * Port of the layout used by SyslogAppender in Log4j 1.x. Provided for
+ * compatibility with existing Log4j 1 configurations.
+ *
+ * Originally developed by Ceki Gülcü and Anders Kristensen.
+ */
+@Configurable(elementType = Layout.ELEMENT_TYPE, printObject = true)
+@Plugin
+public final class Log4j1SyslogLayout extends AbstractStringLayout {
+
+ /**
+ * Builds a SyslogLayout.
+ *
The main arguments are
+ *
+ *
facility: The Facility is used to try to classify the message.
+ *
includeNewLine: If true a newline will be appended to the result.
+ *
escapeNL: Pattern to use for replacing newlines.
+ *
charset: The character set.
+ *
+ * @param the builder type
+ */
+ public static class Builder> extends AbstractStringLayout.Builder
+ implements org.apache.logging.log4j.core.util.Builder {
+
+ public Builder() {
+ setCharset(StandardCharsets.UTF_8);
+ }
+
+ @PluginBuilderAttribute
+ private Facility facility = Facility.USER;
+
+ @PluginBuilderAttribute
+ private boolean facilityPrinting;
+
+ @PluginBuilderAttribute
+ private boolean header;
+
+ @PluginElement("Layout")
+ private Layout extends Serializable> messageLayout;
+
+ @Override
+ public Log4j1SyslogLayout build() {
+ if (messageLayout != null && !(messageLayout instanceof StringLayout)) {
+ LOGGER.error("Log4j1SyslogLayout: the message layout must be a StringLayout.");
+ return null;
+ }
+ return new Log4j1SyslogLayout(facility, facilityPrinting, header, (StringLayout) messageLayout, getCharset());
+ }
+
+ public Facility getFacility() {
+ return facility;
+ }
+
+ public boolean isFacilityPrinting() {
+ return facilityPrinting;
+ }
+
+ public boolean isHeader() {
+ return header;
+ }
+
+ public Layout extends Serializable> getMessageLayout() {
+ return messageLayout;
+ }
+
+ public B setFacility(final Facility facility) {
+ this.facility = facility;
+ return asBuilder();
+ }
+
+ public B setFacilityPrinting(final boolean facilityPrinting) {
+ this.facilityPrinting = facilityPrinting;
+ return asBuilder();
+ }
+
+ public B setHeader(final boolean header) {
+ this.header = header;
+ return asBuilder();
+ }
+
+ public B setMessageLayout(final Layout extends Serializable> messageLayout) {
+ this.messageLayout = messageLayout;
+ return asBuilder();
+ }
+ }
+
+ @PluginBuilderFactory
+ public static > B newBuilder() {
+ return new Builder().asBuilder();
+ }
+
+ /**
+ * Host name used to identify messages from this appender.
+ */
+ private static final String localHostname = NetUtils.getLocalHostname();
+
+ private final Facility facility;
+ private final boolean facilityPrinting;
+ private final boolean header;
+ private final StringLayout messageLayout;
+
+ /**
+ * Date format used if header = true.
+ */
+ private static final String[] dateFormatOptions = {"MMM dd HH:mm:ss", null, "en"};
+ private final LogEventPatternConverter dateConverter = DatePatternConverter.newInstance(dateFormatOptions);
+
+
+ private Log4j1SyslogLayout(final Facility facility, final boolean facilityPrinting, final boolean header,
+ final StringLayout messageLayout, final Charset charset) {
+ super(charset);
+ this.facility = facility;
+ this.facilityPrinting = facilityPrinting;
+ this.header = header;
+ this.messageLayout = messageLayout;
+ }
+
+ /**
+ * Formats a {@link LogEvent} in conformance with the BSD Log record format.
+ *
+ * @param event The LogEvent
+ * @return the event formatted as a String.
+ */
+ @Override
+ public String toSerializable(final LogEvent event) {
+ // The messageLayout also uses the thread-bound StringBuilder,
+ // so we generate the message first
+ final String message = messageLayout != null ? messageLayout.toSerializable(event)
+ : event.getMessage().getFormattedMessage();
+ final StringBuilder buf = getStringBuilder();
+
+ buf.append('<');
+ buf.append(Priority.getPriority(facility, event.getLevel()));
+ buf.append('>');
+
+ if (header) {
+ final int index = buf.length() + 4;
+ dateConverter.format(event, buf);
+ // RFC 3164 says leading space, not leading zero on days 1-9
+ if (buf.charAt(index) == '0') {
+ buf.setCharAt(index, Chars.SPACE);
+ }
+
+ buf.append(Chars.SPACE);
+ buf.append(localHostname);
+ buf.append(Chars.SPACE);
+ }
+
+ if (facilityPrinting) {
+ buf.append(facility != null ? facility.name().toLowerCase() : "user").append(':');
+ }
+
+ buf.append(message);
+ // TODO: splitting message into 1024 byte chunks?
+ return buf.toString();
+ }
+
+ /**
+ * Gets this SyslogLayout's content format. Specified by:
+ *
Key: "formatType" Value: "logfilepatternreceiver" (format uses the keywords supported by
+ * LogFilePatternReceiver)
+ *
+ *
+ * @return Map of content format keys supporting SyslogLayout
+ */
+ @Override
+ public Map getContentFormat() {
+ final Map result = new HashMap<>();
+ result.put("structured", "false");
+ result.put("formatType", "logfilepatternreceiver");
+ result.put("dateFormat", dateFormatOptions[0]);
+ if (header) {
+ result.put("format", "TIMESTAMP PROP(HOSTNAME) MESSAGE");
+ } else {
+ result.put("format", "MESSAGE");
+ }
+ return result;
+ }
+
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1XmlLayout.java b/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1XmlLayout.java
index 9522b9e9e66..4eb629c62a7 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1XmlLayout.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1XmlLayout.java
@@ -16,32 +16,36 @@
*/
package org.apache.log4j.layout;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.nio.charset.StandardCharsets;
-import java.util.List;
-
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.config.Node;
-import org.apache.logging.log4j.core.config.plugins.Plugin;
-import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
-import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.AbstractStringLayout;
import org.apache.logging.log4j.core.layout.ByteBufferDestination;
import org.apache.logging.log4j.core.util.Transform;
-import org.apache.logging.log4j.util.BiConsumer;
+import org.apache.logging.log4j.plugins.Configurable;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.plugins.PluginAttribute;
+import org.apache.logging.log4j.plugins.PluginFactory;
import org.apache.logging.log4j.util.ReadOnlyStringMap;
import org.apache.logging.log4j.util.Strings;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Objects;
+
/**
* Port of XMLLayout in Log4j 1.x. Provided for compatibility with existing Log4j 1 configurations.
*
* Originally developed by Ceki Gülcü, Mathias Bogaert.
*/
-@Plugin(name = "Log4j1XmlLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true)
+@Configurable(elementType = Layout.ELEMENT_TYPE, printObject = true)
+@Plugin
public final class Log4j1XmlLayout extends AbstractStringLayout {
+ /** We yield to the \r\n heresy. */
+ private static final String EOL = "\r\n";
+
private final boolean locationInfo;
private final boolean properties;
@@ -84,8 +88,6 @@ public String toSerializable(final LogEvent event) {
}
private void formatTo(final LogEvent event, final StringBuilder buf) {
- // We yield to the \r\n heresy.
-
buf.append("\r\n");
+ buf.append("\">");
+ buf.append(EOL);
buf.append("\r\n");
+ buf.append("]]>");
+ buf.append(EOL);
final List ndc = event.getContextStack().asList();
if (!ndc.isEmpty()) {
buf.append("\r\n");
+ buf.append("]]>");
+ buf.append(EOL);
}
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
- final Throwable thrown = event.getThrown();
+ final Throwable thrown = event.getThrown();
if (thrown != null) {
buf.append("\r\n");
+ buf.append("]]>");
+ buf.append(EOL);
}
if (locationInfo) {
@@ -129,7 +135,8 @@ private void formatTo(final LogEvent event, final StringBuilder buf) {
buf.append(Transform.escapeHtmlTags(source.getFileName()));
buf.append("\" line=\"");
buf.append(source.getLineNumber());
- buf.append("\"/>\r\n");
+ buf.append("\"/>");
+ buf.append(EOL);
}
}
@@ -137,23 +144,24 @@ private void formatTo(final LogEvent event, final StringBuilder buf) {
final ReadOnlyStringMap contextMap = event.getContextData();
if (!contextMap.isEmpty()) {
buf.append("\r\n");
- contextMap.forEach(new BiConsumer() {
- @Override
- public void accept(final String key, final String val) {
- if (val != null) {
- buf.append("\r\n");
- }
+ contextMap.forEach((key, val) -> {
+ if (val != null) {
+ buf.append("");
+ buf.append(EOL);
}
});
- buf.append("\r\n");
+ buf.append("");
+ buf.append(EOL);
}
}
- buf.append("\r\n\r\n");
+ buf.append("");
+ buf.append(EOL);
+ buf.append(EOL);
}
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/legacy/core/CategoryUtil.java b/log4j-1.2-api/src/main/java/org/apache/log4j/legacy/core/CategoryUtil.java
new file mode 100644
index 00000000000..3e5dc2c515d
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/legacy/core/CategoryUtil.java
@@ -0,0 +1,163 @@
+/*
+ * 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.legacy.core;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Supplier;
+
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.spi.LoggerContext;
+
+/**
+ * Delegates to {@code Logger} methods implemented by {@code log4j-core} if appropriate.
+ */
+public final class CategoryUtil {
+
+ private static org.apache.logging.log4j.core.Logger asCore(final Logger logger) {
+ return (org.apache.logging.log4j.core.Logger) logger;
+ }
+
+ private static T get(final Logger logger, final Supplier run, final T defaultValue) {
+ return isCore(logger) ? run.get() : defaultValue;
+ }
+
+ /**
+ * Gets the appenders attached directly to this logger.
+ *
+ * @param logger The target logger.
+ * @return A Map containing the Appender's name as the key and the Appender as the value.
+ */
+ public static Map getAppenders(final Logger logger) {
+ return get(logger, () -> getDirectAppenders(logger), Collections.emptyMap());
+ }
+
+ private static Map getDirectAppenders(final Logger logger) {
+ return CategoryUtil.getExactLoggerConfig(logger)
+ .map(LoggerConfig::getAppenders)
+ .orElse(Collections.emptyMap());
+ }
+
+ private static Optional getExactLoggerConfig(final Logger logger) {
+ return Optional.of(asCore(logger).get()).filter(lc -> logger.getName().equals(lc.getName()));
+ }
+
+ /**
+ * Delegates to {@link org.apache.logging.log4j.core.Logger#getFilters()} if appropriate.
+ *
+ * @param logger The target logger.
+ * @return An Iterator over all the Filters associated with the Logger.
+ */
+ public static Iterator getFilters(final Logger logger) {
+ return get(logger, asCore(logger)::getFilters, null);
+ }
+
+ /**
+ * Delegates to {@link org.apache.logging.log4j.core.Logger#getContext()} if appropriate.
+ *
+ * @param logger The target logger.
+ * @return the LoggerContext.
+ */
+ public static LoggerContext getLoggerContext(final Logger logger) {
+ return get(logger, asCore(logger)::getContext, null);
+ }
+
+ /**
+ * Delegates to {@link org.apache.logging.log4j.core.Logger#getParent()} if appropriate.
+ *
+ * @param logger The target logger.
+ * @return The parent Logger.
+ */
+ public static Logger getParent(final Logger logger) {
+ return get(logger, asCore(logger)::getParent, null);
+ }
+
+ /**
+ * Delegates to {@link org.apache.logging.log4j.core.Logger#isAdditive()} if appropriate.
+ *
+ * @param logger The target logger.
+ * @return true if the associated LoggerConfig is additive, false otherwise.
+ */
+ public static boolean isAdditive(final Logger logger) {
+ return get(logger, asCore(logger)::isAdditive, false);
+ }
+
+ private static boolean isCore(final Logger logger) {
+ return logger instanceof org.apache.logging.log4j.core.Logger;
+ }
+
+ /**
+ * Delegates to {@link org.apache.logging.log4j.core.Logger#setAdditive(boolean)} if appropriate.
+ *
+ * @param logger The target logger.
+ * @param additive Boolean value to indicate whether the Logger is additive or not.
+ */
+ public static void setAdditivity(final Logger logger, final boolean additive) {
+ if (isCore(logger)) {
+ asCore(logger).setAdditive(additive);
+ }
+ }
+
+ /**
+ * Delegates to {@link org.apache.logging.log4j.core.Logger#setLevel(Level)} if appropriate.
+ *
+ * @param logger The target logger.
+ * @param level The Level to use on this Logger, may be null.
+ */
+ public static void setLevel(final Logger logger, final Level level) {
+ if (isCore(logger)) {
+ asCore(logger).setLevel(level);
+ }
+ }
+
+ /**
+ * Adds an appender to the logger. This method requires a check for the presence
+ * of Log4j Core or it will cause a {@code ClassNotFoundException}.
+ *
+ * @param logger The target logger.
+ * @param appender A Log4j2 appender.
+ */
+ public static void addAppender(final Logger logger, final Appender appender) {
+ if (appender instanceof AppenderAdapter.Adapter) {
+ appender.start();
+ }
+ asCore(logger).addAppender(appender);
+ }
+
+ /**
+ * Sends the event to all appenders directly connected with the logger. This
+ * method requires a check for the presence of Log4j Core or it will cause a
+ * {@code ClassNotFoundException}.
+ *
+ * @param logger The target logger.
+ * @param event The event to send.
+ */
+ public static void log(final Logger logger, final LogEvent event) {
+ getExactLoggerConfig(logger).ifPresent(lc -> lc.log(event));
+ }
+
+ private CategoryUtil() {
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/legacy/core/ContextUtil.java b/log4j-1.2-api/src/main/java/org/apache/log4j/legacy/core/ContextUtil.java
new file mode 100644
index 00000000000..89809809c02
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/legacy/core/ContextUtil.java
@@ -0,0 +1,50 @@
+/*
+ * 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.legacy.core;
+
+import org.apache.logging.log4j.spi.LoggerContext;
+
+/**
+ * Delegates to {@code LoggerContext} methods implemented by {@code log4j-core} if appropriate.
+ */
+public final class ContextUtil {
+
+ /**
+ * Delegates to {@link org.apache.logging.log4j.core.LoggerContext#reconfigure()} if appropriate.
+ *
+ * @param loggerContext The target logger context.
+ */
+ public static void reconfigure(LoggerContext loggerContext) {
+ if (loggerContext instanceof org.apache.logging.log4j.core.LoggerContext) {
+ ((org.apache.logging.log4j.core.LoggerContext) loggerContext).reconfigure();
+ }
+ }
+
+ /**
+ * Delegates to {@link org.apache.logging.log4j.core.LoggerContext#close()} if appropriate.
+ *
+ * @param loggerContext The target logger context.
+ */
+ public static void shutdown(LoggerContext loggerContext) {
+ if (loggerContext instanceof org.apache.logging.log4j.core.LoggerContext) {
+ ((org.apache.logging.log4j.core.LoggerContext) loggerContext).close();
+ }
+ }
+
+ private ContextUtil() {
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/or/DefaultRenderer.java b/log4j-1.2-api/src/main/java/org/apache/log4j/or/DefaultRenderer.java
new file mode 100644
index 00000000000..e8f632181e1
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/or/DefaultRenderer.java
@@ -0,0 +1,40 @@
+/*
+ * 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.or;
+
+/**
+ * The default ObjectRenderer renders objects by calling their {@code toString()} method.
+ *
+ * @since 1.0
+ */
+class DefaultRenderer implements ObjectRenderer {
+
+ DefaultRenderer() {
+ }
+
+ /**
+ * Render the object passed as parameter by calling its {@code toString()} method.
+ */
+ public String doRender(final Object o) {
+ try {
+ return o.toString();
+ } catch (Exception ex) {
+ return ex.toString();
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/or/ObjectRenderer.java b/log4j-1.2-api/src/main/java/org/apache/log4j/or/ObjectRenderer.java
new file mode 100644
index 00000000000..4e627c24acb
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/or/ObjectRenderer.java
@@ -0,0 +1,29 @@
+/*
+ * 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.or;
+
+/**
+ * Converts objects to Strings.
+ */
+public interface ObjectRenderer {
+ /**
+ * Render the object passed as parameter as a String.
+ * @param o The object to render.
+ * @return The String representation of the object.
+ */
+ String doRender(Object o);
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/or/RendererMap.java b/log4j-1.2-api/src/main/java/org/apache/log4j/or/RendererMap.java
new file mode 100644
index 00000000000..b70b1a56751
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/or/RendererMap.java
@@ -0,0 +1,179 @@
+/*
+ * 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.or;
+
+import java.util.Hashtable;
+
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.RendererSupport;
+import org.apache.logging.log4j.core.util.Loader;
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * Map class objects to an {@link ObjectRenderer}.
+ *
+ * @author Ceki Gülcü
+ * @since version 1.0
+ */
+public class RendererMap {
+
+ Hashtable map;
+
+ static ObjectRenderer defaultRenderer = new DefaultRenderer();
+
+ public RendererMap() {
+ map = new Hashtable();
+ }
+
+ /**
+ * Add a renderer to a hierarchy passed as parameter.
+ */
+ static public void addRenderer(RendererSupport repository, String renderedClassName, String renderingClassName) {
+ StatusLogger.getLogger().debug("Rendering class: [" + renderingClassName + "], Rendered class: [" + renderedClassName + "].");
+ ObjectRenderer renderer = (ObjectRenderer) OptionConverter.instantiateByClassName(renderingClassName, ObjectRenderer.class, null);
+ if (renderer == null) {
+ StatusLogger.getLogger().error("Could not instantiate renderer [" + renderingClassName + "].");
+ return;
+ }
+ try {
+ Class renderedClass = Loader.loadClass(renderedClassName);
+ repository.setRenderer(renderedClass, renderer);
+ } catch (ClassNotFoundException e) {
+ StatusLogger.getLogger().error("Could not find class [" + renderedClassName + "].", e);
+ }
+ }
+
+ /**
+ * Find the appropriate renderer for the class type of the o parameter. This is accomplished by calling the
+ * {@link #get(Class)} method. Once a renderer is found, it is applied on the object o and the result is
+ * returned as a {@link String}.
+ */
+ public String findAndRender(Object o) {
+ if (o == null)
+ return null;
+ else
+ return get(o.getClass()).doRender(o);
+ }
+
+ /**
+ * Syntactic sugar method that calls {@link #get(Class)} with the class of the object parameter.
+ */
+ public ObjectRenderer get(Object o) {
+ if (o == null)
+ return null;
+ else
+ return get(o.getClass());
+ }
+
+ /**
+ * Search the parents of clazz for a renderer. The renderer closest in the hierarchy will be returned. If
+ * no renderers could be found, then the default renderer is returned.
+ *
+ *
+ * The search first looks for a renderer configured for clazz. If a renderer could not be found, then the
+ * search continues by looking at all the interfaces implemented by clazz including the super-interfaces of
+ * each interface. If a renderer cannot be found, then the search looks for a renderer defined for the parent
+ * (superclass) of clazz. If that fails, then all the interfaces implemented by the parent of
+ * clazz are searched and so on.
+ *
+ *
+ * For example, if A0, A1, A2 are classes and X0, X1, X2, Y0, Y1 are interfaces where A2 extends A1 which in turn
+ * extends A0 and similarly X2 extends X1 which extends X0 and Y1 extends Y0. Let us also assume that A1 implements the
+ * Y0 interface and that A2 implements the X2 interface.
+ *
+ *
+ * The table below shows the results returned by the get(A2.class) method depending on the renderers added
+ * to the map.
+ *
+ *
+ *
+ *
+ *
Added renderers
+ *
Value returned by get(A2.class)
+ *
+ *
+ *
A0Renderer
+ *
A0Renderer
+ *
+ *
+ *
A0Renderer, A1Renderer
+ *
A1Renderer
+ *
+ *
+ *
X0Renderer
+ *
X0Renderer
+ *
+ *
+ *
A1Renderer, X0Renderer
+ *
X0Renderer
+ *
+ *
+ *
+ *
+ * This search algorithm is not the most natural, although it is particularly easy to implement. Future log4j versions
+ * may implement a more intuitive search algorithm. However, the present algorithm should be acceptable in the
+ * vast majority of circumstances.
+ *
+ */
+ public ObjectRenderer get(Class clazz) {
+ // System.out.println("\nget: "+clazz);
+ ObjectRenderer r = null;
+ for (Class c = clazz; c != null; c = c.getSuperclass()) {
+ // System.out.println("Searching for class: "+c);
+ r = (ObjectRenderer) map.get(c);
+ if (r != null) {
+ return r;
+ }
+ r = searchInterfaces(c);
+ if (r != null)
+ return r;
+ }
+ return defaultRenderer;
+ }
+
+ ObjectRenderer searchInterfaces(Class c) {
+ // System.out.println("Searching interfaces of class: "+c);
+
+ ObjectRenderer r = (ObjectRenderer) map.get(c);
+ if (r != null) {
+ return r;
+ }
+ Class[] ia = c.getInterfaces();
+ for (int i = 0; i < ia.length; i++) {
+ r = searchInterfaces(ia[i]);
+ if (r != null)
+ return r;
+ }
+ return null;
+ }
+
+ public ObjectRenderer getDefaultRenderer() {
+ return defaultRenderer;
+ }
+
+ public void clear() {
+ map.clear();
+ }
+
+ /**
+ * Register an {@link ObjectRenderer} for clazz.
+ */
+ public void put(Class clazz, ObjectRenderer or) {
+ map.put(clazz, or);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/or/ThreadGroupRenderer.java b/log4j-1.2-api/src/main/java/org/apache/log4j/or/ThreadGroupRenderer.java
new file mode 100644
index 00000000000..959d927e890
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/or/ThreadGroupRenderer.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.log4j.or;
+
+import org.apache.log4j.Layout;
+
+/**
+ */
+public class ThreadGroupRenderer implements ObjectRenderer {
+
+ @Override
+ public
+ String doRender(Object obj) {
+ if(obj instanceof ThreadGroup) {
+ StringBuilder sb = new StringBuilder();
+ ThreadGroup threadGroup = (ThreadGroup) obj;
+ sb.append("java.lang.ThreadGroup[name=");
+ sb.append(threadGroup.getName());
+ sb.append(", maxpri=");
+ sb.append(threadGroup.getMaxPriority());
+ sb.append("]");
+ Thread[] threads = new Thread[threadGroup.activeCount()];
+ threadGroup.enumerate(threads);
+ for (Thread thread : threads) {
+ sb.append(Layout.LINE_SEP);
+ sb.append(" Thread=[");
+ sb.append(thread.getName());
+ sb.append(",");
+ sb.append(thread.getPriority());
+ sb.append(",");
+ sb.append(thread.isDaemon());
+ sb.append("]");
+ }
+ return sb.toString();
+ }
+ try {
+ // this is the best we can do
+ return obj.toString();
+ } catch(Exception ex) {
+ return ex.toString();
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/or/jms/MessageRenderer.java b/log4j-1.2-api/src/main/java/org/apache/log4j/or/jms/MessageRenderer.java
new file mode 100644
index 00000000000..14e1594be96
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/or/jms/MessageRenderer.java
@@ -0,0 +1,87 @@
+/*
+ * 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.or.jms;
+
+import javax.jms.DeliveryMode;
+import javax.jms.JMSException;
+import javax.jms.Message;
+
+import org.apache.log4j.or.ObjectRenderer;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * Log4j 1.x JMS Message Renderer
+ */
+public class MessageRenderer implements ObjectRenderer {
+ private static final Logger LOGGER = StatusLogger.getLogger();
+
+ /**
+ Render a {@link javax.jms.Message}.
+ */
+ @Override
+ public
+ String doRender(Object obj) {
+ if (obj instanceof Message) {
+ StringBuilder sb = new StringBuilder();
+ Message message = (Message) obj;
+ try {
+ sb.append("DeliveryMode=");
+ switch(message.getJMSDeliveryMode()) {
+ case DeliveryMode.NON_PERSISTENT :
+ sb.append("NON_PERSISTENT");
+ break;
+ case DeliveryMode.PERSISTENT :
+ sb.append("PERSISTENT");
+ break;
+ default: sb.append("UNKNOWN");
+ }
+ sb.append(", CorrelationID=");
+ sb.append(message.getJMSCorrelationID());
+
+ sb.append(", Destination=");
+ sb.append(message.getJMSDestination());
+
+ sb.append(", Expiration=");
+ sb.append(message.getJMSExpiration());
+
+ sb.append(", MessageID=");
+ sb.append(message.getJMSMessageID());
+
+ sb.append(", Priority=");
+ sb.append(message.getJMSPriority());
+
+ sb.append(", Redelivered=");
+ sb.append(message.getJMSRedelivered());
+
+ sb.append(", ReplyTo=");
+ sb.append(message.getJMSReplyTo());
+
+ sb.append(", Timestamp=");
+ sb.append(message.getJMSTimestamp());
+
+ sb.append(", Type=");
+ sb.append(message.getJMSType());
+
+ } catch(JMSException e) {
+ LOGGER.error("Could not parse Message.", e);
+ }
+ return sb.toString();
+ }
+ return obj.toString();
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/FormattingInfo.java b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/FormattingInfo.java
new file mode 100644
index 00000000000..d8faaf415e1
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/FormattingInfo.java
@@ -0,0 +1,128 @@
+/*
+ * 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.pattern;
+
+/**
+ * Modifies the output of a pattern converter for a specified minimum and maximum width and alignment.
+ */
+public final class FormattingInfo {
+ /**
+ * Array of spaces.
+ */
+ private static final char[] SPACES = new char[] {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '};
+
+ /**
+ * Default instance.
+ */
+ private static final FormattingInfo DEFAULT = new FormattingInfo(false, 0, Integer.MAX_VALUE);
+
+ /**
+ * Gets default instance.
+ *
+ * @return default instance.
+ */
+ public static FormattingInfo getDefault() {
+ return DEFAULT;
+ }
+
+ /**
+ * Minimum length.
+ */
+ private final int minLength;
+
+ /**
+ * Maximum length.
+ */
+ private final int maxLength;
+
+ /**
+ * Alignment.
+ */
+ private final boolean leftAlign;
+
+ /**
+ * Creates new instance.
+ *
+ * @param leftAlign left align if true.
+ * @param minLength minimum length.
+ * @param maxLength maximum length.
+ */
+ public FormattingInfo(final boolean leftAlign, final int minLength, final int maxLength) {
+ this.leftAlign = leftAlign;
+ this.minLength = minLength;
+ this.maxLength = maxLength;
+ }
+
+ /**
+ * Adjust the content of the buffer based on the specified lengths and alignment.
+ *
+ * @param fieldStart start of field in buffer.
+ * @param buffer buffer to be modified.
+ */
+ public void format(final int fieldStart, final StringBuffer buffer) {
+ final int rawLength = buffer.length() - fieldStart;
+
+ if (rawLength > maxLength) {
+ buffer.delete(fieldStart, buffer.length() - maxLength);
+ } else if (rawLength < minLength) {
+ if (leftAlign) {
+ final int fieldEnd = buffer.length();
+ buffer.setLength(fieldStart + minLength);
+
+ for (int i = fieldEnd; i < buffer.length(); i++) {
+ buffer.setCharAt(i, ' ');
+ }
+ } else {
+ int padLength = minLength - rawLength;
+
+ for (; padLength > 8; padLength -= 8) {
+ buffer.insert(fieldStart, SPACES);
+ }
+
+ buffer.insert(fieldStart, SPACES, 0, padLength);
+ }
+ }
+ }
+
+ /**
+ * Get maximum length.
+ *
+ * @return maximum length.
+ */
+ public int getMaxLength() {
+ return maxLength;
+ }
+
+ /**
+ * Get minimum length.
+ *
+ * @return minimum length.
+ */
+ public int getMinLength() {
+ return minLength;
+ }
+
+ /**
+ * Determine if left aligned.
+ *
+ * @return true if left aligned.
+ */
+ public boolean isLeftAligned() {
+ return leftAlign;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1LevelPatternConverter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1LevelPatternConverter.java
new file mode 100644
index 00000000000..3be8fb9e1e8
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1LevelPatternConverter.java
@@ -0,0 +1,50 @@
+/*
+ * 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.pattern;
+
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.pattern.ConverterKeys;
+import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
+import org.apache.logging.log4j.core.pattern.PatternConverter;
+import org.apache.logging.log4j.plugins.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
+
+/**
+ * Outputs the Log4j 1.x level name.
+ */
+@Namespace(PatternConverter.CATEGORY)
+@Plugin("Log4j1LevelPatternConverter")
+@ConverterKeys({ "v1Level" })
+public class Log4j1LevelPatternConverter extends LogEventPatternConverter {
+
+ private static final Log4j1LevelPatternConverter INSTANCE = new Log4j1LevelPatternConverter();
+
+ public static Log4j1LevelPatternConverter newInstance(final String[] options) {
+ return INSTANCE;
+ }
+
+ private Log4j1LevelPatternConverter() {
+ super("Log4j1Level", "v1Level");
+ }
+
+ @Override
+ public void format(LogEvent event, StringBuilder toAppendTo) {
+ toAppendTo.append(OptionConverter.convertLevel(event.getLevel()).toString());
+ }
+
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1MdcPatternConverter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1MdcPatternConverter.java
index b4ae0c5e93b..f5e08ad48c5 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1MdcPatternConverter.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1MdcPatternConverter.java
@@ -17,10 +17,11 @@
package org.apache.log4j.pattern;
import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.pattern.ConverterKeys;
import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
import org.apache.logging.log4j.core.pattern.PatternConverter;
+import org.apache.logging.log4j.plugins.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
import org.apache.logging.log4j.util.TriConsumer;
/**
@@ -28,7 +29,8 @@
* output the entire contents of the properties, or to output the value of a specific key
* within the property bundle when this pattern converter has the option set.
*/
-@Plugin(name = "Log4j1MdcPatternConverter", category = PatternConverter.CATEGORY)
+@Namespace(PatternConverter.CATEGORY)
+@Plugin("Log4j1MdcPatternConverter")
@ConverterKeys({ "properties" })
public final class Log4j1MdcPatternConverter extends LogEventPatternConverter {
/**
@@ -79,10 +81,5 @@ public void format(final LogEvent event, final StringBuilder toAppendTo) {
}
}
- private static TriConsumer APPEND_EACH = new TriConsumer() {
- @Override
- public void accept(final String key, final Object value, final StringBuilder toAppendTo) {
- toAppendTo.append('{').append(key).append(',').append(value).append('}');
- }
- };
+ private static TriConsumer APPEND_EACH = (key, value, toAppendTo) -> toAppendTo.append('{').append(key).append(',').append(value).append('}');
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1NdcPatternConverter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1NdcPatternConverter.java
index 405db0093c8..d6c3e02ff1c 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1NdcPatternConverter.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1NdcPatternConverter.java
@@ -17,10 +17,11 @@
package org.apache.log4j.pattern;
import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.pattern.ConverterKeys;
import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
import org.apache.logging.log4j.core.pattern.PatternConverter;
+import org.apache.logging.log4j.plugins.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
import org.apache.logging.log4j.util.Strings;
import java.util.List;
@@ -29,7 +30,8 @@
/**
* Returns the event's NDC in a StringBuilder.
*/
-@Plugin(name = "Log4j1NdcPatternConverter", category = PatternConverter.CATEGORY)
+@Namespace(PatternConverter.CATEGORY)
+@Plugin("Log4j1NdcPatternConverter")
@ConverterKeys({ "ndc" })
public final class Log4j1NdcPatternConverter extends LogEventPatternConverter {
/**
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/NameAbbreviator.java b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/NameAbbreviator.java
new file mode 100644
index 00000000000..8905ecbffd2
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/NameAbbreviator.java
@@ -0,0 +1,345 @@
+/*
+ * 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.pattern;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * NameAbbreviator generates abbreviated logger and class names.
+ */
+public abstract class NameAbbreviator {
+
+ /**
+ * Abbreviator that drops starting path elements.
+ */
+ private static class DropElementAbbreviator extends NameAbbreviator {
+ /**
+ * Maximum number of path elements to output.
+ */
+ private final int count;
+
+ /**
+ * Create new instance.
+ *
+ * @param count maximum number of path elements to output.
+ */
+ public DropElementAbbreviator(final int count) {
+ this.count = count;
+ }
+
+ /**
+ * Abbreviate name.
+ *
+ * @param buf buffer to append abbreviation.
+ * @param nameStart start of name to abbreviate.
+ */
+ @Override
+ public void abbreviate(final int nameStart, final StringBuffer buf) {
+ int i = count;
+ for (int pos = buf.indexOf(".", nameStart); pos != -1; pos = buf.indexOf(".", pos + 1)) {
+ if (--i == 0) {
+ buf.delete(nameStart, pos + 1);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Abbreviator that drops starting path elements.
+ */
+ private static class MaxElementAbbreviator extends NameAbbreviator {
+ /**
+ * Maximum number of path elements to output.
+ */
+ private final int count;
+
+ /**
+ * Create new instance.
+ *
+ * @param count maximum number of path elements to output.
+ */
+ public MaxElementAbbreviator(final int count) {
+ this.count = count;
+ }
+
+ /**
+ * Abbreviate name.
+ *
+ * @param buf buffer to append abbreviation.
+ * @param nameStart start of name to abbreviate.
+ */
+ @Override
+ public void abbreviate(final int nameStart, final StringBuffer buf) {
+ // We substract 1 from 'len' when assigning to 'end' to avoid out of
+ // bounds exception in return r.substring(end+1, len). This can happen if
+ // precision is 1 and the category name ends with a dot.
+ int end = buf.length() - 1;
+
+ final String bufString = buf.toString();
+ for (int i = count; i > 0; i--) {
+ end = bufString.lastIndexOf(".", end - 1);
+
+ if ((end == -1) || (end < nameStart)) {
+ return;
+ }
+ }
+
+ buf.delete(nameStart, end + 1);
+ }
+ }
+
+ /**
+ * Abbreviator that simply appends full name to buffer.
+ */
+ private static class NOPAbbreviator extends NameAbbreviator {
+ /**
+ * Constructor.
+ */
+ public NOPAbbreviator() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void abbreviate(final int nameStart, final StringBuffer buf) {
+ }
+ }
+
+ /**
+ * Pattern abbreviator.
+ *
+ *
+ */
+ private static class PatternAbbreviator extends NameAbbreviator {
+ /**
+ * Element abbreviation patterns.
+ */
+ private final PatternAbbreviatorFragment[] fragments;
+
+ /**
+ * Create PatternAbbreviator.
+ *
+ * @param fragments element abbreviation patterns.
+ */
+ public PatternAbbreviator(final List fragments) {
+ if (fragments.size() == 0) {
+ throw new IllegalArgumentException("fragments must have at least one element");
+ }
+
+ this.fragments = new PatternAbbreviatorFragment[fragments.size()];
+ fragments.toArray(this.fragments);
+ }
+
+ /**
+ * Abbreviate name.
+ *
+ * @param buf buffer that abbreviated name is appended.
+ * @param nameStart start of name.
+ */
+ @Override
+ public void abbreviate(final int nameStart, final StringBuffer buf) {
+ //
+ // all non-terminal patterns are executed once
+ //
+ int pos = nameStart;
+
+ for (int i = 0; (i < (fragments.length - 1)) && (pos < buf.length()); i++) {
+ pos = fragments[i].abbreviate(buf, pos);
+ }
+
+ //
+ // last pattern in executed repeatedly
+ //
+ final PatternAbbreviatorFragment terminalFragment = fragments[fragments.length - 1];
+
+ while ((pos < buf.length()) && (pos >= 0)) {
+ pos = terminalFragment.abbreviate(buf, pos);
+ }
+ }
+ }
+
+ /**
+ * Fragment of an pattern abbreviator.
+ *
+ */
+ private static class PatternAbbreviatorFragment {
+ /**
+ * Count of initial characters of element to output.
+ */
+ private final int charCount;
+
+ /**
+ * Character used to represent dropped characters. '\0' indicates no representation of dropped characters.
+ */
+ private final char ellipsis;
+
+ /**
+ * Creates a PatternAbbreviatorFragment.
+ *
+ * @param charCount number of initial characters to preserve.
+ * @param ellipsis character to represent elimination of characters, '\0' if no ellipsis is desired.
+ */
+ public PatternAbbreviatorFragment(final int charCount, final char ellipsis) {
+ this.charCount = charCount;
+ this.ellipsis = ellipsis;
+ }
+
+ /**
+ * Abbreviate element of name.
+ *
+ * @param buf buffer to receive element.
+ * @param startPos starting index of name element.
+ * @return starting index of next element.
+ */
+ public int abbreviate(final StringBuffer buf, final int startPos) {
+ int nextDot = buf.toString().indexOf(".", startPos);
+
+ if (nextDot != -1) {
+ if ((nextDot - startPos) > charCount) {
+ buf.delete(startPos + charCount, nextDot);
+ nextDot = startPos + charCount;
+
+ if (ellipsis != '\0') {
+ buf.insert(nextDot, ellipsis);
+ nextDot++;
+ }
+ }
+
+ nextDot++;
+ }
+
+ return nextDot;
+ }
+ }
+
+ /**
+ * Default (no abbreviation) abbreviator.
+ */
+ private static final NameAbbreviator DEFAULT = new NOPAbbreviator();
+
+ /**
+ * Gets an abbreviator.
+ *
+ * For example, "%logger{2}" will output only 2 elements of the logger name, %logger{-2} will drop 2 elements from the
+ * logger name, "%logger{1.}" will output only the first character of the non-final elements in the name,
+ * "%logger{1~.2~} will output the first character of the first element, two characters of the second and subsequent
+ * elements and will use a tilde to indicate abbreviated characters.
+ *
+ * @param pattern abbreviation pattern.
+ * @return abbreviator, will not be null.
+ */
+ public static NameAbbreviator getAbbreviator(final String pattern) {
+ if (pattern.length() > 0) {
+ // if pattern is just spaces and numbers then
+ // use MaxElementAbbreviator
+ final String trimmed = pattern.trim();
+
+ if (trimmed.length() == 0) {
+ return DEFAULT;
+ }
+
+ int i = 0;
+ if (trimmed.length() > 0) {
+ if (trimmed.charAt(0) == '-') {
+ i++;
+ }
+ for (; (i < trimmed.length()) && (trimmed.charAt(i) >= '0') && (trimmed.charAt(i) <= '9'); i++) {
+ }
+ }
+
+ //
+ // if all blanks and digits
+ //
+ if (i == trimmed.length()) {
+ final int elements = Integer.parseInt(trimmed);
+ if (elements >= 0) {
+ return new MaxElementAbbreviator(elements);
+ } else {
+ return new DropElementAbbreviator(-elements);
+ }
+ }
+
+ final ArrayList fragments = new ArrayList(5);
+ char ellipsis;
+ int charCount;
+ int pos = 0;
+
+ while ((pos < trimmed.length()) && (pos >= 0)) {
+ int ellipsisPos = pos;
+
+ if (trimmed.charAt(pos) == '*') {
+ charCount = Integer.MAX_VALUE;
+ ellipsisPos++;
+ } else {
+ if ((trimmed.charAt(pos) >= '0') && (trimmed.charAt(pos) <= '9')) {
+ charCount = trimmed.charAt(pos) - '0';
+ ellipsisPos++;
+ } else {
+ charCount = 0;
+ }
+ }
+
+ ellipsis = '\0';
+
+ if (ellipsisPos < trimmed.length()) {
+ ellipsis = trimmed.charAt(ellipsisPos);
+
+ if (ellipsis == '.') {
+ ellipsis = '\0';
+ }
+ }
+
+ fragments.add(new PatternAbbreviatorFragment(charCount, ellipsis));
+ pos = trimmed.indexOf(".", pos);
+
+ if (pos == -1) {
+ break;
+ }
+
+ pos++;
+ }
+
+ return new PatternAbbreviator(fragments);
+ }
+
+ //
+ // no matching abbreviation, return defaultAbbreviator
+ //
+ return DEFAULT;
+ }
+
+ /**
+ * Gets default abbreviator.
+ *
+ * @return default abbreviator.
+ */
+ public static NameAbbreviator getDefaultAbbreviator() {
+ return DEFAULT;
+ }
+
+ /**
+ * Abbreviates a name in a StringBuffer.
+ *
+ * @param nameStart starting position of name in buf.
+ * @param buf buffer, may not be null.
+ */
+ public abstract void abbreviate(final int nameStart, final StringBuffer buf);
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/MapRewritePolicy.java b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/MapRewritePolicy.java
new file mode 100644
index 00000000000..f16ed3803bd
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/MapRewritePolicy.java
@@ -0,0 +1,123 @@
+/*
+ * 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.rewrite;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.bridge.LogEventAdapter;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.message.MapMessage;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.util.SortedArrayStringMap;
+
+/**
+ * This policy rewrites events where the message of the
+ * original event implements java.util.Map.
+ * All other events are passed through unmodified.
+ * If the map contains a "message" entry, the value will be
+ * used as the message for the rewritten event. The rewritten
+ * event will have a property set that is the combination of the
+ * original property set and the other members of the message map.
+ * If both the original property set and the message map
+ * contain the same entry, the value from the message map
+ * will overwrite the original property set.
+ *
+ * The combination of the RewriteAppender and this policy
+ * performs the same actions as the MapFilter from log4j 1.3.
+ *
+ */
+public class MapRewritePolicy implements RewritePolicy {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public LoggingEvent rewrite(final LoggingEvent source) {
+ Object msg = source.getMessage();
+ if (msg instanceof MapMessage || msg instanceof Map) {
+ Map props = source.getProperties() != null ? new HashMap<>(source.getProperties())
+ : new HashMap<>();
+ @SuppressWarnings("unchecked")
+ Map eventProps = msg instanceof Map ? (Map) msg : ((MapMessage) msg).getData();
+ //
+ // if the map sent in the logging request
+ // has "message" entry, use that as the message body
+ // otherwise, use the entire map.
+ //
+ Message newMessage = null;
+ Object newMsg = eventProps.get("message");
+ if (newMsg != null) {
+ newMessage = new SimpleMessage(newMsg.toString());
+ for (Map.Entry entry : eventProps.entrySet()) {
+ if (!("message".equals(entry.getKey()))) {
+ props.put(entry.getKey(), entry.getValue().toString());
+ }
+ }
+ } else {
+ return source;
+ }
+
+ LogEvent event;
+ if (source instanceof LogEventAdapter) {
+ event = new Log4jLogEvent.Builder(((LogEventAdapter) source).getEvent())
+ .setMessage(newMessage)
+ .setContextData(new SortedArrayStringMap(props))
+ .build();
+ } else {
+ LocationInfo info = source.getLocationInformation();
+ StackTraceElement element = new StackTraceElement(info.getClassName(), info.getMethodName(),
+ info.getFileName(), Integer.parseInt(info.getLineNumber()));
+ Thread thread = getThread(source.getThreadName());
+ long threadId = thread != null ? thread.getId() : 0;
+ int threadPriority = thread != null ? thread.getPriority() : 0;
+ event = Log4jLogEvent.newBuilder()
+ .setContextData(new SortedArrayStringMap(props))
+ .setLevel(OptionConverter.convertLevel(source.getLevel()))
+ .setLoggerFqcn(source.getFQNOfLoggerClass())
+ .setMarker(null)
+ .setMessage(newMessage)
+ .setSource(element)
+ .setLoggerName(source.getLoggerName())
+ .setThreadName(source.getThreadName())
+ .setThreadId(threadId)
+ .setThreadPriority(threadPriority)
+ .setThrown(source.getThrowableInformation().getThrowable())
+ .setTimeMillis(source.getTimeStamp())
+ .setNanoTime(0)
+ .setThrownProxy(null)
+ .build();
+ }
+ return new LogEventAdapter(event);
+ }
+ return source;
+
+ }
+
+ private Thread getThread(String name) {
+ for (Thread thread : Thread.getAllStackTraces().keySet()) {
+ if (thread.getName().equals(name)) {
+ return thread;
+ }
+ }
+ return null;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/PropertyRewritePolicy.java b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/PropertyRewritePolicy.java
new file mode 100644
index 00000000000..a84deacb4d6
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/PropertyRewritePolicy.java
@@ -0,0 +1,126 @@
+/*
+ * 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.rewrite;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.apache.log4j.bridge.LogEventAdapter;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.util.SortedArrayStringMap;
+
+/**
+ * This policy rewrites events by adding
+ * a user-specified list of properties to the event.
+ * Existing properties are not modified.
+ *
+ * The combination of the RewriteAppender and this policy
+ * performs the same actions as the PropertyFilter from log4j 1.3.
+ *
+ */
+public class PropertyRewritePolicy implements RewritePolicy {
+ private Map properties = Collections.EMPTY_MAP;
+
+ public PropertyRewritePolicy() {
+ }
+
+ /**
+ * Set a string representing the property name/value pairs.
+ *
+ * Form:
+ *
+ *
+ * propname1=propvalue1,propname2=propvalue2
+ *
+ *
+ * @param properties The properties.
+ */
+ public void setProperties(String properties) {
+ Map newMap = new HashMap<>();
+ StringTokenizer pairs = new StringTokenizer(properties, ",");
+ while (pairs.hasMoreTokens()) {
+ StringTokenizer entry = new StringTokenizer(pairs.nextToken(), "=");
+ newMap.put(entry.nextElement().toString().trim(), entry.nextElement().toString().trim());
+ }
+ synchronized (this) {
+ this.properties = newMap;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public LoggingEvent rewrite(final LoggingEvent source) {
+ if (!properties.isEmpty()) {
+ Map rewriteProps = source.getProperties() != null ? new HashMap<>(source.getProperties())
+ : new HashMap<>();
+ for (Map.Entry entry : properties.entrySet()) {
+ if (!rewriteProps.containsKey(entry.getKey())) {
+ rewriteProps.put(entry.getKey(), entry.getValue());
+ }
+ }
+ LogEvent event;
+ if (source instanceof LogEventAdapter) {
+ event = new Log4jLogEvent.Builder(((LogEventAdapter) source).getEvent())
+ .setContextData(new SortedArrayStringMap(rewriteProps))
+ .build();
+ } else {
+ LocationInfo info = source.getLocationInformation();
+ StackTraceElement element = new StackTraceElement(info.getClassName(), info.getMethodName(),
+ info.getFileName(), Integer.parseInt(info.getLineNumber()));
+ Thread thread = getThread(source.getThreadName());
+ long threadId = thread != null ? thread.getId() : 0;
+ int threadPriority = thread != null ? thread.getPriority() : 0;
+ event = Log4jLogEvent.newBuilder()
+ .setContextData(new SortedArrayStringMap(rewriteProps))
+ .setLevel(OptionConverter.convertLevel(source.getLevel()))
+ .setLoggerFqcn(source.getFQNOfLoggerClass())
+ .setMarker(null)
+ .setMessage(new SimpleMessage(source.getRenderedMessage()))
+ .setSource(element)
+ .setLoggerName(source.getLoggerName())
+ .setThreadName(source.getThreadName())
+ .setThreadId(threadId)
+ .setThreadPriority(threadPriority)
+ .setThrown(source.getThrowableInformation().getThrowable())
+ .setTimeMillis(source.getTimeStamp())
+ .setNanoTime(0)
+ .setThrownProxy(null)
+ .build();
+ }
+ return new LogEventAdapter(event);
+ }
+ return source;
+ }
+
+ private Thread getThread(String name) {
+ for (Thread thread : Thread.getAllStackTraces().keySet()) {
+ if (thread.getName().equals(name)) {
+ return thread;
+ }
+ }
+ return null;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/RewritePolicy.java b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/RewritePolicy.java
new file mode 100644
index 00000000000..9570218e787
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/RewritePolicy.java
@@ -0,0 +1,36 @@
+/*
+ * 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.rewrite;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * This interface is implemented to provide a rewrite
+ * strategy for RewriteAppender. RewriteAppender will
+ * call the rewrite method with a source logging event.
+ * The strategy may return that event, create a new event
+ * or return null to suppress the logging request.
+ */
+public interface RewritePolicy {
+ /**
+ * Rewrite a logging event.
+ * @param source a logging event that may be returned or
+ * used to create a new logging event.
+ * @return a logging event or null to suppress processing.
+ */
+ LoggingEvent rewrite(final LoggingEvent source);
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/AppenderAttachable.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/AppenderAttachable.java
new file mode 100644
index 00000000000..261c5bb84a9
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/AppenderAttachable.java
@@ -0,0 +1,78 @@
+/*
+ * 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.spi;
+
+import java.util.Enumeration;
+
+import org.apache.log4j.Appender;
+
+/**
+ * Interface for attaching appenders to objects.
+ */
+public interface AppenderAttachable {
+
+ /**
+ * Add an appender.
+ * @param newAppender The Appender to add.
+ */
+ void addAppender(Appender newAppender);
+
+ /**
+ * Get all previously added appenders as an Enumeration.
+ * @return The Enumeration of the Appenders.
+ */
+ Enumeration getAllAppenders();
+
+ /**
+ * Get an appender by name.
+ * @param name The name of the Appender.
+ * @return The Appender.
+ */
+ Appender getAppender(String name);
+
+
+ /**
+ * Returns true if the specified appender is in list of
+ * attached, false otherwise.
+ * @param appender The Appender to check.
+ * @return true if the Appender is attached.
+ *
+ * @since 1.2
+ */
+ boolean isAttached(Appender appender);
+
+ /**
+ * Remove all previously added appenders.
+ */
+ void removeAllAppenders();
+
+
+ /**
+ * Remove the appender passed as parameter from the list of appenders.
+ * @param appender The Appender to remove.
+ */
+ void removeAppender(Appender appender);
+
+
+ /**
+ * Remove the appender with the name passed as parameter from the
+ * list of appenders.
+ * @param name The name of the Appender to remove.
+ */
+ void removeAppender(String name);
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/Configurator.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/Configurator.java
new file mode 100644
index 00000000000..15b9c6eda9a
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/Configurator.java
@@ -0,0 +1,60 @@
+/*
+ * 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.spi;
+
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * Log4j 1.x Configurator interface.
+ */
+public interface Configurator {
+
+ /**
+ * Special level value signifying inherited behavior. The current value of this string constant is inherited.
+ * {@link #NULL} is a synonym.
+ */
+ public static final String INHERITED = "inherited";
+
+ /**
+ * Special level signifying inherited behavior, same as {@link #INHERITED}. The current value of this string constant
+ * is null.
+ */
+ public static final String NULL = "null";
+
+ /**
+ * Interpret a resource pointed by a InputStream and set up log4j accordingly.
+ *
+ * The configuration is done relative to the hierarchy parameter.
+ *
+ * @param inputStream The InputStream to parse
+ * @param loggerRepository The hierarchy to operation upon.
+ *
+ * @since 1.2.17
+ */
+ void doConfigure(InputStream inputStream, final LoggerRepository loggerRepository);
+
+ /**
+ * Interpret a resource pointed by a URL and set up log4j accordingly.
+ *
+ * The configuration is done relative to the hierarchy parameter.
+ *
+ * @param url The URL to parse
+ * @param loggerRepository The hierarchy to operation upon.
+ */
+ void doConfigure(URL url, final LoggerRepository loggerRepository);
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/DefaultRepositorySelector.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/DefaultRepositorySelector.java
new file mode 100644
index 00000000000..e4313732677
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/DefaultRepositorySelector.java
@@ -0,0 +1,32 @@
+/*
+ * 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.spi;
+
+public class DefaultRepositorySelector implements RepositorySelector {
+
+ final LoggerRepository repository;
+
+ public DefaultRepositorySelector(final LoggerRepository repository) {
+ this.repository = repository;
+ }
+
+ @Override
+ public LoggerRepository getLoggerRepository() {
+ return repository;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ErrorCode.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ErrorCode.java
new file mode 100644
index 00000000000..752b077a849
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ErrorCode.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.spi;
+
+
+/**
+ This interface defines commonly encountered error codes.
+ */
+public interface ErrorCode {
+
+ public final int GENERIC_FAILURE = 0;
+ public final int WRITE_FAILURE = 1;
+ public final int FLUSH_FAILURE = 2;
+ public final int CLOSE_FAILURE = 3;
+ public final int FILE_OPEN_FAILURE = 4;
+ public final int MISSING_LAYOUT = 5;
+ public final int ADDRESS_PARSE_FAILURE = 6;
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ErrorHandler.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ErrorHandler.java
index 2e6410391b7..f3b11f95169 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ErrorHandler.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ErrorHandler.java
@@ -49,7 +49,7 @@ public interface ErrorHandler {
/**
* Equivalent to the {@link #error(String, Exception, int,
- * LoggingEvent)} with the the event parameter set to
+ * LoggingEvent)} with the event parameter set to
* null.
*
* @param message The message associated with the error.
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/Filter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/Filter.java
index 9bba50cbcf8..91917abeae9 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/Filter.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/Filter.java
@@ -21,6 +21,16 @@
*/
public abstract class Filter {
+ static {
+ boolean temp;
+ try {
+ temp = Class.forName("org.apache.logging.log4j.core.Filter") != null;
+ } catch (Exception | LinkageError e) {
+ temp = false;
+ }
+ isCorePresent = temp;
+ }
+
/**
* The log event must be dropped immediately without consulting
* with the remaining filters, if any, in the chain.
@@ -47,11 +57,14 @@ public abstract class Filter {
@Deprecated
public Filter next;
+ private static final boolean isCorePresent;
+
/**
* Usually filters options become active when set. We provide a
* default do-nothing implementation for convenience.
*/
public void activateOptions() {
+ // noop
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LocationInfo.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LocationInfo.java
new file mode 100644
index 00000000000..4b20f2deb0f
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LocationInfo.java
@@ -0,0 +1,135 @@
+/*
+ * 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.spi;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * The internal representation of caller location information.
+ *
+ * @since 0.8.3
+ */
+public class LocationInfo implements Serializable {
+
+ /**
+ * When location information is not available the constant NA is returned. Current value of this string
+ * constant is ?.
+ */
+ public final static String NA = "?";
+
+ static final long serialVersionUID = -1325822038990805636L;
+
+ private final StackTraceElement stackTraceElement;
+
+ public String fullInfo;
+
+ /**
+ * Constructs a new instance.
+ */
+ public LocationInfo(final StackTraceElement stackTraceElement) {
+ this.stackTraceElement = Objects.requireNonNull(stackTraceElement, "stackTraceElement");
+ this.fullInfo = stackTraceElement.toString();
+ }
+
+ /**
+ * Constructs a new instance.
+ *
+ * @param file source file name
+ * @param declaringClass class name
+ * @param methodName method
+ * @param line source line number
+ *
+ * @since 1.2.15
+ */
+ public LocationInfo(final String file, final String declaringClass, final String methodName, final String line) {
+ this(new StackTraceElement(declaringClass, methodName, file, Integer.parseInt(line)));
+ }
+
+ /**
+ * Constructs a new instance.
+ */
+ public LocationInfo(final Throwable throwable, final String fqnOfCallingClass) {
+ String declaringClass = null, methodName = null, file = null, line = null;
+ if (throwable != null && fqnOfCallingClass != null) {
+ final StackTraceElement[] elements = throwable.getStackTrace();
+ String prevClass = NA;
+ for (int i = elements.length - 1; i >= 0; i--) {
+ final String thisClass = elements[i].getClassName();
+ if (fqnOfCallingClass.equals(thisClass)) {
+ final int caller = i + 1;
+ if (caller < elements.length) {
+ declaringClass = prevClass;
+ methodName = elements[caller].getMethodName();
+ file = elements[caller].getFileName();
+ if (file == null) {
+ file = NA;
+ }
+ final int lineNo = elements[caller].getLineNumber();
+ if (lineNo < 0) {
+ line = NA;
+ } else {
+ line = String.valueOf(lineNo);
+ }
+ final StringBuilder builder = new StringBuilder();
+ builder.append(declaringClass);
+ builder.append(".");
+ builder.append(methodName);
+ builder.append("(");
+ builder.append(file);
+ builder.append(":");
+ builder.append(line);
+ builder.append(")");
+ this.fullInfo = builder.toString();
+ }
+ break;
+ }
+ prevClass = thisClass;
+ }
+ }
+ this.stackTraceElement = new StackTraceElement(declaringClass, methodName, file, Integer.parseInt(line));
+ this.fullInfo = stackTraceElement.toString();
+ }
+
+ /**
+ * Gets the fully qualified class name of the caller making the logging request.
+ */
+ public String getClassName() {
+ return stackTraceElement.getClassName();
+ }
+
+ /**
+ * Gets the file name of the caller.
+ */
+ public String getFileName() {
+ return stackTraceElement.getFileName();
+ }
+
+ /**
+ * Gets the line number of the caller.
+ */
+ public String getLineNumber() {
+ return Integer.toString(stackTraceElement.getLineNumber());
+ }
+
+ /**
+ * Gets the method name of the caller.
+ */
+ public String getMethodName() {
+ return stackTraceElement.getMethodName();
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggerFactory.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggerFactory.java
index e2f37080572..69c30db9dd4 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggerFactory.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggerFactory.java
@@ -19,7 +19,6 @@
import org.apache.log4j.Logger;
/**
- *
* Implement this interface to create new instances of Logger or a sub-class of Logger.
*
*
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggingEvent.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggingEvent.java
index 5f4b1727b90..73dd528efca 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggingEvent.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggingEvent.java
@@ -16,8 +16,216 @@
*/
package org.apache.log4j.spi;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.log4j.Category;
+import org.apache.log4j.Level;
+import org.apache.log4j.Priority;
+import org.apache.log4j.bridge.LogEventAdapter;
+
/**
- * No-op version of Log4j 1.2 LoggingEvent.
+ * No-op version of Log4j 1.2 LoggingEvent. This class is not directly used by Log4j 1.x clients but is used by
+ * the Log4j 2 LogEvent adapter to be compatible with Log4j 1.x components.
*/
public class LoggingEvent {
+
+ /**
+ Returns the time when the application started, in milliseconds
+ elapsed since 01.01.1970.
+ @return the JVM start time.
+ */
+ public static long getStartTime() {
+ return LogEventAdapter.getStartTime();
+ }
+
+ /**
+ * The number of milliseconds elapsed from 1/1/1970 until logging event was created.
+ */
+ public final long timeStamp;
+
+ /**
+ * Constructs a new instance.
+ */
+ public LoggingEvent() {
+ timeStamp = System.currentTimeMillis();
+ }
+
+ /**
+ * Create new instance.
+ *
+ * @since 1.2.15
+ * @param fqnOfCategoryClass Fully qualified class name of Logger implementation.
+ * @param logger The logger generating this event.
+ * @param timeStamp the timestamp of this logging event
+ * @param level The level of this event.
+ * @param message The message of this event.
+ * @param threadName thread name
+ * @param throwable The throwable of this event.
+ * @param ndc Nested diagnostic context
+ * @param info Location info
+ * @param properties MDC properties
+ */
+ public LoggingEvent(final String fqnOfCategoryClass, final Category logger, final long timeStamp, final Level level, final Object message,
+ final String threadName, final ThrowableInformation throwable, final String ndc, final LocationInfo info, final Map properties) {
+ this.timeStamp = timeStamp;
+ }
+
+ /**
+ * Instantiate a LoggingEvent from the supplied parameters.
+ *
+ *
+ * Except {@link #timeStamp} all the other fields of LoggingEvent are filled when actually needed.
+ *
+ *
+ * @param logger The logger generating this event.
+ * @param timeStamp the timestamp of this logging event
+ * @param level The level of this event.
+ * @param message The message of this event.
+ * @param throwable The throwable of this event.
+ */
+ public LoggingEvent(String fqnOfCategoryClass, Category logger, long timeStamp, Priority level, Object message, Throwable throwable) {
+ this.timeStamp = timeStamp;
+ }
+
+ /**
+ * Instantiate a LoggingEvent from the supplied parameters.
+ *
+ *
+ * Except {@link #timeStamp} all the other fields of LoggingEvent are filled when actually needed.
+ *
+ *
+ * @param logger The logger generating this event.
+ * @param level The level of this event.
+ * @param message The message of this event.
+ * @param throwable The throwable of this event.
+ */
+ public LoggingEvent(String fqnOfCategoryClass, Category logger, Priority level, Object message, Throwable throwable) {
+ timeStamp = System.currentTimeMillis();
+ }
+
+ public String getFQNOfLoggerClass() {
+ return null;
+ }
+
+ /**
+ * Return the level of this event. Use this form instead of directly
+ * accessing the level field.
+ * @return Always returns null.
+ */
+ public Level getLevel() {
+ return null;
+ }
+
+ /**
+ Set the location information for this logging event. The collected
+ information is cached for future use.
+ @return Always returns null.
+ */
+ public LocationInfo getLocationInformation() {
+ return null;
+ }
+
+ /**
+ * Gets the logger of the event.
+ * Use should be restricted to cloning events.
+ * @return Always returns null.
+ * @since 1.2.15
+ */
+ public Category getLogger() {
+ return null;
+ }
+
+ /**
+ * Return the name of the logger. Use this form instead of directly
+ * accessing the categoryName field.
+ * @return Always returns null.
+ */
+ public String getLoggerName() {
+ return null;
+ }
+
+ public
+ Object getMDC(String key) {
+ return null;
+ }
+
+ /**
+ Obtain a copy of this thread's MDC prior to serialization or
+ asynchronous logging.
+ */
+ public void getMDCCopy() {
+ }
+
+ /**
+ Return the message for this logging event.
+
+
Before serialization, the returned object is the message
+ passed by the user to generate the logging event. After
+ serialization, the returned value equals the String form of the
+ message possibly after object rendering.
+ @return Always returns null.
+ @since 1.1 */
+ public
+ Object getMessage() {
+ return null;
+ }
+
+ public
+ String getNDC() {
+ return null;
+ }
+
+ public Map getProperties() {
+ return null;
+ }
+
+ public String getProperty(final String key) {
+ return null;
+ }
+
+ public Set getPropertyKeySet() {
+ return null;
+ }
+
+ public String getRenderedMessage() {
+ return null;
+ }
+
+ public String getThreadName() {
+ return null;
+ }
+
+ /**
+ Returns the throwable information contained within this
+ event. May be null if there is no such information.
+
+
Note that the {@link Throwable} object contained within a
+ {@link ThrowableInformation} does not survive serialization.
+ @return Always returns null.
+ @since 1.1 */
+ public ThrowableInformation getThrowableInformation() {
+ return null;
+ }
+
+ /**
+ Return this event's throwable's string[] representation.
+ @return Always returns null.
+ */
+ public String[] getThrowableStrRep() {
+ return null;
+ }
+
+ public long getTimeStamp() {
+ return 0;
+ }
+
+ public Object removeProperty(String propName) {
+ return null;
+ }
+
+ public void setProperty(final String propName,
+ final String propValue) {
+
+ }
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/NOPLogger.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/NOPLogger.java
new file mode 100644
index 00000000000..0c61358783f
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/NOPLogger.java
@@ -0,0 +1,268 @@
+/*
+ * 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.spi;
+
+import java.util.Enumeration;
+import java.util.ResourceBundle;
+import java.util.Vector;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.Priority;
+
+/**
+ * No-operation implementation of Logger used by NOPLoggerRepository.
+ *
+ * @since 1.2.15
+ */
+public final class NOPLogger extends Logger {
+
+ /**
+ * Create instance of Logger.
+ *
+ * @param repo repository, may not be null.
+ * @param name name, may not be null, use "root" for root logger.
+ */
+ public NOPLogger(final NOPLoggerRepository repo, final String name) {
+ super(name);
+ this.repository = repo;
+ this.level = Level.OFF;
+ this.parent = this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addAppender(final Appender newAppender) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void assertLog(final boolean assertion, final String msg) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void callAppenders(final LoggingEvent event) {
+ // NOP
+ }
+
+ void closeNestedAppenders() {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void debug(final Object message) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void debug(final Object message, final Throwable t) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void error(final Object message) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void error(final Object message, final Throwable t) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void fatal(final Object message) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void fatal(final Object message, final Throwable t) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Enumeration getAllAppenders() {
+ return new Vector<>().elements();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Appender getAppender(final String name) {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Priority getChainedPriority() {
+ return getEffectiveLevel();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Level getEffectiveLevel() {
+ return Level.OFF;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ResourceBundle getResourceBundle() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void info(final Object message) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void info(final Object message, final Throwable t) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isAttached(final Appender appender) {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isDebugEnabled() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isEnabledFor(final Priority level) {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isInfoEnabled() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isTraceEnabled() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void l7dlog(final Priority priority, final String key, final Object[] params, final Throwable t) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void l7dlog(final Priority priority, final String key, final Throwable t) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void log(final Priority priority, final Object message) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void log(final Priority priority, final Object message, final Throwable t) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void log(final String callerFQCN, final Priority level, final Object message, final Throwable t) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void removeAllAppenders() {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void removeAppender(final Appender appender) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void removeAppender(final String name) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setLevel(final Level level) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setPriority(final Priority priority) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setResourceBundle(final ResourceBundle bundle) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void trace(final Object message) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void trace(final Object message, final Throwable t) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void warn(final Object message) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void warn(final Object message, final Throwable t) {
+ // NOP
+ }
+
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/NOPLoggerRepository.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/NOPLoggerRepository.java
new file mode 100644
index 00000000000..f64996e9aad
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/NOPLoggerRepository.java
@@ -0,0 +1,154 @@
+/*
+ * 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.spi;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Category;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+
+/**
+ * No-operation implementation of LoggerRepository which is used when LogManager.repositorySelector is erroneously
+ * nulled during class reloading.
+ *
+ * @since 1.2.15
+ */
+public final class NOPLoggerRepository implements LoggerRepository {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addHierarchyEventListener(final HierarchyEventListener listener) {
+ // NOP
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void emitNoAppenderWarning(final Category cat) {
+ // NOP
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Logger exists(final String name) {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void fireAddAppenderEvent(final Category logger, final Appender appender) {
+ // NOP
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Enumeration getCurrentCategories() {
+ return getCurrentLoggers();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Enumeration getCurrentLoggers() {
+ return new Vector<>().elements();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Logger getLogger(final String name) {
+ return new NOPLogger(this, name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Logger getLogger(final String name, final LoggerFactory factory) {
+ return new NOPLogger(this, name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Logger getRootLogger() {
+ return new NOPLogger(this, "root");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Level getThreshold() {
+ return Level.OFF;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isDisabled(final int level) {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void resetConfiguration() {
+ // NOP
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setThreshold(final Level level) {
+ // NOP
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setThreshold(final String val) {
+ // NOP
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void shutdown() {
+ // NOP
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RendererSupport.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RendererSupport.java
new file mode 100644
index 00000000000..0a0b7401668
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RendererSupport.java
@@ -0,0 +1,29 @@
+/*
+ * 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.spi;
+
+import org.apache.log4j.or.ObjectRenderer;
+import org.apache.log4j.or.RendererMap;
+
+public interface RendererSupport {
+
+ public RendererMap getRendererMap();
+
+ public void setRenderer(Class renderedClass, ObjectRenderer renderer);
+
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RepositorySelector.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RepositorySelector.java
index 95666594a85..86434a5381a 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RepositorySelector.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RepositorySelector.java
@@ -17,26 +17,26 @@
package org.apache.log4j.spi;
/**
-
- The LogManager uses one (and only one)
- RepositorySelector implementation to select the
- {@link org.apache.log4j.spi.LoggerRepository} for a particular application context.
-
-
It is the responsibility of the RepositorySelector
- implementation to track the application context. Log4j makes no
- assumptions about the application context or on its management.
-
-
See also {@link org.apache.log4j.LogManager LogManager}.
-
- @since 1.2
-
+ *
+ * The LogManager uses one (and only one) RepositorySelector implementation to select the
+ * {@link LoggerRepository} for a particular application context.
+ *
+ *
+ * It is the responsibility of the RepositorySelector implementation to track the application context.
+ * Log4j makes no assumptions about the application context or on its management.
+ *
+ *
+ *
+ * See also {@link org.apache.log4j.LogManager LogManager}.
+ *
+ * @since 1.2
*/
public interface RepositorySelector {
/**
- * Returns a {@link org.apache.log4j.spi.LoggerRepository} depending on the
- * context. Implementers must make sure that a valid (non-null)
+ * Gets a {@link LoggerRepository} depending on the context. Implementers must make sure that a valid (non-null)
* LoggerRepository is returned.
+ *
* @return a LoggerRepository.
*/
LoggerRepository getLoggerRepository();
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RootLogger.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RootLogger.java
new file mode 100644
index 00000000000..79db4915b50
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RootLogger.java
@@ -0,0 +1,65 @@
+/*
+ * 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.spi;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.helpers.LogLog;
+
+/**
+ * RootLogger sits at the top of the logger hierarchy. It is a regular logger except that it provides several guarantees.
+ *
+ * First, it cannot be assigned a null level. Second, since root logger cannot have a parent, the
+ * {@link #getChainedLevel} method always returns the value of the level field without walking the hierarchy.
+ *
+ */
+public final class RootLogger extends Logger {
+
+ /**
+ * The root logger names itself as "root". However, the root logger cannot be retrieved by name.
+ */
+ public RootLogger(Level level) {
+ // The Log4j 1 root logger name is "root".
+ // The Log4j 2 root logger name is "".
+ super("root");
+ setLevel(level);
+ }
+
+ /**
+ * Gets the assigned level value without walking the logger hierarchy.
+ */
+ public final Level getChainedLevel() {
+ return getLevel();
+ }
+
+ /**
+ * Sets the log level.
+ *
+ * Setting a null value to the level of the root logger may have catastrophic results. We prevent this here.
+ *
+ * @since 0.8.3
+ */
+ public final void setLevel(Level level) {
+ if (level == null) {
+ LogLog.error("You have tried to set a null level to root.", new Throwable());
+ } else {
+ super.setLevel(level);
+ }
+ }
+
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableInformation.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableInformation.java
new file mode 100644
index 00000000000..6726e0017e7
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableInformation.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.spi;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.List;
+
+import org.apache.log4j.Category;
+import org.apache.logging.log4j.util.Strings;
+
+/**
+ * Log4j's internal representation of throwables.
+ */
+public class ThrowableInformation implements Serializable {
+
+ static final long serialVersionUID = -4748765566864322735L;
+
+ private transient Throwable throwable;
+ private transient Category category;
+ private String[] rep;
+ private static final Method TO_STRING_LIST;
+
+ static {
+ Method method = null;
+ try {
+ final Class> throwables = Class.forName("org.apache.logging.log4j.core.util.Throwables");
+ method = throwables.getMethod("toStringList", Throwable.class);
+ } catch (ClassNotFoundException | NoSuchMethodException ex) {
+ // Ignore the exception if Log4j-core is not present.
+ }
+ TO_STRING_LIST = method;
+ }
+
+ /**
+ * Constructs new instance.
+ *
+ * @since 1.2.15
+ * @param r String representation of throwable.
+ */
+ public ThrowableInformation(final String[] r) {
+ this.rep = r != null ? r.clone() : null;
+ }
+
+ /**
+ * Constructs new instance.
+ */
+ public ThrowableInformation(final Throwable throwable) {
+ this.throwable = throwable;
+ }
+
+ /**
+ * Constructs a new instance.
+ *
+ * @param throwable throwable, may not be null.
+ * @param category category used to obtain ThrowableRenderer, may be null.
+ * @since 1.2.16
+ */
+ public ThrowableInformation(final Throwable throwable, final Category category) {
+ this(throwable);
+ this.category = category;
+ this.rep = null;
+ }
+
+ public Throwable getThrowable() {
+ return throwable;
+ }
+
+ public synchronized String[] getThrowableStrRep() {
+ if (TO_STRING_LIST != null && throwable != null) {
+ try {
+ @SuppressWarnings("unchecked")
+ final List elements = (List) TO_STRING_LIST.invoke(null, throwable);
+ if (elements != null) {
+ return elements.toArray(Strings.EMPTY_ARRAY);
+ }
+ } catch (final ReflectiveOperationException ex) {
+ // Ignore the exception.
+ }
+ }
+ return rep;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableRenderer.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableRenderer.java
new file mode 100644
index 00000000000..e2e87a156b2
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableRenderer.java
@@ -0,0 +1,34 @@
+/*
+ * 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.spi;
+
+/**
+ * Implemented by classes that render instances of java.lang.Throwable (exceptions and errors) into a string
+ * representation.
+ *
+ * @since 1.2.16
+ */
+public interface ThrowableRenderer {
+
+ /**
+ * Render Throwable.
+ *
+ * @param t throwable, may not be null.
+ * @return String representation.
+ */
+ public String[] doRender(Throwable t);
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableRendererSupport.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableRendererSupport.java
new file mode 100644
index 00000000000..37b17e04519
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableRendererSupport.java
@@ -0,0 +1,38 @@
+/*
+ * 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.spi;
+
+/**
+ * Implemented by logger repositories that support configurable rendering of Throwables.
+ *
+ * @since 1.2.16
+ */
+public interface ThrowableRendererSupport {
+ /**
+ * Get throwable renderer.
+ *
+ * @return throwable renderer, may be null.
+ */
+ ThrowableRenderer getThrowableRenderer();
+
+ /**
+ * Set throwable renderer.
+ *
+ * @param renderer renderer, may be null.
+ */
+ void setThrowableRenderer(ThrowableRenderer renderer);
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/TriggeringEventEvaluator.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/TriggeringEventEvaluator.java
new file mode 100644
index 00000000000..96e86afdcdd
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/TriggeringEventEvaluator.java
@@ -0,0 +1,40 @@
+/*
+ * 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.spi;
+
+/**
+ * Implementors decide when to perform an appender specific action.
+ *
+ *
+ * For example, the {@code org.apache.log4j.net.SMTPAppender} sends an email when the
+ * {@link #isTriggeringEvent(LoggingEvent)} method returns {@code true} and adds the event to an internal buffer when
+ * the returned result is {@code false}.
+ *
+ *
+ * @since version 1.0
+ */
+public interface TriggeringEventEvaluator {
+
+ /**
+ * Tests if this the triggering event.
+ *
+ * @param event The vent to test.
+ * @return Whether this the triggering event.
+ */
+ public boolean isTriggeringEvent(LoggingEvent event);
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/varia/DenyAllFilter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/DenyAllFilter.java
new file mode 100644
index 00000000000..4646bdaf4f1
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/DenyAllFilter.java
@@ -0,0 +1,65 @@
+/*
+ * 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.varia;
+
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Denies all logging events.
+ *
+ *
+ * You can add this filter to the end of a filter chain to switch from the default "accept all unless instructed
+ * otherwise" filtering behavior to a "deny all unless instructed otherwise" behavior.
+ *
+ *
+ * @since 0.9.0
+ */
+public class DenyAllFilter extends Filter {
+
+ /**
+ * Always returns the integer constant {@link Filter#DENY} regardless of the {@link LoggingEvent} parameter.
+ *
+ * @param event The LoggingEvent to filter.
+ * @return Always returns {@link Filter#DENY}.
+ */
+ @Override
+ public int decide(final LoggingEvent event) {
+ return Filter.DENY;
+ }
+
+ /**
+ * Returns null as there are no options.
+ *
+ * @deprecated We now use JavaBeans introspection to configure components. Options strings are no longer needed.
+ */
+ @Deprecated
+ public String[] getOptionStrings() {
+ return null;
+ }
+
+ /**
+ * No options to set.
+ *
+ * @deprecated Use the setter method for the option directly instead of the generic setOption method.
+ */
+ @Deprecated
+ public void setOption(final String key, final String value) {
+ // noop
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/varia/FallbackErrorHandler.java b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/FallbackErrorHandler.java
new file mode 100644
index 00000000000..e2a18985067
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/FallbackErrorHandler.java
@@ -0,0 +1,126 @@
+/*
+ * 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.varia;
+
+import java.io.InterruptedIOException;
+import java.util.Vector;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Logger;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * An ErrorHandler with a secondary appender. This secondary appender takes over if the primary appender fails for
+ * whatever reason.
+ *
+ *
+ * The error message is printed on System.err, and logged in the new secondary appender.
+ *
+ */
+public class FallbackErrorHandler implements ErrorHandler {
+
+ Appender backup;
+ Appender primary;
+ Vector loggers;
+
+ public FallbackErrorHandler() {
+ // noop
+ }
+
+ /**
+ * No options to activate.
+ */
+ public void activateOptions() {
+ // noop
+ }
+
+ /**
+ * Print a the error message passed as parameter on System.err.
+ */
+ @Override
+ public void error(final String message) {
+ // if(firstTime) {
+ // LogLog.error(message);
+ // firstTime = false;
+ // }
+ }
+
+ /**
+ * Prints the message and the stack trace of the exception on System.err.
+ */
+ @Override
+ public void error(final String message, final Exception e, final int errorCode) {
+ error(message, e, errorCode, null);
+ }
+
+ /**
+ * Prints the message and the stack trace of the exception on System.err.
+ */
+ @Override
+ public void error(final String message, final Exception e, final int errorCode, final LoggingEvent event) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.debug("FB: The following error reported: " + message, e);
+ LogLog.debug("FB: INITIATING FALLBACK PROCEDURE.");
+ if (loggers != null) {
+ for (int i = 0; i < loggers.size(); i++) {
+ final Logger l = (Logger) loggers.elementAt(i);
+ LogLog.debug("FB: Searching for [" + primary.getName() + "] in logger [" + l.getName() + "].");
+ LogLog.debug("FB: Replacing [" + primary.getName() + "] by [" + backup.getName() + "] in logger [" + l.getName() + "].");
+ l.removeAppender(primary);
+ LogLog.debug("FB: Adding appender [" + backup.getName() + "] to logger " + l.getName());
+ l.addAppender(backup);
+ }
+ }
+ }
+
+ /**
+ * The appender to which this error handler is attached.
+ */
+ @Override
+ public void setAppender(final Appender primary) {
+ LogLog.debug("FB: Setting primary appender to [" + primary.getName() + "].");
+ this.primary = primary;
+ }
+
+ /**
+ * Set the backup appender.
+ */
+ @Override
+ public void setBackupAppender(final Appender backup) {
+ LogLog.debug("FB: Setting backup appender to [" + backup.getName() + "].");
+ this.backup = backup;
+ }
+
+ /**
+ * Adds the logger passed as parameter to the list of loggers that we need to search for in case of appender
+ * failure.
+ */
+ @Override
+ public void setLogger(final Logger logger) {
+ LogLog.debug("FB: Adding logger [" + logger.getName() + "].");
+ if (loggers == null) {
+ loggers = new Vector();
+ }
+ loggers.addElement(logger);
+ }
+
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/varia/LevelMatchFilter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/LevelMatchFilter.java
new file mode 100644
index 00000000000..c7693e41028
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/LevelMatchFilter.java
@@ -0,0 +1,91 @@
+/*
+ * 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.varia;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Simple filter based on level matching.
+ *
+ *
+ * The filter admits two options LevelToMatch and AcceptOnMatch. If there is an exact match between the
+ * value of the LevelToMatch option and the level of the {@link LoggingEvent}, then the {@link #decide} method
+ * returns {@link Filter#ACCEPT} in case the AcceptOnMatch option value is set to true, if it is
+ * false then {@link Filter#DENY} is returned. If there is no match, {@link Filter#NEUTRAL} is returned.
+ *
+ *
+ * @since 1.2
+ */
+public class LevelMatchFilter extends Filter {
+
+ /**
+ * Do we return ACCEPT when a match occurs. Default is true.
+ */
+ boolean acceptOnMatch = true;
+
+ /**
+ */
+ Level levelToMatch;
+
+ /**
+ * Return the decision of this filter.
+ *
+ * Returns {@link Filter#NEUTRAL} if the LevelToMatch option is not set or if there is not match. Otherwise, if
+ * there is a match, then the returned decision is {@link Filter#ACCEPT} if the AcceptOnMatch property is set to
+ * true. The returned decision is {@link Filter#DENY} if the AcceptOnMatch property is set to false.
+ *
+ */
+ @Override
+ public int decide(final LoggingEvent event) {
+ if (this.levelToMatch == null) {
+ return Filter.NEUTRAL;
+ }
+
+ boolean matchOccured = false;
+ if (this.levelToMatch.equals(event.getLevel())) {
+ matchOccured = true;
+ }
+
+ if (matchOccured) {
+ if (this.acceptOnMatch) {
+ return Filter.ACCEPT;
+ }
+ return Filter.DENY;
+ }
+ return Filter.NEUTRAL;
+ }
+
+ public boolean getAcceptOnMatch() {
+ return acceptOnMatch;
+ }
+
+ public String getLevelToMatch() {
+ return levelToMatch == null ? null : levelToMatch.toString();
+ }
+
+ public void setAcceptOnMatch(final boolean acceptOnMatch) {
+ this.acceptOnMatch = acceptOnMatch;
+ }
+
+ public void setLevelToMatch(final String level) {
+ levelToMatch = OptionConverter.toLevel(level, null);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/varia/LevelRangeFilter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/LevelRangeFilter.java
new file mode 100644
index 00000000000..fdd5c10ec45
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/LevelRangeFilter.java
@@ -0,0 +1,131 @@
+/*
+ * 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.varia;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * This is a very simple filter based on level matching, which can be used to reject messages with priorities outside a
+ * certain range.
+ *
+ * The filter admits three options LevelMin, LevelMax and AcceptOnMatch.
+ *
+ *
+ * If the level of the {@link LoggingEvent} is not between Min and Max (inclusive), then {@link Filter#DENY} is
+ * returned.
+ *
+ *
+ * If the Logging event level is within the specified range, then if AcceptOnMatch is true, {@link Filter#ACCEPT}
+ * is returned, and if AcceptOnMatch is false, {@link Filter#NEUTRAL} is returned.
+ *
+ *
+ * If LevelMinw is not defined, then there is no minimum acceptable level (ie a level is never rejected for
+ * being too "low"/unimportant). If LevelMax is not defined, then there is no maximum acceptable level (ie
+ * a level is never rejected for beeing too "high"/important).
+ *
+ *
+ * Refer to the {@link org.apache.log4j.AppenderSkeleton#setThreshold setThreshold} method available to all
+ * appenders extending {@link org.apache.log4j.AppenderSkeleton} for a more convenient way to filter out events by
+ * level.
+ *
+ */
+public class LevelRangeFilter extends Filter {
+
+ /**
+ * Do we return ACCEPT when a match occurs. Default is false, so that later filters get run by default
+ */
+ boolean acceptOnMatch;
+
+ Level levelMin;
+ Level levelMax;
+
+ /**
+ * Return the decision of this filter.
+ */
+ @Override
+ public int decide(final LoggingEvent event) {
+ if (this.levelMin != null) {
+ if (!event.getLevel().isGreaterOrEqual(levelMin)) {
+ // level of event is less than minimum
+ return Filter.DENY;
+ }
+ }
+
+ if (this.levelMax != null) {
+ if (event.getLevel().toInt() > levelMax.toInt()) {
+ // level of event is greater than maximum
+ // Alas, there is no Level.isGreater method. and using
+ // a combo of isGreaterOrEqual && !Equal seems worse than
+ // checking the int values of the level objects..
+ return Filter.DENY;
+ }
+ }
+
+ if (acceptOnMatch) {
+ // this filter set up to bypass later filters and always return
+ // accept if level in range
+ return Filter.ACCEPT;
+ }
+ // event is ok for this filter; allow later filters to have a look..
+ return Filter.NEUTRAL;
+ }
+
+ /**
+ * Get the value of the AcceptOnMatch option.
+ */
+ public boolean getAcceptOnMatch() {
+ return acceptOnMatch;
+ }
+
+ /**
+ * Get the value of the LevelMax option.
+ */
+ public Level getLevelMax() {
+ return levelMax;
+ }
+
+ /**
+ * Get the value of the LevelMin option.
+ */
+ public Level getLevelMin() {
+ return levelMin;
+ }
+
+ /**
+ * Set the AcceptOnMatch option.
+ */
+ public void setAcceptOnMatch(final boolean acceptOnMatch) {
+ this.acceptOnMatch = acceptOnMatch;
+ }
+
+ /**
+ * Set the LevelMax option.
+ */
+ public void setLevelMax(final Level levelMax) {
+ this.levelMax = levelMax;
+ }
+
+ /**
+ * Set the LevelMin option.
+ */
+ public void setLevelMin(final Level levelMin) {
+ this.levelMin = levelMin;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/varia/NullAppender.java b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/NullAppender.java
new file mode 100644
index 00000000000..cf6647523e4
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/NullAppender.java
@@ -0,0 +1,87 @@
+/*
+ * 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.varia;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * A NullAppender never outputs a message to any device.
+ */
+public class NullAppender extends AppenderSkeleton {
+
+ private static final NullAppender INSTANCE = new NullAppender();
+
+ /**
+ * Whenever you can, use this method to retreive an instance instead of instantiating a new one with new.
+ */
+ public static NullAppender getNullAppender() {
+ return INSTANCE;
+ }
+
+ public NullAppender() {
+ // noop
+ }
+
+ /**
+ * There are no options to acticate.
+ */
+ @Override
+ public void activateOptions() {
+ // noop
+ }
+
+ /**
+ * Does not do anything.
+ */
+ @Override
+ protected void append(final LoggingEvent event) {
+ // noop
+ }
+
+ @Override
+ public void close() {
+ // noop
+ }
+
+ /**
+ * Does not do anything.
+ */
+ @Override
+ public void doAppend(final LoggingEvent event) {
+ // noop
+ }
+
+ /**
+ * Whenever you can, use this method to retreive an instance instead of instantiating a new one with new.
+ *
+ * @deprecated Use getNullAppender instead. getInstance should have been static.
+ */
+ @Deprecated
+ public NullAppender getInstance() {
+ return INSTANCE;
+ }
+
+ /**
+ * NullAppenders do not need a layout.
+ */
+ @Override
+ public boolean requiresLayout() {
+ return false;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/varia/ReloadingPropertyConfigurator.java b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/ReloadingPropertyConfigurator.java
new file mode 100644
index 00000000000..a59d92ca67a
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/ReloadingPropertyConfigurator.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.varia;
+
+import java.io.InputStream;
+import java.net.URL;
+
+import org.apache.log4j.PropertyConfigurator;
+import org.apache.log4j.spi.Configurator;
+import org.apache.log4j.spi.LoggerRepository;
+
+public class ReloadingPropertyConfigurator implements Configurator {
+
+ PropertyConfigurator delegate = new PropertyConfigurator();
+
+ public ReloadingPropertyConfigurator() {
+ }
+
+ /**
+ * @since 1.2.17
+ */
+ @Override
+ public void doConfigure(final InputStream inputStream, final LoggerRepository repository) {
+ // noop
+ }
+
+ @Override
+ public void doConfigure(final URL url, final LoggerRepository repository) {
+ // noop
+ }
+
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/varia/StringMatchFilter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/StringMatchFilter.java
new file mode 100644
index 00000000000..c84dbce39d1
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/StringMatchFilter.java
@@ -0,0 +1,106 @@
+/*
+ * 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.varia;
+
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Simple filter based on string matching.
+ *
+ *
+ * The filter admits two options StringToMatch and AcceptOnMatch. If there is a match between the value of
+ * the StringToMatch option and the message of the {@link org.apache.log4j.spi.LoggingEvent}, then the
+ * {@link #decide(LoggingEvent)} method returns {@link org.apache.log4j.spi.Filter#ACCEPT} if the AcceptOnMatch
+ * option value is true, if it is false then {@link org.apache.log4j.spi.Filter#DENY} is returned. If there is no match,
+ * {@link org.apache.log4j.spi.Filter#NEUTRAL} is returned.
+ *
+ *
+ * @since 0.9.0
+ */
+public class StringMatchFilter extends Filter {
+
+ /**
+ * @deprecated Options are now handled using the JavaBeans paradigm. This constant is not longer needed and will be
+ * removed in the near term.
+ */
+ @Deprecated
+ public static final String STRING_TO_MATCH_OPTION = "StringToMatch";
+
+ /**
+ * @deprecated Options are now handled using the JavaBeans paradigm. This constant is not longer needed and will be
+ * removed in the near term.
+ */
+ @Deprecated
+ public static final String ACCEPT_ON_MATCH_OPTION = "AcceptOnMatch";
+
+ boolean acceptOnMatch = true;
+ String stringToMatch;
+
+ /**
+ * Returns {@link Filter#NEUTRAL} is there is no string match.
+ */
+ @Override
+ public int decide(final LoggingEvent event) {
+ final String msg = event.getRenderedMessage();
+ if (msg == null || stringToMatch == null) {
+ return Filter.NEUTRAL;
+ }
+ if (msg.indexOf(stringToMatch) == -1) {
+ return Filter.NEUTRAL;
+ }
+ return acceptOnMatch ? Filter.ACCEPT : Filter.DENY;
+ }
+
+ public boolean getAcceptOnMatch() {
+ return acceptOnMatch;
+ }
+
+ /**
+ * @deprecated We now use JavaBeans introspection to configure components. Options strings are no longer needed.
+ */
+ @Deprecated
+ public String[] getOptionStrings() {
+ return new String[] {STRING_TO_MATCH_OPTION, ACCEPT_ON_MATCH_OPTION};
+ }
+
+ public String getStringToMatch() {
+ return stringToMatch;
+ }
+
+ public void setAcceptOnMatch(final boolean acceptOnMatch) {
+ this.acceptOnMatch = acceptOnMatch;
+ }
+
+ /**
+ * @deprecated Use the setter method for the option directly instead of the generic setOption method.
+ */
+ @Deprecated
+ public void setOption(final String key, final String value) {
+ if (key.equalsIgnoreCase(STRING_TO_MATCH_OPTION)) {
+ stringToMatch = value;
+ } else if (key.equalsIgnoreCase(ACCEPT_ON_MATCH_OPTION)) {
+ acceptOnMatch = OptionConverter.toBoolean(value, acceptOnMatch);
+ }
+ }
+
+ public void setStringToMatch(final String s) {
+ stringToMatch = s;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/DOMConfigurator.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/DOMConfigurator.java
index 04a45551e03..b0cbb582b1f 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/DOMConfigurator.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/DOMConfigurator.java
@@ -16,65 +16,141 @@
*/
package org.apache.log4j.xml;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
+import java.io.StringWriter;
import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.Properties;
import javax.xml.parsers.FactoryConfigurationError;
+import org.apache.log4j.LogManager;
import org.apache.log4j.config.PropertySetter;
+import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.spi.LoggerRepository;
+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.core.net.UrlConnectionFactory;
+import org.apache.logging.log4j.core.util.IOUtils;
import org.w3c.dom.Element;
/**
+ * Use this class to initialize the log4j environment using a DOM tree.
*
+ *
+ * Sometimes it is useful to see how log4j is reading configuration files. You can enable log4j internal logging by
+ * defining the log4j.debug variable on the java command line. Alternatively, set the debug
+ * attribute in the log4j:configuration element. As in
+ *
+ *
+ * There are sample XML files included in the package.
+ *
+ * @since 0.8.3
*/
public class DOMConfigurator {
- public void doConfigure(final String filename, final LoggerRepository repository) {
+ public static void configure(final Element element) {
}
- public void doConfigure(final URL url, final LoggerRepository repository) {
+ public static void configure(final String fileName) throws FactoryConfigurationError {
+ final Path path = Paths.get(fileName);
+ try (final InputStream inputStream = Files.newInputStream(path)) {
+ final ConfigurationSource source = new ConfigurationSource(inputStream, path);
+ final LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+ Configuration configuration;
+ configuration = new XmlConfigurationFactory().getConfiguration(context, source);
+ LogManager.getRootLogger().removeAllAppenders();
+ Configurator.reconfigure(configuration);
+ } catch (final IOException e) {
+ throw new FactoryConfigurationError(e);
+ }
}
- public void doConfigure(final InputStream inputStream, final LoggerRepository repository)
- throws FactoryConfigurationError {
+ public static void configure(final URL url) throws FactoryConfigurationError {
+ new DOMConfigurator().doConfigure(url, LogManager.getLoggerRepository());
}
- public void doConfigure(final Reader reader, final LoggerRepository repository)
- throws FactoryConfigurationError {
+ public static void configureAndWatch(final String fileName) {
+ // TODO Watch
+ configure(fileName);
}
- public void doConfigure(final Element element, final LoggerRepository repository) {
+ public static void configureAndWatch(final String fileName, final long delay) {
+ XMLWatchdog xdog = new XMLWatchdog(fileName);
+ xdog.setDelay(delay);
+ xdog.start();
}
- public static void configure(final Element element) {
+ public static Object parseElement(final Element element, final Properties props, @SuppressWarnings("rawtypes") final Class expectedClass) {
+ return null;
}
- public static void configureAndWatch(final String configFilename) {
+ public static void setParameter(final Element elem, final PropertySetter propSetter, final Properties props) {
+
}
- public static void configureAndWatch(final String configFilename, final long delay) {
+ public static String subst(final String value, final Properties props) {
+ return OptionConverter.substVars(value, props);
}
- public static void configure(final String filename) throws FactoryConfigurationError {
+ private void doConfigure(final ConfigurationSource source) {
+ final LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+ Configuration configuration;
+ configuration = new XmlConfigurationFactory().getConfiguration(context, source);
+ Configurator.reconfigure(configuration);
}
- public static void configure(final URL url) throws FactoryConfigurationError {
+ public void doConfigure(final Element element, final LoggerRepository repository) {
}
- public static String subst(final String value, final Properties props) {
- return value;
+ public void doConfigure(final InputStream inputStream, final LoggerRepository repository) throws FactoryConfigurationError {
+ try {
+ doConfigure(new ConfigurationSource(inputStream));
+ } catch (final IOException e) {
+ throw new FactoryConfigurationError(e);
+ }
}
- public static void setParameter(final Element elem, final PropertySetter propSetter, final Properties props) {
+ public void doConfigure(final Reader reader, final LoggerRepository repository) throws FactoryConfigurationError {
+ try {
+ final StringWriter sw = new StringWriter();
+ IOUtils.copy(reader, sw);
+ doConfigure(new ConfigurationSource(new ByteArrayInputStream(sw.toString().getBytes(StandardCharsets.UTF_8))));
+ } catch (final IOException e) {
+ throw new FactoryConfigurationError(e);
+ }
+ }
+ public void doConfigure(final String fileName, final LoggerRepository repository) {
+ configure(fileName);
}
- public static Object parseElement(final Element element, final Properties props,
- @SuppressWarnings("rawtypes") final Class expectedClass)
- throws Exception {
- return null;
+ public void doConfigure(final URL url, final LoggerRepository repository) {
+ try {
+ final URLConnection connection = UrlConnectionFactory.createConnection(url);
+ try (InputStream inputStream = connection.getInputStream()) {
+ doConfigure(new ConfigurationSource(inputStream, url));
+ }
+ } catch (final IOException e) {
+ throw new FactoryConfigurationError(e);
+ }
}
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/Log4jEntityResolver.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/Log4jEntityResolver.java
new file mode 100644
index 00000000000..f4cb122ee7c
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/Log4jEntityResolver.java
@@ -0,0 +1,51 @@
+/*
+ * 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 java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.Constants;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+
+/**
+ * An {@link EntityResolver} specifically designed to return
+ * log4j.dtd which is embedded within the log4j jar
+ * file.
+ */
+public class Log4jEntityResolver implements EntityResolver {
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ private static final String PUBLIC_ID = "-//APACHE//DTD LOG4J 1.2//EN";
+
+ @Override
+ public InputSource resolveEntity(String publicId, String systemId) {
+ if (systemId.endsWith("log4j.dtd") || PUBLIC_ID.equals(publicId)) {
+ Class> clazz = getClass();
+ InputStream in = clazz.getResourceAsStream("/org/apache/log4j/xml/log4j.dtd");
+ if (in == null) {
+ LOGGER.warn("Could not find [log4j.dtd] using [{}] class loader, parsed without DTD.",
+ clazz.getClassLoader());
+ in = new ByteArrayInputStream(Constants.EMPTY_BYTE_ARRAY);
+ }
+ return new InputSource(in);
+ }
+ return null;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/UnrecognizedElementHandler.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/UnrecognizedElementHandler.java
new file mode 100644
index 00000000000..81e290b2d1f
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/UnrecognizedElementHandler.java
@@ -0,0 +1,43 @@
+/*
+ * 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 java.util.Properties;
+
+import org.w3c.dom.Element;
+
+/**
+ * When implemented by an object configured by DOMConfigurator,
+ * the handle method will be called when an unrecognized child
+ * element is encountered. Unrecognized child elements of
+ * the log4j:configuration element will be dispatched to
+ * the logger repository if it supports this interface.
+ *
+ * @since 1.2.15
+ */
+public interface UnrecognizedElementHandler {
+ /**
+ * Called to inform a configured object when
+ * an unrecognized child element is encountered.
+ * @param element element, may not be null.
+ * @param props properties in force, may be null.
+ * @return true if configured object recognized the element
+ * @throws Exception throw an exception to prevent activation
+ * of the configured object.
+ */
+ boolean parseUnrecognizedElement(Element element, Properties props) throws Exception;
+}
\ No newline at end of file
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XMLWatchdog.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XMLWatchdog.java
new file mode 100644
index 00000000000..753bd8b9dbd
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XMLWatchdog.java
@@ -0,0 +1,37 @@
+/*
+ * 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.LogManager;
+import org.apache.log4j.helpers.FileWatchdog;
+import org.apache.log4j.spi.LoggerRepository;
+
+class XMLWatchdog extends FileWatchdog {
+
+ XMLWatchdog(final String filename) {
+ super(filename);
+ }
+
+ /**
+ * Calls {@link DOMConfigurator#doConfigure(String, LoggerRepository)} with the filename to reconfigure Log4j.
+ */
+ @Override
+ public void doOnChange() {
+ new DOMConfigurator().doConfigure(filename, LogManager.getLoggerRepository());
+ }
+}
\ No newline at end of file
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 00000000000..07eb38db8a9
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java
@@ -0,0 +1,823 @@
+/*
+ * 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 java.io.IOException;
+import java.io.InterruptedIOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.stream.IntStream;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.FactoryConfigurationError;
+
+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.FilterAdapter;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertySetter;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.rewrite.RewritePolicy;
+import org.apache.log4j.spi.AppenderAttachable;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+import org.apache.logging.log4j.core.Filter.Result;
+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.LoggerConfig;
+import org.apache.logging.log4j.core.config.status.StatusConfiguration;
+import org.apache.logging.log4j.core.filter.ThresholdFilter;
+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;
+
+/**
+ * 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 THRESHOLD_ATTR = "threshold";
+ 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";
+
+ // key: appenderName, value: appender
+ private final Map appenderMap;
+
+ private final Properties props = null;
+
+ public XmlConfiguration(final LoggerContext loggerContext, final ConfigurationSource source,
+ int monitorIntervalSeconds) {
+ super(loggerContext, source, monitorIntervalSeconds);
+ appenderMap = new HashMap<>();
+ }
+
+ public void addAppenderIfAbsent(Appender appender) {
+ appenderMap.putIfAbsent(appender.getName(), appender);
+ }
+
+ /**
+ * Configures log4j by reading in a log4j.dtd compliant XML
+ * configuration file.
+ */
+ @Override
+ public void doConfigure() throws FactoryConfigurationError {
+ ConfigurationSource source = getConfigurationSource();
+ ParseAction action = new ParseAction() {
+ @Override
+ public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
+ @SuppressWarnings("resource") // The ConfigurationSource and its caller manages the InputStream.
+ InputSource inputSource = new InputSource(source.getInputStream());
+ inputSource.setSystemId("dummy://log4j.dtd");
+ return parser.parse(inputSource);
+ }
+
+ @Override
+ 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 succeeded.");
+ 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 Configuration reconfigure() {
+ try {
+ final ConfigurationSource source = getConfigurationSource().resetInputStream();
+ if (source == null) {
+ return null;
+ }
+ final XmlConfigurationFactory factory = new XmlConfigurationFactory();
+ final XmlConfiguration config =
+ (XmlConfiguration) factory.getConfiguration(getLoggerContext(), source);
+ return config == null || config.getState() != State.INITIALIZING ? null : config;
+ } catch (final IOException ex) {
+ LOGGER.error("Cannot locate file {}: {}", getConfigurationSource(), ex);
+ }
+ return null;
+ }
+
+ /**
+ * 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;
+ }
+ // 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;
+ }
+ appender = parseAppender(element);
+ if (appender != null) {
+ appenderMap.put(appenderName, appender);
+ }
+ return appender;
+ }
+
+ /**
+ * Used internally to parse appenders by IDREF element.
+ * @param appenderRef The Appender Reference Element.
+ * @return The Appender.
+ */
+ 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.
+ * @param appenderElement The Appender Element.
+ * @return The Appender.
+ */
+ 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)));
+ final AtomicReference filterChain = new AtomicReference<>();
+ 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:
+ addFilter(filterChain, currentElement);
+ 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);
+ }
+ }
+ });
+ final Filter head = filterChain.get();
+ if (head != null) {
+ appender.addFilter(head);
+ }
+ 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;
+ }
+
+ public RewritePolicy parseRewritePolicy(Element rewritePolicyElement) {
+ String className = subst(rewritePolicyElement.getAttribute(CLASS_ATTR));
+ LOGGER.debug("Class name: [" + className + ']');
+ RewritePolicy policy = manager.parseRewritePolicy(className, rewritePolicyElement, this);
+ if (policy == null) {
+ policy = buildRewritePolicy(className, rewritePolicyElement);
+ }
+ return policy;
+ }
+
+ private RewritePolicy buildRewritePolicy(String className, Element element) {
+ try {
+ RewritePolicy policy = LoaderUtil.newInstanceOf(className);
+ PropertySetter propSetter = new PropertySetter(policy);
+
+ forEachElement(element.getChildNodes(), currentElement -> {
+ if (currentElement.getTagName().equalsIgnoreCase(PARAM_TAG)) {
+ setParameter(currentElement, propSetter);
+ }
+ });
+ propSetter.activate();
+ return policy;
+ } catch (ConsumerException ex) {
+ Throwable t = ex.getCause();
+ if (t instanceof InterruptedException || t instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.error("Could not create an RewritePolicy. Reported error follows.", t);
+ } catch (Exception oops) {
+ if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.error("Could not create an RewritePolicy. 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.
+ * @param filterElement The Filter Element.
+ * @return The Filter.
+ */
+ public void addFilter(final AtomicReference ref, final Element filterElement) {
+ final Filter value = parseFilters(filterElement);
+ ref.accumulateAndGet(value, FilterAdapter::addFilter);
+ }
+
+ /**
+ * 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) {
+ filter = buildFilter(className, filterElement);
+ }
+ return filter;
+ }
+
+ private Filter buildFilter(final String className, final Element filterElement) {
+ try {
+ Filter filter = LoaderUtil.newInstanceOf(className);
+ PropertySetter propSetter = new PropertySetter(filter);
+
+ forEachElement(filterElement.getChildNodes(), currentElement -> {
+ // Parse appender parameters
+ switch (currentElement.getTagName()) {
+ case PARAM_TAG :
+ setParameter(currentElement, propSetter);
+ break;
+ }
+ });
+ propSetter.activate();
+ return filter;
+ } catch (ConsumerException ex) {
+ Throwable t = ex.getCause();
+ if (t instanceof InterruptedException || t instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.error("Could not create an Filter. Reported error follows.", t);
+ } catch (Exception oops) {
+ if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.error("Could not create an Filter. Reported error follows.", oops);
+ }
+ return null;
+ }
+
+ /**
+ * Used internally to parse an category element.
+ */
+ private void parseCategory(Element loggerElement) {
+ // Create a new org.apache.log4j.Category object from the 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 root 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.
+ * @param layoutElement The Layout Element.
+ * @return The Layout.
+ */
+ 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 (Exception e) {
+ Throwable cause = e.getCause();
+ if (e instanceof InterruptedException
+ || e instanceof InterruptedIOException
+ || cause instanceof InterruptedException
+ || cause instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.error("Could not create the Layout. Reported error follows.", e);
+ }
+ 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));
+ final Level level;
+ if (EMPTY_STR.equals(className)) {
+ level = OptionConverter.toLevel(priStr, DEFAULT_LEVEL);
+ } else {
+ level = OptionConverter.toLevel(className, priStr, DEFAULT_LEVEL);
+ }
+ logger.setLevel(level != null ? level.getVersion2Level() : null);
+ }
+ LOGGER.debug("{} level set to {}", catName, logger.getLevel());
+ }
+
+ private void setParameter(Element element, PropertySetter propSetter) {
+ String name = subst(element.getAttribute(NAME_ATTR));
+ String value = element.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 log4j.dtd.
+ */
+ 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();
+
+ final String threshold = subst(element.getAttribute(THRESHOLD_ATTR));
+ if (threshold != null) {
+ final org.apache.logging.log4j.Level level = OptionConverter.convertLevel(threshold.trim(),
+ org.apache.logging.log4j.Level.ALL);
+ addFilter(ThresholdFilter.createFilter(level, Result.NEUTRAL, Result.DENY));
+ }
+
+ 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("Log4j 1 renderers are not supported by Log4j 2 and will be ignored.");
+ break;
+ case THROWABLE_RENDERER_TAG:
+ LOGGER.warn("Log4j 1 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);
+ addAppender(AppenderAdapter.adapt(appender));
+ break;
+ default:
+ quietParseUnrecognizedElement(null, currentElement, props);
+ }
+ });
+ }
+
+ private String subst(final String value) {
+ return getStrSubstitutor().replace(value);
+ }
+
+ public static void forEachElement(NodeList list, Consumer consumer) {
+ IntStream.range(0, list.getLength()).mapToObj(list::item)
+ .filter(node -> node.getNodeType() == Node.ELEMENT_NODE)
+ .forEach(node -> consumer.accept((Element) node));
+ }
+
+ 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();
+
+ @Override
+ public void error(final SAXParseException ex) {
+ emitMessage("Continuable parsing error ", ex);
+ }
+
+ @Override
+ public void fatalError(final SAXParseException ex) {
+ emitMessage("Fatal parsing error ", ex);
+ }
+
+ @Override
+ 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
new file mode 100644
index 00000000000..dfe157684dd
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java
@@ -0,0 +1,77 @@
+/*
+ * 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.config.Log4j1Configuration;
+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.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.util.PropertiesUtil;
+
+/**
+ * Constructs a Configuration usable in Log4j 2 from a Log4j 1 configuration file.
+ */
+@Namespace(ConfigurationFactory.NAMESPACE)
+@Plugin("Log4j1XmlConfigurationFactory")
+@Order(2)
+public class XmlConfigurationFactory extends ConfigurationFactory {
+
+ public static final String FILE_EXTENSION = ".xml";
+
+ /**
+ * 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[] { FILE_EXTENSION };
+ }
+
+ @Override
+ public Configuration getConfiguration(LoggerContext loggerContext, ConfigurationSource source) {
+ int interval = PropertiesUtil.getProperties().getIntegerProperty(Log4j1Configuration.MONITOR_INTERVAL, 0);
+ return new XmlConfiguration(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/resources/org/apache/log4j/xml/log4j.dtd b/log4j-1.2-api/src/main/resources/org/apache/log4j/xml/log4j.dtd
new file mode 100644
index 00000000000..f8e433a50e6
--- /dev/null
+++ b/log4j-1.2-api/src/main/resources/org/apache/log4j/xml/log4j.dtd
@@ -0,0 +1,237 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/site/markdown/index.md b/log4j-1.2-api/src/site/markdown/index.md
index a80e6873f6d..4eb2916a3c3 100644
--- a/log4j-1.2-api/src/site/markdown/index.md
+++ b/log4j-1.2-api/src/site/markdown/index.md
@@ -18,12 +18,26 @@
# Log4j 1.2 Bridge
-The Log4j 1.2 Bridge allows applications coded to use Log4j 1.2 API to use
-Log4j 2 instead.
+The Log4j 1.2 Bridge allows applications coded to use Log4j 1.2 API to use Log4j 2 instead.
## Requirements
-The Log4j 1.2 bridge is dependent on the Log4j 2 API and implementation.
+The Log4j 1.2 bridge is dependent on the Log4j 2 API. The following Log4j 1.x methods will behave differently when
+the Log4j 2 Core module is included then when it is not:
+
+| Method | Without log4j-core | With log4j-core |
+| ----------------------------- | ------------------ | ------------------------------------ |
+| Category.getParent() | Returns null | Returns parent logger |
+| Category.setLevel() | NoOp | Sets Logger Level |
+| Category.setPriority() | NoOp | Sets Logger Level |
+| Category.getAdditivity() | Returns false | Returns Logger's additivity setting |
+| Category.setAdditivity() | NoOp | Sets additivity of LoggerConfig |
+| Category.getResourceBundle() | NoOp | Returns the resource bundle associated with the Logger |
+| BasicConfigurator.configure() | NoOp | Reconfigures Log4j 2 |
+
+If log4j-core is not present location information will not be accurate in calls using the Log4j 1.2 API. The config
+package which attempts to convert Log4j 1.x configurations to Log4j 2 is not supported without Log4j 2.
+
For more information, see [Runtime Dependencies](../runtime-dependencies.html).
## Usage
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/BasicConfigurationFactory.java b/log4j-1.2-api/src/test/java/org/apache/log4j/BasicConfigurationFactory.java
index d231d82346c..ac9778e097f 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/BasicConfigurationFactory.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/BasicConfigurationFactory.java
@@ -16,8 +16,6 @@
*/
package org.apache.log4j;
-import java.net.URI;
-
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.AbstractConfiguration;
@@ -26,6 +24,8 @@
import org.apache.logging.log4j.core.config.ConfigurationSource;
import org.apache.logging.log4j.core.config.LoggerConfig;
+import java.net.URI;
+
/**
*
*/
@@ -46,9 +46,7 @@ public Configuration getConfiguration(final LoggerContext loggerContext, final S
return new BasicConfiguration(loggerContext);
}
- public class BasicConfiguration extends AbstractConfiguration {
-
- private static final long serialVersionUID = -2716784321395089563L;
+ public static class BasicConfiguration extends AbstractConfiguration {
private static final String DEFAULT_LEVEL = "org.apache.logging.log4j.level";
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/BasicConfiguratorTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/BasicConfiguratorTest.java
new file mode 100644
index 00000000000..d14dcf9c220
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/BasicConfiguratorTest.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.log4j;
+
+import org.apache.log4j.varia.NullAppender;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test {@link BasicConfigurator}.
+ */
+public class BasicConfiguratorTest {
+
+ @Test
+ public void testConfigure() {
+ // TODO More...
+ BasicConfigurator.configure();
+ }
+
+ @Test
+ public void testResetConfiguration() {
+ // TODO More...
+ BasicConfigurator.resetConfiguration();
+ }
+
+ @Test
+ public void testConfigureAppender() {
+ BasicConfigurator.configure(null);
+ // TODO More...
+ }
+
+ @Test
+ public void testConfigureConsoleAppender() {
+ // TODO What to do? Map to Log4j 2 Appender deeper in the code?
+ BasicConfigurator.configure(new ConsoleAppender());
+ }
+
+ @Test
+ public void testConfigureNullAppender() {
+ // The NullAppender name is null and we do not want an NPE when the name is used as a key in a ConcurrentHashMap.
+ BasicConfigurator.configure(NullAppender.getNullAppender());
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/CallerInformationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/CallerInformationTest.java
index a5b30907f07..fd35e4a9c76 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/CallerInformationTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/CallerInformationTest.java
@@ -20,8 +20,8 @@
import java.util.List;
-import org.apache.logging.log4j.junit.LoggerContextRule;
-import org.apache.logging.log4j.test.appender.ListAppender;
+import org.apache.logging.log4j.core.test.junit.LoggerContextRule;
+import org.apache.logging.log4j.core.test.appender.ListAppender;
import org.junit.ClassRule;
import org.junit.Test;
@@ -34,7 +34,7 @@ public class CallerInformationTest {
public static final LoggerContextRule ctx = new LoggerContextRule(CONFIG);
@Test
- public void testClassLogger() throws Exception {
+ public void testClassLogger() {
final ListAppender app = ctx.getListAppender("Class").clear();
final Logger logger = Logger.getLogger("ClassLogger");
logger.info("Ignored message contents.");
@@ -48,7 +48,7 @@ public void testClassLogger() throws Exception {
}
@Test
- public void testMethodLogger() throws Exception {
+ public void testMethodLogger() {
final ListAppender app = ctx.getListAppender("Method").clear();
final Logger logger = Logger.getLogger("MethodLogger");
logger.info("More messages.");
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/CategoryTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/CategoryTest.java
index f6711933b8d..3d46a78643b 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/CategoryTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/CategoryTest.java
@@ -1,71 +1,83 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
*/
-
package org.apache.log4j;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.Enumeration;
import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.spi.LoggingEvent;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.ConfigurationFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.core.test.appender.ListAppender;
+import org.apache.logging.log4j.message.MapMessage;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.ObjectMessage;
-import org.apache.logging.log4j.test.appender.ListAppender;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.util.Cast;
+import org.apache.logging.log4j.util.Constants;
import org.apache.logging.log4j.util.Strings;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
/**
* Tests of Category.
*/
public class CategoryTest {
- static ConfigurationFactory cf = new BasicConfigurationFactory();
-
- private static ListAppender appender = new ListAppender("List");
+ private static final String VERSION1_APPENDER_NAME = "Version1List";
+ private static final String VERSION2_APPENDER_NAME = "List";
+ private static ListAppender appender = new ListAppender(VERSION2_APPENDER_NAME);
+ private static org.apache.log4j.ListAppender version1Appender = new org.apache.log4j.ListAppender();
- @BeforeClass
+ @BeforeAll
public static void setupClass() {
appender.start();
- ConfigurationFactory.setConfigurationFactory(cf);
- LoggerContext.getContext().reconfigure();
+ version1Appender.setName(VERSION1_APPENDER_NAME);
+ System.setProperty(ConfigurationFactory.CONFIGURATION_FACTORY_PROPERTY, BasicConfigurationFactory.class.getName());
}
- @AfterClass
+ @AfterAll
public static void cleanupClass() {
- ConfigurationFactory.removeConfigurationFactory(cf);
appender.stop();
+ System.clearProperty(ConfigurationFactory.CONFIGURATION_FACTORY_PROPERTY);
}
- @Before
+ @BeforeEach
public void before() {
appender.clear();
}
-
+
+ @Test
+ public void testExist() {
+ assertNull(Category.exists("Does not exist for sure"));
+ }
+
/**
* Tests Category.forcedLog.
*/
@@ -74,27 +86,66 @@ public void before() {
public void testForcedLog() {
final MockCategory category = new MockCategory("org.example.foo");
category.setAdditivity(false);
- category.getLogger().addAppender(appender);
+ ((org.apache.logging.log4j.core.Logger) category.getLogger()).addAppender(appender);
+ // Logging a String
category.info("Hello, World");
- final List list = appender.getEvents();
+ List list = appender.getEvents();
int events = list.size();
- assertTrue("Number of events should be 1, was " + events, events == 1);
+ assertEquals(1, events, "Number of events should be 1, was " + events);
LogEvent event = list.get(0);
Message msg = event.getMessage();
- assertNotNull("No message", msg);
- assertTrue("Incorrect Message type", msg instanceof ObjectMessage);
+ assertNotNull(msg, "No message");
+ // LOG4J2-3080: use message type consistently
+ assertTrue(msg instanceof SimpleMessage, "Incorrect Message type");
+ assertEquals("Hello, World", msg.getFormat());
+ appender.clear();
+ // Logging a String map
+ category.info(Collections.singletonMap("hello", "world"));
+ list = appender.getEvents();
+ events = list.size();
+ assertTrue(events == 1, "Number of events should be 1, was " + events);
+ event = list.get(0);
+ msg = event.getMessage();
+ assertNotNull(msg, "No message");
+ assertTrue(msg instanceof MapMessage, "Incorrect Message type");
Object[] objects = msg.getParameters();
- assertTrue("Incorrect Object type", objects[0] instanceof String);
+ assertEquals("world", objects[0]);
appender.clear();
- category.log(Priority.INFO, "Hello, World");
+ // Logging a generic map
+ category.info(Collections.singletonMap(1234L, "world"));
+ list = appender.getEvents();
+ events = list.size();
+ assertTrue(events == 1, "Number of events should be 1, was " + events);
+ event = list.get(0);
+ msg = event.getMessage();
+ assertNotNull(msg, "No message");
+ assertTrue(msg instanceof MapMessage, "Incorrect Message type");
+ objects = msg.getParameters();
+ assertEquals("world", objects[0]);
+ appender.clear();
+ // Logging an object
+ final Object obj = new Object();
+ category.info(obj);
+ list = appender.getEvents();
events = list.size();
- assertTrue("Number of events should be 1, was " + events, events == 1);
+ assertTrue(events == 1, "Number of events should be 1, was " + events);
event = list.get(0);
msg = event.getMessage();
- assertNotNull("No message", msg);
- assertTrue("Incorrect Message type", msg instanceof ObjectMessage);
+ assertNotNull(msg, "No message");
+ assertTrue(msg instanceof ObjectMessage, "Incorrect Message type");
objects = msg.getParameters();
- assertTrue("Incorrect Object type", objects[0] instanceof String);
+ assertEquals(obj, objects[0]);
+ appender.clear();
+
+ category.log(Priority.INFO, "Hello, World");
+ list = appender.getEvents();
+ events = list.size();
+ assertTrue(events == 1, "Number of events should be 1, was " + events);
+ event = list.get(0);
+ msg = event.getMessage();
+ assertNotNull(msg, "No message");
+ assertTrue(msg instanceof SimpleMessage, "Incorrect Message type");
+ assertEquals("Hello, World", msg.getFormat());
appender.clear();
}
@@ -106,7 +157,7 @@ public void testForcedLog() {
@Test
public void testGetChainedPriorityReturnType() throws Exception {
final Method method = Category.class.getMethod("getChainedPriority", (Class[]) null);
- assertTrue(method.getReturnType() == Priority.class);
+ assertSame(method.getReturnType(), Priority.class);
}
/**
@@ -118,7 +169,7 @@ public void testL7dlog() {
logger.setLevel(Level.ERROR);
final Priority debug = Level.DEBUG;
logger.l7dlog(debug, "Hello, World", null);
- assertTrue(appender.getEvents().size() == 0);
+ assertTrue(appender.getEvents().isEmpty());
}
/**
@@ -129,8 +180,8 @@ public void testL7dlog4Param() {
final Logger logger = Logger.getLogger("org.example.foo");
logger.setLevel(Level.ERROR);
final Priority debug = Level.DEBUG;
- logger.l7dlog(debug, "Hello, World", new Object[0], null);
- assertTrue(appender.getEvents().size() == 0);
+ logger.l7dlog(debug, "Hello, World", Constants.EMPTY_OBJECT_ARRAY, null);
+ assertTrue(appender.getEvents().isEmpty());
}
/**
@@ -148,8 +199,8 @@ public void testExistingLog4j2Logger() {
final Priority debug = Level.DEBUG;
// the next line will throw an exception if the LogManager loggers
// aren't supported by 1.2 Logger/Category
- logger.l7dlog(debug, "Hello, World", new Object[0], null);
- assertTrue(appender.getEvents().size() == 0);
+ logger.l7dlog(debug, "Hello, World", Constants.EMPTY_OBJECT_ARRAY, null);
+ assertTrue(appender.getEvents().isEmpty());
}
/**
@@ -165,22 +216,192 @@ public void testSetPriority() {
logger.setPriority(debug);
}
+ /**
+ * Tests setPriority(Priority).
+ *
+ * @deprecated
+ */
+ @Deprecated
+ @Test
+ public void testSetPriorityNull() {
+ Logger.getLogger("org.example.foo").setPriority(null);
+ }
+
@Test
public void testClassName() {
final Category category = Category.getInstance("TestCategory");
- final Layout layout = PatternLayout.newBuilder().withPattern("%d %p %C{1.} [%t] %m%n").build();
+ final Layout layout = PatternLayout.newBuilder().setPattern("%d %p %C{1.} [%t] %m%n").build();
final ListAppender appender = new ListAppender("List2", null, layout, false, false);
appender.start();
category.setAdditivity(false);
- category.getLogger().addAppender(appender);
+ ((org.apache.logging.log4j.core.Logger) category.getLogger()).addAppender(appender);
category.error("Test Message");
final List msgs = appender.getMessages();
- assertTrue("Incorrect number of messages. Expected 1 got " + msgs.size(), msgs.size() == 1);
+ assertEquals(1, msgs.size(), "Incorrect number of messages. Expected 1 got " + msgs.size());
final String msg = msgs.get(0);
appender.clear();
final String threadName = Thread.currentThread().getName();
final String expected = "ERROR o.a.l.CategoryTest [" + threadName + "] Test Message" + Strings.LINE_SEPARATOR;
- assertTrue("Incorrect message " + Strings.dquote(msg) + " expected " + Strings.dquote(expected), msg.endsWith(expected));
+ assertTrue(msg.endsWith(expected),
+ "Incorrect message " + Strings.dquote(msg) + " expected " + Strings.dquote(expected));
+ }
+
+ @Test
+ public void testStringLog() {
+ final String payload = "payload";
+ testMessageImplementation(
+ payload,
+ SimpleMessage.class,
+ message -> assertEquals(message.getFormattedMessage(), payload));
+ }
+
+ @Test
+ public void testCharSequenceLog() {
+ final CharSequence payload = new CharSequence() {
+
+ @Override
+ public int length() {
+ return 3;
+ }
+
+ @Override
+ public char charAt(final int index) {
+ return "abc".charAt(index);
+ }
+
+ @Override
+ public CharSequence subSequence(final int start, final int end) {
+ return "abc".subSequence(start, end);
+ }
+
+ @Override
+ public String toString() {
+ return "abc";
+ }
+
+ };
+ testMessageImplementation(
+ payload,
+ SimpleMessage.class,
+ message -> assertEquals(message.getFormattedMessage(), payload.toString()));
+ }
+
+ @Test
+ public void testMapLog() {
+ final String key = "key";
+ final Object value = 0xDEADBEEF;
+ final Map payload = Collections.singletonMap(key, value);
+ testMessageImplementation(
+ payload,
+ MapMessage.class,
+ message -> assertEquals(message.getData(), payload));
+ }
+
+ @Test
+ public void testObjectLog() {
+ final Object payload = new Object();
+ testMessageImplementation(
+ payload,
+ ObjectMessage.class,
+ message -> assertEquals(message.getParameter(), payload));
+ }
+
+ private void testMessageImplementation(
+ final Object messagePayload,
+ final Class expectedMessageClass,
+ final Consumer messageTester) {
+
+ // Setup the logger and the appender.
+ final Category category = Category.getInstance("TestCategory");
+ final org.apache.logging.log4j.core.Logger logger =
+ (org.apache.logging.log4j.core.Logger) category.getLogger();
+ logger.addAppender(appender);
+
+ // Log the message payload.
+ category.info(messagePayload);
+
+ // Verify collected log events.
+ final List events = appender.getEvents();
+ assertEquals(1, events.size(), "was expecting a single event");
+ final LogEvent logEvent = events.get(0);
+
+ // Verify the collected message.
+ final Message message = logEvent.getMessage();
+ final Class extends Message> actualMessageClass = message.getClass();
+ assertTrue(expectedMessageClass.isAssignableFrom(actualMessageClass),
+ "was expecting message to be instance of " + expectedMessageClass + ", found: " + actualMessageClass);
+ final M typedMessage = Cast.cast(message);
+ messageTester.accept(typedMessage);
+
+ }
+
+ @Test
+ public void testAddAppender() {
+ try {
+ final Logger rootLogger = LogManager.getRootLogger();
+ int count = version1Appender.getEvents().size();
+ rootLogger.addAppender(version1Appender);
+ final Logger logger = LogManager.getLogger(CategoryTest.class);
+ final org.apache.log4j.ListAppender appender = new org.apache.log4j.ListAppender();
+ appender.setName("appender2");
+ logger.addAppender(appender);
+ // Root logger
+ rootLogger.info("testAddLogger");
+ assertEquals(++count, version1Appender.getEvents().size(), "adding at root works");
+ assertEquals(0, appender.getEvents().size(), "adding at child works");
+ // Another logger
+ logger.info("testAddLogger2");
+ assertEquals(++count, version1Appender.getEvents().size(), "adding at root works");
+ assertEquals(1, appender.getEvents().size(), "adding at child works");
+ // Call appenders
+ final LoggingEvent event = new LoggingEvent();
+ logger.callAppenders(event);
+ assertEquals(++count, version1Appender.getEvents().size(), "callAppenders");
+ assertEquals(2, appender.getEvents().size(), "callAppenders");
+ } finally {
+ LogManager.resetConfiguration();
+ }
+ }
+
+ @Test
+ public void testGetAppender() {
+ try {
+ final Logger rootLogger = LogManager.getRootLogger();
+ final org.apache.logging.log4j.core.Logger v2RootLogger = (org.apache.logging.log4j.core.Logger) rootLogger
+ .getLogger();
+ v2RootLogger.addAppender(AppenderAdapter.adapt(version1Appender));
+ v2RootLogger.addAppender(appender);
+ final List rootAppenders = Collections.list(rootLogger.getAllAppenders());
+ assertEquals(1, rootAppenders.size(), "only v1 appenders");
+ assertTrue(rootAppenders.get(0) instanceof org.apache.log4j.ListAppender, "appender is a v1 ListAppender");
+ assertEquals(VERSION1_APPENDER_NAME, rootLogger.getAppender(VERSION1_APPENDER_NAME).getName(),
+ "explicitly named appender");
+ Appender v2ListAppender = rootLogger.getAppender(VERSION2_APPENDER_NAME);
+ assertTrue(v2ListAppender instanceof AppenderWrapper, "explicitly named appender");
+ assertTrue(((AppenderWrapper) v2ListAppender).getAppender() instanceof ListAppender,
+ "appender is a v2 ListAppender");
+
+ final Logger logger = LogManager.getLogger(CategoryTest.class);
+ final org.apache.logging.log4j.core.Logger v2Logger = (org.apache.logging.log4j.core.Logger) logger
+ .getLogger();
+ final org.apache.log4j.ListAppender loggerAppender = new org.apache.log4j.ListAppender();
+ loggerAppender.setName("appender2");
+ v2Logger.addAppender(AppenderAdapter.adapt(loggerAppender));
+ final List appenders = Collections.list(logger.getAllAppenders());
+ assertEquals(1, appenders.size(), "no parent appenders");
+ assertEquals(loggerAppender, appenders.get(0));
+ assertNull(logger.getAppender(VERSION1_APPENDER_NAME), "no parent appenders");
+ assertNull(logger.getAppender(VERSION2_APPENDER_NAME), "no parent appenders");
+
+ final Logger childLogger = LogManager.getLogger(CategoryTest.class.getName() + ".child");
+ final Enumeration childAppenders = childLogger.getAllAppenders();
+ assertFalse(childAppenders.hasMoreElements(), "no parent appenders");
+ assertNull(childLogger.getAppender("appender2"), "no parent appenders");
+ assertNull(childLogger.getAppender(VERSION1_APPENDER_NAME), "no parent appenders");
+ assertNull(childLogger.getAppender(VERSION2_APPENDER_NAME), "no parent appenders");
+ } finally {
+ LogManager.resetConfiguration();
+ }
}
/**
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/ConsoleAppenderTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/ConsoleAppenderTest.java
new file mode 100644
index 00000000000..00a8d00f758
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/ConsoleAppenderTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Used to test Log4j 1 support.
+ */
+public class ConsoleAppenderTest {
+
+ private ConsoleAppender consoleAppender;
+
+ @BeforeEach
+ public void beforeEach() {
+ consoleAppender = new ConsoleAppender();
+ }
+
+ @Test
+ public void testFollow() {
+ // Only really care that it compiles, behavior is secondary at this level.
+ consoleAppender.setFollow(true);
+ assertTrue(consoleAppender.getFollow());
+ }
+
+ @Test
+ public void testTarget() {
+ // Only really care that it compiles, behavior is secondary at this level.
+ consoleAppender.setTarget(ConsoleAppender.SYSTEM_OUT);
+ assertEquals(ConsoleAppender.SYSTEM_OUT, consoleAppender.getTarget());
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/CustomAppenderSkeleton.java b/log4j-1.2-api/src/test/java/org/apache/log4j/CustomAppenderSkeleton.java
new file mode 100644
index 00000000000..37acfc4a433
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/CustomAppenderSkeleton.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.log4j;
+
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Used to test Log4j 1 support. All we are looking for here is that this code compiles.
+ */
+public class CustomAppenderSkeleton extends AppenderSkeleton {
+
+ @Override
+ protected void append(final LoggingEvent event) {
+ // NOOP @Override
+ }
+
+ @Override
+ public void close() {
+ // NOOP @Override
+ }
+
+ @SuppressWarnings({"cast", "unused"})
+ public void compilerAccessToWriterAppenderSkeletonVariables() {
+ if (closed) {
+ // Yep, it compiles.
+ final boolean compileMe = closed;
+ }
+ if (errorHandler instanceof ErrorHandler) {
+ // Yep, it compiles.
+ final ErrorHandler other = errorHandler;
+ }
+ if (headFilter instanceof Filter) {
+ // Yep, it compiles.
+ final Filter other = headFilter;
+ }
+ if (layout instanceof Layout) {
+ // Yep, it compiles.
+ final Layout other = layout;
+ }
+ if (name instanceof String) {
+ // Yep, it compiles.
+ final String other = name;
+ }
+ if (tailFilter instanceof Filter) {
+ // Yep, it compiles.
+ final Filter other = tailFilter;
+ }
+ if (threshold instanceof Priority) {
+ // Yep, it compiles.
+ final Priority other = threshold;
+ }
+ }
+
+ @Override
+ public boolean requiresLayout() {
+ // NOOP @Override
+ return false;
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/CustomConsoleAppender.java b/log4j-1.2-api/src/test/java/org/apache/log4j/CustomConsoleAppender.java
new file mode 100644
index 00000000000..d26b32fab5f
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/CustomConsoleAppender.java
@@ -0,0 +1,84 @@
+/*
+ * 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;
+
+import org.apache.log4j.helpers.QuietWriter;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+
+/**
+ * Used to test Log4j 1 support. All we are looking for here is that this code compiles.
+ */
+public class CustomConsoleAppender extends ConsoleAppender {
+
+ public CustomConsoleAppender() {
+ super();
+ }
+
+ public CustomConsoleAppender(final Layout layout) {
+ super(layout);
+ }
+
+ public CustomConsoleAppender(final Layout layout, final String target) {
+ super(layout, target);
+ }
+
+ @SuppressWarnings({"cast", "unused"})
+ public void compilerAccessToConsoleAppenderInstanceVariables() {
+ if (target instanceof String) {
+ final String other = name;
+ }
+ }
+
+ @SuppressWarnings({"cast", "unused"})
+ public void compilerAccessToWriterAppenderInstanceVariables() {
+ if (immediateFlush) {
+ final boolean other = immediateFlush;
+ }
+ if (encoding instanceof String) {
+ final String other = encoding;
+ }
+ if (qw instanceof QuietWriter) {
+ final QuietWriter other = qw;
+ }
+ }
+
+ @SuppressWarnings({"cast", "unused"})
+ public void compilerAccessToWriterAppenderSkeletonVariables() {
+ if (closed) {
+ final boolean compileMe = closed;
+ }
+ if (errorHandler instanceof ErrorHandler) {
+ final ErrorHandler other = errorHandler;
+ }
+ if (headFilter instanceof Filter) {
+ final Filter other = headFilter;
+ }
+ if (layout instanceof Layout) {
+ final Layout other = layout;
+ }
+ if (name instanceof String) {
+ final String other = name;
+ }
+ if (tailFilter instanceof Filter) {
+ final Filter other = tailFilter;
+ }
+ if (threshold instanceof Priority) {
+ final Priority other = threshold;
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/CustomFileAppender.java b/log4j-1.2-api/src/test/java/org/apache/log4j/CustomFileAppender.java
new file mode 100644
index 00000000000..ae57b8bba0b
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/CustomFileAppender.java
@@ -0,0 +1,50 @@
+/*
+ * 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;
+
+public class CustomFileAppender extends FileAppender {
+
+ private boolean booleanA;
+ private int intA;
+ private String stringA;
+
+ public boolean getBooleanA() {
+ return booleanA;
+ }
+
+ public int getIntA() {
+ return intA;
+ }
+
+ public String getStringA() {
+ return stringA;
+ }
+
+ public void setBooleanA(final boolean booleanA) {
+ this.booleanA = booleanA;
+ }
+
+ public void setIntA(final int intA) {
+ this.intA = intA;
+ }
+
+ public void setStringA(final String stringA) {
+ this.stringA = stringA;
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/CustomNoopAppender.java b/log4j-1.2-api/src/test/java/org/apache/log4j/CustomNoopAppender.java
new file mode 100644
index 00000000000..15e6fa09cbc
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/CustomNoopAppender.java
@@ -0,0 +1,67 @@
+/*
+ * 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;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+public class CustomNoopAppender extends AppenderSkeleton {
+
+ private boolean booleanA;
+ private int intA;
+ private String stringA;
+
+ @Override
+ protected void append(final LoggingEvent event) {
+ // Noop
+ }
+
+ @Override
+ public void close() {
+ // Noop
+ }
+
+ public boolean getBooleanA() {
+ return booleanA;
+ }
+
+ public int getIntA() {
+ return intA;
+ }
+
+ public String getStringA() {
+ return stringA;
+ }
+
+ @Override
+ public boolean requiresLayout() {
+ return false;
+ }
+
+ public void setBooleanA(final boolean booleanA) {
+ this.booleanA = booleanA;
+ }
+
+ public void setIntA(final int intA) {
+ this.intA = intA;
+ }
+
+ public void setStringA(final String stringA) {
+ this.stringA = stringA;
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/CustomWriterAppender.java b/log4j-1.2-api/src/test/java/org/apache/log4j/CustomWriterAppender.java
new file mode 100644
index 00000000000..b1be153326b
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/CustomWriterAppender.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;
+
+import org.apache.log4j.helpers.QuietWriter;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+
+/**
+ * Used to test Log4j 1 support. All we are looking for here is that this code compiles.
+ */
+public class CustomWriterAppender extends WriterAppender {
+
+ public void compilerAccessToWriterAppenderInstanceVariables() {
+ if (immediateFlush) {
+ // Yep, it compiles.
+ final boolean other = immediateFlush;
+ }
+ if (encoding instanceof String) {
+ // Yep, it compiles.
+ final String other = encoding;
+ }
+ if (qw instanceof QuietWriter) {
+ // Yep, it compiles.
+ final QuietWriter other = qw;
+ }
+ }
+
+ @SuppressWarnings({"cast", "unused"})
+ public void compilerAccessToWriterAppenderSkeletonVariables() {
+ if (closed) {
+ // Yep, it compiles.
+ final boolean compileMe = closed;
+ }
+ if (errorHandler instanceof ErrorHandler) {
+ // Yep, it compiles.
+ final ErrorHandler other = errorHandler;
+ }
+ if (headFilter instanceof Filter) {
+ // Yep, it compiles.
+ final Filter other = headFilter;
+ }
+ if (layout instanceof Layout) {
+ // Yep, it compiles.
+ final Layout other = layout;
+ }
+ if (name instanceof String) {
+ // Yep, it compiles.
+ final String other = name;
+ }
+ if (tailFilter instanceof Filter) {
+ // Yep, it compiles.
+ final Filter other = tailFilter;
+ }
+ if (threshold instanceof Priority) {
+ // Yep, it compiles.
+ final Priority other = threshold;
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/LayoutTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/LayoutTest.java
new file mode 100644
index 00000000000..991c3e93d83
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/LayoutTest.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;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for Layout.
+ *
+ */
+public class LayoutTest extends TestCase {
+
+ /**
+ * Concrete Layout class for tests.
+ */
+ private static final class MockLayout extends Layout {
+ /**
+ * @{inheritDoc}
+ */
+ public void activateOptions() {
+ }
+
+ /**
+ * @{inheritDoc}
+ */
+ public String format(final LoggingEvent event) {
+ return "Mock";
+ }
+
+ /**
+ * @{inheritDoc}
+ */
+ public boolean ignoresThrowable() {
+ return true;
+ }
+ }
+
+ /**
+ * Expected content type.
+ */
+ private final String contentType;
+
+ /**
+ * Expected value for ignoresThrowable.
+ */
+ private final boolean ignoresThrowable;
+
+ /**
+ * Expected value for header.
+ */
+ private final String header;
+
+ /**
+ * Expected value for footer.
+ */
+ private final String footer;
+
+ /**
+ * Construct a new instance of LayoutTest.
+ *
+ * @param testName test name.
+ */
+ public LayoutTest(final String testName) {
+ super(testName);
+ contentType = "text/plain";
+ ignoresThrowable = true;
+ header = null;
+ footer = null;
+ }
+
+ /**
+ * Constructor for use by derived tests.
+ *
+ * @param testName name of test.
+ * @param expectedContentType expected value for getContentType().
+ * @param expectedIgnoresThrowable expected value for ignoresThrowable().
+ * @param expectedHeader expected value for getHeader().
+ * @param expectedFooter expected value for getFooter().
+ */
+ protected LayoutTest(final String testName, final String expectedContentType, final boolean expectedIgnoresThrowable, final String expectedHeader,
+ final String expectedFooter) {
+ super(testName);
+ contentType = expectedContentType;
+ ignoresThrowable = expectedIgnoresThrowable;
+ header = expectedHeader;
+ footer = expectedFooter;
+ }
+
+ /**
+ * Creates layout for test.
+ *
+ * @return new instance of Layout.
+ */
+ protected Layout createLayout() {
+ return new MockLayout();
+ }
+
+ /**
+ * Tests format.
+ *
+ * @throws Exception derived tests, particular XMLLayoutTest, may throw exceptions.
+ */
+ public void testFormat() throws Exception {
+ Logger logger = Logger.getLogger("org.apache.log4j.LayoutTest");
+ LoggingEvent event = new LoggingEvent("org.apache.log4j.Logger", logger, Level.INFO, "Hello, World", null);
+ String result = createLayout().format(event);
+ assertEquals("Mock", result);
+ }
+
+ /**
+ * Tests getContentType.
+ */
+ public void testGetContentType() {
+ assertEquals(contentType, createLayout().getContentType());
+ }
+
+ /**
+ * Tests getFooter.
+ */
+ public void testGetFooter() {
+ assertEquals(footer, createLayout().getFooter());
+ }
+
+ /**
+ * Tests getHeader.
+ */
+ public void testGetHeader() {
+ assertEquals(header, createLayout().getHeader());
+ }
+
+ /**
+ * Tests ignoresThrowable.
+ */
+ public void testIgnoresThrowable() {
+ assertEquals(ignoresThrowable, createLayout().ignoresThrowable());
+ }
+
+ /**
+ * Tests Layout.LINE_SEP.
+ */
+ public void testLineSep() {
+ assertEquals(System.getProperty("line.separator"), Layout.LINE_SEP);
+ }
+
+ /**
+ * Tests Layout.LINE_SEP.
+ */
+ public void testLineSepLen() {
+ assertEquals(Layout.LINE_SEP.length(), Layout.LINE_SEP_LEN);
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/LevelTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/LevelTest.java
index bb991ad33da..5f9c673d89d 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/LevelTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/LevelTest.java
@@ -17,13 +17,14 @@
package org.apache.log4j;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
import java.util.Locale;
import org.apache.log4j.util.SerializationTestHelper;
import org.junit.Test;
-import static org.junit.Assert.*;
-
/**
* Tests of Level.
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/ListAppender.java b/log4j-1.2-api/src/test/java/org/apache/log4j/ListAppender.java
new file mode 100644
index 00000000000..c1d5a799aaf
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/ListAppender.java
@@ -0,0 +1,87 @@
+/*
+ * 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;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Used to test Log4j 1 support.
+ */
+public class ListAppender extends AppenderSkeleton {
+ // Use Collections.synchronizedList rather than CopyOnWriteArrayList because we expect
+ // more frequent writes than reads.
+ final List events = Collections.synchronizedList(new ArrayList<>());
+
+ private final List messages = Collections.synchronizedList(new ArrayList<>());
+
+
+ private static final String WINDOWS_LINE_SEP = "\r\n";
+
+ @Override
+ protected void append(LoggingEvent event) {
+ Layout layout = getLayout();
+ if (layout != null) {
+ String result = layout.format(event);
+ if (result != null) {
+ messages.add(result);
+ }
+ } else {
+ events.add(event);
+ }
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public boolean requiresLayout() {
+ return false;
+ }
+
+ /** Returns an immutable snapshot of captured log events */
+ public List getEvents() {
+ return Collections.unmodifiableList(new ArrayList<>(events));
+ }
+
+ /** Returns an immutable snapshot of captured messages */
+ public List getMessages() {
+ return Collections.unmodifiableList(new ArrayList<>(messages));
+ }
+
+ /**
+ * Polls the messages list for it to grow to a given minimum size at most timeout timeUnits and return a copy of
+ * what we have so far.
+ */
+ public List getMessages(final int minSize, final long timeout, final TimeUnit timeUnit) throws InterruptedException {
+ final long endMillis = System.currentTimeMillis() + timeUnit.toMillis(timeout);
+ while (messages.size() < minSize && System.currentTimeMillis() < endMillis) {
+ Thread.sleep(100);
+ }
+ return getMessages();
+ }
+
+ public String toString() {
+ return String.format("ListAppender[%s]", getName());
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/LogManagerTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/LogManagerTest.java
new file mode 100644
index 00000000000..b3d56b21813
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/LogManagerTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link LogManager}.
+ */
+public class LogManagerTest {
+
+ private static final String SIMPLE_NAME = LogManagerTest.class.getSimpleName();
+
+ List getCurrentLoggerNames() {
+ return Collections.list((Enumeration) LogManager.getCurrentLoggers()).stream().map(Logger::getName).collect(Collectors.toList());
+ }
+
+ @Test
+ public void testGetCurrentLoggers() {
+ Logger.getLogger(SIMPLE_NAME);
+ Logger.getLogger(SIMPLE_NAME + ".foo");
+ Logger.getLogger(SIMPLE_NAME + ".foo.bar");
+ final List names = getCurrentLoggerNames();
+ assertTrue(names.contains(SIMPLE_NAME));
+ assertTrue(names.contains(SIMPLE_NAME + ".foo"));
+ assertTrue(names.contains(SIMPLE_NAME + ".foo.bar"));
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithMDCTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithMDCTest.java
index c48e35ece7a..ece28261648 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithMDCTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithMDCTest.java
@@ -16,14 +16,15 @@
*/
package org.apache.log4j;
-import org.apache.logging.log4j.junit.LoggerContextRule;
-import org.apache.logging.log4j.test.appender.ListAppender;
-import org.junit.ClassRule;
-import org.junit.Test;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import java.util.List;
-import static org.junit.Assert.*;
+import org.apache.logging.log4j.core.test.appender.ListAppender;
+import org.apache.logging.log4j.core.test.junit.LoggerContextRule;
+import org.junit.ClassRule;
+import org.junit.Test;
/**
* Test logging with MDC values.
@@ -36,17 +37,22 @@ public class LogWithMDCTest {
public static final LoggerContextRule CTX = new LoggerContextRule(CONFIG);
@Test
- public void testMDC() throws Exception {
+ public void testMDC() {
MDC.put("Key1", "John");
MDC.put("Key2", "Smith");
- final Logger logger = Logger.getLogger("org.apache.test.logging");
- logger.debug("This is a test");
- final ListAppender listApp = (ListAppender) CTX.getAppender("List");
- assertNotNull(listApp);
- final List msgs = listApp.getMessages();
- assertNotNull("No messages received", msgs);
- assertTrue(msgs.size() == 1);
- assertTrue("Key1 is missing", msgs.get(0).contains("Key1=John"));
- assertTrue("Key2 is missing", msgs.get(0).contains("Key2=Smith"));
+ try {
+ final Logger logger = Logger.getLogger("org.apache.test.logging");
+ logger.debug("This is a test");
+ final ListAppender listApp = (ListAppender) CTX.getAppender("List");
+ assertNotNull(listApp);
+ final List msgs = listApp.getMessages();
+ assertNotNull("No messages received", msgs);
+ assertTrue(msgs.size() == 1);
+ assertTrue("Key1 is missing", msgs.get(0).contains("Key1=John"));
+ assertTrue("Key2 is missing", msgs.get(0).contains("Key2=Smith"));
+ } finally {
+ MDC.remove("Key1");
+ MDC.remove("Key2");
+ }
}
}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithRouteTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithRouteTest.java
index 06ad4e8090c..19a464228bd 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithRouteTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithRouteTest.java
@@ -16,38 +16,44 @@
*/
package org.apache.log4j;
-import org.apache.logging.log4j.junit.LoggerContextRule;
-import org.apache.logging.log4j.test.appender.ListAppender;
-import org.junit.ClassRule;
-import org.junit.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.test.appender.ListAppender;
+import org.apache.logging.log4j.core.test.junit.LoggerContextSource;
+import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
+import org.junit.jupiter.api.Test;
/**
* Test passing MDC values to the Routing appender.
*/
+@UsingThreadContextMap
public class LogWithRouteTest {
private static final String CONFIG = "log-RouteWithMDC.xml";
- @ClassRule
- public static final LoggerContextRule CTX = new LoggerContextRule(CONFIG);
-
@Test
- public void testMDC() throws Exception {
+ @LoggerContextSource(CONFIG)
+ public void testMDC(final Configuration configuration) {
MDC.put("Type", "Service");
MDC.put("Name", "John Smith");
- final Logger logger = Logger.getLogger("org.apache.test.logging");
- logger.debug("This is a test");
- final ListAppender listApp = (ListAppender) CTX.getAppender("List");
- assertNotNull(listApp);
- final List msgs = listApp.getMessages();
- assertNotNull("No messages received", msgs);
- assertTrue(msgs.size() == 1);
- assertTrue("Type is missing", msgs.get(0).contains("Type=Service"));
- assertTrue("Name is missing", msgs.get(0).contains("Name=John Smith"));
+ try {
+ final Logger logger = Logger.getLogger("org.apache.test.logging");
+ logger.debug("This is a test");
+ final ListAppender listApp = configuration.getAppender("List");
+ assertNotNull(listApp);
+ final List msgs = listApp.getMessages();
+ assertNotNull(msgs, "No messages received");
+ assertEquals(1, msgs.size());
+ assertTrue(msgs.get(0).contains("Type=Service"), "Type is missing");
+ assertTrue(msgs.get(0).contains("Name=John Smith"), "Name is missing");
+ } finally {
+ MDC.remove("Type");
+ MDC.remove("Name");
+ }
}
}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/LoggerJira3410Test.java b/log4j-1.2-api/src/test/java/org/apache/log4j/LoggerJira3410Test.java
new file mode 100644
index 00000000000..bdb661a8b40
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/LoggerJira3410Test.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.config.TestConfigurator;
+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.util.SortedArrayStringMap;
+import org.junit.Test;
+
+/**
+ * Tests Jira3410.
+ */
+public class LoggerJira3410Test {
+
+ @Test
+ public void test() throws Exception {
+ try (LoggerContext loggerContext = TestConfigurator.configure("target/test-classes/log4j1-list.properties")) {
+ final Logger logger = LogManager.getLogger("test");
+ //
+ Map map = new HashMap<>(1);
+ map.put(Long.MAX_VALUE, 1);
+ logger.debug(map);
+ //
+ map.put(null, null);
+ logger.debug(map);
+ //
+ logger.debug(new SortedArrayStringMap((Map) map));
+ //
+ final Configuration configuration = loggerContext.getConfiguration();
+ final Map appenders = configuration.getAppenders();
+ ListAppender listAppender = null;
+ for (final Map.Entry entry : appenders.entrySet()) {
+ if (entry.getKey().equals("list")) {
+ listAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+ }
+ }
+ assertNotNull("No Message Appender", listAppender);
+ final List messages = listAppender.getMessages();
+ assertTrue("No messages", messages != null && !messages.isEmpty());
+ final String msg0 = messages.get(0);
+ final String msg1 = messages.get(1);
+ final String msg2 = messages.get(2);
+ // TODO Should be 1, not "1".
+ // TODO Where are the {} characters?
+ assertTrue(msg0, msg0.trim().endsWith(Long.MAX_VALUE + "=\"1\""));
+ //
+ // TODO Should be 1, not "1".
+ // TODO Should be null, not "null".
+ // TODO Where are the {} characters?
+ // TODO Where is the , characters?
+ assertTrue(msg1, msg1.trim().endsWith("null=\"null\" " + Long.MAX_VALUE + "=\"1\""));
+ //
+ assertTrue(msg2, msg2.trim().endsWith("{null=null, " + Long.MAX_VALUE + "=1}"));
+ }
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/LoggerTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/LoggerTest.java
index d21fa6243df..63403010654 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/LoggerTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/LoggerTest.java
@@ -17,6 +17,13 @@
package org.apache.log4j;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
@@ -26,14 +33,13 @@
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.config.Property;
import org.apache.logging.log4j.core.layout.PatternLayout;
-import org.apache.logging.log4j.test.appender.ListAppender;
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
+import org.apache.logging.log4j.core.test.appender.ListAppender;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
/**
* Used for internal unit testing the Logger class.
@@ -50,28 +56,26 @@ public class LoggerTest {
// A short message.
static String MSG = "M";
- static ConfigurationFactory configurationFactory = new BasicConfigurationFactory();
-
- @BeforeClass
+ @BeforeAll
public static void setUpClass() {
rbUS = ResourceBundle.getBundle("L7D", new Locale("en", "US"));
assertNotNull(rbUS);
rbFR = ResourceBundle.getBundle("L7D", new Locale("fr", "FR"));
- assertNotNull("Got a null resource bundle.", rbFR);
+ assertNotNull(rbFR, "Got a null resource bundle.");
rbCH = ResourceBundle.getBundle("L7D", new Locale("fr", "CH"));
- assertNotNull("Got a null resource bundle.", rbCH);
+ assertNotNull(rbCH, "Got a null resource bundle.");
- ConfigurationFactory.setConfigurationFactory(configurationFactory);
+ System.setProperty(ConfigurationFactory.CONFIGURATION_FACTORY_PROPERTY, BasicConfigurationFactory.class.getName());
}
- @AfterClass
+ @AfterAll
public static void tearDownClass() {
- ConfigurationFactory.removeConfigurationFactory(configurationFactory);
+ System.clearProperty(ConfigurationFactory.CONFIGURATION_FACTORY_PROPERTY);
}
- @After
+ @AfterEach
public void tearDown() {
LoggerContext.getContext().reconfigure();
a1 = null;
@@ -117,24 +121,25 @@ public void testAppender2() {
@Test
public void testAdditivity1() {
final Logger loggerA = Logger.getLogger("a");
+ assertEquals(Level.DEBUG, loggerA.getLevel());
final Logger loggerAB = Logger.getLogger("a.b");
- final CountingAppender coutingAppender = new CountingAppender();
- coutingAppender.start();
+ final CountingAppender countingAppender = new CountingAppender();
+ countingAppender.start();
try {
- loggerA.getLogger().addAppender(coutingAppender);
+ ((org.apache.logging.log4j.core.Logger) loggerA.getLogger()).addAppender(countingAppender);
- assertEquals(0, coutingAppender.counter);
+ assertEquals(0, countingAppender.counter);
loggerAB.debug(MSG);
- assertEquals(1, coutingAppender.counter);
+ assertEquals(1, countingAppender.counter);
loggerAB.info(MSG);
- assertEquals(2, coutingAppender.counter);
+ assertEquals(2, countingAppender.counter);
loggerAB.warn(MSG);
- assertEquals(3, coutingAppender.counter);
+ assertEquals(3, countingAppender.counter);
loggerAB.error(MSG);
- assertEquals(4, coutingAppender.counter);
- coutingAppender.stop();
+ assertEquals(4, countingAppender.counter);
+ countingAppender.stop();
} finally {
- loggerA.getLogger().removeAppender(coutingAppender);
+ ((org.apache.logging.log4j.core.Logger) loggerA.getLogger()).removeAppender(countingAppender);
}
}
@@ -154,8 +159,8 @@ public void testAdditivity2() {
ca2.start();
try {
- a.getLogger().addAppender(ca1);
- abc.getLogger().addAppender(ca2);
+ ((org.apache.logging.log4j.core.Logger) a.getLogger()).addAppender(ca1);
+ ((org.apache.logging.log4j.core.Logger) abc.getLogger()).addAppender(ca2);
assertEquals(ca1.counter, 0);
assertEquals(ca2.counter, 0);
@@ -174,8 +179,8 @@ public void testAdditivity2() {
ca1.stop();
ca2.stop();
} finally {
- a.getLogger().removeAppender(ca1);
- abc.getLogger().removeAppender(ca2);
+ ((org.apache.logging.log4j.core.Logger) a.getLogger()).removeAppender(ca1);
+ ((org.apache.logging.log4j.core.Logger) abc.getLogger()).removeAppender(ca2);
}}
/**
@@ -196,9 +201,9 @@ public void testAdditivity3() {
final CountingAppender caABC = new CountingAppender();
caABC.start();
try {
- root.getLogger().addAppender(caRoot);
- a.getLogger().addAppender(caA);
- abc.getLogger().addAppender(caABC);
+ ((org.apache.logging.log4j.core.Logger) root.getLogger()).addAppender(caRoot);
+ ((org.apache.logging.log4j.core.Logger) a.getLogger()).addAppender(caA);
+ ((org.apache.logging.log4j.core.Logger) abc.getLogger()).addAppender(caABC);
assertEquals(caRoot.counter, 0);
assertEquals(caA.counter, 0);
@@ -224,9 +229,9 @@ public void testAdditivity3() {
caA.stop();
caABC.stop();
} finally {
- root.getLogger().removeAppender(caRoot);
- a.getLogger().removeAppender(caA);
- abc.getLogger().removeAppender(caABC);
+ ((org.apache.logging.log4j.core.Logger) root.getLogger()).removeAppender(caRoot);
+ ((org.apache.logging.log4j.core.Logger) a.getLogger()).removeAppender(caA);
+ ((org.apache.logging.log4j.core.Logger) abc.getLogger()).removeAppender(caABC);
}}
/* Don't support getLoggerRepository
@@ -317,7 +322,7 @@ public void testRB2() {
final Logger root = Logger.getRootLogger();
root.setResourceBundle(rbUS);
ResourceBundle t = root.getResourceBundle();
- assertTrue(t == rbUS);
+ assertSame(t, rbUS);
final Logger x = Logger.getLogger("x");
final Logger x_y = Logger.getLogger("x.y");
@@ -337,7 +342,7 @@ public void testRB3() {
final Logger root = Logger.getRootLogger();
root.setResourceBundle(rbUS);
ResourceBundle t = root.getResourceBundle();
- assertTrue(t == rbUS);
+ assertSame(t, rbUS);
final Logger x = Logger.getLogger("x");
final Logger x_y = Logger.getLogger("x.y");
@@ -389,7 +394,7 @@ public void testTrace() {
final ListAppender appender = new ListAppender("List");
appender.start();
final Logger root = Logger.getRootLogger();
- root.getLogger().addAppender(appender);
+ ((org.apache.logging.log4j.core.Logger) root.getLogger()).addAppender(appender);
root.setLevel(Level.INFO);
final Logger tracer = Logger.getLogger("com.example.Tracer");
@@ -405,7 +410,7 @@ public void testTrace() {
assertEquals(org.apache.logging.log4j.Level.TRACE, event.getLevel());
assertEquals("Message 1", event.getMessage().getFormat());
appender.stop();
- root.getLogger().removeAppender(appender);
+ ((org.apache.logging.log4j.core.Logger) root.getLogger()).removeAppender(appender);
}
/**
@@ -417,7 +422,7 @@ public void testTraceWithException() {
appender.start();
final Logger root = Logger.getRootLogger();
try {
- root.getLogger().addAppender(appender);
+ ((org.apache.logging.log4j.core.Logger) root.getLogger()).addAppender(appender);
root.setLevel(Level.INFO);
final Logger tracer = Logger.getLogger("com.example.Tracer");
@@ -435,7 +440,7 @@ public void testTraceWithException() {
assertEquals("Message 1", event.getMessage().getFormattedMessage());
appender.stop();
} finally {
- root.getLogger().removeAppender(appender);
+ ((org.apache.logging.log4j.core.Logger) root.getLogger()).removeAppender(appender);
}
}
@@ -448,7 +453,7 @@ public void testIsTraceEnabled() {
appender.start();
final Logger root = Logger.getRootLogger();
try {
- root.getLogger().addAppender(appender);
+ ((org.apache.logging.log4j.core.Logger) root.getLogger()).addAppender(appender);
root.setLevel(Level.INFO);
final Logger tracer = Logger.getLogger("com.example.Tracer");
@@ -458,31 +463,31 @@ public void testIsTraceEnabled() {
assertFalse(root.isTraceEnabled());
appender.stop();
} finally {
- root.getLogger().removeAppender(appender);
+ ((org.apache.logging.log4j.core.Logger) root.getLogger()).removeAppender(appender);
}
}
@Test
@SuppressWarnings("deprecation")
public void testLog() {
- final PatternLayout layout = PatternLayout.newBuilder().withPattern("%d %C %L %m").build();
+ final PatternLayout layout = PatternLayout.newBuilder().setPattern("%d %C %L %m").build();
final ListAppender appender = new ListAppender("List", null, layout, false, false);
appender.start();
final Logger root = Logger.getRootLogger();
try {
- root.getLogger().addAppender(appender);
+ ((org.apache.logging.log4j.core.Logger) root.getLogger()).addAppender(appender);
root.setLevel(Level.INFO);
final MyLogger log = new MyLogger(root);
log.logInfo("This is a test", null);
root.log(Priority.INFO, "Test msg2", null);
root.log(Priority.INFO, "Test msg3");
final List msgs = appender.getMessages();
- assertTrue("Incorrect number of messages", msgs.size() == 3);
+ assertEquals(3, msgs.size(), "Incorrect number of messages");
final String msg = msgs.get(0);
- assertTrue("Message contains incorrect class name: " + msg, msg.contains(LoggerTest.class.getName()));
+ assertTrue(msg.contains(LoggerTest.class.getName()), "Message contains incorrect class name: " + msg);
appender.stop();
} finally {
- root.getLogger().removeAppender(appender);
+ ((org.apache.logging.log4j.core.Logger) root.getLogger()).removeAppender(appender);
}
}
@@ -502,12 +507,10 @@ public void logInfo(final String msg, final Throwable t) {
private static class CountingAppender extends AbstractAppender {
- private static final long serialVersionUID = 1L;
-
int counter;
CountingAppender() {
- super("Counter", null, null);
+ super("Counter", null, null, true, Property.EMPTY_ARRAY);
counter = 0;
}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/LoggingTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/LoggingTest.java
index a30dd881bf5..e72f3bec7c8 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/LoggingTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/LoggingTest.java
@@ -16,11 +16,11 @@
*/
package org.apache.log4j;
-import org.apache.logging.log4j.junit.LoggerContextRule;
-import org.junit.ClassRule;
-import org.junit.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.Assert.*;
+import org.apache.logging.log4j.core.test.junit.LoggerContextSource;
+import org.junit.jupiter.api.Test;
/**
*
@@ -29,15 +29,13 @@ public class LoggingTest {
private static final String CONFIG = "log4j2-config.xml";
- @ClassRule
- public static final LoggerContextRule CTX = new LoggerContextRule(CONFIG);
-
@Test
+ @LoggerContextSource(CONFIG)
public void testParent() {
final Logger logger = Logger.getLogger("org.apache.test.logging.Test");
final Category parent = logger.getParent();
- assertNotNull("No parent Logger", parent);
- assertEquals("Incorrect parent logger", "org.apache.test.logging", parent.getName());
+ assertNotNull(parent, "No parent Logger");
+ assertEquals("org.apache.test.logging", parent.getName(), "Incorrect parent logger");
}
}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/MDCTestCase.java b/log4j-1.2-api/src/test/java/org/apache/log4j/MDCTestCase.java
index c0e5ba59418..f86a6e67282 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/MDCTestCase.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/MDCTestCase.java
@@ -5,9 +5,9 @@
* 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.
@@ -34,14 +34,14 @@ public void tearDown() {
}
@Test
- public void testPut() throws Exception {
+ public void testPut() {
MDC.put("key", "some value");
Assert.assertEquals("some value", MDC.get("key"));
Assert.assertEquals(1, MDC.getContext().size());
}
@Test
- public void testRemoveLastKey() throws Exception {
+ public void testRemoveLastKey() {
MDC.put("key", "some value");
MDC.remove("key");
}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/NDCTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/NDCTest.java
index c8baf0ff9ba..41ba01510db 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/NDCTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/NDCTest.java
@@ -16,6 +16,8 @@
*/
package org.apache.log4j;
+import java.util.Stack;
+
import org.apache.logging.log4j.util.Strings;
import org.junit.Assert;
import org.junit.Test;
@@ -33,4 +35,14 @@ public void testPeekEmpty() {
NDC.clear();
Assert.assertEquals(Strings.EMPTY, NDC.peek());
}
+
+ @SuppressWarnings({"rawtypes"})
+ @Test
+ public void testCompileCloneToInherit() {
+ NDC.inherit(NDC.cloneStack());
+ final Stack stackRaw = NDC.cloneStack();
+ NDC.inherit(stackRaw);
+ final Stack> stackAny = NDC.cloneStack();
+ NDC.inherit(stackAny);
+ }
}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/PriorityTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/PriorityTest.java
index 63321a74d7e..09f6386b21f 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/PriorityTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/PriorityTest.java
@@ -17,12 +17,14 @@
package org.apache.log4j;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
import java.util.Locale;
import org.junit.Test;
-import static org.junit.Assert.*;
-
/**
* Tests of Priority.
*
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/PropertyConfiguratorTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/PropertyConfiguratorTest.java
new file mode 100644
index 00000000000..347bfd0c4cc
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/PropertyConfiguratorTest.java
@@ -0,0 +1,427 @@
+/*
+ * 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;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Properties;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.OptionHandler;
+import org.apache.log4j.spi.RootLogger;
+import org.apache.log4j.spi.ThrowableRenderer;
+import org.apache.log4j.spi.ThrowableRendererSupport;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link PropertyConfigurator}.
+ */
+public class PropertyConfiguratorTest {
+
+ /**
+ * Mock definition of FilterBasedTriggeringPolicy from extras companion.
+ */
+ public static final class FilterBasedTriggeringPolicy extends TriggeringPolicy {
+ private Filter filter;
+
+ public FilterBasedTriggeringPolicy() {
+ }
+
+ public Filter getFilter() {
+ return filter;
+
+ }
+
+ public void setFilter(final Filter val) {
+ filter = val;
+ }
+ }
+
+ /**
+ * Mock definition of FixedWindowRollingPolicy from extras companion.
+ */
+ public static final class FixedWindowRollingPolicy extends RollingPolicy {
+ private String activeFileName;
+ private String fileNamePattern;
+ private int minIndex;
+
+ public FixedWindowRollingPolicy() {
+ minIndex = -1;
+ }
+
+ public String getActiveFileName() {
+ return activeFileName;
+ }
+
+ public String getFileNamePattern() {
+ return fileNamePattern;
+ }
+
+ public int getMinIndex() {
+ return minIndex;
+ }
+
+ public void setActiveFileName(final String val) {
+ activeFileName = val;
+ }
+
+ public void setFileNamePattern(final String val) {
+ fileNamePattern = val;
+ }
+
+ public void setMinIndex(final int val) {
+ minIndex = val;
+ }
+ }
+
+ /**
+ * Mock ThrowableRenderer for testThrowableRenderer. See bug 45721.
+ */
+ public static class MockThrowableRenderer implements ThrowableRenderer, OptionHandler {
+ private boolean activated = false;
+ private boolean showVersion = true;
+
+ public MockThrowableRenderer() {
+ }
+
+ @Override
+ public void activateOptions() {
+ activated = true;
+ }
+
+ @Override
+ public String[] doRender(final Throwable t) {
+ return new String[0];
+ }
+
+ public boolean getShowVersion() {
+ return showVersion;
+ }
+
+ public boolean isActivated() {
+ return activated;
+ }
+
+ public void setShowVersion(final boolean v) {
+ showVersion = v;
+ }
+ }
+
+ /**
+ * Mock definition of org.apache.log4j.rolling.RollingFileAppender from extras companion.
+ */
+ public static final class RollingFileAppender extends AppenderSkeleton {
+ private RollingPolicy rollingPolicy;
+ private TriggeringPolicy triggeringPolicy;
+ private boolean append;
+
+ public RollingFileAppender() {
+
+ }
+
+ @Override
+ public void append(final LoggingEvent event) {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ public boolean getAppend() {
+ return append;
+ }
+
+ public RollingPolicy getRollingPolicy() {
+ return rollingPolicy;
+ }
+
+ public TriggeringPolicy getTriggeringPolicy() {
+ return triggeringPolicy;
+ }
+
+ @Override
+ public boolean requiresLayout() {
+ return true;
+ }
+
+ public void setAppend(final boolean val) {
+ append = val;
+ }
+
+ public void setRollingPolicy(final RollingPolicy policy) {
+ rollingPolicy = policy;
+ }
+
+ public void setTriggeringPolicy(final TriggeringPolicy policy) {
+ triggeringPolicy = policy;
+ }
+ }
+
+ /**
+ * Mock definition of org.apache.log4j.rolling.RollingPolicy from extras companion.
+ */
+ public static class RollingPolicy implements OptionHandler {
+ private boolean activated = false;
+
+ public RollingPolicy() {
+
+ }
+
+ @Override
+ public void activateOptions() {
+ activated = true;
+ }
+
+ public final boolean isActivated() {
+ return activated;
+ }
+
+ }
+
+ /**
+ * Mock definition of TriggeringPolicy from extras companion.
+ */
+ public static class TriggeringPolicy implements OptionHandler {
+ private boolean activated = false;
+
+ public TriggeringPolicy() {
+
+ }
+
+ @Override
+ public void activateOptions() {
+ activated = true;
+ }
+
+ public final boolean isActivated() {
+ return activated;
+ }
+
+ }
+
+ private static final String FILTER1_PROPERTIES = "target/test-classes/log4j1-1.2.17/input/filter1.properties";
+
+ private static final String CAT_A_NAME = "categoryA";
+
+ private static final String CAT_B_NAME = "categoryB";
+
+ private static final String CAT_C_NAME = "categoryC";
+
+ /**
+ * Test for bug 40944. Did not catch IllegalArgumentException on Properties.load and close input stream.
+ *
+ * @throws IOException if IOException creating properties file.
+ */
+ @Test
+ public void testBadUnicodeEscape() throws IOException {
+ final String fileName = "target/badescape.properties";
+ try (FileWriter writer = new FileWriter(fileName)) {
+ writer.write("log4j.rootLogger=\\uXX41");
+ }
+ PropertyConfigurator.configure(fileName);
+ final File file = new File(fileName);
+ assertTrue(file.delete());
+ assertFalse(file.exists());
+ }
+
+ /**
+ * Tests configuring Log4J from an InputStream.
+ *
+ * @since 1.2.17
+ */
+ @Test
+ public void testInputStream() throws IOException {
+ final Path file = Paths.get(FILTER1_PROPERTIES);
+ assertTrue(Files.exists(file));
+ try (InputStream inputStream = Files.newInputStream(file)) {
+ PropertyConfigurator.configure(inputStream);
+ }
+ this.validateNested();
+ LogManager.resetConfiguration();
+ }
+
+ /**
+ * Test for bug 47465. configure(URL) did not close opened JarURLConnection.
+ *
+ * @throws IOException if IOException creating properties jar.
+ */
+ @Test
+ public void testJarURL() throws IOException {
+ final File dir = new File("output");
+ dir.mkdirs();
+ final File file = new File("output/properties.jar");
+ try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(file))) {
+ zos.putNextEntry(new ZipEntry(LogManager.DEFAULT_CONFIGURATION_FILE));
+ zos.write("log4j.rootLogger=debug".getBytes());
+ zos.closeEntry();
+ }
+ final URL url = new URL("jar:" + file.toURI().toURL() + "!/" + LogManager.DEFAULT_CONFIGURATION_FILE);
+ PropertyConfigurator.configure(url);
+ assertTrue(file.delete());
+ assertFalse(file.exists());
+ }
+
+ @Test
+ public void testLocalVsGlobal() {
+ LoggerRepository repos1, repos2;
+ final Logger catA = Logger.getLogger(CAT_A_NAME);
+ final Logger catB = Logger.getLogger(CAT_B_NAME);
+ final Logger catC = Logger.getLogger(CAT_C_NAME);
+
+ final Properties globalSettings = new Properties();
+ globalSettings.put("log4j.logger." + CAT_A_NAME, Level.WARN.toString());
+ globalSettings.put("log4j.logger." + CAT_B_NAME, Level.WARN.toString());
+ globalSettings.put("log4j.logger." + CAT_C_NAME, Level.DEBUG.toString());
+ PropertyConfigurator.configure(globalSettings);
+ assertEquals(Level.WARN, catA.getLevel());
+ assertEquals(Level.WARN, catB.getLevel());
+ assertEquals(Level.DEBUG, catC.getLevel());
+
+ assertEquals(Level.WARN, catA.getLoggerRepository().getLogger(CAT_A_NAME).getLevel());
+ assertEquals(Level.WARN, catB.getLoggerRepository().getLogger(CAT_B_NAME).getLevel());
+ assertEquals(Level.DEBUG, catC.getLoggerRepository().getLogger(CAT_C_NAME).getLevel());
+
+ final Properties repos1Settings = new Properties();
+ repos1Settings.put("log4j.logger." + CAT_A_NAME, Level.DEBUG.toString());
+ repos1Settings.put("log4j.logger." + CAT_B_NAME, Level.INFO.toString());
+ repos1 = new Hierarchy(new RootLogger(Level.OFF));
+ new PropertyConfigurator().doConfigure(repos1Settings, repos1);
+ assertEquals(Level.DEBUG, repos1.getLogger(CAT_A_NAME).getLevel());
+ assertEquals(Level.INFO, repos1.getLogger(CAT_B_NAME).getLevel());
+
+ final Properties repos2Settings = new Properties();
+ repos2Settings.put("log4j.logger." + CAT_A_NAME, Level.INFO.toString());
+ repos2Settings.put("log4j.logger." + CAT_B_NAME, Level.DEBUG.toString());
+ repos2 = new Hierarchy(new RootLogger(Level.OFF));
+ new PropertyConfigurator().doConfigure(repos2Settings, repos2);
+ assertEquals(Level.INFO, repos2.getLogger(CAT_A_NAME).getLevel());
+ assertEquals(Level.DEBUG, repos2.getLogger(CAT_B_NAME).getLevel());
+ }
+
+ /**
+ * Tests processing of nested objects, see bug 36384.
+ */
+ public void testNested() {
+ try {
+ PropertyConfigurator.configure(FILTER1_PROPERTIES);
+ this.validateNested();
+ } finally {
+ LogManager.resetConfiguration();
+ }
+ }
+
+ /**
+ * Test processing of log4j.reset property, see bug 17531.
+ */
+ @Test
+ public void testReset() {
+ final VectorAppender appender = new VectorAppender();
+ appender.setName("A1");
+ Logger.getRootLogger().addAppender(appender);
+ final Properties properties = new Properties();
+ properties.put("log4j.reset", "true");
+ PropertyConfigurator.configure(properties);
+ assertNull(Logger.getRootLogger().getAppender("A1"));
+ LogManager.resetConfiguration();
+ }
+
+ /**
+ * Test of log4j.throwableRenderer support. See bug 45721.
+ */
+ public void testThrowableRenderer() {
+ final Properties properties = new Properties();
+ properties.put("log4j.throwableRenderer", "org.apache.log4j.PropertyConfiguratorTest$MockThrowableRenderer");
+ properties.put("log4j.throwableRenderer.showVersion", "false");
+ PropertyConfigurator.configure(properties);
+ final ThrowableRendererSupport repo = (ThrowableRendererSupport) LogManager.getLoggerRepository();
+ final MockThrowableRenderer renderer = (MockThrowableRenderer) repo.getThrowableRenderer();
+ LogManager.resetConfiguration();
+// assertNotNull(renderer);
+// assertEquals(true, renderer.isActivated());
+// assertEquals(false, renderer.getShowVersion());
+ }
+
+ /**
+ * Test for bug 40944. configure(URL) never closed opened stream.
+ *
+ * @throws IOException if IOException creating properties file.
+ */
+ @Test
+ public void testURL() throws IOException {
+ final File file = new File("target/unclosed.properties");
+ try (FileWriter writer = new FileWriter(file)) {
+ writer.write("log4j.rootLogger=debug");
+ }
+ final URL url = file.toURI().toURL();
+ PropertyConfigurator.configure(url);
+ assertTrue(file.delete());
+ assertFalse(file.exists());
+ }
+
+ /**
+ * Test for bug 40944. configure(URL) did not catch IllegalArgumentException and did not close stream.
+ *
+ * @throws IOException if IOException creating properties file.
+ */
+ @Test
+ public void testURLBadEscape() throws IOException {
+ final File file = new File("target/urlbadescape.properties");
+ try (FileWriter writer = new FileWriter(file)) {
+ writer.write("log4j.rootLogger=\\uXX41");
+ }
+ final URL url = file.toURI().toURL();
+ PropertyConfigurator.configure(url);
+ assertTrue(file.delete());
+ assertFalse(file.exists());
+ }
+
+ public void validateNested() {
+ final Logger logger = Logger.getLogger("org.apache.log4j.PropertyConfiguratorTest");
+ final String appenderName = "ROLLING";
+ // Appender OK
+ final Appender appender = logger.getAppender(appenderName);
+ assertNotNull(appender);
+ // Down-cast?
+// final RollingFileAppender rfa = (RollingFileAppender) appender;
+// assertNotNull(appenderName, rfa);
+// final FixedWindowRollingPolicy rollingPolicy = (FixedWindowRollingPolicy) rfa.getRollingPolicy();
+// assertEquals("filterBase-test1.log", rollingPolicy.getActiveFileName());
+// assertEquals("filterBased-test1.%i", rollingPolicy.getFileNamePattern());
+// assertEquals(0, rollingPolicy.getMinIndex());
+// assertTrue(rollingPolicy.isActivated());
+// final FilterBasedTriggeringPolicy triggeringPolicy = (FilterBasedTriggeringPolicy) rfa.getTriggeringPolicy();
+// final LevelRangeFilter filter = (LevelRangeFilter) triggeringPolicy.getFilter();
+// assertTrue(Level.INFO.equals(filter.getLevelMin()));
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/VelocityTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/VelocityTest.java
index 99bd5a2c7c6..c1165d74fd3 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/VelocityTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/VelocityTest.java
@@ -34,7 +34,7 @@
public class VelocityTest {
private static LoggerContext context;
-
+
@BeforeClass
public static void setupClass() {
context = LoggerContext.getContext(false);
@@ -44,8 +44,8 @@ public static void setupClass() {
public static void tearDownClass() {
Configurator.shutdown(context);
StatusLogger.getLogger().reset();
- }
-
+ }
+
@Test
public void testVelocity() {
Velocity.init();
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/bridge/LogEventWrapperTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/bridge/LogEventWrapperTest.java
new file mode 100644
index 00000000000..3f599d64b26
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/bridge/LogEventWrapperTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.bridge;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.core.LogEvent;
+import org.junit.Test;
+
+public class LogEventWrapperTest {
+
+ @Test
+ public void testThread() {
+ Thread currentThread = Thread.currentThread();
+ String threadName = currentThread.getName();
+ LoggingEvent log4j1Event = new LoggingEvent() {
+
+ @Override
+ public String getThreadName() {
+ return threadName;
+ }
+ };
+ LogEvent log4j2Event = new LogEventWrapper(log4j1Event);
+ assertEquals(currentThread.getId(), log4j2Event.getThreadId());
+ assertEquals(currentThread.getPriority(), log4j2Event.getThreadPriority());
+ }
+
+ @Test
+ public void testToImmutable() {
+ LogEventWrapper wrapper = new LogEventWrapper(new LoggingEvent());
+ assertSame(wrapper, wrapper.toImmutable());
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/builders/BuilderManagerTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/builders/BuilderManagerTest.java
new file mode 100644
index 00000000000..562fa317a02
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/builders/BuilderManagerTest.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.log4j.builders;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.FileAppender;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.varia.StringMatchFilter;
+import org.apache.logging.log4j.plugins.di.DI;
+import org.apache.logging.log4j.plugins.di.Injector;
+import org.junit.jupiter.api.Test;
+
+import java.util.Properties;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class BuilderManagerTest {
+
+ /**
+ * This test ensures that instantiation failures due to missing parameters
+ * always return an empty wrapper instead of null, hence disabling the
+ * "instantiate by classname" fallback mechanism for supported components.
+ */
+ @Test
+ public void testReturnInvalidValueOnError() {
+ final PropertiesConfiguration config = new PropertiesConfiguration(null, null);
+ final Injector injector = DI.createInjector();
+ final BuilderManager manager = injector.getInstance(BuilderManager.class);
+ final Properties props = new Properties();
+ props.setProperty("FILE", FileAppender.class.getName());
+ props.setProperty("FILE.filter.1", StringMatchFilter.class.getName());
+ // Parse an invalid StringMatchFilter
+ final Filter filter = manager.parse(StringMatchFilter.class.getName(), "FILE.filter", props, config,
+ BuilderManager.INVALID_FILTER);
+ assertEquals(BuilderManager.INVALID_FILTER, filter);
+ // Parse an invalid FileAppender
+ final Appender appender = manager.parseAppender("FILE", FileAppender.class.getName(), "FILE", "FILE.layout",
+ "FILE.filter.", props, config);
+ assertEquals(BuilderManager.INVALID_APPENDER, appender);
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/builders/Log4j2ListAppenderBuilder.java b/log4j-1.2-api/src/test/java/org/apache/log4j/builders/Log4j2ListAppenderBuilder.java
new file mode 100644
index 00000000000..ca730ef35d4
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/builders/Log4j2ListAppenderBuilder.java
@@ -0,0 +1,88 @@
+/*
+ * 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.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.builders.appender.AppenderBuilder;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.core.test.appender.ListAppender;
+import org.apache.logging.log4j.plugins.Namespace;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.w3c.dom.Element;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.apache.log4j.builders.BuilderManager.NAMESPACE;
+import static org.apache.log4j.xml.XmlConfiguration.*;
+
+/**
+ * Builder for the native Log4j 2.x list appender to be used in the tests.
+ */
+@Namespace(NAMESPACE)
+@Plugin("org.apache.logging.log4j.test.appender.ListAppender")
+public class Log4j2ListAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+ public Log4j2ListAppenderBuilder() {
+ }
+
+ public Log4j2ListAppenderBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Appender parseAppender(Element element, XmlConfiguration configuration) {
+ final String name = getNameAttribute(element);
+ final AtomicReference layout = new AtomicReference<>();
+ final AtomicReference filter = new AtomicReference<>();
+ forEachElement(element.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case LAYOUT_TAG :
+ layout.set(configuration.parseLayout(currentElement));
+ break;
+ case FILTER_TAG :
+ configuration.addFilter(filter, currentElement);
+ break;
+ default :
+ }
+ });
+ return createAppender(name, layout.get(), filter.get());
+ }
+
+ @Override
+ public Appender parseAppender(String name, String appenderPrefix, String layoutPrefix, String filterPrefix,
+ Properties props, PropertiesConfiguration configuration) {
+ final Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+ final Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+ return createAppender(name, layout, filter);
+ }
+
+ private Appender createAppender(String name, Layout layout, Filter filter) {
+ final org.apache.logging.log4j.core.Layout> log4j2Layout = LayoutAdapter.adapt(layout);
+ return AppenderWrapper.adapt(
+ ListAppender.newBuilder()
+ .setName(name)
+ .setLayout(log4j2Layout)
+ .setFilter(AbstractBuilder.buildFilters(null, filter))
+ .build());
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/builders/layout/PatternLayoutBuilderTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/builders/layout/PatternLayoutBuilderTest.java
new file mode 100644
index 00000000000..e8a8280b169
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/builders/layout/PatternLayoutBuilderTest.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.log4j.builders.layout;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Arrays;
+import java.util.stream.Stream;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+public class PatternLayoutBuilderTest {
+
+ static Stream patterns() {
+ return Arrays.asList(
+ Arguments.of("%p", "%v1Level"),
+ Arguments.of("%100p", "%100v1Level"),
+ Arguments.of("%-100p", "%-100v1Level"),
+ Arguments.of("%x", "%ndc"),
+ Arguments.of("%X", "%properties"),
+ Arguments.of("%.20x", "%.20ndc"),
+ Arguments.of("%pid", "%pid"),
+ Arguments.of("%xEx", "%xEx"),
+ Arguments.of("%XX", "%XX"),
+ Arguments.of("%p id", "%v1Level id"),
+ Arguments.of("%x Ex", "%ndc Ex"),
+ Arguments.of("%X X", "%properties X"))
+ .stream();
+ }
+
+ @ParameterizedTest
+ @MethodSource("patterns")
+ public void testLevelPatternReplacement(final String v1Pattern, final String v2Pattern) {
+ final PatternLayoutBuilder builder = new PatternLayoutBuilder();
+ final PatternLayout layout = (PatternLayout) LayoutAdapter.adapt(builder.createLayout(v1Pattern, null));
+ assertEquals(v2Pattern, layout.getConversionPattern());
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationConverterTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationConverterTest.java
index 9c973ee2b54..bee8a50418c 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationConverterTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationConverterTest.java
@@ -1,19 +1,3 @@
-package org.apache.log4j.config;
-
-import java.io.IOException;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@@ -31,6 +15,28 @@
* limitations under the license.
*/
+package org.apache.log4j.config;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.xml.sax.SAXException;
+
@RunWith(Parameterized.class)
public abstract class AbstractLog4j1ConfigurationConverterTest {
@@ -49,20 +55,32 @@ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attr
private final Path pathIn;
public AbstractLog4j1ConfigurationConverterTest(final Path path) {
- super();
this.pathIn = path;
}
@Test
- public void test() throws IOException {
+ public void test() throws Exception {
final Path tempFile = Files.createTempFile("log4j2", ".xml");
try {
final Log4j1ConfigurationConverter.CommandLineArguments cla = new Log4j1ConfigurationConverter.CommandLineArguments();
cla.setPathIn(pathIn);
cla.setPathOut(tempFile);
Log4j1ConfigurationConverter.run(cla);
+ checkWellFormedXml(tempFile);
+ checkUnnecessaryEscaping(tempFile);
} finally {
Files.deleteIfExists(tempFile);
}
}
+
+ private void checkUnnecessaryEscaping(Path tempFile) throws IOException {
+ for (String line : Files.readAllLines(tempFile)) {
+ assertFalse(line.endsWith("
"));
+ }
+
+ }
+
+ private void checkWellFormedXml(Path xmlFilePath) throws SAXException, IOException, ParserConfigurationException {
+ DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xmlFilePath.toUri().toString());
+ }
}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java
new file mode 100644
index 00000000000..f04ec2bbd8a
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java
@@ -0,0 +1,528 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.URISyntaxException;
+import java.nio.file.FileSystemException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.log4j.ListAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.bridge.AppenderAdapter.Adapter;
+import org.apache.log4j.bridge.FilterAdapter;
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.apache.logging.log4j.core.appender.ConsoleAppender.Target;
+import org.apache.logging.log4j.core.appender.FileAppender;
+import org.apache.logging.log4j.core.appender.NullAppender;
+import org.apache.logging.log4j.core.appender.OutputStreamManager;
+import org.apache.logging.log4j.core.appender.RollingFileAppender;
+import org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.filter.CompositeFilter;
+import org.apache.logging.log4j.core.filter.Filterable;
+import org.apache.logging.log4j.core.filter.ThresholdFilter;
+import org.apache.logging.log4j.core.layout.HtmlLayout;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.core.util.CloseShieldOutputStream;
+
+public abstract class AbstractLog4j1ConfigurationTest {
+
+ abstract Configuration getConfiguration(String configResourcePrefix) throws URISyntaxException, IOException;
+
+ protected LoggerContext configure(String configResourcePrefix) throws URISyntaxException, IOException {
+ Configurator.reconfigure(getConfiguration(configResourcePrefix));
+ return (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+ }
+
+ public void testConsoleCapitalization() throws Exception {
+ final Configuration config = getConfiguration("config-1.2/log4j-capitalization");
+ final Appender capitalized = config.getAppender("ConsoleCapitalized");
+ assertNotNull(capitalized);
+ assertEquals(capitalized.getClass(), ConsoleAppender.class);
+ final Appender javaStyle = config.getAppender("ConsoleJavaStyle");
+ assertNotNull(javaStyle);
+ assertEquals(javaStyle.getClass(), ConsoleAppender.class);
+ testConsoleAppender((ConsoleAppender) capitalized, (ConsoleAppender) javaStyle);
+ }
+
+ private void testConsoleAppender(ConsoleAppender expected, ConsoleAppender actual) {
+ assertEquals("immediateFlush", expected.getImmediateFlush(), actual.getImmediateFlush());
+ assertEquals("target", expected.getTarget(), actual.getTarget());
+ assertEquals("layoutClass", expected.getLayout().getClass(), actual.getLayout().getClass());
+ if (expected.getLayout() instanceof PatternLayout) {
+ patternLayoutEquals((PatternLayout) expected.getLayout(), (PatternLayout) actual.getLayout());
+ }
+ }
+
+ private void patternLayoutEquals(PatternLayout expected, PatternLayout actual) {
+ assertEquals(expected.getCharset(), actual.getCharset());
+ assertEquals(expected.getConversionPattern(), actual.getConversionPattern());
+ }
+
+ private Layout> testConsole(final String configResource) throws Exception {
+ final Configuration configuration = getConfiguration(configResource);
+ final String name = "Console";
+ final ConsoleAppender appender = configuration.getAppender(name);
+ assertNotNull("Missing appender '" + name + "' in configuration " + configResource + " → " + configuration, appender);
+ assertEquals("follow", true, getFollowProperty(appender));
+ assertEquals(Target.SYSTEM_ERR, appender.getTarget());
+ //
+ final LoggerConfig loggerConfig = configuration.getLoggerConfig("com.example.foo");
+ assertNotNull(loggerConfig);
+ assertEquals(Level.DEBUG, loggerConfig.getLevel());
+ // immediateFlush is always true in Log4j 2.x
+ configuration.start();
+ configuration.stop();
+ return appender.getLayout();
+ }
+
+ public void testConsoleTtccLayout() throws Exception {
+ final PatternLayout layout = (PatternLayout) testConsole("config-1.2/log4j-console-TTCCLayout");
+ assertEquals("%d{ISO8601}{CET} %p - %m%n", layout.getConversionPattern());
+ }
+
+ public void testRollingFileAppender() throws Exception {
+ testRollingFileAppender("config-1.2/log4j-RollingFileAppender", "RFA", "target/hadoop.log.%i");
+ }
+
+ public void testDailyRollingFileAppender() throws Exception {
+ testDailyRollingFileAppender("config-1.2/log4j-DailyRollingFileAppender", "DRFA", "target/hadoop.log%d{.dd-MM-yyyy}");
+ }
+
+ public void testRollingFileAppenderWithProperties() throws Exception {
+ testRollingFileAppender("config-1.2/log4j-RollingFileAppender-with-props", "RFA", "target/hadoop.log.%i");
+ }
+
+ public void testSystemProperties1() throws Exception {
+ final String tempFileName = System.getProperty("java.io.tmpdir") + "/hadoop.log";
+ final Path tempFilePath = new File(tempFileName).toPath();
+ Files.deleteIfExists(tempFilePath);
+ final Configuration configuration = getConfiguration("config-1.2/log4j-system-properties-1");
+ try {
+ final RollingFileAppender appender = configuration.getAppender("RFA");
+ assertEquals("append", false, getAppendProperty(appender));
+ assertEquals("bufferSize", 1000, appender.getManager().getBufferSize());
+ assertEquals("immediateFlush", false, appender.getImmediateFlush());
+ final DefaultRolloverStrategy rolloverStrategy = (DefaultRolloverStrategy) appender.getManager().getRolloverStrategy();
+ assertEquals(16, rolloverStrategy.getMaxIndex());
+ final CompositeTriggeringPolicy ctp = (CompositeTriggeringPolicy) appender.getTriggeringPolicy();
+ final TriggeringPolicy[] triggeringPolicies = ctp.getTriggeringPolicies();
+ assertEquals(1, triggeringPolicies.length);
+ final TriggeringPolicy tp = triggeringPolicies[0];
+ assertTrue(tp.getClass().getName(), tp instanceof SizeBasedTriggeringPolicy);
+ final SizeBasedTriggeringPolicy sbtp = (SizeBasedTriggeringPolicy) tp;
+ assertEquals(20 * 1024 * 1024, sbtp.getMaxFileSize());
+ appender.stop(10, TimeUnit.SECONDS);
+ // System.out.println("expected: " + tempFileName + " Actual: " +
+ // appender.getFileName());
+ assertEquals(tempFileName, appender.getFileName());
+ } finally {
+ configuration.start();
+ configuration.stop();
+ try {
+ Files.deleteIfExists(tempFilePath);
+ } catch (final FileSystemException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public void testSystemProperties2() throws Exception {
+ final Configuration configuration = getConfiguration("config-1.2/log4j-system-properties-2");
+ final RollingFileAppender appender = configuration.getAppender("RFA");
+ final String tmpDir = System.getProperty("java.io.tmpdir");
+ assertEquals(tmpDir + "/hadoop.log", appender.getFileName());
+ appender.stop(10, TimeUnit.SECONDS);
+ // Try to clean up
+ try {
+ Path path = new File(appender.getFileName()).toPath();
+ Files.deleteIfExists(path);
+ path = new File("${java.io.tmpdir}").toPath();
+ Files.deleteIfExists(path);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void testRollingFileAppender(final String configResource, final String name, final String filePattern)
+ throws Exception {
+ final Configuration configuration = getConfiguration(configResource);
+ final Appender appender = configuration.getAppender(name);
+ assertNotNull(appender);
+ assertEquals(name, appender.getName());
+ assertTrue(appender.getClass().getName(), appender instanceof RollingFileAppender);
+ final RollingFileAppender rfa = (RollingFileAppender) appender;
+ assertEquals("append", false, getAppendProperty(rfa));
+ assertEquals("bufferSize", 1000, rfa.getManager().getBufferSize());
+ assertEquals("immediateFlush", false, rfa.getImmediateFlush());
+ assertEquals("target/hadoop.log", rfa.getFileName());
+ assertEquals(filePattern, rfa.getFilePattern());
+ final TriggeringPolicy triggeringPolicy = rfa.getTriggeringPolicy();
+ assertNotNull(triggeringPolicy);
+ assertTrue(triggeringPolicy.getClass().getName(), triggeringPolicy instanceof CompositeTriggeringPolicy);
+ final CompositeTriggeringPolicy ctp = (CompositeTriggeringPolicy) triggeringPolicy;
+ final TriggeringPolicy[] triggeringPolicies = ctp.getTriggeringPolicies();
+ assertEquals(1, triggeringPolicies.length);
+ final TriggeringPolicy tp = triggeringPolicies[0];
+ assertTrue(tp.getClass().getName(), tp instanceof SizeBasedTriggeringPolicy);
+ final SizeBasedTriggeringPolicy sbtp = (SizeBasedTriggeringPolicy) tp;
+ assertEquals(256 * 1024 * 1024, sbtp.getMaxFileSize());
+ final RolloverStrategy rolloverStrategy = rfa.getManager().getRolloverStrategy();
+ assertTrue(rolloverStrategy.getClass().getName(), rolloverStrategy instanceof DefaultRolloverStrategy);
+ final DefaultRolloverStrategy drs = (DefaultRolloverStrategy) rolloverStrategy;
+ assertEquals(20, drs.getMaxIndex());
+ configuration.start();
+ configuration.stop();
+ }
+
+ private void testDailyRollingFileAppender(final String configResource, final String name, final String filePattern)
+ throws Exception {
+ final Configuration configuration = getConfiguration(configResource);
+ try {
+ final Appender appender = configuration.getAppender(name);
+ assertNotNull(appender);
+ assertEquals(name, appender.getName());
+ assertTrue(appender.getClass().getName(), appender instanceof RollingFileAppender);
+ final RollingFileAppender rfa = (RollingFileAppender) appender;
+ assertEquals("append", false, getAppendProperty(rfa));
+ assertEquals("bufferSize", 1000, rfa.getManager().getBufferSize());
+ assertEquals("immediateFlush", false, rfa.getImmediateFlush());
+ assertEquals("target/hadoop.log", rfa.getFileName());
+ assertEquals(filePattern, rfa.getFilePattern());
+ final TriggeringPolicy triggeringPolicy = rfa.getTriggeringPolicy();
+ assertNotNull(triggeringPolicy);
+ assertTrue(triggeringPolicy.getClass().getName(), triggeringPolicy instanceof CompositeTriggeringPolicy);
+ final CompositeTriggeringPolicy ctp = (CompositeTriggeringPolicy) triggeringPolicy;
+ final TriggeringPolicy[] triggeringPolicies = ctp.getTriggeringPolicies();
+ assertEquals(1, triggeringPolicies.length);
+ final TriggeringPolicy tp = triggeringPolicies[0];
+ assertTrue(tp.getClass().getName(), tp instanceof TimeBasedTriggeringPolicy);
+ final TimeBasedTriggeringPolicy tbtp = (TimeBasedTriggeringPolicy) tp;
+ assertEquals(1, tbtp.getInterval());
+ final RolloverStrategy rolloverStrategy = rfa.getManager().getRolloverStrategy();
+ assertTrue(rolloverStrategy.getClass().getName(), rolloverStrategy instanceof DefaultRolloverStrategy);
+ final DefaultRolloverStrategy drs = (DefaultRolloverStrategy) rolloverStrategy;
+ assertEquals(Integer.MAX_VALUE, drs.getMaxIndex());
+ } finally {
+ configuration.start();
+ configuration.stop();
+ }
+ }
+
+ private Layout> testFile(final String configResource) throws Exception {
+ final Configuration configuration = getConfiguration(configResource);
+ final FileAppender appender = configuration.getAppender("File");
+ assertNotNull(appender);
+ assertEquals("target/mylog.txt", appender.getFileName());
+ //
+ final LoggerConfig loggerConfig = configuration.getLoggerConfig("com.example.foo");
+ assertNotNull(loggerConfig);
+ assertEquals(Level.DEBUG, loggerConfig.getLevel());
+ assertEquals("append", false, getAppendProperty(appender));
+ assertEquals("bufferSize", 1000, appender.getManager().getBufferSize());
+ assertEquals("immediateFlush", false, appender.getImmediateFlush());
+ configuration.start();
+ configuration.stop();
+ return appender.getLayout();
+ }
+
+ public void testConsoleEnhancedPatternLayout() throws Exception {
+ final PatternLayout layout = (PatternLayout) testConsole("config-1.2/log4j-console-EnhancedPatternLayout");
+ // %p, %X and %x converted to their Log4j 1.x bridge equivalent
+ assertEquals("%d{ISO8601} [%t][%c] %-5v1Level %properties %ndc: %m%n", layout.getConversionPattern());
+ }
+
+ public void testConsoleHtmlLayout() throws Exception {
+ final HtmlLayout layout = (HtmlLayout) testConsole("config-1.2/log4j-console-HtmlLayout");
+ assertEquals("Headline", layout.getTitle());
+ assertTrue(layout.isLocationInfo());
+ }
+
+ public void testConsolePatternLayout() throws Exception {
+ final PatternLayout layout = (PatternLayout) testConsole("config-1.2/log4j-console-PatternLayout");
+ // %p converted to its Log4j 1.x bridge equivalent
+ assertEquals("%d{ISO8601} [%t][%c] %-5v1Level: %m%n", layout.getConversionPattern());
+ }
+
+ public void testConsoleSimpleLayout() throws Exception {
+ final PatternLayout layout = (PatternLayout) testConsole("config-1.2/log4j-console-SimpleLayout");
+ assertEquals("%v1Level - %m%n", layout.getConversionPattern());
+ }
+
+ public void testFileSimpleLayout() throws Exception {
+ final PatternLayout layout = (PatternLayout) testFile("config-1.2/log4j-file-SimpleLayout");
+ assertEquals("%v1Level - %m%n", layout.getConversionPattern());
+ }
+
+ public void testNullAppender() throws Exception {
+ final Configuration configuration = getConfiguration("config-1.2/log4j-NullAppender");
+ final Appender appender = configuration.getAppender("NullAppender");
+ assertNotNull(appender);
+ assertEquals("NullAppender", appender.getName());
+ assertTrue(appender.getClass().getName(), appender instanceof NullAppender);
+ }
+
+ private boolean getFollowProperty(final ConsoleAppender consoleAppender)
+ throws Exception, NoSuchFieldException, IllegalAccessException {
+ CloseShieldOutputStream wrapperStream = (CloseShieldOutputStream) getOutputStream(consoleAppender.getManager());
+ Field delegateField = CloseShieldOutputStream.class.getDeclaredField("delegate");
+ delegateField.setAccessible(true);
+ boolean follow = !System.out.equals(delegateField.get(wrapperStream));
+ return follow;
+ }
+
+ private boolean getAppendProperty(final RollingFileAppender appender) throws Exception {
+ return getAppendProperty((FileOutputStream) getOutputStream(appender.getManager()));
+ }
+
+ private boolean getAppendProperty(final FileAppender appender) throws Exception {
+ return getAppendProperty((FileOutputStream) getOutputStream(appender.getManager()));
+ }
+
+ private boolean getAppendProperty(final FileOutputStream os) throws Exception {
+ // Java 11
+ final Field appendField = FileDescriptor.class.getDeclaredField("append");
+ appendField.setAccessible(true);
+ return (Boolean) appendField.get(os.getFD());
+ }
+
+ private OutputStream getOutputStream(final OutputStreamManager manager) throws Exception {
+ final Method getOutputStream = OutputStreamManager.class.getDeclaredMethod("getOutputStream");
+ getOutputStream.setAccessible(true);
+ return (OutputStream) getOutputStream.invoke(manager);
+ }
+
+ private Layout> testLayout(final Configuration config, final String appenderName) {
+ final ConsoleAppender appender = config.getAppender(appenderName);
+ assertNotNull("Missing appender '" + appenderName + "' in configuration " + config.getConfigurationSource(), appender);
+ return appender.getLayout();
+ }
+
+ /**
+ * Test if the default values from Log4j 1.x are respected.
+ */
+ public void testDefaultValues() throws Exception {
+ final Configuration config = getConfiguration("config-1.2/log4j-defaultValues");
+ // HtmlLayout
+ final HtmlLayout htmlLayout = (HtmlLayout) testLayout(config, "HTMLLayout");
+ assertNotNull(htmlLayout);
+ assertEquals("title", "Log4J Log Messages", htmlLayout.getTitle());
+ assertEquals("locationInfo", false, htmlLayout.isLocationInfo());
+ // PatternLayout
+ final PatternLayout patternLayout = (PatternLayout) testLayout(config, "PatternLayout");
+ assertNotNull(patternLayout);
+ assertEquals("conversionPattern", "%m%n", patternLayout.getConversionPattern());
+ // TTCCLayout
+ final PatternLayout ttccLayout = (PatternLayout) testLayout(config, "TTCCLayout");
+ assertNotNull(ttccLayout);
+ assertEquals("equivalent conversion pattern", "%r [%t] %p %c %notEmpty{%ndc }- %m%n", ttccLayout.getConversionPattern());
+ // TODO: XMLLayout
+ // final XmlLayout xmlLayout = (XmlLayout) testLayout(config, "XMLLayout");
+ // assertNotNull(xmlLayout);
+ // ConsoleAppender
+ final ConsoleAppender consoleAppender = config.getAppender("ConsoleAppender");
+ assertNotNull(consoleAppender);
+ assertEquals("target", Target.SYSTEM_OUT, consoleAppender.getTarget());
+ boolean follow = getFollowProperty(consoleAppender);
+ assertEquals("follow", false, follow);
+ // DailyRollingFileAppender
+ final RollingFileAppender dailyRollingFileAppender = config.getAppender("DailyRollingFileAppender");
+ assertNotNull(dailyRollingFileAppender);
+ assertEquals("equivalent file pattern", "target/dailyRollingFileAppender%d{.yyyy-MM-dd}",
+ dailyRollingFileAppender.getFilePattern());
+ assertEquals("append", true, getAppendProperty(dailyRollingFileAppender));
+ assertEquals("bufferSize", 8192, dailyRollingFileAppender.getManager().getBufferSize());
+ assertEquals("immediateFlush", true, dailyRollingFileAppender.getImmediateFlush());
+ // FileAppender
+ final FileAppender fileAppender = config.getAppender("FileAppender");
+ assertNotNull(fileAppender);
+ assertEquals("append", true, getAppendProperty(fileAppender));
+ assertEquals("bufferSize", 8192, fileAppender.getManager().getBufferSize());
+ assertEquals("immediateFlush", true, fileAppender.getImmediateFlush());
+ // RollingFileAppender
+ final RollingFileAppender rollingFileAppender = config.getAppender("RollingFileAppender");
+ assertNotNull(rollingFileAppender);
+ assertEquals("equivalent file pattern", "target/rollingFileAppender.%i", rollingFileAppender.getFilePattern());
+ final CompositeTriggeringPolicy compositePolicy = rollingFileAppender.getManager().getTriggeringPolicy();
+ assertEquals(1, compositePolicy.getTriggeringPolicies().length);
+ final SizeBasedTriggeringPolicy sizePolicy = (SizeBasedTriggeringPolicy) compositePolicy.getTriggeringPolicies()[0];
+ assertEquals("maxFileSize", 10 * 1024 * 1024L, sizePolicy.getMaxFileSize());
+ final DefaultRolloverStrategy strategy = (DefaultRolloverStrategy) rollingFileAppender.getManager()
+ .getRolloverStrategy();
+ assertEquals("maxBackupIndex", 1, strategy.getMaxIndex());
+ assertEquals("append", true, getAppendProperty(rollingFileAppender));
+ assertEquals("bufferSize", 8192, rollingFileAppender.getManager().getBufferSize());
+ assertEquals("immediateFlush", true, rollingFileAppender.getImmediateFlush());
+ config.start();
+ config.stop();
+ }
+
+ /**
+ * Checks a hierarchy of filters.
+ *
+ * @param filter
+ * @return the number of filters
+ */
+ private int checkFilters(final org.apache.logging.log4j.core.Filter filter) {
+ int count = 0;
+ if (filter instanceof CompositeFilter) {
+ for (final org.apache.logging.log4j.core.Filter part : ((CompositeFilter) filter).getFiltersArray()) {
+ count += checkFilters(part);
+ }
+ } else if (filter instanceof FilterAdapter) {
+ // Don't create adapters from wrappers
+ assertFalse("found FilterAdapter of a FilterWrapper", ((FilterAdapter) filter).getFilter() instanceof FilterWrapper);
+ count += checkFilters(((FilterAdapter) filter).getFilter());
+ } else {
+ count++;
+ }
+ return count;
+ }
+
+ /**
+ * Checks a hierarchy of filters.
+ *
+ * @param filter
+ * @return the number of filters
+ */
+ private int checkFilters(final org.apache.log4j.spi.Filter filter) {
+ int count = 0;
+ if (filter instanceof FilterWrapper) {
+ // Don't create wrappers from adapters
+ assertFalse("found FilterWrapper of a FilterAdapter", ((FilterWrapper) filter).getFilter() instanceof FilterAdapter);
+ count += checkFilters(((FilterWrapper) filter).getFilter());
+ } else {
+ count++;
+ }
+ // We prefer a:
+ // CompositeFilter of native Log4j 2.x filters
+ // over a:
+ // FilterAdapter of a chain of FilterWrappers.
+ assertNull("found chain of Log4j 1.x filters", filter.getNext());
+ return count;
+ }
+
+ public void testMultipleFilters(final Path folder) throws Exception {
+ System.setProperty("test.tmpDir", folder.toString());
+ try (LoggerContext loggerContext = configure("log4j-multipleFilters")) {
+ final Configuration configuration = loggerContext.getConfiguration();
+ assertNotNull(configuration);
+ // Check only number of filters.
+ final Filterable console = configuration.getAppender("CONSOLE");
+ assertNotNull(console);
+ assertEquals(4, checkFilters(console.getFilter()));
+ final Filterable file = configuration.getAppender("FILE");
+ assertNotNull(file);
+ assertEquals(4, checkFilters(file.getFilter()));
+ final Filterable rfa = configuration.getAppender("RFA");
+ assertNotNull(rfa);
+ assertEquals(4, checkFilters(rfa.getFilter()));
+ final Filterable drfa = configuration.getAppender("DRFA");
+ assertNotNull(drfa);
+ assertEquals(4, checkFilters(drfa.getFilter()));
+ // List appenders
+ final Appender appender = configuration.getAppender("LIST");
+ assertNotNull(appender);
+ assertEquals(3, checkFilters(((Filterable)appender).getFilter()));
+ final ListAppender legacyAppender = (ListAppender) ((Adapter) appender).getAppender();
+ final org.apache.logging.log4j.core.test.appender.ListAppender nativeAppender = configuration.getAppender("LIST2");
+ assertEquals(3, checkFilters(((Filterable)nativeAppender).getFilter()));
+ final Logger logger = LogManager.getLogger(PropertiesConfigurationTest.class);
+ int expected = 0;
+ // message blocked by Threshold
+ logger.trace("NEUTRAL message");
+ assertEquals(expected, legacyAppender.getEvents().size());
+ assertEquals(expected, nativeAppender.getEvents().size());
+ // message blocked by DenyAll filter
+ logger.warn("NEUTRAL message");
+ assertEquals(expected, legacyAppender.getEvents().size());
+ assertEquals(expected, nativeAppender.getEvents().size());
+ // message accepted by level filter
+ logger.info("NEUTRAL message");
+ expected++;
+ assertEquals(expected, legacyAppender.getEvents().size());
+ assertEquals(expected, nativeAppender.getEvents().size());
+ // message accepted by "StartsWith" filter
+ logger.warn("ACCEPT message");
+ expected++;
+ assertEquals(expected, legacyAppender.getEvents().size());
+ assertEquals(expected, nativeAppender.getEvents().size());
+ // message blocked by "StartsWith" filter
+ logger.info("DENY message");
+ assertEquals(expected, legacyAppender.getEvents().size());
+ assertEquals(expected, nativeAppender.getEvents().size());
+ } finally {
+ System.clearProperty("test.tmpDir");
+ }
+ }
+
+ public void testGlobalThreshold() throws Exception {
+ try (final LoggerContext ctx = configure("config-1.2/log4j-global-threshold")) {
+ final Configuration config = ctx.getConfiguration();
+ final Filter filter = config.getFilter();
+ assertTrue(filter instanceof ThresholdFilter);
+ ThresholdFilter thresholdFilter = (ThresholdFilter)filter;
+ assertEquals(Level.INFO, thresholdFilter.getLevel());
+ assertEquals(Filter.Result.NEUTRAL, thresholdFilter.getOnMatch());
+ assertEquals(Filter.Result.DENY, thresholdFilter.getOnMismatch());
+
+ final Logger logger = LogManager.getLogger(PropertiesConfigurationTest.class);
+ // List appender
+ final Appender appender = config.getAppender("LIST");
+ assertNotNull(appender);
+ final ListAppender legacyAppender = (ListAppender) ((Adapter) appender).getAppender();
+ // Stopped by root logger level
+ logger.trace("TRACE");
+ assertEquals(0, legacyAppender.getEvents().size());
+ // Stopped by global threshold
+ logger.debug("DEBUG");
+ assertEquals(0, legacyAppender.getEvents().size());
+ // Accepted
+ logger.info("INFO");
+ assertEquals(1, legacyAppender.getEvents().size());
+ }
+ }
+}
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 00000000000..90f41a21ace
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AsyncAppenderTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Test configuration from XML.
+ */
+@Tag("sleepy")
+public class AsyncAppenderTest {
+
+ @Test
+ public void testAsyncXml() throws Exception {
+ try (final LoggerContext loggerContext = TestConfigurator.configure("target/test-classes/log4j1-async.xml")) {
+ final Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ Thread.sleep(50);
+ final Configuration configuration = loggerContext.getConfiguration();
+ final Map appenders = configuration.getAppenders();
+ ListAppender messageAppender = null;
+ for (final Map.Entry entry : appenders.entrySet()) {
+ if (entry.getKey().equals("list")) {
+ messageAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+ }
+ }
+ assertNotNull(messageAppender, "No Message Appender");
+ final List messages = messageAppender.getMessages();
+ assertTrue(messages != null && messages.size() > 0, "No messages");
+ }
+ }
+
+ @Test
+ public void testAsyncProperties() throws Exception {
+ try (final LoggerContext loggerContext = TestConfigurator.configure("target/test-classes/log4j1-async.properties")) {
+ final Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ Thread.sleep(50);
+ final Configuration configuration = loggerContext.getConfiguration();
+ final Map appenders = configuration.getAppenders();
+ ListAppender messageAppender = null;
+ for (final Map.Entry entry : appenders.entrySet()) {
+ if (entry.getKey().equals("list")) {
+ messageAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+ }
+ }
+ assertNotNull(messageAppender, "No Message Appender");
+ final List messages = messageAppender.getMessages();
+ assertTrue(messages != null && messages.size() > 0, "No messages");
+ }
+ }
+}
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
new file mode 100644
index 00000000000..8f8769ead23
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AutoConfigTest.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.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.config.Configuration;
+import org.apache.logging.log4j.core.test.junit.LoggerContextSource;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Test configuration from XML.
+ */
+public class AutoConfigTest {
+
+ @Test
+ @LoggerContextSource(value = "log4j.xml", v1config = true)
+ public void testListAppender(final org.apache.logging.log4j.core.LoggerContext context) {
+ Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ Configuration configuration = context.getConfiguration();
+ Map appenders = configuration.getAppenders();
+ ListAppender eventAppender = null;
+ ListAppender messageAppender = null;
+ for (Map.Entry 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(eventAppender, "No Event Appender");
+ assertNotNull(messageAppender, "No Message Appender");
+ List events = eventAppender.getEvents();
+ assertTrue(events != null && events.size() > 0, "No events");
+ List messages = messageAppender.getMessages();
+ assertTrue(messages != null && messages.size() > 0, "No messages");
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationConverterHadoopTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationConverterHadoopTest.java
index 152f5dd97e6..2cb91bc55f9 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationConverterHadoopTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationConverterHadoopTest.java
@@ -1,12 +1,3 @@
-package org.apache.log4j.config;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.List;
-
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@@ -24,6 +15,15 @@
* limitations under the license.
*/
+package org.apache.log4j.config;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
@RunWith(Parameterized.class)
public class Log4j1ConfigurationConverterHadoopTest extends AbstractLog4j1ConfigurationConverterTest {
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationConverterSparkTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationConverterSparkTest.java
index 2b39d4ff311..ff5c8dc82fb 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationConverterSparkTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationConverterSparkTest.java
@@ -1,12 +1,3 @@
-package org.apache.log4j.config;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.List;
-
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@@ -24,6 +15,15 @@
* limitations under the license.
*/
+package org.apache.log4j.config;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
@RunWith(Parameterized.class)
public class Log4j1ConfigurationConverterSparkTest extends AbstractLog4j1ConfigurationConverterTest {
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationFactoryTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationFactoryTest.java
index 8d3e4e2b3e7..4d7e315268e 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationFactoryTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationFactoryTest.java
@@ -16,48 +16,49 @@
*/
package org.apache.log4j.config;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
-import java.io.File;
+import java.io.Serializable;
import java.net.URISyntaxException;
import java.net.URL;
-import java.nio.file.FileSystemException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.concurrent.TimeUnit;
import org.apache.log4j.layout.Log4j1XmlLayout;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.apache.logging.log4j.core.appender.ConsoleAppender.Target;
-import org.apache.logging.log4j.core.appender.FileAppender;
-import org.apache.logging.log4j.core.appender.NullAppender;
-import org.apache.logging.log4j.core.appender.RollingFileAppender;
-import org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy;
-import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
-import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
-import org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy;
-import org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy;
-import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
-import org.apache.logging.log4j.core.layout.HtmlLayout;
+import org.apache.logging.log4j.core.filter.ThresholdFilter;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.junit.Test;
-public class Log4j1ConfigurationFactoryTest {
+public class Log4j1ConfigurationFactoryTest extends AbstractLog4j1ConfigurationTest {
+
+ private static final String SUFFIX = ".properties";
+
+ @Override
+ protected Configuration getConfiguration(final String configResource) throws URISyntaxException {
+ final URL configLocation = ClassLoader.getSystemResource(configResource + SUFFIX);
+ assertNotNull(configLocation, configResource);
+ final Configuration configuration = new Log4j1ConfigurationFactory().getConfiguration(null, "test", configLocation.toURI());
+ assertNotNull(configuration);
+ return configuration;
+ }
private Layout> testConsole(final String configResource) throws Exception {
final Configuration configuration = getConfiguration(configResource);
final String name = "Console";
final ConsoleAppender appender = configuration.getAppender(name);
- assertNotNull("Missing appender '" + name + "' in configuration " + configResource + " → " + configuration,
- appender);
+ assertNotNull(appender,
+ "Missing appender '" + name + "' in configuration " + configResource + " → " + configuration);
assertEquals(Target.SYSTEM_ERR, appender.getTarget());
//
final LoggerConfig loggerConfig = configuration.getLoggerConfig("com.example.foo");
@@ -68,182 +69,124 @@ private Layout> testConsole(final String configResource) throws Exception {
return appender.getLayout();
}
- private Layout> testFile(final String configResource) throws Exception {
- final Configuration configuration = getConfiguration(configResource);
- final FileAppender appender = configuration.getAppender("File");
- assertNotNull(appender);
- assertEquals("target/mylog.txt", appender.getFileName());
- //
- final LoggerConfig loggerConfig = configuration.getLoggerConfig("com.example.foo");
- assertNotNull(loggerConfig);
- assertEquals(Level.DEBUG, loggerConfig.getLevel());
- configuration.start();
- configuration.stop();
- return appender.getLayout();
- }
-
- private Configuration getConfiguration(final String configResource) throws URISyntaxException {
- final URL configLocation = ClassLoader.getSystemResource(configResource);
- assertNotNull(configResource, configLocation);
- final Configuration configuration = new Log4j1ConfigurationFactory().getConfiguration(null, "test",
- configLocation.toURI());
- assertNotNull(configuration);
- return configuration;
- }
-
- @Test
- public void testConsoleEnhancedPatternLayout() throws Exception {
- final PatternLayout layout = (PatternLayout) testConsole(
- "config-1.2/log4j-console-EnhancedPatternLayout.properties");
- assertEquals("%d{ISO8601} [%t][%c] %-5p %properties %ndc: %m%n", layout.getConversionPattern());
- }
+ @Override
+ @Test
+ public void testConsoleEnhancedPatternLayout() throws Exception {
+ super.testConsoleEnhancedPatternLayout();
+ }
- @Test
- public void testConsoleHtmlLayout() throws Exception {
- final HtmlLayout layout = (HtmlLayout) testConsole("config-1.2/log4j-console-HtmlLayout.properties");
- assertEquals("Headline", layout.getTitle());
- assertTrue(layout.isLocationInfo());
- }
+ @Override
+ @Test
+ public void testConsoleHtmlLayout() throws Exception {
+ super.testConsoleHtmlLayout();
+ }
- @Test
- public void testConsolePatternLayout() throws Exception {
- final PatternLayout layout = (PatternLayout) testConsole("config-1.2/log4j-console-PatternLayout.properties");
- assertEquals("%d{ISO8601} [%t][%c] %-5p: %m%n", layout.getConversionPattern());
- }
+ @Test
+ public void testConsolePatternLayout() throws Exception {
+ super.testConsolePatternLayout();
+ }
- @Test
- public void testConsoleSimpleLayout() throws Exception {
- final PatternLayout layout = (PatternLayout) testConsole("config-1.2/log4j-console-SimpleLayout.properties");
- assertEquals("%level - %m%n", layout.getConversionPattern());
- }
+ @Override
+ @Test
+ public void testConsoleSimpleLayout() throws Exception {
+ super.testConsoleSimpleLayout();
+ }
- @Test
- public void testConsoleTtccLayout() throws Exception {
- final PatternLayout layout = (PatternLayout) testConsole("config-1.2/log4j-console-TTCCLayout.properties");
- assertEquals("%r [%t] %p %notEmpty{%ndc }- %m%n", layout.getConversionPattern());
- }
+ @Override
+ @Test
+ public void testConsoleTtccLayout() throws Exception {
+ super.testConsoleTtccLayout();
+ }
- @Test
- public void testConsoleXmlLayout() throws Exception {
- final Log4j1XmlLayout layout = (Log4j1XmlLayout) testConsole("config-1.2/log4j-console-XmlLayout.properties");
- assertTrue(layout.isLocationInfo());
- assertFalse(layout.isProperties());
- }
+ @Test
+ public void testConsoleXmlLayout() throws Exception {
+ final Log4j1XmlLayout layout = (Log4j1XmlLayout) testConsole("config-1.2/log4j-console-XmlLayout");
+ assertTrue(layout.isLocationInfo());
+ assertFalse(layout.isProperties());
+ }
- @Test
- public void testFileSimpleLayout() throws Exception {
- final PatternLayout layout = (PatternLayout) testFile("config-1.2/log4j-file-SimpleLayout.properties");
- assertEquals("%level - %m%n", layout.getConversionPattern());
- }
+ @Override
+ @Test
+ public void testFileSimpleLayout() throws Exception {
+ super.testFileSimpleLayout();
+ }
- @Test
+ @Override
+ @Test
public void testNullAppender() throws Exception {
- final Configuration configuration = getConfiguration("config-1.2/log4j-NullAppender.properties");
- final Appender appender = configuration.getAppender("NullAppender");
- assertNotNull(appender);
- assertEquals("NullAppender", appender.getName());
- assertTrue(appender.getClass().getName(), appender instanceof NullAppender);
+ super.testNullAppender();
}
- @Test
- public void testRollingFileAppender() throws Exception {
- testRollingFileAppender("config-1.2/log4j-RollingFileAppender.properties", "RFA", "target/hadoop.log.%i");
- }
+ @Override
+ @Test
+ public void testRollingFileAppender() throws Exception {
+ super.testRollingFileAppender();
+ }
- @Test
- public void testDailyRollingFileAppender() throws Exception {
- testDailyRollingFileAppender("config-1.2/log4j-DailyRollingFileAppender.properties", "DRFA", "target/hadoop.log%d{.yyyy-MM-dd}");
- }
+ @Override
+ @Test
+ public void testDailyRollingFileAppender() throws Exception {
+ super.testDailyRollingFileAppender();
+ }
- @Test
- public void testRollingFileAppenderWithProperties() throws Exception {
- testRollingFileAppender("config-1.2/log4j-RollingFileAppender-with-props.properties", "RFA", "target/hadoop.log.%i");
- }
+ @Test
+ public void testRollingFileAppenderWithProperties() throws Exception {
+ super.testRollingFileAppenderWithProperties();
+ }
- @Test
- public void testSystemProperties1() throws Exception {
- final String tempFileName = System.getProperty("java.io.tmpdir") + "/hadoop.log";
- final Path tempFilePath = new File(tempFileName).toPath();
- Files.deleteIfExists(tempFilePath);
- try {
- final Configuration configuration = getConfiguration("config-1.2/log4j-system-properties-1.properties");
- final RollingFileAppender appender = configuration.getAppender("RFA");
- appender.stop(10, TimeUnit.SECONDS);
- System.out.println("expected: " + tempFileName + " Actual: " + appender.getFileName());
- assertEquals(tempFileName, appender.getFileName());
- } finally {
- try {
- Files.deleteIfExists(tempFilePath);
- } catch (FileSystemException e) {
- e.printStackTrace();
- }
- }
- }
+ @Override
+ @Test
+ public void testSystemProperties1() throws Exception {
+ super.testSystemProperties1();
+ }
- @Test
- public void testSystemProperties2() throws Exception {
- final Configuration configuration = getConfiguration("config-1.2/log4j-system-properties-2.properties");
- final RollingFileAppender appender = configuration.getAppender("RFA");
- assertEquals("${java.io.tmpdir}/hadoop.log", appender.getFileName());
- appender.stop(10, TimeUnit.SECONDS);
- Path path = new File(appender.getFileName()).toPath();
- Files.deleteIfExists(path);
- path = new File("${java.io.tmpdir}").toPath();
- Files.deleteIfExists(path);
- }
+ @Override
+ @Test
+ public void testSystemProperties2() throws Exception {
+ super.testSystemProperties2();
+ }
- private void testRollingFileAppender(final String configResource, final String name, final String filePattern) throws URISyntaxException {
- final Configuration configuration = getConfiguration(configResource);
- final Appender appender = configuration.getAppender(name);
- assertNotNull(appender);
- assertEquals(name, appender.getName());
- assertTrue(appender.getClass().getName(), appender instanceof RollingFileAppender);
- final RollingFileAppender rfa = (RollingFileAppender) appender;
- assertEquals("target/hadoop.log", rfa.getFileName());
- assertEquals(filePattern, rfa.getFilePattern());
- final TriggeringPolicy triggeringPolicy = rfa.getTriggeringPolicy();
- assertNotNull(triggeringPolicy);
- assertTrue(triggeringPolicy.getClass().getName(), triggeringPolicy instanceof CompositeTriggeringPolicy);
- final CompositeTriggeringPolicy ctp = (CompositeTriggeringPolicy) triggeringPolicy;
- final TriggeringPolicy[] triggeringPolicies = ctp.getTriggeringPolicies();
- assertEquals(1, triggeringPolicies.length);
- final TriggeringPolicy tp = triggeringPolicies[0];
- assertTrue(tp.getClass().getName(), tp instanceof SizeBasedTriggeringPolicy);
- final SizeBasedTriggeringPolicy sbtp = (SizeBasedTriggeringPolicy) tp;
- assertEquals(256 * 1024 * 1024, sbtp.getMaxFileSize());
- final RolloverStrategy rolloverStrategy = rfa.getManager().getRolloverStrategy();
- assertTrue(rolloverStrategy.getClass().getName(), rolloverStrategy instanceof DefaultRolloverStrategy);
- final DefaultRolloverStrategy drs = (DefaultRolloverStrategy) rolloverStrategy;
- assertEquals(20, drs.getMaxIndex());
- configuration.start();
- configuration.stop();
- }
+ @Override
+ @Test
+ public void testConsoleCapitalization() throws Exception {
+ super.testConsoleCapitalization();
+ }
- private void testDailyRollingFileAppender(final String configResource, final String name, final String filePattern) throws URISyntaxException {
- final Configuration configuration = getConfiguration(configResource);
- final Appender appender = configuration.getAppender(name);
- assertNotNull(appender);
- assertEquals(name, appender.getName());
- assertTrue(appender.getClass().getName(), appender instanceof RollingFileAppender);
- final RollingFileAppender rfa = (RollingFileAppender) appender;
- assertEquals("target/hadoop.log", rfa.getFileName());
- assertEquals(filePattern, rfa.getFilePattern());
- final TriggeringPolicy triggeringPolicy = rfa.getTriggeringPolicy();
- assertNotNull(triggeringPolicy);
- assertTrue(triggeringPolicy.getClass().getName(), triggeringPolicy instanceof CompositeTriggeringPolicy);
- final CompositeTriggeringPolicy ctp = (CompositeTriggeringPolicy) triggeringPolicy;
- final TriggeringPolicy[] triggeringPolicies = ctp.getTriggeringPolicies();
- assertEquals(1, triggeringPolicies.length);
- final TriggeringPolicy tp = triggeringPolicies[0];
- assertTrue(tp.getClass().getName(), tp instanceof TimeBasedTriggeringPolicy);
- final TimeBasedTriggeringPolicy tbtp = (TimeBasedTriggeringPolicy) tp;
- assertEquals(1, tbtp.getInterval());
- final RolloverStrategy rolloverStrategy = rfa.getManager().getRolloverStrategy();
- assertTrue(rolloverStrategy.getClass().getName(), rolloverStrategy instanceof DefaultRolloverStrategy);
- final DefaultRolloverStrategy drs = (DefaultRolloverStrategy) rolloverStrategy;
- assertEquals(Integer.MAX_VALUE, drs.getMaxIndex());
- configuration.start();
- configuration.stop();
- }
+ @Override
+ @Test
+ public void testDefaultValues() throws Exception {
+ super.testDefaultValues();
+ }
+
+ @Test
+ public void testUntrimmedValues() throws Exception {
+ try {
+ final Configuration config = getConfiguration("config-1.2/log4j-untrimmed");
+ final LoggerConfig rootLogger = config.getRootLogger();
+ assertEquals(Level.DEBUG, rootLogger.getLevel());
+ final Appender appender = config.getAppender("Console");
+ assertTrue(appender instanceof ConsoleAppender);
+ final Layout extends Serializable> layout = appender.getLayout();
+ assertTrue(layout instanceof PatternLayout);
+ assertEquals("%v1Level - %m%n", ((PatternLayout)layout).getConversionPattern());
+ // No filter support
+ config.start();
+ config.stop();
+ } catch (NoClassDefFoundError e) {
+ fail(e.getMessage());
+ }
+ }
+ @Test
+ public void testGlobalThreshold() throws Exception {
+ try (final LoggerContext ctx = configure("config-1.2/log4j-global-threshold")) {
+ final Configuration config = ctx.getConfiguration();
+ final Filter filter = config.getFilter();
+ assertTrue(filter instanceof ThresholdFilter);
+ ThresholdFilter thresholdFilter = (ThresholdFilter)filter;
+ assertEquals(Level.INFO, thresholdFilter.getLevel());
+ assertEquals(Filter.Result.NEUTRAL, thresholdFilter.getOnMatch());
+ assertEquals(Filter.Result.DENY, thresholdFilter.getOnMismatch());
+ }
+ }
}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/MapRewriteAppenderTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/MapRewriteAppenderTest.java
new file mode 100644
index 00000000000..e0399e72dbc
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/MapRewriteAppenderTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.test.junit.LoggerContextSource;
+import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Test RewriteAppender
+ */
+@UsingThreadContextMap
+public class MapRewriteAppenderTest {
+
+ @Test
+ @LoggerContextSource(value = "log4j1-mapRewrite.xml", v1config = true)
+ public void testRewrite() {
+ Logger logger = LogManager.getLogger("test");
+ Map map = new HashMap<>();
+ map.put("message", "This is a test");
+ map.put("hello", "world");
+ logger.debug(map);
+ LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+ Configuration configuration = context.getConfiguration();
+ Map appenders = configuration.getAppenders();
+ ListAppender eventAppender = null;
+ for (Map.Entry entry : appenders.entrySet()) {
+ if (entry.getKey().equals("events")) {
+ eventAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+ }
+ }
+ assertNotNull(eventAppender, "No Event Appender");
+ List events = eventAppender.getEvents();
+ assertTrue(events != null && events.size() > 0, "No events");
+ assertNotNull(events.get(0).getProperties(), "No properties in the event");
+ assertTrue(events.get(0).getProperties().containsKey("hello"), "Key was not inserted");
+ assertEquals("world", events.get(0).getProperties().get("hello"), "Key value is incorrect");
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/NeutralFilterFixture.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/NeutralFilterFixture.java
new file mode 100644
index 00000000000..f6a20b2bc56
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/NeutralFilterFixture.java
@@ -0,0 +1,32 @@
+/*
+ * 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.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * A test fixture used by {@code src/test/resources/LOG4J2-3247.properties}.
+ */
+public class NeutralFilterFixture extends Filter {
+
+ @Override
+ public int decide(LoggingEvent event) {
+ return NEUTRAL;
+ }
+
+}
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 00000000000..549f7c1b5c5
--- /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-1.properties");
+ }
+
+ @Test
+ public void testProperties() {
+ 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 00000000000..bf4705e9d3f
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationTest.java
@@ -0,0 +1,320 @@
+/*
+ * 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 static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.net.URISyntaxException;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Map;
+
+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.bridge.FilterAdapter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.apache.logging.log4j.core.appender.FileAppender;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.filter.CompositeFilter;
+import org.apache.logging.log4j.core.filter.DenyAllFilter;
+import org.apache.logging.log4j.core.filter.Filterable;
+import org.apache.logging.log4j.core.filter.LevelRangeFilter;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+/**
+ * Test configuration from Properties.
+ */
+public class PropertiesConfigurationTest extends AbstractLog4j1ConfigurationTest {
+
+ private static final String TEST_KEY = "log4j.test.tmpdir";
+ private static final String SUFFIX = ".properties";
+
+ @Override
+ Configuration getConfiguration(String configResourcePrefix) throws URISyntaxException, IOException {
+ final String configResource = configResourcePrefix + SUFFIX;
+ final InputStream inputStream = ClassLoader.getSystemResourceAsStream(configResource);
+ final ConfigurationSource source = new ConfigurationSource(inputStream);
+ final LoggerContext context = LoggerContext.getContext(false);
+ final Configuration configuration = new PropertiesConfigurationFactory().getConfiguration(context, source);
+ assertNotNull(configuration, "No configuration created");
+ configuration.initialize();
+ return configuration;
+ }
+
+ @Test
+ public void testConfigureNullPointerException() throws Exception {
+ try (LoggerContext loggerContext = TestConfigurator.configure("target/test-classes/LOG4J2-3247.properties")) {
+ // [LOG4J2-3247] configure() should not throw an NPE.
+ final Configuration configuration = loggerContext.getConfiguration();
+ assertNotNull(configuration);
+ final Appender appender = configuration.getAppender("CONSOLE");
+ assertNotNull(appender);
+ }
+ }
+
+ @Test
+ public void testConsoleAppenderFilter() throws Exception {
+ try (LoggerContext loggerContext = TestConfigurator.configure("target/test-classes/LOG4J2-3247.properties")) {
+ // LOG4J2-3281 PropertiesConfiguration.buildAppender not adding filters to appender
+ final Configuration configuration = loggerContext.getConfiguration();
+ assertNotNull(configuration);
+ final Appender appender = configuration.getAppender("CONSOLE");
+ assertNotNull(appender);
+ final Filterable filterable = (Filterable) appender;
+ final FilterAdapter filter = (FilterAdapter) filterable.getFilter();
+ assertNotNull(filter);
+ assertTrue(filter.getFilter() instanceof NeutralFilterFixture);
+ }
+ }
+
+ @Test
+ public void testCustomAppenderFilter() throws Exception {
+ try (LoggerContext loggerContext = TestConfigurator.configure("target/test-classes/LOG4J2-3281.properties")) {
+ // LOG4J2-3281 PropertiesConfiguration.buildAppender not adding filters to appender
+ final Configuration configuration = loggerContext.getConfiguration();
+ assertNotNull(configuration);
+ final Appender appender = configuration.getAppender("CUSTOM");
+ assertNotNull(appender);
+ final Filterable filterable = (Filterable) appender;
+ final FilterAdapter filter = (FilterAdapter) filterable.getFilter();
+ assertNotNull(filter);
+ assertTrue(filter.getFilter() instanceof NeutralFilterFixture);
+ }
+ }
+
+ @Test
+ public void testConsoleAppenderLevelRangeFilter() throws Exception {
+ try (LoggerContext loggerContext = TestConfigurator.configure("target/test-classes/LOG4J2-3326.properties")) {
+ final Configuration configuration = loggerContext.getConfiguration();
+ assertNotNull(configuration);
+ final Appender appender = configuration.getAppender("CUSTOM");
+ assertNotNull(appender);
+ final Filterable filterable = (Filterable) appender;
+ final CompositeFilter filter = (CompositeFilter) filterable.getFilter();
+ final org.apache.logging.log4j.core.Filter[] filters = filter.getFiltersArray();
+ final LevelRangeFilter customFilterReal = (LevelRangeFilter) filters[0];
+ assertEquals(Level.ALL, customFilterReal.getMinLevel());
+ final LevelRangeFilter defaultFilter = (LevelRangeFilter) filters[1];
+ assertEquals(Level.TRACE, defaultFilter.getMinLevel());
+ }
+ }
+
+ @Test
+ public void testConfigureAppenderDoesNotExist() throws Exception {
+ // Verify that we tolerate a logger which specifies an appender that does not exist.
+ try (LoggerContext loggerContext = TestConfigurator.configure("target/test-classes/LOG4J2-3407.properties")) {
+ final Configuration configuration = loggerContext.getConfiguration();
+ assertNotNull(configuration);
+ }
+ }
+
+ @Test
+ public void testListAppender() throws Exception {
+ try (LoggerContext loggerContext = TestConfigurator.configure("target/test-classes/log4j1-list.properties")) {
+ final Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ final Configuration configuration = loggerContext.getConfiguration();
+ final Map appenders = configuration.getAppenders();
+ ListAppender eventAppender = null;
+ ListAppender messageAppender = null;
+ for (final Map.Entry 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(eventAppender, "No Event Appender");
+ assertNotNull(messageAppender, "No Message Appender");
+ final List events = eventAppender.getEvents();
+ assertTrue(events != null && events.size() > 0, "No events");
+ final List messages = messageAppender.getMessages();
+ assertTrue(messages != null && messages.size() > 0, "No messages");
+ }
+ }
+
+ @Test
+ public void testProperties() throws Exception {
+ try (LoggerContext loggerContext = TestConfigurator.configure("target/test-classes/log4j1-file-1.properties")) {
+ final Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ File file = new File("target/temp.A1");
+ assertTrue(file.exists(), "File A1 was not created");
+ assertTrue(file.length() > 0, "File A1 is empty");
+ file = new File("target/temp.A2");
+ assertTrue(file.exists(), "File A2 was not created");
+ assertTrue(file.length() > 0, "File A2 is empty");
+ }
+ }
+
+ @Test
+ public void testSystemProperties() throws Exception {
+ final String testPathLocation = "target";
+ System.setProperty(TEST_KEY, testPathLocation);
+ try (LoggerContext loggerContext = TestConfigurator.configure("target/test-classes/config-1.2/log4j-FileAppender-with-props.properties")) {
+ // [LOG4J2-3312] Bridge does not convert properties.
+ final Configuration configuration = loggerContext.getConfiguration();
+ assertNotNull(configuration);
+ final String name = "FILE_APPENDER";
+ final Appender appender = configuration.getAppender(name);
+ assertNotNull(appender, name);
+ assertTrue(appender instanceof FileAppender, appender.getClass().getName());
+ final FileAppender fileAppender = (FileAppender) appender;
+ // Two slashes because that's how the config file is setup.
+ assertEquals(testPathLocation + "/hadoop.log", fileAppender.getFileName());
+ } finally {
+ System.clearProperty(TEST_KEY);
+ }
+ }
+
+
+ @Override
+ @Test
+ public void testConsoleEnhancedPatternLayout() throws Exception {
+ super.testConsoleEnhancedPatternLayout();
+ }
+
+ @Override
+ @Test
+ public void testConsoleHtmlLayout() throws Exception {
+ super.testConsoleHtmlLayout();
+ }
+
+ @Override
+ @Test
+ public void testConsolePatternLayout() throws Exception {
+ super.testConsolePatternLayout();
+ }
+
+ @Override
+ @Test
+ public void testConsoleSimpleLayout() throws Exception {
+ super.testConsoleSimpleLayout();
+ }
+
+ @Override
+ @Test
+ public void testFileSimpleLayout() throws Exception {
+ super.testFileSimpleLayout();
+ }
+
+ @Override
+ @Test
+ public void testNullAppender() throws Exception {
+ super.testNullAppender();
+ }
+
+ @Override
+ @Test
+ public void testConsoleCapitalization() throws Exception {
+ super.testConsoleCapitalization();
+ }
+
+ @Override
+ @Test
+ public void testConsoleTtccLayout() throws Exception {
+ super.testConsoleTtccLayout();
+ }
+
+ @Override
+ @Test
+ public void testRollingFileAppender() throws Exception {
+ super.testRollingFileAppender();
+ }
+
+ @Override
+ @Test
+ public void testDailyRollingFileAppender() throws Exception {
+ super.testDailyRollingFileAppender();
+ }
+
+ @Override
+ @Test
+ public void testRollingFileAppenderWithProperties() throws Exception {
+ super.testRollingFileAppenderWithProperties();
+ }
+
+ @Override
+ @Test
+ public void testSystemProperties1() throws Exception {
+ super.testSystemProperties1();
+ }
+
+ @Override
+ @Test
+ public void testSystemProperties2() throws Exception {
+ super.testSystemProperties2();
+ }
+
+ @Override
+ @Test
+ public void testDefaultValues() throws Exception {
+ super.testDefaultValues();
+ }
+
+ @Override
+ @Test
+ public void testMultipleFilters(final @TempDir Path tmpFolder) throws Exception {
+ super.testMultipleFilters(tmpFolder);
+ }
+
+ @Test
+ public void testUntrimmedValues() throws Exception {
+ try {
+ final Configuration config = getConfiguration("config-1.2/log4j-untrimmed");
+ final LoggerConfig rootLogger = config.getRootLogger();
+ assertEquals(Level.DEBUG, rootLogger.getLevel());
+ final Appender appender = config.getAppender("Console");
+ assertTrue(appender instanceof ConsoleAppender);
+ final Layout extends Serializable> layout = appender.getLayout();
+ assertTrue(layout instanceof PatternLayout);
+ assertEquals("%v1Level - %m%n", ((PatternLayout)layout).getConversionPattern());
+ final Filter filter = ((Filterable) appender).getFilter();
+ assertTrue(filter instanceof DenyAllFilter);
+ config.start();
+ config.stop();
+ } catch (NoClassDefFoundError e) {
+ fail(e.getMessage());
+ }
+ }
+
+ @Override
+ @Test
+ public void testGlobalThreshold() throws Exception {
+ super.testGlobalThreshold();
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesReconfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesReconfigurationTest.java
new file mode 100644
index 00000000000..2b7d3bb7ba2
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesReconfigurationTest.java
@@ -0,0 +1,221 @@
+/*
+ * 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.commons.lang3.function.FailableConsumer;
+import org.apache.log4j.CustomFileAppender;
+import org.apache.log4j.CustomNoopAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.FileAppender;
+import org.apache.logging.log4j.core.appender.FileManager;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationListener;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.config.Reconfigurable;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.time.Duration;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Test reconfiguring with an XML configuration.
+ */
+public class PropertiesReconfigurationTest {
+
+ private class TestListener implements ConfigurationListener {
+
+ @Override
+ public synchronized void onChange(final Reconfigurable reconfigurable) {
+ toggle.countDown();
+ }
+
+ }
+
+ private static final String CONFIG_CUSTOM_APPENDERS_1 = "target/test-classes/log4j1-appenders-custom-1.properties";
+ private static final String CONFIG_CUSTOM_APPENDERS_2 = "target/test-classes/log4j1-appenders-custom-2.properties";
+
+ private static final String CONFIG_FILE_APPENDER_1 = "target/test-classes/log4j1-file-1.properties";
+ private static final String CONFIG_FILE_APPENDER_2 = "target/test-classes/log4j1-file-2.properties";
+
+ private static final Duration FIVE_MINUTES = Duration.ofMinutes(5);
+
+ private final CountDownLatch toggle = new CountDownLatch(1);
+
+ private void assertCustomFileAppender(final org.apache.log4j.Appender appender, final boolean expectBoolean, final int expectInt,
+ final String expectString) {
+ final CustomFileAppender customAppender = (CustomFileAppender) appender;
+ assertEquals(expectBoolean, customAppender.getBooleanA());
+ assertEquals(expectInt, customAppender.getIntA());
+ assertEquals(expectString, customAppender.getStringA());
+ }
+
+ private void assertCustomNoopAppender(final org.apache.log4j.Appender appender, final boolean expectBoolean, final int expectInt,
+ final String expectString) {
+ final CustomNoopAppender customAppender = (CustomNoopAppender) appender;
+ assertEquals(expectBoolean, customAppender.getBooleanA());
+ assertEquals(expectInt, customAppender.getIntA());
+ assertEquals(expectString, customAppender.getStringA());
+ }
+
+ private void checkConfigureCustomAppenders(final String configPath, final boolean expectAppend, final int expectInt, final String expectString,
+ FailableConsumer configurator) throws IOException {
+ final File file = new File(configPath);
+ assertTrue(file.exists(), "No Config file");
+ try (LoggerContext context = TestConfigurator.configure(file.toString())) {
+ final Logger logger = LogManager.getLogger("test");
+ logger.info("Hello");
+ // V1
+ checkCustomAppender("A1", expectAppend, expectInt, expectString);
+ checkCustomFileAppender("A2", expectAppend, expectInt, expectString);
+ }
+ }
+
+ private void checkConfigureFileAppender(final String configPath, final boolean expectAppend) throws IOException {
+ final File file = new File(configPath);
+ assertTrue(file.exists(), "No Config file");
+ try (LoggerContext context = TestConfigurator.configure(file.toString())) {
+ final Logger logger = LogManager.getLogger("test");
+ logger.info("Hello");
+ final Configuration configuration = context.getConfiguration();
+ // Core
+ checkCoreFileAppender(expectAppend, configuration, "A1");
+ checkCoreFileAppender(expectAppend, configuration, "A2");
+ // V1
+ checkFileAppender(expectAppend, "A1");
+ checkFileAppender(expectAppend, "A2");
+ }
+ }
+
+ private void checkCoreFileAppender(final boolean expectAppend, final Appender appender) {
+ assertNotNull(appender);
+ final FileAppender fileAppender = (FileAppender) appender;
+ @SuppressWarnings("resource")
+ final FileManager manager = fileAppender.getManager();
+ assertNotNull(manager);
+ assertEquals(expectAppend, manager.isAppend());
+ }
+
+ private void checkCoreFileAppender(final boolean expectAppend, final Configuration configuration, final String appenderName) {
+ checkCoreFileAppender(expectAppend, configuration.getAppender(appenderName));
+ }
+
+ private void checkCustomAppender(final String appenderName, final boolean expectBoolean, final int expectInt, final String expectString) {
+ final Logger logger = LogManager.getRootLogger();
+ final org.apache.log4j.Appender appender = logger.getAppender(appenderName);
+ assertNotNull(appender);
+ assertCustomNoopAppender(appender, expectBoolean, expectInt, expectString);
+ assertCustomNoopAppender(getAppenderFromContext(appenderName), expectBoolean, expectInt, expectString);
+ }
+
+ private void checkCustomFileAppender(final String appenderName, final boolean expectBoolean, final int expectInt, final String expectString) {
+ final Logger logger = LogManager.getRootLogger();
+ final org.apache.log4j.Appender appender = logger.getAppender(appenderName);
+ assertNotNull(appender);
+ assertCustomFileAppender(appender, expectBoolean, expectInt, expectString);
+ assertCustomFileAppender(getAppenderFromContext(appenderName), expectBoolean, expectInt, expectString);
+ }
+
+ private void checkFileAppender(final boolean expectAppend, final String appenderName) {
+ final Logger logger = LogManager.getRootLogger();
+ final org.apache.log4j.Appender appender = logger.getAppender(appenderName);
+ assertNotNull(appender);
+ final AppenderWrapper appenderWrapper = (AppenderWrapper) appender;
+ checkCoreFileAppender(expectAppend, appenderWrapper.getAppender());
+ }
+
+ @SuppressWarnings("unchecked")
+ private T getAppenderFromContext(final String appenderName) {
+ final LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+ final LoggerConfig loggerConfig = context.getConfiguration().getRootLogger();
+ final AppenderAdapter.Adapter adapter = (AppenderAdapter.Adapter) loggerConfig.getAppenders().get(appenderName);
+ return adapter != null ? (T) adapter.getAppender() : null;
+ }
+
+ /**
+ * Tests that configuring and reconfiguring CUSTOM appenders properly pick up different settings.
+ */
+ @Test
+ public void testCustomAppenders_TestConfigurator() throws IOException {
+ checkConfigureCustomAppenders(CONFIG_CUSTOM_APPENDERS_1, true, 1, "A", TestConfigurator::configure);
+ checkConfigureCustomAppenders(CONFIG_CUSTOM_APPENDERS_2, false, 2, "B", TestConfigurator::configure);
+ checkConfigureCustomAppenders(CONFIG_CUSTOM_APPENDERS_1, true, 1, "A", TestConfigurator::configure);
+ checkConfigureCustomAppenders(CONFIG_CUSTOM_APPENDERS_2, false, 2, "B", TestConfigurator::configure);
+ }
+
+ /**
+ * Tests that configuring and reconfiguring CUSTOM appenders properly pick up different settings.
+ */
+ @Test
+ public void testCustomAppenders_PropertyConfigurator() throws IOException {
+ checkConfigureCustomAppenders(CONFIG_CUSTOM_APPENDERS_1, true, 1, "A", PropertyConfigurator::configure);
+ checkConfigureCustomAppenders(CONFIG_CUSTOM_APPENDERS_2, false, 2, "B", PropertyConfigurator::configure);
+ checkConfigureCustomAppenders(CONFIG_CUSTOM_APPENDERS_1, true, 1, "A", PropertyConfigurator::configure);
+ checkConfigureCustomAppenders(CONFIG_CUSTOM_APPENDERS_2, false, 2, "B", PropertyConfigurator::configure);
+ }
+
+ /**
+ * Tests that configuring and reconfiguring STOCK file appenders properly pick up different settings.
+ */
+ @Test
+ public void testFileAppenders() throws Exception {
+ checkConfigureFileAppender(CONFIG_FILE_APPENDER_1, false);
+ checkConfigureFileAppender(CONFIG_FILE_APPENDER_2, true);
+ checkConfigureFileAppender(CONFIG_FILE_APPENDER_1, false);
+ checkConfigureFileAppender(CONFIG_FILE_APPENDER_2, true);
+ }
+
+ @Test
+ @Tag("sleepy")
+ public void testTestListener() throws Exception {
+ System.setProperty(Log4j1Configuration.MONITOR_INTERVAL, "1");
+ final File file = new File(CONFIG_FILE_APPENDER_1);
+ assertTrue(file.exists(), "No Config file");
+ final long configMillis = file.lastModified();
+ assertTrue(file.setLastModified(configMillis - FIVE_MINUTES.toMillis()), "Unable to modified file time");
+ try (LoggerContext context = TestConfigurator.configure(file.toString())) {
+ final Logger logger = LogManager.getLogger("test");
+ logger.info("Hello");
+ final Configuration original = context.getConfiguration();
+ final TestListener listener = new TestListener();
+ original.addListener(listener);
+ file.setLastModified(System.currentTimeMillis());
+ try {
+ if (!toggle.await(3, TimeUnit.SECONDS)) {
+ fail("Reconfiguration timed out");
+ }
+ // Allow reconfiguration to complete.
+ Thread.sleep(500);
+ } catch (final InterruptedException ie) {
+ fail("Reconfiguration interupted");
+ }
+ final Configuration updated = context.getConfiguration();
+ assertNotSame(original, updated, "Configurations are the same");
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesRollingWithPropertiesTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesRollingWithPropertiesTest.java
new file mode 100644
index 00000000000..9ad993c96f4
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesRollingWithPropertiesTest.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.log4j.config;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.test.junit.LoggerContextSource;
+import org.apache.logging.log4j.test.junit.CleanUpDirectories;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.nio.file.Paths;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Test configuration from Properties.
+ */
+public class PropertiesRollingWithPropertiesTest {
+
+ private static final String TEST_DIR = "target/PropertiesRollingWithPropertiesTest";
+
+ @BeforeAll
+ static void beforeAll() {
+ System.setProperty("test.directory", TEST_DIR);
+ }
+
+ @AfterAll
+ static void afterAll() {
+ System.clearProperty("test.directory");
+ }
+
+ @Test
+ @CleanUpDirectories(TEST_DIR)
+ @LoggerContextSource(value = "log4j1-rolling-properties.properties", v1config = true)
+ public void testProperties(final LoggerContext context) throws Exception {
+ final Logger logger = context.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ assertThat(Paths.get(TEST_DIR, "somefile.log")).exists().isNotEmptyFile();
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/RewriteAppenderTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/RewriteAppenderTest.java
new file mode 100644
index 00000000000..cb92229235e
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/RewriteAppenderTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.ThreadContext;
+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.test.junit.LoggerContextSource;
+import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Test RewriteAppender
+ */
+@UsingThreadContextMap
+public class RewriteAppenderTest {
+
+ @Test
+ @LoggerContextSource(value = "log4j1-rewrite.xml", v1config = true)
+ public void testRewrite(final LoggerContext context) {
+ Logger logger = LogManager.getLogger("test");
+ ThreadContext.put("key1", "This is a test");
+ ThreadContext.put("hello", "world");
+ long logTime = System.currentTimeMillis();
+ logger.debug("Say hello");
+ Configuration configuration = context.getConfiguration();
+ Map appenders = configuration.getAppenders();
+ ListAppender eventAppender = null;
+ for (Map.Entry entry : appenders.entrySet()) {
+ if (entry.getKey().equals("events")) {
+ eventAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+ }
+ }
+ assertNotNull(eventAppender, "No Event Appender");
+ List events = eventAppender.getEvents();
+ assertTrue(events != null && events.size() > 0, "No events");
+ assertNotNull(events.get(0).getProperties(), "No properties in the event");
+ assertTrue(events.get(0).getProperties().containsKey("key2"), "Key was not inserted");
+ assertEquals("Log4j", events.get(0).getProperties().get("key2"), "Key value is incorrect");
+ assertTrue(events.get(0).getTimeStamp() >= logTime, "Timestamp is before point of logging");
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/SocketAppenderConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SocketAppenderConfigurationTest.java
new file mode 100644
index 00000000000..9434168bca8
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SocketAppenderConfigurationTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.log4j.layout.Log4j1XmlLayout;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.appender.SocketAppender;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.filter.ThresholdFilter;
+import org.apache.logging.log4j.core.net.Protocol;
+import org.apache.logging.log4j.core.net.TcpSocketManager;
+import org.junit.Test;
+
+/**
+ * Tests configuring a Syslog appender.
+ */
+public class SocketAppenderConfigurationTest {
+
+ private SocketAppender check(final Protocol expected, final Configuration configuration) {
+ final Map appenders = configuration.getAppenders();
+ assertNotNull(appenders);
+ final String appenderName = "socket";
+ final Appender appender = appenders.get(appenderName);
+ assertNotNull(appender, "Missing appender " + appenderName);
+ final SocketAppender socketAppender = (SocketAppender) appender;
+ @SuppressWarnings("resource")
+ final TcpSocketManager manager = (TcpSocketManager) socketAppender.getManager();
+ final String prefix = expected + ":";
+ assertTrue(manager.getName().startsWith(prefix), () -> String.format("'%s' does not start with '%s'", manager.getName(), prefix));
+ // Threshold
+ final ThresholdFilter filter = (ThresholdFilter) socketAppender.getFilter();
+ assertEquals(Level.DEBUG, filter.getLevel());
+ // Host
+ assertEquals("localhost", manager.getHost());
+ // Port
+ assertEquals(9999, manager.getPort());
+ // Port
+ assertEquals(100, manager.getReconnectionDelayMillis());
+ return socketAppender;
+ }
+
+ private void checkProtocolPropertiesConfig(final Protocol expected, final String xmlPath) throws IOException {
+ check(expected, TestConfigurator.configure(xmlPath).getConfiguration());
+ }
+
+ private SocketAppender checkProtocolXmlConfig(final Protocol expected, final String xmlPath) throws IOException {
+ return check(expected, TestConfigurator.configure(xmlPath).getConfiguration());
+ }
+
+ @Test
+ public void testProperties() throws Exception {
+ checkProtocolXmlConfig(Protocol.TCP, "target/test-classes/log4j1-socket.properties");
+ }
+
+ @Test
+ public void testPropertiesXmlLayout() throws Exception {
+ final SocketAppender socketAppender = checkProtocolXmlConfig(Protocol.TCP, "target/test-classes/log4j1-socket-xml-layout.properties");
+ assertTrue(socketAppender.getLayout() instanceof Log4j1XmlLayout);
+ }
+
+ @Test
+ public void testXml() throws Exception {
+ checkProtocolXmlConfig(Protocol.TCP, "target/test-classes/log4j1-socket.xml");
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/StartsWithFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/StartsWithFilter.java
new file mode 100644
index 00000000000..db16d84f653
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/StartsWithFilter.java
@@ -0,0 +1,38 @@
+/*
+ * 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.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * A Filter used in tests.
+ */
+public class StartsWithFilter extends Filter {
+
+ @Override
+ public int decide(LoggingEvent event) {
+ String message = String.valueOf(event.getMessage());
+ if (message.startsWith("DENY")) {
+ return DENY;
+ } else if (message.startsWith("ACCEPT")) {
+ return ACCEPT;
+ }
+ return NEUTRAL;
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderConfigurationTest.java
new file mode 100644
index 00000000000..cdf9b2f8c22
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderConfigurationTest.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 static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.appender.SyslogAppender;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.filter.ThresholdFilter;
+import org.apache.logging.log4j.core.net.AbstractSocketManager;
+import org.apache.logging.log4j.core.net.Protocol;
+import org.junit.Test;
+
+/**
+ * Tests configuring a Syslog appender.
+ */
+public class SyslogAppenderConfigurationTest {
+
+ private void check(final Protocol expected, final Configuration configuration) {
+ final Map appenders = configuration.getAppenders();
+ assertNotNull(appenders);
+ final String appenderName = "syslog";
+ final Appender appender = appenders.get(appenderName);
+ assertNotNull(appender, "Missing appender " + appenderName);
+ final SyslogAppender syslogAppender = (SyslogAppender) appender;
+ @SuppressWarnings("resource")
+ final AbstractSocketManager manager = syslogAppender.getManager();
+ final String prefix = expected + ":";
+ assertTrue(manager.getName().startsWith(prefix), () -> String.format("'%s' does not start with '%s'", manager.getName(), prefix));
+ // Threshold
+ final ThresholdFilter filter = (ThresholdFilter) syslogAppender.getFilter();
+ assertEquals(Level.DEBUG, filter.getLevel());
+ // Host
+ assertEquals("localhost", manager.getHost());
+ // Port
+ assertEquals(9999, manager.getPort());
+ }
+
+ private void checkProtocolPropertiesConfig(final Protocol expected, final String xmlPath) throws IOException {
+ check(expected, TestConfigurator.configure(xmlPath).getConfiguration());
+ }
+
+ private void checkProtocolXmlConfig(final Protocol expected, final String xmlPath) throws IOException {
+ check(expected, TestConfigurator.configure(xmlPath).getConfiguration());
+ }
+
+ @Test
+ public void testPropertiesProtocolDefault() throws Exception {
+ checkProtocolPropertiesConfig(Protocol.TCP, "target/test-classes/log4j1-syslog-protocol-default.properties");
+ }
+
+ @Test
+ public void testPropertiesProtocolTcp() throws Exception {
+ checkProtocolPropertiesConfig(Protocol.TCP, "target/test-classes/log4j1-syslog-protocol-tcp.properties");
+ }
+
+ @Test
+ public void testPropertiesProtocolUdp() throws Exception {
+ checkProtocolPropertiesConfig(Protocol.UDP, "target/test-classes/log4j1-syslog-protocol-udp.properties");
+ }
+
+ @Test
+ public void testXmlProtocolDefault() throws Exception {
+ checkProtocolXmlConfig(Protocol.TCP, "target/test-classes/log4j1-syslog.xml");
+ }
+
+ @Test
+ public void testXmlProtocolTcp() throws Exception {
+ checkProtocolXmlConfig(Protocol.TCP, "target/test-classes/log4j1-syslog-protocol-tcp.xml");
+ }
+
+ @Test
+ public void testXmlProtocolUdp() throws Exception {
+ checkProtocolXmlConfig(Protocol.UDP, "target/test-classes/log4j1-syslog-protocol-udp.xml");
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderTest.java
new file mode 100644
index 00000000000..346f9e06c63
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.test.net.mock.MockSyslogServer;
+import org.apache.logging.log4j.core.test.net.mock.MockSyslogServerFactory;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+/**
+ * Class Description goes here.
+ */
+@Tag("sleepy")
+public class SyslogAppenderTest {
+
+ // TODO Use an ephemeral port, save it in a sys prop, and update test config files.
+ private static final int PORTNUM = 9999;
+ private MockSyslogServer syslogServer;
+
+ @BeforeAll
+ public static void beforeClass() {
+ System.setProperty("log4j.configuration", "target/test-classes/log4j1-syslog.xml");
+ }
+
+ @BeforeEach
+ public void setUp() {
+ }
+
+ @AfterEach
+ public void teardown() {
+ if (syslogServer != null) {
+ syslogServer.shutdown();
+ }
+ }
+
+ @Test
+ public void sendMessage() throws Exception {
+ initTCPTestEnvironment();
+ Logger logger = LogManager.getLogger(SyslogAppenderTest.class);
+ logger.info("This is a test");
+ List messages = null;
+ for (int i = 0; i < 5; ++i) {
+ Thread.sleep(250);
+ messages = syslogServer.getMessageList();
+ if (messages != null && messages.size() > 0) {
+ break;
+ }
+ }
+ assertNotNull(messages, "No messages received");
+ assertEquals(1, messages.size(), "Sent message not detected");
+ }
+
+
+ protected void initTCPTestEnvironment() throws IOException {
+ syslogServer = MockSyslogServerFactory.createTCPSyslogServer(1, PORTNUM);
+ syslogServer.start();
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/TestConfigurator.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/TestConfigurator.java
new file mode 100644
index 00000000000..c87156682b9
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/TestConfigurator.java
@@ -0,0 +1,55 @@
+/*
+ * 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 static org.junit.Assert.assertNotNull;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.apache.log4j.xml.XmlConfigurationFactory;
+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;
+
+public class TestConfigurator {
+
+ public static LoggerContext configure(final String configLocation) throws IOException {
+ final Path path = Paths.get(configLocation);
+ try (final InputStream inputStream = Files.newInputStream(path)) {
+ final ConfigurationSource source = new ConfigurationSource(inputStream, path);
+ final LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+ Configuration configuration = null;
+ if (configLocation.endsWith(PropertiesConfigurationFactory.FILE_EXTENSION)) {
+ configuration = new PropertiesConfigurationFactory().getConfiguration(context, source);
+ } else if (configLocation.endsWith(XmlConfigurationFactory.FILE_EXTENSION)) {
+ configuration = new XmlConfigurationFactory().getConfiguration(context, source);
+ } else {
+ fail("Test infra does not support " + configLocation);
+ }
+ 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
new file mode 100644
index 00000000000..8967e6d80b0
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationFactoryTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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 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 XmlConfigurationFactoryTest {
+
+ @BeforeClass
+ public static void beforeClass() {
+ System.setProperty(ConfigurationFactory.LOG4J1_CONFIGURATION_FILE_PROPERTY, "target/test-classes/log4j1-file.xml");
+ }
+ @Test
+ public void testXML() {
+ 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/XmlConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.java
new file mode 100644
index 00000000000..f1e118bee8e
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.java
@@ -0,0 +1,188 @@
+/*
+ * 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 java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Map;
+
+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.ConfigurationFactory;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.util.Lazy;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Test configuration from XML.
+ */
+public class XmlConfigurationTest extends AbstractLog4j1ConfigurationTest {
+
+ private static final String SUFFIX = ".xml";
+
+ @Override
+ Configuration getConfiguration(String configResourcePrefix) throws URISyntaxException, IOException {
+ final String configResource = configResourcePrefix + SUFFIX;
+ final InputStream inputStream = ClassLoader.getSystemResourceAsStream(configResource);
+ final ConfigurationSource source = new ConfigurationSource(inputStream);
+ final LoggerContext context = LoggerContext.getContext(false);
+ final Configuration configuration = context.getInjector()
+ .registerBinding(ConfigurationFactory.KEY, Lazy.lazy(XmlConfigurationFactory::new))
+ .getInstance(ConfigurationFactory.KEY)
+ .getConfiguration(context, source);
+ assertNotNull(configuration, "No configuration created");
+ configuration.initialize();
+ return configuration;
+ }
+
+ @Test
+ public void testXML() throws Exception {
+ configure("log4j1-file");
+ Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ File file = new File("target/temp.A1");
+ assertTrue(file.exists(), "File A1 was not created");
+ assertTrue(file.length() > 0, "File A1 is empty");
+ file = new File("target/temp.A2");
+ assertTrue(file.exists(), "File A2 was not created");
+ assertTrue(file.length() > 0, "File A2 is empty");
+ }
+
+ @Test
+ public void testListAppender() throws Exception {
+ LoggerContext loggerContext = configure("log4j1-list");
+ Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ Configuration configuration = loggerContext.getConfiguration();
+ Map appenders = configuration.getAppenders();
+ ListAppender eventAppender = null;
+ ListAppender messageAppender = null;
+ for (Map.Entry 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(eventAppender, "No Event Appender");
+ assertNotNull(messageAppender, "No Message Appender");
+ List events = eventAppender.getEvents();
+ assertTrue(events != null && events.size() > 0, "No events");
+ List messages = messageAppender.getMessages();
+ assertTrue(messages != null && messages.size() > 0, "No messages");
+ }
+
+ @Override
+ @Test
+ public void testConsoleEnhancedPatternLayout() throws Exception {
+ super.testConsoleEnhancedPatternLayout();
+ }
+
+ @Override
+ @Test
+ public void testConsoleHtmlLayout() throws Exception {
+ super.testConsoleHtmlLayout();
+ }
+
+ @Override
+ @Test
+ public void testConsolePatternLayout() throws Exception {
+ super.testConsolePatternLayout();
+ }
+
+ @Override
+ @Test
+ public void testConsoleSimpleLayout() throws Exception {
+ super.testConsoleSimpleLayout();
+ }
+
+ @Override
+ @Test
+ public void testFileSimpleLayout() throws Exception {
+ super.testFileSimpleLayout();
+ }
+
+ @Override
+ @Test
+ public void testNullAppender() throws Exception {
+ super.testNullAppender();
+ }
+
+ @Override
+ @Test
+ public void testConsoleCapitalization() throws Exception {
+ super.testConsoleCapitalization();
+ }
+
+ @Override
+ @Test
+ public void testConsoleTtccLayout() throws Exception {
+ super.testConsoleTtccLayout();
+ }
+
+ @Override
+ @Test
+ public void testRollingFileAppender() throws Exception {
+ super.testRollingFileAppender();
+ }
+
+ @Override
+ @Test
+ public void testDailyRollingFileAppender() throws Exception {
+ super.testDailyRollingFileAppender();
+ }
+
+ @Override
+ @Test
+ public void testSystemProperties1() throws Exception {
+ super.testSystemProperties1();
+ }
+
+ @Override
+ @Test
+ public void testDefaultValues() throws Exception {
+ super.testDefaultValues();
+ }
+
+ @Override
+ @Test
+ public void testMultipleFilters(final @TempDir Path tmpFolder) throws Exception {
+ super.testMultipleFilters(tmpFolder);
+ }
+
+ @Override
+ @Test
+ public void testGlobalThreshold() throws Exception {
+ super.testGlobalThreshold();
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlReconfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlReconfigurationTest.java
new file mode 100644
index 00000000000..835f1c44215
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlReconfigurationTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationListener;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.apache.logging.log4j.core.config.Reconfigurable;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Test reconfiguring with an XML configuration.
+ */
+@Tag("sleepy")
+public class XmlReconfigurationTest {
+
+ private static final String CONFIG = "target/test-classes/log4j1-file.xml";
+ private static final long FIVE_MINUTES = 5 * 60 * 1000;
+
+ private final CountDownLatch toggle = new CountDownLatch(1);
+
+ @Test
+ public void testReconfiguration() throws Exception {
+ System.setProperty(Log4j1Configuration.MONITOR_INTERVAL, "1");
+ File file = new File(CONFIG);
+ assertNotNull(file, "No Config file");
+ long configMillis = file.lastModified();
+ Assertions.assertTrue(file.setLastModified(configMillis - FIVE_MINUTES), "Unable to modified file time");
+ LoggerContext context = configure(file);
+ Logger logger = LogManager.getLogger("test");
+ logger.info("Hello");
+ Configuration original = context.getConfiguration();
+ TestListener listener = new TestListener();
+ original.addListener(listener);
+ file.setLastModified(System.currentTimeMillis());
+ try {
+ if (!toggle.await(3, TimeUnit.SECONDS)) {
+ fail("Reconfiguration timed out");
+ }
+ // Allow reconfiguration to complete.
+ Thread.sleep(500);
+ } catch (InterruptedException ie) {
+ fail("Reconfiguration interupted");
+ }
+ Configuration updated = context.getConfiguration();
+ assertNotSame(original, updated, "Configurations are the same");
+ }
+
+ private class TestListener implements ConfigurationListener {
+
+ public synchronized void onChange(final Reconfigurable reconfigurable) {
+ toggle.countDown();
+ }
+
+ }
+
+ private LoggerContext configure(File configFile) throws Exception {
+ InputStream is = new FileInputStream(configFile);
+ ConfigurationSource source = new ConfigurationSource(is, configFile);
+ LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+ Configuration configuration = new XmlConfigurationFactory().getConfiguration(context, source);
+ assertNotNull(configuration, "No configuration created");
+ Configurator.reconfigure(configuration);
+ return context;
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlRollingWithPropertiesTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlRollingWithPropertiesTest.java
new file mode 100644
index 00000000000..05ee7db08bc
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlRollingWithPropertiesTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.Logger;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.test.junit.LoggerContextSource;
+import org.apache.logging.log4j.test.junit.CleanUpDirectories;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.nio.file.Paths;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Test configuration from Properties.
+ */
+public class XmlRollingWithPropertiesTest {
+
+ private static final String TEST_DIR = "target/XmlRollingWithPropertiesTest";
+
+ @BeforeAll
+ static void beforeAll() {
+ System.setProperty("test.directory", TEST_DIR);
+ }
+
+ @AfterAll
+ static void afterAll() {
+ System.clearProperty("test.directory");
+ }
+
+ @Test
+ @CleanUpDirectories(TEST_DIR)
+ @LoggerContextSource(value = "log4j1-rolling-properties.xml", v1config = true)
+ public void testProperties(final LoggerContext context) {
+ // ${test.directory}/logs/etl.log
+ final Logger logger = context.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ assertThat(Paths.get(TEST_DIR, "logs/etl.log")).exists().isNotEmptyFile();
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/defaultInit/TestCase1.java b/log4j-1.2-api/src/test/java/org/apache/log4j/defaultInit/TestCase1.java
new file mode 100644
index 00000000000..44f5f04ec7a
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/defaultInit/TestCase1.java
@@ -0,0 +1,55 @@
+/*
+ * 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.defaultInit;
+
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.junit.Ignore;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+@Ignore
+public class TestCase1 extends TestCase {
+
+ public TestCase1(String name) {
+ super(name);
+ }
+
+ public void setUp() {
+ }
+
+ public void tearDown() {
+ LogManager.shutdown();
+ }
+
+ public void noneTest() {
+ Logger root = Logger.getRootLogger();
+ boolean rootIsConfigured = root.getAllAppenders().hasMoreElements();
+ assertTrue(!rootIsConfigured);
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite();
+ suite.addTest(new TestCase1("noneTest"));
+ return suite;
+ }
+
+}
+
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/defaultInit/TestCase2.java b/log4j-1.2-api/src/test/java/org/apache/log4j/defaultInit/TestCase2.java
new file mode 100644
index 00000000000..33b2915903c
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/defaultInit/TestCase2.java
@@ -0,0 +1,59 @@
+/*
+ * 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.defaultInit;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import junit.framework.Test;
+import java.util.Enumeration;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Logger;
+import org.junit.Ignore;
+import org.apache.log4j.LogManager;
+
+@Ignore
+public class TestCase2 extends TestCase {
+
+ public TestCase2(String name) {
+ super(name);
+ }
+
+ public void setUp() {
+ }
+
+ public void tearDown() {
+ LogManager.shutdown();
+ }
+
+ public void xmlTest() {
+ Logger root = Logger.getRootLogger();
+ boolean rootIsConfigured = root.getAllAppenders().hasMoreElements();
+ assertTrue(rootIsConfigured);
+ Enumeration e = root.getAllAppenders();
+ Appender appender = (Appender) e.nextElement();
+ assertEquals(appender.getName(), "D1");
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite();
+ suite.addTest(new TestCase2("xmlTest"));
+ return suite;
+ }
+
+}
+
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/defaultInit/TestCase3.java b/log4j-1.2-api/src/test/java/org/apache/log4j/defaultInit/TestCase3.java
new file mode 100644
index 00000000000..73c176f6e03
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/defaultInit/TestCase3.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.defaultInit;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.config.TestConfigurator;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+@Disabled
+public class TestCase3 {
+
+ @AfterAll
+ public static void tearDown() {
+ LogManager.shutdown();
+ }
+
+ @Test
+ public void propertiesTest() throws IOException {
+ TestConfigurator.configure("src/test/resources/log4j1-1.2.17/input/defaultInit3.properties");
+ Logger root = Logger.getRootLogger();
+ boolean rootIsConfigured = root.getAllAppenders().hasMoreElements();
+ assertTrue(rootIsConfigured);
+ Enumeration e = root.getAllAppenders();
+ Appender appender = (Appender) e.nextElement();
+ assertEquals(appender.getName(), "D3");
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/defaultInit/TestCase4.java b/log4j-1.2-api/src/test/java/org/apache/log4j/defaultInit/TestCase4.java
new file mode 100644
index 00000000000..c44b546d156
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/defaultInit/TestCase4.java
@@ -0,0 +1,60 @@
+/*
+ * 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.defaultInit;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import junit.framework.Test;
+import java.util.Enumeration;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Logger;
+import org.junit.Ignore;
+import org.apache.log4j.LogManager;
+
+@Ignore
+public class TestCase4 extends TestCase {
+
+ public TestCase4(String name) {
+ super(name);
+ }
+
+ public void setUp() {
+ }
+
+ public void tearDown() {
+ LogManager.shutdown();
+ }
+
+ public void combinedTest() {
+ Logger root = Logger.getRootLogger();
+ boolean rootIsConfigured = root.getAllAppenders().hasMoreElements();
+ assertTrue(rootIsConfigured);
+ Enumeration e = root.getAllAppenders();
+ Appender appender = (Appender) e.nextElement();
+ assertEquals(appender.getName(), "D1");
+ assertEquals(e.hasMoreElements(), false);
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite();
+ suite.addTest(new TestCase4("combinedTest"));
+ return suite;
+ }
+
+}
+
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/BoundedFIFOTestCase.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/BoundedFIFOTestCase.java
new file mode 100644
index 00000000000..c5ff7179fbf
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/BoundedFIFOTestCase.java
@@ -0,0 +1,233 @@
+/*
+ * 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.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggingEvent;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Test {@link BoundedFIFO}.
+ *
+ * @since 0.9.1
+ */
+public class BoundedFIFOTestCase extends TestCase {
+ static Logger cat = Logger.getLogger("x");
+
+ static int MAX = 1000;
+
+ static LoggingEvent[] e = new LoggingEvent[MAX];
+
+ public static Test suite() {
+ final TestSuite suite = new TestSuite();
+ suite.addTest(new BoundedFIFOTestCase("test1"));
+ suite.addTest(new BoundedFIFOTestCase("test2"));
+ suite.addTest(new BoundedFIFOTestCase("testResize1"));
+ suite.addTest(new BoundedFIFOTestCase("testResize2"));
+ suite.addTest(new BoundedFIFOTestCase("testResize3"));
+ return suite;
+ }
+
+ {
+ for (int i = 0; i < MAX; i++) {
+ e[i] = new LoggingEvent("", cat, Level.DEBUG, "e" + i, null);
+ }
+ }
+
+ public BoundedFIFOTestCase(final String name) {
+ super(name);
+ }
+
+ int min(final int a, final int b) {
+ return a < b ? a : b;
+ }
+
+ @Override
+ public void setUp() {
+
+ }
+
+ /**
+ * Pattern: +++++..-----..
+ */
+ public void test1() {
+ for (int size = 1; size <= 128; size *= 2) {
+ final BoundedFIFO bf = new BoundedFIFO(size);
+
+ assertEquals(bf.getMaxSize(), size);
+ assertNull(bf.get());
+
+ int i;
+ int j;
+ int k;
+
+ for (i = 1; i < 2 * size; i++) {
+ for (j = 0; j < i; j++) {
+ // System.out.println("Putting "+e[j]);
+ bf.put(e[j]);
+ assertEquals(bf.length(), j < size ? j + 1 : size);
+ }
+ final int max = size < j ? size : j;
+ j--;
+ for (k = 0; k <= j; k++) {
+ // System.out.println("max="+max+", j="+j+", k="+k);
+ assertEquals(bf.length(), max - k > 0 ? max - k : 0);
+ final Object r = bf.get();
+ // System.out.println("Got "+r);
+ if (k >= size) {
+ assertNull(r);
+ } else {
+ assertEquals(r, e[k]);
+ }
+ }
+ }
+ // System.out.println("Passed size="+size);
+ }
+ }
+
+ /**
+ * Pattern: ++++--++--++
+ */
+ public void test2() {
+ final int size = 3;
+ final BoundedFIFO bf = new BoundedFIFO(size);
+
+ bf.put(e[0]);
+ assertEquals(bf.get(), e[0]);
+ assertNull(bf.get());
+
+ bf.put(e[1]);
+ assertEquals(bf.length(), 1);
+ bf.put(e[2]);
+ assertEquals(bf.length(), 2);
+ bf.put(e[3]);
+ assertEquals(bf.length(), 3);
+ assertEquals(bf.get(), e[1]);
+ assertEquals(bf.length(), 2);
+ assertEquals(bf.get(), e[2]);
+ assertEquals(bf.length(), 1);
+ assertEquals(bf.get(), e[3]);
+ assertEquals(bf.length(), 0);
+ assertNull(bf.get());
+ assertEquals(bf.length(), 0);
+ }
+
+ /**
+ * Pattern ++++++++++++++++++++ (insert only);
+ */
+ public void testResize1() {
+ final int size = 10;
+
+ for (int n = 1; n < size * 2; n++) {
+ for (int i = 0; i < size * 2; i++) {
+
+ final BoundedFIFO bf = new BoundedFIFO(size);
+ for (int f = 0; f < i; f++) {
+ bf.put(e[f]);
+ }
+
+ bf.resize(n);
+ final int expectedSize = min(n, min(i, size));
+ assertEquals(bf.length(), expectedSize);
+ for (int c = 0; c < expectedSize; c++) {
+ assertEquals(bf.get(), e[c]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Pattern ++...+ --...-
+ */
+ public void testResize2() {
+ final int size = 10;
+
+ for (int n = 1; n < size * 2; n++) {
+ for (int i = 0; i < size * 2; i++) {
+ for (int d = 0; d < min(i, size); d++) {
+
+ final BoundedFIFO bf = new BoundedFIFO(size);
+ for (int p = 0; p < i; p++) {
+ bf.put(e[p]);
+ }
+
+ for (int g = 0; g < d; g++) {
+ bf.get();
+ }
+
+ // x = the number of elems in
+ final int x = bf.length();
+
+ bf.resize(n);
+
+ final int expectedSize = min(n, x);
+ assertEquals(bf.length(), expectedSize);
+
+ for (int c = 0; c < expectedSize; c++) {
+ assertEquals(bf.get(), e[c + d]);
+ }
+ assertNull(bf.get());
+ }
+ }
+ }
+ }
+
+ /**
+ * Pattern: i inserts, d deletes, r inserts
+ */
+ public void testResize3() {
+ final int size = 10;
+
+ for (int n = 1; n < size * 2; n++) {
+ for (int i = 0; i < size; i++) {
+ for (int d = 0; d < i; d++) {
+ for (int r = 0; r < d; r++) {
+
+ final BoundedFIFO bf = new BoundedFIFO(size);
+ for (int p0 = 0; p0 < i; p0++) {
+ bf.put(e[p0]);
+ }
+
+ for (int g = 0; g < d; g++) {
+ bf.get();
+ }
+ for (int p1 = 0; p1 < r; p1++) {
+ bf.put(e[i + p1]);
+ }
+
+ final int x = bf.length();
+
+ bf.resize(n);
+
+ final int expectedSize = min(n, x);
+ assertEquals(bf.length(), expectedSize);
+
+ for (int c = 0; c < expectedSize; c++) {
+ assertEquals(bf.get(), e[c + d]);
+ }
+ // assertNull(bf.get());
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/CyclicBufferTestCase.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/CyclicBufferTestCase.java
new file mode 100644
index 00000000000..bb294d10640
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/CyclicBufferTestCase.java
@@ -0,0 +1,159 @@
+/*
+ * 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 uses the JUnit framework for internal unit testing. JUnit
+// available from
+//
+// http://www.junit.org
+
+package org.apache.log4j.helpers;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggingEvent;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Tests {@link CyclicBuffer}.
+ */
+public class CyclicBufferTestCase extends TestCase {
+
+ static Logger cat = Logger.getLogger("x");
+
+ static int MAX = 1000;
+
+ static LoggingEvent[] e = new LoggingEvent[MAX];
+
+ public static Test suite() {
+ final TestSuite suite = new TestSuite();
+ suite.addTest(new CyclicBufferTestCase("test0"));
+ suite.addTest(new CyclicBufferTestCase("test1"));
+ suite.addTest(new CyclicBufferTestCase("testResize"));
+ return suite;
+ }
+
+ {
+ for (int i = 0; i < MAX; i++) {
+ e[i] = new LoggingEvent("", cat, Level.DEBUG, "e" + i, null);
+ }
+ }
+
+ public CyclicBufferTestCase(final String name) {
+ super(name);
+ }
+
+ void doTest1(final int size) {
+ // System.out.println("Doing test with size = "+size);
+ final CyclicBuffer cb = new CyclicBuffer(size);
+
+ assertEquals(cb.getMaxSize(), size);
+
+ for (int i = -(size + 10); i < (size + 10); i++) {
+ assertNull(cb.get(i));
+ }
+
+ for (int i = 0; i < MAX; i++) {
+ cb.add(e[i]);
+ final int limit = i < size - 1 ? i : size - 1;
+
+ // System.out.println("\nLimit is " + limit + ", i="+i);
+
+ for (int j = limit; j >= 0; j--) {
+ // System.out.println("i= "+i+", j="+j);
+ assertEquals(cb.get(j), e[i - (limit - j)]);
+ }
+ assertNull(cb.get(-1));
+ assertNull(cb.get(limit + 1));
+ }
+ }
+
+ void doTestResize(final int initialSize, final int numberOfAdds, final int newSize) {
+ // System.out.println("initialSize = "+initialSize+", numberOfAdds="
+ // +numberOfAdds+", newSize="+newSize);
+ final CyclicBuffer cb = new CyclicBuffer(initialSize);
+ for (int i = 0; i < numberOfAdds; i++) {
+ cb.add(e[i]);
+ }
+ cb.resize(newSize);
+
+ int offset = numberOfAdds - initialSize;
+ if (offset < 0) {
+ offset = 0;
+ }
+
+ int len = newSize < numberOfAdds ? newSize : numberOfAdds;
+ len = len < initialSize ? len : initialSize;
+ // System.out.println("Len = "+len+", offset="+offset);
+ for (int j = 0; j < len; j++) {
+ assertEquals(cb.get(j), e[offset + j]);
+ }
+
+ }
+
+ @Override
+ public void setUp() {
+
+ }
+
+ public void test0() {
+ final int size = 2;
+
+ CyclicBuffer cb = new CyclicBuffer(size);
+ assertEquals(cb.getMaxSize(), size);
+
+ cb.add(e[0]);
+ assertEquals(cb.length(), 1);
+ assertEquals(cb.get(), e[0]);
+ assertEquals(cb.length(), 0);
+ assertNull(cb.get());
+ assertEquals(cb.length(), 0);
+
+ cb = new CyclicBuffer(size);
+ cb.add(e[0]);
+ cb.add(e[1]);
+ assertEquals(cb.length(), 2);
+ assertEquals(cb.get(), e[0]);
+ assertEquals(cb.length(), 1);
+ assertEquals(cb.get(), e[1]);
+ assertEquals(cb.length(), 0);
+ assertNull(cb.get());
+ assertEquals(cb.length(), 0);
+
+ }
+
+ /**
+ * Test a buffer of size 1,2,4,8,..,128
+ */
+ public void test1() {
+ for (int bufSize = 1; bufSize <= 128; bufSize *= 2) {
+ doTest1(bufSize);
+ }
+ }
+
+ public void testResize() {
+ for (int isize = 1; isize <= 128; isize *= 2) {
+ doTestResize(isize, isize / 2 + 1, isize / 2 + 1);
+ doTestResize(isize, isize / 2 + 1, isize + 10);
+ doTestResize(isize, isize + 10, isize / 2 + 1);
+ doTestResize(isize, isize + 10, isize + 10);
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/DateLayoutTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/DateLayoutTest.java
new file mode 100644
index 00000000000..e650f3df6da
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/DateLayoutTest.java
@@ -0,0 +1,287 @@
+/*
+ * 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 java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.TimeZone;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.LayoutTest;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Tests {@link DateLayout}.
+ */
+public class DateLayoutTest extends LayoutTest {
+
+ /**
+ * Construct a new instance of LayoutTest.
+ *
+ * @param testName test name.
+ */
+ public DateLayoutTest(final String testName) {
+ super(testName);
+ }
+
+ /**
+ * Constructor for use by derived tests.
+ *
+ * @param testName name of test.
+ * @param expectedContentType expected value for getContentType().
+ * @param expectedIgnoresThrowable expected value for ignoresThrowable().
+ * @param expectedHeader expected value for getHeader().
+ * @param expectedFooter expected value for getFooter().
+ */
+ protected DateLayoutTest(final String testName, final String expectedContentType, final boolean expectedIgnoresThrowable, final String expectedHeader,
+ final String expectedFooter) {
+ super(testName, expectedContentType, expectedIgnoresThrowable, expectedHeader, expectedFooter);
+ }
+
+ /**
+ * @{inheritDoc}
+ */
+ protected Layout createLayout() {
+ return new MockLayout();
+ }
+
+ /**
+ * Tests DateLayout.NULL_DATE_FORMAT constant.
+ */
+ public void testNullDateFormat() {
+ assertEquals("NULL", DateLayout.NULL_DATE_FORMAT);
+ }
+
+ /**
+ * Tests DateLayout.RELATIVE constant.
+ */
+ public void testRelativeTimeDateFormat() {
+ assertEquals("RELATIVE", DateLayout.RELATIVE_TIME_DATE_FORMAT);
+ }
+
+ /**
+ * Tests DateLayout.DATE_FORMAT_OPTION constant.
+ *
+ * @deprecated since constant is deprecated
+ */
+ public void testDateFormatOption() {
+ assertEquals("DateFormat", DateLayout.DATE_FORMAT_OPTION);
+ }
+
+ /**
+ * Tests DateLayout.TIMEZONE_OPTION constant.
+ *
+ * @deprecated since constant is deprecated
+ */
+ public void testTimeZoneOption() {
+ assertEquals("TimeZone", DateLayout.TIMEZONE_OPTION);
+ }
+
+ /**
+ * Tests getOptionStrings().
+ *
+ * @deprecated since getOptionStrings is deprecated.
+ *
+ */
+ public void testGetOptionStrings() {
+ String[] options = ((DateLayout) createLayout()).getOptionStrings();
+ assertEquals(2, options.length);
+ }
+
+ /**
+ * Tests setting DateFormat through setOption method.
+ *
+ * @deprecated since setOption is deprecated.
+ */
+ public void testSetOptionDateFormat() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setOption("dAtefOrmat", "foobar");
+ assertEquals("FOOBAR", layout.getDateFormat());
+ }
+
+ /**
+ * Tests setting TimeZone through setOption method.
+ *
+ * @deprecated since setOption is deprecated.
+ */
+ public void testSetOptionTimeZone() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setOption("tImezOne", "+05:00");
+ assertEquals("+05:00", layout.getTimeZone());
+ }
+
+ /**
+ * Tests setDateFormat.
+ */
+ public void testSetDateFormat() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("ABSOLUTE");
+ assertEquals("ABSOLUTE", layout.getDateFormat());
+ }
+
+ /**
+ * Tests setTimeZone.
+ */
+ public void testSetTimeZone() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setTimeZone("+05:00");
+ assertEquals("+05:00", layout.getTimeZone());
+ }
+
+ /**
+ * Tests 2 parameter setDateFormat with null.
+ */
+ public void testSetDateFormatNull() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat((String) null, null);
+ }
+
+ /**
+ * Tests 2 parameter setDateFormat with "NULL".
+ */
+ public void testSetDateFormatNullString() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("NuLL", null);
+ }
+
+ /**
+ * Tests 2 parameter setDateFormat with "RELATIVE".
+ */
+ public void testSetDateFormatRelative() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("rElatIve", TimeZone.getDefault());
+ }
+
+ /**
+ * Tests 2 parameter setDateFormat with "ABSOLUTE".
+ */
+ public void testSetDateFormatAbsolute() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("aBsolUte", TimeZone.getDefault());
+ }
+
+ /**
+ * Tests 2 parameter setDateFormat with "DATETIME".
+ */
+ public void testSetDateFormatDateTime() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("dAte", TimeZone.getDefault());
+ }
+
+ /**
+ * Tests 2 parameter setDateFormat with "ISO8601".
+ */
+ public void testSetDateFormatISO8601() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("iSo8601", TimeZone.getDefault());
+ }
+
+ /**
+ * Tests 2 parameter setDateFormat with "HH:mm:ss".
+ */
+ public void testSetDateFormatSimple() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("HH:mm:ss", TimeZone.getDefault());
+ }
+
+ /**
+ * Tests activateOptions.
+ */
+ public void testActivateOptions() {
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("HH:mm:ss");
+ layout.setTimeZone("+05:00");
+ layout.activateOptions();
+ }
+
+ /**
+ * Tests setDateFormat(DateFormat, TimeZone).
+ */
+ public void testSetDateFormatWithFormat() {
+ DateFormat format = new SimpleDateFormat("HH:mm");
+ DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat(format, TimeZone.getDefault());
+ }
+
+ /**
+ * Tests IS08601DateFormat class.
+ *
+ * @deprecated since ISO8601DateFormat is deprecated
+ */
+ public void testISO8601Format() {
+ DateFormat format = new ISO8601DateFormat();
+ Calendar calendar = Calendar.getInstance();
+ calendar.clear();
+ calendar.set(1970, 0, 1, 0, 0, 0);
+ String actual = format.format(calendar.getTime());
+ assertEquals("1970-01-01 00:00:00,000", actual);
+ }
+
+ /**
+ * Tests DateTimeDateFormat class.
+ *
+ * @deprecated since DateTimeDateFormat is deprecated
+ */
+ public void testDateTimeFormat() {
+ DateFormat format = new DateTimeDateFormat();
+ Calendar calendar = Calendar.getInstance();
+ calendar.clear();
+ calendar.set(1970, 0, 1, 0, 0, 0);
+ String actual = format.format(calendar.getTime());
+ SimpleDateFormat df = new SimpleDateFormat("dd MMM yyyy HH:mm:ss,SSS");
+ String expected = df.format(calendar.getTime());
+ assertEquals(expected, actual);
+ }
+
+ /**
+ * Concrete Layout class for tests.
+ */
+ private static final class MockLayout extends DateLayout {
+ /**
+ * Create new instance of MockLayout.
+ */
+ public MockLayout() {
+ //
+ // checks that protected fields are properly initialized
+ assertNotNull(pos);
+ assertNotNull(date);
+ assertNull(dateFormat);
+ }
+
+ /**
+ * @{inheritDoc}
+ */
+ public String format(final LoggingEvent event) {
+ return "Mock";
+ }
+
+ /**
+ * @{inheritDoc}
+ */
+ public void activateOptions() {
+ }
+
+ /**
+ * @{inheritDoc}
+ */
+ public boolean ignoresThrowable() {
+ return true;
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/LogLogTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/LogLogTest.java
new file mode 100644
index 00000000000..5341e1d3dfb
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/LogLogTest.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.helpers;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests {@link LogLog}.
+ */
+public class LogLogTest extends TestCase {
+
+ /**
+ * Create new instance of LogLogTest.
+ *
+ * @param testName test name
+ */
+ public LogLogTest(final String testName) {
+ super(testName);
+ }
+
+ /**
+ * Check value of CONFIG_DEBUG_KEY.
+ *
+ * @deprecated since constant is deprecated
+ */
+ @Deprecated
+ public void testConfigDebugKey() {
+ assertEquals("log4j.configDebug", LogLog.CONFIG_DEBUG_KEY);
+ }
+
+ /**
+ * Check value of DEBUG_KEY.
+ */
+ public void testDebugKey() {
+ assertEquals("log4j.debug", LogLog.DEBUG_KEY);
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/OptionConverterLevelTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/OptionConverterLevelTest.java
new file mode 100644
index 00000000000..da7852acf35
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/OptionConverterLevelTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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 static org.apache.log4j.helpers.OptionConverter.toLog4j1Level;
+import static org.apache.log4j.helpers.OptionConverter.toLog4j2Level;
+import static org.junit.Assert.assertNull;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.util.Arrays;
+import java.util.stream.Stream;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Priority;
+import org.apache.log4j.bridge.LogEventAdapter;
+import org.apache.logging.log4j.spi.StandardLevel;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+public class OptionConverterLevelTest {
+
+ static Stream standardLevels() {
+ return Arrays.stream(StandardLevel.values())
+ .map(Enum::name)
+ .map(name -> Arguments.of(Level.toLevel(name), org.apache.logging.log4j.Level.toLevel(name)));
+ }
+
+ /**
+ * Test if the standard levels are transformed correctly.
+ *
+ * @param log4j1Level
+ * @param log4j2Level
+ */
+ @ParameterizedTest
+ @MethodSource("standardLevels")
+ public void testStandardLevelConversion(final Level log4j1Level, final org.apache.logging.log4j.Level log4j2Level) {
+ assertEquals(log4j2Level, OptionConverter.convertLevel(log4j1Level));
+ assertEquals(log4j1Level, OptionConverter.convertLevel(log4j2Level));
+ }
+
+ /**
+ * Test if the conversion works at an integer level.
+ *
+ * @param log4j1Level
+ * @param log4j2Level
+ */
+ @ParameterizedTest
+ @MethodSource("standardLevels")
+ public void testStandardIntLevelConversion(final Level log4j1Level,
+ final org.apache.logging.log4j.Level log4j2Level) {
+ assertEquals(log4j2Level.intLevel(), toLog4j2Level(log4j1Level.toInt()));
+ assertEquals(log4j1Level.toInt(), toLog4j1Level(log4j2Level.intLevel()));
+ }
+
+ @Test
+ public void testMaxMinCutoff() {
+ // The cutoff values are transformed into ALL and OFF
+ assertEquals(StandardLevel.ALL.intLevel(), toLog4j2Level(OptionConverter.MIN_CUTOFF_LEVEL));
+ assertEquals(StandardLevel.OFF.intLevel(), toLog4j2Level(OptionConverter.MAX_CUTOFF_LEVEL));
+ // Maximal and minimal Log4j 1.x values different from ALL or OFF
+ int minTransformed = toLog4j1Level(toLog4j2Level(OptionConverter.MIN_CUTOFF_LEVEL + 1));
+ assertEquals(OptionConverter.MIN_CUTOFF_LEVEL + 1, minTransformed);
+ int maxTransformed = toLog4j1Level(toLog4j2Level(OptionConverter.MAX_CUTOFF_LEVEL - 1));
+ assertEquals(OptionConverter.MAX_CUTOFF_LEVEL - 1, maxTransformed);
+ // Maximal and minimal Log4j 2.x value different from ALL or OFF
+ minTransformed = toLog4j2Level(toLog4j1Level(StandardLevel.OFF.intLevel() + 1));
+ assertEquals(StandardLevel.OFF.intLevel() + 1, minTransformed);
+ maxTransformed = toLog4j2Level(toLog4j1Level(StandardLevel.ALL.intLevel() - 1));
+ assertEquals(StandardLevel.ALL.intLevel() - 1, maxTransformed);
+ }
+
+ /**
+ * Test if the values in at least the TRACE to FATAL range are transformed
+ * correctly.
+ */
+ @Test
+ public void testUsefulRange() {
+ for (int intLevel = StandardLevel.OFF.intLevel(); intLevel <= StandardLevel.TRACE.intLevel(); intLevel++) {
+ assertEquals(intLevel, toLog4j2Level(toLog4j1Level(intLevel)));
+ }
+ for (int intLevel = Level.TRACE_INT; intLevel < OptionConverter.MAX_CUTOFF_LEVEL; intLevel = intLevel + 100) {
+ assertEquals(intLevel, toLog4j1Level(toLog4j2Level(intLevel)));
+ }
+ }
+
+ /**
+ * Levels defined in Log4j 2.x should have an equivalent in Log4j 1.x. Those are
+ * used in {@link LogEventAdapter}.
+ */
+ @Test
+ public void testCustomLog4j2Levels() {
+ final int infoDebug = (StandardLevel.INFO.intLevel() + StandardLevel.DEBUG.intLevel()) / 2;
+ final org.apache.logging.log4j.Level v2Level = org.apache.logging.log4j.Level.forName("INFO_DEBUG", infoDebug);
+ final Level v1Level = OptionConverter.toLevel("INFO_DEBUG#" + org.apache.logging.log4j.Level.class.getName(), null);
+ assertNotNull(v1Level);
+ assertEquals(v2Level, v1Level.getVersion2Level());
+ final int expectedLevel = (Priority.INFO_INT + Priority.DEBUG_INT) / 2;
+ assertEquals(expectedLevel, v1Level.toInt());
+ // convertLevel
+ assertEquals(v1Level, OptionConverter.convertLevel(v2Level));
+ // Non-existent level
+ assertNull(OptionConverter.toLevel("WARN_INFO#" + org.apache.logging.log4j.Level.class.getName(), null));
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/OptionConverterTestCase.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/OptionConverterTestCase.java
new file mode 100644
index 00000000000..83ded203055
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/OptionConverterTestCase.java
@@ -0,0 +1,177 @@
+/*
+ * 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 uses the JUnit framework for internal unit testing. JUnit
+// is available from "http://www.junit.org".
+
+package org.apache.log4j.helpers;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.PropertyConfiguratorTest;
+import org.apache.log4j.xml.XLevel;
+import org.junit.Ignore;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Test variable substitution code.
+ *
+ * @since 1.0
+ */
+@Ignore("WIP")
+public class OptionConverterTestCase extends TestCase {
+
+ Properties props;
+
+ public OptionConverterTestCase(String name) {
+ super(name);
+ }
+
+ public void setUp() {
+ props = new Properties();
+ props.put("TOTO", "wonderful");
+ props.put("key1", "value1");
+ props.put("key2", "value2");
+ // Log4J will NPE without this:
+ props.put("line.separator", System.getProperty("line.separator"));
+ // Log4J will throw an Error without this:
+ props.put("java.home", System.getProperty("java.home"));
+ System.setProperties(props);
+
+ }
+
+ public void tearDown() {
+ props = null;
+ LogManager.resetConfiguration();
+ }
+
+ public void varSubstTest1() {
+ String r;
+
+ r = OptionConverter.substVars("hello world.", null);
+ assertEquals("hello world.", r);
+
+ r = OptionConverter.substVars("hello ${TOTO} world.", null);
+
+ assertEquals("hello wonderful world.", r);
+ }
+
+ public void varSubstTest2() {
+ String r;
+
+ r = OptionConverter.substVars("Test2 ${key1} mid ${key2} end.", null);
+ assertEquals("Test2 value1 mid value2 end.", r);
+ }
+
+ public void varSubstTest3() {
+ String r;
+
+ r = OptionConverter.substVars("Test3 ${unset} mid ${key1} end.", null);
+ assertEquals("Test3 mid value1 end.", r);
+ }
+
+ public void varSubstTest4() {
+ String val = "Test4 ${incomplete ";
+ try {
+ OptionConverter.substVars(val, null);
+ } catch (IllegalArgumentException e) {
+ String errorMsg = e.getMessage();
+ // System.out.println('['+errorMsg+']');
+ assertEquals('"' + val + "\" has no closing brace. Opening brace at position 6.", errorMsg);
+ }
+ }
+
+ public void varSubstTest5() {
+ Properties props = new Properties();
+ props.put("p1", "x1");
+ props.put("p2", "${p1}");
+ String res = OptionConverter.substVars("${p2}", props);
+ System.out.println("Result is [" + res + "].");
+ assertEquals(res, "x1");
+ }
+
+ /**
+ * Tests configuring Log4J from an InputStream.
+ *
+ * @since 1.2.17
+ */
+ public void testInputStream() throws IOException {
+ File file = new File("src/test/resources/log4j1-1.2.17/input/filter1.properties");
+ assertTrue(file.exists());
+ try (FileInputStream inputStream = new FileInputStream(file)) {
+ OptionConverter.selectAndConfigure(inputStream, null, LogManager.getLoggerRepository());
+ }
+ new PropertyConfiguratorTest().validateNested();
+ }
+
+ public void toLevelTest1() {
+ String val = "INFO";
+ Level p = OptionConverter.toLevel(val, null);
+ assertEquals(p, Level.INFO);
+ }
+
+ public void toLevelTest2() {
+ String val = "INFO#org.apache.log4j.xml.XLevel";
+ Level p = OptionConverter.toLevel(val, null);
+ assertEquals(p, Level.INFO);
+ }
+
+ public void toLevelTest3() {
+ String val = "TRACE#org.apache.log4j.xml.XLevel";
+ Level p = OptionConverter.toLevel(val, null);
+ assertEquals(p, XLevel.TRACE);
+ }
+
+ public void toLevelTest4() {
+ String val = "TR#org.apache.log4j.xml.XLevel";
+ Level p = OptionConverter.toLevel(val, null);
+ assertEquals(p, null);
+ }
+
+ public void toLevelTest5() {
+ String val = "INFO#org.apache.log4j.xml.TOTO";
+ Level p = OptionConverter.toLevel(val, null);
+ assertEquals(p, null);
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite();
+ suite.addTest(new OptionConverterTestCase("varSubstTest5"));
+ suite.addTest(new OptionConverterTestCase("varSubstTest1"));
+ suite.addTest(new OptionConverterTestCase("varSubstTest2"));
+ suite.addTest(new OptionConverterTestCase("varSubstTest3"));
+ suite.addTest(new OptionConverterTestCase("varSubstTest4"));
+
+ suite.addTest(new OptionConverterTestCase("testInputStream"));
+
+ suite.addTest(new OptionConverterTestCase("toLevelTest1"));
+ suite.addTest(new OptionConverterTestCase("toLevelTest2"));
+ suite.addTest(new OptionConverterTestCase("toLevelTest3"));
+ suite.addTest(new OptionConverterTestCase("toLevelTest4"));
+ suite.addTest(new OptionConverterTestCase("toLevelTest5"));
+ return suite;
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/PatternParserTestCase.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/PatternParserTestCase.java
new file mode 100644
index 00000000000..e91cc0f3588
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/PatternParserTestCase.java
@@ -0,0 +1,131 @@
+/*
+ * 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.FileAppender;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.MDC;
+import org.apache.log4j.PatternLayout;
+import org.apache.log4j.util.Compare;
+import org.junit.Ignore;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Test case for helpers/PatternParser.java. Tests the various conversion patterns supported by PatternParser. This test
+ * class tests PatternParser via the PatternLayout class which uses it.
+ */
+@Ignore("WIP")
+public class PatternParserTestCase extends TestCase {
+
+ static String OUTPUT_FILE = "target/PatternParser";
+ static String WITNESS_FILE = "target/witness/PatternParser";
+
+ static String msgPattern = "%m%n";
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite();
+ suite.addTest(new PatternParserTestCase("mdcPattern"));
+ return suite;
+ }
+ Logger root;
+
+ Logger logger;
+
+ public PatternParserTestCase(String name) {
+ super(name);
+ }
+
+ /**
+ * Test case for MDC conversion pattern.
+ */
+ public void mdcPattern() throws Exception {
+
+ String mdcMsgPattern1 = "%m : %X%n";
+ String mdcMsgPattern2 = "%m : %X{key1}%n";
+ String mdcMsgPattern3 = "%m : %X{key2}%n";
+ String mdcMsgPattern4 = "%m : %X{key3}%n";
+ String mdcMsgPattern5 = "%m : %X{key1},%X{key2},%X{key3}%n";
+
+ // set up appender
+ PatternLayout layout = new PatternLayout(msgPattern);
+ Appender appender = new FileAppender(layout, OUTPUT_FILE + "_mdc", false);
+
+ // set appender on root and set level to debug
+ root.addAppender(appender);
+ root.setLevel(Level.DEBUG);
+
+ // output starting message
+ root.debug("starting mdc pattern test");
+
+ layout.setConversionPattern(mdcMsgPattern1);
+ root.debug("empty mdc, no key specified in pattern");
+
+ layout.setConversionPattern(mdcMsgPattern2);
+ root.debug("empty mdc, key1 in pattern");
+
+ layout.setConversionPattern(mdcMsgPattern3);
+ root.debug("empty mdc, key2 in pattern");
+
+ layout.setConversionPattern(mdcMsgPattern4);
+ root.debug("empty mdc, key3 in pattern");
+
+ layout.setConversionPattern(mdcMsgPattern5);
+ root.debug("empty mdc, key1, key2, and key3 in pattern");
+
+ MDC.put("key1", "value1");
+ MDC.put("key2", "value2");
+
+ layout.setConversionPattern(mdcMsgPattern1);
+ root.debug("filled mdc, no key specified in pattern");
+
+ layout.setConversionPattern(mdcMsgPattern2);
+ root.debug("filled mdc, key1 in pattern");
+
+ layout.setConversionPattern(mdcMsgPattern3);
+ root.debug("filled mdc, key2 in pattern");
+
+ layout.setConversionPattern(mdcMsgPattern4);
+ root.debug("filled mdc, key3 in pattern");
+
+ layout.setConversionPattern(mdcMsgPattern5);
+ root.debug("filled mdc, key1, key2, and key3 in pattern");
+
+ MDC.remove("key1");
+ MDC.remove("key2");
+
+ layout.setConversionPattern(msgPattern);
+ root.debug("finished mdc pattern test");
+
+ assertTrue(Compare.compare(OUTPUT_FILE + "_mdc", WITNESS_FILE + "_mdc"));
+ }
+
+ public void setUp() {
+ root = Logger.getRootLogger();
+ root.removeAllAppenders();
+ }
+
+ public void tearDown() {
+ root.getLoggerRepository().resetConfiguration();
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/UtilLoggingLevelTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/UtilLoggingLevelTest.java
new file mode 100644
index 00000000000..3b53aa668d6
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/UtilLoggingLevelTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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 static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.List;
+import java.util.stream.Stream;
+
+import org.apache.log4j.Level;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+
+/**
+ * Unit tests for UtilLoggingLevel.
+ */
+public class UtilLoggingLevelTest {
+
+ /**
+ * Test toLevel("fiNeSt").
+ */
+ public void testToLevelFINEST() {
+ assertEquals(UtilLoggingLevel.FINEST, UtilLoggingLevel.toLevel("fiNeSt"));
+ }
+
+ static Stream namesAndLevels() {
+ return UtilLoggingLevel.getAllPossibleLevels()
+ .stream()
+ .map(level -> Arguments.of(level.toString() + "#" + UtilLoggingLevel.class.getName(), level));
+ }
+
+ @ParameterizedTest
+ @MethodSource("namesAndLevels")
+ public void testOptionConverterToLevel(final String name, final UtilLoggingLevel level) {
+ assertTrue(level == OptionConverter.toLevel(name, Level.ALL), "get v1 level by name");
+ // Comparison of Log4j 2.x levels
+ assertTrue(level.getVersion2Level() == org.apache.logging.log4j.Level.getLevel(name), "get v2 level by name");
+ // Test convertLevel
+ assertTrue(level == OptionConverter.convertLevel(level.getVersion2Level()), "convert level v2 -> v1");
+ }
+}
+
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/layout/Log4j1SyslogLayoutTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/layout/Log4j1SyslogLayoutTest.java
new file mode 100644
index 00000000000..2a1e0b997a6
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/layout/Log4j1SyslogLayoutTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.layout;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.stream.Stream;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.StringLayout;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.core.net.Facility;
+import org.apache.logging.log4j.core.time.MutableInstant;
+import org.apache.logging.log4j.core.util.NetUtils;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+public class Log4j1SyslogLayoutTest {
+
+ private static final SimpleMessage MESSAGE = new SimpleMessage("Hello world!");
+ private static final long TIMESTAMP = LocalDateTime.of(2022, 4, 5, 12, 34, 56).atZone(ZoneId.systemDefault())
+ .toEpochSecond();
+ private static final String localhostName = NetUtils.getLocalHostname();
+
+ private static LogEvent createLogEvent() {
+ final MutableInstant instant = new MutableInstant();
+ instant.initFromEpochSecond(TIMESTAMP, 0);
+ final LogEvent event = mock(LogEvent.class);
+ when(event.getInstant()).thenReturn(instant);
+ when(event.getMessage()).thenReturn(MESSAGE);
+ when(event.getLevel()).thenReturn(Level.INFO);
+ return event;
+ }
+
+ static Stream configurations() {
+ return Stream
+ .of(Arguments.of("<30>Hello world!", Facility.DAEMON, false, false),
+ Arguments.of("<30>Apr 5 12:34:56 %s Hello world!", Facility.DAEMON, true, false),
+ Arguments.of("<30>daemon:Hello world!", Facility.DAEMON, false, true),
+ Arguments.of("<30>Apr 5 12:34:56 %s daemon:Hello world!", Facility.DAEMON, true, true))
+ .map(args -> {
+ final Object[] objs = args.get();
+ objs[0] = String.format((String) objs[0], localhostName);
+ return Arguments.of(objs);
+ });
+ }
+
+ @ParameterizedTest
+ @MethodSource("configurations")
+ public void testSimpleLayout(String expected, Facility facility, boolean header, boolean facilityPrinting) {
+ final LogEvent logEvent = createLogEvent();
+ StringLayout appenderLayout = Log4j1SyslogLayout.newBuilder()
+ .setFacility(facility)
+ .setHeader(header)
+ .setFacilityPrinting(facilityPrinting)
+ .build();
+ assertEquals(expected, appenderLayout.toSerializable(logEvent));
+ final StringLayout messageLayout = PatternLayout.newBuilder()
+ .setPattern("%m")
+ .build();
+ appenderLayout = Log4j1SyslogLayout.newBuilder()
+ .setFacility(facility)
+ .setHeader(header)
+ .setFacilityPrinting(facilityPrinting)
+ .setMessageLayout(messageLayout)
+ .build();
+ assertEquals(expected, appenderLayout.toSerializable(logEvent));
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/layout/Log4j1XmlLayoutTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/layout/Log4j1XmlLayoutTest.java
index 395cf3b3e98..1c2cf7130b6 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/layout/Log4j1XmlLayoutTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/layout/Log4j1XmlLayoutTest.java
@@ -21,12 +21,17 @@
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.impl.ContextDataFactory;
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.test.junit.ThreadContextRule;
import org.apache.logging.log4j.message.SimpleMessage;
import org.apache.logging.log4j.util.StringMap;
+import org.junit.Rule;
import org.junit.Test;
public class Log4j1XmlLayoutTest {
+ @Rule
+ public ThreadContextRule threadContextRule = new ThreadContextRule();
+
@Test
public void testWithoutThrown() {
final Log4j1XmlLayout layout = Log4j1XmlLayout.createLayout(false, true);
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/FormattingInfoTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/FormattingInfoTest.java
new file mode 100644
index 00000000000..d4a396dc924
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/FormattingInfoTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.pattern;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for FormattingInfo.
+ *
+ * @author Curt Arnold
+ *
+ */
+public class FormattingInfoTest extends TestCase {
+ /**
+ * Create a new instance.
+ *
+ * @param name test name
+ */
+ public FormattingInfoTest(final String name) {
+ super(name);
+ }
+
+ /**
+ * Check constructor
+ *
+ */
+ public void testConstructor() {
+ FormattingInfo field = new FormattingInfo(true, 3, 6);
+ assertNotNull(field);
+ assertEquals(3, field.getMinLength());
+ assertEquals(6, field.getMaxLength());
+ assertEquals(true, field.isLeftAligned());
+ }
+
+ /**
+ * Check that getDefault does not return null.
+ *
+ */
+ public void testGetDefault() {
+ FormattingInfo field = FormattingInfo.getDefault();
+ assertNotNull(field);
+ assertEquals(0, field.getMinLength());
+ assertEquals(Integer.MAX_VALUE, field.getMaxLength());
+ assertEquals(false, field.isLeftAligned());
+ }
+
+ /**
+ * Add padding to left since field is not minimum width.
+ */
+ public void testPadLeft() {
+ StringBuffer buf = new StringBuffer("foobar");
+ FormattingInfo field = new FormattingInfo(false, 5, 10);
+ field.format(2, buf);
+ assertEquals("fo obar", buf.toString());
+ }
+
+ /**
+ * Add padding to right since field is not minimum width.
+ */
+ public void testPadRight() {
+ StringBuffer buf = new StringBuffer("foobar");
+ FormattingInfo field = new FormattingInfo(true, 5, 10);
+ field.format(2, buf);
+ assertEquals("foobar ", buf.toString());
+ }
+
+ /**
+ * Field exceeds maximum width
+ */
+ public void testTruncate() {
+ StringBuffer buf = new StringBuffer("foobar");
+ FormattingInfo field = new FormattingInfo(true, 0, 3);
+ field.format(2, buf);
+ assertEquals("fobar", buf.toString());
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/Log4j1LevelPatternConverterTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/Log4j1LevelPatternConverterTest.java
new file mode 100644
index 00000000000..11150b0113b
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/Log4j1LevelPatternConverterTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.pattern;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.apache.log4j.Level;
+import org.apache.logging.log4j.core.LogEvent;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+public class Log4j1LevelPatternConverterTest {
+
+ /**
+ * Tests if the converter returns the Log4j 1.x {@code toString()} value of
+ * custom Log4j 1.x levels.
+ *
+ * @param level a Log4j 1.x level
+ */
+ @ParameterizedTest
+ @MethodSource("org.apache.log4j.helpers.UtilLoggingLevel#getAllPossibleLevels")
+ public void testUtilLoggingLevels(final Level level) {
+ final Log4j1LevelPatternConverter converter = Log4j1LevelPatternConverter.newInstance(null);
+ final LogEvent logEvent = mock(LogEvent.class);
+ when(logEvent.getLevel()).thenReturn(level.getVersion2Level());
+ final StringBuilder result = new StringBuilder();
+ converter.format(logEvent, result);
+ assertEquals(level.toString(), result.toString());
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/Log4j1NdcPatternConverterTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/Log4j1NdcPatternConverterTest.java
index 2f0b80f78ab..170e7bf68f3 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/Log4j1NdcPatternConverterTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/Log4j1NdcPatternConverterTest.java
@@ -16,17 +16,17 @@
*/
package org.apache.log4j.pattern;
+import static org.junit.Assert.assertEquals;
+
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
-import org.apache.logging.log4j.junit.ThreadContextStackRule;
+import org.apache.logging.log4j.test.junit.ThreadContextStackRule;
import org.apache.logging.log4j.message.SimpleMessage;
import org.junit.Rule;
import org.junit.Test;
-import static org.junit.Assert.assertEquals;
-
public class Log4j1NdcPatternConverterTest {
@Rule
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/NameAbbreviatorTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/NameAbbreviatorTest.java
new file mode 100644
index 00000000000..5494a5974bc
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/NameAbbreviatorTest.java
@@ -0,0 +1,329 @@
+/*
+ * 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.pattern;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for NameAbbrevator.
+ *
+ */
+public class NameAbbreviatorTest extends TestCase {
+ /**
+ * Create a new instance.
+ *
+ * @param name test name
+ */
+ public NameAbbreviatorTest(final String name) {
+ super(name);
+ }
+
+ /**
+ * Check that getAbbreviator(" ") returns default abbreviator.
+ *
+ */
+ public void testBlank() {
+ NameAbbreviator abbrev = NameAbbreviator.getAbbreviator(" ");
+ NameAbbreviator defaultAbbrev = NameAbbreviator.getDefaultAbbreviator();
+ assertTrue(abbrev == defaultAbbrev);
+ }
+
+ /**
+ * Check that blanks are trimmed in evaluating abbreviation pattern.
+ */
+ public void testBlankOne() {
+ NameAbbreviator abbrev = NameAbbreviator.getAbbreviator(" 1 ");
+ StringBuffer buf = new StringBuffer("DEBUG - ");
+ int fieldStart = buf.length();
+ buf.append("org.example.foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - ", buf.toString());
+ }
+
+ /**
+ * Check that getDefaultAbbreviator does not return null.
+ *
+ */
+ public void testGetDefault() {
+ NameAbbreviator abbrev = NameAbbreviator.getDefaultAbbreviator();
+ assertNotNull(abbrev);
+ }
+
+ /**
+ * Check that getAbbreviator("-1").abbreviate() drops first name element.
+ *
+ */
+ public void testMinusOne() {
+ NameAbbreviator abbrev = NameAbbreviator.getAbbreviator("-1");
+ StringBuffer buf = new StringBuffer("DEBUG - ");
+ int fieldStart = buf.length();
+ buf.append("org.example.foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - example.foo.bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - ", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append(".");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - ", buf.toString());
+
+ }
+
+ /**
+ * Check that getAbbreviator("1.*.2").abbreviate drops all but the first character from the first element, uses all of
+ * the second element and drops all but the first two characters of the rest of the non-final elements.
+ *
+ */
+ public void testMulti() {
+ NameAbbreviator abbrev = NameAbbreviator.getAbbreviator("1.*.2");
+ StringBuffer buf = new StringBuffer("DEBUG - ");
+ int fieldStart = buf.length();
+ buf.append("org.example.foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - o.example.fo.bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("org.example.foo.");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - o.example.fo.", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - f.bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - ", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append(".");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - .", buf.toString());
+ }
+
+ /**
+ * Check that getAbbreviator("1").abbreviate() drops all but the final name element.
+ *
+ */
+ public void testOne() {
+ NameAbbreviator abbrev = NameAbbreviator.getAbbreviator("1");
+ StringBuffer buf = new StringBuffer("DEBUG - ");
+ int fieldStart = buf.length();
+ buf.append("org.example.foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - ", buf.toString());
+ }
+
+ /**
+ * Check that getAbbreviator("1.").abbreviate abbreviates non-final elements to one character.
+ *
+ */
+ public void testOneDot() {
+ NameAbbreviator abbrev = NameAbbreviator.getAbbreviator("1.");
+ StringBuffer buf = new StringBuffer("DEBUG - ");
+ int fieldStart = buf.length();
+ buf.append("org.example.foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - o.e.f.bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("org.example.foo.");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - o.e.f.", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - f.bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - ", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append(".");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - .", buf.toString());
+ }
+
+ /**
+ * Check that getAbbreviator("1~.").abbreviate abbreviates non-final elements to one character and a tilde.
+ *
+ */
+ public void testOneTildeDot() {
+ NameAbbreviator abbrev = NameAbbreviator.getAbbreviator("1~.");
+ StringBuffer buf = new StringBuffer("DEBUG - ");
+ int fieldStart = buf.length();
+ buf.append("org.example.foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - o~.e~.f~.bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("org.example.foo.");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - o~.e~.f~.", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - f~.bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - ", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append(".");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - .", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("o.e.f.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - o.e.f.bar", buf.toString());
+ }
+
+ /**
+ * Check that getAbbreviator("2").abbreviate drops all but the last two elements.
+ *
+ */
+ public void testTwo() {
+ NameAbbreviator abbrev = NameAbbreviator.getAbbreviator("2");
+ StringBuffer buf = new StringBuffer("DEBUG - ");
+ int fieldStart = buf.length();
+ buf.append("org.example.foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - foo.bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - foo.bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - bar", buf.toString());
+ }
+
+ /**
+ * Check that "0" drops all name content.
+ *
+ */
+ public void testZero() {
+ NameAbbreviator abbrev = NameAbbreviator.getAbbreviator("0");
+ StringBuffer buf = new StringBuffer("DEBUG - ");
+ int fieldStart = buf.length();
+ buf.append("org.example.foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - ", buf.toString());
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/spi/LocationInfoTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/spi/LocationInfoTest.java
new file mode 100644
index 00000000000..a6daefcaa79
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/spi/LocationInfoTest.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.log4j.spi;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for LocationInfo.
+ */
+public class LocationInfoTest extends TestCase {
+
+ /**
+ * Tests four parameter constructor.
+ */
+ public void testFourParamConstructor() {
+ final String className = LocationInfoTest.class.getName();
+ final String methodName = "testFourParamConstructor";
+ final String fileName = "LocationInfoTest.java";
+ final String lineNumber = "41";
+ LocationInfo li = new LocationInfo(fileName, className, methodName, lineNumber);
+ assertEquals(className, li.getClassName());
+ assertEquals(methodName, li.getMethodName());
+ assertEquals(fileName, li.getFileName());
+ assertEquals(lineNumber, li.getLineNumber());
+ assertEquals(className + "." + methodName + "(" + fileName + ":" + lineNumber + ")", li.fullInfo);
+ }
+
+ /**
+ * Class with name that is a substring of its caller.
+ */
+ private static class NameSubstring {
+ /**
+ * Construct a LocationInfo. Location should be immediate caller of this method.
+ *
+ * @return location info.
+ */
+ public static LocationInfo getInfo() {
+ return new LocationInfo(new Throwable(), NameSubstring.class.getName());
+
+ }
+ }
+
+ /**
+ * Class whose name is contains the name of the class that obtains the LocationInfo.
+ */
+ private static class NameSubstringCaller {
+ /**
+ * Construct a locationInfo. Location should be this location.
+ *
+ * @return location info.
+ */
+ public static LocationInfo getInfo() {
+ return NameSubstring.getInfo();
+ }
+
+ }
+
+ /**
+ * Tests creation of location info when the logger class name is a substring of one of the other classes in the stack
+ * trace. See bug 44888.
+ */
+ public void testLocationInfo() {
+ LocationInfo li = NameSubstringCaller.getInfo();
+ assertEquals(NameSubstringCaller.class.getName(), li.getClassName());
+ assertEquals("getInfo", li.getMethodName());
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/spi/ThrowableInformationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/spi/ThrowableInformationTest.java
new file mode 100644
index 00000000000..e032bd78e7a
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/spi/ThrowableInformationTest.java
@@ -0,0 +1,338 @@
+/*
+ * 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.spi;
+
+import java.io.PrintWriter;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests {@link ThrowableInformation}.
+ */
+public class ThrowableInformationTest extends TestCase {
+
+ /**
+ * Create ThrowableInformationTest.
+ *
+ * @param name test name.
+ */
+ public ThrowableInformationTest(final String name) {
+ super(name);
+ }
+
+ /**
+ * Custom throwable that only calls methods overridden by VectorWriter in log4j 1.2.14 and earlier.
+ */
+ private static final class OverriddenThrowable extends Throwable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create new instance.
+ */
+ public OverriddenThrowable() {
+ }
+
+ /**
+ * Print stack trace.
+ *
+ * @param s print writer.
+ */
+ public void printStackTrace(final PrintWriter s) {
+ s.print((Object) "print(Object)");
+ s.print("print(char[])".toCharArray());
+ s.print("print(String)");
+ s.println((Object) "println(Object)");
+ s.println("println(char[])".toCharArray());
+ s.println("println(String)");
+ s.write("write(char[])".toCharArray());
+ s.write("write(char[], int, int)".toCharArray(), 2, 8);
+ s.write("write(String, int, int)", 2, 8);
+ }
+ }
+
+ /**
+ * Test capturing stack trace from a throwable that only uses the PrintWriter methods overridden in log4j 1.2.14 and
+ * earlier.
+ */
+ public void testOverriddenBehavior() {
+ ThrowableInformation ti = new ThrowableInformation(new OverriddenThrowable());
+ String[] rep = ti.getThrowableStrRep();
+ assertEquals(4, rep.length);
+ assertEquals("print(Object)print(char[])print(String)println(Object)", rep[0]);
+ assertEquals("println(char[])", rep[1]);
+ assertEquals("println(String)", rep[2]);
+ assertEquals("write(char[])ite(charite(Stri", rep[3]);
+ }
+
+ /**
+ * Custom throwable that calls methods not overridden by VectorWriter in log4j 1.2.14 and earlier.
+ */
+ private static final class NotOverriddenThrowable extends Throwable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create new instance.
+ */
+ public NotOverriddenThrowable() {
+ }
+
+ /**
+ * Print stack trace.
+ *
+ * @param s print writer.
+ */
+ public void printStackTrace(final PrintWriter s) {
+ s.print(true);
+ s.print('a');
+ s.print(1);
+ s.print(2L);
+ s.print(Float.MAX_VALUE);
+ s.print(Double.MIN_VALUE);
+ s.println(true);
+ s.println('a');
+ s.println(1);
+ s.println(2L);
+ s.println(Float.MAX_VALUE);
+ s.println(Double.MIN_VALUE);
+ s.write('C');
+ }
+ }
+
+ /**
+ * Test capturing stack trace from a throwable that uses the PrintWriter methods not overridden in log4j 1.2.14 and
+ * earlier.
+ */
+ public void testNotOverriddenBehavior() {
+ ThrowableInformation ti = new ThrowableInformation(new NotOverriddenThrowable());
+ String[] rep = ti.getThrowableStrRep();
+ assertEquals(7, rep.length);
+ StringBuffer buf = new StringBuffer(String.valueOf(true));
+ buf.append('a');
+ buf.append(String.valueOf(1));
+ buf.append(String.valueOf(2L));
+ buf.append(String.valueOf(Float.MAX_VALUE));
+ buf.append(String.valueOf(Double.MIN_VALUE));
+ buf.append(String.valueOf(true));
+ assertEquals(buf.toString(), rep[0]);
+ assertEquals("a", rep[1]);
+ assertEquals(String.valueOf(1), rep[2]);
+ assertEquals(String.valueOf(2L), rep[3]);
+ assertEquals(String.valueOf(Float.MAX_VALUE), rep[4]);
+ assertEquals(String.valueOf(Double.MIN_VALUE), rep[5]);
+ assertEquals("C", rep[6]);
+ }
+
+ /**
+ * Custom throwable that calls methods of VectorWriter with null.
+ */
+ private static final class NullThrowable extends Throwable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create new instance.
+ */
+ public NullThrowable() {
+ }
+
+ /**
+ * Print stack trace.
+ *
+ * @param s print writer.
+ */
+ public void printStackTrace(final PrintWriter s) {
+ s.print((Object) null);
+ s.print((String) null);
+ s.println((Object) null);
+ s.println((String) null);
+ }
+ }
+
+ /**
+ * Test capturing stack trace from a throwable that passes null to PrintWriter methods.
+ */
+
+ public void testNull() {
+ ThrowableInformation ti = new ThrowableInformation(new NullThrowable());
+ String[] rep = ti.getThrowableStrRep();
+ assertEquals(2, rep.length);
+ String nullStr = String.valueOf((Object) null);
+ assertEquals(nullStr + nullStr + nullStr, rep[0]);
+ assertEquals(nullStr, rep[1]);
+ }
+
+ /**
+ * Custom throwable that does nothing in printStackTrace.
+ */
+ private static final class EmptyThrowable extends Throwable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create new instance.
+ */
+ public EmptyThrowable() {
+ }
+
+ /**
+ * Print stack trace.
+ *
+ * @param s print writer.
+ */
+ public void printStackTrace(final PrintWriter s) {
+ }
+ }
+
+ /**
+ * Test capturing stack trace from a throwable that does nothing on a call to printStackTrace.
+ */
+
+ public void testEmpty() {
+ ThrowableInformation ti = new ThrowableInformation(new EmptyThrowable());
+ String[] rep = ti.getThrowableStrRep();
+ assertEquals(0, rep.length);
+ }
+
+ /**
+ * Custom throwable that emits a specified string in printStackTrace.
+ */
+ private static final class StringThrowable extends Throwable {
+ private static final long serialVersionUID = 1L;
+ /**
+ * Stack trace.
+ */
+ private final String stackTrace;
+
+ /**
+ * Create new instance.
+ *
+ * @param trace stack trace.
+ */
+ public StringThrowable(final String trace) {
+ stackTrace = trace;
+ }
+
+ /**
+ * Print stack trace.
+ *
+ * @param s print writer.
+ */
+ public void printStackTrace(final PrintWriter s) {
+ s.print(stackTrace);
+ }
+ }
+
+ /**
+ * Test capturing stack trace from throwable that just has a line feed.
+ */
+ public void testLineFeed() {
+ ThrowableInformation ti = new ThrowableInformation(new StringThrowable("\n"));
+ String[] rep = ti.getThrowableStrRep();
+ assertEquals(1, rep.length);
+ assertEquals("", rep[0]);
+ }
+
+ /**
+ * Test capturing stack trace from throwable that just has a carriage return.
+ */
+ public void testCarriageReturn() {
+ ThrowableInformation ti = new ThrowableInformation(new StringThrowable("\r"));
+ String[] rep = ti.getThrowableStrRep();
+ assertEquals(1, rep.length);
+ assertEquals("", rep[0]);
+ }
+
+ /**
+ * Test parsing of line breaks.
+ */
+ public void testParsing() {
+ ThrowableInformation ti = new ThrowableInformation(new StringThrowable("Line1\rLine2\nLine3\r\nLine4\n\rLine6"));
+ String[] rep = ti.getThrowableStrRep();
+ assertEquals(6, rep.length);
+ assertEquals("Line1", rep[0]);
+ assertEquals("Line2", rep[1]);
+ assertEquals("Line3", rep[2]);
+ assertEquals("Line4", rep[3]);
+ assertEquals("", rep[4]);
+ assertEquals("Line6", rep[5]);
+ }
+
+ /**
+ * Test capturing stack trace from throwable that a line feed followed by blank.
+ */
+ public void testLineFeedBlank() {
+ ThrowableInformation ti = new ThrowableInformation(new StringThrowable("\n "));
+ String[] rep = ti.getThrowableStrRep();
+ assertEquals(2, rep.length);
+ assertEquals("", rep[0]);
+ assertEquals(" ", rep[1]);
+ }
+
+ /**
+ * Test that getThrowable returns the throwable provided to the constructor.
+ */
+ public void testGetThrowable() {
+ Throwable t = new StringThrowable("Hello, World");
+ ThrowableInformation ti = new ThrowableInformation(t);
+ assertSame(t, ti.getThrowable());
+ }
+
+ /**
+ * Tests isolation of returned string representation from internal state of ThrowableInformation. log4j 1.2.15 and
+ * earlier did not isolate initial call. See bug 44032.
+ */
+ public void testIsolation() {
+ ThrowableInformation ti = new ThrowableInformation(new StringThrowable("Hello, World"));
+ String[] rep = ti.getThrowableStrRep();
+ assertEquals("Hello, World", rep[0]);
+ rep[0] = "Bonjour, Monde";
+ String[] rep2 = ti.getThrowableStrRep();
+ assertEquals("Hello, World", rep2[0]);
+ }
+
+ /**
+ * Custom throwable that throws a runtime exception when printStackTrace is called.
+ */
+ private static final class NastyThrowable extends Throwable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create new instance.
+ */
+ public NastyThrowable() {
+ }
+
+ /**
+ * Print stack trace.
+ *
+ * @param s print writer.
+ */
+ public void printStackTrace(final PrintWriter s) {
+ s.print("NastyException");
+ throw new RuntimeException("Intentional exception");
+ }
+ }
+
+ /**
+ * Tests that a failure in printStackTrace does not percolate out of getThrowableStrRep().
+ *
+ */
+ public void testNastyException() {
+ ThrowableInformation ti = new ThrowableInformation(new NastyThrowable());
+ String[] rep = ti.getThrowableStrRep();
+ assertEquals("NastyException", rep[0]);
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/AbsoluteDateAndTimeFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/AbsoluteDateAndTimeFilter.java
new file mode 100644
index 00000000000..73f3c2bfcdb
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/AbsoluteDateAndTimeFilter.java
@@ -0,0 +1,36 @@
+/*
+ * 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.util;
+
+import org.apache.oro.text.perl.Perl5Util;
+
+public class AbsoluteDateAndTimeFilter implements Filter {
+
+ Perl5Util util = new Perl5Util();
+
+ @Override
+ public String filter(final String in) {
+ final String pat = "/" + Filter.ABSOLUTE_DATE_AND_TIME_PAT + "/";
+
+ if (util.match(pat, in)) {
+ return util.substitute("s/" + Filter.ABSOLUTE_DATE_AND_TIME_PAT + "//", in);
+ } else {
+ return in;
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/AbsoluteTimeFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/AbsoluteTimeFilter.java
new file mode 100644
index 00000000000..4528e8abbaf
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/AbsoluteTimeFilter.java
@@ -0,0 +1,35 @@
+/*
+ * 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.util;
+
+import org.apache.oro.text.perl.Perl5Util;
+
+public class AbsoluteTimeFilter implements Filter {
+
+ Perl5Util util = new Perl5Util();
+
+ @Override
+ public String filter(final String in) {
+ final String pat = "/" + Filter.ABSOLUTE_TIME_PAT + "/";
+
+ if (util.match(pat, in)) {
+ return util.substitute("s/" + Filter.ABSOLUTE_TIME_PAT + "//", in);
+ }
+ return in;
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/Compare.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/Compare.java
new file mode 100644
index 00000000000..b65b9e9ce36
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/Compare.java
@@ -0,0 +1,150 @@
+/*
+ * 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.util;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+public class Compare {
+
+ static final int B1_NULL = -1;
+ static final int B2_NULL = -2;
+
+ public static boolean compare(final Class testClass, final String file1, final String file2) throws IOException {
+ try (final BufferedReader in1 = new BufferedReader(new FileReader(file1));
+ final BufferedReader in2 = new BufferedReader(new InputStreamReader(open(testClass, file2)))) {
+ return compare(testClass, file1, file2, in1, in2);
+ }
+ }
+
+ public static boolean compare(final Class testClass, final String file1, final String file2, final BufferedReader in1, final BufferedReader in2)
+ throws IOException {
+
+ String s1;
+ int lineCounter = 0;
+
+ while ((s1 = in1.readLine()) != null) {
+ lineCounter++;
+
+ final String s2 = in2.readLine();
+
+ if (!s1.equals(s2)) {
+ System.out.println("Files [" + file1 + "] and [" + file2 + "] differ on line " + lineCounter);
+ System.out.println("One reads: [" + s1 + "].");
+ System.out.println("Other reads:[" + s2 + "].");
+ outputFile(testClass, file1);
+ outputFile(testClass, file2);
+
+ return false;
+ }
+ }
+
+ // the second file is longer
+ if (in2.read() != -1) {
+ System.out.println("File [" + file2 + "] longer than file [" + file1 + "].");
+ outputFile(testClass, file1);
+ outputFile(testClass, file2);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ static public boolean compare(final String file1, final String file2) throws FileNotFoundException, IOException {
+ try (final BufferedReader in1 = new BufferedReader(new FileReader(file1)); final BufferedReader in2 = new BufferedReader(new FileReader(file2))) {
+
+ String s1;
+ int lineCounter = 0;
+ while ((s1 = in1.readLine()) != null) {
+ lineCounter++;
+ final String s2 = in2.readLine();
+ if (!s1.equals(s2)) {
+ System.out.println("Files [" + file1 + "] and [" + file2 + "] differ on line " + lineCounter);
+ System.out.println("One reads: [" + s1 + "].");
+ System.out.println("Other reads:[" + s2 + "].");
+ return false;
+ }
+ }
+
+ // the second file is longer
+ if (in2.read() != -1) {
+ System.out.println("File [" + file2 + "] longer than file [" + file1 + "].");
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ private static final InputStream open(final Class testClass, final String fileName) throws IOException {
+ String resourceName = fileName;
+ if (fileName.startsWith("witness/")) {
+ resourceName = fileName.substring(fileName.lastIndexOf('/') + 1);
+ }
+ InputStream is = testClass.getResourceAsStream(resourceName);
+ if (is == null) {
+ final File file = new File(fileName);
+ if (file.exists()) {
+ is = new FileInputStream(file);
+ } else {
+ throw new FileNotFoundException("Resource " + resourceName + " not found");
+ }
+ }
+ return is;
+ }
+
+ /**
+ *
+ * Prints file on the console.
+ *
+ */
+ private static void outputFile(final Class testClass, final String file) throws IOException {
+ try (final InputStream is = open(testClass, file); final BufferedReader in1 = new BufferedReader(new InputStreamReader(is))) {
+
+ String s1;
+ int lineCounter = 0;
+ System.out.println("--------------------------------");
+ System.out.println("Contents of " + file + ":");
+
+ while ((s1 = in1.readLine()) != null) {
+ lineCounter++;
+ System.out.print(lineCounter);
+
+ if (lineCounter < 10) {
+ System.out.print(" : ");
+ } else if (lineCounter < 100) {
+ System.out.print(" : ");
+ } else if (lineCounter < 1000) {
+ System.out.print(" : ");
+ } else {
+ System.out.print(": ");
+ }
+
+ System.out.println(s1);
+ }
+ }
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/ControlFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/ControlFilter.java
new file mode 100644
index 00000000000..325d460e548
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/ControlFilter.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.util;
+
+import java.util.Arrays;
+
+import org.apache.oro.text.perl.Perl5Util;
+
+public class ControlFilter implements Filter {
+
+ Perl5Util util = new Perl5Util();
+
+ String[] allowedPatterns;
+
+ public ControlFilter(final String[] allowedPatterns) {
+ this.allowedPatterns = allowedPatterns;
+ }
+
+ @Override
+ public String filter(final String in) throws UnexpectedFormatException {
+ final int len = allowedPatterns.length;
+ for (int i = 0; i < len; i++) {
+ // System.out.println("["+allowedPatterns[i]+"]");
+ if (util.match("/" + allowedPatterns[i] + "/", in)) {
+ // System.out.println("["+in+"] matched ["+allowedPatterns[i]);
+ return in;
+ }
+ }
+
+ throw new UnexpectedFormatException("[" + in + "] allowedPatterns = " + Arrays.toString(allowedPatterns));
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/EnhancedJunitTestRunnerFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/EnhancedJunitTestRunnerFilter.java
new file mode 100644
index 00000000000..172d317464c
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/EnhancedJunitTestRunnerFilter.java
@@ -0,0 +1,58 @@
+/*
+ * 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.util;
+
+import org.apache.oro.text.perl.Perl5Util;
+
+public class EnhancedJunitTestRunnerFilter implements Filter {
+ private static final String[] PATTERNS = {"at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner", "at org.apache.tools.ant",
+ "at junit.textui.TestRunner", "at com.intellij.rt.execution.junit", "at java.lang.reflect.Method.invoke", "at org.apache.maven.", "at org.codehaus.",
+ "at org.junit.internal.runners.", "at junit.framework.JUnit4TestAdapter"};
+
+ private final Perl5Util util = new Perl5Util();
+
+ public EnhancedJunitTestRunnerFilter() {
+ }
+
+ /**
+ * Filter out stack trace lines coming from the various JUnit TestRunners.
+ */
+ @Override
+ public String filter(final String in) {
+ if (in == null) {
+ return null;
+ }
+
+ //
+ // restore the one instance of Method.invoke that we actually want
+ //
+ if (in.indexOf("at junit.framework.TestCase.runTest") != -1) {
+ return "\tat java.lang.reflect.Method.invoke(X)\n\t" + in.trim();
+ }
+
+ for (final String element : PATTERNS) {
+ if (in.indexOf(element) != -1) {
+ return null;
+ }
+ }
+ if (util.match("/\\sat /", in)) {
+ return "\t" + in.trim();
+ }
+ return in;
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/EnhancedLineNumberFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/EnhancedLineNumberFilter.java
new file mode 100644
index 00000000000..b112a03b5c1
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/EnhancedLineNumberFilter.java
@@ -0,0 +1,42 @@
+/*
+ * 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.util;
+
+import java.util.regex.Pattern;
+
+public class EnhancedLineNumberFilter implements Filter {
+ private final Pattern linePattern;
+ private final Pattern nativePattern;
+
+ public EnhancedLineNumberFilter() {
+ linePattern = Pattern.compile("\\(.*:\\d{1,4}\\)");
+ nativePattern = Pattern.compile("\\(Native Method\\)");
+ }
+
+ @Override
+ public String filter(final String in) {
+
+ if (linePattern.matcher(in).find()) {
+ return linePattern.matcher(in).replaceAll("(X)");
+ } else if (nativePattern.matcher(in).find()) {
+ return nativePattern.matcher(in).replaceAll("(X)");
+ } else {
+ return in;
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/Filter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/Filter.java
new file mode 100644
index 00000000000..16bb43d5d83
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/Filter.java
@@ -0,0 +1,35 @@
+/*
+ * 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.util;
+
+public interface Filter {
+
+ final String BASIC_PAT = "\\[main\\] (FATAL|ERROR|WARN|INFO|DEBUG)";
+ final String ISO8601_PAT = "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2},\\d{3}";
+
+ // 06 avr. 2002 18:36:32,036
+ // 18 fevr. 2002 20:05:36,222
+ static public final String ABSOLUTE_DATE_AND_TIME_PAT = "^\\d{1,2} .{2,6}\\.? 2\\d{3} \\d{2}:\\d{2}:\\d{2},\\d{3}";
+
+ // 18:54:19,201
+ static public final String ABSOLUTE_TIME_PAT = "^\\d{2}:\\d{2}:\\d{2},\\d{3}";
+
+ static public final String RELATIVE_TIME_PAT = "^\\d{1,10}";
+
+ String filter(String in) throws UnexpectedFormatException;
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/ISO8601Filter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/ISO8601Filter.java
new file mode 100644
index 00000000000..47232ad4e12
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/ISO8601Filter.java
@@ -0,0 +1,35 @@
+/*
+ * 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.util;
+
+import org.apache.oro.text.perl.Perl5Util;
+
+public class ISO8601Filter implements Filter {
+
+ Perl5Util util = new Perl5Util();
+
+ @Override
+ public String filter(final String in) {
+ final String pat = "/" + ISO8601_PAT + "/";
+
+ if (util.match(pat, in)) {
+ return util.substitute("s/" + ISO8601_PAT + "//", in);
+ }
+ return in;
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/JunitTestRunnerFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/JunitTestRunnerFilter.java
new file mode 100644
index 00000000000..175224a96ba
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/JunitTestRunnerFilter.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.util;
+
+import org.apache.oro.text.perl.Perl5Util;
+
+public class JunitTestRunnerFilter implements Filter {
+ Perl5Util util = new Perl5Util();
+
+ /**
+ * Filter out stack trace lines coming from the various JUnit TestRunners.
+ */
+ @Override
+ public String filter(final String in) {
+ if (in == null) {
+ return null;
+ }
+
+ if (util.match("/at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner/", in)) {
+ return null;
+ } else if (util.match("/at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner/", in)) {
+ return null;
+ } else if (util.match("/at com.intellij/", in)) {
+ return null;
+ } else if (in.indexOf("at junit.") >= 0 && in.indexOf("ui.TestRunner") >= 0) {
+ return null;
+ } else if (in.indexOf("org.apache.maven") >= 0) {
+ return null;
+ } else if (in.indexOf("junit.internal") >= 0) {
+ return null;
+ } else if (in.indexOf("JUnit4TestAdapter") >= 0) {
+ return null;
+ } else if (util.match("/\\sat /", in)) {
+ return "\t" + in.trim();
+ } else {
+ return in;
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/LineNumberFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/LineNumberFilter.java
new file mode 100644
index 00000000000..45c65786a3f
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/LineNumberFilter.java
@@ -0,0 +1,36 @@
+/*
+ * 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.util;
+
+import org.apache.oro.text.perl.Perl5Util;
+
+public class LineNumberFilter implements Filter {
+
+ Perl5Util util = new Perl5Util();
+
+ @Override
+ public String filter(final String in) {
+ if (util.match("/\\(.*:\\d{1,4}\\)/", in)) {
+ return util.substitute("s/:\\d{1,4}\\)/:XXX)/", in);
+ }
+ if (in.indexOf(", Compiled Code") >= 0) {
+ return util.substitute("s/, Compiled Code/:XXX/", in);
+ }
+ return in;
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/RelativeTimeFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/RelativeTimeFilter.java
new file mode 100644
index 00000000000..a15ffb10c06
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/RelativeTimeFilter.java
@@ -0,0 +1,36 @@
+/*
+ * 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.util;
+
+import org.apache.oro.text.perl.Perl5Util;
+
+public class RelativeTimeFilter implements Filter {
+
+ Perl5Util util = new Perl5Util();
+
+ @Override
+ public String filter(final String in) {
+ final String pat = "/" + Filter.RELATIVE_TIME_PAT + "/";
+
+ if (util.match(pat, in)) {
+ // System.out.println("Removing relative time from line ["+in+"]");
+ return util.substitute("s/" + Filter.RELATIVE_TIME_PAT + "//", in);
+ }
+ return in;
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/SerializationTestHelper.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/SerializationTestHelper.java
index 057646370e3..a3584c2def7 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/util/SerializationTestHelper.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/SerializationTestHelper.java
@@ -30,65 +30,20 @@
import org.apache.commons.io.FileUtils;
-
/**
* Utiities for serialization tests.
*/
public class SerializationTestHelper {
/**
- * Private constructor.
- */
- private SerializationTestHelper() {
- }
-
- /**
- * Creates a clone by serializing object and
- * deserializing byte stream.
- *
- * @param obj object to serialize and deserialize.
- * @return clone
- * @throws IOException on IO error.
- * @throws ClassNotFoundException if class not found.
- */
- public static Object serializeClone(final Object obj)
- throws IOException, ClassNotFoundException {
- final ByteArrayOutputStream memOut = new ByteArrayOutputStream();
- try (final ObjectOutputStream objOut = new ObjectOutputStream(memOut)) {
- objOut.writeObject(obj);
- }
-
- final ByteArrayInputStream src = new ByteArrayInputStream(memOut.toByteArray());
- final ObjectInputStream objIs = new ObjectInputStream(src);
-
- return objIs.readObject();
- }
-
- /**
- * Deserializes a specified file.
- *
- * @param witness serialization file, may not be null.
- * @return deserialized object.
- * @throws Exception thrown on IO or deserialization exception.
- */
- public static Object deserializeStream(final String witness) throws Exception {
- try (final ObjectInputStream objIs = new ObjectInputStream(new FileInputStream(witness))) {
- return objIs.readObject();
- }
- }
-
- /**
- * Checks the serialization of an object against an file
- * containing the expected serialization.
+ * Checks the serialization of an object against an file containing the expected serialization.
*
- * @param witness name of file containing expected serialization.
- * @param obj object to be serialized.
- * @param skip positions in serialized stream that should not be compared.
+ * @param witness name of file containing expected serialization.
+ * @param obj object to be serialized.
+ * @param skip positions in serialized stream that should not be compared.
* @param endCompare position to stop comparison.
* @throws Exception thrown on IO or serialization exception.
*/
- public static void assertSerializationEquals(
- final String witness, final Object obj, final int[] skip,
- final int endCompare) throws Exception {
+ public static void assertSerializationEquals(final String witness, final Object obj, final int[] skip, final int endCompare) throws Exception {
final ByteArrayOutputStream memOut = new ByteArrayOutputStream();
try (final ObjectOutputStream objOut = new ObjectOutputStream(memOut)) {
objOut.writeObject(obj);
@@ -100,15 +55,13 @@ public static void assertSerializationEquals(
/**
* Asserts the serialized form of an object.
*
- * @param witness file name of expected serialization.
- * @param actual byte array of actual serialization.
- * @param skip positions to skip comparison.
+ * @param witness file name of expected serialization.
+ * @param actual byte array of actual serialization.
+ * @param skip positions to skip comparison.
* @param endCompare position to stop comparison.
* @throws IOException thrown on IO or serialization exception.
*/
- public static void assertStreamEquals(
- final String witness, final byte[] actual, final int[] skip,
- final int endCompare) throws IOException {
+ public static void assertStreamEquals(final String witness, final byte[] actual, final int[] skip, final int endCompare) throws IOException {
final File witnessFile = new File(witness);
if (witnessFile.exists()) {
@@ -129,20 +82,55 @@ public static void assertStreamEquals(
for (int i = 0; i < endScan; i++) {
if ((skipIndex < skip.length) && (skip[skipIndex] == i)) {
skipIndex++;
- } else {
- if (expected[i] != actual[i]) {
- assertEquals(
- "Difference at offset " + i, expected[i], actual[i]);
- }
+ } else if (expected[i] != actual[i]) {
+ assertEquals("Difference at offset " + i, expected[i], actual[i]);
}
}
} else {
//
- // if the file doesn't exist then
- // assume that we are setting up and need to write it
+ // if the file doesn't exist then
+ // assume that we are setting up and need to write it
FileUtils.writeByteArrayToFile(witnessFile, actual);
fail("Writing witness file " + witness);
}
}
-}
+ /**
+ * Deserializes a specified file.
+ *
+ * @param witness serialization file, may not be null.
+ * @return deserialized object.
+ * @throws Exception thrown on IO or deserialization exception.
+ */
+ public static Object deserializeStream(final String witness) throws Exception {
+ try (final ObjectInputStream objIs = new ObjectInputStream(new FileInputStream(witness))) {
+ return objIs.readObject();
+ }
+ }
+
+ /**
+ * Creates a clone by serializing object and deserializing byte stream.
+ *
+ * @param obj object to serialize and deserialize.
+ * @return clone
+ * @throws IOException on IO error.
+ * @throws ClassNotFoundException if class not found.
+ */
+ public static Object serializeClone(final Object obj) throws IOException, ClassNotFoundException {
+ final ByteArrayOutputStream memOut = new ByteArrayOutputStream();
+ try (final ObjectOutputStream objOut = new ObjectOutputStream(memOut)) {
+ objOut.writeObject(obj);
+ }
+
+ final ByteArrayInputStream src = new ByteArrayInputStream(memOut.toByteArray());
+ final ObjectInputStream objIs = new ObjectInputStream(src);
+
+ return objIs.readObject();
+ }
+
+ /**
+ * Private constructor.
+ */
+ private SerializationTestHelper() {
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/SunReflectFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/SunReflectFilter.java
new file mode 100644
index 00000000000..14c8e00bb64
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/SunReflectFilter.java
@@ -0,0 +1,43 @@
+/*
+ * 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.util;
+
+import org.apache.oro.text.perl.Perl5Util;
+
+/**
+ * The sun.reflect.* and java.lang.reflect.* lines are not present in all JDKs.
+ */
+public class SunReflectFilter implements Filter {
+ Perl5Util util = new Perl5Util();
+
+ @Override
+ public String filter(final String in) {
+ if ((in == null) || util.match("/at sun.reflect/", in) || (in.indexOf("at java.lang.reflect.") >= 0)) {
+ return null;
+ }
+ if (in.indexOf("Compiled Code") >= 0) {
+ if (in.indexOf("junit.framework.TestSuite") >= 0) {
+ return util.substitute("s/Compiled Code/TestSuite.java:XXX/", in);
+ }
+ }
+ if (util.match("/\\(Method.java:.*\\)/", in)) {
+ return util.substitute("s/\\(Method.java:.*\\)/(Native Method)/", in);
+ }
+ return in;
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/Transformer.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/Transformer.java
new file mode 100644
index 00000000000..79d659e332e
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/Transformer.java
@@ -0,0 +1,59 @@
+/*
+ * 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.util;
+
+import java.io.BufferedReader;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintStream;
+
+public class Transformer {
+
+ public static void transform(final String in, final String out, final Filter filter) throws IOException, UnexpectedFormatException {
+
+ String line;
+ final BufferedReader input = new BufferedReader(new FileReader(in));
+ final PrintStream output = new PrintStream(new FileOutputStream(out));
+
+ // Initialization of input and output omitted
+ while ((line = input.readLine()) != null) {
+ line = filter.filter(line);
+ output.println(line);
+ }
+ }
+
+ public static void transform(final String in, final String out, final Filter[] filters) throws IOException, UnexpectedFormatException {
+
+ String line;
+ final BufferedReader input = new BufferedReader(new FileReader(in));
+ final PrintStream output = new PrintStream(new FileOutputStream(out, false));
+
+ // Initialization of input and output omitted
+ while ((line = input.readLine()) != null) {
+ // apply all filters
+ for (final Filter filter : filters) {
+ line = filter.filter(line);
+ }
+ if (line != null) {
+ output.println(line);
+ }
+ }
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/UnexpectedFormatException.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/UnexpectedFormatException.java
new file mode 100644
index 00000000000..ed25a357d8d
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/UnexpectedFormatException.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.util;
+
+public class UnexpectedFormatException extends Exception {
+
+ private static final long serialVersionUID = 1787725660780924147L;
+
+ public UnexpectedFormatException(final String msg) {
+ super(msg);
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/XMLLineAttributeFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/XMLLineAttributeFilter.java
new file mode 100644
index 00000000000..9fcd07016a5
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/XMLLineAttributeFilter.java
@@ -0,0 +1,36 @@
+/*
+ * 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.util;
+
+import org.apache.oro.text.perl.Perl5Util;
+
+public class XMLLineAttributeFilter implements Filter {
+
+ Perl5Util util = new Perl5Util();
+
+ @Override
+ public String filter(final String in) {
+ if (util.match("/line=\"\\d{1,3}\"/", in)) {
+ return util.substitute("s/line=\"\\d{1,3}\"/line=\"X\"/", in);
+ } else if (util.match("/line=\"?\"/", in)) {
+ return util.substitute("s/line=\"?\"/line=\"X\"/", in);
+ } else {
+ return in;
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/XMLTimestampFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/XMLTimestampFilter.java
new file mode 100644
index 00000000000..5012831f37a
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/XMLTimestampFilter.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.util;
+
+import org.apache.oro.text.perl.Perl5Util;
+
+public class XMLTimestampFilter implements Filter {
+
+ Perl5Util util = new Perl5Util();
+
+ @Override
+ public String filter(final String in) {
+ if (util.match("/timestamp=\"\\d{10,13}\"/", in)) {
+ return util.substitute("s/timestamp=\"\\d{10,13}\"/timestamp=\"XXX\"/", in);
+ }
+ return in;
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/xml/DOMTestCase.java b/log4j-1.2-api/src/test/java/org/apache/log4j/xml/DOMTestCase.java
new file mode 100644
index 00000000000..a01aa88fe2b
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/xml/DOMTestCase.java
@@ -0,0 +1,473 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.FileAppender;
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.VectorAppender;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.LoggerFactory;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.OptionHandler;
+import org.apache.log4j.spi.ThrowableRenderer;
+import org.apache.log4j.spi.ThrowableRendererSupport;
+import org.apache.log4j.util.Compare;
+import org.apache.log4j.util.ControlFilter;
+import org.apache.log4j.util.Filter;
+import org.apache.log4j.util.ISO8601Filter;
+import org.apache.log4j.util.JunitTestRunnerFilter;
+import org.apache.log4j.util.LineNumberFilter;
+import org.apache.log4j.util.SunReflectFilter;
+import org.apache.log4j.util.Transformer;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+public class DOMTestCase {
+
+ /**
+ * CustomErrorHandler for testCategoryFactory2.
+ */
+ public static class CustomErrorHandler implements ErrorHandler {
+ public CustomErrorHandler() {
+ }
+
+ public void activateOptions() {
+ }
+
+ @Override
+ public void error(final String message) {
+ }
+
+ @Override
+ public void error(final String message, final Exception e, final int errorCode) {
+ }
+
+ @Override
+ public void error(final String message, final Exception e, final int errorCode, final LoggingEvent event) {
+ }
+
+ @Override
+ public void setAppender(final Appender appender) {
+ }
+
+ @Override
+ public void setBackupAppender(final Appender appender) {
+ }
+
+ @Override
+ public void setLogger(final Logger logger) {
+ }
+ }
+
+ /**
+ * CustomLogger implementation for testCategoryFactory1 and 2.
+ */
+ private static class CustomLogger extends Logger {
+ /**
+ * Creates new instance.
+ *
+ * @param name logger name.
+ */
+ public CustomLogger(final String name) {
+ super(name);
+ }
+ }
+
+ /**
+ * Creates new instances of CustomLogger.
+ */
+ public static class CustomLoggerFactory implements LoggerFactory {
+
+ /**
+ * Additivity, expected to be set false in configuration file.
+ */
+ private boolean additivity;
+
+ /**
+ * Create new instance of factory.
+ */
+ public CustomLoggerFactory() {
+ additivity = true;
+ }
+
+ /**
+ * Create new logger.
+ *
+ * @param name logger name.
+ * @return new logger.
+ */
+ @Override
+ public Logger makeNewLoggerInstance(final String name) {
+ final Logger logger = new CustomLogger(name);
+ assertFalse(additivity);
+ return logger;
+ }
+
+ /**
+ * Set additivity.
+ *
+ * @param newVal new value of additivity.
+ */
+ public void setAdditivity(final boolean newVal) {
+ additivity = newVal;
+ }
+ }
+
+ /**
+ * Mock ThrowableRenderer for testThrowableRenderer. See bug 45721.
+ */
+ public static class MockThrowableRenderer implements ThrowableRenderer, OptionHandler {
+ private boolean activated = false;
+ private boolean showVersion = true;
+
+ public MockThrowableRenderer() {
+ }
+
+ @Override
+ public void activateOptions() {
+ activated = true;
+ }
+
+ @Override
+ public String[] doRender(final Throwable t) {
+ return new String[0];
+ }
+
+ public boolean getShowVersion() {
+ return showVersion;
+ }
+
+ public boolean isActivated() {
+ return activated;
+ }
+
+ public void setShowVersion(final boolean v) {
+ showVersion = v;
+ }
+ }
+
+ static String TEMP_A1 = "target/output/temp.A1";
+ static String TEMP_A2 = "target/output/temp.A2";
+ static String FILTERED_A1 = "target/output/filtered.A1";
+ static String FILTERED_A2 = "target/output/filtered.A2";
+ static String EXCEPTION1 = "java.lang.Exception: Just testing";
+ static String EXCEPTION2 = "\\s*at .*\\(.*\\)";
+ static String EXCEPTION3 = "\\s*at .*\\(Native Method\\)";
+ static String EXCEPTION4 = "\\s*at .*\\(.*Compiled Code\\)";
+ static String EXCEPTION5 = "\\s*at .*\\(.*libgcj.*\\)";
+ static String TEST1_1A_PAT = "(TRACE|DEBUG|INFO |WARN |ERROR|FATAL) \\w*\\.\\w* - Message \\d";
+ static String TEST1_1B_PAT = "(TRACE|DEBUG|INFO |WARN |ERROR|FATAL) root - Message \\d";
+ static String TEST1_2_PAT = "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2},\\d{3} " + "\\[main]\\ (TRACE|DEBUG|INFO|WARN|ERROR|FATAL) .* - Message \\d";
+
+ private static final boolean Log4j1ActualAppender = false;
+
+ private static final boolean AssumeThrowableRendererSupport = false;
+
+ Logger root;
+
+ Logger logger;
+
+ void common() {
+ final String oldThreadName = Thread.currentThread().getName();
+ Thread.currentThread().setName("main");
+
+ int i = -1;
+
+ logger.trace("Message " + ++i);
+ root.trace("Message " + i);
+
+ logger.debug("Message " + ++i);
+ root.debug("Message " + i);
+
+ logger.info("Message " + ++i);
+ root.info("Message " + i);
+
+ logger.warn("Message " + ++i);
+ root.warn("Message " + i);
+
+ logger.error("Message " + ++i);
+ root.error("Message " + i);
+
+ logger.log(Level.FATAL, "Message " + ++i);
+ root.log(Level.FATAL, "Message " + i);
+
+ final Exception e = new Exception("Just testing");
+ logger.debug("Message " + ++i, e);
+ root.debug("Message " + i, e);
+
+ logger.error("Message " + ++i, e);
+ root.error("Message " + i, e);
+
+ Thread.currentThread().setName(oldThreadName);
+ }
+
+ @BeforeEach
+ public void setUp() {
+ root = Logger.getRootLogger();
+ logger = Logger.getLogger(DOMTestCase.class);
+ }
+
+ @AfterEach
+ public void tearDown() {
+ root.getLoggerRepository().resetConfiguration();
+ }
+
+ @Test
+ @Disabled
+ public void test1() throws Exception {
+ DOMConfigurator.configure("src/test/resources/log4j1-1.2.17/input/xml/DOMTestCase1.xml");
+ common();
+
+ final ControlFilter cf1 = new ControlFilter(new String[] {TEST1_1A_PAT, TEST1_1B_PAT, EXCEPTION1, EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+
+ final ControlFilter cf2 = new ControlFilter(new String[] {TEST1_2_PAT, EXCEPTION1, EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+
+ Transformer.transform(TEMP_A1, FILTERED_A1, new Filter[] {cf1, new LineNumberFilter(), new SunReflectFilter(), new JunitTestRunnerFilter()});
+
+ Transformer.transform(TEMP_A2, FILTERED_A2,
+ new Filter[] {cf2, new LineNumberFilter(), new ISO8601Filter(), new SunReflectFilter(), new JunitTestRunnerFilter()});
+
+ assertTrue(Compare.compare(FILTERED_A1, "witness/dom.A1.1"));
+ assertTrue(Compare.compare(FILTERED_A2, "witness/dom.A2.1"));
+ }
+
+ /**
+ * Tests processing of external entities in XML file.
+ */
+ @Test
+ @Disabled
+ public void test4() throws Exception {
+ DOMConfigurator.configure("src/test/resources/log4j1-1.2.17/input/xml/DOMTest4.xml");
+ common();
+
+ final ControlFilter cf1 = new ControlFilter(new String[] {TEST1_1A_PAT, TEST1_1B_PAT, EXCEPTION1, EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+
+ final ControlFilter cf2 = new ControlFilter(new String[] {TEST1_2_PAT, EXCEPTION1, EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+
+ Transformer.transform(TEMP_A1 + ".4", FILTERED_A1 + ".4",
+ new Filter[] {cf1, new LineNumberFilter(), new SunReflectFilter(), new JunitTestRunnerFilter()});
+
+ Transformer.transform(TEMP_A2 + ".4", FILTERED_A2 + ".4",
+ new Filter[] {cf2, new LineNumberFilter(), new ISO8601Filter(), new SunReflectFilter(), new JunitTestRunnerFilter()});
+
+ assertTrue(Compare.compare(FILTERED_A1 + ".4", "witness/dom.A1.4"));
+ assertTrue(Compare.compare(FILTERED_A2 + ".4", "witness/dom.A2.4"));
+ }
+
+ /**
+ * Tests that loggers mentioned in logger elements use the specified categoryFactory. See bug 33708.
+ */
+ @Test
+ @Disabled
+ public void testCategoryFactory1() {
+ DOMConfigurator.configure("src/test/resources/log4j1-1.2.17/input/xml/categoryfactory1.xml");
+ //
+ // logger not explicitly mentioned in configuration,
+ // should use default factory
+ final Logger logger2 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testCategoryFactory1.2");
+ assertFalse(logger2.toString(), logger2 instanceof CustomLogger);
+ //
+ // logger explicitly mentioned in configuration,
+ // should be a CustomLogger
+ final Logger logger1 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testCategoryFactory1.1");
+ assertTrue(logger1.toString(), logger1 instanceof CustomLogger);
+ //
+ // logger not explicitly mentioned in configuration,
+ // should use default factory
+ final Logger logger2Bis = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testCategoryFactory1.2");
+ assertFalse(logger2Bis.toString(), logger2Bis instanceof CustomLogger);
+ }
+
+ /**
+ * Tests that loggers mentioned in logger-ref elements use the specified categoryFactory. See bug 33708.
+ */
+ @Test
+ @Disabled
+ public void testCategoryFactory2() {
+ DOMConfigurator.configure("src/test/resources/log4j1-1.2.17/input/xml/categoryfactory2.xml");
+ //
+ // logger not explicitly mentioned in configuration,
+ // should use default factory
+ final Logger logger2 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testCategoryFactory2.2");
+ assertFalse(logger2.toString(), logger2 instanceof CustomLogger);
+ //
+ // logger explicitly mentioned in configuration,
+ // should be a CustomLogger
+ final Logger logger1 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testCategoryFactory2.1");
+ assertTrue(logger1.toString(), logger1 instanceof CustomLogger);
+ //
+ // logger not explicitly mentioned in configuration,
+ // should use default factory
+ final Logger logger2Bis = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testCategoryFactory2.2");
+ assertFalse(logger2Bis.toString(), logger2Bis instanceof CustomLogger);
+ }
+
+ /**
+ * Test checks that configureAndWatch does initial configuration, see bug 33502.
+ *
+ * @throws Exception if IO error.
+ */
+ @Test
+ public void testConfigureAndWatch() throws Exception {
+ DOMConfigurator.configureAndWatch("src/test/resources/log4j1-1.2.17/input/xml/DOMTestCase1.xml");
+ assertNotNull(Logger.getRootLogger().getAppender("A1"));
+ }
+
+ /**
+ * Test for bug 47465. configure(URL) did not close opened JarURLConnection.
+ *
+ * @throws IOException if IOException creating properties jar.
+ */
+ @Test
+ public void testJarURL() throws IOException {
+ final File input = new File("src/test/resources/log4j1-1.2.17/input/xml/defaultInit.xml");
+ System.out.println(input.getAbsolutePath());
+ final File configJar = new File("target/output/xml.jar");
+ final File dir = new File("target/output");
+ dir.mkdirs();
+ try (final InputStream inputStream = new FileInputStream(input);
+ final FileOutputStream out = new FileOutputStream(configJar);
+ final ZipOutputStream zos = new ZipOutputStream(out)) {
+ zos.putNextEntry(new ZipEntry("log4j.xml"));
+ int len;
+ final byte[] buf = new byte[1024];
+ while ((len = inputStream.read(buf)) > 0) {
+ zos.write(buf, 0, len);
+ }
+ zos.closeEntry();
+ }
+ final URL urlInJar = new URL("jar:" + configJar.toURL() + "!/log4j.xml");
+ DOMConfigurator.configure(urlInJar);
+ assertTrue(configJar.delete());
+ assertFalse(configJar.exists());
+ }
+
+ /**
+ * Tests that loggers mentioned in logger elements use the specified loggerFactory. See bug 33708.
+ */
+ @Test
+ @Disabled("TODO")
+ public void testLoggerFactory1() {
+ DOMConfigurator.configure("src/test/resources/log4j1-1.2.17/input/xml/loggerfactory1.xml");
+ //
+ // logger not explicitly mentioned in configuration,
+ // should use default factory
+ final Logger logger2 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testLoggerFactory1.2");
+ assertNotNull(logger2);
+ assertFalse(logger2.toString(), logger2 instanceof CustomLogger);
+ //
+ // logger explicitly mentioned in configuration,
+ // should be a CustomLogger
+ final Logger logger1 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testLoggerFactory1.1");
+ assertNotNull(logger1);
+ assertTrue(logger1.toString(), logger1 instanceof CustomLogger);
+ //
+ // logger not explicitly mentioned in configuration,
+ // should use default factory
+ final Logger logger2Bis = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testLoggerFactory1.2");
+ assertNotNull(logger2Bis);
+ assertFalse(logger2Bis.toString(), logger2Bis instanceof CustomLogger);
+ }
+
+ /**
+ * This test checks that the subst method of an extending class is checked when evaluating parameters. See bug 43325.
+ *
+ */
+ @Test
+ public void testOverrideSubst() {
+ final DOMConfigurator configurator = new DOMConfigurator() {
+ protected String subst(final String value) {
+ if ("target/output/temp.A1".equals(value)) {
+ return "target/output/subst-test.A1";
+ }
+ return value;
+ }
+ };
+ configurator.doConfigure("src/test/resources/log4j1-1.2.17/input/xml/DOMTestCase1.xml", LogManager.getLoggerRepository());
+ final String name = "A1";
+ final Appender appender = Logger.getRootLogger().getAppender(name);
+ assertNotNull(name, appender);
+ if (Log4j1ActualAppender) {
+ final FileAppender a1 = (FileAppender) appender;
+ assertNotNull(name, a1);
+ final String file = a1.getFile();
+ assertEquals("target/output/subst-test.A1", file);
+ } else {
+ final AppenderWrapper wrapper = (AppenderWrapper) appender;
+ assertNotNull(name, wrapper);
+ final org.apache.logging.log4j.core.appender.FileAppender a1 = (org.apache.logging.log4j.core.appender.FileAppender) wrapper.getAppender();
+ assertNotNull(wrapper.toString(), a1);
+ final String file = a1.getFileName();
+ assertNotNull(a1.toString(), file);
+ // TODO Support this or not?
+ // assertEquals("target/output/subst-test.A1", file);
+ }
+ }
+
+ /**
+ * Tests that reset="true" on log4j:configuration element resets repository before configuration.
+ *
+ * @throws Exception thrown on error.
+ */
+ @Test
+ public void testReset() throws Exception {
+ final VectorAppender appender = new VectorAppender();
+ appender.setName("V1");
+ Logger.getRootLogger().addAppender(appender);
+ DOMConfigurator.configure("src/test/resources/log4j1-1.2.17/input/xml/testReset.xml");
+ assertNull(Logger.getRootLogger().getAppender("V1"));
+ }
+
+ /**
+ * Test of log4j.throwableRenderer support. See bug 45721.
+ */
+ @Test
+ public void testThrowableRenderer1() {
+ DOMConfigurator.configure("src/test/resources/log4j1-1.2.17/input/xml/throwableRenderer1.xml");
+ final ThrowableRendererSupport repo = (ThrowableRendererSupport) LogManager.getLoggerRepository();
+ final MockThrowableRenderer renderer = (MockThrowableRenderer) repo.getThrowableRenderer();
+ LogManager.resetConfiguration();
+ if (AssumeThrowableRendererSupport) {
+ assertNotNull(renderer);
+ assertEquals(true, renderer.isActivated());
+ assertEquals(false, renderer.getShowVersion());
+ }
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/xml/XLevel.java b/log4j-1.2-api/src/test/java/org/apache/log4j/xml/XLevel.java
new file mode 100644
index 00000000000..d90438c070a
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/xml/XLevel.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.xml;
+
+import org.apache.log4j.Level;
+
+/**
+ * This class introduces a new level called TRACE. TRACE has lower level than DEBUG.
+ */
+public class XLevel extends Level {
+ private static final long serialVersionUID = 7288304330257085144L;
+
+ public static final int TRACE_INT = Level.DEBUG_INT - 1;
+ public static final int LETHAL_INT = Level.FATAL_INT + 1;
+
+ private static String TRACE_STR = "TRACE";
+ private static String LETHAL_STR = "LETHAL";
+
+ public static final XLevel TRACE = new XLevel(TRACE_INT, TRACE_STR, 7);
+ public static final XLevel LETHAL = new XLevel(LETHAL_INT, LETHAL_STR, 0);
+
+ public static Level toLevel(final int i) throws IllegalArgumentException {
+ switch (i) {
+ case TRACE_INT:
+ return XLevel.TRACE;
+ case LETHAL_INT:
+ return XLevel.LETHAL;
+ }
+ return Level.toLevel(i);
+ }
+
+ /**
+ * Convert the string passed as argument to a level. If the conversion fails, then this method returns {@link #TRACE}.
+ */
+ public static Level toLevel(final String sArg) {
+ return toLevel(sArg, XLevel.TRACE);
+ }
+
+ public static Level toLevel(final String sArg, final Level defaultValue) {
+
+ if (sArg == null) {
+ return defaultValue;
+ }
+ final String stringVal = sArg.toUpperCase();
+
+ if (stringVal.equals(TRACE_STR)) {
+ return XLevel.TRACE;
+ } else if (stringVal.equals(LETHAL_STR)) {
+ return XLevel.LETHAL;
+ }
+
+ return Level.toLevel(sArg, defaultValue);
+ }
+
+ protected XLevel(final int level, final String strLevel, final int syslogEquiv) {
+ super(level, strLevel, syslogEquiv);
+ }
+
+}
diff --git a/log4j-1.2-api/src/test/resources/L7D_en_US.properties b/log4j-1.2-api/src/test/resources/L7D_en_US.properties
index c3c2802d525..d5775c616e8 100644
--- a/log4j-1.2-api/src/test/resources/L7D_en_US.properties
+++ b/log4j-1.2-api/src/test/resources/L7D_en_US.properties
@@ -1,3 +1,4 @@
+#
# 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.
@@ -12,6 +13,8 @@
# 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.
+#
+
test=This is the English, US test.
hello_world=Hello world.
msg1=This is test number {0} with string argument {1}.
diff --git a/log4j-1.2-api/src/test/resources/L7D_fr.properties b/log4j-1.2-api/src/test/resources/L7D_fr.properties
index 25b878aebc6..9beb5b74193 100644
--- a/log4j-1.2-api/src/test/resources/L7D_fr.properties
+++ b/log4j-1.2-api/src/test/resources/L7D_fr.properties
@@ -1,3 +1,4 @@
+#
# 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.
@@ -12,6 +13,8 @@
# 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.
+#
+
test=Ceci est le test en francais pour la France.
hello_world=Bonjour la France.
msg1=Ceci est le test numero {0} contenant l''argument {1}.
diff --git a/log4j-1.2-api/src/test/resources/L7D_fr_CH.properties b/log4j-1.2-api/src/test/resources/L7D_fr_CH.properties
index ba9b1ffbfc6..ef9d824e09a 100644
--- a/log4j-1.2-api/src/test/resources/L7D_fr_CH.properties
+++ b/log4j-1.2-api/src/test/resources/L7D_fr_CH.properties
@@ -1,3 +1,4 @@
+#
# 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.
@@ -12,5 +13,7 @@
# 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.
+#
+
test=Ceci est le test en francais pour la p'tite Suisse.
hello world=Salut le monde.
diff --git a/log4j-1.2-api/src/test/resources/LOG4J2-3247.properties b/log4j-1.2-api/src/test/resources/LOG4J2-3247.properties
new file mode 100644
index 00000000000..da261165f80
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/LOG4J2-3247.properties
@@ -0,0 +1,36 @@
+#
+# 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.filter.1=org.apache.log4j.config.NeutralFilterFixture
+log4j.appender.CONSOLE.filter.1.onMatch=neutral
+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, CONSOLE, A1, A2
diff --git a/log4j-1.2-api/src/test/resources/LOG4J2-3281.properties b/log4j-1.2-api/src/test/resources/LOG4J2-3281.properties
new file mode 100644
index 00000000000..4f309104278
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/LOG4J2-3281.properties
@@ -0,0 +1,26 @@
+#
+# 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.CUSTOM=org.apache.log4j.CustomNoopAppender
+log4j.appender.CUSTOM.filter.1=org.apache.log4j.config.NeutralFilterFixture
+log4j.appender.CUSTOM.filter.1.onMatch=neutral
+log4j.appender.CUSTOM.Target=System.out
+log4j.appender.CUSTOM.layout=org.apache.log4j.PatternLayout
+log4j.appender.CUSTOM.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+
+log4j.logger.org.apache.log4j.xml=trace, CUSTOM
+log4j.rootLogger=trace, CUSTOM
diff --git a/log4j-1.2-api/src/test/resources/LOG4J2-3326.properties b/log4j-1.2-api/src/test/resources/LOG4J2-3326.properties
new file mode 100644
index 00000000000..e21d0d394c2
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/LOG4J2-3326.properties
@@ -0,0 +1,23 @@
+#
+# 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.CUSTOM=org.apache.log4j.CustomNoopAppender
+log4j.appender.CUSTOM.filter.1=org.apache.log4j.varia.LevelRangeFilter
+log4j.appender.CUSTOM.filter.1.levelMin=ALL
+log4j.appender.CUSTOM.filter.2=org.apache.log4j.varia.LevelRangeFilter
+
+log4j.rootLogger=trace, CUSTOM
diff --git a/log4j-1.2-api/src/test/resources/LOG4J2-3407.properties b/log4j-1.2-api/src/test/resources/LOG4J2-3407.properties
new file mode 100644
index 00000000000..499a458ab51
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/LOG4J2-3407.properties
@@ -0,0 +1,26 @@
+#
+# 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.filter.1=org.apache.log4j.config.NeutralFilterFixture
+log4j.appender.CONSOLE.filter.1.onMatch=neutral
+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.logger.org.apache.log4j.xml=trace, A1
+log4j.rootLogger=trace, CONSOLE
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-auth-examples/src/main/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-auth-examples/src/main/resources/log4j.properties
index 5fa402026c2..8489a000a45 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-auth-examples/src/main/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-auth-examples/src/main/resources/log4j.properties
@@ -1,16 +1,20 @@
#
-# Licensed 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
+# 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
+# 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. See accompanying LICENSE file.
+# limitations under the License.
#
+
log4j.appender.test=org.apache.log4j.ConsoleAppender
log4j.appender.test.Target=System.out
log4j.appender.test.layout=org.apache.log4j.PatternLayout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-common/src/main/conf/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-common/src/main/conf/log4j.properties
index b08514c1703..265e1ee54d7 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-common/src/main/conf/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-common/src/main/conf/log4j.properties
@@ -1,18 +1,19 @@
-# 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
+# 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.
+#
# Define some default values that can be overridden by system properties
hadoop.root.logger=INFO,console
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-common/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-common/src/test/resources/log4j.properties
index ced0687caad..3ec3024e9f6 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-common/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-common/src/test/resources/log4j.properties
@@ -1,14 +1,20 @@
-# Licensed 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
+# 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
#
-# 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.
+# 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 configuration used during build and unit tests
log4j.rootLogger=info,stdout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-kms/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-kms/src/test/resources/log4j.properties
index b347d275e2f..9e7af6c2c6a 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-kms/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-kms/src/test/resources/log4j.properties
@@ -1,11 +1,10 @@
#
-# 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
+# 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
#
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-minikdc/src/main/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-minikdc/src/main/resources/log4j.properties
index 9efd671a087..230096ee0b2 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-minikdc/src/main/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-minikdc/src/main/resources/log4j.properties
@@ -1,11 +1,10 @@
#
-# 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
+# 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
#
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-nfs/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-nfs/src/test/resources/log4j.properties
index ced0687caad..3ec3024e9f6 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-nfs/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-nfs/src/test/resources/log4j.properties
@@ -1,14 +1,20 @@
-# Licensed 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
+# 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
#
-# 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.
+# 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 configuration used during build and unit tests
log4j.rootLogger=info,stdout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs-client/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs-client/src/test/resources/log4j.properties
index 73788467112..3253e4ea06e 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs-client/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs-client/src/test/resources/log4j.properties
@@ -1,19 +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
+# 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
+# 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.
+# 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 configuration used during build and unit tests
log4j.rootLogger=info,stdout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs/src/contrib/bkjournal/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs/src/contrib/bkjournal/src/test/resources/log4j.properties
index 52aac432644..c65f9038f27 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs/src/contrib/bkjournal/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs/src/contrib/bkjournal/src/test/resources/log4j.properties
@@ -1,22 +1,18 @@
#
-#
-# 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.
-#
+# 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.
#
#
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs/src/test/resources/log4j.properties
index 73788467112..3253e4ea06e 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs/src/test/resources/log4j.properties
@@ -1,19 +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
+# 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
+# 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.
+# 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 configuration used during build and unit tests
log4j.rootLogger=info,stdout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/resources/log4j.properties
index 81a3f6ad5d2..189e39268b6 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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 configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/test/resources/log4j.properties
index 81a3f6ad5d2..189e39268b6 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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 configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/test/resources/log4j.properties
index 81a3f6ad5d2..189e39268b6 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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 configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/resources/log4j.properties
index 81a3f6ad5d2..189e39268b6 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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 configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/resources/log4j.properties
index 81a3f6ad5d2..189e39268b6 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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 configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-nativetask/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-nativetask/src/test/resources/log4j.properties
index 81a3f6ad5d2..189e39268b6 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-nativetask/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-nativetask/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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 configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-shuffle/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-shuffle/src/test/resources/log4j.properties
index 81a3f6ad5d2..189e39268b6 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-shuffle/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-shuffle/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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 configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-aws/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-aws/src/test/resources/log4j.properties
index 1330ed1aef3..6876cf0851a 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-aws/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-aws/src/test/resources/log4j.properties
@@ -1,14 +1,20 @@
-# Licensed 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
+# 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
#
-# 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.
+# 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 configuration used during build and unit tests
log4j.rootLogger=info,stdout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-azure/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-azure/src/test/resources/log4j.properties
index 73ee3f9c6ce..43631108a79 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-azure/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-azure/src/test/resources/log4j.properties
@@ -1,19 +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
+# 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
+# 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.
+# 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 configuration used during build and unit tests
log4j.rootLogger=INFO,stdout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-openstack/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-openstack/src/test/resources/log4j.properties
index 6aeb41dcdd0..2744e4d52be 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-openstack/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-openstack/src/test/resources/log4j.properties
@@ -1,32 +1,19 @@
#
-# 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
+# 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
+# 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.
+# 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.
#
-
-# Licensed 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 configuration used during build and unit tests
log4j.rootLogger=INFO,stdout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-sls/src/main/sample-conf/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-sls/src/main/sample-conf/log4j.properties
index cfd405b16e7..46bfc946989 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-sls/src/main/sample-conf/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-sls/src/main/sample-conf/log4j.properties
@@ -1,16 +1,20 @@
#
-# Licensed 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
+# 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
+# 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. See accompanying LICENSE file.
+# limitations under the License.
#
+
log4j.appender.test=org.apache.log4j.ConsoleAppender
log4j.appender.test.Target=System.out
log4j.appender.test.layout=org.apache.log4j.PatternLayout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/resources/log4j.properties
index 81a3f6ad5d2..189e39268b6 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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 configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-unmanaged-am-launcher/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-unmanaged-am-launcher/src/test/resources/log4j.properties
index e46856e7209..d1b0064579a 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-unmanaged-am-launcher/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-unmanaged-am-launcher/src/test/resources/log4j.properties
@@ -1,33 +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
+# 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
+# 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.
+# 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.
#
-# Licensed 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 configuration used during build and unit tests
log4j.rootLogger=INFO,stdout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/resources/log4j.properties
index 81a3f6ad5d2..189e39268b6 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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 configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-registry/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-registry/src/test/resources/log4j.properties
index bed1abcbd08..93ec4426993 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-registry/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-registry/src/test/resources/log4j.properties
@@ -1,18 +1,19 @@
-# 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
+# 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.
#
-# 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 configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/src/test/resources/log4j.properties
index 81a3f6ad5d2..189e39268b6 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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 configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/resources/log4j.properties
index 81a3f6ad5d2..189e39268b6 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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 configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/resources/log4j.properties
index 81a3f6ad5d2..189e39268b6 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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 configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-tests/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-tests/src/test/resources/log4j.properties
index c088bb7fdff..d1b0064579a 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-tests/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-tests/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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 configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice-hbase-tests/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice-hbase-tests/src/test/resources/log4j.properties
index 81a3f6ad5d2..189e39268b6 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice-hbase-tests/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice-hbase-tests/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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 configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice/src/test/resources/log4j.properties
index 81a3f6ad5d2..189e39268b6 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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 configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-DailyRollingFileAppender.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-DailyRollingFileAppender.properties
index 123a51db0b3..0af97ae6b71 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-DailyRollingFileAppender.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-DailyRollingFileAppender.properties
@@ -1,3 +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 1.2 Configuration.
@@ -13,10 +30,14 @@ log4j.rootLogger=TRACE, DRFA
#
log4j.appender.DRFA=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.DRFA.Append=false
+log4j.appender.DRFA.BufferedIO=true
+log4j.appender.DRFA.BufferSize=1000
log4j.appender.DRFA.File=${hadoop.log.dir}/${hadoop.log.file}
+log4j.appender.DRFA.ImmediateFlush=false
# Rollover at midnight
-log4j.appender.DRFA.DatePattern=.yyyy-MM-dd
+log4j.appender.DRFA.DatePattern=.dd-MM-yyyy
log4j.appender.DRFA.layout=org.apache.log4j.PatternLayout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-DailyRollingFileAppender.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-DailyRollingFileAppender.xml
new file mode 100644
index 00000000000..9076d32a16c
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-DailyRollingFileAppender.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-FileAppender-with-props.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-FileAppender-with-props.properties
new file mode 100644
index 00000000000..e503ae1bd46
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-FileAppender-with-props.properties
@@ -0,0 +1,41 @@
+#
+# 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 1.2 Configuration.
+#
+
+hadoop.log.file=hadoop.log
+
+log4j.rootLogger=TRACE, FILE_APPENDER
+
+#
+# Rolling File Appender
+#
+hadoop.log.maxfilesize=256MB
+hadoop.log.maxbackupindex=20
+log4j.appender.FILE_APPENDER=org.apache.log4j.FileAppender
+log4j.appender.FILE_APPENDER.Append=false
+log4j.appender.FILE_APPENDER.BufferedIO=true
+log4j.appender.FILE_APPENDER.BufferSize=1000
+log4j.appender.FILE_APPENDER.File=${log4j.test.tmpdir}/${hadoop.log.file}
+log4j.appender.FILE_APPENDER.ImmediateFlush=false
+log4j.appender.FILE_APPENDER.layout=org.apache.log4j.PatternLayout
+
+# Pattern format: Date LogLevel LoggerName LogMessage
+log4j.appender.FILE_APPENDER.layout.ConversionPattern=%d{ISO8601} %p %c: %m%n
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-NullAppender.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-NullAppender.properties
index d89a4f4eadf..5d90e85e165 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-NullAppender.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-NullAppender.properties
@@ -1,3 +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 1.2 Configuration.
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-NullAppender.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-NullAppender.xml
new file mode 100644
index 00000000000..7153cabaeaa
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-NullAppender.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-RollingFileAppender-with-props.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-RollingFileAppender-with-props.properties
index b664bb8ccd0..fd6c6451d7f 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-RollingFileAppender-with-props.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-RollingFileAppender-with-props.properties
@@ -1,3 +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 1.2 Configuration.
@@ -14,8 +31,11 @@ log4j.rootLogger=TRACE, RFA
hadoop.log.maxfilesize=256MB
hadoop.log.maxbackupindex=20
log4j.appender.RFA=org.apache.log4j.RollingFileAppender
+log4j.appender.RFA.Append=false
+log4j.appender.RFA.BufferedIO=true
+log4j.appender.RFA.BufferSize=1000
log4j.appender.RFA.File=${hadoop.log.dir}/${hadoop.log.file}
-
+log4j.appender.RFA.ImmediateFlush=false
log4j.appender.RFA.MaxFileSize=${hadoop.log.maxfilesize}
log4j.appender.RFA.MaxBackupIndex=${hadoop.log.maxbackupindex}
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-RollingFileAppender.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-RollingFileAppender.properties
index 55234bab0ba..310471a3969 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-RollingFileAppender.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-RollingFileAppender.properties
@@ -1,3 +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 1.2 Configuration.
@@ -9,8 +26,11 @@ log4j.rootLogger=TRACE, RFA
# Rolling File Appender - cap space usage at 5gb.
#
log4j.appender.RFA=org.apache.log4j.RollingFileAppender
+log4j.appender.RFA.Append=false
+log4j.appender.RFA.BufferedIO=true
+log4j.appender.RFA.BufferSize=1000
log4j.appender.RFA.File=target/hadoop.log
-
+log4j.appender.RFA.ImmediateFlush=false
log4j.appender.RFA.MaxFileSize=256MB
log4j.appender.RFA.MaxBackupIndex=20
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-RollingFileAppender.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-RollingFileAppender.xml
new file mode 100644
index 00000000000..1ad1d4b1f59
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-RollingFileAppender.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-capitalization.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-capitalization.properties
new file mode 100644
index 00000000000..89a5bcb24ce
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-capitalization.properties
@@ -0,0 +1,44 @@
+#
+# 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 1.2 Configuration.
+#
+
+log4j.rootLogger=TRACE, ConsoleCapitalized, ConsoleJavaStyle
+
+##############################################################################
+#
+# The Console log
+#
+
+log4j.appender.ConsoleCapitalized=org.apache.log4j.ConsoleAppender
+log4j.appender.ConsoleCapitalized.Encoding=ISO-8859-1
+log4j.appender.ConsoleCapitalized.Follow=true
+log4j.appender.ConsoleCapitalized.ImmediateFlush=false
+log4j.appender.ConsoleCapitalized.Target=System.err
+log4j.appender.ConsoleCapitalized.layout=org.apache.log4j.PatternLayout
+log4j.appender.ConsoleCapitalized.layout.ConversionPattern=%d{ISO8601} [%t][%c] %-5p: %m%n
+
+log4j.appender.ConsoleJavaStyle=org.apache.log4j.ConsoleAppender
+log4j.appender.ConsoleJavaStyle.Encoding=ISO-8859-1
+log4j.appender.ConsoleJavaStyle.Follow=true
+log4j.appender.ConsoleJavaStyle.ImmediateFlush=false
+log4j.appender.ConsoleJavaStyle.Target=System.err
+log4j.appender.ConsoleJavaStyle.layout=org.apache.log4j.PatternLayout
+log4j.appender.ConsoleJavaStyle.layout.ConversionPattern=%d{ISO8601} [%t][%c] %-5p: %m%n
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-capitalization.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-capitalization.xml
new file mode 100644
index 00000000000..c353fe09d8a
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-capitalization.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-EnhancedPatternLayout.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-EnhancedPatternLayout.properties
index 6793eb26ec2..ba1197c0147 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-EnhancedPatternLayout.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-EnhancedPatternLayout.properties
@@ -1,3 +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 1.2 Configuration.
@@ -11,6 +28,7 @@ log4j.rootLogger=TRACE, Console
#
log4j.appender.Console=org.apache.log4j.ConsoleAppender
+log4j.appender.Console.Follow=true
log4j.appender.Console.Target=System.err
log4j.appender.Console.layout=org.apache.log4j.EnhancedPatternLayout
log4j.appender.Console.layout.ConversionPattern=%d{ISO8601} [%t][%c] %-5p %X %x: %m%n
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-EnhancedPatternLayout.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-EnhancedPatternLayout.xml
new file mode 100644
index 00000000000..3e1b2a0ce36
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-EnhancedPatternLayout.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-HtmlLayout.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-HtmlLayout.properties
index 216a12ebf58..ac94b1df1c9 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-HtmlLayout.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-HtmlLayout.properties
@@ -1,3 +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 1.2 Configuration.
@@ -11,6 +28,7 @@ log4j.rootLogger=TRACE, Console
#
log4j.appender.Console=org.apache.log4j.ConsoleAppender
+log4j.appender.Console.Follow=true
log4j.appender.Console.Target=System.err
log4j.appender.Console.layout=org.apache.log4j.HTMLLayout
log4j.appender.Console.layout.Title=Headline
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-HtmlLayout.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-HtmlLayout.xml
new file mode 100644
index 00000000000..f03131da8b6
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-HtmlLayout.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-PatternLayout.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-PatternLayout.properties
index 810a494eb6a..679afd3fe44 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-PatternLayout.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-PatternLayout.properties
@@ -1,3 +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 1.2 Configuration.
@@ -11,6 +28,7 @@ log4j.rootLogger=TRACE, Console
#
log4j.appender.Console=org.apache.log4j.ConsoleAppender
+log4j.appender.Console.Follow=true
log4j.appender.Console.Target=System.err
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d{ISO8601} [%t][%c] %-5p: %m%n
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-PatternLayout.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-PatternLayout.xml
new file mode 100644
index 00000000000..65ae57fe14e
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-PatternLayout.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-SimpleLayout.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-SimpleLayout.properties
index 5a8ac4eb9a0..42f29d3956b 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-SimpleLayout.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-SimpleLayout.properties
@@ -1,3 +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 1.2 Configuration.
@@ -11,6 +28,7 @@ log4j.rootLogger=TRACE, Console
#
log4j.appender.Console=org.apache.log4j.ConsoleAppender
+log4j.appender.Console.Follow=true
log4j.appender.Console.Target=System.err
log4j.appender.Console.layout=org.apache.log4j.SimpleLayout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-SimpleLayout.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-SimpleLayout.xml
new file mode 100644
index 00000000000..30263bf315a
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-SimpleLayout.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-TTCCLayout.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-TTCCLayout.properties
index 80d38c2a53e..01cc0b0c010 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-TTCCLayout.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-TTCCLayout.properties
@@ -1,3 +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 1.2 Configuration.
@@ -11,9 +28,13 @@ log4j.rootLogger=TRACE, Console
#
log4j.appender.Console=org.apache.log4j.ConsoleAppender
+log4j.appender.Console.Follow=true
log4j.appender.Console.Target=System.err
log4j.appender.Console.layout=org.apache.log4j.TTCCLayout
-log4j.appender.Console.layout.ThreadPrinting=true
+log4j.appender.Console.layout.ThreadPrinting=false
log4j.appender.Console.layout.CategoryPrefixing=false
+log4j.appender.Console.layout.ContextPrinting=false
+log4j.appender.Console.layout.DateFormat=ISO8601
+log4j.appender.Console.layout.TimeZone=CET
log4j.logger.com.example.foo = DEBUG
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-TTCCLayout.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-TTCCLayout.xml
new file mode 100644
index 00000000000..b0900dabbd3
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-TTCCLayout.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-XmlLayout.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-XmlLayout.properties
index c8190ecd8bd..f8a22af99c5 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-XmlLayout.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-XmlLayout.properties
@@ -1,3 +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 1.2 Configuration.
@@ -11,6 +28,7 @@ log4j.rootLogger=TRACE, Console
#
log4j.appender.Console=org.apache.log4j.ConsoleAppender
+log4j.appender.Console.Follow=true
log4j.appender.Console.Target=System.err
log4j.appender.Console.layout=org.apache.log4j.xml.XMLLayout
log4j.appender.Console.layout.LocationInfo=true
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-defaultValues.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-defaultValues.properties
new file mode 100644
index 00000000000..15627a4d4a3
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-defaultValues.properties
@@ -0,0 +1,82 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Configuration file with minimal number of non-default values
+#
+log4j.rootLogger = TRACE, HTMLLayout, PatternLayout, TTCCLayout, XMLLayout,\
+ConsoleAppender, DailyRollingFileAppender, FileAppender, RollingFileAppender
+
+##############################################################################
+#
+# HTMLLayout
+#
+log4j.appender.HTMLLayout=org.apache.log4j.ConsoleAppender
+log4j.appender.HTMLLayout.layout=org.apache.log4j.HTMLLayout
+
+##############################################################################
+#
+# PatternLayout
+#
+log4j.appender.PatternLayout=org.apache.log4j.ConsoleAppender
+log4j.appender.PatternLayout.layout=org.apache.log4j.PatternLayout
+
+##############################################################################
+#
+# TTCCLayout
+#
+log4j.appender.TTCCLayout=org.apache.log4j.ConsoleAppender
+log4j.appender.TTCCLayout.layout=org.apache.log4j.TTCCLayout
+
+##############################################################################
+#
+# XMLLayout
+#
+log4j.appender.XMLLayout=org.apache.log4j.ConsoleAppender
+log4j.appender.XMLLayout.layout=org.apache.log4j.xml.XMLLayout
+
+##############################################################################
+#
+# ConsoleAppender
+#
+log4j.appender.ConsoleAppender=org.apache.log4j.ConsoleAppender
+log4j.appender.ConsoleAppender.layout=org.apache.log4j.SimpleLayout
+
+##############################################################################
+#
+# DailyRollingFileAppender
+#
+log4j.appender.DailyRollingFileAppender=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.DailyRollingFileAppender.File=target/dailyRollingFileAppender
+log4j.appender.DailyRollingFileAppender.layout=org.apache.log4j.SimpleLayout
+
+##############################################################################
+#
+# FileAppender
+#
+log4j.appender.FileAppender=org.apache.log4j.FileAppender
+log4j.appender.FileAppender.File=target/fileAppender
+log4j.appender.FileAppender.layout=org.apache.log4j.SimpleLayout
+
+##############################################################################
+#
+# RollingFileAppender
+#
+log4j.appender.RollingFileAppender=org.apache.log4j.RollingFileAppender
+log4j.appender.RollingFileAppender.File=target/rollingFileAppender
+log4j.appender.RollingFileAppender.layout=org.apache.log4j.SimpleLayout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-defaultValues.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-defaultValues.xml
new file mode 100644
index 00000000000..7d5d3576bab
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-defaultValues.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-file-SimpleLayout.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-file-SimpleLayout.properties
index 4d3ec0d6af3..d42e654e795 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-file-SimpleLayout.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-file-SimpleLayout.properties
@@ -1,3 +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 1.2 Configuration.
@@ -11,7 +28,11 @@ log4j.rootLogger=TRACE, File
#
log4j.appender.File=org.apache.log4j.FileAppender
+log4j.appender.File.Append=false
+log4j.appender.File.BufferedIO=true
+log4j.appender.File.BufferSize=1000
log4j.appender.File.File=target/mylog.txt
+log4j.appender.File.ImmediateFlush=false
log4j.appender.File.layout=org.apache.log4j.SimpleLayout
log4j.logger.com.example.foo = DEBUG
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-file-SimpleLayout.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-file-SimpleLayout.xml
new file mode 100644
index 00000000000..fdccce77acc
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-file-SimpleLayout.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-global-threshold.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-global-threshold.properties
new file mode 100644
index 00000000000..f76d396f7f9
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-global-threshold.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.threshold = info
+log4j.appender.LIST = org.apache.log4j.ListAppender
+log4j.rootLogger = debug, LIST
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-global-threshold.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-global-threshold.xml
new file mode 100644
index 00000000000..fb969cad411
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-global-threshold.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-system-properties-1.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-system-properties-1.properties
index a82c4c37e9b..167cd86eb42 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-system-properties-1.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-system-properties-1.properties
@@ -1,3 +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 1.2 Configuration.
@@ -11,4 +28,10 @@ log4j.rootLogger=TRACE, RFA
# Rolling File Appender
#
log4j.appender.RFA=org.apache.log4j.RollingFileAppender
+log4j.appender.RFA.Append=false
+log4j.appender.RFA.BufferedIO=true
+log4j.appender.RFA.BufferSize=1000
log4j.appender.RFA.File=${java.io.tmpdir}/${hadoop.log.file}
+log4j.appender.RFA.ImmediateFlush=false
+log4j.appender.RFA.MaxBackupIndex=16
+log4j.appender.RFA.MaxFileSize=20 MB
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-system-properties-1.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-system-properties-1.xml
new file mode 100644
index 00000000000..ccfd9e748ae
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-system-properties-1.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-system-properties-2.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-system-properties-2.properties
index 9228434054e..686bc77ecd6 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-system-properties-2.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-system-properties-2.properties
@@ -1,3 +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 1.2 Configuration.
@@ -12,4 +29,10 @@ log4j.rootLogger=TRACE, RFA
# Rolling File Appender
#
log4j.appender.RFA=org.apache.log4j.RollingFileAppender
+log4j.appender.RFA.Append=false
+log4j.appender.RFA.BufferedIO=true
+log4j.appender.RFA.BufferSize=1000
log4j.appender.RFA.File=${hadoop.log.dir}/${hadoop.log.file}
+log4j.appender.RFA.ImmediateFlush=false
+log4j.appender.RFA.MaxBackupIndex=16
+log4j.appender.RFA.MaxBackupSize=20 MB
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-untrimmed.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-untrimmed.properties
new file mode 100644
index 00000000000..66b2fbaa5de
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-untrimmed.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+###
+# Warning: this file contains INTENTIONAL trailing spaces on all properties
+#
+
+log4j.threshold = INFO
+
+log4j.appender.Console = org.apache.log4j.ConsoleAppender
+log4j.appender.Console.layout = org.apache.log4j.SimpleLayout
+log4j.appender.Console.filter.1 = org.apache.log4j.varia.DenyAllFilter
+
+log4j.rootLogger = DEBUG , Console
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/R/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/R/log4j.properties
index cce8d9152d3..6b286c954a3 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/R/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/R/log4j.properties
@@ -6,7 +6,7 @@
# (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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/common/network-common/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/common/network-common/src/test/resources/log4j.properties
index e8da774f7ca..60a759aa691 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/common/network-common/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/common/network-common/src/test/resources/log4j.properties
@@ -6,7 +6,7 @@
# (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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/common/network-shuffle/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/common/network-shuffle/src/test/resources/log4j.properties
index e73978908b6..42d49093d67 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/common/network-shuffle/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/common/network-shuffle/src/test/resources/log4j.properties
@@ -6,7 +6,7 @@
# (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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/core/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/core/src/test/resources/log4j.properties
index fb9d9851cb4..db816d7ba64 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/core/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/core/src/test/resources/log4j.properties
@@ -6,7 +6,7 @@
# (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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/flume-sink/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/flume-sink/src/test/resources/log4j.properties
index 1e3f163f95c..aaebccb88b2 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/flume-sink/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/flume-sink/src/test/resources/log4j.properties
@@ -6,7 +6,7 @@
# (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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/flume/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/flume/src/test/resources/log4j.properties
index fd51f8faf56..49f20f081c2 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/flume/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/flume/src/test/resources/log4j.properties
@@ -6,7 +6,7 @@
# (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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/java8-tests/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/java8-tests/src/test/resources/log4j.properties
index 3706a6e3613..636e5562e58 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/java8-tests/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/java8-tests/src/test/resources/log4j.properties
@@ -6,7 +6,7 @@
# (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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kafka-0-10/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kafka-0-10/src/test/resources/log4j.properties
index 75e3b53a093..fe58d0ad132 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kafka-0-10/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kafka-0-10/src/test/resources/log4j.properties
@@ -6,7 +6,7 @@
# (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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kafka-0-8/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kafka-0-8/src/test/resources/log4j.properties
index fd51f8faf56..49f20f081c2 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kafka-0-8/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kafka-0-8/src/test/resources/log4j.properties
@@ -6,7 +6,7 @@
# (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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kinesis-asl/src/main/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kinesis-asl/src/main/resources/log4j.properties
index 4f5ea7bafe4..b0e9b37f04f 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kinesis-asl/src/main/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kinesis-asl/src/main/resources/log4j.properties
@@ -6,7 +6,7 @@
# (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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kinesis-asl/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kinesis-asl/src/test/resources/log4j.properties
index 3706a6e3613..636e5562e58 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kinesis-asl/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kinesis-asl/src/test/resources/log4j.properties
@@ -6,7 +6,7 @@
# (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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/graphx/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/graphx/src/test/resources/log4j.properties
index 3706a6e3613..636e5562e58 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/graphx/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/graphx/src/test/resources/log4j.properties
@@ -6,7 +6,7 @@
# (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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/launcher/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/launcher/src/test/resources/log4j.properties
index 744c456cb29..debf8f6caa7 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/launcher/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/launcher/src/test/resources/log4j.properties
@@ -6,7 +6,7 @@
# (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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/mllib/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/mllib/src/test/resources/log4j.properties
index fd51f8faf56..49f20f081c2 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/mllib/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/mllib/src/test/resources/log4j.properties
@@ -6,7 +6,7 @@
# (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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/repl/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/repl/src/test/resources/log4j.properties
index 7665bd5e7c0..bc5933f8d81 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/repl/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/repl/src/test/resources/log4j.properties
@@ -6,7 +6,7 @@
# (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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/catalyst/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/catalyst/src/test/resources/log4j.properties
index 3706a6e3613..636e5562e58 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/catalyst/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/catalyst/src/test/resources/log4j.properties
@@ -6,7 +6,7 @@
# (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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/core/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/core/src/test/resources/log4j.properties
index 33b9ecf1e28..3b058c8599b 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/core/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/core/src/test/resources/log4j.properties
@@ -6,7 +6,7 @@
# (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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/hive/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/hive/src/test/resources/log4j.properties
index fea3404769d..3f91a2b3734 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/hive/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/hive/src/test/resources/log4j.properties
@@ -6,7 +6,7 @@
# (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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/streaming/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/streaming/src/test/resources/log4j.properties
index fd51f8faf56..49f20f081c2 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/streaming/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/streaming/src/test/resources/log4j.properties
@@ -6,7 +6,7 @@
# (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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/yarn/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/yarn/src/test/resources/log4j.properties
index d13454d5ae5..d1632ee69e1 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/yarn/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/yarn/src/test/resources/log4j.properties
@@ -6,7 +6,7 @@
# (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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/log-RouteWithMDC.xml b/log4j-1.2-api/src/test/resources/log-RouteWithMDC.xml
index eb7e8a57ddf..17a263f878d 100644
--- a/log4j-1.2-api/src/test/resources/log-RouteWithMDC.xml
+++ b/log4j-1.2-api/src/test/resources/log-RouteWithMDC.xml
@@ -14,7 +14,6 @@
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.
-
-->
diff --git a/log4j-1.2-api/src/test/resources/log4j-multipleFilters.properties b/log4j-1.2-api/src/test/resources/log4j-multipleFilters.properties
new file mode 100644
index 00000000000..177d60ca3d3
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j-multipleFilters.properties
@@ -0,0 +1,69 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+log4j.appender.LIST=org.apache.log4j.ListAppender
+log4j.appender.LIST.Threshold=DEBUG
+log4j.appender.LIST.filter.1=org.apache.log4j.config.StartsWithFilter
+log4j.appender.LIST.filter.2=org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.LIST.filter.2.LevelToMatch=INFO
+log4j.appender.LIST.filter.2.AcceptOnMatch=true
+log4j.appender.LIST.filter.3=org.apache.log4j.varia.DenyAllFilter
+
+log4j.appender.LIST2=org.apache.logging.log4j.test.appender.ListAppender
+log4j.appender.LIST2.Threshold=DEBUG
+log4j.appender.LIST2.filter.1=org.apache.log4j.config.StartsWithFilter
+log4j.appender.LIST2.filter.2=org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.LIST2.filter.2.LevelToMatch=INFO
+log4j.appender.LIST2.filter.2.AcceptOnMatch=true
+log4j.appender.LIST2.filter.3=org.apache.log4j.varia.DenyAllFilter
+
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.Threshold=DEBUG
+log4j.appender.CONSOLE.filter.1=org.apache.log4j.config.StartsWithFilter
+log4j.appender.CONSOLE.filter.2=org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.CONSOLE.filter.2.LevelToMatch=INFO
+log4j.appender.CONSOLE.filter.2.AcceptOnMatch=true
+log4j.appender.CONSOLE.filter.3=org.apache.log4j.varia.DenyAllFilter
+
+log4j.appender.FILE=org.apache.log4j.FileAppender
+log4j.appender.FILE.Threshold=DEBUG
+log4j.appender.FILE.File=${test.tmpDir}/file-appender.log
+log4j.appender.FILE.filter.1=org.apache.log4j.config.StartsWithFilter
+log4j.appender.FILE.filter.2=org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.FILE.filter.2.LevelToMatch=INFO
+log4j.appender.FILE.filter.2.AcceptOnMatch=true
+log4j.appender.FILE.filter.3=org.apache.log4j.varia.DenyAllFilter
+
+log4j.appender.RFA=org.apache.log4j.RollingFileAppender
+log4j.appender.RFA.Threshold=DEBUG
+log4j.appender.RFA.File=${test.tmpDir}/rolling-file-appender.log
+log4j.appender.RFA.filter.1=org.apache.log4j.config.StartsWithFilter
+log4j.appender.RFA.filter.2=org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.RFA.filter.2.LevelToMatch=INFO
+log4j.appender.RFA.filter.2.AcceptOnMatch=true
+log4j.appender.RFA.filter.3=org.apache.log4j.varia.DenyAllFilter
+
+log4j.appender.DRFA=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.DRFA.Threshold=DEBUG
+log4j.appender.DRFA.File=${test.tmpDir}/daily-rolling-file-appender.log
+log4j.appender.DRFA.filter.1=org.apache.log4j.config.StartsWithFilter
+log4j.appender.DRFA.filter.2=org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.DRFA.filter.2.LevelToMatch=INFO
+log4j.appender.DRFA.filter.2.AcceptOnMatch=true
+log4j.appender.DRFA.filter.3=org.apache.log4j.varia.DenyAllFilter
+
+log4j.rootLogger=TRACE, LIST, LIST2, CONSOLE, FILE, RFA, DRFA
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j-multipleFilters.xml b/log4j-1.2-api/src/test/resources/log4j-multipleFilters.xml
new file mode 100644
index 00000000000..ea256e719f1
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j-multipleFilters.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j.xml b/log4j-1.2-api/src/test/resources/log4j.xml
new file mode 100644
index 00000000000..b18ad75ea27
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/RFA1.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/RFA1.properties
new file mode 100644
index 00000000000..5cbd8a41564
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/RFA1.properties
@@ -0,0 +1,29 @@
+#
+# 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.rootLogger=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.RollingFileAppender
+log4j.appender.testAppender.file=output/RFA-test1.log
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%m\n
+log4j.appender.testAppender.maxFileSize=100
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/defaultInit3.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/defaultInit3.properties
new file mode 100644
index 00000000000..fe899d38dce
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/defaultInit3.properties
@@ -0,0 +1,23 @@
+#
+# 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.rootCategory=DEBUG, D3
+log4j.appender.D3=org.apache.log4j.FileAppender
+log4j.appender.D3.File=output/temp
+log4j.appender.D3.Append=false
+log4j.appender.D3.layout=org.apache.log4j.PatternLayout
+log4j.appender.D3.layout.ConversionPattern=%d [%t] %-5p %.16c - %m%n
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/fallback1.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/fallback1.properties
new file mode 100644
index 00000000000..270c9960517
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/fallback1.properties
@@ -0,0 +1,34 @@
+#
+# 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.debug=true
+log4j.appender.PRIMARY=org.apache.log4j.FileAppender
+log4j.appender.PRIMARY.errorhandler=org.apache.log4j.varia.FallbackErrorHandler
+log4j.appender.PRIMARY.errorhandler.root-ref=true
+log4j.appender.PRIMARY.errorhandler.appender-ref=FALLBACK
+log4j.appender.PRIMARY.file=/xyz/:x.log
+log4j.appender.PRIMARY.append=false
+log4j.appender.PRIMARY.layout=org.apache.log4j.PatternLayout
+log4j.appender.PRIMARY.layout.conversionPattern=%-5p %c{2} - %m%n
+
+log4j.appender.FALLBACK=org.apache.log4j.FileAppender
+log4j.appender.FALLBACK.File=output/temp
+log4j.appender.FALLBACK.Append=false
+log4j.appender.FALLBACK.layout=org.apache.log4j.PatternLayout
+log4j.appender.FALLBACK.layout.ConversionPattern=FALLBACK - %c - %m%n
+
+log4j.rootLogger=DEBUG, PRIMARY
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/filter1.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/filter1.properties
new file mode 100644
index 00000000000..1a3eddbd437
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/filter1.properties
@@ -0,0 +1,34 @@
+#
+# 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.ROLLING=org.apache.log4j.PropertyConfiguratorTest$RollingFileAppender
+log4j.appender.ROLLING.append=false
+log4j.appender.ROLLING.rollingPolicy=org.apache.log4j.PropertyConfiguratorTest$FixedWindowRollingPolicy
+log4j.appender.ROLLING.rollingPolicy.activeFileName=filterBase-test1.log
+log4j.appender.ROLLING.rollingPolicy.fileNamePattern=filterBased-test1.%i
+log4j.appender.ROLLING.rollingPolicy.minIndex=0
+log4j.appender.ROLLING.triggeringPolicy=org.apache.log4j.PropertyConfiguratorTest$FilterBasedTriggeringPolicy
+log4j.appender.ROLLING.triggeringPolicy.filter=org.apache.log4j.varia.LevelRangeFilter
+log4j.appender.ROLLING.triggeringPolicy.filter.levelMin=info
+log4j.appender.ROLLING.layout=org.apache.log4j.PatternLayout
+log4j.appender.ROLLING.layout.ConversionPattern=%m%n
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.ConversionPattern=%m%n
+log4j.logger.org.apache.log4j.PropertyConfiguratorTest=debug, ROLLING
+log4j.additivity.org.apache.log4j.rolling.FilterBasedRollingTest=false
+log4j.roolLogger=info, CONSOLE
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold1.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold1.properties
new file mode 100644
index 00000000000..ce182724574
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold1.properties
@@ -0,0 +1,24 @@
+#
+# 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.threshold=OFF
+log4j.rootLogger=,A
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.File=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%p [%t] %c{2} = %m%n
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold2.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold2.properties
new file mode 100644
index 00000000000..fb60102dddb
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold2.properties
@@ -0,0 +1,24 @@
+#
+# 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.threshold=FATAL
+log4j.rootLogger=,A
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.File=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%p [%t] %c{2} = %m%n
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold3.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold3.properties
new file mode 100644
index 00000000000..0047e515b29
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold3.properties
@@ -0,0 +1,24 @@
+#
+# 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.threshold=ERROR
+log4j.rootLogger=,A
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.File=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%p [%t] %c{2} = %m%n
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold4.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold4.properties
new file mode 100644
index 00000000000..e441527066e
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold4.properties
@@ -0,0 +1,24 @@
+#
+# 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.threshold=WARN
+log4j.rootLogger=,A
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.File=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%p [%t] %c{2} = %m%n
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold5.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold5.properties
new file mode 100644
index 00000000000..111ffc87f50
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold5.properties
@@ -0,0 +1,24 @@
+#
+# 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.threshold=INFO
+log4j.rootLogger=,A
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.File=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%p [%t] %c{2} = %m%n
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold6.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold6.properties
new file mode 100644
index 00000000000..bfce3ae2419
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold6.properties
@@ -0,0 +1,24 @@
+#
+# 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.threshold=DEBUG
+log4j.rootLogger=,A
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.File=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%p [%t] %c{2} = %m%n
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold7.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold7.properties
new file mode 100644
index 00000000000..04843e4a901
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold7.properties
@@ -0,0 +1,24 @@
+#
+# 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.threshold=TRACE#org.apache.log4j.xml.XLevel
+log4j.rootLogger=ALL,A
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.File=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%p [%t] %c{2} = %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold8.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold8.properties
new file mode 100644
index 00000000000..b524a8e7709
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold8.properties
@@ -0,0 +1,24 @@
+#
+# 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.threshold=ALL
+log4j.rootLogger=ALL,A
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.File=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%p [%t] %c{2} = %m%n
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout.mdc.1.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout.mdc.1.properties
new file mode 100644
index 00000000000..2f5edd37a2a
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout.mdc.1.properties
@@ -0,0 +1,28 @@
+#
+# 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.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%-5p - %m %X%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout1.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout1.properties
new file mode 100644
index 00000000000..0afcdce03f3
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout1.properties
@@ -0,0 +1,28 @@
+#
+# 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.rootLogger=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.file=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%-5p - %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout10.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout10.properties
new file mode 100644
index 00000000000..0acc4971c21
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout10.properties
@@ -0,0 +1,28 @@
+#
+# 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.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append= false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=[%t] %-5p %l: %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout11.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout11.properties
new file mode 100644
index 00000000000..98b8a19674b
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout11.properties
@@ -0,0 +1,28 @@
+#
+# 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.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%-5p [%t] %c{2}: %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout12.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout12.properties
new file mode 100644
index 00000000000..eae07a67dd2
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout12.properties
@@ -0,0 +1,28 @@
+#
+# 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.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=[%t] %-5p %C.%M(%F:%L): %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout13.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout13.properties
new file mode 100644
index 00000000000..687b8c42a88
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout13.properties
@@ -0,0 +1,28 @@
+#
+# 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.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=[%t] %-5p %C{3}.%M(%F:%L): %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout14.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout14.properties
new file mode 100644
index 00000000000..e08ab00fc0f
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout14.properties
@@ -0,0 +1,28 @@
+#
+# 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.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%-5p [%t] %c{1.}: %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout15.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout15.properties
new file mode 100644
index 00000000000..2daac99948c
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout15.properties
@@ -0,0 +1,29 @@
+#
+# 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.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedMyPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%5p %-4# - %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout16.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout16.properties
new file mode 100644
index 00000000000..c97f824892c
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout16.properties
@@ -0,0 +1,29 @@
+#
+# 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.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/patternLayout16.log
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss}{GMT}Z %d{yyyy-MM-dd HH:mm:ss}{GMT-6}-0600 - %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout2.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout2.properties
new file mode 100644
index 00000000000..7f97accbeab
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout2.properties
@@ -0,0 +1,28 @@
+#
+# 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.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append= false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{ISO8601} [%t] %-5p %.16c - %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout3.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout3.properties
new file mode 100644
index 00000000000..b9ea02a8f10
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout3.properties
@@ -0,0 +1,28 @@
+#
+# 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.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} [%t] %-5p %.16c - %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout4.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout4.properties
new file mode 100644
index 00000000000..a9284ca4dbb
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout4.properties
@@ -0,0 +1,28 @@
+#
+# 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.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{DATE} [%t] %-5p %.16c - %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout5.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout5.properties
new file mode 100644
index 00000000000..64da2f93fdf
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout5.properties
@@ -0,0 +1,28 @@
+#
+# 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.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{dd MMM yyyy HH:mm:ss,SSS} [%t] %-5p %.16c - %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout6.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout6.properties
new file mode 100644
index 00000000000..982250ba6ee
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout6.properties
@@ -0,0 +1,29 @@
+#
+# 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.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{ABSOLUTE} [%t] %-5p %.16c - %m%n
+
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout7.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout7.properties
new file mode 100644
index 00000000000..188ddd82cfb
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout7.properties
@@ -0,0 +1,28 @@
+#
+# 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.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{HH:mm:ss,SSS} [%t] %-5p %.16c - %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout8.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout8.properties
new file mode 100644
index 00000000000..b9e07834e02
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout8.properties
@@ -0,0 +1,28 @@
+#
+# 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.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%r [%t] %-5p %.16c - %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout9.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout9.properties
new file mode 100644
index 00000000000..97b1d8152ff
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout9.properties
@@ -0,0 +1,28 @@
+#
+# 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.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=[%t] %-5p %.16c : %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout.mdc.1.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout.mdc.1.properties
new file mode 100644
index 00000000000..bbe1d0f912d
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout.mdc.1.properties
@@ -0,0 +1,28 @@
+#
+# 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.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%-5p - %m %X%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout1.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout1.properties
new file mode 100644
index 00000000000..4d3be036cc6
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout1.properties
@@ -0,0 +1,23 @@
+#
+# 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.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%-5p - %m%n
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout10.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout10.properties
new file mode 100644
index 00000000000..fd6fabc1635
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout10.properties
@@ -0,0 +1,23 @@
+#
+# 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.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File= output/temp
+log4j.appender.testAppender.Append= false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=[%t] %-5p %l: %m%n
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout11.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout11.properties
new file mode 100644
index 00000000000..1de0dec5a67
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout11.properties
@@ -0,0 +1,23 @@
+#
+# 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.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%-5p [%t] %c{2}: %m%n
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout12.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout12.properties
new file mode 100644
index 00000000000..1f17f72da53
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout12.properties
@@ -0,0 +1,23 @@
+#
+# 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.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=[%t] %-5p %C.%M(%F:%L): %m%n
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout13.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout13.properties
new file mode 100644
index 00000000000..10da01b77c9
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout13.properties
@@ -0,0 +1,23 @@
+#
+# 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.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File= output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=[%t] %-5p %C{3}.%M(%F:%L): %m%n
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout14.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout14.properties
new file mode 100644
index 00000000000..56720864203
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout14.properties
@@ -0,0 +1,23 @@
+#
+# 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.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File= output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.MyPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%5p %-4# - %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout2.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout2.properties
new file mode 100644
index 00000000000..b2f79c49910
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout2.properties
@@ -0,0 +1,23 @@
+#
+# 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.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append= false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{ISO8601} [%t] %-5p %.16c - %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout3.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout3.properties
new file mode 100644
index 00000000000..98b2ac31db6
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout3.properties
@@ -0,0 +1,23 @@
+#
+# 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.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} [%t] %-5p %.16c - %m%n
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout4.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout4.properties
new file mode 100644
index 00000000000..91df3c286f8
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout4.properties
@@ -0,0 +1,23 @@
+#
+# 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.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{DATE} [%t] %-5p %.16c - %m%n
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout5.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout5.properties
new file mode 100644
index 00000000000..3bcaf7b6b53
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout5.properties
@@ -0,0 +1,23 @@
+#
+# 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.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{dd MMM yyyy HH:mm:ss,SSS} [%t] %-5p %.16c - %m%n
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout6.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout6.properties
new file mode 100644
index 00000000000..c1eeb6c057d
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout6.properties
@@ -0,0 +1,23 @@
+#
+# 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.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{ABSOLUTE} [%t] %-5p %.16c - %m%n
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout7.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout7.properties
new file mode 100644
index 00000000000..0af90f125d9
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout7.properties
@@ -0,0 +1,23 @@
+#
+# 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.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{HH:mm:ss,SSS} [%t] %-5p %.16c - %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout8.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout8.properties
new file mode 100644
index 00000000000..f43eca2b389
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout8.properties
@@ -0,0 +1,23 @@
+#
+# 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.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%r [%t] %-5p %.16c - %m%n
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout9.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout9.properties
new file mode 100644
index 00000000000..045efcf50ec
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout9.properties
@@ -0,0 +1,23 @@
+#
+# 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.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=[%t] %-5p %.16c : %m%n
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer1.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer1.properties
new file mode 100644
index 00000000000..0c99b0be151
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer1.properties
@@ -0,0 +1,26 @@
+#
+# 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.rootLogger=TRACE, A
+log4j.logger.org.apache.log4j.test.ShortSocketServer=WARN
+log4j.logger.org.apache.log4j.net.SocketNode=WARN
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.file=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%5p %x [%t] %c %m%n
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer2.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer2.properties
new file mode 100644
index 00000000000..077e8ec6f8c
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer2.properties
@@ -0,0 +1,26 @@
+#
+# 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.rootLogger=TRACE, A
+log4j.logger.org.apache.log4j.test.ShortSocketServer=WARN
+log4j.logger.org.apache.log4j.net.SocketNode=WARN
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.file=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%5p %x [%t] %C (%F:%L) %m%n
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer3.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer3.properties
new file mode 100644
index 00000000000..5476e85d287
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer3.properties
@@ -0,0 +1,25 @@
+#
+# 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.rootLogger=TRACE, A
+log4j.Logger.org.apache.log4j.test.ShortSocketServer=WARN
+log4j.Logger.org.apache.log4j.net.SocketNode=WARN
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.file=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%5p %x [%t] %C (%F:%L) %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer4.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer4.properties
new file mode 100644
index 00000000000..66a408dc36f
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer4.properties
@@ -0,0 +1,25 @@
+#
+# 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.rootLogger=TRACE, A
+log4j.Logger.org.apache.log4j.test.ShortSocketServer=WARN
+log4j.Logger.org.apache.log4j.net.SocketNode=WARN
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.file=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%5p %x %X{key1}%X{key4} [%t] %c{1} - %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer5.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer5.properties
new file mode 100644
index 00000000000..aeea42cc91d
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer5.properties
@@ -0,0 +1,25 @@
+#
+# 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.rootLogger=TRACE, A
+log4j.Logger.org.apache.log4j.test.ShortSocketServer=WARN
+log4j.Logger.org.apache.log4j.net.SocketNode=WARN
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.file=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%5p %x %X{key1}%X{key5} [%t] %c{1} - %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer6.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer6.properties
new file mode 100644
index 00000000000..e6966a2589f
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer6.properties
@@ -0,0 +1,25 @@
+#
+# 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.rootLogger=TRACE, A
+log4j.Logger.org.apache.log4j.test.ShortSocketServer=WARN
+log4j.Logger.org.apache.log4j.net.SocketNode=WARN
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.file=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%5p %x %X{hostID} %X{key6} [%t] %c{1} - %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer7.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer7.properties
new file mode 100644
index 00000000000..c43d54ec6af
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer7.properties
@@ -0,0 +1,25 @@
+#
+# 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.rootLogger=TRACE, A
+log4j.Logger.org.apache.log4j.test.ShortSocketServer=WARN
+log4j.Logger.org.apache.log4j.net.SocketNode=WARN
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.file=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%5p %x %X{hostID} %X{key7} [%t] %c{1} - %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer8.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer8.properties
new file mode 100644
index 00000000000..9b81ddb688e
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer8.properties
@@ -0,0 +1,25 @@
+#
+# 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.rootLogger=TRACE, A
+log4j.Logger.org.apache.log4j.test.ShortSocketServer=WARN
+log4j.Logger.org.apache.log4j.net.SocketNode=WARN
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.file=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%5p %x %X{hostID} %X{key8} [%t] %c{1} - %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/DOMTest4.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/DOMTest4.xml
new file mode 100644
index 00000000000..b4535e2d0d8
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/DOMTest4.xml
@@ -0,0 +1,44 @@
+
+
+
+]>
+
+
+ &a1;
+ &a2;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/DOMTest4_A1.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/DOMTest4_A1.xml
new file mode 100644
index 00000000000..9b945e60aab
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/DOMTest4_A1.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/DOMTest4_A2.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/DOMTest4_A2.xml
new file mode 100644
index 00000000000..e5e26c02485
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/DOMTest4_A2.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/DOMTestCase1.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/DOMTestCase1.xml
new file mode 100644
index 00000000000..90917d1c4cd
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/DOMTestCase1.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/SocketAppenderTestConfig.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/SocketAppenderTestConfig.xml
new file mode 100644
index 00000000000..8588e7f692b
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/SocketAppenderTestConfig.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/categoryfactory1.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/categoryfactory1.xml
new file mode 100644
index 00000000000..eba5a33181e
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/categoryfactory1.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/categoryfactory2.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/categoryfactory2.xml
new file mode 100644
index 00000000000..9f48b1ee96b
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/categoryfactory2.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/customLevel1.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/customLevel1.xml
new file mode 100644
index 00000000000..5c798d2229c
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/customLevel1.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/customLevel2.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/customLevel2.xml
new file mode 100644
index 00000000000..88735808cdb
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/customLevel2.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/customLevel3.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/customLevel3.xml
new file mode 100644
index 00000000000..f8c014ad90b
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/customLevel3.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/customLevel4.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/customLevel4.xml
new file mode 100644
index 00000000000..e1bd0ded9b3
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/customLevel4.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/customLogger1.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/customLogger1.xml
new file mode 100644
index 00000000000..7ccb8bd1ccc
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/customLogger1.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/customLogger2.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/customLogger2.xml
new file mode 100644
index 00000000000..7065aace82c
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/customLogger2.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/customLogger3.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/customLogger3.xml
new file mode 100644
index 00000000000..02c7e9f120b
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/customLogger3.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/defaultInit.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/defaultInit.xml
new file mode 100644
index 00000000000..62da4c17593
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/defaultInit.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/fallback1.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/fallback1.xml
new file mode 100644
index 00000000000..dc542dffe59
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/fallback1.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/loggerfactory1.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/loggerfactory1.xml
new file mode 100644
index 00000000000..467245002af
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/loggerfactory1.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/smtpAppender1.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/smtpAppender1.xml
new file mode 100644
index 00000000000..1df1a91466d
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/smtpAppender1.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/testReset.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/testReset.xml
new file mode 100644
index 00000000000..185209e64b2
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/testReset.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/throwableRenderer1.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/throwableRenderer1.xml
new file mode 100644
index 00000000000..2496870e0a2
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/throwableRenderer1.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/L7D_en_US.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/L7D_en_US.properties
new file mode 100644
index 00000000000..d5775c616e8
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/L7D_en_US.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.
+#
+
+test=This is the English, US test.
+hello_world=Hello world.
+msg1=This is test number {0} with string argument {1}.
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/L7D_fr.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/L7D_fr.properties
new file mode 100644
index 00000000000..9beb5b74193
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/L7D_fr.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.
+#
+
+test=Ceci est le test en francais pour la France.
+hello_world=Bonjour la France.
+msg1=Ceci est le test numero {0} contenant l''argument {1}.
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/L7D_fr_CH.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/L7D_fr_CH.properties
new file mode 100644
index 00000000000..ef9d824e09a
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/L7D_fr_CH.properties
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+
+test=Ceci est le test en francais pour la p'tite Suisse.
+hello world=Salut le monde.
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/TestLogSFPatterns.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/TestLogSFPatterns.properties
new file mode 100644
index 00000000000..854127a5f7a
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/TestLogSFPatterns.properties
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+Iteration0=Iteration {}
+Hello1=Hello, World
+Malformed=Hello, {.
+Hello2=Hello, {}World
+Hello3=Hello, {}
+Hello4={}, {}.
+Hello5={}{} {}.
+Hello6={}{} {}{}
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/TestLogMFPatterns.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/TestLogMFPatterns.properties
new file mode 100644
index 00000000000..00c3d71833f
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/TestLogMFPatterns.properties
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+Iteration0=Iteration {0}
+Hello1=Hello, World
+Malformed=Hello, {.
+Hello2=Hello, {0}World
+Hello3=Hello, {0}
+Hello4={1}, {0}.
+Hello5={1}{2} {0}.
+Hello6={1}{2} {0}{3}
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/TestLogSFPatterns.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/TestLogSFPatterns.properties
new file mode 100644
index 00000000000..854127a5f7a
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/TestLogSFPatterns.properties
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+Iteration0=Iteration {}
+Hello1=Hello, World
+Malformed=Hello, {.
+Hello2=Hello, {}World
+Hello3=Hello, {}
+Hello4={}, {}.
+Hello5={}{} {}.
+Hello6={}{} {}{}
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/map.log b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/map.log
new file mode 100644
index 00000000000..3ce933f832d
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/map.log
@@ -0,0 +1,3 @@
+INFO org.apache.log4j.rewrite.RewriteAppenderTest - p1: p2: Message 0
+INFO org.apache.log4j.rewrite.RewriteAppenderTest - p1:Hello p2:World {p1=Hello, p2=World, x1=Mundo}
+INFO org.apache.log4j.rewrite.RewriteAppenderTest - p1:Hello p2:World Message 1
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/map.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/map.xml
new file mode 100644
index 00000000000..7cb60b7c850
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/map.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/property.log b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/property.log
new file mode 100644
index 00000000000..9aa2c499137
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/property.log
@@ -0,0 +1,2 @@
+INFO org.apache.log4j.rewrite.RewriteAppenderTest - p1:Hello p2:World Message 0
+INFO org.apache.log4j.rewrite.RewriteAppenderTest - p1:Hola p2:World Message 1
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/property.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/property.xml
new file mode 100644
index 00000000000..13a04f8e2cf
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/property.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/reflection.log b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/reflection.log
new file mode 100644
index 00000000000..da0b52f2dc4
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/reflection.log
@@ -0,0 +1,3 @@
+INFO org.apache.log4j.rewrite.RewriteAppenderTest - p1: p2: Message 0
+INFO org.apache.log4j.rewrite.RewriteAppenderTest - p1: p2:Hello I am bean.
+INFO org.apache.log4j.rewrite.RewriteAppenderTest - p1:Hola p2:Hello Welcome to The Hub
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/reflection.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/reflection.xml
new file mode 100644
index 00000000000..643850b2ad9
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/reflection.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-appenders-custom-1.properties b/log4j-1.2-api/src/test/resources/log4j1-appenders-custom-1.properties
new file mode 100644
index 00000000000..05c7b652144
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-appenders-custom-1.properties
@@ -0,0 +1,38 @@
+#
+# 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.CustomNoopAppender
+log4j.appender.A1.booleanA=true
+log4j.appender.A1.intA=1
+log4j.appender.A1.stringA=A
+#
+log4j.appender.A2=org.apache.log4j.CustomFileAppender
+log4j.appender.A2.booleanA=true
+log4j.appender.A2.intA=1
+log4j.appender.A2.stringA=A
+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
diff --git a/log4j-1.2-api/src/test/resources/log4j1-appenders-custom-2.properties b/log4j-1.2-api/src/test/resources/log4j1-appenders-custom-2.properties
new file mode 100644
index 00000000000..6daf3fec6e4
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-appenders-custom-2.properties
@@ -0,0 +1,38 @@
+#
+# 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.CustomNoopAppender
+log4j.appender.A1.booleanA=false
+log4j.appender.A1.intA=2
+log4j.appender.A1.stringA=B
+#
+log4j.appender.A2=org.apache.log4j.CustomFileAppender
+log4j.appender.A2.booleanA=false
+log4j.appender.A2.intA=2
+log4j.appender.A2.stringA=B
+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
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 00000000000..533346669b8
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-async.properties
@@ -0,0 +1,23 @@
+#
+# 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
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 00000000000..bdc8528a3a3
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-async.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-file-1.properties b/log4j-1.2-api/src/test/resources/log4j1-file-1.properties
new file mode 100644
index 00000000000..96822ec16fe
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-file-1.properties
@@ -0,0 +1,36 @@
+#
+# 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
diff --git a/log4j-1.2-api/src/test/resources/log4j1-file-2.properties b/log4j-1.2-api/src/test/resources/log4j1-file-2.properties
new file mode 100644
index 00000000000..106df246bd7
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-file-2.properties
@@ -0,0 +1,37 @@
+#
+# 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.
+#
+# This is the "same" as log4j1-file-1.properties but with some appender properties changes to test reconfiguration.
+
+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=true
+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=true
+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
diff --git a/log4j-1.2-api/src/test/resources/log4j1-file.xml b/log4j-1.2-api/src/test/resources/log4j1-file.xml
new file mode 100644
index 00000000000..cad9c250eb7
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-file.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000000..d903d02578e
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-list.properties
@@ -0,0 +1,22 @@
+#
+# 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
diff --git a/log4j-1.2-api/src/test/resources/log4j1-list.xml b/log4j-1.2-api/src/test/resources/log4j1-list.xml
new file mode 100644
index 00000000000..b18ad75ea27
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-list.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-mapRewrite.xml b/log4j-1.2-api/src/test/resources/log4j1-mapRewrite.xml
new file mode 100644
index 00000000000..8559058d2b3
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-mapRewrite.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-rewrite.xml b/log4j-1.2-api/src/test/resources/log4j1-rewrite.xml
new file mode 100644
index 00000000000..aefb3d0171f
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-rewrite.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-rolling-properties.properties b/log4j-1.2-api/src/test/resources/log4j1-rolling-properties.properties
new file mode 100644
index 00000000000..2066bbdd1f4
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-rolling-properties.properties
@@ -0,0 +1,33 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+log4j.debug=true
+
+# Properties for substitution
+somelogfile=somefile.log
+maxfilesize=256MB
+maxbackupindex=20
+
+log4j.rootLogger=TRACE, RFA
+
+# Appender configuration with variables
+log4j.appender.RFA=org.apache.log4j.RollingFileAppender
+log4j.appender.RFA.File=${test.directory}/${somelogfile}
+log4j.appender.RFA.MaxFileSize=${maxfilesize}
+log4j.appender.RFA.MaxBackupIndex=${maxbackupindex}
+log4j.appender.RFA.layout=org.apache.log4j.PatternLayout
+log4j.appender.RFA.layout.ConversionPattern=%d{ISO8601} %p %c: %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-rolling-properties.xml b/log4j-1.2-api/src/test/resources/log4j1-rolling-properties.xml
new file mode 100644
index 00000000000..f5b08aa9c99
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-rolling-properties.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-socket-xml-layout.properties b/log4j-1.2-api/src/test/resources/log4j1-socket-xml-layout.properties
new file mode 100644
index 00000000000..35ceb970baf
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-socket-xml-layout.properties
@@ -0,0 +1,24 @@
+#
+# 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.rootLogger=DEBUG,socket
+log4j.appender.socket=org.apache.log4j.net.SocketAppender
+log4j.appender.socket.remoteHost=localhost
+log4j.appender.socket.port=9999
+log4j.appender.socket.reconnectionDelay=100
+log4j.appender.socket.layout=org.apache.log4j.xml.XMLLayout
+log4j.appender.socket.Threshold=DEBUG
diff --git a/log4j-1.2-api/src/test/resources/log4j1-socket.properties b/log4j-1.2-api/src/test/resources/log4j1-socket.properties
new file mode 100644
index 00000000000..3072b46b17a
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-socket.properties
@@ -0,0 +1,25 @@
+#
+# 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.rootLogger=DEBUG,socket
+log4j.appender.socket=org.apache.log4j.net.SocketAppender
+log4j.appender.socket.remoteHost=localhost
+log4j.appender.socket.port=9999
+log4j.appender.socket.reconnectionDelay=100
+log4j.appender.socket.layout=org.apache.log4j.PatternLayout
+log4j.appender.socket.layout.conversionPattern=Main[%pid] :%t: %c %-4p - %m\n
+log4j.appender.socket.Threshold=DEBUG
diff --git a/log4j-1.2-api/src/test/resources/log4j1-socket.xml b/log4j-1.2-api/src/test/resources/log4j1-socket.xml
new file mode 100644
index 00000000000..eca3a0fe2f8
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-socket.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-default.properties b/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-default.properties
new file mode 100644
index 00000000000..2bfda78e8d5
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-default.properties
@@ -0,0 +1,25 @@
+#
+# 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.rootLogger=DEBUG,syslog
+log4j.appender.syslog=org.apache.log4j.net.SyslogAppender
+log4j.appender.syslog.Threshold=DEBUG
+log4j.appender.syslog.syslogHost=localhost:9999
+log4j.appender.syslog.header=true
+log4j.appender.syslog.Facility=LOCAL3
+log4j.appender.syslog.layout=org.apache.log4j.PatternLayout
+log4j.appender.syslog.layout.conversionPattern=Main[%pid] :%t: %c %-4p - %m\n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-tcp.properties b/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-tcp.properties
new file mode 100644
index 00000000000..9a95669b823
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-tcp.properties
@@ -0,0 +1,26 @@
+#
+# 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.rootLogger=DEBUG,syslog
+log4j.appender.syslog=org.apache.log4j.net.SyslogAppender
+log4j.appender.syslog.Threshold=DEBUG
+log4j.appender.syslog.syslogHost=localhost:9999
+log4j.appender.syslog.protocol=TCP
+log4j.appender.syslog.header=true
+log4j.appender.syslog.Facility=LOCAL3
+log4j.appender.syslog.layout=org.apache.log4j.PatternLayout
+log4j.appender.syslog.layout.conversionPattern=Main[%pid] :%t: %c %-4p - %m\n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-tcp.xml b/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-tcp.xml
new file mode 100644
index 00000000000..47c3768709d
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-tcp.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-udp.properties b/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-udp.properties
new file mode 100644
index 00000000000..b7ab6a7937e
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-udp.properties
@@ -0,0 +1,26 @@
+#
+# 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.rootLogger=DEBUG,syslog
+log4j.appender.syslog=org.apache.log4j.net.SyslogAppender
+log4j.appender.syslog.Threshold=DEBUG
+log4j.appender.syslog.syslogHost=localhost:9999
+log4j.appender.syslog.protocol=UDP
+log4j.appender.syslog.header=true
+log4j.appender.syslog.Facility=LOCAL3
+log4j.appender.syslog.layout=org.apache.log4j.PatternLayout
+log4j.appender.syslog.layout.conversionPattern=Main[%pid] :%t: %c %-4p - %m\n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-udp.xml b/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-udp.xml
new file mode 100644
index 00000000000..279ec94cb9e
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-udp.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-syslog.xml b/log4j-1.2-api/src/test/resources/log4j1-syslog.xml
new file mode 100644
index 00000000000..4fd96a8b180
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-syslog.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j2-config.xml b/log4j-1.2-api/src/test/resources/log4j2-config.xml
index 2427af82cbe..11040d3aa39 100644
--- a/log4j-1.2-api/src/test/resources/log4j2-config.xml
+++ b/log4j-1.2-api/src/test/resources/log4j2-config.xml
@@ -14,7 +14,6 @@
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.
-
-->
diff --git a/log4j-1.2-api/src/test/resources/logWithMDC.xml b/log4j-1.2-api/src/test/resources/logWithMDC.xml
index 1fe848295fe..97f3b86ed75 100644
--- a/log4j-1.2-api/src/test/resources/logWithMDC.xml
+++ b/log4j-1.2-api/src/test/resources/logWithMDC.xml
@@ -14,7 +14,6 @@
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.
-
-->
@@ -37,4 +36,4 @@
-
\ No newline at end of file
+
diff --git a/log4j-api-java9/pom.xml b/log4j-api-java9/pom.xml
deleted file mode 100644
index 183ad14dd3c..00000000000
--- a/log4j-api-java9/pom.xml
+++ /dev/null
@@ -1,153 +0,0 @@
-
-
-
- 4.0.0
-
- org.apache.logging.log4j
- log4j
- 2.10.1-SNAPSHOT
- ../
-
- log4j-api-java9
- pom
- Apache Log4j API Java 9 support
- The Apache Log4j API (Java 9)
-
- ${basedir}/..
- API Documentation
- /api
-
-
-
- junit
- junit
- test
-
-
- org.apache.maven
- maven-core
- test
-
-
-
-
-
- org.apache.maven.plugins
- maven-toolchains-plugin
- 1.1
-
-
-
- toolchain
-
-
-
-
-
-
- 9
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
-
- default-compile
- compile
-
- compile
-
-
-
- default-test-compile
- test-compile
-
- testCompile
-
-
-
-
- 9
- 9
- 9
- none
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
-
- 2.13
-
-
- test
- test
-
- test
-
-
-
-
-
- true
-
- 2C
- true
-
- **/Test*.java
- **/*Test.java
-
-
- **/*FuncTest.java
-
-
-
-
- maven-assembly-plugin
-
-
- zip
- package
-
- single
-
-
- log4j-api-java9-${project.version}
- false
-
- src/assembly/java9.xml
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-deploy-plugin
- ${deploy.plugin.version}
-
- true
-
-
-
-
-
-
diff --git a/log4j-api-java9/src/assembly/java9.xml b/log4j-api-java9/src/assembly/java9.xml
deleted file mode 100644
index 3e595612db4..00000000000
--- a/log4j-api-java9/src/assembly/java9.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
- src
-
- zip
-
- /
-
-
- ${project.build.outputDirectory}
- /classes/META-INF/versions/9
-
- **/*.class
-
-
- module-info.class
- **/Dummy.class
- **/spi/Provider.class
- **/util/PropertySource.class
- **/message/ThreadDumpMessage.class
- **/message/ThreadDumpMessage$ThreadInfoFactory.class
-
-
-
- ${project.build.outputDirectory}
- /classes
-
- module-info.class
-
-
-
-
diff --git a/log4j-api-java9/src/main/java/module-info.java b/log4j-api-java9/src/main/java/module-info.java
deleted file mode 100644
index 3cb22e02da6..00000000000
--- a/log4j-api-java9/src/main/java/module-info.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-module org.apache.logging.log4j {
- exports org.apache.logging.log4j;
- exports org.apache.logging.log4j.message;
- exports org.apache.logging.log4j.simple;
- exports org.apache.logging.log4j.spi;
- exports org.apache.logging.log4j.status;
- exports org.apache.logging.log4j.util;
-
- uses org.apache.logging.log4j.spi.Provider;
- uses org.apache.logging.log4j.util.PropertySource;
- uses org.apache.logging.log4j.message.ThreadDumpMessage.ThreadInfoFactory;
-}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/Dummy.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/Dummy.java
deleted file mode 100644
index 24012e6ae3a..00000000000
--- a/log4j-api-java9/src/main/java/org/apache/logging/log4j/Dummy.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j;
-
-/**
- * This is a dummy class and is only here to allow module-info.java to compile. It will not
- * be copied into the log4j-api module.
- */
-public class Dummy {
-}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/log4j/util/PropertySource.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/log4j/util/PropertySource.java
deleted file mode 100644
index f94d10a9b28..00000000000
--- a/log4j-api-java9/src/main/java/org/apache/logging/log4j/log4j/util/PropertySource.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.util;
-
-/**
- * This is a dummy class and is only here to allow module-info.java to compile. It will not
- * be copied into the log4j-api module.
- */
-public class PropertySource {
-}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/message/Dummy.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/message/Dummy.java
deleted file mode 100644
index 082e36ea3e7..00000000000
--- a/log4j-api-java9/src/main/java/org/apache/logging/log4j/message/Dummy.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.message;
-
-/**
- * This is a dummy class and is only here to allow module-info.java to compile. It will not
- * be copied into the log4j-api module.
- */
-public class Dummy {
-}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java
deleted file mode 100644
index 8b1af5dd565..00000000000
--- a/log4j-api-java9/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.message;
-
-/**
- * This is a dummy class and is only here to allow module-info.java to compile. It will not
- * be copied into the log4j-api module.
- */
-public class ThreadDumpMessage {
- public static interface ThreadInfoFactory {
-
- }
-}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/simple/Dummy.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/simple/Dummy.java
deleted file mode 100644
index c3a24e282bf..00000000000
--- a/log4j-api-java9/src/main/java/org/apache/logging/log4j/simple/Dummy.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.simple;
-
-/**
- * This is a dummy class and is only here to allow module-info.java to compile. It will not
- * be copied into the log4j-api module.
- */
-public class Dummy {
-}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/spi/Provider.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/spi/Provider.java
deleted file mode 100644
index 65b86380e9e..00000000000
--- a/log4j-api-java9/src/main/java/org/apache/logging/log4j/spi/Provider.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.spi;
-
-/**
- * This is a dummy class and is only here to allow module-info.java to compile. It will not
- * be copied into the log4j-api module.
- */
-public class Provider {
-}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/status/Dummy.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/status/Dummy.java
deleted file mode 100644
index d53dc43e753..00000000000
--- a/log4j-api-java9/src/main/java/org/apache/logging/log4j/status/Dummy.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.status;
-
-/**
- * This is a dummy class and is only here to allow module-info.java to compile. It will not
- * be copied into the log4j-api module.
- */
-public class Dummy {
-}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/ProcessIdUtil.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/ProcessIdUtil.java
deleted file mode 100644
index e467413db25..00000000000
--- a/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/ProcessIdUtil.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.util;
-
-public class ProcessIdUtil {
-
- public static final String DEFAULT_PROCESSID = "-";
-
- public static String getProcessId() {
- try {
- return Long.toString(ProcessHandle.current().pid());
- } catch(Exception ex) {
- return DEFAULT_PROCESSID;
- }
- }
-}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/StackLocator.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/StackLocator.java
deleted file mode 100644
index b0661d0fe14..00000000000
--- a/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/StackLocator.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.util;
-
-import java.util.List;
-import java.util.Stack;
-import java.util.stream.Collectors;
-
-/**
- * Consider this class private. Determines the caller's class.
- */
-public class StackLocator {
-
- private final static StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
-
- private final static StackWalker stackWalker = StackWalker.getInstance();
-
- private final static StackLocator INSTANCE = new StackLocator();
-
-
- public static StackLocator getInstance() {
- return INSTANCE;
- }
-
- private StackLocator() {
- }
-
- public Class> getCallerClass(final String fqcn) {
- return getCallerClass(fqcn, "");
- }
-
- public Class> getCallerClass(final String fqcn, final String pkg) {
- return walker.walk(s -> s.dropWhile(f -> !f.getClassName().equals(fqcn)).
- dropWhile(f -> f.getClassName().equals(fqcn)).dropWhile(f -> !f.getClassName().startsWith(pkg)).
- findFirst()).map(StackWalker.StackFrame::getDeclaringClass).orElse(null);
- }
-
- public Class> getCallerClass(final Class> anchor) {
- return walker.walk(s -> s.dropWhile(f -> !f.getDeclaringClass().equals(anchor)).
- dropWhile(f -> f.getDeclaringClass().equals(anchor)).findFirst()).
- map(StackWalker.StackFrame::getDeclaringClass).orElse(null);
- }
-
- public Class> getCallerClass(final int depth) {
- ;
- return walker.walk(s -> s.skip(depth).findFirst()).map(StackWalker.StackFrame::getDeclaringClass).orElse(null);
- }
-
- public Stack> getCurrentStackTrace() {
- Stack> stack = new Stack>();
- List> classes = walker.walk(s -> s.map(f -> f.getDeclaringClass()).collect(Collectors.toList()));
- stack.addAll(classes);
- return stack;
- }
-
- public StackTraceElement calcLocation(final String fqcnOfLogger) {
- return stackWalker.walk(
- s -> s.dropWhile(f -> !f.getClassName().equals(fqcnOfLogger)) // drop the top frames until we reach the logger
- .dropWhile(f -> f.getClassName().equals(fqcnOfLogger)) // drop the logger frames
- .findFirst())
- .get()
- .toStackTraceElement();
- }
-
- public StackTraceElement getStackTraceElement(final int depth) {
- return stackWalker.walk(s -> s.skip(depth).findFirst()).get().toStackTraceElement();
- }
-}
diff --git a/log4j-api-java9/src/test/java/org/apache/logging/log4j/util/ProcessIdUtilTest.java b/log4j-api-java9/src/test/java/org/apache/logging/log4j/util/ProcessIdUtilTest.java
deleted file mode 100644
index 6b5368f5992..00000000000
--- a/log4j-api-java9/src/test/java/org/apache/logging/log4j/util/ProcessIdUtilTest.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.util;
-
-import org.junit.Test;
-import static org.junit.Assert.assertFalse;
-
-public class ProcessIdUtilTest {
-
- @Test
- public void processIdTest() throws Exception {
- String processId = ProcessIdUtil.getProcessId();
- assertFalse("ProcessId is default", processId.equals(ProcessIdUtil.DEFAULT_PROCESSID));
- }
-}
diff --git a/log4j-api-java9/src/test/java/org/apache/logging/log4j/util/StackLocatorTest.java b/log4j-api-java9/src/test/java/org/apache/logging/log4j/util/StackLocatorTest.java
deleted file mode 100644
index 77396c92fb3..00000000000
--- a/log4j-api-java9/src/test/java/org/apache/logging/log4j/util/StackLocatorTest.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.util;
-
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.BlockJUnit4ClassRunner;
-import org.junit.runners.ParentRunner;
-
-import java.util.Stack;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertSame;
-
-@RunWith(BlockJUnit4ClassRunner.class)
-public class StackLocatorTest {
-
- private static StackLocator stackLocator;
-
- @BeforeClass
- public static void setupClass() {
-
- stackLocator = StackLocator.getInstance();
- }
-
- @Test
- public void testGetCallerClass() throws Exception {
- final Class> expected = StackLocatorTest.class;
- final Class> actual = stackLocator.getCallerClass(1);
- assertSame(expected, actual);
- }
-
- @Test
- public void testGetCallerClassNameViaStackTrace() throws Exception {
- final Class> expected = StackLocatorTest.class;
- final Class> actual = Class.forName(new Throwable().getStackTrace()[0].getClassName());
- assertSame(expected, actual);
- }
-
- @Test
- public void testGetCurrentStackTrace() throws Exception {
- final Stack> classes = stackLocator.getCurrentStackTrace();
- final Stack> reversed = new Stack<>();
- reversed.ensureCapacity(classes.size());
- while (!classes.empty()) {
- reversed.push(classes.pop());
- }
- while (reversed.peek() != StackLocator.class) {
- reversed.pop();
- }
- reversed.pop(); // ReflectionUtil
- assertSame(StackLocatorTest.class, reversed.pop());
- }
-
- @Test
- public void testGetCallerClassViaName() throws Exception {
- final Class> expected = BlockJUnit4ClassRunner.class;
- final Class> actual = stackLocator.getCallerClass("org.junit.runners.ParentRunner");
- // if this test fails in the future, it's probably because of a JUnit upgrade; check the new stack trace and
- // update this test accordingly
- assertSame(expected, actual);
- }
-
- @Test
- public void testGetCallerClassViaAnchorClass() throws Exception {
- final Class> expected = BlockJUnit4ClassRunner.class;
- final Class> actual = stackLocator.getCallerClass(ParentRunner.class);
- // if this test fails in the future, it's probably because of a JUnit upgrade; check the new stack trace and
- // update this test accordingly
- assertSame(expected, actual);
- }
-
- @Test
- public void testLocateClass() {
- ClassLocator locator = new ClassLocator();
- Class> clazz = locator.locateClass();
- assertNotNull("Could not locate class", clazz);
- assertEquals("Incorrect class", this.getClass(), clazz);
- }
-
- private final class Foo {
-
- private StackTraceElement foo() {
- return new Bar().bar();
- }
-
- }
-
- private final class Bar {
-
- private StackTraceElement bar() {
- return baz();
- }
-
- private StackTraceElement baz() {
- return quux();
- }
-
- }
-
- private StackTraceElement quux() {
- return stackLocator.calcLocation("org.apache.logging.log4j.util.StackLocatorTest$Bar");
- }
-
- @Test
- public void testCalcLocation() {
- /*
- * We are setting up a stack trace that looks like:
- * - org.apache.logging.log4j.util.StackLocatorTest#quux(line:118)
- * - org.apache.logging.log4j.util.StackLocatorTest$Bar#baz(line:112)
- * - org.apache.logging.log4j.util.StackLocatorTest$Bar#bar(line:108)
- * - org.apache.logging.log4j.util.StackLocatorTest$Foo(line:100)
- *
- * We are pretending that org.apache.logging.log4j.util.StackLocatorTest$Bar is the logging class, and
- * org.apache.logging.log4j.util.StackLocatorTest$Foo is where the log line emanated.
- */
- final StackTraceElement element = new Foo().foo();
- assertEquals("org.apache.logging.log4j.util.StackLocatorTest$Foo", element.getClassName());
- assertEquals(100, element.getLineNumber());
- }
-
- class ClassLocator {
-
- public Class> locateClass() {
- return stackLocator.getCallerClass(ClassLocator.class);
- }
- }
-
-}
diff --git a/log4j-api-test/pom.xml b/log4j-api-test/pom.xml
new file mode 100644
index 00000000000..da6ffa1860a
--- /dev/null
+++ b/log4j-api-test/pom.xml
@@ -0,0 +1,338 @@
+
+
+
+ 4.0.0
+
+ org.apache.logging.log4j
+ log4j
+ 3.0.0-SNAPSHOT
+
+ log4j-api-test
+ jar
+ Apache Log4j API Tests
+ The Apache Log4j API Test
+
+ ${basedir}/..
+ API Documentation
+ /api
+ true
+
+
+
+ org.apache.logging.log4j
+ log4j-api
+
+
+ org.assertj
+ assertj-core
+
+
+ org.apache.commons
+ commons-lang3
+
+
+ org.hamcrest
+ hamcrest
+
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+ junit
+ junit
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+
+
+ org.junit-pioneer
+ junit-pioneer
+
+
+ org.junit.platform
+ junit-platform-commons
+
+
+ org.junit.platform
+ junit-platform-launcher
+
+
+ org.opentest4j
+ opentest4j
+
+
+ uk.org.webcompere
+ system-stubs-core
+ test
+
+
+ uk.org.webcompere
+ system-stubs-jupiter
+ test
+
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+
+ add-source
+
+
+
+ ${project.basedir}/src/main/module
+
+
+
+
+
+
+ org.apache.felix
+ maven-bundle-plugin
+
+
+ org.apache.logging.log4j.*
+
+ sun.reflect;resolution:=optional,
+ *
+
+ org.apache.logging.log4j.util.Activator
+ <_fixupmessages>"Classes found in the wrong directory";is:=warning
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-clean-plugin
+
+
+ remove-module-info
+ process-classes
+
+ clean
+
+
+ true
+
+
+ ${project.build.outputDirectory}
+
+ module-info.class
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+ com.google.errorprone
+ error_prone_core
+ ${errorprone.version}
+
+
+
+
+
+ compile-module-info
+
+ compile
+
+ prepare-package
+
+
+ ${project.basedir}/src/main/module
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+
+
+ false
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ 1
+ true
+ true
+ random
+
+ dynamic
+ 1
+ true
+ same_thread
+ concurrent
+
+ true
+ org.junit.jupiter.api.ClassOrderer$Random
+
+ false
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-changes-plugin
+
+
+
+ changes-report
+
+
+
+
+ %URL%/%ISSUE%
+ true
+ API
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+
+
+ ${log4jParentDir}/checkstyle.xml
+ ${log4jParentDir}/checkstyle-suppressions.xml
+ false
+ basedir=${basedir}
+ licensedir=${log4jParentDir}/checkstyle-header.txt
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+ <p align="center">Copyright © {inceptionYear}-{currentYear} {organizationName}. All Rights Reserved.<br />
+ Apache Logging, Apache Log4j, Log4j, Apache, the Apache feather logo, the Apache Logging project logo,
+ and the Apache Log4j logo are trademarks of The Apache Software Foundation.</p>
+
+ false
+ false
+ true
+
+ https://www.osgi.org/javadoc/r4v43/core/
+
+ 8
+
+
+
+ non-aggregate
+
+ javadoc
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jxr-plugin
+
+
+ non-aggregate
+
+ jxr
+
+
+
+ aggregate
+
+ aggregate
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-pmd-plugin
+
+ ${maven.compiler.target}
+
+
+
+ com.github.spotbugs
+ spotbugs-maven-plugin
+
+
+
+
+
+ parallel-tests
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+
+
+ junit.jupiter.execution.parallel.enabled = true
+ junit.jupiter.execution.parallel.mode.default = concurrent
+
+
+ balanced
+ 1C
+ true
+
+
+
+
+
+
+
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/AbstractSerializationTest.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/AbstractSerializationTest.java
new file mode 100644
index 00000000000..06b38775cb9
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/AbstractSerializationTest.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.test;
+
+import static org.apache.logging.log4j.test.SerializableMatchers.serializesRoundTrip;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.io.Serializable;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestInstance.Lifecycle;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * Subclasses tests {@link Serializable} objects.
+ */
+@TestInstance(Lifecycle.PER_CLASS)
+public abstract class AbstractSerializationTest {
+
+ protected abstract Stream data();
+
+ @ParameterizedTest
+ @MethodSource("data")
+ public void testSerializationRoundtripEquals(Serializable serializable) {
+ assertThat(serializable, serializesRoundTrip(serializable));
+ }
+
+ @ParameterizedTest
+ @MethodSource("data")
+ public void testSerializationRoundtripNoException(Serializable serializable) {
+ assertThat(serializable, serializesRoundTrip());
+ }
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/SerializableMatchers.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/SerializableMatchers.java
similarity index 98%
rename from log4j-api/src/test/java/org/apache/logging/log4j/SerializableMatchers.java
rename to log4j-api-test/src/main/java/org/apache/logging/log4j/test/SerializableMatchers.java
index 7545b964a96..d98359d3d99 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/SerializableMatchers.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/SerializableMatchers.java
@@ -14,7 +14,7 @@
* See the license for the specific language governing permissions and
* limitations under the license.
*/
-package org.apache.logging.log4j;
+package org.apache.logging.log4j.test;
import java.io.Serializable;
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/TestLogger.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestLogger.java
similarity index 90%
rename from log4j-api/src/test/java/org/apache/logging/log4j/TestLogger.java
rename to log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestLogger.java
index 5f7abb7dbda..5b9c5b6ac92 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/TestLogger.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestLogger.java
@@ -14,7 +14,7 @@
* See the license for the specific language governing permissions and
* limitations under the license.
*/
-package org.apache.logging.log4j;
+package org.apache.logging.log4j.test;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
@@ -22,6 +22,9 @@
import java.util.List;
import java.util.Map;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.MessageFactory;
import org.apache.logging.log4j.spi.AbstractLogger;
@@ -53,6 +56,12 @@ public List getEntries() {
@Override
public void logMessage(final String fqcn, final Level level, final Marker marker, final Message msg, final Throwable throwable) {
+ log(level, marker, fqcn, null, msg, throwable);
+ }
+
+ @Override
+ protected void log(final Level level, final Marker marker, final String fqcn, final StackTraceElement location,
+ final Message message, final Throwable throwable) {
final StringBuilder sb = new StringBuilder();
if (marker != null) {
sb.append(marker);
@@ -60,14 +69,18 @@ public void logMessage(final String fqcn, final Level level, final Marker marker
sb.append(' ');
sb.append(level.toString());
sb.append(' ');
- sb.append(msg.getFormattedMessage());
+ if (location != null) {
+ sb.append(location.toString());
+ sb.append(' ');
+ }
+ sb.append(message.getFormattedMessage());
final Map mdc = ThreadContext.getImmutableContext();
if (mdc.size() > 0) {
sb.append(' ');
sb.append(mdc.toString());
sb.append(' ');
}
- final Object[] params = msg.getParameters();
+ final Object[] params = message.getParameters();
Throwable t;
if (throwable == null && params != null && params.length > 0 && params[params.length - 1] instanceof Throwable) {
t = (Throwable) params[params.length - 1];
@@ -81,7 +94,6 @@ public void logMessage(final String fqcn, final Level level, final Marker marker
sb.append(baos.toString());
}
list.add(sb.toString());
- //System.out.println(sb.toString());
}
@Override
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/TestLoggerContext.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestLoggerContext.java
similarity index 92%
rename from log4j-api/src/test/java/org/apache/logging/log4j/TestLoggerContext.java
rename to log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestLoggerContext.java
index 7ca2fd69e79..906a2335b08 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/TestLoggerContext.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestLoggerContext.java
@@ -14,20 +14,20 @@
* See the license for the specific language governing permissions and
* limitations under the license.
*/
-package org.apache.logging.log4j;
+package org.apache.logging.log4j.test;
-import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import org.apache.logging.log4j.message.MessageFactory;
-import org.apache.logging.log4j.spi.LoggerContext;
import org.apache.logging.log4j.spi.ExtendedLogger;
+import org.apache.logging.log4j.spi.LoggerContext;
/**
*
*/
public class TestLoggerContext implements LoggerContext {
- private final Map map = new HashMap<>();
+ private final Map map = new ConcurrentHashMap<>();
@Override
public ExtendedLogger getLogger(final String name) {
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/TestLoggerContextFactory.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestLoggerContextFactory.java
similarity index 92%
rename from log4j-api/src/test/java/org/apache/logging/log4j/TestLoggerContextFactory.java
rename to log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestLoggerContextFactory.java
index 847fedd81c5..08185527439 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/TestLoggerContextFactory.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestLoggerContextFactory.java
@@ -14,7 +14,7 @@
* See the license for the specific language governing permissions and
* limitations under the license.
*/
-package org.apache.logging.log4j;
+package org.apache.logging.log4j.test;
import java.net.URI;
@@ -43,4 +43,9 @@ public LoggerContext getContext(final String fqcn, final ClassLoader loader, fin
@Override
public void removeContext(final LoggerContext context) {
}
+
+ @Override
+ public boolean isClassLoaderDependent() {
+ return false;
+ }
}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestProperties.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestProperties.java
new file mode 100644
index 00000000000..49bb1e58775
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestProperties.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.test;
+
+/**
+ * A container for per-test properties.
+ */
+public interface TestProperties {
+
+ String getProperty(final String key);
+
+ boolean containsProperty(final String key);
+
+ void setProperty(final String key, final String value);
+
+ default void setProperty(final String key, final boolean value) {
+ setProperty(key, value ? "true" : "false");
+ }
+
+ default void setProperty(final String key, final int value) {
+ setProperty(key, Integer.toString(value));
+ }
+
+ void clearProperty(final String key);
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextHolder.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/ThreadContextHolder.java
similarity index 96%
rename from log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextHolder.java
rename to log4j-api-test/src/main/java/org/apache/logging/log4j/test/ThreadContextHolder.java
index 4c85bde3671..35182a4524f 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextHolder.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/ThreadContextHolder.java
@@ -15,10 +15,11 @@
* limitations under the license.
*/
-package org.apache.logging.log4j;
+package org.apache.logging.log4j.test;
import java.util.Map;
+import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.ThreadContext.ContextStack;
/**
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextUtilityClass.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/ThreadContextUtilityClass.java
similarity index 83%
rename from log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextUtilityClass.java
rename to log4j-api-test/src/main/java/org/apache/logging/log4j/test/ThreadContextUtilityClass.java
index c6e1826fad4..d39dbdc4406 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextUtilityClass.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/ThreadContextUtilityClass.java
@@ -14,19 +14,23 @@
* See the license for the specific language governing permissions and
* limitations under the license.
*/
-package org.apache.logging.log4j;
+package org.apache.logging.log4j.test;
import java.util.Map;
-import org.apache.logging.log4j.Timer;
import org.apache.logging.log4j.ThreadContext;
-import static org.junit.Assert.*;
-
+import org.apache.logging.log4j.util.Timer;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
public class ThreadContextUtilityClass {
- public static void perfTest() throws Exception {
+ public static void perfTest() {
ThreadContext.clearMap();
final Timer complete = new Timer("ThreadContextTest");
complete.start();
@@ -92,13 +96,13 @@ public static void testGetImmutableContextReturnsImmutableMapIfNonEmpty() {
ThreadContext.clearMap();
ThreadContext.put("key", "val");
final Map immutable = ThreadContext.getImmutableContext();
- immutable.put("otherkey", "otherval");
+ assertThrows(UnsupportedOperationException.class, () -> immutable.put("otherkey", "otherval"));
}
public static void testGetImmutableContextReturnsImmutableMapIfEmpty() {
ThreadContext.clearMap();
final Map immutable = ThreadContext.getImmutableContext();
- immutable.put("otherkey", "otherval");
+ assertThrows(UnsupportedOperationException.class, () -> immutable.put("otherkey", "otherval"));
}
public static void testGetImmutableStackReturnsEmptyStackIfEmpty() {
@@ -113,4 +117,8 @@ public static void testPut() {
ThreadContext.put("testKey", "testValue");
assertEquals("testValue", ThreadContext.get("testKey"));
}
+
+ public static void reset() {
+ ThreadContext.init();
+ }
}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/AbstractExternalFileCleaner.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/AbstractExternalFileCleaner.java
new file mode 100644
index 00000000000..c144b356ef8
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/AbstractExternalFileCleaner.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.test.junit;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Assert;
+import org.junit.rules.ExternalResource;
+
+/**
+ * This class should not perform logging using Log4j to avoid accidentally
+ * loading or re-loading Log4j configurations.
+ */
+public abstract class AbstractExternalFileCleaner extends ExternalResource {
+
+ protected static final String CLEANER_MARKER = "CLEANER";
+
+ private static final int SLEEP_RETRY_MILLIS = 200;
+ private final boolean cleanAfter;
+ private final boolean cleanBefore;
+ private final Set files;
+ private final int maxTries;
+ private final PrintStream printStream;
+
+ public AbstractExternalFileCleaner(final boolean before, final boolean after, final int maxTries,
+ final PrintStream logger, final File... files) {
+ this.cleanBefore = before;
+ this.cleanAfter = after;
+ this.maxTries = maxTries;
+ this.files = new HashSet<>(files.length);
+ this.printStream = logger;
+ for (final File file : files) {
+ this.files.add(file.toPath());
+ }
+ }
+
+ public AbstractExternalFileCleaner(final boolean before, final boolean after, final int maxTries,
+ final PrintStream logger, final Path... files) {
+ this.cleanBefore = before;
+ this.cleanAfter = after;
+ this.maxTries = maxTries;
+ this.printStream = logger;
+ this.files = new HashSet<>(Arrays.asList(files));
+ }
+
+ public AbstractExternalFileCleaner(final boolean before, final boolean after, final int maxTries,
+ final PrintStream logger, final String... fileNames) {
+ this.cleanBefore = before;
+ this.cleanAfter = after;
+ this.maxTries = maxTries;
+ this.printStream = logger;
+ this.files = new HashSet<>(fileNames.length);
+ for (final String fileName : fileNames) {
+ this.files.add(Paths.get(fileName));
+ }
+ }
+
+ @Override
+ protected void after() {
+ if (cleanAfter()) {
+ this.clean();
+ }
+ }
+
+ @Override
+ protected void before() {
+ if (cleanBefore()) {
+ this.clean();
+ }
+ }
+
+ protected void clean() {
+ final Map failures = new HashMap<>();
+ // Clean and gather failures
+ for (final Path path : getPaths()) {
+ if (Files.exists(path)) {
+ for (int i = 0; i < getMaxTries(); i++) {
+ try {
+ if (clean(path, i)) {
+ if (failures.containsKey(path)) {
+ failures.remove(path);
+ }
+ break;
+ }
+ } catch (final IOException e) {
+ println(CLEANER_MARKER + ": Caught exception cleaning: " + this);
+ printStackTrace(e);
+ // We will try again.
+ failures.put(path, e);
+ }
+ try {
+ Thread.sleep(SLEEP_RETRY_MILLIS);
+ } catch (final InterruptedException ignored) {
+ // ignore
+ }
+ }
+ }
+ }
+ // Fail on failures
+ if (failures.size() > 0) {
+ final StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for (final Map.Entry failure : failures.entrySet()) {
+ failure.getValue().printStackTrace();
+ if (!first) {
+ sb.append(", ");
+ }
+ sb.append(failure.getKey()).append(" failed with ").append(failure.getValue());
+ first = false;
+ }
+ Assert.fail(sb.toString());
+ }
+ }
+
+ protected abstract boolean clean(Path path, int tryIndex) throws IOException;
+
+ public boolean cleanAfter() {
+ return cleanAfter;
+ }
+
+ public boolean cleanBefore() {
+ return cleanBefore;
+ }
+
+ public int getMaxTries() {
+ return maxTries;
+ }
+
+ public Set getPaths() {
+ return files;
+ }
+
+ public PrintStream getPrintStream() {
+ return printStream;
+ }
+
+ protected void printf(final String format, final Object... args) {
+ if (printStream != null) {
+ printStream.printf(format, args);
+ }
+ }
+
+ protected void println(final String msg) {
+ if (printStream != null) {
+ printStream.println(msg);
+ }
+ }
+
+ protected void printStackTrace(final Throwable t) {
+ if (printStream != null) {
+ t.printStackTrace(printStream);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " [files=" + files + ", cleanAfter=" + cleanAfter + ", cleanBefore="
+ + cleanBefore + "]";
+ }
+
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/AbstractFileCleaner.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/AbstractFileCleaner.java
new file mode 100644
index 00000000000..351361d0b9d
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/AbstractFileCleaner.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.test.junit;
+
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.LockSupport;
+import java.util.stream.Collectors;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+abstract class AbstractFileCleaner implements BeforeEachCallback {
+
+ private static final int MAX_TRIES = Integer.getInteger("log4j2.junit.fileCleanerMaxTries", 10);
+
+ private static final int SLEEP_PERIOD_MILLIS = Integer.getInteger("log4j2.junit.fileCleanerSleepPeriodMillis", 200);
+ private static final int SLEEP_BASE_PERIOD_MILLIS = SLEEP_PERIOD_MILLIS / MAX_TRIES;
+
+ @Override
+ public void beforeEach(final ExtensionContext context) throws Exception {
+ clean(context);
+ }
+
+ private void clean(final ExtensionContext context) {
+ final Collection paths = getPathsForTest(context);
+ if (paths.isEmpty()) {
+ return;
+ }
+ final Map failures = new ConcurrentHashMap<>();
+ for (final Path path : paths) {
+ if (Files.exists(path)) {
+ for (int i = 0, sleepMillis = SLEEP_BASE_PERIOD_MILLIS; i < MAX_TRIES; i++, sleepMillis <<= 1) {
+ try {
+ if (delete(path)) {
+ failures.remove(path);
+ break;
+ }
+ } catch (final IOException e) {
+ failures.put(path, e);
+ }
+ LockSupport.parkNanos(path, TimeUnit.MILLISECONDS.toNanos(sleepMillis));
+ if (Thread.interrupted()) {
+ failures.put(path, new InterruptedIOException());
+ Thread.currentThread().interrupt();
+ break;
+ }
+ }
+ }
+ }
+ if (!failures.isEmpty()) {
+ final String message = failures.entrySet().stream()
+ .map(e -> e.getKey() + " failed with " + e.getValue())
+ .collect(Collectors.joining(", "));
+ fail(message);
+ }
+ }
+
+ abstract Collection getPathsForTest(final ExtensionContext context);
+
+ abstract boolean delete(final Path path) throws IOException;
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ClassMatchers.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ClassMatchers.java
new file mode 100644
index 00000000000..d83ee72fd23
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ClassMatchers.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.test.junit;
+
+import org.apache.logging.log4j.util.LoaderUtil;
+import org.hamcrest.CustomTypeSafeMatcher;
+import org.hamcrest.Matcher;
+
+public final class ClassMatchers {
+
+ private static final Matcher LOADABLE_CLASS_NAME = new CustomTypeSafeMatcher("a loadable class name") {
+ @Override
+ protected boolean matchesSafely(final String item) {
+ return LoaderUtil.isClassAvailable(item);
+ }
+ };
+
+ public static Matcher loadableClassName() {
+ return LOADABLE_CLASS_NAME;
+ }
+
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/CleanFiles.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/CleanFiles.java
new file mode 100644
index 00000000000..1782fa02cc7
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/CleanFiles.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.test.junit;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+/**
+ * A JUnit test rule to automatically delete files after a test is run.
+ *
+ * For example:
+ *
+ *
+ *
+ * @Rule
+ * public CleanFiles files = new CleanFiles("path/to/file.txt");
+ *
+ *
+ * This class should not perform logging using Log4j to avoid accidentally
+ * loading or re-loading Log4j configurations.
+ *
+ *
+ */
+public class CleanFiles extends AbstractExternalFileCleaner {
+ private static final int MAX_TRIES = 10;
+
+ public CleanFiles(final boolean before, final boolean after, final int maxTries, final File... files) {
+ super(before, after, maxTries, null, files);
+ }
+
+ public CleanFiles(final boolean before, final boolean after, final int maxTries, final String... fileNames) {
+ super(before, after, maxTries, null, fileNames);
+ }
+
+ public CleanFiles(final File... files) {
+ super(true, true, MAX_TRIES, null, files);
+ }
+
+ public CleanFiles(final Path... paths) {
+ super(true, true, MAX_TRIES, null, paths);
+ }
+
+ public CleanFiles(final String... fileNames) {
+ super(true, true, MAX_TRIES, null, fileNames);
+ }
+
+ @Override
+ protected boolean clean(final Path path, final int tryIndex) throws IOException {
+ return Files.deleteIfExists(path);
+ }
+
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/CleanFolders.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/CleanFolders.java
new file mode 100644
index 00000000000..4099eec0f45
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/CleanFolders.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.test.junit;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+
+/**
+ * A JUnit test rule to automatically delete folders recursively before
+ * (optional) and after (optional) a test is run.
+ *
+ * This class should not perform logging using Log4j to avoid accidentally
+ * loading or re-loading Log4j configurations.
+ *
+ */
+public class CleanFolders extends AbstractExternalFileCleaner {
+
+ public static final class DeleteAllFileVisitor extends SimpleFileVisitor {
+
+ private final PrintStream printStream;
+
+ public DeleteAllFileVisitor(final PrintStream logger) {
+ this.printStream = logger;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
+ printf("%s Deleting directory %s\n", CLEANER_MARKER, dir);
+ final boolean deleted = Files.deleteIfExists(dir);
+ printf("%s Deleted directory %s: %s\n", CLEANER_MARKER, dir, deleted);
+ return FileVisitResult.CONTINUE;
+ }
+
+ protected void printf(final String format, final Object... args) {
+ if (printStream != null) {
+ printStream.printf(format, args);
+ }
+ }
+
+ @Override
+ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
+ printf("%s Deleting file %s with %s\n", CLEANER_MARKER, file, attrs);
+ final boolean deleted = Files.deleteIfExists(file);
+ printf(CLEANER_MARKER, "%s Deleted file %s: %s\n", file, deleted);
+ return FileVisitResult.CONTINUE;
+ }
+ }
+
+ private static final int MAX_TRIES = 10;
+
+ public CleanFolders(final boolean before, final boolean after, final int maxTries, final File... files) {
+ super(before, after, maxTries, null, files);
+ }
+
+ public CleanFolders(final boolean before, final boolean after, final int maxTries, final Path... paths) {
+ super(before, after, maxTries, null, paths);
+ }
+
+ public CleanFolders(final boolean before, final boolean after, final int maxTries, final String... fileNames) {
+ super(before, after, maxTries, null, fileNames);
+ }
+
+ public CleanFolders(final File... folders) {
+ super(true, true, MAX_TRIES, null, folders);
+ }
+
+ public CleanFolders(final Path... paths) {
+ super(true, true, MAX_TRIES, null, paths);
+ }
+
+ public CleanFolders(final PrintStream logger, final File... folders) {
+ super(true, true, MAX_TRIES, logger, folders);
+ }
+
+ public CleanFolders(final String... folderNames) {
+ super(true, true, MAX_TRIES, null, folderNames);
+ }
+
+ @Override
+ protected boolean clean(final Path path, final int tryIndex) throws IOException {
+ cleanFolder(path, tryIndex);
+ return true;
+ }
+
+ private void cleanFolder(final Path folder, final int tryIndex) throws IOException {
+ if (Files.exists(folder) && Files.isDirectory(folder)) {
+ Files.walkFileTree(folder, new DeleteAllFileVisitor(getPrintStream()));
+ }
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/CleanUpDirectories.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/CleanUpDirectories.java
new file mode 100644
index 00000000000..bdc185e133c
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/CleanUpDirectories.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.test.junit;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+
+/**
+ * JUnit extension to automatically clean up a list of directories and their contents before and after test execution.
+ * This will automatically retry deletion up to 10 times per file while pausing for 200ms each time.
+ * These can be overridden with system properties {@code log4j2.junit.fileCleanerMaxTries} and
+ * {@code log4j2.junit.fileCleanerSleepPeriodMillis}.
+ *
+ * @see DirectoryCleaner
+ * @see CleanUpFiles
+ * @since 2.14.0
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Documented
+@Inherited
+@ExtendWith(DirectoryCleaner.class)
+public @interface CleanUpDirectories {
+ String[] value();
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/CleanUpFiles.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/CleanUpFiles.java
new file mode 100644
index 00000000000..1ad6bcaf277
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/CleanUpFiles.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.test.junit;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+
+/**
+ * JUnit extension to automatically clean up a list of files before and after test execution.
+ * This will automatically retry deletion up to 10 times per file while pausing for 200ms each time.
+ * These can be overridden with system properties {@code log4j2.junit.fileCleanerMaxTries} and
+ * {@code log4j2.junit.fileCleanerSleepPeriodMillis}.
+ *
+ * @see FileCleaner
+ * @see CleanUpDirectories
+ * @since 2.14.0
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Documented
+@Inherited
+@ExtendWith(FileCleaner.class)
+public @interface CleanUpFiles {
+ String[] value();
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/DirectoryCleaner.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/DirectoryCleaner.java
new file mode 100644
index 00000000000..a0f8e02dea7
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/DirectoryCleaner.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.logging.log4j.test.junit;
+
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Collection;
+import java.util.HashSet;
+
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+class DirectoryCleaner extends AbstractFileCleaner {
+ @Override
+ Collection getPathsForTest(final ExtensionContext context) {
+ final Collection paths = new HashSet<>();
+ final CleanUpDirectories testClassAnnotation = context.getRequiredTestClass().getAnnotation(CleanUpDirectories.class);
+ if (testClassAnnotation != null) {
+ for (final String path : testClassAnnotation.value()) {
+ paths.add(Paths.get(path));
+ }
+ }
+ final CleanUpDirectories testMethodAnnotation = context.getRequiredTestMethod().getAnnotation(CleanUpDirectories.class);
+ if (testMethodAnnotation != null) {
+ for (final String path : testMethodAnnotation.value()) {
+ paths.add(Paths.get(path));
+ }
+ }
+ return paths;
+ }
+
+ @Override
+ boolean delete(final Path path) throws IOException {
+ if (Files.exists(path) && Files.isDirectory(path)) {
+ Files.walkFileTree(path, new SimpleFileVisitor() {
+ @Override
+ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
+ Files.deleteIfExists(file);
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
+ Files.deleteIfExists(dir);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+ return true;
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ExtensionContextAnchor.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ExtensionContextAnchor.java
new file mode 100644
index 00000000000..0b4cc50d609
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ExtensionContextAnchor.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.test.junit;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
+
+public class ExtensionContextAnchor
+ implements BeforeAllCallback, BeforeEachCallback, AfterAllCallback, AfterEachCallback {
+
+ public static Namespace LOG4J2_NAMESPACE = Namespace.create("org.apache.logging.log4j.junit");
+ private static final ThreadLocal EXTENSION_CONTEXT = new InheritableThreadLocal<>();
+
+ private static void bind(ExtensionContext context) {
+ EXTENSION_CONTEXT.set(context);
+ }
+
+ private static void unbind(ExtensionContext context) {
+ EXTENSION_CONTEXT.set(context.getParent().orElse(null));
+ }
+
+ public static ExtensionContext getContext() {
+ return EXTENSION_CONTEXT.get();
+ }
+
+ public static ExtensionContext getContext(ExtensionContext context) {
+ return context != null ? context : EXTENSION_CONTEXT.get();
+ }
+
+ static T getAttribute(Object key, Class clazz, ExtensionContext context) {
+ final ExtensionContext actualContext = getContext(context);
+ assertNotNull(actualContext, "missing ExtensionContext");
+ return actualContext.getStore(LOG4J2_NAMESPACE).get(key, clazz);
+ }
+
+ static void setAttribute(Object key, Object value, ExtensionContext context) {
+ final ExtensionContext actualContext = getContext(context);
+ assertNotNull(actualContext, "missing ExtensionContext");
+ actualContext.getStore(LOG4J2_NAMESPACE).put(key, value);
+ }
+
+ static void removeAttribute(Object key, ExtensionContext context) {
+ final ExtensionContext actualContext = getContext(context);
+ if (actualContext != null) {
+ actualContext.getStore(LOG4J2_NAMESPACE).remove(key);
+ }
+ }
+
+ @Override
+ public void afterEach(ExtensionContext context) throws Exception {
+ unbind(context);
+ }
+
+ @Override
+ public void afterAll(ExtensionContext context) throws Exception {
+ unbind(context);
+ }
+
+ @Override
+ public void beforeEach(ExtensionContext context) throws Exception {
+ bind(context);
+ }
+
+ @Override
+ public void beforeAll(ExtensionContext context) throws Exception {
+ bind(context);
+ }
+
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/FileCleaner.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/FileCleaner.java
new file mode 100644
index 00000000000..3ca16ac7172
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/FileCleaner.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.test.junit;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collection;
+import java.util.HashSet;
+
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+class FileCleaner extends AbstractFileCleaner {
+ @Override
+ Collection getPathsForTest(final ExtensionContext context) {
+ final Collection paths = new HashSet<>();
+ final CleanUpFiles testClassAnnotation = context.getRequiredTestClass().getAnnotation(CleanUpFiles.class);
+ if (testClassAnnotation != null) {
+ for (final String path : testClassAnnotation.value()) {
+ paths.add(Paths.get(path));
+ }
+ }
+ final CleanUpFiles testMethodAnnotation = context.getRequiredTestMethod().getAnnotation(CleanUpFiles.class);
+ if (testMethodAnnotation != null) {
+ for (final String path : testMethodAnnotation.value()) {
+ paths.add(Paths.get(path));
+ }
+ }
+ return paths;
+ }
+
+ @Override
+ boolean delete(final Path path) throws IOException {
+ return Files.deleteIfExists(path);
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/InitializesThreadContext.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/InitializesThreadContext.java
new file mode 100644
index 00000000000..33a8d9cc399
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/InitializesThreadContext.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.test.junit;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.logging.log4j.ThreadContext;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+
+/**
+ * Marks a test class that initializes the {@link ThreadContext} class;
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.METHOD })
+@Documented
+@Inherited
+@ExtendWith(ThreadContextInitializer.class)
+@ResourceLock(value = Resources.THREAD_CONTEXT, mode = ResourceAccessMode.READ_WRITE)
+public @interface InitializesThreadContext {
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Log4j2LauncherSessionListener.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Log4j2LauncherSessionListener.java
new file mode 100644
index 00000000000..51abd82afe3
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Log4j2LauncherSessionListener.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.test.junit;
+
+import org.apache.logging.log4j.util.PropertiesUtil;
+import org.junit.platform.launcher.LauncherSession;
+import org.junit.platform.launcher.LauncherSessionListener;
+
+/**
+ * Global Log4j2 test setup.
+ */
+public class Log4j2LauncherSessionListener implements LauncherSessionListener {
+
+ @Override
+ public void launcherSessionOpened(LauncherSession session) {
+ // Prevents `PropertiesUtil` from initializing (and caching the results)
+ // in the middle of a test.
+ PropertiesUtil.getProperties();
+ }
+
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/junit/LogManagerLoggerContextFactoryRule.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/LogManagerLoggerContextFactoryRule.java
similarity index 93%
rename from log4j-api/src/test/java/org/apache/logging/log4j/junit/LogManagerLoggerContextFactoryRule.java
rename to log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/LogManagerLoggerContextFactoryRule.java
index 98735ab8552..6b4d0d9825a 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/junit/LogManagerLoggerContextFactoryRule.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/LogManagerLoggerContextFactoryRule.java
@@ -14,7 +14,7 @@
* See the license for the specific language governing permissions and
* limitations under the license.
*/
-package org.apache.logging.log4j.junit;
+package org.apache.logging.log4j.test.junit;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.spi.LoggerContextFactory;
@@ -23,7 +23,10 @@
/**
* Sets the {@link LogManager}'s {@link LoggerContextFactory} to the given instance before the test and restores it to
* the original value after the test.
+ *
+ * @deprecated Use {@link LoggerContextFactoryExtension} with JUnit 5
*/
+@Deprecated
public class LogManagerLoggerContextFactoryRule extends ExternalResource {
private final LoggerContextFactory loggerContextFactory;
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/LoggerContextFactoryExtension.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/LoggerContextFactoryExtension.java
new file mode 100644
index 00000000000..89d412eb39b
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/LoggerContextFactoryExtension.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.test.junit;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.spi.LoggerContextFactory;
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+/**
+ * JUnit 5 extension that sets a particular {@link LoggerContextFactory} for the entire run of tests in a class.
+ *
+ * @since 2.14.0
+ */
+public class LoggerContextFactoryExtension implements BeforeAllCallback, AfterAllCallback {
+
+ private static final String KEY = "previousFactory";
+ private final LoggerContextFactory loggerContextFactory;
+
+ public LoggerContextFactoryExtension(LoggerContextFactory loggerContextFactory) {
+ this.loggerContextFactory = loggerContextFactory;
+ }
+
+ @Override
+ public void beforeAll(ExtensionContext context) throws Exception {
+ getStore(context).put(KEY, LogManager.getFactory());
+ LogManager.setFactory(loggerContextFactory);
+ }
+
+ @Override
+ public void afterAll(ExtensionContext context) throws Exception {
+ LogManager.setFactory(getStore(context).get(KEY, LoggerContextFactory.class));
+ }
+
+ private ExtensionContext.Store getStore(ExtensionContext context) {
+ return context.getStore(ExtensionContext.Namespace.create(getClass(), context.getRequiredTestClass()));
+ }
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/junit/Mutable.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Mutable.java
similarity index 95%
rename from log4j-api/src/test/java/org/apache/logging/log4j/junit/Mutable.java
rename to log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Mutable.java
index b81a12b420d..2e89bd1b799 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/junit/Mutable.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Mutable.java
@@ -15,7 +15,7 @@
* limitations under the license.
*/
-package org.apache.logging.log4j.junit;
+package org.apache.logging.log4j.test.junit;
/**
* Helper class for JUnit tests.
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Resources.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Resources.java
new file mode 100644
index 00000000000..4f85703d858
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Resources.java
@@ -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.
+ */
+
+package org.apache.logging.log4j.test.junit;
+
+import org.junit.jupiter.api.parallel.ResourceLock;
+
+/**
+ * Constants to use the the {@link ResourceLock} annotation.
+ *
+ */
+public class Resources {
+
+ public static final String THREAD_CONTEXT = "log4j2.ThreadContext";
+
+ public static final String MARKER_MANAGER = "log4j2.MarkerManager";
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SecurityManagerTestRule.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SecurityManagerTestRule.java
new file mode 100644
index 00000000000..57a9f713a5e
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SecurityManagerTestRule.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.test.junit;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Sets a security manager for a test run. The current security manager is first saved then restored after the test is
+ * run.
+ *
+ * Using a security manager can mess up other tests so this is best used from integration tests (classes that end in
+ * "IT" instead of "Test" and "TestCase".)
+ *
+ *
+ *
+ * When this test rule is evaluated, it will:
+ *
+ *
+ *
Save the current SecurityManager.
+ *
Set the SecurityManager to the instance supplied to this rule.
+ *
Evaluate the test statement.
+ *
Reset the current SecurityManager to the one from step (1).
+ *
+ *
+ * @since 2.11.0
+ */
+public class SecurityManagerTestRule implements TestRule {
+
+ /**
+ * Constructs a new instance with the given {@link SecurityManager}.
+ *
+ * When this test rule is evaluated, it will:
+ *
+ *
+ *
Save the current SecurityManager.
+ *
Set the SecurityManager to the instance supplied to this rule.
+ *
Evaluate the test statement.
+ *
Reset the current SecurityManager to the one from step (1).
+ *
+ *
+ * @param securityManager
+ * the {@link SecurityManager} to use while running a test.
+ */
+ public SecurityManagerTestRule(final SecurityManager securityManager) {
+ super();
+ this.securityManager = securityManager;
+ }
+
+ private SecurityManager securityManagerBefore;
+ private final SecurityManager securityManager;
+
+ @Override
+ public Statement apply(final Statement base, final Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ before();
+ try {
+ base.evaluate();
+ } finally {
+ after();
+ }
+ }
+
+ private void after() {
+ System.setSecurityManager(securityManagerBefore);
+ }
+
+ private void before() {
+ securityManagerBefore = System.getSecurityManager();
+ System.setSecurityManager(securityManager);
+
+ }
+ };
+ }
+
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/junit/SerialUtil.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SerialUtil.java
similarity index 95%
rename from log4j-api/src/test/java/org/apache/logging/log4j/junit/SerialUtil.java
rename to log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SerialUtil.java
index 211eca6b44d..5fe8e2d05dc 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/junit/SerialUtil.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SerialUtil.java
@@ -15,7 +15,7 @@
* limitations under the license.
*/
-package org.apache.logging.log4j.junit;
+package org.apache.logging.log4j.test.junit;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -52,7 +52,7 @@ public static byte[] serialize(final Serializable obj) {
* @param data byte array representing the serialized object
* @return the deserialized object
*/
- @SuppressWarnings("unchecked")
+ @SuppressWarnings({"unchecked", "BanSerializableRead"})
public static T deserialize(final byte[] data) {
try {
final ByteArrayInputStream bas = new ByteArrayInputStream(data);
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SetTestProperty.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SetTestProperty.java
new file mode 100644
index 00000000000..aa8a81e943f
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SetTestProperty.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.test.junit;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junitpioneer.jupiter.ReadsEnvironmentVariable;
+import org.junitpioneer.jupiter.ReadsSystemProperty;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Registers a Log4j2 system property with the {@link TestPropertySource}. The
+ * property will also be available in configuration files using the
+ * {@code ${test:...} lookup.
+ *
+ */
+@Retention(RUNTIME)
+@Target({ TYPE, METHOD })
+@Inherited
+@Documented
+@ExtendWith(ExtensionContextAnchor.class)
+@ExtendWith(TestPropertyResolver.class)
+@Repeatable(SetTestProperty.SetTestProperties.class)
+@ReadsSystemProperty
+@ReadsEnvironmentVariable
+public @interface SetTestProperty {
+
+ String key();
+
+ String value();
+
+ @Retention(RUNTIME)
+ @Target({ TYPE, METHOD })
+ @Documented
+ @Inherited
+ public @interface SetTestProperties {
+
+ SetTestProperty[] value();
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerLevel.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerLevel.java
new file mode 100644
index 00000000000..dcceba9432e
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerLevel.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.test.junit;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.parallel.ResourceLock;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * JUnit 5 test extension that sets a specific StatusLogger logging level for each test.
+ *
+ * @since 2.14.0
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Documented
+@Inherited
+@ExtendWith(StatusLoggerLevelExtension.class)
+@ResourceLock("log4j2.StatusLogger")
+public @interface StatusLoggerLevel {
+ /** Name of {@link org.apache.logging.log4j.Level} to use for status logger. */
+ String value();
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerLevelExtension.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerLevelExtension.java
new file mode 100644
index 00000000000..13a86fa8455
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerLevelExtension.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.test.junit;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+class StatusLoggerLevelExtension implements BeforeEachCallback, AfterEachCallback {
+
+ private static final String KEY = "previousLevel";
+
+ @Override
+ public void beforeEach(ExtensionContext context) throws Exception {
+ final StatusLoggerLevel annotation = context.getRequiredTestClass().getAnnotation(StatusLoggerLevel.class);
+ if (annotation == null) {
+ return;
+ }
+ final StatusLogger logger = StatusLogger.getLogger();
+ getStore(context).put(KEY, logger.getLevel());
+ logger.setLevel(Level.valueOf(annotation.value()));
+ }
+
+ @Override
+ public void afterEach(ExtensionContext context) throws Exception {
+ StatusLogger.getLogger().setLevel(getStore(context).get(KEY, Level.class));
+ }
+
+ private ExtensionContext.Store getStore(ExtensionContext context) {
+ return context.getStore(ExtensionContext.Namespace
+ .create(getClass(), context.getRequiredTestInstance()));
+ }
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/junit/StatusLoggerRule.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerRule.java
similarity index 93%
rename from log4j-api/src/test/java/org/apache/logging/log4j/junit/StatusLoggerRule.java
rename to log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerRule.java
index 11f945a4333..ab41d2c570a 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/junit/StatusLoggerRule.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerRule.java
@@ -14,7 +14,7 @@
* See the license for the specific language governing permissions and
* limitations under the license.
*/
-package org.apache.logging.log4j.junit;
+package org.apache.logging.log4j.test.junit;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.status.StatusLogger;
@@ -25,7 +25,9 @@
* Log4j configuration file.
*
* @since 2.8
+ * @deprecated Use {@link StatusLoggerLevel} with JUnit 5
*/
+@Deprecated
public class StatusLoggerRule extends ExternalResource {
private final StatusLogger statusLogger = StatusLogger.getLogger();
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TestPropertyResolver.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TestPropertyResolver.java
new file mode 100644
index 00000000000..abb659f2556
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TestPropertyResolver.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.test.junit;
+
+import org.apache.logging.log4j.test.TestProperties;
+import org.apache.logging.log4j.util.ReflectionUtil;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolutionException;
+import org.junit.platform.commons.support.HierarchyTraversalMode;
+import org.junit.platform.commons.support.ModifierSupport;
+import org.junit.platform.commons.support.ReflectionSupport;
+
+public class TestPropertyResolver extends TypeBasedParameterResolver
+ implements BeforeAllCallback, BeforeEachCallback {
+
+ public TestPropertyResolver() {
+ super(TestProperties.class);
+ }
+
+ @Override
+ public void beforeEach(ExtensionContext context) throws Exception {
+ final TestProperties props = TestPropertySource.createProperties(context);
+ final SetTestProperty[] setProperties = context.getRequiredTestMethod()
+ .getAnnotationsByType(SetTestProperty.class);
+ if (setProperties.length > 0) {
+ for (final SetTestProperty setProperty : setProperties) {
+ props.setProperty(setProperty.key(), setProperty.value());
+ }
+ }
+ final Class> testClass = context.getRequiredTestClass();
+ Object testInstance = context.getRequiredTestInstance();
+ ReflectionSupport
+ .findFields(testClass,
+ field -> ModifierSupport.isNotStatic(field)
+ && field.getType().isAssignableFrom(TestProperties.class),
+ HierarchyTraversalMode.BOTTOM_UP)
+ .forEach(field -> ReflectionUtil.setFieldValue(field, testInstance, props));
+ }
+
+ @Override
+ public void beforeAll(ExtensionContext context) throws Exception {
+ final TestProperties props = TestPropertySource.createProperties(context);
+ final SetTestProperty[] setProperties = context.getRequiredTestClass()
+ .getAnnotationsByType(SetTestProperty.class);
+ if (setProperties.length > 0) {
+ for (final SetTestProperty setProperty : setProperties) {
+ props.setProperty(setProperty.key(), setProperty.value());
+ }
+ }
+ final Class> testClass = context.getRequiredTestClass();
+ ReflectionSupport
+ .findFields(testClass,
+ field -> ModifierSupport.isStatic(field)
+ && field.getType().isAssignableFrom(TestProperties.class),
+ HierarchyTraversalMode.BOTTOM_UP)
+ .forEach(field -> ReflectionUtil.setStaticFieldValue(field, props));
+ }
+
+ @Override
+ public TestProperties resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
+ throws ParameterResolutionException {
+ return TestPropertySource.createProperties(extensionContext);
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TestPropertySource.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TestPropertySource.java
new file mode 100644
index 00000000000..1ad7e93c1eb
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TestPropertySource.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.test.junit;
+
+import org.apache.logging.log4j.test.TestProperties;
+import org.apache.logging.log4j.util.PropertySource;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
+import org.junit.jupiter.api.extension.ExtensionContext.Store;
+
+public class TestPropertySource implements PropertySource {
+
+ private static final String PREFIX = "log4j2.";
+ private static final Namespace NAMESPACE = ExtensionContextAnchor.LOG4J2_NAMESPACE.append("properties");
+ private static final TestProperties EMPTY_PROPERTIES = new EmptyTestProperties();
+
+ @Override
+ public int getPriority() {
+ // Highest priority
+ return Integer.MIN_VALUE;
+ }
+
+ public static TestProperties createProperties(ExtensionContext context) {
+ TestProperties props = getProperties(context);
+ // Make sure that the properties do not come from the parent ExtensionContext
+ if (props instanceof JUnitTestProperties && context.equals(((JUnitTestProperties) props).getContext())) {
+ return props;
+ }
+ props = new JUnitTestProperties(context);
+ ExtensionContextAnchor.setAttribute(TestProperties.class, props, context);
+ return props;
+ }
+
+ public static TestProperties getProperties() {
+ return getProperties(null);
+ }
+
+ private static TestProperties getProperties(ExtensionContext context) {
+ final ExtensionContext actualContext = context != null ? context : ExtensionContextAnchor.getContext();
+ if (actualContext != null) {
+ TestProperties props = ExtensionContextAnchor.getAttribute(TestProperties.class, TestProperties.class,
+ actualContext);
+ if (props != null) {
+ return props;
+ }
+ }
+ return EMPTY_PROPERTIES;
+ }
+
+ @Override
+ public CharSequence getNormalForm(Iterable extends CharSequence> tokens) {
+ final CharSequence camelCase = Util.joinAsCamelCase(tokens);
+ // Do not use Strings to prevent recursive initialization
+ return camelCase.length() > 0 ? PREFIX + camelCase.toString() : null;
+ }
+
+ @Override
+ public String getProperty(String key) {
+ return getProperties().getProperty(key);
+ }
+
+ @Override
+ public boolean containsProperty(String key) {
+ return getProperties().containsProperty(key);
+ }
+
+ private static class JUnitTestProperties implements TestProperties {
+
+ private final ExtensionContext context;
+ private final Store store;
+
+ public JUnitTestProperties(ExtensionContext context) {
+ this.context = context;
+ this.store = context.getStore(NAMESPACE);
+ }
+
+ public ExtensionContext getContext() {
+ return context;
+ }
+
+ @Override
+ public String getProperty(String key) {
+ return store.get(key, String.class);
+ }
+
+ @Override
+ public boolean containsProperty(String key) {
+ return getProperty(key) != null;
+ }
+
+ @Override
+ public void setProperty(String key, String value) {
+ store.put(key, value);
+ }
+
+ @Override
+ public void clearProperty(String key) {
+ store.remove(key, String.class);
+ }
+
+ }
+
+ private static class EmptyTestProperties implements TestProperties {
+
+ @Override
+ public String getProperty(String key) {
+ return null;
+ }
+
+ @Override
+ public boolean containsProperty(String key) {
+ return false;
+ }
+
+ @Override
+ public void setProperty(String key, String value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void clearProperty(String key) {
+ throw new UnsupportedOperationException();
+ }
+
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextInitializer.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextInitializer.java
new file mode 100644
index 00000000000..c246c3ce5fc
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextInitializer.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.test.junit;
+
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.test.ThreadContextUtilityClass;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource;
+import org.junit.platform.commons.support.AnnotationSupport;
+
+
+class ThreadContextInitializer implements BeforeAllCallback, BeforeEachCallback {
+
+ @Override
+ public void beforeAll(ExtensionContext context) throws Exception {
+ if (AnnotationSupport.isAnnotated(context.getRequiredTestClass(), InitializesThreadContext.class)) {
+ resetThreadContext(context);
+ }
+ }
+
+ @Override
+ public void beforeEach(ExtensionContext context) throws Exception {
+ if (AnnotationSupport.isAnnotated(context.getRequiredTestMethod(), InitializesThreadContext.class)) {
+ resetThreadContext(context);
+ }
+ }
+
+ private void resetThreadContext(ExtensionContext context) {
+ ThreadContextUtilityClass.reset();
+ // We use `CloseableResource` instead of `afterAll` to reset the
+ // ThreadContextFactory
+ // *after* the `@SetSystemProperty` extension has restored the properties
+ ExtensionContextAnchor.setAttribute(ThreadContext.class, new CloseableResource() {
+ @Override
+ public void close() throws Throwable {
+ ThreadContextUtilityClass.reset();
+ }
+
+ }, context);
+ }
+
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextMapExtension.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextMapExtension.java
new file mode 100644
index 00000000000..cb68eb295dc
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextMapExtension.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.test.junit;
+
+import java.util.Map;
+
+import org.apache.logging.log4j.ThreadContext;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+class ThreadContextMapExtension implements BeforeEachCallback {
+ private static class ThreadContextMapStore implements ExtensionContext.Store.CloseableResource {
+ private final Map previousMap = ThreadContext.getImmutableContext();
+
+ private ThreadContextMapStore() {
+ ThreadContext.clearMap();
+ }
+
+ @Override
+ public void close() throws Throwable {
+ // TODO LOG4J2-1517 Add ThreadContext.setContext(Map)
+ ThreadContext.clearMap();
+ ThreadContext.putAll(previousMap);
+ }
+ }
+
+ @Override
+ public void beforeEach(final ExtensionContext context) throws Exception {
+ context.getStore(ExtensionContextAnchor.LOG4J2_NAMESPACE)
+ .getOrComputeIfAbsent(ThreadContextMapStore.class);
+ }
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/junit/ThreadContextMapRule.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextMapRule.java
similarity index 90%
rename from log4j-api/src/test/java/org/apache/logging/log4j/junit/ThreadContextMapRule.java
rename to log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextMapRule.java
index 406630382af..deb7c772368 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/junit/ThreadContextMapRule.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextMapRule.java
@@ -14,7 +14,7 @@
* See the license for the specific language governing permissions and
* limitations under the license.
*/
-package org.apache.logging.log4j.junit;
+package org.apache.logging.log4j.test.junit;
/**
* Restores the ThreadContext to it's initial map values after a JUnit test.
@@ -25,7 +25,10 @@
* @Rule
* public final ThreadContextMapRule threadContextRule = new ThreadContextMapRule();
*
+ *
+ * @deprecated use {@link UsingThreadContextMap} with JUnit 5
*/
+@Deprecated
public class ThreadContextMapRule extends ThreadContextRule {
/**
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/junit/ThreadContextRule.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextRule.java
similarity index 92%
rename from log4j-api/src/test/java/org/apache/logging/log4j/junit/ThreadContextRule.java
rename to log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextRule.java
index dad7b5b32bf..1ec1789ca8b 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/junit/ThreadContextRule.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextRule.java
@@ -14,10 +14,10 @@
* See the license for the specific language governing permissions and
* limitations under the license.
*/
-package org.apache.logging.log4j.junit;
+package org.apache.logging.log4j.test.junit;
import org.apache.logging.log4j.ThreadContext;
-import org.apache.logging.log4j.ThreadContextHolder;
+import org.apache.logging.log4j.test.ThreadContextHolder;
import org.junit.rules.ExternalResource;
/**
@@ -29,7 +29,10 @@
* @Rule
* public final ThreadContextRule threadContextRule = new ThreadContextRule();
*
+ *
+ * @deprecated use {@link UsingAnyThreadContext} with JUnit 5
*/
+@Deprecated
public class ThreadContextRule extends ExternalResource {
private final boolean restoreMap;
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextStackExtension.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextStackExtension.java
new file mode 100644
index 00000000000..c3ebe7b484b
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextStackExtension.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.test.junit;
+
+import org.apache.logging.log4j.ThreadContext;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
+import org.junit.jupiter.api.extension.ExtensionContext.Store;
+
+import java.util.Collection;
+
+class ThreadContextStackExtension implements BeforeEachCallback {
+ private static class ThreadContextStackStore implements Store.CloseableResource {
+ private final Collection previousStack = ThreadContext.getImmutableStack();
+
+ private ThreadContextStackStore() {
+ ThreadContext.clearStack();
+ }
+
+ @Override
+ public void close() throws Throwable {
+ ThreadContext.setStack(previousStack);
+ }
+ }
+
+ @Override
+ public void beforeEach(ExtensionContext context) throws Exception {
+ context.getStore(Namespace.create(ThreadContext.class, context.getRequiredTestClass(), context.getRequiredTestInstance()))
+ .getOrComputeIfAbsent(ThreadContextStackStore.class);
+ }
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/junit/ThreadContextStackRule.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextStackRule.java
similarity index 91%
rename from log4j-api/src/test/java/org/apache/logging/log4j/junit/ThreadContextStackRule.java
rename to log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextStackRule.java
index 1581dc8a7c4..77c74cd475d 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/junit/ThreadContextStackRule.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextStackRule.java
@@ -14,7 +14,7 @@
* See the license for the specific language governing permissions and
* limitations under the license.
*/
-package org.apache.logging.log4j.junit;
+package org.apache.logging.log4j.test.junit;
/**
* Restores the ThreadContext to it's initial stack values after a JUnit test.
@@ -25,7 +25,9 @@
* @Rule
* public final ThreadContextStackRule threadContextRule = new ThreadContextStackRule();
*
+ * @deprecated use {@link UsingThreadContextStack} with JUnit 5
*/
+@Deprecated
public class ThreadContextStackRule extends ThreadContextRule {
/**
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TypeBasedParameterResolver.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TypeBasedParameterResolver.java
new file mode 100644
index 00000000000..8cf338493b0
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TypeBasedParameterResolver.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.test.junit;
+
+import java.lang.reflect.Type;
+
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolutionException;
+import org.junit.jupiter.api.extension.ParameterResolver;
+
+public abstract class TypeBasedParameterResolver implements ParameterResolver {
+
+ private final Type supportedParameterType;
+
+ public TypeBasedParameterResolver(Type supportedParameterType) {
+ this.supportedParameterType = supportedParameterType;
+ }
+
+ @Override
+ public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
+ throws ParameterResolutionException {
+ return this.supportedParameterType.equals(parameterContext.getParameter().getParameterizedType());
+ }
+
+ @Override
+ public abstract T resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
+ throws ParameterResolutionException;
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/URLStreamHandlerFactories.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/URLStreamHandlerFactories.java
new file mode 100644
index 00000000000..aa11328f703
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/URLStreamHandlerFactories.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.test.junit;
+
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.opentest4j.TestAbortedException;
+
+import java.lang.reflect.Field;
+import java.net.URL;
+import java.net.URLStreamHandlerFactory;
+import java.util.Hashtable;
+import java.util.stream.Stream;
+
+public class URLStreamHandlerFactories {
+ private static final Field FACTORY_FIELD = Stream.of(URL.class.getDeclaredFields())
+ .filter(field -> URLStreamHandlerFactory.class.equals(field.getType()))
+ .findFirst()
+ .orElseThrow(() -> new TestAbortedException("java.net.URL does not declare a java.net.URLStreamHandlerFactory field"));
+ private static final Field HANDLERS_FIELD = FieldUtils.getDeclaredField(URL.class, "handlers", true);
+
+ public static URLStreamHandlerFactory getURLStreamHandlerFactory() {
+ try {
+ return (URLStreamHandlerFactory) FieldUtils.readStaticField(FACTORY_FIELD, true);
+ } catch (IllegalAccessException e) {
+ throw new IllegalAccessError(e.getMessage());
+ }
+ }
+
+ public static void setURLStreamHandlerFactory(final URLStreamHandlerFactory factory) {
+ try {
+ final Object handlers = HANDLERS_FIELD.get(null);
+ if (handlers instanceof Hashtable, ?>) {
+ ((Hashtable, ?>) handlers).clear();
+ }
+ FieldUtils.writeStaticField(FACTORY_FIELD, null, true);
+ } catch (IllegalAccessException e) {
+ throw new IllegalAccessError(e.getMessage());
+ }
+ if (factory != null) {
+ URL.setURLStreamHandlerFactory(factory);
+ }
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/URLStreamHandlerFactoryExtension.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/URLStreamHandlerFactoryExtension.java
new file mode 100644
index 00000000000..2450657628b
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/URLStreamHandlerFactoryExtension.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.test.junit;
+
+import org.apache.logging.log4j.util.ReflectionUtil;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.platform.commons.support.AnnotationSupport;
+
+import java.net.URLStreamHandlerFactory;
+
+public class URLStreamHandlerFactoryExtension implements BeforeAllCallback {
+ @Override
+ public void beforeAll(final ExtensionContext context) throws Exception {
+ final Class> testClass = context.getRequiredTestClass();
+ final URLStreamHandlerFactory factory = AnnotationSupport.findAnnotation(testClass, UsingURLStreamHandlerFactory.class)
+ .map(UsingURLStreamHandlerFactory::value)
+ .map(ReflectionUtil::instantiate)
+ .orElseThrow();
+ final URLStreamHandlerFactory oldFactory = URLStreamHandlerFactories.getURLStreamHandlerFactory();
+ URLStreamHandlerFactories.setURLStreamHandlerFactory(factory);
+ context.getStore(ExtensionContext.Namespace.create(getClass(), testClass))
+ .put(URLStreamHandlerFactory.class, (ExtensionContext.Store.CloseableResource) () ->
+ URLStreamHandlerFactories.setURLStreamHandlerFactory(oldFactory));
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingAnyThreadContext.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingAnyThreadContext.java
new file mode 100644
index 00000000000..e88914707c6
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingAnyThreadContext.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.test.junit;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a test class as using {@link org.apache.logging.log4j.ThreadContext} APIs. This will automatically clear and restore
+ * both the thread context map and stack for each test invocation.
+ *
+ * @since 2.14.0
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
+@Documented
+@Inherited
+@UsingThreadContextMap
+@UsingThreadContextStack
+public @interface UsingAnyThreadContext {
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingTestProperties.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingTestProperties.java
new file mode 100644
index 00000000000..cc696522ab7
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingTestProperties.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.test.junit;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import org.apache.logging.log4j.test.TestProperties;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junitpioneer.jupiter.ReadsEnvironmentVariable;
+import org.junitpioneer.jupiter.ReadsSystemProperty;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * A field or method parameter of type {@link TestProperties} will be injected with a per-test source of Log4j2's
+ * system properties.
+ */
+@Retention(RUNTIME)
+@Target({ TYPE, METHOD })
+@Inherited
+@Documented
+@ExtendWith(ExtensionContextAnchor.class)
+@ExtendWith(TestPropertyResolver.class)
+@ReadsSystemProperty
+@ReadsEnvironmentVariable
+public @interface UsingTestProperties {
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextMap.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextMap.java
new file mode 100644
index 00000000000..41ba316e9a8
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextMap.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.test.junit;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junitpioneer.jupiter.ReadsSystemProperty;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a test class as using {@link org.apache.logging.log4j.spi.ThreadContextMap} APIs. This will automatically clear and
+ * restore the thread context map (MDC) for each test invocation.
+ *
+ * @since 2.14.0
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
+@Documented
+@Inherited
+@ExtendWith(ThreadContextMapExtension.class)
+@ReadsSystemProperty
+@ResourceLock(value = Resources.THREAD_CONTEXT, mode = ResourceAccessMode.READ)
+public @interface UsingThreadContextMap {
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextStack.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextStack.java
new file mode 100644
index 00000000000..6d8251a6714
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextStack.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.test.junit;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junitpioneer.jupiter.ReadsSystemProperty;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a test class as using {@link org.apache.logging.log4j.spi.ThreadContextStack} APIs. This will automatically clear and
+ * restore the thread context stack (NDC) for each test invocation.
+ *
+ * @since 2.14.0
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
+@Documented
+@Inherited
+@ExtendWith(ThreadContextStackExtension.class)
+@ReadsSystemProperty
+public @interface UsingThreadContextStack {
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingURLStreamHandlerFactory.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingURLStreamHandlerFactory.java
new file mode 100644
index 00000000000..25137a7758f
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingURLStreamHandlerFactory.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.test.junit;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.parallel.Isolated;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.net.URLStreamHandlerFactory;
+
+/**
+ * Test extension to use a custom {@link URLStreamHandlerFactory} for all tests in the annotated class.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Documented
+@Inherited
+@ExtendWith(URLStreamHandlerFactoryExtension.class)
+@Isolated
+public @interface UsingURLStreamHandlerFactory {
+ Class extends URLStreamHandlerFactory> value();
+}
diff --git a/log4j-api-test/src/main/module/module-info.java b/log4j-api-test/src/main/module/module-info.java
new file mode 100644
index 00000000000..4cf9336ae38
--- /dev/null
+++ b/log4j-api-test/src/main/module/module-info.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+import org.apache.logging.log4j.util.PropertySource;
+import org.apache.logging.log4j.test.junit.TestPropertySource;
+module org.apache.logging.log4j.test {
+ exports org.apache.logging.log4j.test;
+ exports org.apache.logging.log4j.test.junit;
+
+ opens org.apache.logging.log4j.test.junit to org.junit.platform.commons;
+
+ requires transitive org.apache.logging.log4j;
+ requires static org.apache.commons.lang3;
+ requires static org.assertj.core;
+ requires static org.hamcrest;
+ requires static org.junit.jupiter.api;
+ requires static org.junit.jupiter.params;
+ requires static org.junit.platform.commons;
+ requires static org.junit.platform.launcher;
+ requires static org.junitpioneer;
+ requires static junit;
+
+ provides PropertySource with TestPropertySource;
+}
diff --git a/log4j-api-test/src/main/resources/META-INF/services/org.apache.logging.log4j.util.PropertySource b/log4j-api-test/src/main/resources/META-INF/services/org.apache.logging.log4j.util.PropertySource
new file mode 100644
index 00000000000..2c7cb498d92
--- /dev/null
+++ b/log4j-api-test/src/main/resources/META-INF/services/org.apache.logging.log4j.util.PropertySource
@@ -0,0 +1,15 @@
+# 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.
+org.apache.logging.log4j.test.junit.TestPropertySource
diff --git a/log4j-api-test/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension b/log4j-api-test/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension
new file mode 100644
index 00000000000..ca7ce84edd3
--- /dev/null
+++ b/log4j-api-test/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension
@@ -0,0 +1,15 @@
+# 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.
+org.apache.logging.log4j.test.junit.ExtensionContextAnchor
\ No newline at end of file
diff --git a/log4j-api-test/src/main/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener b/log4j-api-test/src/main/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener
new file mode 100644
index 00000000000..e492405c761
--- /dev/null
+++ b/log4j-api-test/src/main/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener
@@ -0,0 +1,16 @@
+# 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.
+
+org.apache.logging.log4j.test.junit.Log4j2LauncherSessionListener
\ No newline at end of file
diff --git a/log4j-api-test/src/site/markdown/index.md b/log4j-api-test/src/site/markdown/index.md
new file mode 100644
index 00000000000..f83cc0d7a61
--- /dev/null
+++ b/log4j-api-test/src/site/markdown/index.md
@@ -0,0 +1,26 @@
+
+
+
+# Log4j 2 API
+
+The Log4j 2 API provides the interface that applications should code to and provides the
+adapter components required for implementers to create a logging implementation.
+
+## Requirements
+
+As of version 2.4, the Log4j 2 API requires Java 7. Versions 2.3 and earlier require Java 6.
diff --git a/log4j-api-test/src/site/site.xml b/log4j-api-test/src/site/site.xml
new file mode 100644
index 00000000000..e0a45af2225
--- /dev/null
+++ b/log4j-api-test/src/site/site.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/AbstractLoggerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/AbstractLoggerTest.java
new file mode 100644
index 00000000000..6a49e73ccef
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/AbstractLoggerTest.java
@@ -0,0 +1,1319 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j;
+
+import org.apache.logging.log4j.test.junit.Resources;
+import org.apache.logging.log4j.test.junit.StatusLoggerLevel;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.ObjectMessage;
+import org.apache.logging.log4j.message.ParameterizedMessage;
+import org.apache.logging.log4j.message.ParameterizedMessageFactory;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.spi.AbstractLogger;
+import org.apache.logging.log4j.status.StatusData;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.MessageSupplier;
+import org.apache.logging.log4j.util.Supplier;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.*;
+
+@StatusLoggerLevel("WARN")
+@ResourceLock(value = Resources.MARKER_MANAGER, mode = ResourceAccessMode.READ)
+public class AbstractLoggerTest {
+
+ private static final StringBuilder CHAR_SEQ = new StringBuilder("CharSeq");
+
+ // TODO add proper tests for ReusableMessage
+
+ @SuppressWarnings("ThrowableInstanceNeverThrown")
+ private static final Throwable t = new UnsupportedOperationException("Test");
+
+ private static final Class obj = AbstractLogger.class;
+ private static final String pattern = "{}, {}";
+ private static final String p1 = "Long Beach";
+
+ private static final String p2 = "California";
+ private static final Message charSeq = new SimpleMessage(CHAR_SEQ);
+ private static final Message simple = new SimpleMessage("Hello");
+ private static final Message object = new ObjectMessage(obj);
+
+ private static final Message param = new ParameterizedMessage(pattern, p1, p2);
+
+ private final Marker MARKER = MarkerManager.getMarker("TEST");
+ private static final String MARKER_NAME = "TEST";
+
+ private static final LogEvent[] EVENTS = new LogEvent[] {
+ new LogEvent(null, simple, null),
+ new LogEvent(MARKER_NAME, simple, null),
+ new LogEvent(null, simple, t),
+ new LogEvent(MARKER_NAME, simple, t),
+
+ new LogEvent(null, object, null),
+ new LogEvent(MARKER_NAME, object, null),
+ new LogEvent(null, object, t),
+ new LogEvent(MARKER_NAME, object, t),
+
+ new LogEvent(null, param, null),
+ new LogEvent(MARKER_NAME, param, null),
+
+ new LogEvent(null, simple, null),
+ new LogEvent(null, simple, t),
+ new LogEvent(MARKER_NAME, simple, null),
+ new LogEvent(MARKER_NAME, simple, t),
+ new LogEvent(MARKER_NAME, simple, null),
+
+ new LogEvent(null, charSeq, null),
+ new LogEvent(null, charSeq, t),
+ new LogEvent(MARKER_NAME, charSeq, null),
+ new LogEvent(MARKER_NAME, charSeq, t),
+ };
+
+
+ @Test
+ public void testDebug() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.DEBUG);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.debug("Hello");
+ logger.debug((Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.debug(MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.debug("Hello", t);
+ logger.debug((Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.debug(MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.debug(obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.debug(MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.debug(obj, t);
+ logger.debug((Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.debug(MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.debug(pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.debug(MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.debug(simple);
+ logger.debug((Marker) null, simple);
+ logger.debug((Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.debug(simple, t);
+ logger.debug((Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.debug(MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.debug(MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.debug(MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.debug(CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.debug(CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.debug(MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.debug(MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ public void testError() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.ERROR);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.error("Hello");
+ logger.error((Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.error(MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.error("Hello", t);
+ logger.error((Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.error(MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.error(obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.error(MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.error(obj, t);
+ logger.error((Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.error(MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.error(pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.error(MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.error(simple);
+ logger.error((Marker) null, simple);
+ logger.error((Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.error(simple, t);
+ logger.error((Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.error(MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.error(MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.error(MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.error(CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.error(CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.error(MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.error(MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ public void testFatal() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.FATAL);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.fatal("Hello");
+ logger.fatal((Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.fatal(MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.fatal("Hello", t);
+ logger.fatal((Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.fatal(MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.fatal(obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.fatal(MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.fatal(obj, t);
+ logger.fatal((Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.fatal(MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.fatal(pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.fatal(MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.fatal(simple);
+ logger.fatal((Marker) null, simple);
+ logger.fatal((Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.fatal(simple, t);
+ logger.fatal((Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.fatal(MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.fatal(MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.fatal(MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.fatal(CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.fatal(CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.fatal(MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.fatal(MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ public void testInfo() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.INFO);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.info("Hello");
+ logger.info((Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.info(MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.info("Hello", t);
+ logger.info((Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.info(MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.info(obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.info(MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.info(obj, t);
+ logger.info((Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.info(MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.info(pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.info(MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.info(simple);
+ logger.info((Marker) null, simple);
+ logger.info((Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.info(simple, t);
+ logger.info((Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.info(MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.info(MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.info(MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.info(CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.info(CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.info(MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.info(MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ public void testLogDebug() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.DEBUG);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.log(Level.DEBUG, "Hello");
+ logger.log(Level.DEBUG, (Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.log(Level.DEBUG, MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.log(Level.DEBUG, "Hello", t);
+ logger.log(Level.DEBUG, (Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.log(Level.DEBUG, MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.log(Level.DEBUG, obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.log(Level.DEBUG, MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.log(Level.DEBUG, obj, t);
+ logger.log(Level.DEBUG, (Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.log(Level.DEBUG, MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.log(Level.DEBUG, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.log(Level.DEBUG, MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.log(Level.DEBUG, simple);
+ logger.log(Level.DEBUG, (Marker) null, simple);
+ logger.log(Level.DEBUG, (Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.log(Level.DEBUG, simple, t);
+ logger.log(Level.DEBUG, (Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.log(Level.DEBUG, MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.log(Level.DEBUG, MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.log(Level.DEBUG, MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.log(Level.DEBUG, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.log(Level.DEBUG, CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.log(Level.DEBUG, MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.log(Level.DEBUG, MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ public void testLogError() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.ERROR);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.log(Level.ERROR, "Hello");
+ logger.log(Level.ERROR, (Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.log(Level.ERROR, MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.log(Level.ERROR, "Hello", t);
+ logger.log(Level.ERROR, (Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.log(Level.ERROR, MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.log(Level.ERROR, obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.log(Level.ERROR, MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.log(Level.ERROR, obj, t);
+ logger.log(Level.ERROR, (Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.log(Level.ERROR, MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.log(Level.ERROR, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.log(Level.ERROR, MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.log(Level.ERROR, simple);
+ logger.log(Level.ERROR, (Marker) null, simple);
+ logger.log(Level.ERROR, (Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.log(Level.ERROR, simple, t);
+ logger.log(Level.ERROR, (Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.log(Level.ERROR, MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.log(Level.ERROR, MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.log(Level.ERROR, MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.log(Level.ERROR, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.log(Level.ERROR, CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.log(Level.ERROR, MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.log(Level.ERROR, MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ public void testLogFatal() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.FATAL);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.log(Level.FATAL, "Hello");
+ logger.log(Level.FATAL, (Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.log(Level.FATAL, MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.log(Level.FATAL, "Hello", t);
+ logger.log(Level.FATAL, (Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.log(Level.FATAL, MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.log(Level.FATAL, obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.log(Level.FATAL, MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.log(Level.FATAL, obj, t);
+ logger.log(Level.FATAL, (Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.log(Level.FATAL, MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.log(Level.FATAL, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.log(Level.FATAL, MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.log(Level.FATAL, simple);
+ logger.log(Level.FATAL, (Marker) null, simple);
+ logger.log(Level.FATAL, (Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.log(Level.FATAL, simple, t);
+ logger.log(Level.FATAL, (Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.log(Level.FATAL, MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.log(Level.FATAL, MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.log(Level.FATAL, MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.log(Level.FATAL, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.log(Level.FATAL, CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.log(Level.FATAL, MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.log(Level.FATAL, MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ public void testLogInfo() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.INFO);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.log(Level.INFO, "Hello");
+ logger.log(Level.INFO, (Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.log(Level.INFO, MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.log(Level.INFO, "Hello", t);
+ logger.log(Level.INFO, (Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.log(Level.INFO, MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.log(Level.INFO, obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.log(Level.INFO, MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.log(Level.INFO, obj, t);
+ logger.log(Level.INFO, (Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.log(Level.INFO, MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.log(Level.INFO, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.log(Level.INFO, MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.log(Level.INFO, simple);
+ logger.log(Level.INFO, (Marker) null, simple);
+ logger.log(Level.INFO, (Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.log(Level.INFO, simple, t);
+ logger.log(Level.INFO, (Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.log(Level.INFO, MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.log(Level.INFO, MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.log(Level.INFO, MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.log(Level.INFO, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.log(Level.INFO, CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.log(Level.INFO, MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.log(Level.INFO, MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ public void testLogTrace() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.TRACE);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.log(Level.TRACE, "Hello");
+ logger.log(Level.TRACE, (Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.log(Level.TRACE, MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.log(Level.TRACE, "Hello", t);
+ logger.log(Level.TRACE, (Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.log(Level.TRACE, MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.log(Level.TRACE, obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.log(Level.TRACE, MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.log(Level.TRACE, obj, t);
+ logger.log(Level.TRACE, (Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.log(Level.TRACE, MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.log(Level.TRACE, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.log(Level.TRACE, MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.log(Level.TRACE, simple);
+ logger.log(Level.TRACE, (Marker) null, simple);
+ logger.log(Level.TRACE, (Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.log(Level.TRACE, simple, t);
+ logger.log(Level.TRACE, (Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.log(Level.TRACE, MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.log(Level.TRACE, MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.log(Level.TRACE, MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.log(Level.TRACE, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.log(Level.TRACE, CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.log(Level.TRACE, MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.log(Level.TRACE, MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ public void testLogWarn() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.WARN);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.log(Level.WARN, "Hello");
+ logger.log(Level.WARN, (Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.log(Level.WARN, MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.log(Level.WARN, "Hello", t);
+ logger.log(Level.WARN, (Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.log(Level.WARN, MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.log(Level.WARN, obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.log(Level.WARN, MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.log(Level.WARN, obj, t);
+ logger.log(Level.WARN, (Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.log(Level.WARN, MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.log(Level.WARN, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.log(Level.WARN, MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.log(Level.WARN, simple);
+ logger.log(Level.WARN, (Marker) null, simple);
+ logger.log(Level.WARN, (Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.log(Level.WARN, simple, t);
+ logger.log(Level.WARN, (Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.log(Level.WARN, MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.log(Level.WARN, MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.log(Level.WARN, MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.log(Level.WARN, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.log(Level.WARN, CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.log(Level.WARN, MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.log(Level.WARN, MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ public void testTrace() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.TRACE);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.trace("Hello");
+ logger.trace((Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.trace(MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.trace("Hello", t);
+ logger.trace((Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.trace(MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.trace(obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.trace(MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.trace(obj, t);
+ logger.trace((Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.trace(MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.trace(pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.trace(MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.trace(simple);
+ logger.trace((Marker) null, simple);
+ logger.trace((Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.trace(simple, t);
+ logger.trace((Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.trace(MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.trace(MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.trace(MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.trace(CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.trace(CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.trace(MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.trace(MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ public void testWarn() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.WARN);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.warn("Hello");
+ logger.warn((Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.warn(MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.warn("Hello", t);
+ logger.warn((Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.warn(MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.warn(obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.warn(MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.warn(obj, t);
+ logger.warn((Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.warn(MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.warn(pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.warn(MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.warn(simple);
+ logger.warn((Marker) null, simple);
+ logger.warn((Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.warn(simple, t);
+ logger.warn((Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.warn(MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.warn(MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.warn(MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.warn(CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.warn(CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.warn(MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.warn(MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ public void testMessageWithThrowable() {
+ final ThrowableExpectingLogger logger = new ThrowableExpectingLogger(true);
+ final ThrowableMessage message = new ThrowableMessage(t);
+
+ logger.debug(message);
+ logger.error(message);
+ logger.fatal(message);
+ logger.info(message);
+ logger.trace(message);
+ logger.warn(message);
+ logger.log(Level.INFO, message);
+
+ logger.debug(MARKER, message);
+ logger.error(MARKER, message);
+ logger.fatal(MARKER, message);
+ logger.info(MARKER, message);
+ logger.trace(MARKER, message);
+ logger.warn(MARKER, message);
+ logger.log(Level.INFO, MARKER, message);
+ }
+
+ @Test
+ public void testMessageWithoutThrowable() {
+ final ThrowableExpectingLogger logger = new ThrowableExpectingLogger(false);
+ final ThrowableMessage message = new ThrowableMessage(null);
+
+ logger.debug(message);
+ logger.error(message);
+ logger.fatal(message);
+ logger.info(message);
+ logger.trace(message);
+ logger.warn(message);
+ logger.log(Level.INFO, message);
+
+ logger.debug(MARKER, message);
+ logger.error(MARKER, message);
+ logger.fatal(MARKER, message);
+ logger.info(MARKER, message);
+ logger.trace(MARKER, message);
+ logger.warn(MARKER, message);
+ logger.log(Level.INFO, MARKER, message);
+ }
+
+ @Test
+ public void testMessageSupplierWithThrowable() {
+ final ThrowableExpectingLogger logger = new ThrowableExpectingLogger(true);
+ final ThrowableMessage message = new ThrowableMessage(t);
+ final MessageSupplier supplier = () -> message;
+
+ logger.debug(supplier);
+ logger.error(supplier);
+ logger.fatal(supplier);
+ logger.info(supplier);
+ logger.trace(supplier);
+ logger.warn(supplier);
+ logger.log(Level.INFO, supplier);
+
+ logger.debug(MARKER, supplier);
+ logger.error(MARKER, supplier);
+ logger.fatal(MARKER, supplier);
+ logger.info(MARKER, supplier);
+ logger.trace(MARKER, supplier);
+ logger.warn(MARKER, supplier);
+ logger.log(Level.INFO, MARKER, supplier);
+ }
+
+ @Test
+ public void testMessageSupplierWithoutThrowable() {
+ final ThrowableExpectingLogger logger = new ThrowableExpectingLogger(false);
+ final ThrowableMessage message = new ThrowableMessage(null);
+ final MessageSupplier supplier = () -> message;
+
+ logger.debug(supplier);
+ logger.error(supplier);
+ logger.fatal(supplier);
+ logger.info(supplier);
+ logger.trace(supplier);
+ logger.warn(supplier);
+ logger.log(Level.INFO, supplier);
+
+ logger.debug(MARKER, supplier);
+ logger.error(MARKER, supplier);
+ logger.fatal(MARKER, supplier);
+ logger.info(MARKER, supplier);
+ logger.trace(MARKER, supplier);
+ logger.warn(MARKER, supplier);
+ logger.log(Level.INFO, MARKER, supplier);
+ }
+
+ @Test
+ public void testSupplierWithThrowable() {
+ final ThrowableExpectingLogger logger = new ThrowableExpectingLogger(true);
+ final ThrowableMessage message = new ThrowableMessage(t);
+ final Supplier supplier = () -> message;
+
+ logger.debug(supplier);
+ logger.error(supplier);
+ logger.fatal(supplier);
+ logger.info(supplier);
+ logger.trace(supplier);
+ logger.warn(supplier);
+ logger.log(Level.INFO, supplier);
+
+ logger.debug(MARKER, supplier);
+ logger.error(MARKER, supplier);
+ logger.fatal(MARKER, supplier);
+ logger.info(MARKER, supplier);
+ logger.trace(MARKER, supplier);
+ logger.warn(MARKER, supplier);
+ logger.log(Level.INFO, MARKER, supplier);
+ }
+
+ @Test
+ public void testSupplierWithoutThrowable() {
+ final ThrowableExpectingLogger logger = new ThrowableExpectingLogger(false);
+ final ThrowableMessage message = new ThrowableMessage(null);
+ final Supplier supplier = () -> message;
+
+ logger.debug(supplier);
+ logger.error(supplier);
+ logger.fatal(supplier);
+ logger.info(supplier);
+ logger.trace(supplier);
+ logger.warn(supplier);
+ logger.log(Level.INFO, supplier);
+
+ logger.debug(MARKER, supplier);
+ logger.error(MARKER, supplier);
+ logger.fatal(MARKER, supplier);
+ logger.info(MARKER, supplier);
+ logger.trace(MARKER, supplier);
+ logger.warn(MARKER, supplier);
+ logger.log(Level.INFO, MARKER, supplier);
+ }
+
+ @Test
+ @ResourceLock("log4j2.StatusLogger")
+ public void testMessageThrows() {
+ final ThrowableExpectingLogger logger = new ThrowableExpectingLogger(false);
+ logger.error(new TestMessage(() -> {
+ throw new IllegalStateException("Oops!");
+ }, "Message Format"));
+ List statusDatalist = StatusLogger.getLogger().getStatusData();
+ StatusData mostRecent = statusDatalist.get(statusDatalist.size() - 1);
+ assertEquals(Level.WARN, mostRecent.getLevel());
+ assertThat(mostRecent.getFormattedStatus(), containsString(
+ "org.apache.logging.log4j.spi.AbstractLogger caught " +
+ "java.lang.IllegalStateException logging TestMessage: Message Format"));
+ }
+
+ @Test
+ @ResourceLock("log4j2.StatusLogger")
+ public void testMessageThrowsAndNullFormat() {
+ final ThrowableExpectingLogger logger = new ThrowableExpectingLogger(false);
+ logger.error(new TestMessage(() -> {
+ throw new IllegalStateException("Oops!");
+ }, null /* format */));
+ List statusDatalist = StatusLogger.getLogger().getStatusData();
+ StatusData mostRecent = statusDatalist.get(statusDatalist.size() - 1);
+ assertEquals(Level.WARN, mostRecent.getLevel());
+ assertThat(mostRecent.getFormattedStatus(), containsString(
+ "org.apache.logging.log4j.spi.AbstractLogger caught " +
+ "java.lang.IllegalStateException logging TestMessage: "));
+ }
+
+ private static final class TestMessage implements Message {
+ private static final long serialVersionUID = 1L;
+ private final FormattedMessageSupplier formattedMessageSupplier;
+ private final String format;
+ TestMessage(FormattedMessageSupplier formattedMessageSupplier, String format) {
+ this.formattedMessageSupplier = formattedMessageSupplier;
+ this.format = format;
+ }
+
+ @Override
+ public String getFormattedMessage() {
+ return formattedMessageSupplier.getFormattedMessage();
+ }
+
+ @Override
+ public String getFormat() {
+ return format;
+ }
+
+ @Override
+ public Object[] getParameters() {
+ return new Object[0];
+ }
+
+ @Override
+ public Throwable getThrowable() {
+ return null;
+ }
+
+ interface FormattedMessageSupplier {
+ String getFormattedMessage();
+ }
+ }
+
+ private static class CountingLogger extends AbstractLogger {
+ private static final long serialVersionUID = -3171452617952475480L;
+
+ private Level currentLevel;
+ private LogEvent currentEvent;
+ private int charSeqCount;
+ private int objectCount;
+
+ CountingLogger() {
+ super("CountingLogger", ParameterizedMessageFactory.INSTANCE);
+ }
+
+ void setCurrentLevel(final Level currentLevel) {
+ this.currentLevel = currentLevel;
+ }
+
+ void setCurrentEvent(final LogEvent currentEvent) {
+ this.currentEvent = currentEvent;
+ }
+
+ int getCharSeqCount() {
+ return charSeqCount;
+ }
+
+ int getObjectCount() {
+ return objectCount;
+ }
+
+ @Override
+ public Level getLevel() {
+ return currentLevel;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final Message data, final Throwable t) {
+ assertEquals(level, currentLevel, "Incorrect Level. Expected " + currentLevel + ", actual " + level);
+ if (marker == null) {
+ if (currentEvent.markerName != null) {
+ fail("Incorrect marker. Expected " + currentEvent.markerName + ", actual is null");
+ }
+ } else {
+ if (currentEvent.markerName == null) {
+ fail("Incorrect marker. Expected null. Actual is " + marker.getName());
+ } else {
+ assertEquals(currentEvent.markerName, marker.getName(),
+ "Incorrect marker. Expected " + currentEvent.markerName + ", actual " + marker.getName());
+ }
+ }
+ if (data == null) {
+ if (currentEvent.data != null) {
+ fail("Incorrect message. Expected " + currentEvent.data + ", actual is null");
+ }
+ } else {
+ if (currentEvent.data == null) {
+ fail("Incorrect message. Expected null. Actual is " + data.getFormattedMessage());
+ } else {
+ assertTrue(
+ data.getClass().isAssignableFrom(currentEvent.data.getClass()),
+ "Incorrect message type. Expected " + currentEvent.data + ", actual " + data);
+ assertEquals(currentEvent.data.getFormattedMessage(), data.getFormattedMessage(),
+ "Incorrect message. Expected " + currentEvent.data.getFormattedMessage() + ", actual " +
+ data.getFormattedMessage());
+ }
+ }
+ if (t == null) {
+ if (currentEvent.t != null) {
+ fail("Incorrect Throwable. Expected " + currentEvent.t + ", actual is null");
+ }
+ } else {
+ if (currentEvent.t == null) {
+ fail("Incorrect Throwable. Expected null. Actual is " + t);
+ } else {
+ assertEquals(currentEvent.t, t, "Incorrect Throwable. Expected " + currentEvent.t + ", actual " + t);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final CharSequence data, final Throwable t) {
+ charSeqCount++;
+ return isEnabled(level, marker, (Message) new SimpleMessage(data), t);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final Object data, final Throwable t) {
+ objectCount++;
+ return isEnabled(level, marker, new ObjectMessage(data), t);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String data) {
+ return isEnabled(level, marker, (Message) new SimpleMessage(data), null);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String data, final Object... p1) {
+ return isEnabled(level, marker, new ParameterizedMessage(data, p1), null);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0), null);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
+ final Object p1) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1), null);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
+ final Object p1, final Object p2) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1, p2), null);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
+ final Object p1, final Object p2, final Object p3) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1, p2, p3), null);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
+ final Object p1, final Object p2, final Object p3,
+ final Object p4) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1, p2, p3, p4), null);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
+ final Object p1, final Object p2, final Object p3,
+ final Object p4, final Object p5) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5), null);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
+ final Object p1, final Object p2, final Object p3,
+ final Object p4, final Object p5, final Object p6) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6), null);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
+ final Object p1, final Object p2, final Object p3,
+ final Object p4, final Object p5, final Object p6,
+ final Object p7) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6, p7), null);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
+ final Object p1, final Object p2, final Object p3,
+ final Object p4, final Object p5, final Object p6,
+ final Object p7, final Object p8) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6, p7, p8), null);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
+ final Object p1, final Object p2, final Object p3,
+ final Object p4, final Object p5, final Object p6,
+ final Object p7, final Object p8, final Object p9) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9),
+ null);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String data, final Throwable t) {
+ return isEnabled(level, marker, (Message) new SimpleMessage(data), t);
+ }
+
+ @Override
+ public void logMessage(final String fqcn, final Level level, final Marker marker, final Message data, final Throwable t) {
+ assertEquals(level, currentLevel, "Incorrect Level. Expected " + currentLevel + ", actual " + level);
+ if (marker == null) {
+ if (currentEvent.markerName != null) {
+ fail("Incorrect marker. Expected " + currentEvent.markerName + ", actual is null");
+ }
+ } else {
+ if (currentEvent.markerName == null) {
+ fail("Incorrect marker. Expected null. Actual is " + marker.getName());
+ } else {
+ assertEquals(currentEvent.markerName, marker.getName(),
+ "Incorrect marker. Expected " + currentEvent.markerName + ", actual " + marker.getName());
+ }
+ }
+ if (data == null) {
+ if (currentEvent.data != null) {
+ fail("Incorrect message. Expected " + currentEvent.data + ", actual is null");
+ }
+ } else {
+ if (currentEvent.data == null) {
+ fail("Incorrect message. Expected null. Actual is " + data.getFormattedMessage());
+ } else {
+ assertTrue(
+ data.getClass().isAssignableFrom(currentEvent.data.getClass()),
+ "Incorrect message type. Expected " + currentEvent.data + ", actual " + data);
+ assertEquals(currentEvent.data.getFormattedMessage(), data.getFormattedMessage(),
+ "Incorrect message. Expected " + currentEvent.data.getFormattedMessage() + ", actual " +
+ data.getFormattedMessage());
+ }
+ }
+ if (t == null) {
+ if (currentEvent.t != null) {
+ fail("Incorrect Throwable. Expected " + currentEvent.t + ", actual is null");
+ }
+ } else {
+ if (currentEvent.t == null) {
+ fail("Incorrect Throwable. Expected null. Actual is " + t);
+ } else {
+ assertEquals(currentEvent.t, t, "Incorrect Throwable. Expected " + currentEvent.t + ", actual " + t);
+ }
+ }
+ }
+ }
+
+ private static class LogEvent {
+ String markerName;
+ Message data;
+ Throwable t;
+
+ public LogEvent(final String markerName, final Message data, final Throwable t) {
+ this.markerName = markerName;
+ this.data = data;
+ this.t = t;
+ }
+ }
+
+ private static class ThrowableExpectingLogger extends AbstractLogger {
+ private static final long serialVersionUID = -7218195998038685039L;
+ private final boolean expectingThrowables;
+
+ ThrowableExpectingLogger(final boolean expectingThrowables) {
+ super("ThrowableExpectingLogger", ParameterizedMessageFactory.INSTANCE);
+ this.expectingThrowables = expectingThrowables;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final Message message, final Throwable t) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final CharSequence message, final Throwable t) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final Object message, final Throwable t) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Throwable t) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object... params) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, final Object p1) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, final Object p1, final Object p2) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, final Object p1, final Object p2, final Object p3) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, final Object p6) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, final Object p6, final Object p7) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, final Object p6, final Object p7, final Object p8) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, final Object p6, final Object p7, final Object p8, final Object p9) {
+ return true;
+ }
+
+ @Override
+ public void logMessage(final String fqcn, final Level level, final Marker marker, final Message message, final Throwable t) {
+ if(expectingThrowables) {
+ assertNotNull(t, "Expected a Throwable but received null!");
+ } else {
+ assertNull(t, "Expected null but received a Throwable! "+t);
+ }
+ if (message != null) {
+ message.getFormattedMessage();
+ }
+ }
+
+ @Override
+ public Level getLevel() {
+ return Level.INFO;
+ }
+ }
+
+ private static class ThrowableMessage implements Message {
+ private static final long serialVersionUID = 1L;
+ private final Throwable throwable;
+
+ public ThrowableMessage(final Throwable throwable) {
+ this.throwable = throwable;
+ }
+
+ @Override
+ public String getFormattedMessage() {
+ return null;
+ }
+
+ @Override
+ public String getFormat() {
+ return null;
+ }
+
+ @Override
+ public Object[] getParameters() {
+ return new Object[0];
+ }
+
+ @Override
+ public Throwable getThrowable() {
+ return throwable;
+ }
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/CloseableThreadContextTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/CloseableThreadContextTest.java
new file mode 100644
index 00000000000..bc92763e1f9
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/CloseableThreadContextTest.java
@@ -0,0 +1,235 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j;
+
+import org.apache.logging.log4j.test.junit.UsingAnyThreadContext;
+import org.junit.jupiter.api.Test;
+import org.junitpioneer.jupiter.ReadsSystemProperty;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Tests {@link CloseableThreadContext}.
+ *
+ * @since 2.6
+ */
+@ReadsSystemProperty
+@UsingAnyThreadContext
+public class CloseableThreadContextTest {
+
+ private final String key = "key";
+ private final String value = "value";
+
+ @Test
+ public void shouldAddAnEntryToTheMap() {
+ try (final CloseableThreadContext.Instance ignored = CloseableThreadContext.put(key, value)) {
+ assertNotNull(ignored);
+ assertEquals(value, ThreadContext.get(key));
+ }
+ }
+
+ @Test
+ public void shouldAddTwoEntriesToTheMap() {
+ final String key2 = "key2";
+ final String value2 = "value2";
+ try (final CloseableThreadContext.Instance ignored = CloseableThreadContext.put(key, value).put(key2, value2)) {
+ assertNotNull(ignored);
+ assertEquals(value, ThreadContext.get(key));
+ assertEquals(value2, ThreadContext.get(key2));
+ }
+ }
+
+ @Test
+ public void shouldNestEntries() {
+ final String oldValue = "oldValue";
+ final String innerValue = "innerValue";
+ ThreadContext.put(key, oldValue);
+ try (final CloseableThreadContext.Instance ignored = CloseableThreadContext.put(key, value)) {
+ assertNotNull(ignored);
+ assertEquals(value, ThreadContext.get(key));
+ try (final CloseableThreadContext.Instance ignored2 = CloseableThreadContext.put(key, innerValue)) {
+ assertNotNull(ignored2);
+ assertEquals(innerValue, ThreadContext.get(key));
+ }
+ assertEquals(value, ThreadContext.get(key));
+ }
+ assertEquals(oldValue, ThreadContext.get(key));
+ }
+
+ @Test
+ public void shouldPreserveOldEntriesFromTheMapWhenAutoClosed() {
+ final String oldValue = "oldValue";
+ ThreadContext.put(key, oldValue);
+ try (final CloseableThreadContext.Instance ignored = CloseableThreadContext.put(key, value)) {
+ assertNotNull(ignored);
+ assertEquals(value, ThreadContext.get(key));
+ }
+ assertEquals(oldValue, ThreadContext.get(key));
+ }
+
+ @Test
+ public void ifTheSameKeyIsAddedTwiceTheOriginalShouldBeUsed() {
+ final String oldValue = "oldValue";
+ final String secondValue = "innerValue";
+ ThreadContext.put(key, oldValue);
+ try (final CloseableThreadContext.Instance ignored = CloseableThreadContext.put(key, value).put(key, secondValue)) {
+ assertNotNull(ignored);
+ assertEquals(secondValue, ThreadContext.get(key));
+ }
+ assertEquals(oldValue, ThreadContext.get(key));
+ }
+
+ @Test
+ public void shouldPushAndPopAnEntryToTheStack() {
+ final String message = "message";
+ try (final CloseableThreadContext.Instance ignored = CloseableThreadContext.push(message)) {
+ assertNotNull(ignored);
+ assertEquals(message, ThreadContext.peek());
+ }
+ assertEquals("", ThreadContext.peek());
+ }
+
+ @Test
+ public void shouldPushAndPopTwoEntriesToTheStack() {
+ final String message1 = "message1";
+ final String message2 = "message2";
+ try (final CloseableThreadContext.Instance ignored = CloseableThreadContext.push(message1).push(message2)) {
+ assertNotNull(ignored);
+ assertEquals(message2, ThreadContext.peek());
+ }
+ assertEquals("", ThreadContext.peek());
+ }
+
+ @Test
+ public void shouldPushAndPopAParameterizedEntryToTheStack() {
+ final String parameterizedMessage = "message {}";
+ final String parameterizedMessageParameter = "param";
+ final String formattedMessage = parameterizedMessage.replace("{}", parameterizedMessageParameter);
+ try (final CloseableThreadContext.Instance ignored = CloseableThreadContext.push(parameterizedMessage,
+ parameterizedMessageParameter)) {
+ assertNotNull(ignored);
+ assertEquals(formattedMessage, ThreadContext.peek());
+ }
+ assertEquals("", ThreadContext.peek());
+ }
+
+ @Test
+ public void shouldRemoveAnEntryFromTheMapWhenAutoClosed() {
+ try (final CloseableThreadContext.Instance ignored = CloseableThreadContext.put(key, value)) {
+ assertNotNull(ignored);
+ assertEquals(value, ThreadContext.get(key));
+ }
+ assertFalse(ThreadContext.containsKey(key));
+ }
+
+ @Test
+ public void shouldAddEntriesToBothStackAndMap() {
+ final String stackValue = "something";
+ try (final CloseableThreadContext.Instance ignored = CloseableThreadContext.put(key, value).push(stackValue)) {
+ assertNotNull(ignored);
+ assertEquals(value, ThreadContext.get(key));
+ assertEquals(stackValue, ThreadContext.peek());
+ }
+ assertFalse(ThreadContext.containsKey(key));
+ assertEquals("", ThreadContext.peek());
+ }
+
+ @Test
+ public void canReuseCloseableThreadContext() {
+ final String stackValue = "something";
+ // Create a ctc and close it
+ final CloseableThreadContext.Instance ctc = CloseableThreadContext.push(stackValue).put(key, value);
+ assertNotNull(ctc);
+ assertEquals(value, ThreadContext.get(key));
+ assertEquals(stackValue, ThreadContext.peek());
+ ctc.close();
+
+ assertFalse(ThreadContext.containsKey(key));
+ assertEquals("", ThreadContext.peek());
+
+ final String anotherKey = "key2";
+ final String anotherValue = "value2";
+ final String anotherStackValue = "something else";
+ // Use it again
+ ctc.push(anotherStackValue).put(anotherKey, anotherValue);
+ assertEquals(anotherValue, ThreadContext.get(anotherKey));
+ assertEquals(anotherStackValue, ThreadContext.peek());
+ ctc.close();
+
+ assertFalse(ThreadContext.containsKey(anotherKey));
+ assertEquals("", ThreadContext.peek());
+ }
+
+ @Test
+ public void closeIsIdempotent() {
+
+ final String originalMapValue = "map to keep";
+ final String originalStackValue = "stack to keep";
+ ThreadContext.put(key, originalMapValue);
+ ThreadContext.push(originalStackValue);
+
+ final String newMapValue = "temp map value";
+ final String newStackValue = "temp stack to keep";
+ final CloseableThreadContext.Instance ctc = CloseableThreadContext.push(newStackValue).put(key, newMapValue);
+ assertNotNull(ctc);
+
+ ctc.close();
+ assertEquals(originalMapValue, ThreadContext.get(key));
+ assertEquals(originalStackValue, ThreadContext.peek());
+
+ ctc.close();
+ assertEquals(originalMapValue, ThreadContext.get(key));
+ assertEquals(originalStackValue, ThreadContext.peek());
+ }
+
+ @Test
+ public void putAllWillPutAllValues() {
+
+ final String oldValue = "oldValue";
+ ThreadContext.put(key, oldValue);
+
+ final Map valuesToPut = new HashMap<>();
+ valuesToPut.put(key, value);
+
+ try (final CloseableThreadContext.Instance ignored = CloseableThreadContext.putAll(valuesToPut)) {
+ assertNotNull(ignored);
+ assertEquals(value, ThreadContext.get(key));
+ }
+ assertEquals(oldValue, ThreadContext.get(key));
+
+ }
+
+ @Test
+ public void pushAllWillPushAllValues() {
+
+ ThreadContext.push(key);
+ final List messages = ThreadContext.getImmutableStack().asList();
+ ThreadContext.pop();
+
+ try (final CloseableThreadContext.Instance ignored = CloseableThreadContext.pushAll(messages)) {
+ assertNotNull(ignored);
+ assertEquals(key, ThreadContext.peek());
+ }
+ assertEquals("", ThreadContext.peek());
+
+ }
+
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/EventLoggerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/EventLoggerTest.java
similarity index 80%
rename from log4j-api/src/test/java/org/apache/logging/log4j/EventLoggerTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/EventLoggerTest.java
index 181a34481ed..c8d504c20ec 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/EventLoggerTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/EventLoggerTest.java
@@ -16,26 +16,25 @@
*/
package org.apache.logging.log4j;
-import java.util.List;
-import java.util.Locale;
-
import org.apache.logging.log4j.message.StructuredDataMessage;
-import org.junit.Before;
-import org.junit.Test;
+import org.apache.logging.log4j.test.TestLogger;
+import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceLock;
-import static org.hamcrest.CoreMatchers.*;
+import java.util.List;
+import java.util.Locale;
-import static org.junit.Assert.*;
+import static org.assertj.core.api.Assertions.assertThat;
-/**
- *
- */
+@UsingThreadContextMap
public class EventLoggerTest {
TestLogger logger = (TestLogger) LogManager.getLogger("EventLogger");
List results = logger.getEntries();
- @Before
+ @BeforeEach
public void setup() {
results.clear();
}
@@ -51,9 +50,9 @@ public void structuredData() {
msg.put("Amount", "200.00");
EventLogger.logEvent(msg);
ThreadContext.clearMap();
- assertEquals(1, results.size());
+ assertThat(results).hasSize(1);
final String expected = "EVENT OFF Audit [Transfer@18060 Amount=\"200.00\" FromAccount=\"123457\" ToAccount=\"123456\"] Transfer Complete";
- assertThat("Incorrect structured data", results.get(0), startsWith(expected));
+ assertThat(results.get(0)).startsWith(expected);
}
}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/LambdaLoggerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/LambdaLoggerTest.java
similarity index 98%
rename from log4j-api/src/test/java/org/apache/logging/log4j/LambdaLoggerTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/LambdaLoggerTest.java
index f6d1623d682..c0587edad40 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/LambdaLoggerTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/LambdaLoggerTest.java
@@ -17,17 +17,18 @@
package org.apache.logging.log4j;
-import java.util.ArrayList;
-import java.util.List;
-
import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.ReusableMessage;
import org.apache.logging.log4j.message.SimpleMessage;
import org.apache.logging.log4j.spi.AbstractLogger;
import org.apache.logging.log4j.util.Supplier;
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
/**
* Tests the AbstractLogger implementation of the Logger2 interface.
@@ -46,7 +47,9 @@ public LogEvent(final String fqcn, final Level level, final Marker marker, final
this.fqcn = fqcn;
this.level = level;
this.marker = marker;
- this.message = message;
+ this.message = (message instanceof ReusableMessage) ?
+ ((ReusableMessage) message).memento() :
+ message;
this.throwable = t;
}
}
@@ -206,10 +209,10 @@ public String get() {
final MySupplier supplier = new MySupplier();
final MySupplier supplier2 = new MySupplier();
- final Supplier[] supplierArray1 = new Supplier[] {supplier};
- final Supplier[] supplierArray2 = new Supplier[] {supplier, supplier2};
+ final Supplier>[] supplierArray1 = new Supplier>[] {supplier};
+ final Supplier>[] supplierArray2 = new Supplier>[] {supplier, supplier2};
- @Before
+ @BeforeEach
public void beforeEachTest() {
logger2.list.clear();
supplier.invoked = false;
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/LevelTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/LevelTest.java
new file mode 100644
index 00000000000..4ecf62c2b8b
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/LevelTest.java
@@ -0,0 +1,285 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+public class LevelTest {
+
+ @Test
+ public void testDefault() {
+ final Level level = Level.toLevel("Information", Level.ERROR);
+ assertNotNull(level);
+ assertEquals(Level.ERROR, level);
+ }
+
+ @Test
+ public void testForNameEquals() {
+ final String name = "Foo";
+ final int intValue = 1;
+ final Level level = Level.forName(name, intValue);
+ assertNotNull(level);
+ assertEquals(level, Level.forName(name, intValue));
+ assertEquals(level, Level.getLevel(name));
+ assertEquals(level, Level.toLevel(name));
+ assertEquals(intValue, Level.getLevel(name).intLevel());
+ }
+
+ @Test
+ public void testThrowsOnNull() {
+ assertThrowsExactly(IllegalArgumentException.class, () -> Level.forName(null, 100));
+ assertThrowsExactly(IllegalArgumentException.class, () -> Level.getLevel(null));
+ // the intLevel should be checked only if we create a new level
+ assertNull(Level.getLevel("Bar"));
+ assertThrowsExactly(IllegalArgumentException.class, () -> Level.forName("Bar", -1));
+ }
+
+ @Test
+ public void testGoodLevels() {
+ final Level level = Level.toLevel("INFO");
+ assertNotNull(level);
+ assertEquals(Level.INFO, level);
+ }
+
+ @Test
+ public void testLevelsWithSpaces() {
+ Level level = Level.toLevel(" INFO ");
+ assertNotNull(level);
+ assertEquals(Level.INFO, level);
+
+ level = Level.valueOf(" INFO ");
+ assertNotNull(level);
+ assertEquals(Level.INFO, level);
+ }
+
+ @Test
+ public void testIsInRangeErrorToDebug() {
+ assertFalse(Level.OFF.isInRange(Level.ERROR, Level.DEBUG));
+ assertFalse(Level.FATAL.isInRange(Level.ERROR, Level.DEBUG));
+ assertTrue(Level.ERROR.isInRange(Level.ERROR, Level.DEBUG));
+ assertTrue(Level.WARN.isInRange(Level.ERROR, Level.DEBUG));
+ assertTrue(Level.INFO.isInRange(Level.ERROR, Level.DEBUG));
+ assertTrue(Level.DEBUG.isInRange(Level.ERROR, Level.DEBUG));
+ assertFalse(Level.TRACE.isInRange(Level.ERROR, Level.DEBUG));
+ assertFalse(Level.ALL.isInRange(Level.ERROR, Level.DEBUG));
+ }
+
+ @Test
+ public void testIsInRangeFatalToTrace() {
+ assertFalse(Level.OFF.isInRange(Level.FATAL, Level.TRACE));
+ assertTrue(Level.FATAL.isInRange(Level.FATAL, Level.TRACE));
+ assertTrue(Level.ERROR.isInRange(Level.FATAL, Level.TRACE));
+ assertTrue(Level.WARN.isInRange(Level.FATAL, Level.TRACE));
+ assertTrue(Level.INFO.isInRange(Level.FATAL, Level.TRACE));
+ assertTrue(Level.DEBUG.isInRange(Level.FATAL, Level.TRACE));
+ assertTrue(Level.TRACE.isInRange(Level.FATAL, Level.TRACE));
+ assertFalse(Level.ALL.isInRange(Level.FATAL, Level.TRACE));
+ }
+
+ @Test
+ public void testIsInRangeOffToAll() {
+ assertTrue(Level.OFF.isInRange(Level.OFF, Level.ALL));
+ assertTrue(Level.FATAL.isInRange(Level.OFF, Level.ALL));
+ assertTrue(Level.ERROR.isInRange(Level.OFF, Level.ALL));
+ assertTrue(Level.WARN.isInRange(Level.OFF, Level.ALL));
+ assertTrue(Level.INFO.isInRange(Level.OFF, Level.ALL));
+ assertTrue(Level.DEBUG.isInRange(Level.OFF, Level.ALL));
+ assertTrue(Level.TRACE.isInRange(Level.OFF, Level.ALL));
+ assertTrue(Level.ALL.isInRange(Level.OFF, Level.ALL));
+ }
+
+ @Test
+ public void testIsInRangeSameLevels() {
+ // Level.OFF
+ assertTrue(Level.OFF.isInRange(Level.OFF, Level.OFF));
+ assertFalse(Level.OFF.isInRange(Level.FATAL, Level.FATAL));
+ assertFalse(Level.OFF.isInRange(Level.ERROR, Level.ERROR));
+ assertFalse(Level.OFF.isInRange(Level.WARN, Level.WARN));
+ assertFalse(Level.OFF.isInRange(Level.INFO, Level.INFO));
+ assertFalse(Level.OFF.isInRange(Level.DEBUG, Level.DEBUG));
+ assertFalse(Level.OFF.isInRange(Level.TRACE, Level.TRACE));
+ assertFalse(Level.OFF.isInRange(Level.ALL, Level.ALL));
+ // Level.FATAL
+ assertFalse(Level.FATAL.isInRange(Level.OFF, Level.OFF));
+ assertTrue(Level.FATAL.isInRange(Level.FATAL, Level.FATAL));
+ assertFalse(Level.FATAL.isInRange(Level.ERROR, Level.ERROR));
+ assertFalse(Level.FATAL.isInRange(Level.WARN, Level.WARN));
+ assertFalse(Level.FATAL.isInRange(Level.INFO, Level.INFO));
+ assertFalse(Level.FATAL.isInRange(Level.DEBUG, Level.DEBUG));
+ assertFalse(Level.FATAL.isInRange(Level.TRACE, Level.TRACE));
+ assertFalse(Level.FATAL.isInRange(Level.ALL, Level.ALL));
+ // Level.ERROR
+ assertFalse(Level.ERROR.isInRange(Level.OFF, Level.OFF));
+ assertFalse(Level.ERROR.isInRange(Level.FATAL, Level.FATAL));
+ assertTrue(Level.ERROR.isInRange(Level.ERROR, Level.ERROR));
+ assertFalse(Level.ERROR.isInRange(Level.WARN, Level.WARN));
+ assertFalse(Level.ERROR.isInRange(Level.INFO, Level.INFO));
+ assertFalse(Level.ERROR.isInRange(Level.DEBUG, Level.DEBUG));
+ assertFalse(Level.ERROR.isInRange(Level.TRACE, Level.TRACE));
+ assertFalse(Level.ERROR.isInRange(Level.ALL, Level.ALL));
+ // Level.WARN
+ assertFalse(Level.WARN.isInRange(Level.OFF, Level.OFF));
+ assertFalse(Level.WARN.isInRange(Level.FATAL, Level.FATAL));
+ assertFalse(Level.WARN.isInRange(Level.ERROR, Level.ERROR));
+ assertTrue(Level.WARN.isInRange(Level.WARN, Level.WARN));
+ assertFalse(Level.WARN.isInRange(Level.INFO, Level.INFO));
+ assertFalse(Level.WARN.isInRange(Level.DEBUG, Level.DEBUG));
+ assertFalse(Level.WARN.isInRange(Level.TRACE, Level.TRACE));
+ assertFalse(Level.WARN.isInRange(Level.ALL, Level.ALL));
+ // Level.INFO
+ assertFalse(Level.INFO.isInRange(Level.OFF, Level.OFF));
+ assertFalse(Level.INFO.isInRange(Level.FATAL, Level.FATAL));
+ assertFalse(Level.INFO.isInRange(Level.ERROR, Level.ERROR));
+ assertFalse(Level.INFO.isInRange(Level.WARN, Level.WARN));
+ assertTrue(Level.INFO.isInRange(Level.INFO, Level.INFO));
+ assertFalse(Level.INFO.isInRange(Level.DEBUG, Level.DEBUG));
+ assertFalse(Level.INFO.isInRange(Level.TRACE, Level.TRACE));
+ assertFalse(Level.INFO.isInRange(Level.ALL, Level.ALL));
+ // Level.DEBUG
+ assertFalse(Level.DEBUG.isInRange(Level.OFF, Level.OFF));
+ assertFalse(Level.DEBUG.isInRange(Level.FATAL, Level.FATAL));
+ assertFalse(Level.DEBUG.isInRange(Level.ERROR, Level.ERROR));
+ assertFalse(Level.DEBUG.isInRange(Level.WARN, Level.WARN));
+ assertFalse(Level.DEBUG.isInRange(Level.INFO, Level.INFO));
+ assertTrue(Level.DEBUG.isInRange(Level.DEBUG, Level.DEBUG));
+ assertFalse(Level.DEBUG.isInRange(Level.TRACE, Level.TRACE));
+ assertFalse(Level.DEBUG.isInRange(Level.ALL, Level.ALL));
+ // Level.TRACE
+ assertFalse(Level.TRACE.isInRange(Level.OFF, Level.OFF));
+ assertFalse(Level.TRACE.isInRange(Level.FATAL, Level.FATAL));
+ assertFalse(Level.TRACE.isInRange(Level.ERROR, Level.ERROR));
+ assertFalse(Level.TRACE.isInRange(Level.WARN, Level.WARN));
+ assertFalse(Level.TRACE.isInRange(Level.INFO, Level.INFO));
+ assertFalse(Level.TRACE.isInRange(Level.DEBUG, Level.DEBUG));
+ assertTrue(Level.TRACE.isInRange(Level.TRACE, Level.TRACE));
+ assertFalse(Level.TRACE.isInRange(Level.ALL, Level.ALL));
+ // Level.ALL
+ assertFalse(Level.ALL.isInRange(Level.OFF, Level.OFF));
+ assertFalse(Level.ALL.isInRange(Level.FATAL, Level.FATAL));
+ assertFalse(Level.ALL.isInRange(Level.ERROR, Level.ERROR));
+ assertFalse(Level.ALL.isInRange(Level.WARN, Level.WARN));
+ assertFalse(Level.ALL.isInRange(Level.INFO, Level.INFO));
+ assertFalse(Level.ALL.isInRange(Level.DEBUG, Level.DEBUG));
+ assertFalse(Level.ALL.isInRange(Level.TRACE, Level.TRACE));
+ assertTrue(Level.ALL.isInRange(Level.ALL, Level.ALL));
+ }
+
+ @Test
+ public void testIsInRangeWarnToInfo() {
+ assertFalse(Level.OFF.isInRange(Level.WARN, Level.INFO));
+ assertFalse(Level.FATAL.isInRange(Level.WARN, Level.INFO));
+ assertFalse(Level.ERROR.isInRange(Level.WARN, Level.INFO));
+ assertTrue(Level.WARN.isInRange(Level.WARN, Level.INFO));
+ assertTrue(Level.INFO.isInRange(Level.WARN, Level.INFO));
+ assertFalse(Level.DEBUG.isInRange(Level.WARN, Level.INFO));
+ assertFalse(Level.TRACE.isInRange(Level.WARN, Level.INFO));
+ assertFalse(Level.ALL.isInRange(Level.WARN, Level.INFO));
+ }
+
+ @Test
+ public void testIsLessSpecificThan() {
+ // Level.OFF
+ assertTrue(Level.OFF.isLessSpecificThan(Level.OFF));
+ assertFalse(Level.OFF.isLessSpecificThan(Level.FATAL));
+ assertFalse(Level.OFF.isLessSpecificThan(Level.ERROR));
+ assertFalse(Level.OFF.isLessSpecificThan(Level.WARN));
+ assertFalse(Level.OFF.isLessSpecificThan(Level.INFO));
+ assertFalse(Level.OFF.isLessSpecificThan(Level.DEBUG));
+ assertFalse(Level.OFF.isLessSpecificThan(Level.TRACE));
+ assertFalse(Level.OFF.isLessSpecificThan(Level.ALL));
+ // Level.FATAL
+ assertTrue(Level.FATAL.isLessSpecificThan(Level.OFF));
+ assertTrue(Level.FATAL.isLessSpecificThan(Level.FATAL));
+ assertFalse(Level.FATAL.isLessSpecificThan(Level.ERROR));
+ assertFalse(Level.FATAL.isLessSpecificThan(Level.WARN));
+ assertFalse(Level.FATAL.isLessSpecificThan(Level.INFO));
+ assertFalse(Level.FATAL.isLessSpecificThan(Level.DEBUG));
+ assertFalse(Level.FATAL.isLessSpecificThan(Level.TRACE));
+ assertFalse(Level.FATAL.isLessSpecificThan(Level.ALL));
+ // Level.ERROR
+ assertTrue(Level.ERROR.isLessSpecificThan(Level.OFF));
+ assertTrue(Level.ERROR.isLessSpecificThan(Level.FATAL));
+ assertTrue(Level.ERROR.isLessSpecificThan(Level.ERROR));
+ assertFalse(Level.ERROR.isLessSpecificThan(Level.WARN));
+ assertFalse(Level.ERROR.isLessSpecificThan(Level.INFO));
+ assertFalse(Level.ERROR.isLessSpecificThan(Level.DEBUG));
+ assertFalse(Level.ERROR.isLessSpecificThan(Level.TRACE));
+ assertFalse(Level.ERROR.isLessSpecificThan(Level.ALL));
+ // Level.ERROR
+ assertTrue(Level.WARN.isLessSpecificThan(Level.OFF));
+ assertTrue(Level.WARN.isLessSpecificThan(Level.FATAL));
+ assertTrue(Level.WARN.isLessSpecificThan(Level.ERROR));
+ assertTrue(Level.WARN.isLessSpecificThan(Level.WARN));
+ assertFalse(Level.WARN.isLessSpecificThan(Level.INFO));
+ assertFalse(Level.WARN.isLessSpecificThan(Level.DEBUG));
+ assertFalse(Level.WARN.isLessSpecificThan(Level.TRACE));
+ assertFalse(Level.WARN.isLessSpecificThan(Level.ALL));
+ // Level.WARN
+ assertTrue(Level.WARN.isLessSpecificThan(Level.OFF));
+ assertTrue(Level.WARN.isLessSpecificThan(Level.FATAL));
+ assertTrue(Level.WARN.isLessSpecificThan(Level.ERROR));
+ assertTrue(Level.WARN.isLessSpecificThan(Level.WARN));
+ assertFalse(Level.WARN.isLessSpecificThan(Level.INFO));
+ assertFalse(Level.WARN.isLessSpecificThan(Level.DEBUG));
+ assertFalse(Level.WARN.isLessSpecificThan(Level.TRACE));
+ assertFalse(Level.WARN.isLessSpecificThan(Level.ALL));
+ // Level.INFO
+ assertTrue(Level.INFO.isLessSpecificThan(Level.OFF));
+ assertTrue(Level.INFO.isLessSpecificThan(Level.FATAL));
+ assertTrue(Level.INFO.isLessSpecificThan(Level.ERROR));
+ assertTrue(Level.INFO.isLessSpecificThan(Level.WARN));
+ assertTrue(Level.INFO.isLessSpecificThan(Level.INFO));
+ assertFalse(Level.INFO.isLessSpecificThan(Level.DEBUG));
+ assertFalse(Level.INFO.isLessSpecificThan(Level.TRACE));
+ assertFalse(Level.INFO.isLessSpecificThan(Level.ALL));
+ // Level.DEBUG
+ assertTrue(Level.DEBUG.isLessSpecificThan(Level.OFF));
+ assertTrue(Level.DEBUG.isLessSpecificThan(Level.FATAL));
+ assertTrue(Level.DEBUG.isLessSpecificThan(Level.ERROR));
+ assertTrue(Level.DEBUG.isLessSpecificThan(Level.WARN));
+ assertTrue(Level.DEBUG.isLessSpecificThan(Level.INFO));
+ assertTrue(Level.DEBUG.isLessSpecificThan(Level.DEBUG));
+ assertFalse(Level.DEBUG.isLessSpecificThan(Level.TRACE));
+ assertFalse(Level.DEBUG.isLessSpecificThan(Level.ALL));
+ // Level.TRACE
+ assertTrue(Level.TRACE.isLessSpecificThan(Level.OFF));
+ assertTrue(Level.TRACE.isLessSpecificThan(Level.FATAL));
+ assertTrue(Level.TRACE.isLessSpecificThan(Level.ERROR));
+ assertTrue(Level.TRACE.isLessSpecificThan(Level.WARN));
+ assertTrue(Level.TRACE.isLessSpecificThan(Level.INFO));
+ assertTrue(Level.TRACE.isLessSpecificThan(Level.DEBUG));
+ assertTrue(Level.TRACE.isLessSpecificThan(Level.TRACE));
+ assertFalse(Level.TRACE.isLessSpecificThan(Level.ALL));
+ // Level.ALL
+ assertTrue(Level.ALL.isLessSpecificThan(Level.OFF));
+ assertTrue(Level.ALL.isLessSpecificThan(Level.FATAL));
+ assertTrue(Level.ALL.isLessSpecificThan(Level.ERROR));
+ assertTrue(Level.ALL.isLessSpecificThan(Level.WARN));
+ assertTrue(Level.ALL.isLessSpecificThan(Level.INFO));
+ assertTrue(Level.ALL.isLessSpecificThan(Level.DEBUG));
+ assertTrue(Level.ALL.isLessSpecificThan(Level.TRACE));
+ assertTrue(Level.ALL.isLessSpecificThan(Level.ALL));
+ }
+
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/LogManagerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/LogManagerTest.java
new file mode 100644
index 00000000000..845eb9b9b3d
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/LogManagerTest.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j;
+
+import org.apache.logging.log4j.message.ParameterizedMessageFactory;
+import org.apache.logging.log4j.spi.LoggerContext;
+import org.junit.jupiter.api.Test;
+import org.junitpioneer.jupiter.ReadsSystemProperty;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+@ReadsSystemProperty
+public class LogManagerTest {
+
+ @SuppressWarnings("InnerClassMayBeStatic")
+ class Inner {
+ final Logger LOGGER = LogManager.getLogger();
+ }
+
+ @SuppressWarnings("InnerClassMayBeStatic")
+ class InnerByClass {
+ final Logger LOGGER = LogManager.getLogger(InnerByClass.class);
+ }
+
+ static class StaticInner {
+ final static Logger LOGGER = LogManager.getLogger();
+ }
+
+ static class StaticInnerByClass {
+ final static Logger LOGGER = LogManager.getLogger(StaticInnerByClass.class);
+ }
+
+ @Test
+ public void testGetLogger() {
+ Logger logger = LogManager.getLogger();
+ assertNotNull(logger, "No Logger returned");
+ assertEquals(LogManagerTest.class.getName(), logger.getName(), "Incorrect Logger name: " + logger.getName());
+ logger = LogManager.getLogger(ParameterizedMessageFactory.INSTANCE);
+ assertNotNull(logger, "No Logger returned");
+ assertEquals(LogManagerTest.class.getName(), logger.getName(), "Incorrect Logger name: " + logger.getName());
+ logger = LogManager.getLogger((Class>) null);
+ assertNotNull(logger, "No Logger returned");
+ assertEquals(LogManagerTest.class.getName(), logger.getName(), "Incorrect Logger name: " + logger.getName());
+ logger = LogManager.getLogger((Class>) null, ParameterizedMessageFactory.INSTANCE);
+ assertNotNull(logger, "No Logger returned");
+ assertEquals(LogManagerTest.class.getName(), logger.getName(), "Incorrect Logger name: " + logger.getName());
+ logger = LogManager.getLogger((String) null);
+ assertNotNull(logger, "No Logger returned");
+ assertEquals(LogManagerTest.class.getName(), logger.getName(), "Incorrect Logger name: " + logger.getName());
+ logger = LogManager.getLogger((String) null, ParameterizedMessageFactory.INSTANCE);
+ assertNotNull(logger, "No Logger returned");
+ assertEquals(LogManagerTest.class.getName(), logger.getName(), "Incorrect Logger name: " + logger.getName());
+ logger = LogManager.getLogger((Object) null);
+ assertNotNull(logger, "No Logger returned");
+ assertEquals(LogManagerTest.class.getName(), logger.getName(), "Incorrect Logger name: " + logger.getName());
+ logger = LogManager.getLogger((Object) null, ParameterizedMessageFactory.INSTANCE);
+ assertNotNull(logger, "No Logger returned");
+ assertEquals(LogManagerTest.class.getName(), logger.getName(), "Incorrect Logger name: " + logger.getName());
+ }
+
+ @Test
+ public void testGetLoggerForAnonymousInnerClass1() throws IOException {
+ final Closeable closeable = new Closeable() {
+
+ final Logger LOGGER = LogManager.getLogger();
+
+ @Override
+ public void close() throws IOException {
+ assertEquals("org.apache.logging.log4j.LogManagerTest$1", LOGGER.getName());
+ }
+ };
+ closeable.close();
+ }
+
+ @Test
+ public void testGetLoggerForAnonymousInnerClass2() throws IOException {
+ final Closeable closeable = new Closeable() {
+
+ final Logger LOGGER = LogManager.getLogger(getClass());
+
+ @Override
+ public void close() throws IOException {
+ assertEquals("org.apache.logging.log4j.LogManagerTest$2", LOGGER.getName());
+ }
+ };
+ closeable.close();
+ }
+
+ @Test
+ public void testGetLoggerForInner() {
+ assertEquals("org.apache.logging.log4j.LogManagerTest.Inner", new Inner().LOGGER.getName());
+ }
+
+ @Test
+ public void testGetLoggerForInnerByClass() {
+ assertEquals("org.apache.logging.log4j.LogManagerTest.InnerByClass", new InnerByClass().LOGGER.getName());
+ }
+
+ @Test
+ public void testGetLoggerForStaticInner() {
+ assertEquals("org.apache.logging.log4j.LogManagerTest.StaticInner", StaticInner.LOGGER.getName());
+ }
+
+ @Test
+ public void testGetLoggerForStaticInnerByClass() {
+ assertEquals("org.apache.logging.log4j.LogManagerTest.StaticInnerByClass", StaticInnerByClass.LOGGER.getName());
+ }
+
+ @Test
+ public void testShutdown() {
+ final LoggerContext loggerContext = LogManager.getContext(false);
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerSupplierTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerSupplierTest.java
new file mode 100644
index 00000000000..81111808cac
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerSupplierTest.java
@@ -0,0 +1,189 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.Properties;
+
+import org.apache.logging.log4j.message.FormattedMessage;
+import org.apache.logging.log4j.message.JsonMessage;
+import org.apache.logging.log4j.message.LocalizedMessage;
+import org.apache.logging.log4j.message.MessageFormatMessage;
+import org.apache.logging.log4j.message.ObjectArrayMessage;
+import org.apache.logging.log4j.message.ObjectMessage;
+import org.apache.logging.log4j.message.ParameterizedMessage;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.message.StringFormattedMessage;
+import org.apache.logging.log4j.message.ThreadDumpMessage;
+import org.apache.logging.log4j.test.TestLogger;
+import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
+import org.apache.logging.log4j.util.Supplier;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.api.parallel.Resources;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests Logger APIs with {@link Supplier}.
+ */
+@ResourceLock(Resources.LOCALE)
+@UsingThreadContextMap
+public class LoggerSupplierTest {
+
+ private final TestLogger logger = (TestLogger) LogManager.getLogger(LoggerSupplierTest.class);
+
+ private final List results = logger.getEntries();
+
+ Locale defaultLocale;
+
+ @Test
+ public void flowTracing_SupplierOfFormattedMessage() {
+ logger.traceEntry(() -> new FormattedMessage("int foo={}", 1234567890));
+ assertThat(results).hasSize(1);
+ String entry = results.get(0);
+ assertThat(entry).startsWith("ENTER[ FLOW ] TRACE Enter")
+ .contains("(int foo=1234567890)")
+ .doesNotContain("FormattedMessage");
+ }
+
+ @Test
+ public void flowTracing_SupplierOfJsonMessage() {
+ Properties props = new Properties();
+ props.setProperty("foo", "bar");
+ logger.traceEntry(() -> new JsonMessage(props));
+ assertThat(results).hasSize(1);
+ String entry = results.get(0);
+ assertThat(entry).startsWith("ENTER[ FLOW ] TRACE Enter")
+ .contains("\"foo\":\"bar\"")
+ .doesNotContain("JsonMessage");
+ }
+
+ @Test
+ public void flowTracing_SupplierOfLocalizedMessage() {
+ logger.traceEntry(() -> new LocalizedMessage("int foo={}", 1234567890));
+ assertThat(results).hasSize(1);
+ String entry = results.get(0);
+ assertThat(entry).startsWith("ENTER[ FLOW ] TRACE Enter")
+ .contains("(int foo=1234567890)")
+ .doesNotContain("LocalizedMessage");
+ }
+
+ @Test
+ public void flowTracing_SupplierOfLong() {
+ logger.traceEntry(() -> 1234567890L);
+ assertThat(results).hasSize(1);
+ String entry = results.get(0);
+ assertThat(entry).startsWith("ENTER[ FLOW ] TRACE Enter")
+ .contains("(1234567890)")
+ .doesNotContain("SimpleMessage");
+ }
+
+ @Test
+ public void flowTracing_SupplierOfMessageFormatMessage() {
+ logger.traceEntry(() -> new MessageFormatMessage("int foo={0}", 1234567890));
+ assertThat(results).hasSize(1);
+ String entry = results.get(0);
+ assertThat(entry).startsWith("ENTER[ FLOW ] TRACE Enter")
+ .contains("(int foo=1,234,567,890)")
+ .doesNotContain("MessageFormatMessage");
+ }
+
+ @Test
+ public void flowTracing_SupplierOfObjectArrayMessage() {
+ logger.traceEntry(() -> new ObjectArrayMessage(1234567890));
+ assertThat(results).hasSize(1);
+ String entry = results.get(0);
+ assertThat(entry).startsWith("ENTER[ FLOW ] TRACE Enter")
+ .contains("([1234567890])")
+ .doesNotContain("ObjectArrayMessage");
+ }
+
+ @Test
+ public void flowTracing_SupplierOfObjectMessage() {
+ logger.traceEntry(() -> new ObjectMessage(1234567890));
+ assertThat(results).hasSize(1);
+ String entry = results.get(0);
+ assertThat(entry).startsWith("ENTER[ FLOW ] TRACE Enter")
+ .contains("(1234567890)")
+ .doesNotContain("ObjectMessage");
+ }
+
+ @Test
+ public void flowTracing_SupplierOfParameterizedMessage() {
+ logger.traceEntry(() -> new ParameterizedMessage("int foo={}", 1234567890));
+ assertThat(results).hasSize(1);
+ String entry = results.get(0);
+ assertThat(entry).startsWith("ENTER[ FLOW ] TRACE Enter")
+ .contains("(int foo=1234567890)")
+ .doesNotContain("ParameterizedMessage");
+ }
+
+ @Test
+ public void flowTracing_SupplierOfSimpleMessage() {
+ logger.traceEntry(() -> new SimpleMessage("1234567890"));
+ assertThat(results).hasSize(1);
+ String entry = results.get(0);
+ assertThat(entry).startsWith("ENTER[ FLOW ] TRACE Enter")
+ .contains("(1234567890)")
+ .doesNotContain("SimpleMessage");
+ }
+
+ @Test
+ public void flowTracing_SupplierOfString() {
+ logger.traceEntry(() -> "1234567890");
+ assertThat(results).hasSize(1);
+ String entry = results.get(0);
+ assertThat(entry).startsWith("ENTER[ FLOW ] TRACE Enter")
+ .contains("(1234567890)")
+ .doesNotContain("SimpleMessage");
+ }
+
+ @Test
+ public void flowTracing_SupplierOfStringFormattedMessage() {
+ logger.traceEntry(() -> new StringFormattedMessage("int foo=%,d", 1234567890));
+ assertThat(results).hasSize(1);
+ String entry = results.get(0);
+ assertThat(entry).startsWith("ENTER[ FLOW ] TRACE Enter")
+ .contains("(int foo=1,234,567,890)")
+ .doesNotContain("StringFormattedMessage");
+ }
+
+ @Test
+ public void flowTracing_SupplierOfThreadDumpMessage() {
+ logger.traceEntry(() -> new ThreadDumpMessage("Title of ..."));
+ assertThat(results).hasSize(1);
+ String entry = results.get(0);
+ assertThat(entry).startsWith("ENTER[ FLOW ] TRACE Enter").contains("RUNNABLE", "Title of ...", getClass().getName());
+ }
+
+ @BeforeEach
+ public void setup() {
+ results.clear();
+ defaultLocale = Locale.getDefault(Locale.Category.FORMAT);
+ Locale.setDefault(Locale.Category.FORMAT, java.util.Locale.US);
+ }
+
+ @AfterEach
+ public void tearDown() {
+ Locale.setDefault(Locale.Category.FORMAT, defaultLocale);
+ }
+
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerTest.java
new file mode 100644
index 00000000000..a59505acb39
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerTest.java
@@ -0,0 +1,648 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.Properties;
+
+import org.apache.logging.log4j.message.EntryMessage;
+import org.apache.logging.log4j.message.JsonMessage;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.MessageFactory;
+import org.apache.logging.log4j.message.ObjectMessage;
+import org.apache.logging.log4j.message.ParameterizedMessageFactory;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.message.SimpleMessageFactory;
+import org.apache.logging.log4j.message.StringFormatterMessageFactory;
+import org.apache.logging.log4j.message.StructuredDataMessage;
+import org.apache.logging.log4j.test.TestLogger;
+import org.apache.logging.log4j.test.junit.Resources;
+import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
+import org.apache.logging.log4j.util.Strings;
+import org.apache.logging.log4j.util.Supplier;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junitpioneer.jupiter.ReadsSystemProperty;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@ResourceLock(value = Resources.MARKER_MANAGER, mode = ResourceAccessMode.READ)
+@ReadsSystemProperty
+public class LoggerTest {
+
+ private static class TestParameterizedMessageFactory {
+ // empty
+ }
+
+ private static class TestStringFormatterMessageFactory {
+ // empty
+ }
+
+ private final TestLogger logger = (TestLogger) LogManager.getLogger(LoggerTest.class);
+ private final Marker marker = MarkerManager.getMarker("test");
+ private final List results = logger.getEntries();
+
+ @Test
+ public void builder() {
+ logger.atDebug().withLocation().log("Hello");
+ logger.atError().withMarker(marker).log("Hello {}", "John");
+ logger.atWarn().withThrowable(new Throwable("This is a test")).log((Message) new SimpleMessage("Log4j rocks!"));
+ assertEquals(3, results.size());
+ assertThat("Incorrect message 1", results.get(0),
+ equalTo(" DEBUG org.apache.logging.log4j.LoggerTest.builder(LoggerTest.java:72) Hello"));
+ assertThat("Incorrect message 2", results.get(1), equalTo("test ERROR Hello John"));
+ assertThat("Incorrect message 3", results.get(2),
+ startsWith(" WARN Log4j rocks! java.lang.Throwable: This is a test"));
+ assertThat("Throwable incorrect in message 3", results.get(2),
+ containsString("org.apache.logging.log4j.LoggerTest.builder(LoggerTest.java:74)"));
+ }
+
+ @Test
+ public void basicFlow() {
+ logger.traceEntry();
+ logger.traceExit();
+ assertEquals(2, results.size());
+ assertThat(results.get(0)).isEqualTo("ENTER[ FLOW ] TRACE Enter");
+ assertThat(results.get(1)).isEqualTo("EXIT[ FLOW ] TRACE Exit");
+
+ }
+
+ @Test
+ public void flowTracingMessage() {
+ Properties props = new Properties();
+ props.setProperty("foo", "bar");
+ logger.traceEntry(new JsonMessage(props));
+ final Response response = new Response(-1, "Generic error");
+ logger.traceExit(new JsonMessage(response), response);
+ assertThat(results).hasSize(2);
+ assertThat(results.get(0)).startsWith("ENTER[ FLOW ] TRACE Enter").contains("\"foo\":\"bar\"");
+ assertThat(results.get(1)).startsWith("EXIT[ FLOW ] TRACE Exit").contains("\"message\":\"Generic error\"");
+ }
+
+ @Test
+ public void flowTracingString_ObjectArray1() {
+ logger.traceEntry("doFoo(a={}, b={})", 1, 2);
+ logger.traceExit("doFoo(a=1, b=2): {}", 3);
+ assertThat(results).hasSize(2);
+ assertThat(results.get(0)).startsWith("ENTER[ FLOW ] TRACE Enter").contains("doFoo(a=1, b=2)");
+ assertThat(results.get(1)).startsWith("EXIT[ FLOW ] TRACE Exit").contains("doFoo(a=1, b=2): 3");
+ }
+
+ @Test
+ public void flowTracingExitValueOnly() {
+ logger.traceEntry("doFoo(a={}, b={})", 1, 2);
+ logger.traceExit(3);
+ assertThat(results).hasSize(2);
+ assertThat(results.get(0)).startsWith("ENTER[ FLOW ] TRACE Enter").contains("doFoo(a=1, b=2)");
+ assertThat(results.get(1)).startsWith("EXIT[ FLOW ] TRACE Exit").contains("3");
+ }
+
+ @Test
+ public void flowTracingString_ObjectArray2() {
+ final EntryMessage msg = logger.traceEntry("doFoo(a={}, b={})", 1, 2);
+ logger.traceExit(msg, 3);
+ assertThat(results).hasSize(2);
+ assertThat(results.get(0)).startsWith("ENTER[ FLOW ] TRACE Enter").contains("doFoo(a=1, b=2)");
+ assertThat(results.get(1)).startsWith("EXIT[ FLOW ] TRACE Exit").contains("doFoo(a=1, b=2): 3");
+ }
+
+ @Test
+ public void flowTracingVoidReturn() {
+ final EntryMessage msg = logger.traceEntry("doFoo(a={}, b={})", 1, 2);
+ logger.traceExit(msg);
+ assertThat(results).hasSize(2);
+ assertThat(results.get(0)).startsWith("ENTER[ FLOW ] TRACE Enter").contains("doFoo(a=1, b=2)");
+ assertThat(results.get(1)).startsWith("EXIT[ FLOW ] TRACE Exit").endsWith("doFoo(a=1, b=2)");
+ }
+
+ @Test
+ public void flowTracingNoExitArgs() {
+ logger.traceEntry();
+ logger.traceExit();
+ assertThat(results).hasSize(2);
+ assertThat(results.get(0)).startsWith("ENTER[ FLOW ] TRACE Enter");
+ assertThat(results.get(1)).startsWith("EXIT[ FLOW ] TRACE Exit");
+ }
+
+ @Test
+ public void flowTracingNoArgs() {
+ final EntryMessage message = logger.traceEntry();
+ logger.traceExit(message);
+ assertThat(results).hasSize(2);
+ assertThat(results.get(0)).startsWith("ENTER[ FLOW ] TRACE Enter");
+ assertThat(results.get(1)).startsWith("EXIT[ FLOW ] TRACE Exit");
+ }
+
+ @Test
+ public void flowTracingString_SupplierOfObjectMessages() {
+ final EntryMessage msg = logger.traceEntry("doFoo(a={}, b={})", new Supplier() {
+ @Override
+ public Message get() {
+ return new ObjectMessage(1);
+ }
+ }, new Supplier() {
+ @Override
+ public Message get() {
+ return new ObjectMessage(2);
+ }
+ });
+ logger.traceExit(msg, 3);
+ assertThat(results).hasSize(2);
+ assertThat(results.get(0)).startsWith("ENTER[ FLOW ] TRACE Enter").contains("doFoo(a=1, b=2)");
+ assertThat(results.get(1)).startsWith("EXIT[ FLOW ] TRACE Exit").contains("doFoo(a=1, b=2): 3");
+ }
+
+ @Test
+ public void flowTracingString_SupplierOfStrings() {
+ final EntryMessage msg = logger.traceEntry("doFoo(a={}, b={})", new Supplier() {
+ @Override
+ public String get() {
+ return "1";
+ }
+ }, new Supplier() {
+ @Override
+ public String get() {
+ return "2";
+ }
+ });
+ logger.traceExit(msg, 3);
+ assertThat(results).hasSize(2);
+ assertThat(results.get(0)).startsWith("ENTER[ FLOW ] TRACE Enter").contains("doFoo(a=1, b=2)");
+ assertThat(results.get(1)).startsWith("EXIT[ FLOW ] TRACE Exit").contains("doFoo(a=1, b=2): 3");
+ }
+
+ @Test
+ public void flowTracingNoFormat() {
+ logger.traceEntry(null, 1, "2", new ObjectMessage(3));
+ logger.traceExit((String) null, 4);
+ assertThat(results).hasSize(2);
+ assertThat(results.get(0)).isEqualTo("ENTER[ FLOW ] TRACE Enter params(1, 2, 3)");
+ assertThat(results.get(1)).isEqualTo("EXIT[ FLOW ] TRACE Exit with(4)");
+ }
+
+ @Test
+ public void catching() {
+ try {
+ throw new NullPointerException();
+ } catch (final Exception e) {
+ logger.catching(e);
+ assertEquals(1, results.size());
+ assertThat("Incorrect Catching",
+ results.get(0), startsWith("CATCHING[ EXCEPTION ] ERROR Catching java.lang.NullPointerException"));
+ }
+ }
+
+ @Test
+ public void debug() {
+ logger.debug("Debug message");
+ assertEquals(1, results.size());
+ assertTrue(results.get(0).startsWith(" DEBUG Debug message"), "Incorrect message");
+ }
+
+ @Test
+ public void debugObject() {
+ logger.debug(new Date());
+ assertEquals(1, results.size());
+ assertTrue(results.get(0).length() > 7, "Invalid length");
+ }
+
+ @Test
+ public void debugWithParms() {
+ logger.debug("Hello, {}", "World");
+ assertEquals(1, results.size());
+ assertTrue(results.get(0).startsWith(" DEBUG Hello, World"), "Incorrect substitution");
+ }
+
+ @Test
+ public void debugWithParmsAndThrowable() {
+ logger.debug("Hello, {}", "World", new RuntimeException("Test Exception"));
+ assertEquals(1, results.size());
+ assertTrue(
+ results.get(0).startsWith(" DEBUG Hello, World java.lang.RuntimeException: Test Exception"),
+ "Unexpected results: " + results.get(0));
+ }
+
+ @Test
+ @ResourceLock("log4j2.TestLogger")
+ public void getFormatterLogger() {
+ // The TestLogger logger was already created in an instance variable for this class.
+ // The message factory is only used when the logger is created.
+ final TestLogger testLogger = (TestLogger) LogManager.getFormatterLogger();
+ final TestLogger altLogger = (TestLogger) LogManager.getFormatterLogger(getClass());
+ assertEquals(testLogger.getName(), altLogger.getName());
+ assertNotNull(testLogger);
+ assertMessageFactoryInstanceOf(testLogger.getMessageFactory(), StringFormatterMessageFactory.class);
+ assertEqualMessageFactory(StringFormatterMessageFactory.INSTANCE, testLogger);
+ testLogger.debug("%,d", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(String.format(" DEBUG %,d", Integer.MAX_VALUE), testLogger.getEntries().get(0));
+ }
+
+ @Test
+ @ResourceLock("log4j2.TestLogger")
+ public void getFormatterLogger_Class() {
+ // The TestLogger logger was already created in an instance variable for this class.
+ // The message factory is only used when the logger is created.
+ final TestLogger testLogger = (TestLogger) LogManager.getFormatterLogger(TestStringFormatterMessageFactory.class);
+ assertNotNull(testLogger);
+ assertMessageFactoryInstanceOf(testLogger.getMessageFactory(), StringFormatterMessageFactory.class);
+ assertEqualMessageFactory(StringFormatterMessageFactory.INSTANCE, testLogger);
+ testLogger.debug("%,d", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(String.format(" DEBUG %,d", Integer.MAX_VALUE), testLogger.getEntries().get(0));
+ }
+
+ private static void assertMessageFactoryInstanceOf(MessageFactory factory, final Class> cls) {
+ assertTrue(factory.getClass().isAssignableFrom(cls));
+ }
+
+ @Test
+ @ResourceLock("log4j2.TestLogger")
+ public void getFormatterLogger_Object() {
+ // The TestLogger logger was already created in an instance variable for this class.
+ // The message factory is only used when the logger is created.
+ final TestLogger testLogger = (TestLogger) LogManager.getFormatterLogger(new TestStringFormatterMessageFactory());
+ assertNotNull(testLogger);
+ assertMessageFactoryInstanceOf(testLogger.getMessageFactory(), StringFormatterMessageFactory.class);
+ assertEqualMessageFactory(StringFormatterMessageFactory.INSTANCE, testLogger);
+ testLogger.debug("%,d", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(String.format(" DEBUG %,d", Integer.MAX_VALUE), testLogger.getEntries().get(0));
+ }
+
+ @Test
+ @ResourceLock("log4j2.TestLogger")
+ public void getFormatterLogger_String() {
+ final StringFormatterMessageFactory messageFactory = StringFormatterMessageFactory.INSTANCE;
+ final TestLogger testLogger = (TestLogger) LogManager.getFormatterLogger("getLogger_String_StringFormatterMessageFactory");
+ assertNotNull(testLogger);
+ assertMessageFactoryInstanceOf(testLogger.getMessageFactory(), StringFormatterMessageFactory.class);
+ assertEqualMessageFactory(messageFactory, testLogger);
+ testLogger.debug("%,d", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(String.format(" DEBUG %,d", Integer.MAX_VALUE), testLogger.getEntries().get(0));
+ }
+
+ @Test
+ @ResourceLock("log4j2.TestLogger")
+ public void getLogger_Class_ParameterizedMessageFactory() {
+ // The TestLogger logger was already created in an instance variable for this class.
+ // The message factory is only used when the logger is created.
+ final ParameterizedMessageFactory messageFactory = ParameterizedMessageFactory.INSTANCE;
+ final TestLogger testLogger = (TestLogger) LogManager.getLogger(TestParameterizedMessageFactory.class,
+ messageFactory);
+ assertNotNull(testLogger);
+ assertEqualMessageFactory(messageFactory, testLogger);
+ testLogger.debug("{}", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(" DEBUG " + Integer.MAX_VALUE, testLogger.getEntries().get(0));
+ }
+
+ @Test
+ @ResourceLock("log4j2.TestLogger")
+ public void getLogger_Class_StringFormatterMessageFactory() {
+ // The TestLogger logger was already created in an instance variable for this class.
+ // The message factory is only used when the logger is created.
+ final TestLogger testLogger = (TestLogger) LogManager.getLogger(TestStringFormatterMessageFactory.class,
+ StringFormatterMessageFactory.INSTANCE);
+ assertNotNull(testLogger);
+ assertEqualMessageFactory(StringFormatterMessageFactory.INSTANCE, testLogger);
+ testLogger.debug("%,d", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(String.format(" DEBUG %,d", Integer.MAX_VALUE), testLogger.getEntries().get(0));
+ }
+
+ @Test
+ @ResourceLock("log4j2.TestLogger")
+ public void getLogger_Object_ParameterizedMessageFactory() {
+ // The TestLogger logger was already created in an instance variable for this class.
+ // The message factory is only used when the logger is created.
+ final ParameterizedMessageFactory messageFactory = ParameterizedMessageFactory.INSTANCE;
+ final TestLogger testLogger = (TestLogger) LogManager.getLogger(new TestParameterizedMessageFactory(),
+ messageFactory);
+ assertNotNull(testLogger);
+ assertEqualMessageFactory(messageFactory, testLogger);
+ testLogger.debug("{}", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(" DEBUG " + Integer.MAX_VALUE, testLogger.getEntries().get(0));
+ }
+
+ private void assertEqualMessageFactory(final MessageFactory messageFactory, final TestLogger testLogger) {
+ MessageFactory actual = testLogger.getMessageFactory();
+ assertEquals(messageFactory, actual);
+ }
+
+ @Test
+ @ResourceLock("log4j2.TestLogger")
+ public void getLogger_Object_StringFormatterMessageFactory() {
+ // The TestLogger logger was already created in an instance variable for this class.
+ // The message factory is only used when the logger is created.
+ final StringFormatterMessageFactory messageFactory = StringFormatterMessageFactory.INSTANCE;
+ final TestLogger testLogger = (TestLogger) LogManager.getLogger(new TestStringFormatterMessageFactory(),
+ messageFactory);
+ assertNotNull(testLogger);
+ assertEqualMessageFactory(messageFactory, testLogger);
+ testLogger.debug("%,d", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(String.format(" DEBUG %,d", Integer.MAX_VALUE), testLogger.getEntries().get(0));
+ }
+
+ @Test
+ @ResourceLock("log4j2.TestLogger")
+ public void getLogger_String_MessageFactoryMismatch() {
+ final StringFormatterMessageFactory messageFactory = StringFormatterMessageFactory.INSTANCE;
+ final TestLogger testLogger = (TestLogger) LogManager.getLogger("getLogger_String_MessageFactoryMismatch",
+ messageFactory);
+ assertNotNull(testLogger);
+ assertEqualMessageFactory(messageFactory, testLogger);
+ final TestLogger testLogger2 = (TestLogger) LogManager.getLogger("getLogger_String_MessageFactoryMismatch",
+ ParameterizedMessageFactory.INSTANCE);
+ assertNotNull(testLogger2);
+ //TODO: How to test?
+ //This test context always creates new loggers, other test context impls I tried fail other tests.
+ //assertEquals(messageFactory, testLogger2.getMessageFactory());
+ testLogger.debug("%,d", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(String.format(" DEBUG %,d", Integer.MAX_VALUE), testLogger.getEntries().get(0));
+ }
+
+ @Test
+ @ResourceLock("log4j2.TestLogger")
+ public void getLogger_String_ParameterizedMessageFactory() {
+ final ParameterizedMessageFactory messageFactory = ParameterizedMessageFactory.INSTANCE;
+ final TestLogger testLogger = (TestLogger) LogManager.getLogger("getLogger_String_ParameterizedMessageFactory",
+ messageFactory);
+ assertNotNull(testLogger);
+ assertEqualMessageFactory(messageFactory, testLogger);
+ testLogger.debug("{}", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(" DEBUG " + Integer.MAX_VALUE, testLogger.getEntries().get(0));
+ }
+
+ @Test
+ @ResourceLock("log4j2.TestLogger")
+ public void getLogger_String_SimpleMessageFactory() {
+ final SimpleMessageFactory messageFactory = SimpleMessageFactory.INSTANCE;
+ final TestLogger testLogger = (TestLogger) LogManager.getLogger("getLogger_String_StringFormatterMessageFactory",
+ messageFactory);
+ assertNotNull(testLogger);
+ assertEqualMessageFactory(messageFactory, testLogger);
+ testLogger.debug("{} %,d {foo}", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(" DEBUG {} %,d {foo}", testLogger.getEntries().get(0));
+ }
+
+ @Test
+ @ResourceLock("log4j2.TestLogger")
+ public void getLogger_String_StringFormatterMessageFactory() {
+ final StringFormatterMessageFactory messageFactory = StringFormatterMessageFactory.INSTANCE;
+ final TestLogger testLogger = (TestLogger) LogManager.getLogger("getLogger_String_StringFormatterMessageFactory",
+ messageFactory);
+ assertNotNull(testLogger);
+ assertEqualMessageFactory(messageFactory, testLogger);
+ testLogger.debug("%,d", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(String.format(" DEBUG %,d", Integer.MAX_VALUE), testLogger.getEntries().get(0));
+ }
+
+ @Test
+ public void getLoggerByClass() {
+ final Logger classLogger = LogManager.getLogger(LoggerTest.class);
+ assertNotNull(classLogger);
+ }
+
+ @Test
+ public void getLoggerByNullClass() {
+ // Returns a SimpleLogger
+ assertNotNull(LogManager.getLogger((Class>) null));
+ }
+
+ @Test
+ public void getLoggerByNullObject() {
+ // Returns a SimpleLogger
+ assertNotNull(LogManager.getLogger((Object) null));
+ }
+
+ @Test
+ public void getLoggerByNullString() {
+ // Returns a SimpleLogger
+ assertNotNull(LogManager.getLogger((String) null));
+ }
+
+ @Test
+ public void getLoggerByObject() {
+ final Logger classLogger = LogManager.getLogger(this);
+ assertNotNull(classLogger);
+ assertEquals(classLogger, LogManager.getLogger(LoggerTest.class));
+ }
+
+ @Test
+ public void getRootLogger() {
+ assertNotNull(LogManager.getRootLogger());
+ assertNotNull(LogManager.getLogger(Strings.EMPTY));
+ assertNotNull(LogManager.getLogger(LogManager.ROOT_LOGGER_NAME));
+ assertEquals(LogManager.getRootLogger(), LogManager.getLogger(Strings.EMPTY));
+ assertEquals(LogManager.getRootLogger(), LogManager.getLogger(LogManager.ROOT_LOGGER_NAME));
+ }
+
+ @Test
+ public void isAllEnabled() {
+ assertTrue(logger.isEnabled(Level.ALL), "Incorrect level");
+ }
+
+ @Test
+ public void isDebugEnabled() {
+ assertTrue(logger.isDebugEnabled(), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.DEBUG), "Incorrect level");
+ }
+
+ @Test
+ public void isErrorEnabled() {
+ assertTrue(logger.isErrorEnabled(), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.ERROR), "Incorrect level");
+ }
+
+ @Test
+ public void isFatalEnabled() {
+ assertTrue(logger.isFatalEnabled(), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.FATAL), "Incorrect level");
+ }
+
+ @Test
+ public void isInfoEnabled() {
+ assertTrue(logger.isInfoEnabled(), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.INFO), "Incorrect level");
+ }
+
+ @Test
+ public void isOffEnabled() {
+ assertTrue(logger.isEnabled(Level.OFF), "Incorrect level");
+ }
+
+ @Test
+ public void isTraceEnabled() {
+ assertTrue(logger.isTraceEnabled(), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.TRACE), "Incorrect level");
+ }
+
+ @Test
+ public void isWarnEnabled() {
+ assertTrue(logger.isWarnEnabled(), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.WARN), "Incorrect level");
+ }
+
+ @Test
+ public void isAllEnabledWithMarker() {
+ assertTrue(logger.isEnabled(Level.ALL, marker), "Incorrect level");
+ }
+
+ @Test
+ public void isDebugEnabledWithMarker() {
+ assertTrue(logger.isDebugEnabled(marker), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.DEBUG, marker), "Incorrect level");
+ }
+
+ @Test
+ public void isErrorEnabledWithMarker() {
+ assertTrue(logger.isErrorEnabled(marker), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.ERROR, marker), "Incorrect level");
+ }
+
+ @Test
+ public void isFatalEnabledWithMarker() {
+ assertTrue(logger.isFatalEnabled(marker), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.FATAL, marker), "Incorrect level");
+ }
+
+ @Test
+ public void isInfoEnabledWithMarker() {
+ assertTrue(logger.isInfoEnabled(marker), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.INFO, marker), "Incorrect level");
+ }
+
+ @Test
+ public void isOffEnabledWithMarker() {
+ assertTrue(logger.isEnabled(Level.OFF, marker), "Incorrect level");
+ }
+
+ @Test
+ public void isTraceEnabledWithMarker() {
+ assertTrue(logger.isTraceEnabled(marker), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.TRACE, marker), "Incorrect level");
+ }
+
+ @Test
+ public void isWarnEnabledWithMarker() {
+ assertTrue(logger.isWarnEnabled(marker), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.WARN, marker), "Incorrect level");
+ }
+
+ @Test
+ @UsingThreadContextMap
+ public void mdc() {
+ ThreadContext.put("TestYear", Integer.valueOf(2010).toString());
+ logger.debug("Debug message");
+ String testYear = ThreadContext.get("TestYear");
+ assertNotNull(testYear, "Test Year is null");
+ assertEquals("2010", testYear, "Incorrect test year: " + testYear);
+ ThreadContext.clearMap();
+ logger.debug("Debug message");
+ assertEquals(2, results.size());
+ System.out.println("Log line 1: " + results.get(0));
+ System.out.println("log line 2: " + results.get(1));
+ assertTrue(
+ results.get(0).startsWith(" DEBUG Debug message {TestYear=2010}"), "Incorrect MDC: " + results.get(0));
+ assertTrue(
+ results.get(1).startsWith(" DEBUG Debug message"), "MDC not cleared?: " + results.get(1));
+ }
+
+ @Test
+ public void printf() {
+ logger.printf(Level.DEBUG, "Debug message %d", 1);
+ logger.printf(Level.DEBUG, MarkerManager.getMarker("Test"), "Debug message %d", 2);
+ assertEquals(2, results.size());
+ assertThat("Incorrect message", results.get(0), startsWith(" DEBUG Debug message 1"));
+ assertThat("Incorrect message", results.get(1), startsWith("Test DEBUG Debug message 2"));
+ }
+
+ @BeforeEach
+ public void setup() {
+ results.clear();
+ }
+
+ @Test
+ public void structuredData() {
+ ThreadContext.put("loginId", "JohnDoe");
+ ThreadContext.put("ipAddress", "192.168.0.120");
+ ThreadContext.put("locale", Locale.US.getDisplayName());
+ final StructuredDataMessage msg = new StructuredDataMessage("Audit@18060", "Transfer Complete", "Transfer");
+ msg.put("ToAccount", "123456");
+ msg.put("FromAccount", "123457");
+ msg.put("Amount", "200.00");
+ logger.info(MarkerManager.getMarker("EVENT"), msg);
+ ThreadContext.clearMap();
+ assertEquals(1, results.size());
+ assertThat("Incorrect structured data: ", results.get(0), startsWith(
+ "EVENT INFO Transfer [Audit@18060 Amount=\"200.00\" FromAccount=\"123457\" ToAccount=\"123456\"] Transfer Complete"));
+ }
+
+ @Test
+ public void throwing() {
+ logger.throwing(new IllegalArgumentException("Test Exception"));
+ assertEquals(1, results.size());
+ assertThat("Incorrect Throwing",
+ results.get(0), startsWith("THROWING[ EXCEPTION ] ERROR Throwing java.lang.IllegalArgumentException: Test Exception"));
+ }
+
+
+ private static class Response {
+ int status;
+ String message;
+
+ public Response(final int status, final String message) {
+ this.status = status;
+ this.message = message;
+ }
+
+ public int getStatus() {
+ return status;
+ }
+
+ public void setStatus(final int status) {
+ this.status = status;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(final String message) {
+ this.message = message;
+ }
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/MarkerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/MarkerTest.java
new file mode 100644
index 00000000000..c07f94f2596
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/MarkerTest.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.logging.log4j.test.junit.Resources;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+
+@ResourceLock(value = Resources.MARKER_MANAGER, mode = ResourceAccessMode.READ_WRITE)
+public class MarkerTest {
+
+ @BeforeEach
+ public void setUp() {
+ MarkerManager.clear();
+ }
+
+ @Test
+ public void testGetMarker() {
+ final Marker expected = MarkerManager.getMarker("A");
+ assertNull(expected.getParents());
+ }
+
+ @Test
+ public void testGetMarkerWithParents() {
+ final Marker expected = MarkerManager.getMarker("A");
+ final Marker p1 = MarkerManager.getMarker("P1");
+ p1.addParents(MarkerManager.getMarker("PP1"));
+ final Marker p2 = MarkerManager.getMarker("P2");
+ expected.addParents(p1);
+ expected.addParents(p2);
+ assertEquals(2, expected.getParents().length);
+ }
+
+ @Test
+ public void testHasParents() {
+ final Marker parent = MarkerManager.getMarker("PARENT");
+ final Marker existing = MarkerManager.getMarker("EXISTING");
+ assertFalse(existing.hasParents());
+ existing.setParents(parent);
+ assertTrue(existing.hasParents());
+ }
+
+ @Test
+ public void testMarker() {
+ final Marker parent = MarkerManager.getMarker("PARENT");
+ final Marker test1 = MarkerManager.getMarker("TEST1").setParents(parent);
+ final Marker test2 = MarkerManager.getMarker("TEST2").addParents(parent);
+ assertTrue(test1.isInstanceOf(parent), "TEST1 is not an instance of PARENT");
+ assertTrue(test2.isInstanceOf(parent), "TEST2 is not an instance of PARENT");
+ }
+
+ @Test
+ public void testMultipleParents() {
+ final Marker parent1 = MarkerManager.getMarker("PARENT1");
+ final Marker parent2 = MarkerManager.getMarker("PARENT2");
+ final Marker test1 = MarkerManager.getMarker("TEST1").setParents(parent1, parent2);
+ final Marker test2 = MarkerManager.getMarker("TEST2").addParents(parent1, parent2);
+ assertTrue(test1.isInstanceOf(parent1), "TEST1 is not an instance of PARENT1");
+ assertTrue(test1.isInstanceOf(parent2), "TEST1 is not an instance of PARENT2");
+ assertTrue(test2.isInstanceOf(parent1), "TEST2 is not an instance of PARENT1");
+ assertTrue(test2.isInstanceOf(parent2), "TEST2 is not an instance of PARENT2");
+ }
+
+ @Test
+ public void testAddToExistingParents() {
+ final Marker parent = MarkerManager.getMarker("PARENT");
+ final Marker existing = MarkerManager.getMarker("EXISTING");
+ final Marker test1 = MarkerManager.getMarker("TEST1").setParents(existing);
+ test1.addParents(parent);
+ assertTrue(test1.isInstanceOf(parent), "TEST1 is not an instance of PARENT");
+ assertTrue(test1.isInstanceOf(existing), "TEST1 is not an instance of EXISTING");
+ }
+
+
+ @Test
+ public void testDuplicateParents() {
+ final Marker parent = MarkerManager.getMarker("PARENT");
+ final Marker existing = MarkerManager.getMarker("EXISTING");
+ final Marker test1 = MarkerManager.getMarker("TEST1").setParents(existing);
+ test1.addParents(parent);
+ final Marker[] parents = test1.getParents();
+ test1.addParents(existing);
+ assertEquals(parents.length, test1.getParents().length, "duplicate add allowed");
+ test1.addParents(existing, MarkerManager.getMarker("EXTRA"));
+ assertEquals(parents.length + 1, test1.getParents().length, "incorrect add");
+ assertTrue(test1.isInstanceOf(parent), "TEST1 is not an instance of PARENT");
+ assertTrue(test1.isInstanceOf(existing), "TEST1 is not an instance of EXISTING");
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/NoopThreadContextTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/NoopThreadContextTest.java
new file mode 100644
index 00000000000..a93a7cbc30a
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/NoopThreadContextTest.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j;
+
+import org.apache.logging.log4j.spi.LoggingSystemProperties;
+import org.apache.logging.log4j.test.junit.InitializesThreadContext;
+import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
+import org.junit.jupiter.api.Test;
+import org.junitpioneer.jupiter.SetSystemProperty;
+
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * Tests {@link ThreadContext}.
+ */
+public class NoopThreadContextTest {
+
+ @Test
+ @SetSystemProperty(key = LoggingSystemProperties.THREAD_CONTEXT_DISABLED, value = "true")
+ @SetSystemProperty(key = LoggingSystemProperties.THREAD_CONTEXT_MAP_DISABLED, value = "true")
+ @InitializesThreadContext
+ @UsingThreadContextMap
+ public void testNoop() {
+ ThreadContext.put("Test", "Test");
+ final String value = ThreadContext.get("Test");
+ assertNull(value, "value was saved");
+ }
+
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/TestProvider.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/TestProvider.java
similarity index 89%
rename from log4j-api/src/test/java/org/apache/logging/log4j/TestProvider.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/TestProvider.java
index f96181a52c8..24d17e04f7f 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/TestProvider.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/TestProvider.java
@@ -17,12 +17,13 @@
package org.apache.logging.log4j;
import org.apache.logging.log4j.spi.Provider;
+import org.apache.logging.log4j.test.TestLoggerContextFactory;
/**
* Binding for the Log4j API.
*/
public class TestProvider extends Provider {
public TestProvider() {
- super(0, "2.6.0", TestLoggerContextFactory.class);
+ super(0, "3.0.0", TestLoggerContextFactory.class);
}
}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java
new file mode 100644
index 00000000000..0d31ce33d6b
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j;
+
+import org.apache.logging.log4j.test.ThreadContextUtilityClass;
+import org.apache.logging.log4j.test.junit.InitializesThreadContext;
+import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
+import org.apache.logging.log4j.test.junit.UsingThreadContextStack;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import org.junitpioneer.jupiter.SetSystemProperty;
+
+import static org.apache.logging.log4j.spi.LoggingSystemProperties.THREAD_CONTEXT_MAP_INHERITABLE;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Tests {@link ThreadContext}.
+ */
+@SetSystemProperty(key = THREAD_CONTEXT_MAP_INHERITABLE, value = "true")
+@InitializesThreadContext
+public class ThreadContextInheritanceTest {
+
+ @Test
+ @UsingThreadContextStack
+ public void testPush() {
+ ThreadContext.push("Hello");
+ ThreadContext.push("{} is {}", ThreadContextInheritanceTest.class.getSimpleName(), "running");
+ assertEquals(
+ ThreadContext.pop(), "ThreadContextInheritanceTest is running", "Incorrect parameterized stack value");
+ assertEquals(ThreadContext.pop(), "Hello", "Incorrect simple stack value");
+ }
+
+ @Test
+ @SetSystemProperty(key = THREAD_CONTEXT_MAP_INHERITABLE, value = "true")
+ @InitializesThreadContext
+ public void testInheritanceSwitchedOn() throws Exception {
+ ThreadContext.put("Greeting", "Hello");
+ StringBuilder sb = new StringBuilder();
+ TestThread thread = new TestThread(sb);
+ thread.start();
+ thread.join();
+ String str = sb.toString();
+ assertEquals("Hello", str, "Unexpected ThreadContext value. Expected Hello. Actual " + str);
+ sb = new StringBuilder();
+ thread = new TestThread(sb);
+ thread.start();
+ thread.join();
+ str = sb.toString();
+ assertEquals("Hello", str, "Unexpected ThreadContext value. Expected Hello. Actual " + str);
+ }
+
+ @Test
+ @Tag("performance")
+ @UsingThreadContextMap
+ public void perfTest() {
+ ThreadContextUtilityClass.perfTest();
+ }
+
+ @Test
+ @UsingThreadContextMap
+ public void testGetContextReturnsEmptyMapIfEmpty() {
+ ThreadContextUtilityClass.testGetContextReturnsEmptyMapIfEmpty();
+ }
+
+ @Test
+ @UsingThreadContextMap
+ public void testGetContextReturnsMutableCopy() {
+ ThreadContextUtilityClass.testGetContextReturnsMutableCopy();
+ }
+
+ @Test
+ @UsingThreadContextMap
+ public void testGetImmutableContextReturnsEmptyMapIfEmpty() {
+ ThreadContextUtilityClass.testGetImmutableContextReturnsEmptyMapIfEmpty();
+ }
+
+ @Test
+ @UsingThreadContextMap
+ public void testGetImmutableContextReturnsImmutableMapIfNonEmpty() {
+ ThreadContextUtilityClass.testGetImmutableContextReturnsImmutableMapIfNonEmpty();
+ }
+
+ @Test
+ @UsingThreadContextMap
+ public void testGetImmutableContextReturnsImmutableMapIfEmpty() {
+ ThreadContextUtilityClass.testGetImmutableContextReturnsImmutableMapIfEmpty();
+ }
+
+ @Test
+ @UsingThreadContextStack
+ public void testGetImmutableStackReturnsEmptyStackIfEmpty() {
+ ThreadContextUtilityClass.testGetImmutableStackReturnsEmptyStackIfEmpty();
+ }
+
+ @Test
+ @UsingThreadContextMap
+ public void testPut() {
+ ThreadContextUtilityClass.testPut();
+ }
+
+ @Test
+ @UsingThreadContextMap
+ public void testRemove() {
+ assertNull(ThreadContext.get("testKey"));
+ ThreadContext.put("testKey", "testValue");
+ assertEquals("testValue", ThreadContext.get("testKey"));
+
+ ThreadContext.remove("testKey");
+ assertNull(ThreadContext.get("testKey"));
+ assertTrue(ThreadContext.isEmpty());
+ }
+
+ @Test
+ @UsingThreadContextMap
+ public void testContainsKey() {
+ assertFalse(ThreadContext.containsKey("testKey"));
+ ThreadContext.put("testKey", "testValue");
+ assertTrue(ThreadContext.containsKey("testKey"));
+
+ ThreadContext.remove("testKey");
+ assertFalse(ThreadContext.containsKey("testKey"));
+ }
+
+ private static class TestThread extends Thread {
+
+ private final StringBuilder sb;
+
+ public TestThread(final StringBuilder sb) {
+ this.sb = sb;
+ }
+
+ @Override
+ public void run() {
+ final String greeting = ThreadContext.get("Greeting");
+ if (greeting == null) {
+ sb.append("null");
+ } else {
+ sb.append(greeting);
+ }
+ ThreadContext.clearMap();
+ }
+ }
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/TraceLoggingTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/TraceLoggingTest.java
similarity index 92%
rename from log4j-api/src/test/java/org/apache/logging/log4j/TraceLoggingTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/TraceLoggingTest.java
index 81a2ca1ea8c..82cc261f818 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/TraceLoggingTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/TraceLoggingTest.java
@@ -26,13 +26,10 @@
import org.apache.logging.log4j.message.ReusableParameterizedMessageTest;
import org.apache.logging.log4j.message.SimpleMessage;
import org.apache.logging.log4j.spi.AbstractLogger;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
-/**
- *
- */
public class TraceLoggingTest extends AbstractLogger {
static final StringBuilder CHAR_SEQ = new StringBuilder("CharSeq");
private int charSeqCount;
@@ -221,7 +218,7 @@ public boolean isEnabled(final Level level, final Marker marker, final String da
@Override
public void logMessage(final String fqcn, final Level level, final Marker marker, final Message data, final Throwable t) {
- assertTrue("Incorrect Level. Expected " + currentLevel + ", actual " + level, level.equals(currentLevel));
+ assertEquals(level, currentLevel, "Incorrect Level. Expected " + currentLevel + ", actual " + level);
if (marker == null) {
if (currentEvent.markerName != null) {
fail("Incorrect marker. Expected " + currentEvent.markerName + ", actual is null");
@@ -230,8 +227,9 @@ public void logMessage(final String fqcn, final Level level, final Marker marker
if (currentEvent.markerName == null) {
fail("Incorrect marker. Expected null. Actual is " + marker.getName());
} else {
- assertTrue("Incorrect marker. Expected " + currentEvent.markerName + ", actual " +
- marker.getName(), currentEvent.markerName.equals(marker.getName()));
+ assertEquals(currentEvent.markerName, marker.getName(),
+ "Incorrect marker. Expected " + currentEvent.markerName + ", actual " +
+ marker.getName());
}
}
if (data == null) {
@@ -242,11 +240,12 @@ public void logMessage(final String fqcn, final Level level, final Marker marker
if (currentEvent.data == null) {
fail("Incorrect message. Expected null. Actual is " + data.getFormattedMessage());
} else {
- assertTrue("Incorrect message type. Expected " + currentEvent.data + ", actual " + data,
- data.getClass().isAssignableFrom(currentEvent.data.getClass()));
- assertTrue("Incorrect message. Expected " + currentEvent.data.getFormattedMessage() + ", actual " +
- data.getFormattedMessage(),
- currentEvent.data.getFormattedMessage().equals(data.getFormattedMessage()));
+ assertTrue(
+ data.getClass().isAssignableFrom(currentEvent.data.getClass()),
+ "Incorrect message type. Expected " + currentEvent.data + ", actual " + data);
+ assertEquals(currentEvent.data.getFormattedMessage(), data.getFormattedMessage(),
+ "Incorrect message. Expected " + currentEvent.data.getFormattedMessage() + ", actual " +
+ data.getFormattedMessage());
}
}
if (t == null) {
@@ -257,8 +256,7 @@ public void logMessage(final String fqcn, final Level level, final Marker marker
if (currentEvent.t == null) {
fail("Incorrect Throwable. Expected null. Actual is " + t);
} else {
- assertTrue("Incorrect Throwable. Expected " + currentEvent.t + ", actual " + t,
- currentEvent.t.equals(t));
+ assertEquals(currentEvent.t, t, "Incorrect Throwable. Expected " + currentEvent.t + ", actual " + t);
}
}
}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/message/FormattedMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/FormattedMessageTest.java
similarity index 87%
rename from log4j-api/src/test/java/org/apache/logging/log4j/message/FormattedMessageTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/message/FormattedMessageTest.java
index 937d6593e51..70f3c73caec 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/message/FormattedMessageTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/FormattedMessageTest.java
@@ -23,16 +23,17 @@
import java.io.ObjectOutputStream;
import java.util.Locale;
-import org.apache.logging.log4j.junit.Mutable;
+import org.apache.logging.log4j.test.junit.Mutable;
import org.apache.logging.log4j.util.Constants;
-import org.junit.Assert;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.api.parallel.Resources;
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
-/**
- *
- */
+@ResourceLock(value = Resources.LOCALE, mode = ResourceAccessMode.READ)
public class FormattedMessageTest {
private static final String SPACE = Constants.JAVA_MAJOR_VERSION < 9 ? " " : "\u00a0";
@@ -141,7 +142,7 @@ public void testUnsafeWithMutableParams() { // LOG4J2-763
// modify parameter before calling msg.getFormattedMessage
param.set("XYZ");
final String actual = msg.getFormattedMessage();
- assertEquals("Expected most recent param value", "Test message XYZ", actual);
+ assertEquals("Test message XYZ", actual, "Expected most recent param value");
}
@Test
@@ -154,9 +155,10 @@ public void testSafeAfterGetFormattedMessageIsCalled() { // LOG4J2-763
msg.getFormattedMessage(); // freeze the formatted message
param.set("XYZ");
final String actual = msg.getFormattedMessage();
- assertEquals("Should use initial param value", "Test message abc", actual);
+ assertEquals("Test message abc", actual, "Should use initial param value");
}
+ @SuppressWarnings("BanSerializableRead")
@Test
public void testSerialization() throws IOException, ClassNotFoundException {
final FormattedMessage expected = new FormattedMessage("Msg", "a", "b", "c");
@@ -167,9 +169,9 @@ public void testSerialization() throws IOException, ClassNotFoundException {
final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
final ObjectInputStream in = new ObjectInputStream(bais);
final FormattedMessage actual = (FormattedMessage) in.readObject();
- Assert.assertEquals(expected, actual);
- Assert.assertEquals(expected.getFormat(), actual.getFormat());
- Assert.assertEquals(expected.getFormattedMessage(), actual.getFormattedMessage());
- Assert.assertArrayEquals(expected.getParameters(), actual.getParameters());
+ assertEquals(expected, actual);
+ assertEquals(expected.getFormat(), actual.getFormat());
+ assertEquals(expected.getFormattedMessage(), actual.getFormattedMessage());
+ assertArrayEquals(expected.getParameters(), actual.getParameters());
}
}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/message/JsonMessage.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/JsonMessage.java
similarity index 100%
rename from log4j-api/src/test/java/org/apache/logging/log4j/message/JsonMessage.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/message/JsonMessage.java
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/LocalizedMessageFactoryTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/LocalizedMessageFactoryTest.java
new file mode 100644
index 00000000000..6881cd9232e
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/LocalizedMessageFactoryTest.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.message;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.api.parallel.Resources;
+
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Tests {@link LocalizedMessageFactory}.
+ */
+@ResourceLock(value = Resources.LOCALE, mode = ResourceAccessMode.READ)
+public class LocalizedMessageFactoryTest {
+
+ @Test
+ public void testMessageMarkersDataNo() {
+ final LocalizedMessageFactory localizedMessageFactory = new LocalizedMessageFactory(ResourceBundle.getBundle("MF", Locale.US));
+ final Message message = localizedMessageFactory.newMessage("msg1");
+ assertEquals("This is test number {0} with string argument {1}.", message.getFormattedMessage());
+ }
+
+ @Test
+ public void testMessageMarkersNoDataYes() {
+ final LocalizedMessageFactory localizedMessageFactory = new LocalizedMessageFactory(ResourceBundle.getBundle("MF", Locale.US));
+ final Message message = localizedMessageFactory.newMessage("msg1", 1, "two");
+ assertEquals("This is test number 1 with string argument two.", message.getFormattedMessage());
+ }
+
+ @Test
+ public void testNewMessage() {
+ final LocalizedMessageFactory localizedMessageFactory = new LocalizedMessageFactory(ResourceBundle.getBundle("MF", Locale.US));
+ final Message message = localizedMessageFactory.newMessage("hello_world");
+ assertEquals("Hello world.", message.getFormattedMessage());
+ }
+
+ @Test
+ @ResourceLock(Resources.LOCALE)
+ public void testNewMessageUsingBaseName() {
+ final Locale defaultLocale = Locale.getDefault();
+ Locale.setDefault(Locale.US);
+ try {
+ final LocalizedMessageFactory localizedMessageFactory = new LocalizedMessageFactory("MF");
+ final String testMsg = "hello_world";
+ final Message message = localizedMessageFactory.newMessage(testMsg);
+ assertEquals("Hello world.", message.getFormattedMessage());
+ } finally {
+ Locale.setDefault(defaultLocale);
+ }
+ }
+
+ @Test
+ public void testNoMatch() {
+ final LocalizedMessageFactory localizedMessageFactory = new LocalizedMessageFactory(ResourceBundle.getBundle("MF", Locale.US));
+ final Message message = localizedMessageFactory.newMessage("no match");
+ assertEquals("no match", message.getFormattedMessage());
+ }
+
+ @Test
+ public void testNoMatchPercentInMessageArgsYes() {
+ final LocalizedMessageFactory localizedMessageFactory = new LocalizedMessageFactory(ResourceBundle.getBundle("MF", Locale.US));
+ final Message message = localizedMessageFactory.newMessage("C:/Program%20Files/Some%20Company/Some%20Product%20Name/{0}", "One");
+ assertEquals("C:/Program%20Files/Some%20Company/Some%20Product%20Name/One", message.getFormattedMessage());
+ }
+
+ @Test
+ public void testNoMatchPercentInMessageNoArgsNo() {
+ // LOG4J2-3458 LocalizedMessage causes a lot of noise on the console
+ //
+ // ERROR StatusLogger Unable to format msg: C:/Program%20Files/Some%20Company/Some%20Product%20Name/
+ // java.util.UnknownFormatConversionException: Conversion = 'F'
+ // at java.util.Formatter$FormatSpecifier.conversion(Formatter.java:2691)
+ // at java.util.Formatter$FormatSpecifier.(Formatter.java:2720)
+ // at java.util.Formatter.parse(Formatter.java:2560)
+ // at java.util.Formatter.format(Formatter.java:2501)
+ // at java.util.Formatter.format(Formatter.java:2455)
+ // at java.lang.String.format(String.java:2981)
+ // at org.apache.logging.log4j.message.StringFormattedMessage.formatMessage(StringFormattedMessage.java:116)
+ // at org.apache.logging.log4j.message.StringFormattedMessage.getFormattedMessage(StringFormattedMessage.java:88)
+ // at org.apache.logging.log4j.message.FormattedMessage.getFormattedMessage(FormattedMessage.java:178)
+ // at org.apache.logging.log4j.message.LocalizedMessage.getFormattedMessage(LocalizedMessage.java:196)
+ // at
+ // org.apache.logging.log4j.message.LocalizedMessageFactoryTest.testNoMatchPercentInMessage(LocalizedMessageFactoryTest.java:60)
+ //
+ final LocalizedMessageFactory localizedMessageFactory = new LocalizedMessageFactory(ResourceBundle.getBundle("MF", Locale.US));
+ final Message message = localizedMessageFactory.newMessage("C:/Program%20Files/Some%20Company/Some%20Product%20Name/");
+ assertEquals("C:/Program%20Files/Some%20Company/Some%20Product%20Name/", message.getFormattedMessage());
+ }
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/message/LocalizedMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/LocalizedMessageTest.java
similarity index 76%
rename from log4j-api/src/test/java/org/apache/logging/log4j/message/LocalizedMessageTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/message/LocalizedMessageTest.java
index aebe6ed1006..b8a1801d588 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/message/LocalizedMessageTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/LocalizedMessageTest.java
@@ -16,18 +16,22 @@
*/
package org.apache.logging.log4j.message;
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.logging.log4j.test.junit.Mutable;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.api.parallel.Resources;
+
import java.io.Serializable;
import java.util.Locale;
-import org.apache.commons.lang3.SerializationUtils;
-import org.apache.logging.log4j.junit.Mutable;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* Tests LocalizedMessage.
*/
+@ResourceLock(value = Resources.LOCALE, mode = ResourceAccessMode.READ)
public class LocalizedMessageTest {
private T roundtrip(final T msg) {
@@ -71,7 +75,7 @@ public void testUnsafeWithMutableParams() { // LOG4J2-763
// modify parameter before calling msg.getFormattedMessage
param.set("XYZ");
final String actual = msg.getFormattedMessage();
- assertEquals("Expected most recent param value", "Test message XYZ", actual);
+ assertEquals("Test message XYZ", actual, "Expected most recent param value");
}
@Test
@@ -84,6 +88,20 @@ public void testSafeAfterGetFormattedMessageIsCalled() { // LOG4J2-763
msg.getFormattedMessage();
param.set("XYZ");
final String actual = msg.getFormattedMessage();
- assertEquals("Should use initial param value", "Test message abc", actual);
+ assertEquals("Test message abc", actual, "Should use initial param value");
+ }
+
+ @Test
+ @ResourceLock(Resources.LOCALE)
+ public void testMessageUsingBaseName() { // LOG4J2-2850
+ final Locale defaultLocale = Locale.getDefault();
+ Locale.setDefault(Locale.US);
+ try {
+ final String testMsg = "hello_world";
+ final LocalizedMessage msg = new LocalizedMessage("MF", testMsg, null);
+ assertEquals("Hello world.", msg.getFormattedMessage());
+ } finally {
+ Locale.setDefault(defaultLocale);
+ }
}
}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MapMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MapMessageTest.java
new file mode 100644
index 00000000000..106cce2c975
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MapMessageTest.java
@@ -0,0 +1,335 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.message;
+
+import java.math.BigDecimal;
+import java.sql.Time;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.logging.log4j.util.StringBuilderFormattable;
+import org.apache.logging.log4j.util.Strings;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ *
+ */
+public class MapMessageTest {
+
+ @Test
+ public void testMap() {
+ final String testMsg = "Test message {}";
+ final StringMapMessage msg = new StringMapMessage();
+ msg.put("message", testMsg);
+ msg.put("project", "Log4j");
+ final String result = msg.getFormattedMessage();
+ final String expected = "message=\"Test message {}\" project=\"Log4j\"";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testBuilder() {
+ final String testMsg = "Test message {}";
+ final StringMapMessage msg = new StringMapMessage()
+ .with("message", testMsg)
+ .with("project", "Log4j");
+ final String result = msg.getFormattedMessage();
+ final String expected = "message=\"Test message {}\" project=\"Log4j\"";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testXML() {
+ final String testMsg = "Test message {}";
+ final StringMapMessage msg = new StringMapMessage();
+ msg.put("message", testMsg);
+ msg.put("project", "Log4j");
+ final String result = msg.getFormattedMessage(new String[]{"XML"});
+ final String expected = "\n Test message {}\n" +
+ " Log4j\n" +
+ "";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testXMLEscape() {
+ final String testMsg = "Test message ";
+ final StringMapMessage msg = new StringMapMessage();
+ msg.put("message", testMsg);
+ final String result = msg.getFormattedMessage(new String[]{"XML"});
+ final String expected = "\n Test message <foo>\n" +
+ "";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testJSON() {
+ final String testMsg = "Test message {}";
+ final StringMapMessage msg = new StringMapMessage();
+ msg.put("message", testMsg);
+ msg.put("project", "Log4j");
+ final String result = msg.getFormattedMessage(new String[]{"JSON"});
+ final String expected = "{'message':'Test message {}','project':'Log4j'}".replace('\'', '"');
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testJSONEscape() {
+ final String testMsg = "Test message \"Hello, World!\"";
+ final StringMapMessage msg = new StringMapMessage();
+ msg.put("message", testMsg);
+ final String result = msg.getFormattedMessage(new String[]{"JSON"});
+ final String expected = "{\"message\":\"Test message \\\"Hello, World!\\\"\"}";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testJSONEscapeNewlineAndOtherControlCharacters() {
+ final String testMsg = "hello\tworld\r\nhh\bere is it\f";
+ final StringMapMessage msg = new StringMapMessage();
+ msg.put("one\ntwo", testMsg);
+ final String result = msg.getFormattedMessage(new String[]{"JSON"});
+ final String expected =
+ "{\"one\\ntwo\":\"hello\\tworld\\r\\nhh\\bere is it\\f\"}";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testJsonFormatterNestedObjectSupport() {
+ final String actualJson = new ObjectMapMessage()
+ .with("key1", "val1")
+ .with("key2", Collections.singletonMap("key2.1", "val2.1"))
+ .with("key3", Arrays.asList(
+ 3,
+ (byte) 127,
+ 4.5D,
+ 4.6F,
+ Arrays.asList(true, false),
+ new BigDecimal(30),
+ Collections.singletonMap("key3.3", "val3.3")))
+ .with("key4", new LinkedHashMap() {{
+ put("chars", new char[]{'a', 'b', 'c'});
+ put("booleans", new boolean[]{true, false});
+ put("bytes", new byte[]{1, 2});
+ put("shorts", new short[]{3, 4});
+ put("ints", new int[]{256, 257});
+ put("longs", new long[]{2147483648L, 2147483649L});
+ put("floats", new float[]{1.0F, 1.1F});
+ put("doubles", new double[]{2.0D, 2.1D});
+ put("objects", new Object[]{"foo", "bar"});
+ }})
+ .getFormattedMessage(new String[]{"JSON"});
+ final String expectedJson = ("{" +
+ "'key1':'val1'," +
+ "'key2':{'key2.1':'val2.1'}," +
+ "'key3':[3,127,4.5,4.6,[true,false],30,{'key3.3':'val3.3'}]," +
+ "'key4':{" +
+ "'chars':['a','b','c']," +
+ "'booleans':[true,false]," +
+ "'bytes':[1,2]," +
+ "'shorts':[3,4]," +
+ "'ints':[256,257]," +
+ "'longs':[2147483648,2147483649]," +
+ "'floats':[1.0,1.1]," +
+ "'doubles':[2.0,2.1]," +
+ "'objects':['foo','bar']" +
+ "}}").replace('\'', '"');
+ assertEquals(expectedJson, actualJson);
+ }
+
+ @Test
+ public void testJsonFormatterInfiniteRecursionPrevention() {
+ final List recursiveValue = Arrays.asList(1, null);
+ // noinspection CollectionAddedToSelf
+ recursiveValue.set(1, recursiveValue);
+ assertThrows(IllegalArgumentException.class, () -> new ObjectMapMessage()
+ .with("key", recursiveValue)
+ .getFormattedMessage(new String[]{"JSON"}));
+ }
+
+ @Test
+ public void testJsonFormatterMaxDepthViolation() {
+ assertThrows(IllegalArgumentException.class, () -> testJsonFormatterMaxDepth(MapMessageJsonFormatter.MAX_DEPTH - 1));
+ }
+
+ @Test
+ public void testJsonFormatterMaxDepthConformance() {
+ int depth = MapMessageJsonFormatter.MAX_DEPTH - 2;
+ String expectedJson = String
+ .format("{'key':%s1%s}",
+ Strings.repeat("[", depth),
+ Strings.repeat("]", depth))
+ .replace('\'', '"');
+ String actualJson = testJsonFormatterMaxDepth(depth);
+ assertEquals(expectedJson, actualJson);
+ }
+
+ public static String testJsonFormatterMaxDepth(int depth) {
+ List list = new LinkedList<>();
+ list.add(1);
+ while (--depth > 0) {
+ list = new LinkedList<>(Collections.singletonList(list));
+ }
+ return new ObjectMapMessage()
+ .with("key", list)
+ .getFormattedMessage(new String[]{"JSON"});
+ }
+
+ @Test
+ public void testJava() {
+ final String testMsg = "Test message {}";
+ final StringMapMessage msg = new StringMapMessage();
+ msg.put("message", testMsg);
+ msg.put("project", "Log4j");
+ final String result = msg.getFormattedMessage(new String[]{"Java"});
+ final String expected = "{message=\"Test message {}\", project=\"Log4j\"}";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testMutableByDesign() { // LOG4J2-763
+ final StringMapMessage msg = new StringMapMessage();
+
+ // modify parameter before calling msg.getFormattedMessage
+ msg.put("key1", "value1");
+ msg.put("key2", "value2");
+ final String result = msg.getFormattedMessage(new String[]{"Java"});
+ final String expected = "{key1=\"value1\", key2=\"value2\"}";
+ assertEquals(expected, result);
+
+ // modify parameter after calling msg.getFormattedMessage
+ msg.put("key3", "value3");
+ final String result2 = msg.getFormattedMessage(new String[]{"Java"});
+ final String expected2 = "{key1=\"value1\", key2=\"value2\", key3=\"value3\"}";
+ assertEquals(expected2, result2);
+ }
+
+ @Test
+ public void testGetNonStringValue() {
+ final String key = "Key";
+ final ObjectMapMessage msg = new ObjectMapMessage()
+ .with(key, 1L);
+ assertEquals("1", msg.get(key));
+ }
+
+ @Test
+ public void testRemoveNonStringValue() {
+ final String key = "Key";
+ final ObjectMapMessage msg = new ObjectMapMessage()
+ .with(key, 1L);
+ assertEquals("1", msg.remove(key));
+ }
+
+ @Test
+ public void testJSONFormatNonStringValue() {
+ final ObjectMapMessage msg = new ObjectMapMessage().with("key", 1L);
+ final String result = msg.getFormattedMessage(new String[]{"JSON"});
+ final String expected = "{'key':1}".replace('\'', '"');
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testXMLFormatNonStringValue() {
+ final ObjectMapMessage msg = new ObjectMapMessage()
+ .with("key", 1L);
+ final String result = msg.getFormattedMessage(new String[]{"XML"});
+ final String expected = "\n 1\n";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testFormatToUsedInOutputXml() {
+ final ObjectMapMessage msg = new ObjectMapMessage()
+ .with("key", new FormattableTestType());
+ final String result = msg.getFormattedMessage(new String[]{"XML"});
+ final String expected = "\n formatTo\n";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testFormatToUsedInOutputJson() {
+ final ObjectMapMessage msg = new ObjectMapMessage()
+ .with("key", new FormattableTestType());
+ final String result = msg.getFormattedMessage(new String[]{"JSON"});
+ final String expected = "{\"key\":\"formatTo\"}";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testFormatToUsedInOutputJava() {
+ final ObjectMapMessage msg = new ObjectMapMessage()
+ .with("key", new FormattableTestType());
+ final String result = msg.getFormattedMessage(new String[]{"JAVA"});
+ final String expected = "{key=\"formatTo\"}";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testFormatToUsedInOutputDefault() {
+ final ObjectMapMessage msg = new ObjectMapMessage()
+ .with("key", new FormattableTestType());
+ final String result = msg.getFormattedMessage(null);
+ final String expected = "key=\"formatTo\"";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testGetUsesDeepToString() {
+ final String key = "key";
+ final ObjectMapMessage msg = new ObjectMapMessage()
+ .with(key, new FormattableTestType());
+ final String result = msg.get(key);
+ final String expected = "formatTo";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testRemoveUsesDeepToString() {
+ final String key = "key";
+ final ObjectMapMessage msg = new ObjectMapMessage()
+ .with(key, new FormattableTestType());
+ final String result = msg.remove(key);
+ final String expected = "formatTo";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testTime() throws Exception {
+ final Time time = new Time(12, 5, 5);
+ final ObjectMapMessage message = new ObjectMapMessage().with("time", time);
+ assertEquals("time=\"" + time.toString() + "\"",
+ message.getFormattedMessage(), "Incorrect time format");
+ }
+
+ private static final class FormattableTestType implements StringBuilderFormattable {
+
+ @Override
+ public String toString() {
+ return "toString";
+ }
+
+ @Override
+ public void formatTo(StringBuilder buffer) {
+ buffer.append("formatTo");
+ }
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MessageFormatMessageSerializationTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MessageFormatMessageSerializationTest.java
new file mode 100644
index 00000000000..29062932284
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MessageFormatMessageSerializationTest.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.message;
+
+import java.util.stream.Stream;
+
+import org.apache.logging.log4j.test.AbstractSerializationTest;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.api.parallel.Resources;
+
+@ResourceLock(value = Resources.LOCALE, mode = ResourceAccessMode.READ)
+public class MessageFormatMessageSerializationTest extends AbstractSerializationTest {
+
+ protected Stream data() {
+ return Stream.of(
+ new MessageFormatMessage("Test"),
+ new MessageFormatMessage("Test {0} {1}", "message", "test"),
+ new MessageFormatMessage("{0}{1}{2}", 3, '.', 14L));
+ }
+
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/message/MessageFormatMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MessageFormatMessageTest.java
similarity index 82%
rename from log4j-api/src/test/java/org/apache/logging/log4j/message/MessageFormatMessageTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/message/MessageFormatMessageTest.java
index d123b74e1ad..3d4a9b567ae 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/message/MessageFormatMessageTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MessageFormatMessageTest.java
@@ -14,23 +14,25 @@
* See the license for the specific language governing permissions and
* limitations under the license.
*/
-
package org.apache.logging.log4j.message;
-import org.apache.logging.log4j.junit.Mutable;
+import java.util.Locale;
+
+import org.apache.logging.log4j.test.junit.Mutable;
import org.apache.logging.log4j.util.Constants;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.api.parallel.Resources;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
-import java.util.Locale;
-
-/**
- *
- */
+@ResourceLock(value = Resources.LOCALE, mode = ResourceAccessMode.READ)
public class MessageFormatMessageTest {
private static final String SPACE = Constants.JAVA_MAJOR_VERSION < 9 ? " " : "\u00a0";
+ private static final String N_SPACE = Constants.JAVA_MAJOR_VERSION < 15 ? "\u00a0" : "\u202f";
private static final int LOOP_CNT = 500;
String[] array = new String[LOOP_CNT];
@@ -72,7 +74,7 @@ public void testOneIntArgLocaleFrance() {
final String testMsg = "Test message {0,number,currency}";
final MessageFormatMessage msg = new MessageFormatMessage(Locale.FRANCE, testMsg, 1234567890);
final String result = msg.getFormattedMessage();
- final String expected = "Test message 1 234 567 890,00" + SPACE + "€";
+ final String expected = "Test message 1" + N_SPACE + "234" + N_SPACE + "567" + N_SPACE + "890,00" + SPACE + "€";
assertEquals(expected, result);
}
@@ -84,7 +86,7 @@ public void testException() {
final String expected = "Test message Apache";
assertEquals(expected, result);
final Throwable t = msg.getThrowable();
- assertNotNull("No Throwable", t);
+ assertNotNull(t, "No Throwable");
}
@Test
@@ -96,7 +98,7 @@ public void testUnsafeWithMutableParams() { // LOG4J2-763
// modify parameter before calling msg.getFormattedMessage
param.set("XYZ");
final String actual = msg.getFormattedMessage();
- assertEquals("Expected most recent param value", "Test message XYZ", actual);
+ assertEquals("Test message XYZ", actual, "Expected most recent param value");
}
@Test
@@ -109,6 +111,6 @@ public void testSafeAfterGetFormattedMessageIsCalled() { // LOG4J2-763
msg.getFormattedMessage();
param.set("XYZ");
final String actual = msg.getFormattedMessage();
- assertEquals("Should use initial param value", "Test message abc", actual);
+ assertEquals("Test message abc", actual, "Should use initial param value");
}
}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/message/MessageFormatsPerfTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MessageFormatsPerfTest.java
similarity index 92%
rename from log4j-api/src/test/java/org/apache/logging/log4j/message/MessageFormatsPerfTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/message/MessageFormatsPerfTest.java
index a151eac08d1..b5ff0456bb4 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/message/MessageFormatsPerfTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MessageFormatsPerfTest.java
@@ -16,13 +16,19 @@
*/
package org.apache.logging.log4j.message;
-import org.apache.logging.log4j.Timer;
-import org.junit.AfterClass;
-import org.junit.Test;
+import org.apache.logging.log4j.util.Timer;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.api.parallel.Resources;
/**
*
*/
+@Tag("performance")
+@ResourceLock(value = Resources.LOCALE, mode = ResourceAccessMode.READ)
public class MessageFormatsPerfTest {
private static final int LOOP_CNT = 500;
@@ -32,7 +38,7 @@ public class MessageFormatsPerfTest {
private static long msgFormatTime = 0;
private static long formattedTime = 0;
- @AfterClass
+ @AfterAll
public static void after() {
if (stringTime > paramTime) {
System.out.println(String.format("Parameterized is %1$.2f times faster than StringFormat.",
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/message/ObjectArrayMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectArrayMessageTest.java
similarity index 80%
rename from log4j-api/src/test/java/org/apache/logging/log4j/message/ObjectArrayMessageTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectArrayMessageTest.java
index 5f11cf8204f..6196200df2e 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/message/ObjectArrayMessageTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectArrayMessageTest.java
@@ -1,40 +1,42 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.message;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-/**
- * @since 2.4
- */
-public class ObjectArrayMessageTest {
-
- private static final Object[] ARRAY = { "A", "B", "C" };
- private static final ObjectArrayMessage OBJECT_ARRAY_MESSAGE = new ObjectArrayMessage(ARRAY);
-
- @Test
- public void testGetParameters() {
- Assert.assertArrayEquals(ARRAY, OBJECT_ARRAY_MESSAGE.getParameters());
- }
-
- @Test
- public void testGetThrowable() {
- Assert.assertEquals(null, OBJECT_ARRAY_MESSAGE.getThrowable());
- }
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.message;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * @since 2.4
+ */
+public class ObjectArrayMessageTest {
+
+ private static final Object[] ARRAY = { "A", "B", "C" };
+ private static final ObjectArrayMessage OBJECT_ARRAY_MESSAGE = new ObjectArrayMessage(ARRAY);
+
+ @Test
+ public void testGetParameters() {
+ assertArrayEquals(ARRAY, OBJECT_ARRAY_MESSAGE.getParameters());
+ }
+
+ @Test
+ public void testGetThrowable() {
+ assertNull(OBJECT_ARRAY_MESSAGE.getThrowable());
+ }
+
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/message/ObjectMapMessage.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectMapMessage.java
similarity index 100%
rename from log4j-api/src/test/java/org/apache/logging/log4j/message/ObjectMapMessage.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectMapMessage.java
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/message/ObjectMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectMessageTest.java
similarity index 88%
rename from log4j-api/src/test/java/org/apache/logging/log4j/message/ObjectMessageTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectMessageTest.java
index 8f547ae5081..4f3218eeafe 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/message/ObjectMessageTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectMessageTest.java
@@ -19,11 +19,11 @@
import java.io.Serializable;
import java.math.BigDecimal;
-import org.apache.logging.log4j.junit.Mutable;
-import org.apache.logging.log4j.junit.SerialUtil;
-import org.junit.Test;
+import org.apache.logging.log4j.test.junit.Mutable;
+import org.apache.logging.log4j.test.junit.SerialUtil;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
/**
* Tests {@link ObjectMessage}.
@@ -53,7 +53,7 @@ public void testUnsafeWithMutableParams() { // LOG4J2-763
// modify parameter before calling msg.getFormattedMessage
param.set("XYZ");
final String actual = msg.getFormattedMessage();
- assertEquals("Expected most recent param value", "XYZ", actual);
+ assertEquals("XYZ", actual, "Expected most recent param value");
}
@Test
@@ -65,7 +65,7 @@ public void testSafeAfterGetFormattedMessageIsCalled() { // LOG4J2-763
msg.getFormattedMessage();
param.set("XYZ");
final String actual = msg.getFormattedMessage();
- assertEquals("Should use initial param value", "abc", actual);
+ assertEquals("abc", actual, "Should use initial param value");
}
@Test
@@ -83,6 +83,11 @@ class NonSerializable {
public boolean equals(final Object other) {
return other instanceof NonSerializable; // a very lenient equals()
}
+
+ @Override
+ public int hashCode() {
+ return NonSerializable.class.hashCode();
+ }
}
final NonSerializable nonSerializable = new NonSerializable();
assertFalse(nonSerializable instanceof Serializable);
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/message/ParameterFormatterTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterFormatterTest.java
similarity index 89%
rename from log4j-api/src/test/java/org/apache/logging/log4j/message/ParameterFormatterTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterFormatterTest.java
index 6e728f6928e..ba793e363d1 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/message/ParameterFormatterTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterFormatterTest.java
@@ -16,19 +16,21 @@
*/
package org.apache.logging.log4j.message;
+import org.junit.jupiter.api.Test;
+
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
-import org.junit.Test;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
/**
- * Tests ParameterFormatter.
+ * Tests {@link ParameterFormatter}.
*/
public class ParameterFormatterTest {
@Test
- public void testCountArgumentPlaceholders() throws Exception {
+ public void testCountArgumentPlaceholders() {
assertEquals(0, ParameterFormatter.countArgumentPlaceholders(""));
assertEquals(0, ParameterFormatter.countArgumentPlaceholders("aaa"));
assertEquals(0, ParameterFormatter.countArgumentPlaceholders("\\{}"));
@@ -168,9 +170,10 @@ public void testFormatMessageStringArgsWithEscapedEscape() {
}
@Test
- public void testDeepToString() throws Exception {
+ public void testDeepToString() {
final List list = new ArrayList<>();
list.add(1);
+ // noinspection CollectionAddedToSelf
list.add(list);
list.add(2);
final String actual = ParameterFormatter.deepToString(list);
@@ -179,13 +182,29 @@ public void testDeepToString() throws Exception {
}
@Test
- public void testIdentityToString() throws Exception {
+ public void testDeepToStringUsingNonRecursiveButConsequentObjects() {
+ final List list = new ArrayList<>();
+ final Object item = Collections.singletonList(0);
+ list.add(1);
+ list.add(item);
+ list.add(2);
+ list.add(item);
+ list.add(3);
+ final String actual = ParameterFormatter.deepToString(list);
+ final String expected = "[1, [0], 2, [0], 3]";
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testIdentityToString() {
final List list = new ArrayList<>();
list.add(1);
+ // noinspection CollectionAddedToSelf
list.add(list);
list.add(2);
final String actual = ParameterFormatter.identityToString(list);
final String expected = list.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(list));
assertEquals(expected, actual);
}
-}
\ No newline at end of file
+
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java
similarity index 94%
rename from log4j-api/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java
index 2a5bac6ceb3..1c15c5e73a6 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java
@@ -16,10 +16,10 @@
*/
package org.apache.logging.log4j.message;
-import org.apache.logging.log4j.junit.Mutable;
-import org.junit.Test;
+import org.apache.logging.log4j.test.junit.Mutable;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
/**
*
@@ -135,11 +135,11 @@ public void testSafeWithMutableParams() { // LOG4J2-763
// modify parameter before calling msg.getFormattedMessage
param.set("XYZ");
final String actual = msg.getFormattedMessage();
- assertEquals("Should use current param value", "Test message XYZ", actual);
+ assertEquals("Test message XYZ", actual, "Should use current param value");
// modify parameter after calling msg.getFormattedMessage
param.set("000");
final String after = msg.getFormattedMessage();
- assertEquals("Should not change after rendered once", "Test message XYZ", after);
+ assertEquals("Test message XYZ", after, "Should not change after rendered once");
}
}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/message/ReusableMessageFactoryTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ReusableMessageFactoryTest.java
similarity index 96%
rename from log4j-api/src/test/java/org/apache/logging/log4j/message/ReusableMessageFactoryTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/message/ReusableMessageFactoryTest.java
index 7942e5faf55..f5e293134e7 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/message/ReusableMessageFactoryTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ReusableMessageFactoryTest.java
@@ -16,9 +16,9 @@
*/
package org.apache.logging.log4j.message;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
/**
* Tests the ReusableMessageFactory class.
@@ -53,10 +53,10 @@ public void testCreateEventReturnsSameInstance() throws Exception {
private void assertReusableParameterizeMessage(final Message message, final String txt, final Object[] params) {
assertTrue(message instanceof ReusableParameterizedMessage);
final ReusableParameterizedMessage msg = (ReusableParameterizedMessage) message;
- assertTrue("reserved", msg.reserved);
+ assertTrue(msg.reserved, "reserved");
assertEquals(txt, msg.getFormat());
- assertEquals("count", msg.getParameterCount(), params.length);
+ assertEquals(msg.getParameterCount(), params.length, "count");
final Object[] messageParams = msg.getParameters();
for (int i = 0; i < params.length; i++) {
assertEquals(messageParams[i], params[i]);
@@ -127,4 +127,4 @@ public void run() {
ReusableMessageFactory.release(message2[0]);
}
-}
\ No newline at end of file
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/message/ReusableObjectMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ReusableObjectMessageTest.java
similarity index 95%
rename from log4j-api/src/test/java/org/apache/logging/log4j/message/ReusableObjectMessageTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/message/ReusableObjectMessageTest.java
index d4f87a7dd8e..ea386fb3c38 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/message/ReusableObjectMessageTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ReusableObjectMessageTest.java
@@ -16,9 +16,9 @@
*/
package org.apache.logging.log4j.message;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
/**
* Tests ReusableObjectMessage.
@@ -49,8 +49,8 @@ public void testGetFormattedMessage_ReturnsLatestSetString() throws Exception {
}
@Test
- public void testGetFormat_InitiallyNullString() throws Exception {
- assertEquals("null", new ReusableObjectMessage().getFormat());
+ public void testGetFormat_InitiallyNull() throws Exception {
+ assertNull(new ReusableObjectMessage().getFormat());
}
@Test
@@ -119,4 +119,4 @@ public void testFormatTo_WritesLatestSetString() throws Exception {
msg.formatTo(sb);
assertEquals("xyz", sb.toString());
}
-}
\ No newline at end of file
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/message/ReusableParameterizedMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ReusableParameterizedMessageTest.java
similarity index 83%
rename from log4j-api/src/test/java/org/apache/logging/log4j/message/ReusableParameterizedMessageTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/message/ReusableParameterizedMessageTest.java
index b25735c17af..262cf8ed558 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/message/ReusableParameterizedMessageTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ReusableParameterizedMessageTest.java
@@ -16,10 +16,13 @@
*/
package org.apache.logging.log4j.message;
-import org.apache.logging.log4j.junit.Mutable;
-import org.junit.Test;
+import org.apache.logging.log4j.test.junit.Mutable;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.*;
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
/**
* Tests ReusableParameterizedMessage.
@@ -124,12 +127,12 @@ public void testNotSafeWithMutableParams() {
// modify parameter before calling msg.getFormattedMessage
param.set("XYZ");
final String actual = msg.getFormattedMessage();
- assertEquals("Should use current param value", "Test message XYZ", actual);
+ assertEquals("Test message XYZ", actual, "Should use current param value");
// modify parameter after calling msg.getFormattedMessage
param.set("000");
final String after = msg.getFormattedMessage();
- assertEquals("Renders again", "Test message 000", after);
+ assertEquals("Test message 000", after, "Renders again");
}
@Test
@@ -144,4 +147,23 @@ public void testThrowable() {
msg.set(testMsg, "msgs", EXCEPTION2);
assertSame(EXCEPTION2, msg.getThrowable());
}
+
+ @Test
+ public void testParameterConsumer() {
+ final String testMsg = "Test message {}";
+ final ReusableParameterizedMessage msg = new ReusableParameterizedMessage();
+ final Throwable EXCEPTION1 = new IllegalAccessError("#1");
+ msg.set(testMsg, "msg", EXCEPTION1);
+ List expected = new LinkedList<>();
+ expected.add("msg");
+ expected.add(EXCEPTION1);
+ final List actual = new LinkedList<>();
+ msg.forEachParameter(new ParameterConsumer() {
+ @Override
+ public void accept(Object parameter, int parameterIndex, Void state) {
+ actual.add(parameter);
+ }
+ }, null);
+ assertEquals(expected, actual);
+ }
}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/message/ReusableSimpleMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ReusableSimpleMessageTest.java
similarity index 96%
rename from log4j-api/src/test/java/org/apache/logging/log4j/message/ReusableSimpleMessageTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/message/ReusableSimpleMessageTest.java
index 3a56b643175..3a7f441214c 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/message/ReusableSimpleMessageTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ReusableSimpleMessageTest.java
@@ -16,9 +16,9 @@
*/
package org.apache.logging.log4j.message;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
/**
* Tests ReusableSimpleMessage.
@@ -50,7 +50,7 @@ public void testGetFormattedMessage_ReturnsLatestSetString() throws Exception {
@Test
public void testGetFormat_InitiallyStringNull() throws Exception {
- assertEquals("null", new ReusableSimpleMessage().getFormat());
+ assertNull(new ReusableSimpleMessage().getFormat());
}
@Test
@@ -119,4 +119,4 @@ public void testFormatTo_WritesLatestSetString() throws Exception {
msg.formatTo(sb);
assertEquals("xyz", sb.toString());
}
-}
\ No newline at end of file
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/message/SimpleMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/SimpleMessageTest.java
similarity index 94%
rename from log4j-api/src/test/java/org/apache/logging/log4j/message/SimpleMessageTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/message/SimpleMessageTest.java
index 383ecb49263..a43d986a916 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/message/SimpleMessageTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/SimpleMessageTest.java
@@ -16,9 +16,9 @@
*/
package org.apache.logging.log4j.message;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
/**
* Tests the SimpleMessage class.
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/StringFormattedMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/StringFormattedMessageTest.java
new file mode 100644
index 00000000000..08035a3ead3
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/StringFormattedMessageTest.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.message;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Locale;
+
+import org.apache.logging.log4j.test.junit.Mutable;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.api.parallel.Resources;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@ResourceLock(value = Resources.LOCALE, mode = ResourceAccessMode.READ)
+public class StringFormattedMessageTest {
+
+ private static final int LOOP_CNT = 500;
+ String[] array = new String[LOOP_CNT];
+
+ @Test
+ public void testNoArgs() {
+ final String testMsg = "Test message %1s";
+ StringFormattedMessage msg = new StringFormattedMessage(testMsg, (Object[]) null);
+ String result = msg.getFormattedMessage();
+ final String expected = "Test message null";
+ assertEquals(expected, result);
+ final Object[] array = null;
+ msg = new StringFormattedMessage(testMsg, array, null);
+ result = msg.getFormattedMessage();
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testOneStringArg() {
+ final String testMsg = "Test message %1s";
+ final StringFormattedMessage msg = new StringFormattedMessage(testMsg, "Apache");
+ final String result = msg.getFormattedMessage();
+ final String expected = "Test message Apache";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testOneIntArgLocaleUs() {
+ final String testMsg = "Test e = %+10.4f";
+ final StringFormattedMessage msg = new StringFormattedMessage(Locale.US, testMsg, Math.E);
+ final String result = msg.getFormattedMessage();
+ final String expected = "Test e = +2.7183";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testOneArgLocaleFrance() {
+ final String testMsg = "Test e = %+10.4f";
+ final StringFormattedMessage msg = new StringFormattedMessage(Locale.FRANCE, testMsg, Math.E);
+ final String result = msg.getFormattedMessage();
+ final String expected = "Test e = +2,7183";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testException() {
+ final String testMsg = "Test message {0}";
+ final MessageFormatMessage msg = new MessageFormatMessage(testMsg, "Apache", new NullPointerException("Null"));
+ final String result = msg.getFormattedMessage();
+ final String expected = "Test message Apache";
+ assertEquals(expected, result);
+ final Throwable t = msg.getThrowable();
+ assertNotNull(t, "No Throwable");
+ }
+
+ @Test
+ public void testUnsafeWithMutableParams() { // LOG4J2-763
+ final String testMsg = "Test message %s";
+ final Mutable param = new Mutable().set("abc");
+ final StringFormattedMessage msg = new StringFormattedMessage(testMsg, param);
+
+ // modify parameter before calling msg.getFormattedMessage
+ param.set("XYZ");
+ final String actual = msg.getFormattedMessage();
+ assertEquals("Test message XYZ", actual, "Should use initial param value");
+ }
+
+ @Test
+ public void testSafeAfterGetFormattedMessageIsCalled() { // LOG4J2-763
+ final String testMsg = "Test message %s";
+ final Mutable param = new Mutable().set("abc");
+ final StringFormattedMessage msg = new StringFormattedMessage(testMsg, param);
+
+ // modify parameter after calling msg.getFormattedMessage
+ msg.getFormattedMessage();
+ param.set("XYZ");
+ final String actual = msg.getFormattedMessage();
+ assertEquals("Test message abc", actual, "Should use initial param value");
+ }
+
+ @SuppressWarnings("BanSerializableRead")
+ @Test
+ public void testSerialization() throws IOException, ClassNotFoundException {
+ final StringFormattedMessage expected = new StringFormattedMessage("Msg", "a", "b", "c");
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try (final ObjectOutputStream out = new ObjectOutputStream(baos)) {
+ out.writeObject(expected);
+ }
+ final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ final ObjectInputStream in = new ObjectInputStream(bais);
+ final StringFormattedMessage actual = (StringFormattedMessage) in.readObject();
+ assertEquals(expected, actual);
+ assertEquals(expected.getFormat(), actual.getFormat());
+ assertEquals(expected.getFormattedMessage(), actual.getFormattedMessage());
+ assertArrayEquals(expected.getParameters(), actual.getParameters());
+ }
+
+ @Test
+ public void testPercentInMessageNoArgs() {
+ // LOG4J2-3458 LocalizedMessage causes a lot of noise on the console
+ //
+ // ERROR StatusLogger Unable to format msg: C:/Program%20Files/Some%20Company/Some%20Product%20Name/
+ // java.util.UnknownFormatConversionException: Conversion = 'F'
+ // at java.util.Formatter$FormatSpecifier.conversion(Formatter.java:2691)
+ // at java.util.Formatter$FormatSpecifier.(Formatter.java:2720)
+ // at java.util.Formatter.parse(Formatter.java:2560)
+ // at java.util.Formatter.format(Formatter.java:2501)
+ // at java.util.Formatter.format(Formatter.java:2455)
+ // at java.lang.String.format(String.java:2981)
+ // at org.apache.logging.log4j.message.StringFormattedMessage.formatMessage(StringFormattedMessage.java:120)
+ // at org.apache.logging.log4j.message.StringFormattedMessage.getFormattedMessage(StringFormattedMessage.java:88)
+ // at
+ // org.apache.logging.log4j.message.StringFormattedMessageTest.testPercentInMessageNoArgs(StringFormattedMessageTest.java:153)
+ final StringFormattedMessage msg = new StringFormattedMessage("C:/Program%20Files/Some%20Company/Some%20Product%20Name/", new Object[] {});
+ assertEquals("C:/Program%20Files/Some%20Company/Some%20Product%20Name/", msg.getFormattedMessage());
+ }
+
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/message/StructuredDataMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/StructuredDataMessageTest.java
similarity index 89%
rename from log4j-api/src/test/java/org/apache/logging/log4j/message/StructuredDataMessageTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/message/StructuredDataMessageTest.java
index 51a4ceec460..b592fcea8b2 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/message/StructuredDataMessageTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/StructuredDataMessageTest.java
@@ -16,9 +16,9 @@
*/
package org.apache.logging.log4j.message;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
/**
*
@@ -82,11 +82,12 @@ public void testBuilder() {
assertEquals(expected, result);
}
- @Test(expected = IllegalArgumentException.class)
+ @Test
public void testMsgWithKeyTooLong() {
final String testMsg = "Test message {}";
final StructuredDataMessage msg = new StructuredDataMessage("MsgId@12345", testMsg, "Alert");
- msg.put("This is a very long key that will violate the key length validation", "Testing");
+ assertThrows(IllegalArgumentException.class, () ->
+ msg.put("This is a very long key that will violate the key length validation", "Testing"));
}
@Test
@@ -107,4 +108,12 @@ public void testMutableByDesign() { // LOG4J2-763
final String expected2 = "Alert [MsgId@1 memo=\"Added later\" message=\"Test message {}\" project=\"Log4j\"] Test message {}";
assertEquals(expected2, result2);
}
+
+ @Test
+ public void testEnterpriseNoAsOidFragment() {
+ final String testMsg = "Test message {}";
+ final StructuredDataMessage structuredDataMessage = new StructuredDataMessage("XX_DATA@1234.55.6.7", testMsg, "Nothing");
+ assertNotNull(structuredDataMessage);
+ }
+
}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/message/ThreadDumpMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ThreadDumpMessageTest.java
similarity index 82%
rename from log4j-api/src/test/java/org/apache/logging/log4j/message/ThreadDumpMessageTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/message/ThreadDumpMessageTest.java
index 8668d0ab84f..40cb3e78bfd 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/message/ThreadDumpMessageTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ThreadDumpMessageTest.java
@@ -16,16 +16,14 @@
*/
package org.apache.logging.log4j.message;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.ReentrantLock;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
-/**
- *
- */
public class ThreadDumpMessageTest {
@Test
@@ -34,13 +32,14 @@ public void testMessage() {
final String message = msg.getFormattedMessage();
//System.out.print(message);
- assertTrue("No header", message.contains("Testing"));
- assertTrue("No RUNNABLE", message.contains("RUNNABLE"));
- assertTrue("No ThreadDumpMessage", message.contains("ThreadDumpMessage"));
+ assertTrue(message.contains("Testing"), "No header");
+ assertTrue(message.contains("RUNNABLE"), "No RUNNABLE");
+ assertTrue(message.contains("ThreadDumpMessage"), "No ThreadDumpMessage");
}
@Test
+ @Tag("sleepy")
public void testMessageWithLocks() throws Exception {
final ReentrantLock lock = new ReentrantLock();
lock.lock();
@@ -60,9 +59,9 @@ public void testMessageWithLocks() throws Exception {
final String message = msg.getFormattedMessage();
//System.out.print(message);
- assertTrue("No header", message.contains("Testing"));
- assertTrue("No RUNNABLE", message.contains("RUNNABLE"));
- assertTrue("No ThreadDumpMessage", message.contains("ThreadDumpMessage"));
+ assertTrue(message.contains("Testing"), "No header");
+ assertTrue(message.contains("RUNNABLE"), "No RUNNABLE");
+ assertTrue(message.contains("ThreadDumpMessage"), "No ThreadDumpMessage");
//assertTrue("No Locks", message.contains("waiting on"));
//assertTrue("No syncronizers", message.contains("locked syncrhonizers"));
}
@@ -90,7 +89,7 @@ public void run() {
other.start();
other.join();
- assertTrue("No mention of other thread in msg", !actual[0].contains("OtherThread"));
+ assertFalse(actual[0].contains("OtherThread"), "No mention of other thread in msg");
}
@Test
@@ -98,7 +97,7 @@ public void formatTo_usesCachedMessageString() throws Exception {
final ThreadDumpMessage message = new ThreadDumpMessage("");
final String initial = message.getFormattedMessage();
- assertFalse("no ThreadWithCountDownLatch thread yet", initial.contains("ThreadWithCountDownLatch"));
+ assertFalse(initial.contains("ThreadWithCountDownLatch"), "no ThreadWithCountDownLatch thread yet");
final CountDownLatch started = new CountDownLatch(1);
final CountDownLatch keepAlive = new CountDownLatch(1);
@@ -108,13 +107,13 @@ public void formatTo_usesCachedMessageString() throws Exception {
final StringBuilder result = new StringBuilder();
message.formatTo(result);
- assertFalse("no ThreadWithCountDownLatch captured",
- result.toString().contains("ThreadWithCountDownLatch"));
+ assertFalse(
+ result.toString().contains("ThreadWithCountDownLatch"), "no ThreadWithCountDownLatch captured");
assertEquals(initial, result.toString());
keepAlive.countDown(); // allow thread to die
}
- private class Thread1 extends Thread {
+ private static class Thread1 extends Thread {
private final ReentrantLock lock;
public Thread1(final ReentrantLock lock) {
@@ -128,7 +127,7 @@ public void run() {
}
}
- private class Thread2 extends Thread {
+ private static class Thread2 extends Thread {
private final Object obj;
public Thread2(final Object obj) {
@@ -142,7 +141,7 @@ public void run() {
}
}
- private class ThreadWithCountDownLatch extends Thread {
+ private static class ThreadWithCountDownLatch extends Thread {
private final CountDownLatch started;
private final CountDownLatch keepAlive;
volatile boolean finished;
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/simple/SimpleLoggerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/simple/SimpleLoggerTest.java
similarity index 75%
rename from log4j-api/src/test/java/org/apache/logging/log4j/simple/SimpleLoggerTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/simple/SimpleLoggerTest.java
index 0d57e5ee14a..d56c1aedf90 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/simple/SimpleLoggerTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/simple/SimpleLoggerTest.java
@@ -18,15 +18,20 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.junit.LogManagerLoggerContextFactoryRule;
-import org.junit.ClassRule;
-import org.junit.Test;
+import org.apache.logging.log4j.test.junit.LoggerContextFactoryExtension;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.api.parallel.Resources;
+@Tag("smoke")
+// TODO: use a 'LoggerContextFactory` lock
+@ResourceLock(value = Resources.GLOBAL)
public class SimpleLoggerTest {
- @ClassRule
- public static final LogManagerLoggerContextFactoryRule rule = new LogManagerLoggerContextFactoryRule(
- new SimpleLoggerContextFactory());
+ @RegisterExtension
+ public static final LoggerContextFactoryExtension EXTENSION = new LoggerContextFactoryExtension(SimpleLoggerContextFactory.INSTANCE);
private final Logger logger = LogManager.getLogger("TestError");
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java
similarity index 87%
rename from log4j-api/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java
index e3ad5d6bc47..e702e7ceec8 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java
@@ -16,20 +16,18 @@
*/
package org.apache.logging.log4j.spi;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
import java.util.HashMap;
import java.util.Map;
-import org.apache.logging.log4j.ThreadContext;
-import org.junit.Test;
+import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
/**
* Tests the {@code DefaultThreadContextMap} class.
*/
+@UsingThreadContextMap
public class DefaultThreadContextMapTest {
@Test
@@ -129,7 +127,7 @@ private DefaultThreadContextMap createMap() {
assertEquals("value2", map.get("key2"));
return map;
}
-
+
@Test
public void testGetCopyReturnsMutableMap() {
final DefaultThreadContextMap map = new DefaultThreadContextMap(true);
@@ -172,7 +170,7 @@ public void testGetImmutableMapReturnsNullIfEmpty() {
assertNull(map.getImmutableMapOrNull());
}
- @Test(expected = UnsupportedOperationException.class)
+ @Test
public void testGetImmutableMapReturnsImmutableMapIfNonEmpty() {
final DefaultThreadContextMap map = new DefaultThreadContextMap(true);
map.put("key1", "value1");
@@ -182,11 +180,11 @@ public void testGetImmutableMapReturnsImmutableMapIfNonEmpty() {
assertEquals("value1", immutable.get("key1")); // copy has values too
// immutable
- immutable.put("key", "value"); // error
+ assertThrows(UnsupportedOperationException.class, () -> immutable.put("key", "value"));
}
@Test
- public void testGetImmutableMapCopyNotAffectdByContextMapChanges() {
+ public void testGetImmutableMapCopyNotAffectedByContextMapChanges() {
final DefaultThreadContextMap map = new DefaultThreadContextMap(true);
map.put("key1", "value1");
assertFalse(map.isEmpty());
@@ -216,20 +214,13 @@ public void testToStringShowsMapContext() {
@Test
public void testThreadLocalNotInheritableByDefault() {
- System.clearProperty(DefaultThreadContextMap.INHERITABLE_MAP);
- final ThreadLocal> threadLocal = DefaultThreadContextMap.createThreadLocalMap(true);
+ final ThreadLocal> threadLocal = DefaultThreadContextMap.createThreadLocalMap(true, false);
assertFalse(threadLocal instanceof InheritableThreadLocal>);
}
-
+
@Test
public void testThreadLocalInheritableIfConfigured() {
- System.setProperty(DefaultThreadContextMap.INHERITABLE_MAP, "true");
- ThreadContextMapFactory.init();
- try {
- final ThreadLocal> threadLocal = DefaultThreadContextMap.createThreadLocalMap(true);
- assertTrue(threadLocal instanceof InheritableThreadLocal>);
- } finally {
- System.clearProperty(DefaultThreadContextMap.INHERITABLE_MAP);
- }
+ final ThreadLocal> threadLocal = DefaultThreadContextMap.createThreadLocalMap(true, true);
+ assertTrue(threadLocal instanceof InheritableThreadLocal>);
}
}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextStackTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextStackTest.java
similarity index 90%
rename from log4j-api/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextStackTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextStackTest.java
index 2c893aa51f7..6ec90475b93 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextStackTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextStackTest.java
@@ -16,23 +16,26 @@
*/
package org.apache.logging.log4j.spi;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
import java.util.Arrays;
+import java.util.Collections;
import java.util.Iterator;
import org.apache.logging.log4j.ThreadContext.ContextStack;
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
+import org.apache.logging.log4j.test.junit.UsingAnyThreadContext;
+import org.junit.jupiter.api.Test;
+@UsingAnyThreadContext
public class DefaultThreadContextStackTest {
- @Before
- public void before() {
- // clear the thread-local map
- new DefaultThreadContextMap(true).clear();
- }
-
@Test
public void testEqualsVsSameKind() {
final DefaultThreadContextStack stack1 = createStack();
@@ -45,8 +48,8 @@ public void testEqualsVsSameKind() {
@Test
public void testEqualsVsMutable() {
- final DefaultThreadContextStack stack1 = createStack();
- final MutableThreadContextStack stack2 = MutableThreadContextStackTest.createStack();
+ final ThreadContextStack stack1 = createStack();
+ final ThreadContextStack stack2 = MutableThreadContextStackTest.createStack();
assertEquals(stack1, stack1);
assertEquals(stack2, stack2);
assertEquals(stack1, stack2);
@@ -64,7 +67,7 @@ public void testHashCodeVsSameKind() {
public void testImmutableOrNullReturnsNullIfUseStackIsFalse() {
final DefaultThreadContextStack stack = new DefaultThreadContextStack(false);
stack.clear();
- assertEquals(null, stack.getImmutableStackOrNull());
+ assertNull(stack.getImmutableStackOrNull());
}
@Test
@@ -72,19 +75,19 @@ public void testImmutableOrNullReturnsNullIfStackIsEmpty() {
final DefaultThreadContextStack stack = new DefaultThreadContextStack(true);
stack.clear();
assertTrue(stack.isEmpty());
- assertEquals(null, stack.getImmutableStackOrNull());
+ assertNull(stack.getImmutableStackOrNull());
}
@Test
public void testImmutableOrNullReturnsCopyOfContents() {
final DefaultThreadContextStack stack = createStack();
- assertTrue(!stack.isEmpty());
+ assertFalse(stack.isEmpty());
final ContextStack actual = stack.getImmutableStackOrNull();
assertNotNull(actual);
assertEquals(stack, actual);
}
- @Test(expected = UnsupportedOperationException.class)
+ @Test
public void testModifyingImmutableOrNullThrowsException() {
final DefaultThreadContextStack stack = createStack();
final int originalSize = stack.size();
@@ -92,7 +95,7 @@ public void testModifyingImmutableOrNullThrowsException() {
final ContextStack actual = stack.getImmutableStackOrNull();
assertEquals(originalSize, actual.size());
- actual.pop();
+ assertThrows(UnsupportedOperationException.class, () -> actual.pop());
}
@Test
@@ -277,7 +280,7 @@ public void testRemove() {
stack.remove("msg3");
assertEquals(1, stack.size());
- assertTrue(stack.containsAll(Arrays.asList("msg2")));
+ assertTrue(stack.containsAll(Collections.singletonList("msg2")));
assertEquals("msg2", stack.peek());
}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/LoggerAdapterTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/LoggerAdapterTest.java
new file mode 100644
index 00000000000..6a8ed7b6c61
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/LoggerAdapterTest.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.spi;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.test.TestLogger;
+import org.apache.logging.log4j.test.TestLoggerContext;
+import org.apache.logging.log4j.test.TestLoggerContextFactory;
+import org.apache.logging.log4j.simple.SimpleLoggerContext;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Created by Pavel.Sivolobtchik@uxpsystems.com on 2016-10-19.
+ */
+public class LoggerAdapterTest {
+
+ private static class RunnableThreadTest implements Runnable {
+ private final AbstractLoggerAdapter adapter;
+ private final LoggerContext context;
+ private final CountDownLatch doneSignal;
+ private final int index;
+ private Map resultMap;
+
+ private final CountDownLatch startSignal;
+
+ public RunnableThreadTest(final int index, final TestLoggerAdapter adapter, final LoggerContext context,
+ final CountDownLatch startSignal, final CountDownLatch doneSignal) {
+ this.adapter = adapter;
+ this.context = context;
+ this.startSignal = startSignal;
+ this.doneSignal = doneSignal;
+ this.index = index;
+ }
+
+ public Map getResultMap() {
+ return resultMap;
+ }
+
+ @Override
+ public void run() {
+ try {
+ startSignal.await();
+ resultMap = adapter.getLoggersInContext(context);
+ resultMap.put(String.valueOf(index), new TestLogger());
+ doneSignal.countDown();
+ }
+ catch (final Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ }
+
+ private static class TestLoggerAdapter extends AbstractLoggerAdapter {
+
+ @Override
+ protected LoggerContext getContext() {
+ return null;
+ }
+
+ @Override
+ protected Logger newLogger(final String name, final LoggerContext context) {
+ return null;
+ }
+ }
+
+ private static class TestLoggerAdapter2 extends AbstractLoggerAdapter {
+
+ @Override
+ protected Logger newLogger(String name, LoggerContext context) {
+ return context.getLogger(name);
+ }
+
+ @Override
+ protected LoggerContext getContext() {
+ return null;
+ }
+
+ public LoggerContext getContext(String fqcn) {
+ for (LoggerContext lc : registry.keySet()) {
+ TestLoggerContext2 context = (TestLoggerContext2) lc;
+ if (fqcn.equals(context.getName())) {
+ return context;
+ }
+ }
+ LoggerContext lc = new TestLoggerContext2(fqcn, this);
+ registry.put(lc, new ConcurrentHashMap<>());
+ return lc;
+ }
+ }
+
+ private static class TestLoggerContext2 extends TestLoggerContext {
+ private final String name;
+ private final LoggerContextShutdownAware listener;
+
+ public TestLoggerContext2(String name, LoggerContextShutdownAware listener) {
+ this.name = name;
+ this.listener = listener;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void shutdown() {
+ listener.contextShutdown(this);
+ }
+ }
+
+ @Test
+ public void testCleanup() throws Exception {
+ final LoggerContextFactory factory = new TestLoggerContextFactory();
+ final TestLoggerAdapter2 adapter = new TestLoggerAdapter2();
+ for (int i = 0; i < 5; ++i) {
+ LoggerContext lc = adapter.getContext(Integer.toString(i));
+ lc.getLogger(Integer.toString(i));
+ }
+ assertEquals(5, adapter.registry.size(), "Expected 5 LoggerContexts");
+ Set contexts = new HashSet<>(adapter.registry.keySet());
+ for (LoggerContext context : contexts) {
+ ((TestLoggerContext2) context).shutdown();
+ }
+ assertEquals(0, adapter.registry.size(), "Expected 0 LoggerContexts");
+ }
+
+
+
+ /**
+ * Testing synchronization in the getLoggersInContext() method
+ */
+ @Test
+ public synchronized void testGetLoggersInContextSynch() throws Exception {
+ final TestLoggerAdapter adapter = new TestLoggerAdapter();
+
+ final int num = 500;
+
+ final CountDownLatch startSignal = new CountDownLatch(1);
+ final CountDownLatch doneSignal = new CountDownLatch(num);
+
+ final RunnableThreadTest[] instances = new RunnableThreadTest[num];
+ LoggerContext lastUsedContext = null;
+ for (int i = 0; i < num; i++) {
+ if (i % 2 == 0) {
+ //every other time create a new context
+ lastUsedContext = new SimpleLoggerContext();
+ }
+ final RunnableThreadTest runnable = new RunnableThreadTest(i, adapter, lastUsedContext, startSignal, doneSignal);
+ final Thread thread = new Thread(runnable);
+ thread.start();
+ instances[i] = runnable;
+ }
+
+ startSignal.countDown();
+ doneSignal.await();
+
+ for (int i = 0; i < num; i = i + 2) {
+ //maps for the same context should be the same instance
+ final Map resultMap1 = instances[i].getResultMap();
+ final Map resultMap2 = instances[i + 1].getResultMap();
+ assertSame(resultMap1, resultMap2, "not the same map for instances" + i + " and " + (i + 1) + ":");
+ assertEquals(2, resultMap1.size());
+ }
+ }
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/spi/MutableThreadContextStackTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/MutableThreadContextStackTest.java
similarity index 89%
rename from log4j-api/src/test/java/org/apache/logging/log4j/spi/MutableThreadContextStackTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/spi/MutableThreadContextStackTest.java
index 7592067dbeb..4a4a428ae9e 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/spi/MutableThreadContextStackTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/spi/MutableThreadContextStackTest.java
@@ -16,20 +16,20 @@
*/
package org.apache.logging.log4j.spi;
+import org.junit.jupiter.api.Test;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
public class MutableThreadContextStackTest {
@Test
public void testEmptyIfConstructedWithEmptyList() {
- final MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList());
+ final MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList<>());
assertTrue(stack.isEmpty());
}
@@ -43,7 +43,7 @@ public void testConstructorCopiesListContents() {
@Test
public void testPushAndAddIncreaseStack() {
- final MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList());
+ final MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList<>());
stack.clear();
assertTrue(stack.isEmpty());
stack.push("msg1");
@@ -54,7 +54,7 @@ public void testPushAndAddIncreaseStack() {
@Test
public void testPeekReturnsLastAddedItem() {
- final MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList());
+ final MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList<>());
stack.clear();
assertTrue(stack.isEmpty());
stack.push("msg1");
@@ -87,7 +87,7 @@ public void testPopRemovesLastAddedItem() {
@Test
public void testAsList() {
- final MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList());
+ final MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList<>());
stack.clear();
assertTrue(stack.isEmpty());
stack.push("msg1");
@@ -165,7 +165,7 @@ public void testHashCodeVsSameKind() {
* @return
*/
static MutableThreadContextStack createStack() {
- final MutableThreadContextStack stack1 = new MutableThreadContextStack(new ArrayList());
+ final MutableThreadContextStack stack1 = new MutableThreadContextStack(new ArrayList<>());
stack1.clear();
assertTrue(stack1.isEmpty());
stack1.push("msg1");
@@ -228,7 +228,7 @@ public void testRemove() {
stack.remove("msg3");
assertEquals(1, stack.size());
- assertTrue(stack.containsAll(Arrays.asList("msg2")));
+ assertTrue(stack.contains("msg2"));
assertEquals("msg2", stack.peek());
}
@@ -276,7 +276,7 @@ public void testRetainAll() {
@Test
public void testToStringShowsListContents() {
- final MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList());
+ final MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList<>());
assertEquals("[]", stack.toString());
stack.push("msg1");
@@ -302,66 +302,66 @@ public void testIsFrozenIsTrueAfterCallToFreeze() {
assertTrue(stack.isFrozen());
}
- @Test(expected = UnsupportedOperationException.class)
+ @Test
public void testAddAllOnFrozenStackThrowsException() {
final MutableThreadContextStack stack = new MutableThreadContextStack();
stack.freeze();
- stack.addAll(Arrays.asList("a", "b", "c"));
+ assertThrows(UnsupportedOperationException.class, () -> stack.addAll(Arrays.asList("a", "b", "c")));
}
- @Test(expected = UnsupportedOperationException.class)
+ @Test
public void testAddOnFrozenStackThrowsException() {
final MutableThreadContextStack stack = new MutableThreadContextStack();
stack.freeze();
- stack.add("a");
+ assertThrows(UnsupportedOperationException.class, () -> stack.add("a"));
}
- @Test(expected = UnsupportedOperationException.class)
+ @Test
public void testClearOnFrozenStackThrowsException() {
final MutableThreadContextStack stack = new MutableThreadContextStack();
stack.freeze();
- stack.clear();
+ assertThrows(UnsupportedOperationException.class, stack::clear);
}
- @Test(expected = UnsupportedOperationException.class)
+ @Test
public void testPopOnFrozenStackThrowsException() {
final MutableThreadContextStack stack = new MutableThreadContextStack();
stack.freeze();
- stack.pop();
+ assertThrows(UnsupportedOperationException.class, stack::pop);
}
- @Test(expected = UnsupportedOperationException.class)
+ @Test
public void testPushOnFrozenStackThrowsException() {
final MutableThreadContextStack stack = new MutableThreadContextStack();
stack.freeze();
- stack.push("a");
+ assertThrows(UnsupportedOperationException.class, () -> stack.push("a"));
}
- @Test(expected = UnsupportedOperationException.class)
+ @Test
public void testRemoveOnFrozenStackThrowsException() {
final MutableThreadContextStack stack = new MutableThreadContextStack();
stack.freeze();
- stack.remove("a");
+ assertThrows(UnsupportedOperationException.class, () -> stack.remove("a"));
}
- @Test(expected = UnsupportedOperationException.class)
+ @Test
public void testRemoveAllOnFrozenStackThrowsException() {
final MutableThreadContextStack stack = new MutableThreadContextStack();
stack.freeze();
- stack.removeAll(Arrays.asList("a", "b"));
+ assertThrows(UnsupportedOperationException.class, () -> stack.removeAll(Arrays.asList("a", "b")));
}
- @Test(expected = UnsupportedOperationException.class)
+ @Test
public void testRetainAllOnFrozenStackThrowsException() {
final MutableThreadContextStack stack = new MutableThreadContextStack();
stack.freeze();
- stack.retainAll(Arrays.asList("a", "b"));
+ assertThrows(UnsupportedOperationException.class, () -> stack.retainAll(Arrays.asList("a", "b")));
}
- @Test(expected = UnsupportedOperationException.class)
+ @Test
public void testTrimOnFrozenStackThrowsException() {
final MutableThreadContextStack stack = new MutableThreadContextStack();
stack.freeze();
- stack.trim(3);
+ assertThrows(UnsupportedOperationException.class, () -> stack.trim(3));
}
}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/status/StatusConsoleListenerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/status/StatusConsoleListenerTest.java
new file mode 100644
index 00000000000..9d761a1ce73
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/status/StatusConsoleListenerTest.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.status;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogBuilder;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.MessageFactory;
+import org.apache.logging.log4j.message.ParameterizedNoReferenceMessageFactory;
+import org.apache.logging.log4j.simple.SimpleLogger;
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+public class StatusConsoleListenerTest {
+
+ public static final MessageFactory MESSAGE_FACTORY = ParameterizedNoReferenceMessageFactory.INSTANCE;
+
+ @Test
+ void SimpleLogger_should_be_used() {
+
+ // Create a mock `SimpleLoggerFactory`.
+ final SimpleLogger logger = Mockito.mock(SimpleLogger.class);
+ final LogBuilder logBuilder = Mockito.mock(LogBuilder.class);
+ Mockito.when(logger.atLevel(Mockito.any())).thenReturn(logBuilder);
+ Mockito.when(logBuilder.withThrowable(Mockito.any())).thenReturn(logBuilder);
+ Mockito.when(logBuilder.withLocation(Mockito.any())).thenReturn(logBuilder);
+ final StatusLoggerFactory loggerFactory = Mockito.mock(StatusLoggerFactory.class);
+ Mockito
+ .when(loggerFactory.createSimpleLogger(
+ Mockito.any(),
+ Mockito.any(),
+ Mockito.any()))
+ .thenReturn(logger);
+
+ // Create the listener.
+ final PrintStream stream = Mockito.mock(PrintStream.class);
+ final Level level = Mockito.mock(Level.class);
+ final StatusConsoleListener listener = new StatusConsoleListener(level, stream, loggerFactory);
+
+ // Log a message.
+ final StackTraceElement caller = Mockito.mock(StackTraceElement.class);
+ final Message message = Mockito.mock(Message.class);
+ final Throwable throwable = Mockito.mock(Throwable.class);
+ final StatusData statusData = new StatusData(
+ caller,
+ level,
+ message,
+ throwable,
+ null);
+ listener.log(statusData);
+
+ // Verify the call.
+ Mockito
+ .verify(loggerFactory)
+ .createSimpleLogger(
+ Mockito.eq("StatusConsoleListener"),
+ Mockito.same(level),
+ Mockito.same(stream));
+ Mockito.verify(logger).atLevel(Mockito.same(level));
+ Mockito.verify(logBuilder).withThrowable(Mockito.same(throwable));
+ Mockito.verify(logBuilder).withLocation(Mockito.same(caller));
+ Mockito.verify(logBuilder).log(Mockito.same(message));
+
+ }
+
+ @Test
+ void level_and_stream_should_be_honored() throws Exception {
+
+ // Create the listener.
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ final String encoding = "UTF-8";
+ final PrintStream printStream = new PrintStream(outputStream, false, encoding);
+ final StatusConsoleListener listener = new StatusConsoleListener(Level.WARN, printStream);
+
+ // First, log a message that is expected to be logged.
+ final RuntimeException expectedThrowable = new RuntimeException("expectedThrowable");
+ expectedThrowable.setStackTrace(new StackTraceElement[]{
+ new StackTraceElement("expectedThrowableClass", "expectedThrowableMethod", "expectedThrowableFile", 1)
+ });
+ final Message expectedMessage = MESSAGE_FACTORY.newMessage("expectedMessage");
+ listener.log(new StatusData(
+ null, // since ignored by `SimpleLogger`
+ Level.WARN,
+ expectedMessage,
+ expectedThrowable,
+ null)); // as set by `StatusLogger` itself
+
+ // Second, log a message that is expected to be discarded due to its insufficient level.
+ final RuntimeException discardedThrowable = new RuntimeException("discardedThrowable");
+ discardedThrowable.setStackTrace(new StackTraceElement[]{
+ new StackTraceElement("discardedThrowableClass", "discardedThrowableMethod", "discardedThrowableFile", 2)
+ });
+ final Message discardedMessage = MESSAGE_FACTORY.newMessage("discardedMessage");
+ listener.log(new StatusData(
+ null, // since ignored by `SimpleLogger`
+ Level.INFO,
+ discardedMessage,
+ discardedThrowable,
+ null)); // as set by `StatusLogger` itself
+
+ // Collect the output.
+ printStream.flush();
+ final String output = outputStream.toString(encoding);
+
+ // Verify the output.
+ Assertions
+ .assertThat(output)
+ .isNotBlank()
+ .contains(expectedThrowable.getMessage())
+ .contains(expectedMessage.getFormattedMessage())
+ .doesNotContain(discardedThrowable.getMessage())
+ .doesNotContain(discardedMessage.getFormattedMessage());
+
+ }
+
+ @Test
+ void filters_should_be_honored() throws Exception {
+
+ // Create the listener.
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ final String encoding = "UTF-8";
+ final PrintStream printStream = new PrintStream(outputStream, false, encoding);
+ final StatusConsoleListener listener = new StatusConsoleListener(Level.TRACE, printStream);
+
+ // Set the filter.
+ final StackTraceElement caller = new StackTraceElement("callerClass", "callerMethod", "callerFile", 1);
+ listener.setFilters(caller.getClassName());
+
+ // Log the message to be filtered.
+ final Message message = MESSAGE_FACTORY.newMessage("foo");
+ listener.log(new StatusData(
+ caller,
+ Level.TRACE,
+ message,
+ null,
+ null)); // as set by `StatusLogger` itself
+
+ // Verify the filtering.
+ printStream.flush();
+ final String output = outputStream.toString(encoding);
+ Assertions.assertThat(output).isEmpty();
+
+ }
+
+ @Test
+ void non_system_streams_should_be_closed() throws Exception {
+ final PrintStream stream = Mockito.mock(PrintStream.class);
+ final StatusConsoleListener listener = new StatusConsoleListener(Level.WARN, stream);
+ listener.close();
+ Mockito.verify(stream).close();
+ }
+
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/status/StatusLoggerSerializationTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/status/StatusLoggerSerializationTest.java
new file mode 100644
index 00000000000..fedc5bd9562
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/status/StatusLoggerSerializationTest.java
@@ -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.
+ */
+package org.apache.logging.log4j.status;
+
+import java.util.stream.Stream;
+
+import org.apache.logging.log4j.test.AbstractSerializationTest;
+import org.junit.jupiter.api.Disabled;
+
+@Disabled
+public class StatusLoggerSerializationTest extends AbstractSerializationTest {
+
+ protected Stream data() {
+ return Stream.of(StatusLogger.getLogger());
+ }
+
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/test/BetterService.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/test/BetterService.java
new file mode 100644
index 00000000000..ca8b893e0ec
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/test/BetterService.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.test;
+
+public interface BetterService extends Service {
+}
\ No newline at end of file
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/test/Service.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/test/Service.java
new file mode 100644
index 00000000000..25b1b33681c
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/test/Service.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.test;
+
+public interface Service {
+}
\ No newline at end of file
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/test/Service1.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/test/Service1.java
new file mode 100644
index 00000000000..82c233b852c
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/test/Service1.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.test;
+
+public class Service1 implements Service {
+}
\ No newline at end of file
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/test/Service2.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/test/Service2.java
new file mode 100644
index 00000000000..c4a8c39b979
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/test/Service2.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.test;
+
+public class Service2 implements BetterService {
+}
\ No newline at end of file
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/test/ThreadContextTest.java
similarity index 76%
rename from log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/test/ThreadContextTest.java
index a1d11016fd6..42c7067cf50 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/test/ThreadContextTest.java
@@ -14,57 +14,53 @@
* See the license for the specific language governing permissions and
* limitations under the license.
*/
-package org.apache.logging.log4j;
+package org.apache.logging.log4j.test;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
-import org.junit.Test;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.test.junit.UsingAnyThreadContext;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
-/**
- *
- */
+@UsingAnyThreadContext
public class ThreadContextTest {
- public static void reinitThreadContext() {
- ThreadContext.init();
- }
@Test
public void testPush() {
ThreadContext.push("Hello");
- ThreadContext.push("{} is {}", ThreadContextTest.class.getSimpleName(),
- "running");
- assertEquals("Incorrect parameterized stack value",
- ThreadContext.pop(), "ThreadContextTest is running");
- assertEquals("Incorrect simple stack value", ThreadContext.pop(),
- "Hello");
+ ThreadContext.push("{} is {}", ThreadContextTest.class.getSimpleName(), "running");
+ assertEquals(ThreadContext.pop(), "ThreadContextTest is running", "Incorrect parameterized stack value");
+ assertEquals(ThreadContext.pop(), "Hello", "Incorrect simple stack value");
}
@Test
public void testInheritanceSwitchedOffByDefault() throws Exception {
- ThreadContext.clearMap();
ThreadContext.put("Greeting", "Hello");
StringBuilder sb = new StringBuilder();
TestThread thread = new TestThread(sb);
thread.start();
thread.join();
String str = sb.toString();
- assertTrue("Unexpected ThreadContext value. Expected null. Actual "
- + str, "null".equals(str));
+ assertEquals("null", str, "Unexpected ThreadContext value. Expected null. Actual " + str);
sb = new StringBuilder();
thread = new TestThread(sb);
thread.start();
thread.join();
str = sb.toString();
- assertTrue("Unexpected ThreadContext value. Expected null. Actual "
- + str, "null".equals(str));
+ assertEquals("null", str, "Unexpected ThreadContext value. Expected null. Actual " + str);
}
@Test
- public void perfTest() throws Exception {
+ @Tag("performance")
+ public void perfTest() {
ThreadContextUtilityClass.perfTest();
}
@@ -83,12 +79,12 @@ public void testGetImmutableContextReturnsEmptyMapIfEmpty() {
ThreadContextUtilityClass.testGetImmutableContextReturnsEmptyMapIfEmpty();
}
- @Test(expected = UnsupportedOperationException.class)
+ @Test
public void testGetImmutableContextReturnsImmutableMapIfNonEmpty() {
ThreadContextUtilityClass.testGetImmutableContextReturnsImmutableMapIfNonEmpty();
}
- @Test(expected = UnsupportedOperationException.class)
+ @Test
public void testGetImmutableContextReturnsImmutableMapIfEmpty() {
ThreadContextUtilityClass.testGetImmutableContextReturnsImmutableMapIfEmpty();
}
@@ -104,9 +100,19 @@ public void testPut() {
}
@Test
- public void testPutAll() {
+ public void testPutIfNotNull() {
+ ThreadContext.clearMap();
+ assertNull(ThreadContext.get("testKey"));
+ ThreadContext.put("testKey", "testValue");
+ assertEquals("testValue", ThreadContext.get("testKey"));
+ assertEquals("testValue", ThreadContext.get("testKey"), "Incorrect value in test key");
+ ThreadContext.putIfNull("testKey", "new Value");
+ assertEquals("testValue", ThreadContext.get("testKey"), "Incorrect value in test key");
ThreadContext.clearMap();
- //
+ }
+
+ @Test
+ public void testPutAll() {
assertTrue(ThreadContext.isEmpty());
assertFalse(ThreadContext.containsKey("key"));
final int mapSize = 10;
@@ -124,7 +130,6 @@ public void testPutAll() {
@Test
public void testRemove() {
- ThreadContext.clearMap();
assertNull(ThreadContext.get("testKey"));
ThreadContext.put("testKey", "testValue");
assertEquals("testValue", ThreadContext.get("testKey"));
@@ -136,7 +141,6 @@ public void testRemove() {
@Test
public void testRemoveAll() {
- ThreadContext.clearMap();
ThreadContext.put("testKey1", "testValue1");
ThreadContext.put("testKey2", "testValue2");
assertEquals("testValue1", ThreadContext.get("testKey1"));
@@ -151,7 +155,6 @@ public void testRemoveAll() {
@Test
public void testContainsKey() {
- ThreadContext.clearMap();
assertFalse(ThreadContext.containsKey("testKey"));
ThreadContext.put("testKey", "testValue");
assertTrue(ThreadContext.containsKey("testKey"));
@@ -160,7 +163,7 @@ public void testContainsKey() {
assertFalse(ThreadContext.containsKey("testKey"));
}
- private class TestThread extends Thread {
+ private static class TestThread extends Thread {
private final StringBuilder sb;
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/test/junit/TestPropertySourceTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/test/junit/TestPropertySourceTest.java
new file mode 100644
index 00000000000..2962659b55b
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/test/junit/TestPropertySourceTest.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.test.junit;
+
+import org.apache.logging.log4j.test.TestProperties;
+import org.apache.logging.log4j.util.PropertiesUtil;
+import org.apache.logging.log4j.util.PropertyEnvironment;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@UsingTestProperties
+public class TestPropertySourceTest {
+
+ private static TestProperties staticProperties;
+ private TestProperties instanceProperties;
+
+ @Test
+ public void testInjectedFields() {
+ assertThat(staticProperties).isNotNull();
+ assertThat(instanceProperties).isNotNull();
+
+ // Test that per-class properties are overridden by per-test properties
+ final PropertyEnvironment env = PropertiesUtil.getProperties();
+ staticProperties.setProperty("log4j2.staticProperty", "static");
+ staticProperties.setProperty("log4j2.instanceProperty", "static");
+ instanceProperties.setProperty("log4j2.instanceProperty", "instance");
+ assertThat(env.getStringProperty("log4j2.staticProperty")).isEqualTo("static");
+ assertThat(env.getStringProperty("log4j.instanceProperty")).isEqualTo("instance");
+ }
+
+ @Test
+ public void testInjectedParameter(final TestProperties paramProperties) {
+ assertThat(paramProperties).isEqualTo(instanceProperties);
+ }
+
+ @Test
+ @SetTestProperty(key = "log4j2.testSetTestProperty", value = "true")
+ public void testSetTestProperty() {
+ final PropertyEnvironment env = PropertiesUtil.getProperties();
+ assertThat(env.getBooleanProperty("log4j2.testSetTestProperty")).isTrue();
+ }
+}
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/util/AssertTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/AssertTest.java
similarity index 75%
rename from log4j-core/src/test/java/org/apache/logging/log4j/core/util/AssertTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/util/AssertTest.java
index 242c41e892c..9592ee59348 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/util/AssertTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/AssertTest.java
@@ -14,28 +14,19 @@
* See the license for the specific language governing permissions and
* limitations under the license.
*/
-package org.apache.logging.log4j.core.util;
+package org.apache.logging.log4j.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
-/**
- *
- */
-@RunWith(Parameterized.class)
public class AssertTest {
- private final Object value;
- private final boolean isEmpty;
-
- @Parameterized.Parameters
public static Object[][] data() {
return new Object[][]{
// value, isEmpty
@@ -55,14 +46,10 @@ public static Object[][] data() {
};
}
- public AssertTest(final Object value, final boolean isEmpty) {
- this.value = value;
- this.isEmpty = isEmpty;
- }
-
- @Test
- public void isEmpty() throws Exception {
+ @ParameterizedTest
+ @MethodSource("data")
+ public void isEmpty(Object value, boolean isEmpty) throws Exception {
assertEquals(isEmpty, Assert.isEmpty(value));
}
-}
\ No newline at end of file
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/CharsTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/CharsTest.java
new file mode 100644
index 00000000000..fb5e4664a40
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/CharsTest.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.util;
+
+import java.util.stream.IntStream;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class CharsTest {
+ @ParameterizedTest
+ @ValueSource(ints = {-1, 16, 400, -1, 16, 400})
+ public void invalidDigitReturnsNullCharacter(int invalidDigit) {
+ assertAll(
+ () -> assertEquals('\0', Chars.getUpperCaseHex(invalidDigit)),
+ () -> assertEquals('\0', Chars.getLowerCaseHex(invalidDigit))
+ );
+ }
+
+ @Test
+ public void validDigitReturnsProperCharacter() {
+ final char[] expectedLower = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ final char[] expectedUpper = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+ assertAll(IntStream.range(0, 16).mapToObj(i -> () -> assertAll(
+ () -> assertEquals(expectedLower[i], Chars.getLowerCaseHex(i), String.format("Expected %x", i)),
+ () -> assertEquals(expectedUpper[i], Chars.getUpperCaseHex(i), String.format("Expected %X", i))
+ )));
+ }
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/CharsetForNameMain.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/CharsetForNameMain.java
similarity index 100%
rename from log4j-api/src/test/java/org/apache/logging/log4j/util/CharsetForNameMain.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/util/CharsetForNameMain.java
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/ClassLocator.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/ClassLocator.java
similarity index 100%
rename from log4j-api/src/test/java/org/apache/logging/log4j/util/ClassLocator.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/util/ClassLocator.java
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/ClassNameLocator.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/ClassNameLocator.java
similarity index 100%
rename from log4j-api/src/test/java/org/apache/logging/log4j/util/ClassNameLocator.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/util/ClassNameLocator.java
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/ConstantsTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/ConstantsTest.java
new file mode 100644
index 00000000000..0c028b898fe
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/ConstantsTest.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.util;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ConstantsTest {
+
+ @Test
+ public void testJdkVersionDetection() {
+ assertEquals(1, Constants.getMajorVersion("1.1.2"));
+ assertEquals(8, Constants.getMajorVersion("1.8.2"));
+ assertEquals(9, Constants.getMajorVersion("9.1.1"));
+ assertEquals(11, Constants.getMajorVersion("11.1.1"));
+ }
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/DeserializerHelper.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/DeserializerHelper.java
similarity index 97%
rename from log4j-api/src/test/java/org/apache/logging/log4j/util/DeserializerHelper.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/util/DeserializerHelper.java
index 930471d9cd2..31030d3d759 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/util/DeserializerHelper.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/DeserializerHelper.java
@@ -26,6 +26,7 @@
* @see SortedArrayStringMapTest#testDeserializationOfUnknownClass()
*/
public class DeserializerHelper {
+ @SuppressWarnings("BanSerializableRead")
public static void main(final String... args) throws Exception {
final File file = new File(args[0]);
ObjectInputStream in = null;
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/EnvironmentPropertySourceSecurityManagerIT.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/EnvironmentPropertySourceSecurityManagerIT.java
new file mode 100644
index 00000000000..24b0da537ba
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/EnvironmentPropertySourceSecurityManagerIT.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.util;
+
+import java.security.Permission;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledForJreRange;
+import org.junit.jupiter.api.condition.JRE;
+import org.junit.jupiter.api.parallel.ResourceLock;
+
+/**
+ * Tests LOG4J2-2274.
+ *
+ * Using a security manager can mess up other tests so this is best used from
+ * integration tests (classes that end in "IT" instead of "Test" and
+ * "TestCase".)
+ *
+ *
+ * @see EnvironmentPropertySource
+ * @see SecurityManager
+ * @see System#setSecurityManager(SecurityManager)
+ */
+@ResourceLock("java.lang.SecurityManager")
+@DisabledForJreRange(min = JRE.JAVA_18) // custom SecurityManager instances throw UnsupportedOperationException
+public class EnvironmentPropertySourceSecurityManagerIT {
+
+ /**
+ * Always throws a SecurityException for any environment variables permission
+ * check.
+ */
+ private static class TestSecurityManager extends SecurityManager {
+ @Override
+ public void checkPermission(final Permission permission) {
+ if ("getenv.*".equals(permission.getName())) {
+ throw new SecurityException();
+ }
+ }
+ }
+
+ /**
+ * Makes sure we do not blow up with exception below due to a security manager
+ * rejecting environment variable access in {@link EnvironmentPropertySource}.
+ *
+ *
+ * java.lang.NoClassDefFoundError: Could not initialize class org.apache.logging.log4j.util.PropertiesUtil
+ * at org.apache.logging.log4j.status.StatusLogger.(StatusLogger.java:78)
+ * at org.apache.logging.log4j.core.AbstractLifeCycle.(AbstractLifeCycle.java:38)
+ * at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
+ * at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
+ * at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
+ * at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
+ * at org.apache.logging.log4j.core.config.builder.impl.DefaultConfigurationBuilder.build(DefaultConfigurationBuilder.java:172)
+ * at org.apache.logging.log4j.core.config.builder.impl.DefaultConfigurationBuilder.build(DefaultConfigurationBuilder.java:161)
+ * at org.apache.logging.log4j.core.config.builder.impl.DefaultConfigurationBuilder.build(DefaultConfigurationBuilder.java:1)
+ * at org.apache.logging.log4j.util.EnvironmentPropertySourceSecurityManagerTest.test(EnvironmentPropertySourceSecurityManagerTest.java:55)
+ *
+ */
+ @Test
+ public void test() {
+ var existing = System.getSecurityManager();
+ try {
+ System.setSecurityManager(new TestSecurityManager());
+ PropertiesUtil.getProperties();
+ } finally {
+ System.setSecurityManager(existing);
+ }
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/EnvironmentPropertySourceTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/EnvironmentPropertySourceTest.java
new file mode 100644
index 00000000000..407915f5175
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/EnvironmentPropertySourceTest.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.util;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class EnvironmentPropertySourceTest {
+
+ private final PropertySource source = new EnvironmentPropertySource();
+
+ public static Object[][] data() {
+ return new Object[][]{
+ {"LOG4J_CONFIGURATION_FILE", Arrays.asList("configuration", "file")},
+ {"LOG4J_FOO_BAR_PROPERTY", Arrays.asList("foo", "bar", "property")},
+ {"LOG4J_EXACT", Collections.singletonList("EXACT")},
+ {"LOG4J_TEST_PROPERTY_NAME", PropertySource.Util.tokenize("Log4jTestPropertyName")},
+ {null, Collections.emptyList()}
+ };
+ }
+
+ @ParameterizedTest
+ @MethodSource("data")
+ public void testNormalFormFollowsEnvironmentVariableConventions(CharSequence expected, List extends CharSequence> tokens) {
+ assertEquals(expected, source.getNormalForm(tokens));
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/LambdaUtilTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/LambdaUtilTest.java
new file mode 100644
index 00000000000..a4671cab3fd
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/LambdaUtilTest.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.util;
+
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Tests the LambdaUtil class.
+ */
+public class LambdaUtilTest {
+
+ @Test
+ public void testGetSupplierResultOfSupplier() {
+ final String expected = "result";
+ final Object actual = LambdaUtil.get((Supplier) () -> expected);
+ assertSame(expected, actual);
+ }
+
+ @Test
+ public void testGetMessageSupplierResultOfSupplier() {
+ final Message expected = new SimpleMessage("hi");
+ final Message actual = LambdaUtil.get(() -> expected);
+ assertSame(expected, actual);
+ }
+
+ @Test
+ public void testGetSupplierReturnsNullIfSupplierNull() {
+ final Object actual = LambdaUtil.get((Supplier>) null);
+ assertNull(actual);
+ }
+
+ @Test
+ public void testGetMessageSupplierReturnsNullIfSupplierNull() {
+ final Object actual = LambdaUtil.get((MessageSupplier) null);
+ assertNull(actual);
+ }
+
+ @Test
+ public void testGetSupplierExceptionIfSupplierThrowsException() {
+ assertThrows(RuntimeException.class, () -> LambdaUtil.get((Supplier) () -> {
+ throw new RuntimeException();
+ }));
+ }
+
+ @Test
+ public void testGetMessageSupplierExceptionIfSupplierThrowsException() {
+ assertThrows(RuntimeException.class, () -> LambdaUtil.get(() -> {
+ throw new RuntimeException();
+ }));
+ }
+
+ @Test
+ public void testGetAllReturnsResultOfSuppliers() {
+ final String expected1 = "result1";
+ final Supplier function1 = () -> expected1;
+ final String expected2 = "result2";
+ final Supplier function2 = () -> expected2;
+
+ final Supplier>[] functions = { function1, function2 };
+ final Object[] actual = LambdaUtil.getAll(functions);
+ assertEquals(actual.length, functions.length);
+ assertSame(expected1, actual[0]);
+ assertSame(expected2, actual[1]);
+ }
+
+ @Test
+ public void testGetAllReturnsNullArrayIfSupplierArrayNull() {
+ final Object[] actual = LambdaUtil.getAll((Supplier>[]) null);
+ assertNull(actual);
+ }
+
+ @Test
+ public void testGetAllReturnsNullElementsIfSupplierArrayContainsNulls() {
+ final Supplier>[] functions = new Supplier>[3];
+ final Object[] actual = LambdaUtil.getAll(functions);
+ assertEquals(actual.length, functions.length);
+ for (final Object object : actual) {
+ assertNull(object);
+ }
+ }
+
+ @Test
+ public void testGetAllThrowsExceptionIfAnyOfTheSuppliersThrowsException() {
+ final Supplier function1 = () -> "abc";
+ final Supplier function2 = () -> {
+ throw new RuntimeException();
+ };
+
+ final Supplier>[] functions = { function1, function2 };
+ assertThrows(RuntimeException.class, () -> LambdaUtil.getAll(functions));
+ }
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/LegacyPropertiesCompatibilityTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/LegacyPropertiesCompatibilityTest.java
similarity index 89%
rename from log4j-api/src/test/java/org/apache/logging/log4j/util/LegacyPropertiesCompatibilityTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/util/LegacyPropertiesCompatibilityTest.java
index b979d9ad974..d43150b7cdb 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/util/LegacyPropertiesCompatibilityTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/LegacyPropertiesCompatibilityTest.java
@@ -16,30 +16,19 @@
*/
package org.apache.logging.log4j.util;
-import java.util.List;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
+import java.util.List;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
-@RunWith(Parameterized.class)
public class LegacyPropertiesCompatibilityTest {
- private final CharSequence newName;
- private final CharSequence oldName;
-
- public LegacyPropertiesCompatibilityTest(final CharSequence newName, final CharSequence oldName) {
- this.newName = newName;
- this.oldName = oldName;
- }
-
- @Parameterized.Parameters(name = "New: {0}; Old: {1}")
public static Object[][] data() {
return new Object[][]{
{"log4j2.configurationFile", "log4j.configurationFile"},
- {"log4j2.mergeFactory", "log4j.mergeFactory"},
+ {"log4j2.mergeStrategy", "log4j.mergeStrategy"},
{"log4j2.contextSelector", "Log4jContextSelector"},
{"log4j2.logEventFactory", "Log4jLogEventFactory"},
{"log4j2.configurationFactory", "log4j.configurationFactory"},
@@ -94,10 +83,11 @@ public static Object[][] data() {
};
}
- @Test
- public void compareNewWithOldName() throws Exception {
+ @ParameterizedTest
+ @MethodSource("data")
+ public void compareNewWithOldName(final String newName, final String oldName) {
final List newTokens = PropertySource.Util.tokenize(newName);
final List oldTokens = PropertySource.Util.tokenize(oldName);
assertEquals(oldTokens, newTokens);
}
-}
\ No newline at end of file
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/LoaderUtilTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/LoaderUtilTest.java
new file mode 100644
index 00000000000..13700572d3a
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/LoaderUtilTest.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.util;
+
+import java.net.URL;
+import java.util.Collections;
+import java.util.Enumeration;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junitpioneer.jupiter.ReadsSystemProperty;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@ReadsSystemProperty
+public class LoaderUtilTest {
+ @BeforeEach
+ @AfterEach
+ public void reset() {
+ LoaderUtil.forceTcclOnly = null;
+ }
+
+ @Test
+ public void systemClassLoader() {
+ final Thread thread = Thread.currentThread();
+ final ClassLoader tccl = thread.getContextClassLoader();
+
+ LoaderUtil.forceTcclOnly = true;
+ final ClassLoader loader = new ClassLoader(tccl) {
+ @Override
+ public Enumeration getResources(final String name) {
+ return Collections.emptyEnumeration();
+ }
+ };
+ thread.setContextClassLoader(loader);
+ try {
+ assertEquals(0, LoaderUtil.findUrlResources("Log4j-charsets.properties", false).size());
+
+ LoaderUtil.forceTcclOnly = false;
+ assertEquals(1, LoaderUtil.findUrlResources("Log4j-charsets.properties", false).size());
+ } finally {
+ thread.setContextClassLoader(tccl);
+ }
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/Log4jCharsetsPropertiesTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/Log4jCharsetsPropertiesTest.java
new file mode 100644
index 00000000000..7e2b48ccfe6
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/Log4jCharsetsPropertiesTest.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.util;
+
+import java.nio.charset.Charset;
+import java.util.Enumeration;
+import java.util.ResourceBundle;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class Log4jCharsetsPropertiesTest {
+
+ /**
+ * Tests that we can load all mappings.
+ */
+ @Test
+ public void testLoadAll() {
+ ResourceBundle resourceBundle = PropertiesUtil.getCharsetsResourceBundle();
+ Enumeration keys = resourceBundle.getKeys();
+ while (keys.hasMoreElements()) {
+ String key = keys.nextElement();
+ assertFalse(
+ Charset.isSupported(key), String.format("The Charset %s is available and should not be mapped", key));
+ String value = resourceBundle.getString(key);
+ assertTrue(
+ Charset.isSupported(value),
+ String.format("The Charset %s is not available and is mapped from %s", value, key));
+ }
+ }
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/PropertiesPropertySourceTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/PropertiesPropertySourceTest.java
similarity index 75%
rename from log4j-api/src/test/java/org/apache/logging/log4j/util/PropertiesPropertySourceTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/util/PropertiesPropertySourceTest.java
index c1c97efaa8b..5e741d20d5c 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/util/PropertiesPropertySourceTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/PropertiesPropertySourceTest.java
@@ -16,41 +16,33 @@
*/
package org.apache.logging.log4j.util;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
import static org.junit.Assert.assertEquals;
-@RunWith(Parameterized.class)
public class PropertiesPropertySourceTest {
private final PropertySource source = new PropertiesPropertySource(new Properties());
- private final CharSequence expected;
- private final List extends CharSequence> tokens;
-
- public PropertiesPropertySourceTest(final String expected, final List tokens) {
- this.expected = expected;
- this.tokens = tokens;
- }
- @Parameterized.Parameters(name = "{0}")
public static Object[][] data() {
return new Object[][]{
{"log4j2.configurationFile", Arrays.asList("configuration", "file")},
{"log4j2.fooBarProperty", Arrays.asList("foo", "bar", "property")},
{"log4j2.EXACT", Collections.singletonList("EXACT")},
{"log4j2.testPropertyName", PropertySource.Util.tokenize("Log4jTestPropertyName")},
+ {null, Collections.emptyList()}
};
}
- @Test
- public void testNormalFormFollowsCamelCaseConventions() throws Exception {
+ @ParameterizedTest
+ @MethodSource("data")
+ public void testNormalFormFollowsCamelCaseConventions(final String expected, final List tokens) {
assertEquals(expected, source.getNormalForm(tokens));
}
-}
\ No newline at end of file
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/PropertiesUtilOrderTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/PropertiesUtilOrderTest.java
new file mode 100644
index 00000000000..1dc07c7c849
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/PropertiesUtilOrderTest.java
@@ -0,0 +1,203 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.util;
+
+import java.io.InputStream;
+import java.util.Properties;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.api.parallel.Resources;
+
+import uk.org.webcompere.systemstubs.environment.EnvironmentVariables;
+import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension;
+import uk.org.webcompere.systemstubs.properties.SystemProperties;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@ExtendWith(SystemStubsExtension.class)
+@ResourceLock(value = Resources.SYSTEM_PROPERTIES)
+public class PropertiesUtilOrderTest {
+
+ public static class NonEnumerablePropertySource implements PropertySource {
+
+ private final Properties props;
+
+ public NonEnumerablePropertySource(final Properties props) {
+ this.props = props;
+ }
+
+ @Override
+ public int getPriority() {
+ return Integer.MIN_VALUE;
+ }
+
+ @Override
+ public CharSequence getNormalForm(Iterable extends CharSequence> tokens) {
+ final CharSequence camelCase = PropertySource.Util.joinAsCamelCase(tokens);
+ return camelCase.length() > 0 ? "log4j2." + camelCase : null;
+ }
+
+ @Override
+ public String getProperty(String key) {
+ return props.getProperty(key);
+ }
+
+ @Override
+ public boolean containsProperty(String key) {
+ return getProperty(key) != null;
+ }
+
+ }
+
+ public static class NullPropertySource implements PropertySource {
+
+ @Override
+ public int getPriority() {
+ return Integer.MIN_VALUE;
+ }
+
+ }
+
+ private final Properties properties = new Properties();
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ try (final InputStream is = ClassLoader.getSystemResourceAsStream("PropertiesUtilOrderTest.properties")) {
+ properties.load(is);
+ }
+ }
+
+ @Test
+ public void testNormalizedOverrideLegacy() {
+ final PropertiesUtil util = new PropertiesUtil(properties);
+ final String legacy = "props.legacy";
+ final String normalized = "props.normalized";
+ assertEquals(legacy, properties.getProperty("log4j.legacyProperty"));
+ assertTrue(util.hasProperty("log4j.legacyProperty"));
+ assertEquals(normalized, util.getStringProperty("log4j.legacyProperty"));
+ assertEquals(legacy, properties.getProperty("org.apache.logging.log4j.legacyProperty2"));
+ assertTrue(util.hasProperty("log4j.legacyProperty2"));
+ assertEquals(normalized, util.getStringProperty("org.apache.logging.log4j.legacyProperty2"));
+ assertEquals(legacy, properties.getProperty("Log4jLegacyProperty3"));
+ assertTrue(util.hasProperty("log4j.legacyProperty3"));
+ assertEquals(normalized, util.getStringProperty("Log4jLegacyProperty3"));
+ // non-overridden legacy property
+ assertTrue(util.hasProperty("log4j.nonOverriddenLegacy"));
+ assertEquals(legacy, util.getStringProperty("log4j.nonOverriddenLegacy"));
+ }
+
+ @Test
+ public void testFallsBackToTokenMatching() {
+ final PropertiesUtil util = new PropertiesUtil(properties);
+ for (int i = 1; i <= 4; i++) {
+ final String key = "log4j2.tokenBasedProperty" + i;
+ assertTrue(util.hasProperty(key));
+ assertEquals("props.token", util.getStringProperty(key));
+ }
+ // No fall back (a normalized property is present)
+ assertTrue(util.hasProperty("log4j2.normalizedProperty"));
+ assertEquals("props.normalized", util.getStringProperty("log4j2.normalizedProperty"));
+ }
+
+ @Test
+ public void testOrderOfNormalizedProperties(EnvironmentVariables env, SystemProperties sysProps) {
+ properties.remove("log4j2.normalizedProperty");
+ properties.remove("LOG4J_normalized.property");
+ final PropertiesUtil util = new PropertiesUtil(properties);
+ // Same result for both a legacy property and a normalized property
+ assertFalse(util.hasProperty("Log4jNormalizedProperty"));
+ assertEquals(null, util.getStringProperty("Log4jNormalizedProperty"));
+ assertFalse(util.hasProperty("log4j2.normalizedProperty"));
+ assertEquals(null, util.getStringProperty("log4j2.normalizedProperty"));
+
+ properties.setProperty("log4j2.normalizedProperty", "props.normalized");
+ util.reload();
+ assertTrue(util.hasProperty("Log4jNormalizedProperty"));
+ assertEquals("props.normalized", util.getStringProperty("Log4jNormalizedProperty"));
+ assertTrue(util.hasProperty("log4j2.normalizedProperty"));
+ assertEquals("props.normalized", util.getStringProperty("log4j2.normalizedProperty"));
+
+ env.set("LOG4J_NORMALIZED_PROPERTY", "env");
+ util.reload();
+ assertTrue(util.hasProperty("Log4jNormalizedProperty"));
+ assertEquals("env", util.getStringProperty("Log4jNormalizedProperty"));
+ assertTrue(util.hasProperty("log4j2.normalizedProperty"));
+ assertEquals("env", util.getStringProperty("log4j2.normalizedProperty"));
+
+ sysProps.set("log4j2.normalizedProperty", "sysProps");
+ util.reload();
+ assertTrue(util.hasProperty("Log4jNormalizedProperty"));
+ assertEquals("sysProps", util.getStringProperty("Log4jNormalizedProperty"));
+ assertTrue(util.hasProperty("log4j2.normalizedProperty"));
+ assertEquals("sysProps", util.getStringProperty("log4j2.normalizedProperty"));
+ }
+
+ @Test
+ public void testHighPriorityNonEnumerableSource(SystemProperties sysProps) {
+ // In both datasources
+ assertNotNull(properties.getProperty("log4j2.normalizedProperty"));
+ assertNotNull(properties.getProperty("log4j.onlyLegacy"));
+ sysProps.set("log4j2.normalizedProperty", "sysProps.normalized");
+ sysProps.set("log4j.onlyLegacy", "sysProps.legazy");
+ // Only system properties
+ assertNull(properties.getProperty("log4j2.normalizedPropertySysProps"));
+ assertNull(properties.getProperty("log4j.onlyLegacySysProps"));
+ sysProps.set("log4j2.normalizedPropertySysProps", "sysProps.normalized");
+ sysProps.set("log4j.onlyLegacySysProps", "sysProps.legacy");
+ // Only the non enumerable source
+ assertNotNull(properties.getProperty("log4j2.normalizedPropertyProps"));
+ assertNotNull(properties.getProperty("log4j.onlyLegacyProps"));
+
+ final PropertiesUtil util = new PropertiesUtil(new NonEnumerablePropertySource(properties));
+ assertTrue(util.hasProperty("log4j2.normalizedProperty"));
+ assertEquals("props.normalized", util.getStringProperty("log4j2.normalizedProperty"));
+ assertTrue(util.hasProperty("log4j.onlyLegacy"));
+ assertEquals("props.legacy", util.getStringProperty("log4j.onlyLegacy"));
+ assertTrue(util.hasProperty("log4j2.normalizedPropertySysProps"));
+ assertEquals("sysProps.normalized", util.getStringProperty("log4j2.normalizedPropertySysProps"));
+ assertTrue(util.hasProperty("log4j.onlyLegacySysProps"));
+ assertEquals("sysProps.legacy", util.getStringProperty("log4j.onlyLegacySysProps"));
+ assertTrue(util.hasProperty("log4j2.normalizedPropertyProps"));
+ assertEquals("props.normalized", util.getStringProperty("log4j2.normalizedPropertyProps"));
+ assertTrue(util.hasProperty("log4j.onlyLegacyProps"));
+ assertEquals("props.legacy", util.getStringProperty("log4j.onlyLegacyProps"));
+ }
+
+ /**
+ * Checks the for missing null checks. The {@link NullPropertySource} returns
+ * {@code null} in almost every call.
+ *
+ * @param sysProps
+ */
+ @Test
+ public void testNullChecks(SystemProperties sysProps) {
+ sysProps.set("log4j2.someProperty", "sysProps");
+ sysProps.set("Log4jLegacyProperty", "sysProps");
+ final PropertiesUtil util = new PropertiesUtil(new NullPropertySource());
+ assertTrue(util.hasProperty("log4j2.someProperty"));
+ assertEquals("sysProps", util.getStringProperty("log4j2.someProperty"));
+ assertTrue(util.hasProperty("Log4jLegacyProperty"));
+ assertEquals("sysProps", util.getStringProperty("Log4jLegacyProperty"));
+ assertTrue(util.hasProperty("log4j.legacyProperty"));
+ assertEquals("sysProps", util.getStringProperty("log4j.legacyProperty"));
+ assertFalse(util.hasProperty("log4j2.nonExistentProperty"));
+ assertNull(util.getStringProperty("log4j2.nonExistentProperty"));
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/PropertiesUtilTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/PropertiesUtilTest.java
new file mode 100644
index 00000000000..94ac38d882c
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/PropertiesUtilTest.java
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.util;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+import java.util.Properties;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.api.parallel.Resources;
+import org.junitpioneer.jupiter.ReadsSystemProperty;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.*;
+
+public class PropertiesUtilTest {
+
+ private final Properties properties = new Properties();
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ properties.load(ClassLoader.getSystemResourceAsStream("PropertiesUtilTest.properties"));
+ }
+
+ @Test
+ public void testExtractSubset() {
+ assertHasAllProperties(PropertiesUtil.extractSubset(properties, "a"));
+ assertHasAllProperties(PropertiesUtil.extractSubset(properties, "b."));
+ assertHasAllProperties(PropertiesUtil.extractSubset(properties, "c.1"));
+ assertHasAllProperties(PropertiesUtil.extractSubset(properties, "dd"));
+ assertThat(properties).containsOnly(Map.entry("a", "invalid"));
+ }
+
+ @Test
+ public void testPartitionOnCommonPrefix() {
+ final Map parts = PropertiesUtil.partitionOnCommonPrefixes(properties);
+ assertEquals(4, parts.size());
+ assertHasAllProperties(parts.get("a"));
+ assertHasAllProperties(parts.get("b"));
+ assertHasAllProperties(PropertiesUtil.partitionOnCommonPrefixes(parts.get("c")).get("1"));
+ assertHasAllProperties(parts.get("dd"));
+ }
+
+ private static void assertHasAllProperties(final Properties properties) {
+ assertNotNull(properties);
+ assertEquals("1", properties.getProperty("1"));
+ assertEquals("2", properties.getProperty("2"));
+ assertEquals("3", properties.getProperty("3"));
+ }
+
+
+ @Test
+ public void testGetCharsetProperty() {
+ final Properties p = new Properties();
+ p.setProperty("e.1", StandardCharsets.US_ASCII.name());
+ p.setProperty("e.2", "wrong-charset-name");
+ final PropertiesUtil pu = new PropertiesUtil(p);
+
+ assertEquals(Charset.defaultCharset(), pu.getCharsetProperty("e.0"));
+ assertEquals(StandardCharsets.US_ASCII, pu.getCharsetProperty("e.1"));
+ assertEquals(Charset.defaultCharset(), pu.getCharsetProperty("e.2"));
+ }
+
+ @Test
+ @ResourceLock(value = Resources.SYSTEM_PROPERTIES, mode = ResourceAccessMode.READ)
+ public void testGetMappedProperty_sun_stdout_encoding() {
+ final PropertiesUtil pu = new PropertiesUtil(System.getProperties());
+ Charset expected = System.console() == null ? Charset.defaultCharset() : StandardCharsets.UTF_8;
+ assertEquals(expected, pu.getCharsetProperty("sun.stdout.encoding"));
+ }
+
+ @Test
+ @ResourceLock(value = Resources.SYSTEM_PROPERTIES, mode = ResourceAccessMode.READ)
+ public void testGetMappedProperty_sun_stderr_encoding() {
+ final PropertiesUtil pu = new PropertiesUtil(System.getProperties());
+ Charset expected = System.console() == null ? Charset.defaultCharset() : StandardCharsets.UTF_8;
+ assertEquals(expected, pu.getCharsetProperty("sun.err.encoding"));
+ }
+
+ @Test
+ @ResourceLock(Resources.SYSTEM_PROPERTIES)
+ public void testNonStringSystemProperties() {
+ Object key1 = "1";
+ Object key2 = new Object();
+ System.getProperties().put(key1, new Object());
+ System.getProperties().put(key2, "value-2");
+ try {
+ final PropertiesUtil util = new PropertiesUtil(new Properties());
+ assertNull(util.getStringProperty("1"));
+ } finally {
+ System.getProperties().remove(key1);
+ System.getProperties().remove(key2);
+ }
+ }
+
+ @Test
+ @ResourceLock(value = Resources.SYSTEM_PROPERTIES, mode = ResourceAccessMode.READ)
+ public void testPublish() {
+ final Properties props = new Properties();
+ final PropertiesUtil util = new PropertiesUtil(props);
+ String value = System.getProperty("Application");
+ assertNotNull(value, "System property was not published");
+ assertEquals("Log4j", value);
+ }
+
+ private static final String[][] data = {
+ { null, "org.apache.logging.log4j.level" },
+ { null, "Log4jAnotherProperty" },
+ { null, "log4j2.catalinaBase" },
+ { "ok", "log4j2.configurationFile" },
+ { "ok", "log4j2.defaultStatusLevel" },
+ { "ok", "log4j2.newLevel" },
+ { "ok", "log4j2.asyncLoggerTimeout" },
+ { "ok", "log4j2.asyncLoggerConfigRingBufferSize" },
+ { "ok", "log4j2.disableThreadContext" },
+ { "ok", "log4j2.disableThreadContextStack" },
+ { "ok", "log4j2.disableThreadContextMap" },
+ { "ok", "log4j2.isThreadContextMapInheritable" }
+ };
+
+ /**
+ * LOG4J2-3413: Log4j should only resolve properties that start with a 'log4j'
+ * prefix or similar.
+ */
+ @Test
+ @ResourceLock(value = Resources.SYSTEM_PROPERTIES, mode = ResourceAccessMode.READ)
+ public void testResolvesOnlyLog4jProperties() {
+ final PropertiesUtil util = new PropertiesUtil("Jira3413Test.properties");
+ for (final String[] pair : data) {
+ assertEquals(pair[0], util.getStringProperty(pair[1]));
+ }
+ }
+
+ /**
+ * LOG4J2-3559: the fix for LOG4J2-3413 returns the value of 'log4j2.' for each
+ * property not starting with 'log4j'.
+ */
+ @Test
+ @ReadsSystemProperty
+ public void testLog4jProperty() {
+ final Properties props = new Properties();
+ final String incorrect = "log4j2.";
+ final String correct = "not.starting.with.log4j";
+ props.setProperty(incorrect, incorrect);
+ props.setProperty(correct, correct);
+ final PropertiesUtil util = new PropertiesUtil(props);
+ assertEquals(correct, util.getStringProperty(correct));
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/PropertyFilePropertySourceSecurityManagerIT.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/PropertyFilePropertySourceSecurityManagerIT.java
new file mode 100644
index 00000000000..8d137efa848
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/PropertyFilePropertySourceSecurityManagerIT.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.util;
+
+import java.io.FilePermission;
+import java.nio.file.Paths;
+import java.security.Permission;
+import java.util.PropertyPermission;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledForJreRange;
+import org.junit.jupiter.api.condition.JRE;
+import org.junit.jupiter.api.parallel.ResourceLock;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Test related to LOG4J2-2274.
+ *
+ * Using a security manager can mess up other tests so this is best used from
+ * integration tests (classes that end in "IT" instead of "Test" and
+ * "TestCase".)
+ *
+ *
+ * @see PropertyFilePropertySource
+ * @see SecurityManager
+ * @see System#setSecurityManager(SecurityManager)
+ * @see PropertyPermission
+ */
+@ResourceLock("java.lang.SecurityManager")
+@DisabledForJreRange(min = JRE.JAVA_18) // custom SecurityManager instances throw UnsupportedOperationException
+public class PropertyFilePropertySourceSecurityManagerIT {
+
+ @BeforeAll
+ public static void beforeClass() {
+ assertThat(Paths.get(TEST_FIXTURE_PATH)).exists();
+ }
+
+ private static final String TEST_FIXTURE_PATH = "src/test/resources/PropertiesUtilTest.properties";
+
+ /**
+ * Always throws a SecurityException for any environment variables permission
+ * check.
+ */
+ private static class TestSecurityManager extends SecurityManager {
+
+ @Override
+ public void checkPermission(final Permission permission) {
+ if (permission instanceof FilePermission && permission.getName().endsWith(TEST_FIXTURE_PATH)) {
+ throw new SecurityException();
+ }
+ }
+ }
+
+ /**
+ * Makes sure we do not blow up with exception below due to a security manager
+ * rejecting environment variable access in
+ * {@link SystemPropertiesPropertySource}.
+ *
+ *
+ *
+ */
+ @Test
+ public void test() {
+ var existing = System.getSecurityManager();
+ try {
+ System.setSecurityManager(new TestSecurityManager());
+ final PropertiesUtil propertiesUtil = new PropertiesUtil(TEST_FIXTURE_PATH);
+ assertThat(propertiesUtil.getStringProperty("a.1")).isNull();
+ } finally {
+ System.setSecurityManager(existing);
+ }
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/PropertySourceCamelCaseTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/PropertySourceCamelCaseTest.java
new file mode 100644
index 00000000000..aea6942c325
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/PropertySourceCamelCaseTest.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.util;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class PropertySourceCamelCaseTest {
+
+ public static Object[][] data() {
+ return new Object[][]{
+ {"", Collections.singletonList("")},
+ {"foo", Collections.singletonList("foo")},
+ {"fooBar", Arrays.asList("foo", "bar")},
+ {"oneTwoThree", Arrays.asList("one", "two", "three")},
+ };
+ }
+
+ @ParameterizedTest
+ @MethodSource("data")
+ public void testJoinAsCamelCase(final CharSequence expected, final List tokens) {
+ assertEquals(expected, PropertySource.Util.joinAsCamelCase(tokens));
+ }
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/PropertySourceTokenizerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/PropertySourceTokenizerTest.java
similarity index 80%
rename from log4j-api/src/test/java/org/apache/logging/log4j/util/PropertySourceTokenizerTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/util/PropertySourceTokenizerTest.java
index dd1d49cbbe5..6dbf4d0f409 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/util/PropertySourceTokenizerTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/PropertySourceTokenizerTest.java
@@ -16,28 +16,17 @@
*/
package org.apache.logging.log4j.util;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
-@RunWith(Parameterized.class)
public class PropertySourceTokenizerTest {
- private final CharSequence value;
- private final List expectedTokens;
-
- public PropertySourceTokenizerTest(final CharSequence value, final List expectedTokens) {
- this.value = value;
- this.expectedTokens = expectedTokens;
- }
-
- @Parameterized.Parameters(name = "{0}")
public static Object[][] data() {
return new Object[][]{
{"log4j.simple", Collections.singletonList("simple")},
@@ -55,12 +44,17 @@ public static Object[][] data() {
{"log4j2-dashed-propertyName", Arrays.asList("dashed", "property", "name")},
{"Log4jProperty_with.all-the/separators", Arrays.asList("property", "with", "all", "the", "separators")},
{"org.apache.logging.log4j.config.property", Arrays.asList("config", "property")},
+ // LOG4J2-3413
+ {"level", Collections.emptyList()},
+ {"user.home", Collections.emptyList()},
+ {"CATALINA_BASE", Collections.emptyList()}
};
}
- @Test
- public void testTokenize() throws Exception {
+ @ParameterizedTest
+ @MethodSource("data")
+ public void testTokenize(final CharSequence value, final List expectedTokens) {
List tokens = PropertySource.Util.tokenize(value);
assertEquals(expectedTokens, tokens);
}
-}
\ No newline at end of file
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/ProviderUtilTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/ProviderUtilTest.java
similarity index 84%
rename from log4j-api/src/test/java/org/apache/logging/log4j/util/ProviderUtilTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/util/ProviderUtilTest.java
index ce4236afb10..a9b4cb950d1 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/util/ProviderUtilTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/ProviderUtilTest.java
@@ -21,11 +21,11 @@
import java.net.URLClassLoader;
import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.TestLoggerContext;
import org.apache.logging.log4j.spi.LoggerContext;
-import org.junit.Test;
+import org.apache.logging.log4j.test.TestLoggerContext;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertTrue;
public class ProviderUtilTest {
@@ -37,10 +37,10 @@ public void complexTest() throws Exception {
worker.setContextClassLoader(classLoader);
worker.start();
worker.join();
- assertTrue("Incorrect LoggerContext", worker.context instanceof TestLoggerContext);
+ assertTrue(worker.context instanceof TestLoggerContext, "Incorrect LoggerContext");
}
- private class Worker extends Thread {
+ private static class Worker extends Thread {
LoggerContext context = null;
@Override
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/ServiceLoaderUtilTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/ServiceLoaderUtilTest.java
new file mode 100644
index 00000000000..ab8e9051d0e
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/ServiceLoaderUtilTest.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.util;
+
+import java.lang.invoke.MethodHandles;
+import java.util.Collections;
+import java.util.List;
+import java.util.ServiceConfigurationError;
+import java.util.stream.Collectors;
+
+import org.apache.logging.log4j.test.BetterService;
+import org.apache.logging.log4j.test.Service;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class ServiceLoaderUtilTest {
+
+ @Test
+ public void testServiceResolution() {
+ // Run only if we are a module
+ if (ServiceLoaderUtil.class.getModule().isNamed()) {
+ List services = Collections.emptyList();
+ // Service from test module
+ try {
+ services = ServiceLoaderUtil.loadServices(Service.class, MethodHandles.lookup())
+ .collect(Collectors.toList());
+ } catch (ServiceConfigurationError e) {
+ fail(e);
+ }
+ assertEquals(2, services.size(), "Service services");
+ // BetterService from test module
+ services.clear();
+ try {
+ services = ServiceLoaderUtil.loadServices(BetterService.class, MethodHandles.lookup())
+ .collect(Collectors.toList());
+ } catch (ServiceConfigurationError e) {
+ fail(e);
+ }
+ assertEquals(1, services.size(), "BetterService services");
+ // PropertySource from org.apache.logging.log4j module from this module
+ services.clear();
+ try {
+ services = ServiceLoaderUtil.loadServices(PropertySource.class, MethodHandles.lookup())
+ .collect(Collectors.toList());
+ } catch (ServiceConfigurationError e) {
+ fail(e);
+ }
+ assertEquals(0, services.size(), "PropertySource services");
+ }
+ }
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/SortedArrayStringMapTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/SortedArrayStringMapTest.java
similarity index 79%
rename from log4j-api/src/test/java/org/apache/logging/log4j/util/SortedArrayStringMapTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/util/SortedArrayStringMapTest.java
index 3d9ce3e8ae4..af08dbc0a5b 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/util/SortedArrayStringMapTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/SortedArrayStringMapTest.java
@@ -29,34 +29,46 @@
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.Charset;
+import java.util.Arrays;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Map;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
/**
* Tests the SortedArrayStringMap class.
*/
public class SortedArrayStringMapTest {
- @Test(expected = IllegalArgumentException.class)
- public void testConstructorDisallowsNegativeCapacity() throws Exception {
- new SortedArrayStringMap(-1);
+ @Test
+ public void testConstructorDisallowsNegativeCapacity() {
+ assertThrows(IllegalArgumentException.class, () -> new SortedArrayStringMap(-1));
}
- public void testConstructorAllowsZeroCapacity() throws Exception {
+ @Test
+ public void testConstructorAllowsZeroCapacity() {
SortedArrayStringMap sortedArrayStringMap = new SortedArrayStringMap(0);
assertEquals(0, sortedArrayStringMap.size());
}
@Test
- public void testConstructorIgnoresNull() throws Exception {
+ public void testConstructorIgnoresNull() {
assertEquals(0, new SortedArrayStringMap((SortedArrayStringMap) null).size());
}
+ @Test
+ public void testConstructorNonStringKeys() {
+ Map map = new HashMap<>(1);
+ map.put(Long.MAX_VALUE, 1);
+ map.put(null, null);
+ SortedArrayStringMap sMap = new SortedArrayStringMap((Map) map);
+ assertEquals(1, (int) sMap.getValue(Long.toString(Long.MAX_VALUE)));
+ assertEquals((Integer) null, sMap.getValue(null));
+ }
+
@Test
public void testToString() {
final SortedArrayStringMap original = new SortedArrayStringMap();
@@ -70,7 +82,7 @@ public void testToString() {
public void testSerialization() throws Exception {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
+ original.putValue("B", null); // null may be treated differently
original.putValue("3", "3value");
final byte[] binary = serialize(original);
@@ -78,6 +90,14 @@ public void testSerialization() throws Exception {
assertEquals(original, copy);
}
+ @Test
+ public void testSerializationOfEmptyMap() throws Exception {
+ final SortedArrayStringMap original = new SortedArrayStringMap();
+ final byte[] binary = serialize(original);
+ final SortedArrayStringMap copy = deserialize(binary);
+ assertEquals(original, copy);
+ }
+
@Test
public void testSerializationOfNonSerializableValue() throws Exception {
final SortedArrayStringMap original = new SortedArrayStringMap();
@@ -165,16 +185,16 @@ private byte[] serialize(final SortedArrayStringMap data) throws IOException {
return arr.toByteArray();
}
+ @SuppressWarnings("BanSerializableRead")
private SortedArrayStringMap deserialize(final byte[] binary) throws IOException, ClassNotFoundException {
final ByteArrayInputStream inArr = new ByteArrayInputStream(binary);
try (final ObjectInputStream in = new FilteredObjectInputStream(inArr)) {
- final SortedArrayStringMap result = (SortedArrayStringMap) in.readObject();
- return result;
+ return (SortedArrayStringMap) in.readObject();
}
}
@Test
- public void testPutAll() throws Exception {
+ public void testPutAll() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "avalue");
original.putValue("B", "Bvalue");
@@ -195,7 +215,7 @@ public void testPutAll() throws Exception {
}
@Test
- public void testPutAll_overwritesSameKeys2() throws Exception {
+ public void testPutAll_overwritesSameKeys2() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "aORIG");
original.putValue("b", "bORIG");
@@ -210,7 +230,7 @@ public void testPutAll_overwritesSameKeys2() throws Exception {
other.putValue("c", "cc");
original.putAll(other);
- assertEquals("size after put other", 7, original.size());
+ assertEquals(7, original.size(), "size after put other");
assertEquals("aa", original.getValue("a"));
assertEquals("bORIG", original.getValue("b"));
assertEquals("cc", original.getValue("c"));
@@ -221,7 +241,7 @@ public void testPutAll_overwritesSameKeys2() throws Exception {
}
@Test
- public void testPutAll_nullKeyInLargeOriginal() throws Exception {
+ public void testPutAll_nullKeyInLargeOriginal() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue(null, "nullORIG");
original.putValue("a", "aORIG");
@@ -235,7 +255,7 @@ public void testPutAll_nullKeyInLargeOriginal() throws Exception {
other.putValue("a", "aa");
original.putAll(other);
- assertEquals("size after put other", 7, original.size());
+ assertEquals(7, original.size(), "size after put other");
assertEquals("aa", original.getValue("a"));
assertEquals("bORIG", original.getValue("b"));
assertEquals("cORIG", original.getValue("c"));
@@ -246,7 +266,7 @@ public void testPutAll_nullKeyInLargeOriginal() throws Exception {
}
@Test
- public void testPutAll_nullKeyInSmallOriginal() throws Exception {
+ public void testPutAll_nullKeyInSmallOriginal() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue(null, "nullORIG");
original.putValue("a", "aORIG");
@@ -259,7 +279,7 @@ public void testPutAll_nullKeyInSmallOriginal() throws Exception {
other.putValue("a", "aa");
original.putAll(other);
- assertEquals("size after put other", 6, original.size());
+ assertEquals(6, original.size(), "size after put other");
assertEquals("aa", original.getValue("a"));
assertEquals("bORIG", original.getValue("b"));
assertEquals("11", original.getValue("1"));
@@ -269,7 +289,7 @@ public void testPutAll_nullKeyInSmallOriginal() throws Exception {
}
@Test
- public void testPutAll_nullKeyInSmallAdditional() throws Exception {
+ public void testPutAll_nullKeyInSmallAdditional() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "aORIG");
original.putValue("b", "bORIG");
@@ -283,7 +303,7 @@ public void testPutAll_nullKeyInSmallAdditional() throws Exception {
other.putValue("a", "aa");
original.putAll(other);
- assertEquals("size after put other", 7, original.size());
+ assertEquals(7, original.size(), "size after put other");
assertEquals("aa", original.getValue("a"));
assertEquals("bORIG", original.getValue("b"));
assertEquals("cORIG", original.getValue("c"));
@@ -294,7 +314,7 @@ public void testPutAll_nullKeyInSmallAdditional() throws Exception {
}
@Test
- public void testPutAll_nullKeyInLargeAdditional() throws Exception {
+ public void testPutAll_nullKeyInLargeAdditional() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "aORIG");
original.putValue("b", "bORIG");
@@ -307,7 +327,7 @@ public void testPutAll_nullKeyInLargeAdditional() throws Exception {
other.putValue("a", "aa");
original.putAll(other);
- assertEquals("size after put other", 6, original.size());
+ assertEquals(6, original.size(), "size after put other");
assertEquals("aa", original.getValue("a"));
assertEquals("bORIG", original.getValue("b"));
assertEquals("11", original.getValue("1"));
@@ -317,7 +337,7 @@ public void testPutAll_nullKeyInLargeAdditional() throws Exception {
}
@Test
- public void testPutAll_nullKeyInBoth_LargeOriginal() throws Exception {
+ public void testPutAll_nullKeyInBoth_LargeOriginal() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue(null, "nullORIG");
original.putValue("a", "aORIG");
@@ -332,7 +352,7 @@ public void testPutAll_nullKeyInBoth_LargeOriginal() throws Exception {
other.putValue("a", "aa");
original.putAll(other);
- assertEquals("size after put other", 7, original.size());
+ assertEquals(7, original.size(), "size after put other");
assertEquals("aa", original.getValue("a"));
assertEquals("bORIG", original.getValue("b"));
assertEquals("cORIG", original.getValue("c"));
@@ -343,7 +363,7 @@ public void testPutAll_nullKeyInBoth_LargeOriginal() throws Exception {
}
@Test
- public void testPutAll_nullKeyInBoth_SmallOriginal() throws Exception {
+ public void testPutAll_nullKeyInBoth_SmallOriginal() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue(null, "nullORIG");
original.putValue("a", "aORIG");
@@ -357,7 +377,7 @@ public void testPutAll_nullKeyInBoth_SmallOriginal() throws Exception {
other.putValue("a", "aa");
original.putAll(other);
- assertEquals("size after put other", 6, original.size());
+ assertEquals(6, original.size(), "size after put other");
assertEquals("aa", original.getValue("a"));
assertEquals("bORIG", original.getValue("b"));
assertEquals("11", original.getValue("1"));
@@ -367,7 +387,7 @@ public void testPutAll_nullKeyInBoth_SmallOriginal() throws Exception {
}
@Test
- public void testPutAll_overwritesSameKeys1() throws Exception {
+ public void testPutAll_overwritesSameKeys1() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "aORIG");
original.putValue("b", "bORIG");
@@ -380,7 +400,7 @@ public void testPutAll_overwritesSameKeys1() throws Exception {
other.putValue("c", "cc");
original.putAll(other);
- assertEquals("size after put other", 5, original.size());
+ assertEquals(5, original.size(), "size after put other");
assertEquals("aa", original.getValue("a"));
assertEquals("bORIG", original.getValue("b"));
assertEquals("cc", original.getValue("c"));
@@ -391,12 +411,17 @@ public void testPutAll_overwritesSameKeys1() throws Exception {
@Test
public void testEquals() {
final SortedArrayStringMap original = new SortedArrayStringMap();
+ final SortedArrayStringMap other = new SortedArrayStringMap();
+
+ assertEquals(other, original, "Empty maps are equal");
+ assertEquals(other.hashCode(), original.hashCode(), "Empty maps have equal hashcode");
+ assertNotEquals(original, "Object other than SortedArrayStringMap");
+
original.putValue("a", "avalue");
original.putValue("B", "Bvalue");
original.putValue("3", "3value");
assertEquals(original, original); // equal to itself
- final SortedArrayStringMap other = new SortedArrayStringMap();
other.putValue("a", "avalue");
assertNotEquals(original, other);
@@ -405,19 +430,27 @@ public void testEquals() {
other.putValue("3", "3value");
assertEquals(original, other);
+ assertEquals(original.hashCode(), other.hashCode());
other.putValue("3", "otherValue");
assertNotEquals(original, other);
other.putValue("3", null);
assertNotEquals(original, other);
+ assertNotEquals(original.hashCode(), other.hashCode());
other.putValue("3", "3value");
assertEquals(original, other);
+ assertEquals(other, original); // symmetry
+
+ original.putValue("key not in other", "4value");
+ other.putValue("key not in original", "4value");
+ assertNotEquals(original, other);
+ assertNotEquals(other, original); // symmetry
}
@Test
- public void testToMap() throws Exception {
+ public void testToMap() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "avalue");
original.putValue("B", "Bvalue");
@@ -430,11 +463,7 @@ public void testToMap() throws Exception {
assertEquals(expected, original.toMap());
- try {
- original.toMap().put("abc", "xyz");
- } catch (final UnsupportedOperationException ex) {
- fail("Expected map to be mutable, but " + ex);
- }
+ assertDoesNotThrow(() -> original.toMap().put("abc", "xyz"), "Expected map to be mutable");
}
@Test
@@ -443,11 +472,11 @@ public void testPutAll_KeepsExistingValues() {
original.putValue("a", "aaa");
original.putValue("b", "bbb");
original.putValue("c", "ccc");
- assertEquals("size", 3, original.size());
+ assertEquals(3, original.size(), "size");
// add empty context data
original.putAll(new SortedArrayStringMap());
- assertEquals("size after put empty", 3, original.size());
+ assertEquals(3, original.size(), "size after put empty");
assertEquals("aaa", original.getValue("a"));
assertEquals("bbb", original.getValue("b"));
assertEquals("ccc", original.getValue("c"));
@@ -458,7 +487,7 @@ public void testPutAll_KeepsExistingValues() {
other.putValue("3", "333");
original.putAll(other);
- assertEquals("size after put other", 6, original.size());
+ assertEquals(6, original.size(), "size after put other");
assertEquals("aaa", original.getValue("a"));
assertEquals("bbb", original.getValue("b"));
assertEquals("ccc", original.getValue("c"));
@@ -474,11 +503,11 @@ public void testPutAll_sizePowerOfTwo() {
original.putValue("b", "bbb");
original.putValue("c", "ccc");
original.putValue("d", "ddd");
- assertEquals("size", 4, original.size());
+ assertEquals(4, original.size(), "size");
// add empty context data
original.putAll(new SortedArrayStringMap());
- assertEquals("size after put empty", 4, original.size());
+ assertEquals(4, original.size(), "size after put empty");
assertEquals("aaa", original.getValue("a"));
assertEquals("bbb", original.getValue("b"));
assertEquals("ccc", original.getValue("c"));
@@ -491,7 +520,7 @@ public void testPutAll_sizePowerOfTwo() {
other.putValue("4", "444");
original.putAll(other);
- assertEquals("size after put other", 8, original.size());
+ assertEquals(8, original.size(), "size after put other");
assertEquals("aaa", original.getValue("a"));
assertEquals("bbb", original.getValue("b"));
assertEquals("ccc", original.getValue("c"));
@@ -510,7 +539,7 @@ public void testPutAll_largeAddition() {
original.putValue("b", "bbb");
original.putValue("c", "ccc");
original.putValue("d", "ddd");
- assertEquals("size", 5, original.size());
+ assertEquals(5, original.size(), "size");
final SortedArrayStringMap other = new SortedArrayStringMap();
for (int i = 0 ; i < 500; i++) {
@@ -519,7 +548,7 @@ public void testPutAll_largeAddition() {
other.putValue(null, "otherVal");
original.putAll(other);
- assertEquals("size after put other", 505, original.size());
+ assertEquals(505, original.size(), "size after put other");
assertEquals("otherVal", original.getValue(null));
assertEquals("aaa", original.getValue("a"));
assertEquals("bbb", original.getValue("b"));
@@ -536,110 +565,70 @@ public void testPutAllSelfDoesNotModify() {
original.putValue("a", "aaa");
original.putValue("b", "bbb");
original.putValue("c", "ccc");
- assertEquals("size", 3, original.size());
+ assertEquals(3, original.size(), "size");
// putAll with self
original.putAll(original);
- assertEquals("size after put empty", 3, original.size());
+ assertEquals(3, original.size(), "size after put empty");
assertEquals("aaa", original.getValue("a"));
assertEquals("bbb", original.getValue("b"));
assertEquals("ccc", original.getValue("c"));
}
- @Test(expected = ConcurrentModificationException.class)
+ @Test
public void testConcurrentModificationBiConsumerPut() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "aaa");
- original.forEach(new BiConsumer() {
- @Override
- public void accept(final String s, final Object o) {
- original.putValue("c", "other");
- }
- });
+ assertThrows(ConcurrentModificationException.class, () -> original.forEach((s, o) -> original.putValue("c", "other")));
}
- @Test(expected = ConcurrentModificationException.class)
+ @Test
public void testConcurrentModificationBiConsumerPutValue() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "aaa");
- original.forEach(new BiConsumer() {
- @Override
- public void accept(final String s, final Object o) {
- original.putValue("c", "other");
- }
- });
+ assertThrows(ConcurrentModificationException.class, () -> original.forEach((s, o) -> original.putValue("c", "other")));
}
- @Test(expected = ConcurrentModificationException.class)
+ @Test
public void testConcurrentModificationBiConsumerRemove() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "aaa");
- original.forEach(new BiConsumer() {
- @Override
- public void accept(final String s, final Object o) {
- original.remove("a");
- }
- });
+ assertThrows(ConcurrentModificationException.class, () -> original.forEach((s, o) -> original.remove("a")));
}
- @Test(expected = ConcurrentModificationException.class)
+ @Test
public void testConcurrentModificationBiConsumerClear() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "aaa");
- original.forEach(new BiConsumer() {
- @Override
- public void accept(final String s, final Object o) {
- original.clear();
- }
- });
+ assertThrows(ConcurrentModificationException.class, () -> original.forEach((s, o) -> original.clear()));
}
- @Test(expected = ConcurrentModificationException.class)
+ @Test
public void testConcurrentModificationTriConsumerPut() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "aaa");
- original.forEach(new TriConsumer() {
- @Override
- public void accept(final String s, final Object o, final Object o2) {
- original.putValue("c", "other");
- }
- }, null);
+ assertThrows(ConcurrentModificationException.class, () -> original.forEach((s, o, o2) -> original.putValue("c", "other"), null));
}
- @Test(expected = ConcurrentModificationException.class)
+ @Test
public void testConcurrentModificationTriConsumerPutValue() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "aaa");
- original.forEach(new TriConsumer() {
- @Override
- public void accept(final String s, final Object o, final Object o2) {
- original.putValue("c", "other");
- }
- }, null);
+ assertThrows(ConcurrentModificationException.class, () -> original.forEach((s, o, o2) -> original.putValue("c", "other"), null));
}
- @Test(expected = ConcurrentModificationException.class)
+ @Test
public void testConcurrentModificationTriConsumerRemove() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "aaa");
- original.forEach(new TriConsumer() {
- @Override
- public void accept(final String s, final Object o, final Object o2) {
- original.remove("a");
- }
- }, null);
+ assertThrows(ConcurrentModificationException.class, () -> original.forEach((s, o, o2) -> original.remove("a"), null));
}
- @Test(expected = ConcurrentModificationException.class)
+ @Test
public void testConcurrentModificationTriConsumerClear() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "aaa");
- original.forEach(new TriConsumer() {
- @Override
- public void accept(final String s, final Object o, final Object o2) {
- original.clear();
- }
- }, null);
+ assertThrows(ConcurrentModificationException.class, () -> original.forEach((s, o, o2) -> original.clear(), null));
}
@Test
@@ -650,24 +639,24 @@ public void testInitiallyNotFrozen() {
@Test
public void testIsFrozenAfterCallingFreeze() {
final SortedArrayStringMap original = new SortedArrayStringMap();
- assertFalse("before freeze", original.isFrozen());
+ assertFalse(original.isFrozen(), "before freeze");
original.freeze();
- assertTrue("after freeze", original.isFrozen());
+ assertTrue(original.isFrozen(), "after freeze");
}
- @Test(expected = UnsupportedOperationException.class)
+ @Test
public void testFreezeProhibitsPutValue() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.freeze();
- original.putValue("a", "aaa");
+ assertThrows(UnsupportedOperationException.class, () -> original.putValue("a", "aaa"));
}
- @Test(expected = UnsupportedOperationException.class)
+ @Test
public void testFreezeProhibitsRemove() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("b", "bbb");
original.freeze();
- original.remove("b"); // existing key: modifies the collection
+ assertThrows(UnsupportedOperationException.class, () -> original.remove("b")); // existing key: modifies the collection
}
@Test
@@ -675,33 +664,33 @@ public void testFreezeAllowsRemoveOfNonExistingKey() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("b", "bbb");
original.freeze();
- original.remove("a"); // no actual modification
+ assertDoesNotThrow(() -> original.remove("a"));
}
@Test
public void testFreezeAllowsRemoveIfEmpty() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.freeze();
- original.remove("a"); // no exception
+ assertDoesNotThrow(() -> original.remove("a"));
}
- @Test(expected = UnsupportedOperationException.class)
+ @Test
public void testFreezeProhibitsClear() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "aaa");
original.freeze();
- original.clear();
+ assertThrows(UnsupportedOperationException.class, original::clear);
}
@Test
public void testFreezeAllowsClearIfEmpty() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.freeze();
- original.clear();
+ assertDoesNotThrow(original::clear);
}
@Test
- public void testPutInsertsInAlphabeticOrder() throws Exception {
+ public void testPutInsertsInAlphabeticOrder() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "avalue");
original.putValue("B", "Bvalue");
@@ -726,7 +715,7 @@ public void testPutInsertsInAlphabeticOrder() throws Exception {
}
@Test
- public void testPutValueInsertsInAlphabeticOrder() throws Exception {
+ public void testPutValueInsertsInAlphabeticOrder() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "avalue");
original.putValue("B", "Bvalue");
@@ -794,27 +783,27 @@ public void testNullKeysCopiedToAsMap() {
expected.put("3", "3value");
expected.put("c", "cvalue");
expected.put("d", "dvalue");
- assertEquals("initial", expected, original.toMap());
+ assertEquals(expected, original.toMap(), "initial");
original.putValue(null, "nullvalue");
expected.put(null, "nullvalue");
assertEquals(6, original.size());
- assertEquals("with null key", expected, original.toMap());
+ assertEquals(expected, original.toMap(), "with null key");
original.putValue(null, "otherNullvalue");
expected.put(null, "otherNullvalue");
assertEquals(6, original.size());
- assertEquals("with null key value2", expected, original.toMap());
+ assertEquals(expected, original.toMap(), "with null key value2");
original.putValue(null, "nullvalue");
expected.put(null, "nullvalue");
assertEquals(6, original.size());
- assertEquals("with null key value1 again", expected, original.toMap());
+ assertEquals(expected, original.toMap(), "with null key value1 again");
original.putValue(null, "abc");
expected.put(null, "abc");
assertEquals(6, original.size());
- assertEquals("with null key value3", expected, original.toMap());
+ assertEquals(expected, original.toMap(), "with null key value3");
}
@Test
@@ -826,11 +815,11 @@ public void testRemove() {
original.remove("a");
assertEquals(0, original.size());
- assertNull("no a val", original.getValue("a"));
+ assertNull(original.getValue("a"), "no a val");
original.remove("B");
assertEquals(0, original.size());
- assertNull("no B val", original.getValue("B"));
+ assertNull(original.getValue("B"), "no B val");
}
@Test
@@ -850,13 +839,11 @@ public void testRemoveNullsOutRemovedSlot() throws Exception {
final Field f = SortedArrayStringMap.class.getDeclaredField("values");
f.setAccessible(true);
final Object[] values = (Object[]) f.get(original);
- for (int i = 0; i < values.length; i++) {
- assertNull(values[i]);
- }
+ assertAll(Arrays.stream(values).map(value -> () -> assertNull(value)));
}
@Test
- public void testRemoveWhenFull() throws Exception {
+ public void testRemoveWhenFull() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "avalue");
original.putValue("b", "bvalue");
@@ -874,15 +861,15 @@ public void testNullValuesArePreserved() {
original.putValue("a", null);
assertEquals(1, original.size());
- assertNull("no a val", original.getValue("a"));
+ assertNull(original.getValue("a"), "no a val");
original.putValue("B", null);
assertEquals(2, original.size());
- assertNull("no B val", original.getValue("B"));
+ assertNull(original.getValue("B"), "no B val");
}
@Test
- public void testGet() throws Exception {
+ public void testGet() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "avalue");
original.putValue("B", "Bvalue");
@@ -900,7 +887,7 @@ public void testGet() throws Exception {
}
@Test
- public void testGetValue_GetValueAt() throws Exception {
+ public void testGetValue_GetValueAt() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "avalue");
original.putValue("B", "Bvalue");
@@ -941,13 +928,11 @@ public void testClear() throws Exception {
final Field f = SortedArrayStringMap.class.getDeclaredField("values");
f.setAccessible(true);
final Object[] values = (Object[]) f.get(original);
- for (int i = 0; i < values.length; i++) {
- assertNull(values[i]);
- }
+ assertAll(Arrays.stream(values).map(value -> () -> assertNull(value)));
}
@Test
- public void testIndexOfKey() throws Exception {
+ public void testIndexOfKey() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "avalue");
assertEquals(0, original.indexOfKey("a"));
@@ -984,40 +969,40 @@ public void testIndexOfKey() throws Exception {
}
@Test
- public void testContainsKey() throws Exception {
+ public void testContainsKey() {
final SortedArrayStringMap original = new SortedArrayStringMap();
- assertFalse("a", original.containsKey("a"));
- assertFalse("B", original.containsKey("B"));
- assertFalse("3", original.containsKey("3"));
- assertFalse("A", original.containsKey("A"));
+ assertFalse(original.containsKey("a"), "a");
+ assertFalse(original.containsKey("B"), "B");
+ assertFalse(original.containsKey("3"), "3");
+ assertFalse(original.containsKey("A"), "A");
original.putValue("a", "avalue");
- assertTrue("a", original.containsKey("a"));
- assertFalse("B", original.containsKey("B"));
- assertFalse("3", original.containsKey("3"));
- assertFalse("A", original.containsKey("A"));
+ assertTrue(original.containsKey("a"), "a");
+ assertFalse(original.containsKey("B"), "B");
+ assertFalse(original.containsKey("3"), "3");
+ assertFalse(original.containsKey("A"), "A");
original.putValue("B", "Bvalue");
- assertTrue("a", original.containsKey("a"));
- assertTrue("B", original.containsKey("B"));
- assertFalse("3", original.containsKey("3"));
- assertFalse("A", original.containsKey("A"));
+ assertTrue(original.containsKey("a"), "a");
+ assertTrue(original.containsKey("B"), "B");
+ assertFalse(original.containsKey("3"), "3");
+ assertFalse(original.containsKey("A"), "A");
original.putValue("3", "3value");
- assertTrue("a", original.containsKey("a"));
- assertTrue("B", original.containsKey("B"));
- assertTrue("3", original.containsKey("3"));
- assertFalse("A", original.containsKey("A"));
+ assertTrue(original.containsKey("a"), "a");
+ assertTrue(original.containsKey("B"), "B");
+ assertTrue(original.containsKey("3"), "3");
+ assertFalse(original.containsKey("A"), "A");
original.putValue("A", "AAA");
- assertTrue("a", original.containsKey("a"));
- assertTrue("B", original.containsKey("B"));
- assertTrue("3", original.containsKey("3"));
- assertTrue("A", original.containsKey("A"));
+ assertTrue(original.containsKey("a"), "a");
+ assertTrue(original.containsKey("B"), "B");
+ assertTrue(original.containsKey("3"), "3");
+ assertTrue(original.containsKey("A"), "A");
}
@Test
- public void testGetValueAt() throws Exception {
+ public void testGetValueAt() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "avalue");
assertEquals("a", original.getKeyAt(0));
@@ -1039,38 +1024,38 @@ public void testGetValueAt() throws Exception {
}
@Test
- public void testSizeAndIsEmpty() throws Exception {
+ public void testSizeAndIsEmpty() {
final SortedArrayStringMap original = new SortedArrayStringMap();
assertEquals(0, original.size());
- assertTrue("initial", original.isEmpty());
+ assertTrue(original.isEmpty(), "initial");
original.putValue("a", "avalue");
assertEquals(1, original.size());
- assertFalse("size=" + original.size(), original.isEmpty());
+ assertFalse(original.isEmpty(), "size=" + original.size());
original.putValue("B", "Bvalue");
assertEquals(2, original.size());
- assertFalse("size=" + original.size(), original.isEmpty());
+ assertFalse(original.isEmpty(), "size=" + original.size());
original.putValue("3", "3value");
assertEquals(3, original.size());
- assertFalse("size=" + original.size(), original.isEmpty());
+ assertFalse(original.isEmpty(), "size=" + original.size());
original.remove("B");
assertEquals(2, original.size());
- assertFalse("size=" + original.size(), original.isEmpty());
+ assertFalse(original.isEmpty(), "size=" + original.size());
original.remove("3");
assertEquals(1, original.size());
- assertFalse("size=" + original.size(), original.isEmpty());
+ assertFalse(original.isEmpty(), "size=" + original.size());
original.remove("a");
assertEquals(0, original.size());
- assertTrue("size=" + original.size(), original.isEmpty());
+ assertTrue(original.isEmpty(), "size=" + original.size());
}
@Test
- public void testForEachBiConsumer() throws Exception {
+ public void testForEachBiConsumer() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "avalue");
original.putValue("B", "Bvalue");
@@ -1080,10 +1065,10 @@ public void testForEachBiConsumer() throws Exception {
int count = 0;
@Override
public void accept(final String key, final String value) {
- assertEquals("key", key, original.getKeyAt(count));
- assertEquals("val", value, original.getValueAt(count));
+ assertEquals(key, original.getKeyAt(count), "key");
+ assertEquals(value, original.getValueAt(count), "val");
count++;
- assertTrue("count should not exceed size but was " + count, count <= original.size());
+ assertTrue(count <= original.size(), "count should not exceed size but was " + count);
}
});
}
@@ -1092,19 +1077,16 @@ static class State {
SortedArrayStringMap data;
int count;
}
- static TriConsumer COUNTER = new TriConsumer() {
- @Override
- public void accept(final String key, final String value, final State state) {
- assertEquals("key", key, state.data.getKeyAt(state.count));
- assertEquals("val", value, state.data.getValueAt(state.count));
- state.count++;
- assertTrue("count should not exceed size but was " + state.count,
- state.count <= state.data.size());
- }
+ static TriConsumer COUNTER = (key, value, state) -> {
+ assertEquals(key, state.data.getKeyAt(state.count), "key");
+ assertEquals(value, state.data.getValueAt(state.count), "val");
+ state.count++;
+ assertTrue(
+ state.count <= state.data.size(), "count should not exceed size but was " + state.count);
};
@Test
- public void testForEachTriConsumer() throws Exception {
+ public void testForEachTriConsumer() {
final SortedArrayStringMap original = new SortedArrayStringMap();
original.putValue("a", "avalue");
original.putValue("B", "Bvalue");
@@ -1115,4 +1097,4 @@ public void testForEachTriConsumer() throws Exception {
original.forEach(COUNTER, state);
assertEquals(state.count, original.size());
}
-}
\ No newline at end of file
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/StackLocatorUtilTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/StackLocatorUtilTest.java
new file mode 100644
index 00000000000..8fe11c3f8e9
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/StackLocatorUtilTest.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.util;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.engine.execution.InterceptingExecutableInvoker;
+import org.junit.jupiter.engine.execution.InvocationInterceptorChain;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class StackLocatorUtilTest {
+
+ @Test
+ public void testStackTraceEquivalence() throws Throwable {
+ StackTraceElement[] stackTraceElements = expectedStack(new Throwable().getStackTrace());
+ for (int i = 1; i < 10; i++) {
+ final String expected = stackTraceElements[i-1].getClassName();
+ final String actual = StackLocatorUtil.getCallerClass(i).getName();
+ final String fallbackActual = Class.forName(
+ StackLocatorUtil.getStackTraceElement(i).getClassName()).getName();
+ assertSame(expected, actual);
+ assertSame(expected, fallbackActual);
+ }
+ }
+
+ private StackTraceElement[] expectedStack(StackTraceElement[] elements) {
+ StackTraceElement[] elementArray = new StackTraceElement[10];
+ int i = 0;
+ for (int index = 0; index < 10;) {
+ if (elements[i].getClassName().startsWith("org.")) {
+ elementArray[index] = elements[i];
+ ++index;
+ }
+ ++i;
+ }
+ return elementArray;
+ }
+
+ @Test
+ public void testGetCallerClass() throws Exception {
+ final Class> expected = StackLocatorUtilTest.class;
+ final Class> actual = StackLocatorUtil.getCallerClass(1);
+ assertSame(expected, actual);
+ }
+
+ @Test
+ public void testGetCallerClassLoader() throws Exception {
+ assertSame(StackLocatorUtilTest.class.getClassLoader(), StackLocatorUtil.getCallerClassLoader(1));
+ }
+
+ @Test
+ public void testGetCallerClassNameViaStackTrace() throws Exception {
+ final Class> expected = StackLocatorUtilTest.class;
+ final Class> actual = Class.forName(new Throwable().getStackTrace()[0].getClassName());
+ assertSame(expected, actual);
+ }
+
+ @Test
+ public void testGetCurrentStackTrace() throws Exception {
+ final Deque> classes = StackLocatorUtil.getCurrentStackTrace();
+ final Deque> reversed = new ArrayDeque<>(classes.size());
+ while (!classes.isEmpty()) {
+ reversed.push(classes.pop());
+ }
+ while (reversed.peek() != StackLocatorUtil.class) {
+ reversed.pop();
+ }
+ reversed.pop(); // ReflectionUtil
+ assertSame(StackLocatorUtilTest.class, reversed.pop());
+ }
+
+ @Test
+ public void testGetCallerClassViaName() throws Exception {
+ final Class> expected = InterceptingExecutableInvoker.class;
+ final Class> actual = StackLocatorUtil.getCallerClass("org.junit.jupiter.engine.execution.InvocationInterceptorChain");
+ // if this test fails in the future, it's probably because of a JUnit upgrade; check the new stack trace and
+ // update this test accordingly
+ assertSame(expected, actual);
+ }
+
+ @Test
+ public void testGetCallerClassViaAnchorClass() throws Exception {
+ final Class> expected = InterceptingExecutableInvoker.class;
+ final Class> actual = StackLocatorUtil.getCallerClass(InvocationInterceptorChain.class);
+ // if this test fails in the future, it's probably because of a JUnit upgrade; check the new stack trace and
+ // update this test accordingly
+ assertSame(expected, actual);
+ }
+
+ @Test
+ public void testLocateClass() {
+ final ClassLocator locator = new ClassLocator();
+ final Class> clazz = locator.locateClass();
+ assertNotNull(clazz, "Could not locate class");
+ assertEquals(this.getClass(), clazz, "Incorrect class");
+ }
+
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/StringBuildersTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/StringBuildersTest.java
new file mode 100644
index 00000000000..eb759f25eb1
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/StringBuildersTest.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.util;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests the StringBuilders class.
+ */
+public class StringBuildersTest {
+ @Test
+ public void trimToMaxSize() {
+ final StringBuilder sb = new StringBuilder();
+ final char[] value = new char[4 * 1024];
+ sb.append(value);
+
+ assertTrue(sb.length() > Constants.MAX_REUSABLE_MESSAGE_SIZE, "needs trimming");
+ StringBuilders.trimToMaxSize(sb, Constants.MAX_REUSABLE_MESSAGE_SIZE);
+ assertTrue(sb.length() <= Constants.MAX_REUSABLE_MESSAGE_SIZE, "trimmed OK");
+ }
+
+ @Test
+ public void trimToMaxSizeWithLargeCapacity() {
+ final StringBuilder sb = new StringBuilder();
+ final char[] value = new char[4 * 1024];
+ sb.append(value);
+ sb.setLength(0);
+
+ assertTrue(sb.capacity() > Constants.MAX_REUSABLE_MESSAGE_SIZE, "needs trimming");
+ StringBuilders.trimToMaxSize(sb, Constants.MAX_REUSABLE_MESSAGE_SIZE);
+ assertTrue(sb.capacity() <= Constants.MAX_REUSABLE_MESSAGE_SIZE, "trimmed OK");
+ }
+
+ @Test
+ public void escapeJsonCharactersCorrectly() {
+ String jsonValueNotEscaped = "{\"field\n1\":\"value_1\"}";
+ String jsonValueEscaped = "{\\\"field\\n1\\\":\\\"value_1\\\"}";
+
+ StringBuilder sb = new StringBuilder();
+ sb.append(jsonValueNotEscaped);
+ assertEquals(jsonValueNotEscaped, sb.toString());
+ StringBuilders.escapeJson(sb, 0);
+ assertEquals(jsonValueEscaped, sb.toString());
+
+ sb = new StringBuilder();
+ String jsonValuePartiallyEscaped = "{\"field\n1\":\\\"value_1\\\"}";
+ sb.append(jsonValueNotEscaped);
+ assertEquals(jsonValueNotEscaped, sb.toString());
+ StringBuilders.escapeJson(sb, 10);
+ assertEquals(jsonValuePartiallyEscaped, sb.toString());
+ }
+
+ @Test
+ public void escapeJsonCharactersISOControl() {
+ String jsonValueNotEscaped = "{\"field\n1\":\"value" + (char) 0x8F + "_1\"}";
+ String jsonValueEscaped = "{\\\"field\\n1\\\":\\\"value\\u008F_1\\\"}";
+
+ StringBuilder sb = new StringBuilder();
+ sb.append(jsonValueNotEscaped);
+ assertEquals(jsonValueNotEscaped, sb.toString());
+ StringBuilders.escapeJson(sb, 0);
+ assertEquals(jsonValueEscaped, sb.toString());
+ }
+
+ @Test
+ public void escapeXMLCharactersCorrectly() {
+ String xmlValueNotEscaped = "<\"Salt&Peppa'\">";
+ String xmlValueEscaped = "<"Salt&Peppa'">";
+
+ StringBuilder sb = new StringBuilder();
+ sb.append(xmlValueNotEscaped);
+ assertEquals(xmlValueNotEscaped, sb.toString());
+ StringBuilders.escapeXml(sb, 0);
+ assertEquals(xmlValueEscaped, sb.toString());
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/StringsTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/StringsTest.java
new file mode 100644
index 00000000000..7c9695d3a81
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/StringsTest.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.util;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class StringsTest {
+
+ @Test
+ public void testIsEmpty() {
+ assertTrue(Strings.isEmpty(null));
+ assertTrue(Strings.isEmpty(""));
+ assertFalse(Strings.isEmpty(" "));
+ assertFalse(Strings.isEmpty("a"));
+ }
+
+ @Test
+ public void testIsBlank() {
+ assertTrue(Strings.isBlank(null));
+ assertTrue(Strings.isBlank(""));
+ assertTrue(Strings.isBlank(" "));
+ assertTrue(Strings.isBlank("\n"));
+ assertTrue(Strings.isBlank("\r"));
+ assertTrue(Strings.isBlank("\t"));
+ assertFalse(Strings.isEmpty("a"));
+ }
+
+ /**
+ * A sanity test to make sure a typo does not mess up {@link Strings#EMPTY}.
+ */
+ @Test
+ public void testEMPTY() {
+ assertEquals("", Strings.EMPTY);
+ assertEquals(0, Strings.EMPTY.length());
+ }
+
+ @Test
+ public void testConcat() {
+ assertEquals("ab", Strings.concat("a", "b"));
+ assertEquals("a", Strings.concat("a", ""));
+ assertEquals("a", Strings.concat("a", null));
+ assertEquals("b", Strings.concat("", "b"));
+ assertEquals("b", Strings.concat(null, "b"));
+ }
+
+ @Test
+ public void testJoin() {
+ assertNull(Strings.join((Iterable>) null, '.'));
+ assertNull(Strings.join((Iterator>) null, '.'));
+ assertEquals("", Strings.join((Collections.emptyList()), '.'));
+
+ assertEquals("a", Strings.join(Collections.singletonList("a"), '.'));
+ assertEquals("a.b", Strings.join(Arrays.asList("a", "b"), '.'));
+ assertEquals("a.b.c", Strings.join(Arrays.asList("a", "b", "c"), '.'));
+
+ assertEquals("", Strings.join(Collections.singletonList((String) null), ':'));
+ assertEquals(":", Strings.join(Arrays.asList(null, null), ':'));
+ assertEquals("a:", Strings.join(Arrays.asList("a", null), ':'));
+ assertEquals(":b", Strings.join(Arrays.asList(null, "b"), ':'));
+ }
+
+ @Test
+ public void splitList() {
+ String[] list = Strings.splitList("1, 2, 3");
+ assertEquals(3, list.length);
+ list = Strings.splitList("");
+ assertEquals(1, list.length);
+ list = Strings.splitList(null);
+ assertEquals(0, list.length);
+ }
+
+ @Test
+ public void testQuote() {
+ assertEquals("'Q'", Strings.quote("Q"));
+ }
+
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/SystemPropertiesMain.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/SystemPropertiesMain.java
similarity index 100%
rename from log4j-api/src/test/java/org/apache/logging/log4j/util/SystemPropertiesMain.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/util/SystemPropertiesMain.java
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/SystemPropertiesPropertySourceSecurityManagerIT.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/SystemPropertiesPropertySourceSecurityManagerIT.java
new file mode 100644
index 00000000000..439a3788b40
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/SystemPropertiesPropertySourceSecurityManagerIT.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.util;
+
+import java.security.Permission;
+import java.util.PropertyPermission;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledForJreRange;
+import org.junit.jupiter.api.condition.JRE;
+import org.junit.jupiter.api.parallel.ResourceLock;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Test related to LOG4J2-2274.
+ *
+ * Using a security manager can mess up other tests so this is best used from
+ * integration tests (classes that end in "IT" instead of "Test" and
+ * "TestCase".)
+ *
+ *
+ * @see SystemPropertiesPropertySource
+ * @see SecurityManager
+ * @see System#setSecurityManager(SecurityManager)
+ * @see PropertyPermission
+ */
+@ResourceLock("java.lang.SecurityManager")
+@DisabledForJreRange(min = JRE.JAVA_18) // custom SecurityManager instances throw UnsupportedOperationException
+public class SystemPropertiesPropertySourceSecurityManagerIT {
+
+ /**
+ * Always throws a SecurityException for any environment variables permission
+ * check.
+ */
+ private static class TestSecurityManager extends SecurityManager {
+ @Override
+ public void checkPermission(final Permission permission) {
+ if (permission instanceof PropertyPermission) {
+ throw new SecurityException();
+ }
+ }
+ }
+
+ /**
+ * Makes sure we do not blow up with exception below due to a security manager
+ * rejecting environment variable access in
+ * {@link SystemPropertiesPropertySource}.
+ *
+ *
+ * java.lang.ExceptionInInitializerError
+ * at org.apache.logging.log4j.util.SystemPropertiesPropertySourceSecurityManagerTest.test(SystemPropertiesPropertySourceSecurityManagerTest.java:64)
+ * ...
+ * Caused by: java.lang.SecurityException
+ * at org.apache.logging.log4j.util.SystemPropertiesPropertySourceSecurityManagerTest$TestSecurityManager.checkPermission(SystemPropertiesPropertySourceSecurityManagerTest.java:49)
+ * at java.lang.SecurityManager.checkPropertiesAccess(SecurityManager.java:1265)
+ * at java.lang.System.getProperties(System.java:624)
+ * at org.apache.logging.log4j.util.SystemPropertiesPropertySource.forEach(SystemPropertiesPropertySource.java:40)
+ * at org.apache.logging.log4j.util.PropertiesUtil$Environment.reload(PropertiesUtil.java:330)
+ * at org.apache.logging.log4j.util.PropertiesUtil$Environment.(PropertiesUtil.java:322)
+ * at org.apache.logging.log4j.util.PropertiesUtil$Environment.(PropertiesUtil.java:310)
+ * at org.apache.logging.log4j.util.PropertiesUtil.(PropertiesUtil.java:69)
+ * at org.apache.logging.log4j.util.PropertiesUtil.