You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by gg...@apache.org on 2022/01/14 18:33:16 UTC
[logging-log4j2] 02/02: Implement configuration APIs in and PropertyConfigurator and BasicConfigurator.
This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit 73a2cd1cd0e94c7f4f36e4ac9dc72380d30750ef
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Fri Jan 14 13:33:10 2022 -0500
Implement configuration APIs in and PropertyConfigurator and
BasicConfigurator.
- Call sites of public APIs now pick up the proper logger context based
on the call sites' class loader.
- Drop package private interface LoggerRepository2.
- Can't break up this commit into smaller digestible chunks.
---
.../java/org/apache/log4j/BasicConfigurator.java | 32 +-
.../src/main/java/org/apache/log4j/Category.java | 704 ++++++++++++---------
.../src/main/java/org/apache/log4j/Hierarchy.java | 143 +++--
.../src/main/java/org/apache/log4j/LogManager.java | 105 ++-
.../java/org/apache/log4j/LoggerRepository2.java | 31 -
.../org/apache/log4j/PropertyConfigurator.java | 603 +++++++++++++++++-
.../log4j/config/PropertiesConfiguration.java | 272 ++++----
.../org/apache/log4j/BasicConfiguratorTest.java | 56 ++
.../org/apache/log4j/PropertyConfiguratorTest.java | 427 +++++++++++++
9 files changed, 1790 insertions(+), 583 deletions(-)
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 da90e2c..14910d8 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,27 +16,45 @@
*/
package org.apache.log4j;
+import org.apache.logging.log4j.util.StackLocatorUtil;
+
/**
- * Provided for compatibility with Log4j 1.x.
- *
+ * Configures the package.
+ *
+ * <p>
+ * For file based configuration, see {@link PropertyConfigurator}. For XML based configuration, see
+ * {@link org.apache.log4j.xml.DOMConfigurator DOMConfigurator}.
+ * </p>
+ *
* @since 0.8.1
*/
public class BasicConfigurator {
+ /**
+ * Adds a {@link ConsoleAppender} that uses {@link PatternLayout} using the
+ * {@link PatternLayout#TTCC_CONVERSION_PATTERN} and prints to <code>System.out</code> to the root category.
+ */
public static void configure() {
- LogManager.reconfigure();
+ LogManager.reconfigure(StackLocatorUtil.getCallerClassLoader(2));
}
/**
- * No-op implementation.
- * @param appender The appender.
+ * Adds <code>appender</code> 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);
}
+ /**
+ * Resets the default hierarchy to its default. It is equivalent to calling
+ * <code>Category.getDefaultHierarchy().resetConfiguration()</code>.
+ *
+ * See {@link Hierarchy#resetConfiguration()} for more details.
+ */
public static void resetConfiguration() {
- LogManager.resetConfiguration();
+ LogManager.resetConfiguration(StackLocatorUtil.getCallerClassLoader(2));
}
/**
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 286ae32..90857cb 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
@@ -19,13 +19,17 @@ package org.apache.log4j;
import java.util.Enumeration;
import java.util.Map;
import java.util.ResourceBundle;
+import java.util.Vector;
import java.util.concurrent.ConcurrentMap;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.helpers.AppenderAttachableImpl;
import org.apache.log4j.helpers.NullEnumeration;
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.message.LocalizedMessage;
@@ -46,6 +50,78 @@ public class Category implements AppenderAttachable {
private static final String FQCN = Category.class.getName();
/**
+ * 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));
+ }
+
+ /**
+ * Returns all the currently defined categories in the default hierarchy as an {@link java.util.Enumeration
+ * Enumeration}.
+ *
+ * <p>
+ * The root category is <em>not</em> included in the returned {@link Enumeration}.
+ * </p>
+ *
+ * @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;
@@ -81,8 +157,11 @@ public class Category implements AppenderAttachable {
/** 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.
*/
@@ -90,158 +169,101 @@ public class Category implements AppenderAttachable {
this.name = name;
this.logger = context.getLogger(name);
this.repository = LogManager.getLoggerRepository();
- //this.rendererMap = ((RendererSupport) repository).getRendererMap();
+ // 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(Hierarchy.getContext(), name);
}
- Category(final org.apache.logging.log4j.Logger logger) {
- this.logger = logger;
- //rendererMap = ((RendererSupport) LogManager.getLoggerRepository()).getRendererMap();
- }
-
- public static Category getInstance(final String name) {
- // Depth 2 gets the call site of this method.
- return LogManager.getLogger(name, StackLocatorUtil.getCallerClassLoader(2));
- }
-
- public static Category getInstance(@SuppressWarnings("rawtypes") final Class clazz) {
- // Depth 2 gets the call site of this method.
- return LogManager.getLogger(clazz.getName(), StackLocatorUtil.getCallerClassLoader(2));
- }
-
- public final String getName() {
- return logger.getName();
- }
-
- org.apache.logging.log4j.Logger getLogger() {
- return logger;
- }
-
- public final Category getParent() {
- if (!LogManager.isLog4jCorePresent()) {
- return null;
+ /**
+ * Add <code>newAppender</code> to the list of appenders of this Category instance.
+ * <p>
+ * If <code>newAppender</code> is already in the list of appenders, then it won't be added again.
+ * </p>
+ */
+ @Override
+ public void addAppender(final Appender appender) {
+ if (aai == null) {
+ aai = new AppenderAttachableImpl();
}
- final org.apache.logging.log4j.Logger parent = CategoryUtil.getParent(logger);
- final LoggerContext loggerContext = CategoryUtil.getLoggerContext(logger);
- if (parent == null || loggerContext == null) {
- return null;
+ aai.addAppender(appender);
+ if (appender != null) {
+ repository.fireAddAppenderEvent(this, appender);
}
- final ConcurrentMap<String, Logger> loggers = Hierarchy.getLoggersMap(loggerContext);
- final Logger parentLogger = loggers.get(parent.getName());
- return parentLogger == null ? new Category(parent) : parentLogger;
- }
-
- public static Category getRoot() {
- return getInstance(Strings.EMPTY);
}
/**
- * Returns all the currently defined categories in the default hierarchy as an
- * {@link java.util.Enumeration Enumeration}.
+ * If <code>assertion</code> parameter is {@code false}, then logs <code>msg</code> as an {@link #error(Object) error}
+ * statement.
*
* <p>
- * The root category is <em>not</em> included in the returned
- * {@link Enumeration}.
+ * The <code>assert</code> method has been renamed to <code>assertLog</code> because <code>assert</code> is a language
+ * reserved word in JDK 1.4.
* </p>
*
- * @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.
+ * @param assertion The assertion.
+ * @param msg The message to print if <code>assertion</code> is false.
*
- * @return the default LoggerRepository instance.
- * @deprecated Please use {@link LogManager#getLoggerRepository()} instead.
- * @since 1.0
+ * @since 1.2
*/
- @Deprecated
- public static LoggerRepository getDefaultHierarchy() {
- return LogManager.getLoggerRepository();
- }
-
- public Level getEffectiveLevel() {
- switch (logger.getLevel().getStandardLevel()) {
- case ALL:
- return Level.ALL;
- case TRACE:
- return Level.TRACE;
- case DEBUG:
- return Level.DEBUG;
- case INFO:
- return Level.INFO;
- case WARN:
- return Level.WARN;
- case ERROR:
- return Level.ERROR;
- case FATAL:
- return Level.FATAL;
- default:
- // TODO Should this be an IllegalStateException?
- return Level.OFF;
+ public void assertLog(final boolean assertion, final String msg) {
+ if (!assertion) {
+ this.error(msg);
}
}
/**
- * Gets the the {@link LoggerRepository} where this <code>Category</code> instance is attached.
+ * Call the appenders in the hierrachy starting at <code>this</code>. If no appenders could be found, emit a warning.
+ * <p>
+ * 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.
+ * </p>
*
- * @deprecated Please use {@link #getLoggerRepository()} instead.
- * @since 1.1
+ * @param event the event to log.
*/
- @Deprecated
- public LoggerRepository getHierarchy() {
- return repository;
+ public void callAppenders(final LoggingEvent event) {
+ 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);
+ }
}
/**
- * Gets the the {@link LoggerRepository} where this <code>Category</code> is attached.
+ * Closes all attached appenders implementing the AppenderAttachable interface.
*
- * @since 1.2
+ * @since 1.0
*/
- public LoggerRepository getLoggerRepository() {
- return repository;
- }
-
- public Priority getChainedPriority() {
- return getEffectiveLevel();
- }
-
- public final Level getLevel() {
- return getEffectiveLevel();
- }
-
- private String getLevelStr(final Priority priority) {
- return priority == null ? null : priority.levelStr;
- }
-
- public void setLevel(final Level level) {
- setLevel(getLevelStr(level));
- }
-
- public final Level getPriority() {
- return getEffectiveLevel();
- }
-
- public void setPriority(final Priority priority) {
- setLevel(getLevelStr(priority));
- }
-
- private void setLevel(final String levelStr) {
- if (LogManager.isLog4jCorePresent()) {
- CategoryUtil.setLevel(logger, org.apache.logging.log4j.Level.toLevel(levelStr));
+ 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();
+ }
+ }
}
}
@@ -253,10 +275,6 @@ public class Category implements AppenderAttachable {
maybeLog(FQCN, org.apache.logging.log4j.Level.DEBUG, message, t);
}
- public boolean isDebugEnabled() {
- return logger.isDebugEnabled();
- }
-
public void error(final Object message) {
maybeLog(FQCN, org.apache.logging.log4j.Level.ERROR, message, null);
}
@@ -265,22 +283,6 @@ public class Category implements AppenderAttachable {
maybeLog(FQCN, org.apache.logging.log4j.Level.ERROR, message, t);
}
- public boolean isErrorEnabled() {
- return logger.isErrorEnabled();
- }
-
- public void warn(final Object message) {
- maybeLog(FQCN, org.apache.logging.log4j.Level.WARN, message, null);
- }
-
- public void warn(final Object message, final Throwable t) {
- maybeLog(FQCN, org.apache.logging.log4j.Level.WARN, message, t);
- }
-
- public boolean isWarnEnabled() {
- return logger.isWarnEnabled();
- }
-
public void fatal(final Object message) {
maybeLog(FQCN, org.apache.logging.log4j.Level.FATAL, message, null);
}
@@ -289,163 +291,164 @@ public class Category implements AppenderAttachable {
maybeLog(FQCN, org.apache.logging.log4j.Level.FATAL, message, t);
}
- 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 boolean isEnabledFor(final Priority level) {
- final org.apache.logging.log4j.Level lvl = org.apache.logging.log4j.Level.toLevel(level.toString());
- return isEnabledFor(lvl);
- }
-
/**
- * No-op implementation.
- * @param appender The Appender to add.
- */
- @Override
- public void addAppender(final Appender appender) {
- }
-
- /**
- * No-op implementation.
- * @param event The logging event.
- */
- public void callAppenders(final LoggingEvent event) {
- }
-
- /**
- * Closes all attached appenders implementing the AppenderAttachable interface.
+ * 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.
*
- * @since 1.0
+ * @param appender appender, may be null.
*/
- 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();
- }
+ 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);
}
}
}
- @Override
- @SuppressWarnings("rawtypes")
- public Enumeration getAllAppenders() {
- return NullEnumeration.getInstance();
- }
-
- /**
- * No-op implementation.
- * @param name The name of the Appender.
- * @return null.
- */
- @Override
- public Appender getAppender(final String name) {
- return null;
+ 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());
+ if (logger instanceof ExtendedLogger) {
+ @SuppressWarnings("unchecked")
+ final Message msg = message instanceof Message ? (Message) message
+ : message instanceof Map ? new MapMessage((Map) message) : new ObjectMessage(message);
+ ((ExtendedLogger) logger).logMessage(fqcn, lvl, null, msg, t);
+ } else {
+ final ObjectRenderer renderer = get(message.getClass());
+ final Message msg = message instanceof Message ? (Message) message
+ : renderer != null ? new RenderedMessage(renderer, message) : new ObjectMessage(message);
+ logger.log(lvl, msg, t);
+ }
}
- /**
- 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 false;
+ private <T> ObjectRenderer get(final Class<T> 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 null;
}
- /**
- * No-op implementation.
- */
- @Override
- public void removeAllAppenders() {
+ public boolean getAdditivity() {
+ return LogManager.isLog4jCorePresent() ? CategoryUtil.isAdditive(logger) : false;
}
/**
- * No-op implementation.
- * @param appender The Appender to remove.
+ * Get the appenders contained in this category as an {@link Enumeration}. If no appenders can be found, then a
+ * {@link NullEnumeration} is returned.
+ *
+ * @return Enumeration An enumeration of the appenders in this category.
*/
@Override
- public void removeAppender(final Appender appender) {
+ @SuppressWarnings("rawtypes")
+ public Enumeration getAllAppenders() {
+ return aai == null ? NullEnumeration.getInstance() : aai.getAllAppenders();
}
/**
- * No-op implementation.
- * @param name The Appender to remove.
+ * Look for the appender named as <code>name</code>.
+ * <p>
+ * Return the appender with that name if in the list. Return <code>null</code> otherwise.
+ * </p>
*/
@Override
- public void removeAppender(final String name) {
+ public Appender getAppender(final String name) {
+ Appender appender = aai != null ? aai.getAppender(name) : null;
+ if (appender == null && LogManager.isLog4jCorePresent()) {
+ final org.apache.logging.log4j.core.Appender coreAppender = CategoryUtil.getAppenders(logger).get(name);
+ if (coreAppender != null) {
+ addAppender(appender = new AppenderWrapper(coreAppender));
+ }
+ }
+ return appender;
}
- /**
- * No-op implementation.
- */
- public static void shutdown() {
+ public Priority getChainedPriority() {
+ return getEffectiveLevel();
}
- 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());
- if (logger instanceof ExtendedLogger) {
- @SuppressWarnings("unchecked")
- final
- Message msg = message instanceof Message ? (Message) message : message instanceof Map ?
- new MapMessage((Map) message) : new ObjectMessage(message);
- ((ExtendedLogger) logger).logMessage(fqcn, lvl, null, msg, t);
- } else {
- final ObjectRenderer renderer = get(message.getClass());
- final Message msg = message instanceof Message ? (Message) message : renderer != null ?
- new RenderedMessage(renderer, message) : new ObjectMessage(message);
- logger.log(lvl, msg, t);
+ public Level getEffectiveLevel() {
+ switch (logger.getLevel().getStandardLevel()) {
+ case ALL:
+ return Level.ALL;
+ case TRACE:
+ return Level.TRACE;
+ case DEBUG:
+ return Level.DEBUG;
+ case INFO:
+ return Level.INFO;
+ case WARN:
+ return Level.WARN;
+ case ERROR:
+ return Level.ERROR;
+ case FATAL:
+ return Level.FATAL;
+ default:
+ // TODO Should this be an IllegalStateException?
+ return Level.OFF;
}
}
/**
- * Tests if the named category exists (in the default hierarchy).
- *
- * @param name The name to test.
- * @return Whether the name exists.
+ * Gets the the {@link LoggerRepository} where this <code>Category</code> instance is attached.
*
- * @deprecated Please use {@link LogManager#exists(String)} instead.
- * @since 0.8.5
+ * @deprecated Please use {@link #getLoggerRepository()} instead.
+ * @since 1.1
*/
@Deprecated
- public static Logger exists(final String name) {
- return LogManager.exists(name);
+ public LoggerRepository getHierarchy() {
+ return repository;
}
- public boolean getAdditivity() {
- return LogManager.isLog4jCorePresent() ? CategoryUtil.isAdditive(logger) : false;
+ public final Level getLevel() {
+ return getEffectiveLevel();
}
- public void setAdditivity(final boolean additivity) {
- if (LogManager.isLog4jCorePresent()) {
- CategoryUtil.setAdditivity(logger, additivity);
- }
+ private String getLevelStr(final Priority priority) {
+ return priority == null ? null : priority.levelStr;
+ }
+
+ org.apache.logging.log4j.Logger getLogger() {
+ return logger;
}
/**
- * Only the Hiearchy class can set the hiearchy of a category. Default package access is MANDATORY here.
+ * Gets the the {@link LoggerRepository} where this <code>Category</code> is attached.
+ *
+ * @since 1.2
*/
- final void setHierarchy(final LoggerRepository repository) {
- this.repository = repository;
+ public LoggerRepository getLoggerRepository() {
+ return repository;
}
- public void setResourceBundle(final ResourceBundle bundle) {
- this.bundle = bundle;
+ public final String getName() {
+ return logger.getName();
+ }
+
+ 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<String, Logger> loggers = Hierarchy.getLoggersMap(loggerContext);
+ final Logger parentLogger = loggers.get(parent.getName());
+ return parentLogger == null ? new Category(parent) : parentLogger;
+ }
+
+ public final Level getPriority() {
+ return getEffectiveLevel();
}
public ResourceBundle getResourceBundle() {
@@ -471,39 +474,51 @@ public class Category implements AppenderAttachable {
return null;
}
- 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;
+ 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);
}
/**
- * If <code>assertion</code> parameter is {@code false}, then logs
- * <code>msg</code> as an {@link #error(Object) error} statement.
+ * Is the appender passed as parameter attached to this category?
*
- * <p>
- * The <code>assert</code> method has been renamed to <code>assertLog</code>
- * because <code>assert</code> is a language reserved word in JDK 1.4.
- * </p>
- *
- * @param assertion The assertion.
- * @param msg The message to print if <code>assertion</code> is false.
- *
- * @since 1.2
+ * @param appender The Appender to add.
+ * @return true if the appender is attached.
*/
- public void assertLog(final boolean assertion, final String msg) {
- if (!assertion) {
- this.error(msg);
- }
+ @Override
+ public boolean isAttached(final Appender appender) {
+ return aai == null ? false : aai.isAttached(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);
- }
+ public boolean isDebugEnabled() {
+ return logger.isDebugEnabled();
+ }
+
+ private boolean isEnabledFor(final org.apache.logging.log4j.Level level) {
+ return logger.isEnabled(level);
+ }
+
+ public boolean isEnabledFor(final Priority level) {
+ return isEnabledFor(org.apache.logging.log4j.Level.toLevel(level.toString()));
+ }
+
+ public boolean isErrorEnabled() {
+ return logger.isErrorEnabled();
+ }
+
+ public boolean isFatalEnabled() {
+ return logger.isFatalEnabled();
+ }
+
+ public boolean isInfoEnabled() {
+ return logger.isInfoEnabled();
+ }
+
+ public boolean isWarnEnabled() {
+ return logger.isWarnEnabled();
}
public void l7dlog(final Priority priority, final String key, final Object[] params, final Throwable t) {
@@ -513,10 +528,9 @@ public class Category implements AppenderAttachable {
}
}
- public void log(final Priority priority, final Object message, final Throwable t) {
+ public void l7dlog(final Priority priority, final String key, final Throwable t) {
if (isEnabledFor(priority)) {
- @SuppressWarnings("unchecked")
- final Message msg = message instanceof Map ? new MapMessage((Map) message) : new ObjectMessage(message);
+ final Message msg = new LocalizedMessage(bundle, key, null);
forcedLog(FQCN, priority, msg, t);
}
}
@@ -529,6 +543,14 @@ public class Category implements AppenderAttachable {
}
}
+ public void log(final Priority priority, final Object message, final Throwable t) {
+ if (isEnabledFor(priority)) {
+ @SuppressWarnings("unchecked")
+ final Message msg = message instanceof Map ? new MapMessage((Map) message) : new ObjectMessage(message);
+ forcedLog(FQCN, priority, msg, t);
+ }
+ }
+
public void log(final String fqcn, final Priority priority, final Object message, final Throwable t) {
if (isEnabledFor(priority)) {
final Message msg = new ObjectMessage(message);
@@ -536,18 +558,13 @@ public class Category implements AppenderAttachable {
}
}
- void maybeLog(
- final String fqcn,
- final org.apache.logging.log4j.Level level,
- final Object message,
- final Throwable throwable) {
+ 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;
if (message instanceof String) {
msg = new SimpleMessage((String) message);
}
- // SimpleMessage treats String and CharSequence differently, hence
- // this else-if block.
+ // SimpleMessage treats String and CharSequence differently, hence this else-if block.
else if (message instanceof CharSequence) {
msg = new SimpleMessage((CharSequence) message);
} else if (message instanceof Map) {
@@ -565,23 +582,61 @@ public class Category implements AppenderAttachable {
}
}
- private boolean isEnabledFor(final org.apache.logging.log4j.Level level) {
- return logger.isEnabled(level);
- }
-
- private <T> ObjectRenderer get(final Class<T> clazz) {
- ObjectRenderer renderer = null;
- for (Class<? super T> c = clazz; c != null; c = c.getSuperclass()) {
- renderer = rendererMap.get(c);
- if (renderer != null) {
- return renderer;
+ /**
+ * Removes all previously added appenders from this Category instance.
+ * <p>
+ * This is useful when re-reading configuration information.
+ * </p>
+ */
+ @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());
}
- renderer = searchInterfaces(c);
- if (renderer != null) {
- return renderer;
+ aai.removeAllAppenders();
+ for (final Object appender : appenders) {
+ fireRemoveAppenderEvent((Appender) appender);
}
+ aai = null;
+ }
+ }
+
+ /**
+ * 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);
+ }
+ }
+
+ /**
+ * 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) {
+ if (name == null || aai == null) {
+ return;
+ }
+ final Appender appender = aai.getAppender(name);
+ aai.removeAppender(name);
+ if (appender != null) {
+ fireRemoveAppenderEvent(appender);
}
- return null;
}
ObjectRenderer searchInterfaces(final Class<?> c) {
@@ -599,4 +654,43 @@ public class Category implements AppenderAttachable {
return null;
}
+ public void setAdditivity(final boolean additivity) {
+ if (LogManager.isLog4jCorePresent()) {
+ CategoryUtil.setAdditivity(logger, additivity);
+ }
+ }
+
+ /**
+ * 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 setLevel(final Level level) {
+ setLevel(getLevelStr(level));
+ }
+
+ private void setLevel(final String levelStr) {
+ if (LogManager.isLog4jCorePresent()) {
+ CategoryUtil.setLevel(logger, org.apache.logging.log4j.Level.toLevel(levelStr));
+ }
+ }
+
+ public void setPriority(final Priority priority) {
+ setLevel(getLevelStr(priority));
+ }
+
+ public void setResourceBundle(final ResourceBundle bundle) {
+ this.bundle = bundle;
+ }
+
+ public void warn(final Object message) {
+ maybeLog(FQCN, org.apache.logging.log4j.Level.WARN, message, 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/Hierarchy.java b/log4j-1.2-api/src/main/java/org/apache/log4j/Hierarchy.java
index a7a059a..8b2f1f9 100644
--- 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
@@ -16,7 +16,7 @@
*/
// WARNING This class MUST not have references to the Category or
-// WARNING RootCategory classes in its static initiliazation neither
+// WARNING RootCategory classes in its static initialization neither
// WARNING directly nor indirectly.
package org.apache.log4j;
@@ -29,17 +29,19 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.log4j.helpers.LogLog;
+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.Strings;
+import org.apache.logging.log4j.util.StackLocatorUtil;
/**
* This class is specialized in retrieving loggers by name and also maintaining the logger hierarchy.
@@ -58,7 +60,7 @@ import org.apache.logging.log4j.util.Strings;
* provision node.
* </p>
*/
-public class Hierarchy implements LoggerRepository2, RendererSupport, ThrowableRendererSupport {
+public class Hierarchy implements LoggerRepository, RendererSupport, ThrowableRendererSupport {
private static class PrivateLoggerAdapter extends AbstractLoggerAdapter<Logger> {
@@ -170,12 +172,14 @@ public class Hierarchy implements LoggerRepository2, RendererSupport, ThrowableR
*
* <p>
* You should <em>really</em> know what you are doing before invoking this method.
+ * </p>
*
* @since 0.9.0
*/
public void clear() {
// System.out.println("\n\nAbout to clear internal hash table.");
ht.clear();
+ getLoggersMap(getContext()).clear();
}
@Override
@@ -197,8 +201,15 @@ public class Hierarchy implements LoggerRepository2, RendererSupport, ThrowableR
*/
@Override
public Logger exists(final String name) {
- final LoggerContext ctx = getContext();
- if (!ctx.hasLogger(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);
@@ -227,6 +238,10 @@ public class Hierarchy implements LoggerRepository2, RendererSupport, ThrowableR
}
}
+ LoggerContext getContext(final ClassLoader classLoader) {
+ return LogManager.getContext(classLoader);
+ }
+
/**
* @deprecated Please use {@link #getCurrentLoggers} instead.
*/
@@ -241,22 +256,25 @@ public class Hierarchy implements LoggerRepository2, RendererSupport, ThrowableR
*
* <p>
* The root logger is <em>not</em> included in the returned {@link Enumeration}.
+ * </p>
*/
@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 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();
- 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));
}
/**
@@ -265,6 +283,7 @@ public class Hierarchy implements LoggerRepository2, RendererSupport, ThrowableR
* <p>
* 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.
+ * </p>
*
* @param name The name of the logger to retrieve.
*
@@ -274,9 +293,8 @@ public class Hierarchy implements LoggerRepository2, RendererSupport, ThrowableR
return getInstance(getContext(), name);
}
- @Override
- public Logger getLogger(final String name, final ClassLoader classLoader) {
- return getInstance(LogManager.getContext(classLoader), name);
+ Logger getLogger(final String name, final ClassLoader classLoader) {
+ return getInstance(getContext(classLoader), name);
}
/**
@@ -285,6 +303,7 @@ public class Hierarchy implements LoggerRepository2, RendererSupport, ThrowableR
* <p>
* If a logger of that name already exists, then it will be returned. Otherwise, a new logger will be instantiated by
* the <code>factory</code> parameter and linked with its existing ancestors as well as children.
+ * </p>
*
* @param name The name of the logger to retrieve.
* @param factory The factory that will make the new logger instance.
@@ -295,9 +314,8 @@ public class Hierarchy implements LoggerRepository2, RendererSupport, ThrowableR
return getInstance(getContext(), name, factory);
}
- @Override
- public Logger getLogger(final String name, final LoggerFactory factory, final ClassLoader classLoader) {
- return getInstance(LogManager.getContext(classLoader), name, factory);
+ Logger getLogger(final String name, final LoggerFactory factory, final ClassLoader classLoader) {
+ return getInstance(getContext(classLoader), name, factory);
}
/**
@@ -318,6 +336,10 @@ public class Hierarchy implements LoggerRepository2, RendererSupport, ThrowableR
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 <code>enable</code> state.
*
@@ -361,6 +383,7 @@ public class Hierarchy implements LoggerRepository2, RendererSupport, ThrowableR
*
* <p>
* Existing categories are not removed. They are just reset.
+ * </p>
*
* <p>
* This method should be used sparingly and with care as it will block all logging until it is completed.
@@ -370,6 +393,15 @@ public class Hierarchy implements LoggerRepository2, RendererSupport, ThrowableR
*/
@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);
@@ -413,13 +445,13 @@ public class Hierarchy implements LoggerRepository2, RendererSupport, ThrowableR
/**
* Enable logging for logging requests with level <code>l</code> or higher. By default all levels are enabled.
*
- * @param l The minimum level for which logging requests are sent to their appenders.
+ * @param level The minimum level for which logging requests are sent to their appenders.
*/
@Override
- public void setThreshold(final Level l) {
- if (l != null) {
- thresholdInt = l.level;
- threshold = l;
+ public void setThreshold(final Level level) {
+ if (level != null) {
+ thresholdInt = level.level;
+ threshold = level;
}
}
@@ -428,9 +460,9 @@ public class Hierarchy implements LoggerRepository2, RendererSupport, ThrowableR
*/
@Override
public void setThreshold(final String levelStr) {
- final Level l = Level.toLevel(levelStr, null);
- if (l != null) {
- setThreshold(l);
+ final Level level = Level.toLevel(levelStr, null);
+ if (level != null) {
+ setThreshold(level);
} else {
LogLog.warn("Could not convert [" + levelStr + "] to Level.");
}
@@ -440,8 +472,8 @@ public class Hierarchy implements LoggerRepository2, RendererSupport, ThrowableR
* {@inheritDoc}
*/
@Override
- public void setThrowableRenderer(final ThrowableRenderer renderer) {
- throwableRenderer = renderer;
+ public void setThrowableRenderer(final ThrowableRenderer throwableRenderer) {
+ this.throwableRenderer = throwableRenderer;
}
/**
@@ -461,26 +493,37 @@ public class Hierarchy implements LoggerRepository2, RendererSupport, ThrowableR
*/
@Override
public void shutdown() {
- 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();
- }
- }
+ 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);
+ }
}
/**
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 46bff05..b18c8f1 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
@@ -69,20 +69,30 @@ public final class LogManager {
LOG4J_CORE_PRESENT = checkLog4jCore();
//
// By default we use a DefaultRepositorySelector which always returns 'hierarchy'.
- Hierarchy hierarchy = new Hierarchy(new RootLogger(Level.DEBUG));
+ final Hierarchy hierarchy = new Hierarchy(new RootLogger(Level.DEBUG));
repositorySelector = new DefaultRepositorySelector(hierarchy);
}
private static boolean checkLog4jCore() {
try {
return Class.forName("org.apache.logging.log4j.core.LoggerContext") != null;
- } catch (Exception ex) {
+ } catch (final Throwable ex) {
return false;
}
}
+ /**
+ * 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 getLoggerRepository().exists(name);
+ return exists(name, StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ static Logger exists(final String name, final ClassLoader classLoader) {
+ return getHierarchy().exists(name, classLoader);
}
/**
@@ -98,7 +108,7 @@ public final class LogManager {
/**
* Gets an enumeration of the current loggers.
- *
+ *
* @return an enumeration of the current loggers.
*/
@SuppressWarnings("rawtypes")
@@ -116,27 +126,42 @@ public final class LogManager {
// @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) {
- // Depth 2 gets the call site of this method.
- return getLoggerRepository2().getLogger(clazz.getName(), StackLocatorUtil.getCallerClassLoader(2));
+ 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) {
- // Depth 2 gets the call site of this method.
- return getLoggerRepository2().getLogger(name, StackLocatorUtil.getCallerClassLoader(2));
+ 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) {
- return getLoggerRepository2().getLogger(name, 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) {
- // Depth 2 gets the call site of this method.
- return getLoggerRepository2().getLogger(name, factory, StackLocatorUtil.getCallerClassLoader(2));
+ 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) {
- return getLoggerRepository2().getLogger(name, factory, classLoader);
+ final Hierarchy hierarchy = getHierarchy();
+ return hierarchy != null ? hierarchy.getLogger(name, factory, classLoader) : getLoggerRepository().getLogger(name, factory);
}
public static LoggerRepository getLoggerRepository() {
@@ -146,48 +171,62 @@ public final class LogManager {
return repositorySelector.getLoggerRepository();
}
- static LoggerRepository2 getLoggerRepository2() {
- // TODO Hack
- return (LoggerRepository2) getLoggerRepository();
+ /**
+ * Gets the root logger.
+ */
+ public static Logger getRootLogger() {
+ return getRootLogger(StackLocatorUtil.getCallerClassLoader(2));
}
- public static Logger getRootLogger() {
- return getLoggerRepository2().getRootLogger();
+ 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() {
+ static void reconfigure(final ClassLoader classLoader) {
if (isLog4jCorePresent()) {
- ContextUtil.reconfigure(Hierarchy.getContext());
+ ContextUtil.reconfigure(LogManager.getContext(classLoader));
}
}
- /**
- * No-op implementation.
- */
public static void resetConfiguration() {
- // noop
+ resetConfiguration(StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ static void resetConfiguration(final ClassLoader classLoader) {
+ final Hierarchy hierarchy = getHierarchy();
+ if (hierarchy != null) {
+ hierarchy.resetConfiguration(classLoader);
+ } else {
+ getLoggerRepository().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 {
- // noop
+ if (selector == null) {
+ throw new IllegalArgumentException("RepositorySelector must be non-null.");
+ }
+ LogManager.repositorySelector = selector;
}
/**
- * No-op implementation.
+ * Shuts down the current configuration.
*/
public static void shutdown() {
- // noop
+ 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/LoggerRepository2.java b/log4j-1.2-api/src/main/java/org/apache/log4j/LoggerRepository2.java
deleted file mode 100644
index 30c88ee..0000000
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/LoggerRepository2.java
+++ /dev/null
@@ -1,31 +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.log4j;
-
-import org.apache.log4j.spi.LoggerFactory;
-import org.apache.log4j.spi.LoggerRepository;
-
-/**
- * A LoggerRepository that accounts for the caller's class loader.
- */
-interface LoggerRepository2 extends LoggerRepository {
-
- Logger getLogger(String name, ClassLoader classLoader);
-
- Logger getLogger(String name, LoggerFactory factory, ClassLoader classLoader);
-}
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 808220a..45a96d4 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,114 +16,657 @@
*/
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.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.util.StackLocatorUtil;
/**
- * A configurator for properties.
+ * Configures Log4j from properties.
*/
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 <code>filename</code> 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";
+
+ /**
+ * Key for specifying the {@link org.apache.log4j.spi.LoggerFactory LoggerFactory}. Currently set to
+ * "<code>log4j.loggerFactory</code>".
+ */
+ public static final String LOGGER_FACTORY_KEY = "log4j.loggerFactory";
+
+ /**
+ * If property set to true, then hierarchy will be reset before configuration.
+ */
+ 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 <code>properties</code>.
+ * Reads configuration options from <code>properties</code>.
*
* See {@link #doConfigure(String, LoggerRepository)} for the expected format.
*
* @param properties The properties
*/
public static void configure(final Properties properties) {
+ new PropertyConfigurator().doConfigure(properties, LogManager.getLoggerRepository(), StackLocatorUtil.getCallerClassLoader(2));
}
/**
- * Read configuration options from configuration file.
+ * Reads configuration options from configuration file.
*
- * @param configFileName The configuration file.
+ * @param fileName The configuration file.
*/
- public static void configure(final String configFileName) {
+ public static void configure(final String fileName) {
+ new PropertyConfigurator().doConfigure(fileName, LogManager.getLoggerRepository(), StackLocatorUtil.getCallerClassLoader(2));
}
/**
- * Read configuration options from url <code>configURL</code>.
+ * Reads configuration options from url <code>configURL</code>.
*
* @param configURL The configuration URL
*/
public static void configure(final URL configURL) {
+ new PropertyConfigurator().doConfigure(configURL, LogManager.getLoggerRepository(), StackLocatorUtil.getCallerClassLoader(2));
}
/**
- * Like {@link #configureAndWatch(String, long)} except that the
- * default delay as defined by FileWatchdog.DEFAULT_DELAY is
- * used.
+ * Like {@link #configureAndWatch(String, long)} except that the default delay as defined by FileWatchdog.DEFAULT_DELAY
+ * is used.
*
* @param configFilename A file in key=value format.
*/
public static void configureAndWatch(final String configFilename) {
+ configureAndWatch(configFilename, FileWatchdog.DEFAULT_DELAY, StackLocatorUtil.getCallerClassLoader(2));
}
/**
- * Read the configuration file <code>configFilename</code> if it
- * exists. Moreover, a thread will be created that will periodically
- * check if <code>configFilename</code> has been created or
- * modified. The period is determined by the <code>delay</code>
- * argument. If a change or file creation is detected, then
- * <code>configFilename</code> is read to configure log4j.
+ * Reads the configuration file <code>configFilename</code> if it exists. Moreover, a thread will be created that will
+ * periodically check if <code>configFilename</code> has been created or modified. The period is determined by the
+ * <code>delay</code> argument. If a change or file creation is detected, then <code>configFilename</code> is read to
+ * configure log4j.
*
* @param configFilename A file in key=value format.
- * @param delay The delay in milliseconds to wait between each check.
+ * @param delayMillis The delay in milliseconds to wait between each check.
*/
- public static void configureAndWatch(final String configFilename, final long delay) {
+ 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;
}
/**
- * Read configuration options from an InputStream.
+ * 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 <code>Properties</code> 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 hierarchy The hierarchy
+ * @param loggerRepository The hierarchy
*/
@Override
- public void doConfigure(final InputStream inputStream, final LoggerRepository hierarchy) {
+ 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 <code>properties</code>.
+ * Reads configuration options from <code>properties</code>.
*
* See {@link #doConfigure(String, LoggerRepository)} for the expected format.
*
* @param properties The properties
- * @param hierarchy The hierarchy
+ * @param loggerRepository The hierarchy
*/
- public void doConfigure(final Properties properties, final LoggerRepository hierarchy) {
+ public void doConfigure(final Properties properties, final LoggerRepository loggerRepository) {
+ doConfigure(properties, loggerRepository, StackLocatorUtil.getCallerClassLoader(2));
}
/**
- * Read configuration options from configuration file.
+ * Reads configuration options from <code>properties</code>.
+ *
+ * See {@link #doConfigure(String, LoggerRepository)} for the expected format.
*
- * @param configFileName The configuration file
- * @param hierarchy The hierarchy
+ * @param properties The properties
+ * @param loggerRepository The hierarchy
*/
- public void doConfigure(final String configFileName, final LoggerRepository hierarchy) {
+ 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);
+ }
+
+ /**
+ * Reads configuration options from configuration file.
+ *
+ * @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 <code>configURL</code>.
*
- * @param configURL The configuration URL
- * @param hierarchy The hierarchy
+ * @param url The configuration URL
+ * @param loggerRepository The hierarchy
*/
@Override
- public void doConfigure(final URL configURL, final LoggerRepository hierarchy) {
+ 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 = url.openConnection();
+ urlConnection.setUseCaches(false);
+ 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);
+ 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() + "].");
+ appender.addFilter(filter);
+ }
+ } else {
+ LogLog.warn("Missing class definition for filter: [" + key + "]");
+ }
+ }
+ }
+
+ /**
+ * 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, (Level) Level.DEBUG));
+ }
+ 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) {
+ 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/config/PropertiesConfiguration.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java
index 8be58d4..a8d8614 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java
@@ -56,15 +56,21 @@ public class PropertiesConfiguration extends Log4j1Configuration {
private static final String ROOT_CATEGORY_PREFIX = "log4j.rootCategory";
private static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger";
private static final String APPENDER_PREFIX = "log4j.appender.";
- private static final String LOGGER_REF = "logger-ref";
- private static final String ROOT_REF = "root-ref";
+ private static final String LOGGER_REF = "logger-ref";
+ private static final String ROOT_REF = "root-ref";
private static final String APPENDER_REF_TAG = "appender-ref";
- public static final long DEFAULT_DELAY = 60000;
- public static final String DEBUG_KEY="log4j.debug";
+
+ /**
+ * If property set to true, then hierarchy will be reset before configuration.
+ */
+ private static final String RESET_KEY = "log4j.reset";
+
+ public static final String DEBUG_KEY = "log4j.debug";
private static final String INTERNAL_ROOT_NAME = "root";
private final Map<String, Appender> registry = new HashMap<>();
+ private Properties properties;
/**
* Constructs a new instance.
@@ -73,20 +79,44 @@ public class PropertiesConfiguration extends Log4j1Configuration {
* @param source The ConfigurationSource.
* @param monitorIntervalSeconds The monitoring interval in seconds.
*/
- public PropertiesConfiguration(final LoggerContext loggerContext, final ConfigurationSource source,
- final int monitorIntervalSeconds) {
+ public PropertiesConfiguration(final LoggerContext loggerContext, final ConfigurationSource source, final int monitorIntervalSeconds) {
super(loggerContext, source, monitorIntervalSeconds);
}
+ /**
+ * Constructs a new instance.
+ *
+ * @param loggerContext The LoggerContext.
+ * @param properties The ConfigurationSource, may be null.
+ */
+ public PropertiesConfiguration(final LoggerContext loggerContext, final Properties properties) {
+ super(loggerContext, ConfigurationSource.NULL_SOURCE, 0);
+ this.properties = properties;
+ }
+
+ /**
+ * Constructs a new instance.
+ *
+ * @param loggerContext The LoggerContext.
+ * @param properties The ConfigurationSource.
+ */
+ public PropertiesConfiguration(org.apache.logging.log4j.spi.LoggerContext loggerContext, Properties properties) {
+ this((LoggerContext) loggerContext, properties);
+ }
+
@Override
public void doConfigure() {
- final InputStream inputStream = getConfigurationSource().getInputStream();
- final Properties properties = new Properties();
- try {
- properties.load(inputStream);
- } catch (final Exception e) {
- LOGGER.error("Could not read configuration file [{}].", getConfigurationSource().toString(), e);
- return;
+ if (properties == null) {
+ properties = new Properties();
+ final InputStream inputStream = getConfigurationSource().getInputStream();
+ if (inputStream != null) {
+ try {
+ properties.load(inputStream);
+ } catch (final Exception e) {
+ LOGGER.error("Could not read configuration file [{}].", getConfigurationSource().toString(), e);
+ return;
+ }
+ }
}
// If we reach here, then the config file is alright.
doConfigure(properties);
@@ -108,27 +138,26 @@ public class PropertiesConfiguration extends Log4j1Configuration {
}
/**
- * Reads a configuration from a file. <b>The existing configuration is
- * not cleared nor reset.</b> If you require a different behavior,
- * then call {@link LogManager#resetConfiguration
- * resetConfiguration} method before calling
+ * Reads a configuration from a file. <b>The existing configuration is not cleared nor reset.</b> If you require a
+ * different behavior, then call {@link LogManager#resetConfiguration resetConfiguration} method before calling
* <code>doConfigure</code>.
*
- * <p>The configuration file consists of statements in the format
- * <code>key=value</code>. The syntax of different configuration
- * elements are discussed below.
+ * <p>
+ * The configuration file consists of statements in the format <code>key=value</code>. The syntax of different
+ * configuration elements are discussed below.
*
- * <p>The level value can consist of the string values OFF, FATAL,
- * ERROR, WARN, INFO, DEBUG, ALL or a <em>custom level</em> value. A
- * custom level value can be specified in the form
- * level#classname. By default the repository-wide threshold is set
- * to the lowest possible value, namely the level <code>ALL</code>.
+ * <p>
+ * The level value can consist of the string values OFF, FATAL, ERROR, WARN, INFO, DEBUG, ALL or a <em>custom level</em>
+ * value. A custom level value can be specified in the form level#classname. By default the repository-wide threshold is
+ * set to the lowest possible value, namely the level <code>ALL</code>.
* </p>
*
*
* <h3>Appender configuration</h3>
*
- * <p>Appender configuration syntax is:
+ * <p>
+ * Appender configuration syntax is:
+ *
* <pre>
* # For appender named <i>appenderName</i>, set its class.
* # Note: The appender name can contain dots.
@@ -140,8 +169,8 @@ public class PropertiesConfiguration extends Log4j1Configuration {
* log4j.appender.appenderName.optionN=valueN
* </pre>
* <p>
- * For each named appender you can configure its {@link Layout}. The
- * syntax for configuring an appender's layout is:
+ * For each named appender you can configure its {@link Layout}. The syntax for configuring an appender's layout is:
+ *
* <pre>
* log4j.appender.appenderName.layout=fully.qualified.name.of.layout.class
* log4j.appender.appenderName.layout.option1=value1
@@ -150,18 +179,19 @@ public class PropertiesConfiguration extends Log4j1Configuration {
* </pre>
* <p>
* The syntax for adding {@link Filter}s to an appender is:
+ *
* <pre>
* log4j.appender.appenderName.filter.ID=fully.qualified.name.of.filter.class
* log4j.appender.appenderName.filter.ID.option1=value1
* ...
* log4j.appender.appenderName.filter.ID.optionN=valueN
* </pre>
- * The first line defines the class name of the filter identified by ID;
- * subsequent lines with the same ID specify filter option - value
- * pairs. Multiple filters are added to the appender in the lexicographic
- * order of IDs.
+ *
+ * The first line defines the class name of the filter identified by ID; subsequent lines with the same ID specify
+ * filter option - value pairs. Multiple filters are added to the appender in the lexicographic order of IDs.
* <p>
* The syntax for adding an {@link ErrorHandler} to an appender is:
+ *
* <pre>
* log4j.appender.appenderName.errorhandler=fully.qualified.name.of.errorhandler.class
* log4j.appender.appenderName.errorhandler.appender-ref=appenderName
@@ -172,111 +202,97 @@ public class PropertiesConfiguration extends Log4j1Configuration {
*
* <h3>Configuring loggers</h3>
*
- * <p>The syntax for configuring the root logger is:
+ * <p>
+ * The syntax for configuring the root logger is:
+ *
* <pre>
* log4j.rootLogger=[level], appenderName, appenderName, ...
* </pre>
*
- * <p>This syntax means that an optional <em>level</em> can be
- * supplied followed by appender names separated by commas.
+ * <p>
+ * This syntax means that an optional <em>level</em> can be supplied followed by appender names separated by commas.
*
- * <p>The level value can consist of the string values OFF, FATAL,
- * ERROR, WARN, INFO, DEBUG, ALL or a <em>custom level</em> value. A
- * custom level value can be specified in the form
- * <code>level#classname</code>.
+ * <p>
+ * The level value can consist of the string values OFF, FATAL, ERROR, WARN, INFO, DEBUG, ALL or a <em>custom level</em>
+ * value. A custom level value can be specified in the form <code>level#classname</code>.
*
- * <p>If a level value is specified, then the root level is set
- * to the corresponding level. If no level value is specified,
+ * <p>
+ * If a level value is specified, then the root level is set to the corresponding level. If no level value is specified,
* then the root level remains untouched.
*
- * <p>The root logger can be assigned multiple appenders.
+ * <p>
+ * The root logger can be assigned multiple appenders.
*
- * <p>Each <i>appenderName</i> (separated by commas) will be added to
- * the root logger. The named appender is defined using the
- * appender syntax defined above.
+ * <p>
+ * Each <i>appenderName</i> (separated by commas) will be added to the root logger. The named appender is defined using
+ * the appender syntax defined above.
*
- * <p>For non-root categories the syntax is almost the same:
+ * <p>
+ * For non-root categories the syntax is almost the same:
+ *
* <pre>
* log4j.logger.logger_name=[level|INHERITED|NULL], appenderName, appenderName, ...
* </pre>
*
- * <p>The meaning of the optional level value is discussed above
- * in relation to the root logger. In addition however, the value
- * INHERITED can be specified meaning that the named logger should
- * inherit its level from the logger hierarchy.
+ * <p>
+ * The meaning of the optional level value is discussed above in relation to the root logger. In addition however, the
+ * value INHERITED can be specified meaning that the named logger should inherit its level from the logger hierarchy.
*
- * <p>If no level value is supplied, then the level of the
- * named logger remains untouched.
+ * <p>
+ * If no level value is supplied, then the level of the named logger remains untouched.
*
- * <p>By default categories inherit their level from the
- * hierarchy. However, if you set the level of a logger and later
- * decide that that logger should inherit its level, then you should
- * specify INHERITED as the value for the level value. NULL is a
- * synonym for INHERITED.
+ * <p>
+ * By default categories inherit their level from the hierarchy. However, if you set the level of a logger and later
+ * decide that that logger should inherit its level, then you should specify INHERITED as the value for the level value.
+ * NULL is a synonym for INHERITED.
*
- * <p>Similar to the root logger syntax, each <i>appenderName</i>
- * (separated by commas) will be attached to the named logger.
+ * <p>
+ * Similar to the root logger syntax, each <i>appenderName</i> (separated by commas) will be attached to the named
+ * logger.
*
- * <p>See the <a href="../../../../manual.html#additivity">appender
- * additivity rule</a> in the user manual for the meaning of the
- * <code>additivity</code> flag.
+ * <p>
+ * See the <a href="../../../../manual.html#additivity">appender additivity rule</a> in the user manual for the meaning
+ * of the <code>additivity</code> flag.
*
*
- * # Set options for appender named "A1".
- * # Appender "A1" will be a SyslogAppender
+ * # Set options for appender named "A1". # Appender "A1" will be a SyslogAppender
* log4j.appender.A1=org.apache.log4j.net.SyslogAppender
*
- * # The syslog daemon resides on www.abc.net
- * log4j.appender.A1.SyslogHost=www.abc.net
- *
- * # A1's layout is a PatternLayout, using the conversion pattern
- * # <b>%r %-5p %c{2} %M.%L %x - %m\n</b>. Thus, the log output will
- * # include # the relative time since the start of the application in
- * # milliseconds, followed by the level of the log request,
- * # followed by the two rightmost components of the logger name,
- * # followed by the callers method name, followed by the line number,
- * # the nested diagnostic context and finally the message itself.
- * # Refer to the documentation of {@link PatternLayout} for further information
- * # on the syntax of the ConversionPattern key.
- * log4j.appender.A1.layout=org.apache.log4j.PatternLayout
- * log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %c{2} %M.%L %x - %m\n
- *
- * # Set options for appender named "A2"
- * # A2 should be a RollingFileAppender, with maximum file size of 10 MB
- * # using at most one backup file. A2's layout is TTCC, using the
- * # ISO8061 date format with context printing enabled.
- * log4j.appender.A2=org.apache.log4j.RollingFileAppender
- * log4j.appender.A2.MaxFileSize=10MB
- * log4j.appender.A2.MaxBackupIndex=1
- * log4j.appender.A2.layout=org.apache.log4j.TTCCLayout
- * log4j.appender.A2.layout.ContextPrinting=enabled
- * log4j.appender.A2.layout.DateFormat=ISO8601
- *
- * # Root logger set to DEBUG using the A2 appender defined above.
- * log4j.rootLogger=DEBUG, A2
- *
- * # Logger definitions:
- * # The SECURITY logger inherits is level from root. However, it's output
- * # will go to A1 appender defined above. It's additivity is non-cumulative.
- * log4j.logger.SECURITY=INHERIT, A1
+ * # The syslog daemon resides on www.abc.net log4j.appender.A1.SyslogHost=www.abc.net
+ *
+ * # A1's layout is a PatternLayout, using the conversion pattern # <b>%r %-5p %c{2} %M.%L %x - %m\n</b>. Thus, the log
+ * output will # include # the relative time since the start of the application in # milliseconds, followed by the level
+ * of the log request, # followed by the two rightmost components of the logger name, # followed by the callers method
+ * name, followed by the line number, # the nested diagnostic context and finally the message itself. # Refer to the
+ * documentation of {@link PatternLayout} for further information # on the syntax of the ConversionPattern key.
+ * log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %c{2}
+ * %M.%L %x - %m\n
+ *
+ * # Set options for appender named "A2" # A2 should be a RollingFileAppender, with maximum file size of 10 MB # using
+ * at most one backup file. A2's layout is TTCC, using the # ISO8061 date format with context printing enabled.
+ * log4j.appender.A2=org.apache.log4j.RollingFileAppender log4j.appender.A2.MaxFileSize=10MB
+ * log4j.appender.A2.MaxBackupIndex=1 log4j.appender.A2.layout=org.apache.log4j.TTCCLayout
+ * log4j.appender.A2.layout.ContextPrinting=enabled log4j.appender.A2.layout.DateFormat=ISO8601
+ *
+ * # Root logger set to DEBUG using the A2 appender defined above. log4j.rootLogger=DEBUG, A2
+ *
+ * # Logger definitions: # The SECURITY logger inherits is level from root. However, it's output # will go to A1
+ * appender defined above. It's additivity is non-cumulative. log4j.logger.SECURITY=INHERIT, A1
* log4j.additivity.SECURITY=false
*
- * # Only warnings or above will be logged for the logger "SECURITY.access".
- * # Output will go to A1.
+ * # Only warnings or above will be logged for the logger "SECURITY.access". # Output will go to A1.
* log4j.logger.SECURITY.access=WARN
*
*
- * # The logger "class.of.the.day" inherits its level from the
- * # logger hierarchy. Output will go to the appender's of the root
- * # logger, A2 in this case.
- * log4j.logger.class.of.the.day=INHERIT
+ * # The logger "class.of.the.day" inherits its level from the # logger hierarchy. Output will go to the appender's of
+ * the root # logger, A2 in this case. log4j.logger.class.of.the.day=INHERIT
* </pre>
*
- * <p>Refer to the <b>setOption</b> method in each Appender and
- * Layout for class specific options.
+ * <p>
+ * Refer to the <b>setOption</b> method in each Appender and Layout for class specific options.
*
- * <p>Use the <code>#</code> or <code>!</code> characters at the
- * beginning of a line for comments.
+ * <p>
+ * Use the <code>#</code> or <code>!</code> characters at the beginning of a line for comments.
*
* <p>
*/
@@ -297,6 +313,12 @@ public class PropertiesConfiguration extends Log4j1Configuration {
final StatusConfiguration statusConfig = new StatusConfiguration().withStatus(status);
statusConfig.initialize();
+ // if log4j.reset=true then reset hierarchy
+ final String reset = properties.getProperty(RESET_KEY);
+ if (reset != null && OptionConverter.toBoolean(reset, false)) {
+ LogManager.resetConfiguration();
+ }
+
configureRoot(properties);
parseLoggers(properties);
@@ -368,7 +390,7 @@ public class PropertiesConfiguration extends Log4j1Configuration {
/**
* This method must work for the root category as well.
*/
- private void parseLogger(final Properties props, final LoggerConfig logger, final String optionKey, final String loggerName, final String value) {
+ private void parseLogger(final Properties props, final LoggerConfig loggerConfig, final String optionKey, final String loggerName, final String value) {
LOGGER.debug("Parsing for [{}] with value=[{}].", loggerName, value);
// We must skip over ',' but not white space
@@ -386,9 +408,9 @@ public class PropertiesConfiguration extends Log4j1Configuration {
final String levelStr = st.nextToken();
LOGGER.debug("Level token is [{}].", levelStr);
- final org.apache.logging.log4j.Level level = levelStr == null ? org.apache.logging.log4j.Level.ERROR :
- OptionConverter.convertLevel(levelStr, org.apache.logging.log4j.Level.DEBUG);
- logger.setLevel(level);
+ final org.apache.logging.log4j.Level level = levelStr == null ? org.apache.logging.log4j.Level.ERROR
+ : OptionConverter.convertLevel(levelStr, org.apache.logging.log4j.Level.DEBUG);
+ loggerConfig.setLevel(level);
LOGGER.debug("Logger {} level set to {}", loggerName, level);
}
@@ -402,9 +424,8 @@ public class PropertiesConfiguration extends Log4j1Configuration {
LOGGER.debug("Parsing appender named \"{}\".", appenderName);
appender = parseAppender(props, appenderName);
if (appender != null) {
- LOGGER.debug("Adding appender named [{}] to loggerConfig [{}].", appenderName,
- logger.getName());
- logger.addAppender(getAppender(appenderName), null, null);
+ LOGGER.debug("Adding appender named [{}] to loggerConfig [{}].", appenderName, loggerConfig.getName());
+ loggerConfig.addAppender(getAppender(appenderName), null, null);
} else {
LOGGER.debug("Appender named [{}] not found.", appenderName);
}
@@ -436,8 +457,8 @@ public class PropertiesConfiguration extends Log4j1Configuration {
return appender;
}
- private Appender buildAppender(final String appenderName, final String className, final String prefix,
- final String layoutPrefix, final String filterPrefix, final Properties props) {
+ private Appender buildAppender(final String appenderName, final String className, final String prefix, final String layoutPrefix, final String filterPrefix,
+ final Properties props) {
final Appender appender = newInstanceOf(className, "Appender");
if (appender == null) {
return null;
@@ -453,7 +474,7 @@ public class PropertiesConfiguration extends Log4j1Configuration {
}
}
appender.addFilter(parseAppenderFilters(props, filterPrefix, appenderName));
- final String[] keys = new String[] { layoutPrefix };
+ final String[] keys = new String[] {layoutPrefix};
addProperties(appender, keys, props, prefix);
if (appender instanceof AppenderWrapper) {
addAppender(((AppenderWrapper) appender).getAppender());
@@ -487,14 +508,14 @@ public class PropertiesConfiguration extends Log4j1Configuration {
return layout;
}
- public ErrorHandler parseErrorHandler(final Properties props, final String errorHandlerPrefix,
- final String errorHandlerClass, final Appender appender) {
+ public ErrorHandler parseErrorHandler(final Properties props, final String errorHandlerPrefix, final String errorHandlerClass, final Appender appender) {
final ErrorHandler eh = newInstanceOf(errorHandlerClass, "ErrorHandler");
final String[] keys = new String[] {
- errorHandlerPrefix + "." + ROOT_REF,
- errorHandlerPrefix + "." + LOGGER_REF,
- errorHandlerPrefix + "." + APPENDER_REF_TAG
- };
+ // @formatter:off
+ errorHandlerPrefix + "." + ROOT_REF,
+ errorHandlerPrefix + "." + LOGGER_REF,
+ errorHandlerPrefix + "." + APPENDER_REF_TAG};
+ // @formatter:on
addProperties(eh, keys, props, errorHandlerPrefix);
return eh;
}
@@ -515,7 +536,6 @@ public class PropertiesConfiguration extends Log4j1Configuration {
PropertySetter.setProperties(obj, edited, prefix + ".");
}
-
public Filter parseAppenderFilters(final Properties props, final String filterPrefix, final String appenderName) {
// extract filters and filter options from props into a hashtable mapping
// the property name defining the filter class to a list of pre-parsed
@@ -577,13 +597,11 @@ public class PropertiesConfiguration extends Log4j1Configuration {
return filter;
}
-
private static <T> T newInstanceOf(final String className, final String type) {
try {
return LoaderUtil.newInstanceOf(className);
} catch (ReflectiveOperationException ex) {
- LOGGER.error("Unable to create {} {} due to {}:{}", type, className,
- ex.getClass().getSimpleName(), ex.getMessage());
+ LOGGER.error("Unable to create {} {} due to {}:{}", type, className, ex.getClass().getSimpleName(), ex.getMessage());
return null;
}
}
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 0000000..d14dcf9
--- /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/PropertyConfiguratorTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/PropertyConfiguratorTest.java
new file mode 100644
index 0000000..26812ec
--- /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());
+ }
+
+ 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()));
+ }
+}