summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKeith Wall <kwall@apache.org>2012-07-05 09:40:06 +0000
committerKeith Wall <kwall@apache.org>2012-07-05 09:40:06 +0000
commit8b555d057f483874d9384c15bd989c975d18e0b0 (patch)
treeb5fdf00f4a10c465da038df03d6cdeb357501881
parent179f46270e539569e7c57e763ccd8a49ccf09a84 (diff)
downloadqpid-python-8b555d057f483874d9384c15bd989c975d18e0b0.tar.gz
QPID-4109: Re-enable LoggingManagement MBean
* Re-wire up LoggingManagementMBean. * Centralise log4j specific functionality into LoggingFacade class (moving implementation from LoggingManagementMBean and QpidLog4JConfigurator together). * Implement unit-tests for MBean and Facade levels. Reenforce units tests with system tests testing logging management end to end. * Changed QpidBrokerTestCase so that log4j.configuration is _always_ used to obtain the log4j config file regardless of whether test type is spawned or internal (previously log4j.configuration was respected only for internal tests). This was require to be able to write a logging management system test that could safely change the contents of the log4j config without running the risk of effecting other tests. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1357528 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--qpid/java/broker-plugins/jmx/MANIFEST.MF1
-rw-r--r--qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/JMXService.java4
-rw-r--r--qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBean.java810
-rw-r--r--qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBeanTest.java525
-rw-r--r--qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/LoggingManagementTest.java149
-rw-r--r--qpid/java/broker/src/main/java/org/apache/log4j/xml/QpidLog4JConfigurator.java246
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/Broker.java8
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/logging/log4j/LoggingFacade.java579
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/logging/log4j/LoggingFacadeException.java45
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/OsgiSystemPackages.properties2
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/log4j/LoggingFacadeTest.java245
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/log4j/LoggingFacadeTest.log4j.xml41
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java4
-rw-r--r--qpid/java/test-profiles/java-bdb-spawn.0-10.testprofile2
-rw-r--r--qpid/java/test-profiles/java-bdb-spawn.0-8.testprofile2
-rw-r--r--qpid/java/test-profiles/java-bdb-spawn.0-9-1.testprofile2
-rw-r--r--qpid/java/test-profiles/java-bdb-spawn.0-9.testprofile2
-rw-r--r--qpid/java/test-profiles/java-bdb.0-10.testprofile2
-rw-r--r--qpid/java/test-profiles/java-bdb.0-8.testprofile2
-rw-r--r--qpid/java/test-profiles/java-bdb.0-9-1.testprofile2
-rw-r--r--qpid/java/test-profiles/java-bdb.0-9.testprofile2
-rw-r--r--qpid/java/test-profiles/java-dby-mem.0-10.testprofile2
-rw-r--r--qpid/java/test-profiles/java-dby-mem.0-8.testprofile2
-rw-r--r--qpid/java/test-profiles/java-dby-mem.0-9-1.testprofile2
-rw-r--r--qpid/java/test-profiles/java-dby-mem.0-9.testprofile2
-rw-r--r--qpid/java/test-profiles/java-dby-spawn.0-10.testprofile2
-rw-r--r--qpid/java/test-profiles/java-dby-spawn.0-8.testprofile2
-rw-r--r--qpid/java/test-profiles/java-dby-spawn.0-9-1.testprofile2
-rw-r--r--qpid/java/test-profiles/java-dby-spawn.0-9.testprofile2
-rw-r--r--qpid/java/test-profiles/java-dby.0-10.testprofile2
-rw-r--r--qpid/java/test-profiles/java-dby.0-8.testprofile2
-rw-r--r--qpid/java/test-profiles/java-dby.0-9-1.testprofile2
-rw-r--r--qpid/java/test-profiles/java-dby.0-9.testprofile2
-rw-r--r--qpid/java/test-profiles/java-mms-spawn.0-10.testprofile2
-rw-r--r--qpid/java/test-profiles/java-mms-spawn.0-8.testprofile2
-rw-r--r--qpid/java/test-profiles/java-mms-spawn.0-9-1.testprofile2
-rw-r--r--qpid/java/test-profiles/java-mms-spawn.0-9.testprofile2
-rw-r--r--qpid/java/test-profiles/java-mms.0-10.testprofile2
-rw-r--r--qpid/java/test-profiles/java-mms.0-8.testprofile2
-rw-r--r--qpid/java/test-profiles/java-mms.0-9-1.testprofile2
-rw-r--r--qpid/java/test-profiles/java-mms.0-9.testprofile2
-rw-r--r--qpid/java/test-profiles/testprofile.defaults2
42 files changed, 1421 insertions, 1296 deletions
diff --git a/qpid/java/broker-plugins/jmx/MANIFEST.MF b/qpid/java/broker-plugins/jmx/MANIFEST.MF
index b13ff7f132..268f11b67f 100644
--- a/qpid/java/broker-plugins/jmx/MANIFEST.MF
+++ b/qpid/java/broker-plugins/jmx/MANIFEST.MF
@@ -23,6 +23,7 @@ Import-Package: org.apache.qpid,
org.apache.qpid.server.binding,
org.apache.qpid.server.exchange,
org.apache.qpid.server.logging,
+ org.apache.qpid.server.logging.log4j,
org.apache.qpid.server.logging.actors,
org.apache.qpid.server.logging.messages,
org.apache.qpid.server.message,
diff --git a/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/JMXService.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/JMXService.java
index 7519cea4db..7a232d2584 100644
--- a/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/JMXService.java
+++ b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/JMXService.java
@@ -33,11 +33,13 @@ import javax.management.StandardMBean;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.log4j.Logger;
import org.apache.qpid.AMQException;
+import org.apache.qpid.server.jmx.mbeans.LoggingManagementMBean;
import org.apache.qpid.server.jmx.mbeans.UserManagementMBean;
import org.apache.qpid.server.jmx.mbeans.ConfigurationManagementMBean;
import org.apache.qpid.server.jmx.mbeans.ServerInformationMBean;
import org.apache.qpid.server.jmx.mbeans.Shutdown;
import org.apache.qpid.server.jmx.mbeans.VirtualHostMBean;
+import org.apache.qpid.server.logging.log4j.LoggingFacade;
import org.apache.qpid.server.model.Broker;
import org.apache.qpid.server.model.ConfigurationChangeListener;
import org.apache.qpid.server.model.ConfiguredObject;
@@ -58,6 +60,7 @@ public class JMXService implements ConfigurationChangeListener
private final Shutdown _shutdown;
private final ServerInformationMBean _serverInfo;
private final ConfigurationManagementMBean _configManagement;
+ private final LoggingManagementMBean _loggingManagement;
private final Map<ConfiguredObject, AMQManagedObject> _children = new HashMap<ConfiguredObject, AMQManagedObject>();
@@ -80,6 +83,7 @@ public class JMXService implements ConfigurationChangeListener
_shutdown = new Shutdown(_objectRegistry);
_serverInfo = new ServerInformationMBean(_objectRegistry, _broker);
_configManagement = new ConfigurationManagementMBean(_objectRegistry);
+ _loggingManagement = new LoggingManagementMBean(LoggingFacade.getCurrentInstance(), _objectRegistry);
}
public void start() throws IOException, ConfigurationException
diff --git a/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBean.java b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBean.java
index 9ff45979ca..0dac8ebe37 100644
--- a/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBean.java
+++ b/qpid/java/broker-plugins/jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBean.java
@@ -20,23 +20,14 @@
*/
package org.apache.qpid.server.jmx.mbeans;
-import org.apache.log4j.Level;
-import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
-import org.apache.log4j.xml.Log4jEntityResolver;
-import org.apache.log4j.xml.QpidLog4JConfigurator;
-import org.apache.log4j.xml.QpidLog4JConfigurator.IllegalLoggerLevelException;
-import org.apache.log4j.xml.QpidLog4JConfigurator.QpidLog4JSaxErrorHandler;
import org.apache.qpid.management.common.mbeans.LoggingManagement;
import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription;
import org.apache.qpid.server.jmx.AMQManagedObject;
import org.apache.qpid.server.jmx.ManagedObject;
import org.apache.qpid.server.jmx.ManagedObjectRegistry;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NodeList;
-import org.xml.sax.ErrorHandler;
-import org.xml.sax.SAXException;
+import org.apache.qpid.server.logging.log4j.LoggingFacade;
+import org.apache.qpid.server.logging.log4j.LoggingFacadeException;
import javax.management.JMException;
import javax.management.openmbean.CompositeData;
@@ -48,43 +39,23 @@ import javax.management.openmbean.SimpleType;
import javax.management.openmbean.TabularData;
import javax.management.openmbean.TabularDataSupport;
import javax.management.openmbean.TabularType;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-import java.io.File;
+
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Random;
-
-import static org.apache.log4j.xml.QpidLog4JConfigurator.LOCK;
-/** MBean class for BrokerLoggingManagerMBean. It implements all the management features exposed for managing logging. */
+/** MBean class for LoggingManagement. It implements all the management features exposed for managing logging. */
@MBeanDescription("Logging Management Interface")
public class LoggingManagementMBean extends AMQManagedObject implements LoggingManagement
{
+ public static final String INHERITED_PSUEDO_LOG_LEVEL = "INHERITED";
+ private static final Logger LOGGER = Logger.getLogger(LoggingManagementMBean.class);
+ private static final TabularType LOGGER_LEVEL_TABULAR_TYE;
+ private static final CompositeType LOGGER_LEVEL_COMPOSITE_TYPE;
- private static final Logger _logger = Logger.getLogger(LoggingManagementMBean.class);
- private String _log4jConfigFileName;
- private int _log4jLogWatchInterval;
- private static final String INHERITED = "INHERITED";
- private static final String[] LEVELS = new String[]{Level.ALL.toString(), Level.TRACE.toString(),
- Level.DEBUG.toString(), Level.INFO.toString(),
- Level.WARN.toString(), Level.ERROR.toString(),
- Level.FATAL.toString(),Level.OFF.toString(),
- INHERITED};
- private static TabularType _loggerLevelTabularType;
- private static CompositeType _loggerLevelCompositeType;
+ private final LoggingFacade _configurator;
static
{
@@ -92,741 +63,262 @@ public class LoggingManagementMBean extends AMQManagedObject implements LoggingM
{
OpenType[] loggerLevelItemTypes = new OpenType[]{SimpleType.STRING, SimpleType.STRING};
- _loggerLevelCompositeType = new CompositeType("LoggerLevelList", "Logger Level Data",
+ LOGGER_LEVEL_COMPOSITE_TYPE = new CompositeType("LoggerLevelList", "Logger Level Data",
COMPOSITE_ITEM_NAMES.toArray(new String[COMPOSITE_ITEM_NAMES.size()]),
COMPOSITE_ITEM_DESCRIPTIONS.toArray(new String[COMPOSITE_ITEM_DESCRIPTIONS.size()]),
loggerLevelItemTypes);
- _loggerLevelTabularType = new TabularType("LoggerLevel", "List of loggers with levels",
- _loggerLevelCompositeType,
+ LOGGER_LEVEL_TABULAR_TYE = new TabularType("LoggerLevel", "List of loggers with levels",
+ LOGGER_LEVEL_COMPOSITE_TYPE,
TABULAR_UNIQUE_INDEX.toArray(new String[TABULAR_UNIQUE_INDEX.size()]));
}
catch (OpenDataException e)
{
- _logger.error("Tabular data setup for viewing logger levels was incorrect.");
- _loggerLevelTabularType = null;
+ throw new ExceptionInInitializerError(e);
}
}
- public LoggingManagementMBean(String log4jConfigFileName,
- int log4jLogWatchInterval,
- ManagedObjectRegistry registry) throws JMException
+ public LoggingManagementMBean(LoggingFacade configurator, ManagedObjectRegistry registry) throws JMException
{
super(LoggingManagement.class, LoggingManagement.TYPE, registry);
- _log4jConfigFileName = log4jConfigFileName;
- _log4jLogWatchInterval = log4jLogWatchInterval;
register();
+ _configurator = configurator;
}
+ @Override
public String getObjectInstanceName()
{
return LoggingManagement.TYPE;
}
-
+
+ @Override
+ public ManagedObject getParentObject()
+ {
+ return null;
+ }
+
+ @Override
public Integer getLog4jLogWatchInterval()
{
- return _log4jLogWatchInterval;
+ return _configurator.getLog4jLogWatchInterval();
}
+ @Override
public String[] getAvailableLoggerLevels()
{
- return LEVELS;
- }
- @SuppressWarnings("unchecked")
- public synchronized boolean setRuntimeLoggerLevel(String logger, String level)
- {
- //check specified level is valid
- Level newLevel;
- try
- {
- newLevel = getLevel(level);
- }
- catch (Exception e)
- {
- return false;
- }
-
- //check specified logger exists
- Enumeration loggers = LogManager.getCurrentLoggers();
- Boolean loggerExists = false;
-
- while(loggers.hasMoreElements())
- {
- Logger log = (Logger) loggers.nextElement();
- if (log.getName().equals(logger))
- {
- loggerExists = true;
- break;
- }
- }
-
- if(!loggerExists)
- {
- return false;
- }
-
- //set the logger to the new level
- _logger.info("Setting level to " + level + " for logger: " + logger);
+ List<String> levels = _configurator.getAvailableLoggerLevels();
+ List<String> mbeanLevels = new ArrayList<String>(levels);
+ mbeanLevels.add(INHERITED_PSUEDO_LOG_LEVEL);
- Logger log = Logger.getLogger(logger);
- log.setLevel(newLevel);
-
- return true;
+ return mbeanLevels.toArray(new String[mbeanLevels.size()]);
}
-
- @SuppressWarnings("unchecked")
- public synchronized TabularData viewEffectiveRuntimeLoggerLevels()
- {
- if (_loggerLevelTabularType == null)
- {
- _logger.warn("TabluarData type not set up correctly");
- return null;
- }
-
- _logger.info("Getting levels for currently active log4j loggers");
-
- Enumeration loggers = LogManager.getCurrentLoggers();
- TabularData loggerLevelList = new TabularDataSupport(_loggerLevelTabularType);
-
- Logger logger;
- String loggerName;
- String level;
-
- try
- {
- while(loggers.hasMoreElements()){
- logger = (Logger) loggers.nextElement();
-
- loggerName = logger.getName();
- level = logger.getEffectiveLevel().toString();
-
- Object[] itemData = {loggerName, level};
- CompositeData loggerData = new CompositeDataSupport(_loggerLevelCompositeType,
- COMPOSITE_ITEM_NAMES.toArray(new String[COMPOSITE_ITEM_NAMES.size()]), itemData);
- loggerLevelList.put(loggerData);
- }
- }
- catch (OpenDataException e)
- {
- _logger.warn("Unable to create logger level list due to :" + e);
- return null;
- }
-
- return loggerLevelList;
-
- }
-
- public synchronized String getRuntimeRootLoggerLevel()
+ @Override
+ public TabularData viewEffectiveRuntimeLoggerLevels()
{
- Logger rootLogger = Logger.getRootLogger();
+ Map<String, String> levels = _configurator.retrieveRuntimeLoggersLevels();
+ return createTabularDataFromLevelsMap(levels);
+ }
- return rootLogger.getLevel().toString();
+ @Override
+ public String getRuntimeRootLoggerLevel()
+ {
+ return _configurator.retrieveRuntimeRootLoggerLevel();
}
- public synchronized boolean setRuntimeRootLoggerLevel(String level)
+ @Override
+ public boolean setRuntimeRootLoggerLevel(String level)
{
- Level newLevel;
try
{
- newLevel = getLevel(level);
- }
- catch (Exception e)
- {
- return false;
+ validateLevelNotAllowingInherited(level);
}
-
- if(newLevel == null)
+ catch (IllegalArgumentException iae)
{
- //A null Level reference implies inheritance. Setting the runtime RootLogger
- //to null is catastrophic (and prevented by Log4J at startup and runtime anyway).
+ LOGGER.warn(level + " is not a known level");
return false;
}
- _logger.info("Setting RootLogger level to " + level);
-
- Logger log = Logger.getRootLogger();
- log.setLevel(newLevel);
-
+ _configurator.setRuntimeRootLoggerLevel(level);
return true;
}
-
- //method to convert from a string to a log4j Level, throws exception if the given value is invalid
- private Level getLevel(String level) throws Exception
+
+ @Override
+ public boolean setRuntimeLoggerLevel(String logger, String level)
{
- if("null".equalsIgnoreCase(level) || INHERITED.equalsIgnoreCase(level))
+ String validatedLevel;
+ try
{
- //the string "null" or "inherited" signals to inherit from a parent logger,
- //using a null Level reference for the logger.
- return null;
+ validatedLevel = getValidateLevelAllowingInherited(level);
}
-
- Level newLevel = Level.toLevel(level);
-
- //above Level.toLevel call returns a DEBUG Level if the request fails. Check the result.
- if (newLevel.equals(Level.DEBUG) && !(level.equalsIgnoreCase("debug")))
+ catch (IllegalArgumentException iae)
{
- //received DEBUG but we did not ask for it, the Level request failed.
- throw new Exception("Invalid level name");
+ LOGGER.warn(level + " is not a known level");
+ return false;
}
-
- return newLevel;
- }
-
- //method to parse the XML configuration file, validating it in the process, and returning a DOM Document of the content.
- private static synchronized Document parseConfigFile(String fileName) throws IOException
- {
+
try
{
- LOCK.lock();
-
- //check file was specified, exists, and is readable
- if(fileName == null)
- {
- _logger.warn("Provided log4j XML configuration filename is null");
- throw new IOException("Provided log4j XML configuration filename is null");
- }
-
- File configFile = new File(fileName);
-
- if (!configFile.exists())
- {
- _logger.warn("The log4j XML configuration file could not be found: " + fileName);
- throw new IOException("The log4j XML configuration file could not be found");
- }
- else if (!configFile.canRead())
- {
- _logger.warn("The log4j XML configuration file is not readable: " + fileName);
- throw new IOException("The log4j XML configuration file is not readable");
- }
-
- //parse it
- DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
- DocumentBuilder docBuilder;
- Document doc;
-
- ErrorHandler errHandler = new QpidLog4JSaxErrorHandler();
- try
- {
- docFactory.setValidating(true);
- docBuilder = docFactory.newDocumentBuilder();
- docBuilder.setErrorHandler(errHandler);
- docBuilder.setEntityResolver(new Log4jEntityResolver());
- doc = docBuilder.parse(fileName);
- }
- catch (ParserConfigurationException e)
- {
- _logger.warn("Unable to parse the log4j XML file due to possible configuration error: " + e);
- //recommended that MBeans should use java.* and javax.* exceptions only
- throw new IOException("Unable to parse the log4j XML file due to possible configuration error: " + e.getMessage());
- }
- catch (SAXException e)
- {
- _logger.warn("The specified log4j XML file is invalid: " + e);
- //recommended that MBeans should use standard java.* and javax.* exceptions only
- throw new IOException("The specified log4j XML file is invalid: " + e.getMessage());
- }
- catch (IOException e)
- {
- _logger.warn("Unable to parse the specified log4j XML file" + e);
- throw new IOException("Unable to parse the specified log4j XML file: " + e.getMessage());
- }
-
- return doc;
+ _configurator.setRuntimeLoggerLevel(logger, validatedLevel);
}
- finally
+ catch (LoggingFacadeException e)
{
- LOCK.unlock();
+ LOGGER.error("Cannot set runtime logging level", e);
+ return false;
}
+ return true;
}
-
- private static synchronized boolean writeUpdatedConfigFile(String log4jConfigFileName, Document doc) throws IOException
+ @Override
+ public TabularData viewConfigFileLoggerLevels()
{
+ Map<String,String> levels;
try
{
- LOCK.lock();
-
- File log4jConfigFile = new File(log4jConfigFileName);
-
- if (!log4jConfigFile.canWrite())
- {
- _logger.warn("Specified log4j XML configuration file is not writable: " + log4jConfigFile);
- throw new IOException("Specified log4j XML configuration file is not writable");
- }
-
- Transformer transformer = null;
- try
- {
- transformer = TransformerFactory.newInstance().newTransformer();
- }
- catch (Exception e)
- {
- _logger.warn("Could not create an XML transformer: " +e);
- return false;
- }
-
- transformer.setOutputProperty(OutputKeys.INDENT, "yes");
- transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "log4j.dtd");
- DOMSource source = new DOMSource(doc);
-
- File tmp;
- Random r = new Random();
- do
- {
- tmp = new File(log4jConfigFile.getPath() + r.nextInt() + ".tmp");
- }
- while(tmp.exists());
-
- tmp.deleteOnExit();
-
- try
- {
- StreamResult result = new StreamResult(tmp);
- transformer.transform(source, result);
- }
- catch (TransformerException e)
- {
- _logger.warn("Could not transform the XML into new file: " +e);
- throw new IOException("Could not transform the XML into new file: " +e);
- }
-
- // Swap temp file in to replace existing configuration file.
- File old = new File(log4jConfigFile.getAbsoluteFile() + ".old");
- if (old.exists())
- {
- old.delete();
- }
-
- if(!log4jConfigFile.renameTo(old))
- {
- //unable to rename the existing file to the backup name
- _logger.error("Could not backup the existing log4j XML file");
- throw new IOException("Could not backup the existing log4j XML file");
- }
-
- if(!tmp.renameTo(log4jConfigFile))
- {
- //failed to rename the new file to the required filename
-
- if(!old.renameTo(log4jConfigFile))
- {
- //unable to return the backup to required filename
- _logger.error("Could not rename the new log4j configuration file into place, and unable to restore original file");
- throw new IOException("Could not rename the new log4j configuration file into place, and unable to restore original file");
- }
-
- _logger.error("Could not rename the new log4j configuration file into place");
- throw new IOException("Could not rename the new log4j configuration file into place");
- }
-
- return true;
+ levels = _configurator.retrieveConfigFileLoggersLevels();
}
- finally
+ catch (LoggingFacadeException e)
{
- LOCK.unlock();
+ LOGGER.error("Cannot determine logging levels", e);
+ return null;
}
- }
+ return createTabularDataFromLevelsMap(levels);
+ }
- /* The log4j XML configuration file DTD defines three possible element
- * combinations for specifying optional logger+level settings.
- * Must account for the following:
- *
- * <category name="x"> <priority value="y"/> </category> OR
- * <category name="x"> <level value="y"/> </category> OR
- * <logger name="x"> <level value="y"/> </logger>
- *
- * Noting also that the level/priority child element is optional too,
- * and not the only possible child element.
- */
-
- public static synchronized Map<String,String> retrieveConfigFileLoggersLevels(String fileName) throws IOException
+ @Override
+ public String getConfigFileRootLoggerLevel()throws IOException
{
try
{
- LOCK.lock();
-
- Document doc = parseConfigFile(fileName);
-
- HashMap<String,String> loggerLevelList = new HashMap<String,String>();
-
- //retrieve the 'category' element nodes
- NodeList categoryElements = doc.getElementsByTagName("category");
-
- String categoryName;
- String priority = null;
-
- for (int i = 0; i < categoryElements.getLength(); i++)
- {
- Element categoryElement = (Element) categoryElements.item(i);
- categoryName = categoryElement.getAttribute("name");
-
- //retrieve the category's mandatory 'priority' or 'level' element's value.
- //It may not be the only child node, so request by tag name.
- NodeList priorityElements = categoryElement.getElementsByTagName("priority");
- NodeList levelElements = categoryElement.getElementsByTagName("level");
-
- if (priorityElements.getLength() != 0)
- {
- Element priorityElement = (Element) priorityElements.item(0);
- priority = priorityElement.getAttribute("value");
- }
- else if (levelElements.getLength() != 0)
- {
- Element levelElement = (Element) levelElements.item(0);
- priority = levelElement.getAttribute("value");
- }
- else
- {
- //there is no exiting priority or level to view, move onto next category/logger
- continue;
- }
-
- loggerLevelList.put(categoryName, priority);
- }
-
- //retrieve the 'logger' element nodes
- NodeList loggerElements = doc.getElementsByTagName("logger");
-
- String loggerName;
- String level;
-
- for (int i = 0; i < loggerElements.getLength(); i++)
- {
- Element loggerElement = (Element) loggerElements.item(i);
- loggerName = loggerElement.getAttribute("name");
-
- //retrieve the logger's mandatory 'level' element's value
- //It may not be the only child node, so request by tag name.
- NodeList levelElements = loggerElement.getElementsByTagName("level");
-
- Element levelElement = (Element) levelElements.item(0);
- level = levelElement.getAttribute("value");
-
- loggerLevelList.put(loggerName, level);
- }
-
- return loggerLevelList;
+ return _configurator.retrieveConfigFileRootLoggerLevel().toUpperCase();
}
- finally
+ catch (LoggingFacadeException e)
{
- LOCK.unlock();
+ LOGGER.warn("The log4j configuration get config request was aborted: ", e);
+ throw new IOException("The log4j configuration get config request was aborted: " + e.getMessage());
}
}
- public synchronized TabularData viewConfigFileLoggerLevels() throws IOException
+ @Override
+ public boolean setConfigFileLoggerLevel(String logger, String level)
{
+ String validatedLevel;
try
{
- LOCK.lock();
-
- if (_loggerLevelTabularType == null)
- {
- _logger.warn("TabluarData type not set up correctly");
- return null;
- }
-
- _logger.info("Getting logger levels from log4j configuration file");
-
- TabularData loggerLevelList = new TabularDataSupport(_loggerLevelTabularType);
-
- Map<String,String> levels = retrieveConfigFileLoggersLevels(_log4jConfigFileName);
-
- for (Map.Entry<String,String> entry : levels.entrySet())
- {
- String loggerName = entry.getKey();
- String level = entry.getValue();
-
- try
- {
- Object[] itemData = {loggerName, level.toUpperCase()};
- CompositeData loggerData = new CompositeDataSupport(_loggerLevelCompositeType,
- COMPOSITE_ITEM_NAMES.toArray(new String[COMPOSITE_ITEM_NAMES.size()]), itemData);
- loggerLevelList.put(loggerData);
- }
- catch (OpenDataException e)
- {
- _logger.warn("Unable to create logger level list due to :" + e);
- return null;
- }
- }
-
- return loggerLevelList;
+ validatedLevel = getValidateLevelAllowingInherited(level);
}
- finally
+ catch (IllegalArgumentException iae)
{
- LOCK.unlock();
+ LOGGER.warn(level + " is not a known level");
+ return false;
}
- }
- public synchronized boolean setConfigFileLoggerLevel(String logger, String level) throws IOException
- {
try
{
- LOCK.lock();
-
- //check that the specified level is a valid log4j Level
- try
- {
- getLevel(level);
- }
- catch (Exception e)
- {
- //it isnt a valid level
- return false;
- }
-
- _logger.info("Setting level to " + level + " for logger '" + logger
- + "' in log4j xml configuration file: " + _log4jConfigFileName);
-
- Document doc = parseConfigFile(_log4jConfigFileName);
-
- //retrieve the 'category' and 'logger' element nodes
- NodeList categoryElements = doc.getElementsByTagName("category");
- NodeList loggerElements = doc.getElementsByTagName("logger");
-
- //collect them into a single elements list
- List<Element> logElements = new ArrayList<Element>();
-
- for (int i = 0; i < categoryElements.getLength(); i++)
- {
- logElements.add((Element) categoryElements.item(i));
- }
- for (int i = 0; i < loggerElements.getLength(); i++)
- {
- logElements.add((Element) loggerElements.item(i));
- }
-
- //try to locate the specified logger/category in the elements retrieved
- Element logElement = null;
- for (Element e : logElements)
- {
- if (e.getAttribute("name").equals(logger))
- {
- logElement = e;
- break;
- }
- }
-
- if (logElement == null)
- {
- //no loggers/categories with given name found, does not exist to update
- _logger.warn("Specified logger does not exist in the configuration file: " +logger);
- return false;
- }
-
- //retrieve the optional 'priority' or 'level' sub-element value.
- //It may not be the only child node, so request by tag name.
- NodeList priorityElements = logElement.getElementsByTagName("priority");
- NodeList levelElements = logElement.getElementsByTagName("level");
-
- Element levelElement = null;
- if (priorityElements.getLength() != 0)
- {
- levelElement = (Element) priorityElements.item(0);
- }
- else if (levelElements.getLength() != 0)
- {
- levelElement = (Element) levelElements.item(0);
- }
- else
- {
- //there is no exiting priority or level element to update
- return false;
- }
-
- //update the element with the new level/priority
- levelElement.setAttribute("value", level.toLowerCase());
-
- //output the new file
- return writeUpdatedConfigFile(_log4jConfigFileName, doc);
+ _configurator.setConfigFileLoggerLevel(logger, validatedLevel);
}
- finally
+ catch (LoggingFacadeException e)
{
- LOCK.unlock();
+ LOGGER.warn("The log4j configuration set config request was aborted: ", e);
+ return false;
}
+ return true;
}
-
- /* The log4j XML configuration file DTD defines 2 possible element
- * combinations for specifying the optional root logger level settings
- * Must account for the following:
- *
- * <root> <priority value="y"/> </root> OR
- * <root> <level value="y"/> </root>
- *
- * Noting also that the level/priority child element is optional too,
- * and not the only possible child element.
- */
-
- public static synchronized String retrieveConfigFileRootLoggerLevel(String fileName) throws IOException
+ @Override
+ public boolean setConfigFileRootLoggerLevel(String level)
{
try
{
- LOCK.lock();
-
- Document doc = parseConfigFile(fileName);
-
- //retrieve the optional 'root' element node
- NodeList rootElements = doc.getElementsByTagName("root");
-
- if (rootElements.getLength() == 0)
- {
- //there is no root logger definition
- return "N/A";
- }
-
- Element rootElement = (Element) rootElements.item(0);
-
- //retrieve the optional 'priority' or 'level' element value.
- //It may not be the only child node, so request by tag name.
- NodeList priorityElements = rootElement.getElementsByTagName("priority");
- NodeList levelElements = rootElement.getElementsByTagName("level");
- String priority = null;
-
- if (priorityElements.getLength() != 0)
- {
- Element priorityElement = (Element) priorityElements.item(0);
- priority = priorityElement.getAttribute("value");
- }
- else if(levelElements.getLength() != 0)
- {
- Element levelElement = (Element) levelElements.item(0);
- priority = levelElement.getAttribute("value");
- }
-
- if(priority != null)
- {
- return priority;
- }
- else
- {
- return "N/A";
- }
+ validateLevelNotAllowingInherited(level);
}
- finally
+ catch (IllegalArgumentException iae)
{
- LOCK.unlock();
+ LOGGER.warn(level + " is not a known level");
+ return false;
}
- }
-
- public synchronized String getConfigFileRootLoggerLevel() throws IOException
- {
- return retrieveConfigFileRootLoggerLevel(_log4jConfigFileName).toUpperCase();
- }
-
- public synchronized boolean setConfigFileRootLoggerLevel(String level) throws IOException
- {
+
try
{
- LOCK.lock();
-
- //check that the specified level is a valid log4j Level
- try
- {
- Level newLevel = getLevel(level);
- if(newLevel == null)
- {
- //A null Level reference implies inheritance. Setting the config file RootLogger
- //to "null" or "inherited" just ensures it defaults to DEBUG at startup as Log4J
- //prevents this catastrophic situation at startup and runtime anyway.
- return false;
- }
- }
- catch (Exception e)
- {
- //it isnt a valid level
- return false;
- }
-
- _logger.info("Setting level to " + level + " for the Root logger in " +
- "log4j xml configuration file: " + _log4jConfigFileName);
-
- Document doc = parseConfigFile(_log4jConfigFileName);
-
- //retrieve the optional 'root' element node
- NodeList rootElements = doc.getElementsByTagName("root");
-
- if (rootElements.getLength() == 0)
- {
- return false;
- }
-
- Element rootElement = (Element) rootElements.item(0);
-
- //retrieve the optional 'priority' or 'level' sub-element value.
- //It may not be the only child node, so request by tag name.
- NodeList priorityElements = rootElement.getElementsByTagName("priority");
- NodeList levelElements = rootElement.getElementsByTagName("level");
-
- Element levelElement = null;
- if (priorityElements.getLength() != 0)
- {
- levelElement = (Element) priorityElements.item(0);
- }
- else if (levelElements.getLength() != 0)
- {
- levelElement = (Element) levelElements.item(0);
- }
- else
- {
- //there is no exiting priority/level to update
- return false;
- }
-
- //update the element with the new level/priority
- levelElement.setAttribute("value", level);
-
- //output the new file
- return writeUpdatedConfigFile(_log4jConfigFileName, doc);
+ _configurator.setConfigFileRootLoggerLevel(level);
+ return true;
}
- finally
+ catch (LoggingFacadeException e)
{
- LOCK.unlock();
+ LOGGER.warn("The log4j configuration set config request was aborted: ", e);
+ return false;
}
}
- public synchronized void reloadConfigFile() throws IOException
+ @Override
+ public void reloadConfigFile() throws IOException
{
try
{
- LOCK.lock();
- QpidLog4JConfigurator.configure(_log4jConfigFileName);
- _logger.info("Applied log4j configuration from: " + _log4jConfigFileName);
+ _configurator.reload();
}
- catch (IllegalLoggerLevelException e)
+ catch (LoggingFacadeException e)
{
- _logger.warn("The log4j configuration reload request was aborted: " + e);
- //recommended that MBeans should use standard java.* and javax.* exceptions only
+ LOGGER.warn("The log4j configuration reload request was aborted: ", e);
throw new IOException("The log4j configuration reload request was aborted: " + e.getMessage());
}
- catch (ParserConfigurationException e)
- {
- _logger.warn("The log4j configuration reload request was aborted: " + e);
- throw new IOException("The log4j configuration reload request was aborted: " + e.getMessage());
- }
- catch (SAXException e)
+ }
+
+ private String getValidateLevelAllowingInherited(String level)
+ {
+ if(level == null
+ || "null".equalsIgnoreCase(level)
+ || INHERITED_PSUEDO_LOG_LEVEL.equalsIgnoreCase(level))
{
- _logger.warn("The log4j configuration reload request was aborted: " + e);
- //recommended that MBeans should use standard java.* and javax.* exceptions only
- throw new IOException("The log4j configuration reload request was aborted: " + e.getMessage());
+ //the string "null" or "inherited" signals to inherit from a parent logger,
+ //using a null Level reference for the logger.
+ return null;
}
- catch (IOException e)
+
+ validateLevelNotAllowingInherited(level);
+ return level;
+ }
+
+ private void validateLevelNotAllowingInherited(String level)
+ {
+ final List<String> availableLoggerLevels = _configurator.getAvailableLoggerLevels();
+ if (!availableLoggerLevels.contains(level)
+ && !availableLoggerLevels.contains(String.valueOf(level).toUpperCase()))
{
- _logger.warn("The log4j configuration reload request was aborted: " + e);
- throw new IOException("The log4j configuration reload request was aborted: " + e.getMessage());
+ throw new IllegalArgumentException(level + " not known");
}
- finally
+ }
+
+ private TabularData createTabularDataFromLevelsMap(Map<String, String> levels)
+ {
+ TabularData loggerLevelList = new TabularDataSupport(LOGGER_LEVEL_TABULAR_TYE);
+ for (Map.Entry<String,String> entry : levels.entrySet())
{
- LOCK.unlock();
+ String loggerName = entry.getKey();
+ String level = entry.getValue();
+
+ CompositeData loggerData = createRow(loggerName, level);
+ loggerLevelList.put(loggerData);
}
+ return loggerLevelList;
}
- @Override
- public ManagedObject getParentObject()
+
+ private CompositeData createRow(String loggerName, String level)
{
- return null;
+ Object[] itemData = {loggerName, level.toUpperCase()};
+ try
+ {
+ CompositeData loggerData = new CompositeDataSupport(LOGGER_LEVEL_COMPOSITE_TYPE,
+ COMPOSITE_ITEM_NAMES.toArray(new String[COMPOSITE_ITEM_NAMES.size()]), itemData);
+ return loggerData;
+ }
+ catch (OpenDataException ode)
+ {
+ // Should not happen
+ throw new RuntimeException(ode);
+ }
}
}
diff --git a/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBeanTest.java b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBeanTest.java
index 64b942c9a9..ae1be5db00 100644
--- a/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBeanTest.java
+++ b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/server/jmx/mbeans/LoggingManagementMBeanTest.java
@@ -20,415 +20,226 @@
*/
package org.apache.qpid.server.jmx.mbeans;
-import static org.mockito.Mockito.*;
-
-import org.apache.log4j.Level;
-import org.apache.log4j.Logger;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.Matchers.anyString;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
-import org.apache.qpid.server.jmx.ManagedObjectRegistry;
-import org.apache.qpid.server.util.InternalBrokerBaseCase;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.TabularData;
-import static org.apache.qpid.management.common.mbeans.LoggingManagement.LOGGER_LEVEL;
-import static org.apache.qpid.management.common.mbeans.LoggingManagement.LOGGER_NAME;
+import junit.framework.TestCase;
-import javax.management.JMException;
-import javax.management.openmbean.CompositeData;
-import javax.management.openmbean.TabularDataSupport;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
+import org.apache.qpid.management.common.mbeans.LoggingManagement;
+import org.apache.qpid.server.jmx.ManagedObjectRegistry;
+import org.apache.qpid.server.logging.log4j.LoggingFacade;
-public class LoggingManagementMBeanTest extends InternalBrokerBaseCase
+public class LoggingManagementMBeanTest extends TestCase
{
+ private static final String TEST_LEVEL1 = "LEVEL1";
+ private static final String TEST_LEVEL2 = "LEVEL2";
- private static final String TEST_LOGGER = "LoggingManagementMBeanTestLogger";
- private static final String TEST_LOGGER_CHILD1 = "LoggingManagementMBeanTestLogger.child1";
- private static final String TEST_LOGGER_CHILD2 = "LoggingManagementMBeanTestLogger.child2";
-
- private static final String TEST_CATEGORY_PRIORITY = "LogManMBeanTest.category.priority";
- private static final String TEST_CATEGORY_LEVEL = "LogManMBeanTest.category.level";
- private static final String TEST_LOGGER_LEVEL = "LogManMBeanTest.logger.level";
+ private LoggingManagementMBean _loggingMBean;
+ private LoggingFacade _mockLoggingFacade;
+ private ManagedObjectRegistry _mockManagedObjectRegistry;
- private static final String NEWLINE = System.getProperty("line.separator");
+ @Override
+ protected void setUp() throws Exception
+ {
+ _mockLoggingFacade = mock(LoggingFacade.class);
+ final List<String> listOfLevels = new ArrayList<String>()
+ {{
+ add(TEST_LEVEL1);
+ add(TEST_LEVEL2);
+ }};
+ when(_mockLoggingFacade.getAvailableLoggerLevels()).thenReturn(listOfLevels);
- private File _testConfigFile;
+ _mockManagedObjectRegistry = mock(ManagedObjectRegistry.class);
- private ManagedObjectRegistry _registry = mock(ManagedObjectRegistry.class);
+ _loggingMBean = new LoggingManagementMBean(_mockLoggingFacade, _mockManagedObjectRegistry);
+ }
- @Override
- public void setUp() throws Exception
+ public void testMBeanRegistersItself() throws Exception
{
- super.setUp();
- _testConfigFile = createTempTestLog4JConfig();
+ LoggingManagementMBean connectionMBean = new LoggingManagementMBean(_mockLoggingFacade, _mockManagedObjectRegistry);
+ verify(_mockManagedObjectRegistry).registerObject(connectionMBean);
}
- @Override
- public void tearDown() throws Exception
+ public void testLog4jLogWatchInterval() throws Exception
{
- File oldTestConfigFile = new File(_testConfigFile.getAbsolutePath() + ".old");
- if(oldTestConfigFile.exists())
- {
- oldTestConfigFile.delete();
- }
+ final Integer value = 5000;
+ when(_mockLoggingFacade.getLog4jLogWatchInterval()).thenReturn(value);
- _testConfigFile.delete();
-
- super.tearDown();
+ assertEquals("Unexpected watch interval",value, _loggingMBean.getLog4jLogWatchInterval());
}
- private File createTempTestLog4JConfig()
+ public void testGetAvailableLoggerLevels() throws Exception
{
- File tmpFile = null;
- try
- {
- tmpFile = File.createTempFile("LogManMBeanTestLog4jConfig", ".tmp");
- tmpFile.deleteOnExit();
-
- FileWriter fstream = new FileWriter(tmpFile);
- BufferedWriter writer = new BufferedWriter(fstream);
-
- writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+NEWLINE);
- writer.write("<!DOCTYPE log4j:configuration SYSTEM \"log4j.dtd\">"+NEWLINE);
-
- writer.write("<log4j:configuration xmlns:log4j=\"http://jakarta.apache.org/log4j/\" debug=\"null\" " +
- "threshold=\"null\">"+NEWLINE);
-
- writer.write(" <appender class=\"org.apache.log4j.ConsoleAppender\" name=\"STDOUT\">"+NEWLINE);
- writer.write(" <layout class=\"org.apache.log4j.PatternLayout\">"+NEWLINE);
- writer.write(" <param name=\"ConversionPattern\" value=\"%d %-5p [%t] %C{2} (%F:%L) - %m%n\"/>"+NEWLINE);
- writer.write(" </layout>"+NEWLINE);
- writer.write(" </appender>"+NEWLINE);
-
- //Example of a 'category' with a 'priority'
- writer.write(" <category additivity=\"true\" name=\"" + TEST_CATEGORY_PRIORITY +"\">"+NEWLINE);
- writer.write(" <priority value=\"info\"/>"+NEWLINE);
- writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE);
- writer.write(" </category>"+NEWLINE);
-
- //Example of a 'category' with a 'level'
- writer.write(" <category additivity=\"true\" name=\"" + TEST_CATEGORY_LEVEL +"\">"+NEWLINE);
- writer.write(" <level value=\"warn\"/>"+NEWLINE);
- writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE);
- writer.write(" </category>"+NEWLINE);
-
- //Example of a 'logger' with a 'level'
- writer.write(" <logger additivity=\"true\" name=\"" + TEST_LOGGER_LEVEL + "\">"+NEWLINE);
- writer.write(" <level value=\"error\"/>"+NEWLINE);
- writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE);
- writer.write(" </logger>"+NEWLINE);
-
- //'root' logger
- writer.write(" <root>"+NEWLINE);
- writer.write(" <priority value=\"info\"/>"+NEWLINE);
- writer.write(" <appender-ref ref=\"STDOUT\"/>"+NEWLINE);
- writer.write(" </root>"+NEWLINE);
-
- writer.write("</log4j:configuration>"+NEWLINE);
-
- writer.flush();
- writer.close();
- }
- catch (IOException e)
- {
- fail("Unable to create temporary test log4j configuration");
- }
-
- return tmpFile;
+ String[] actualLevels = _loggingMBean.getAvailableLoggerLevels();
+ assertEquals(3, actualLevels.length);
+ assertEquals(TEST_LEVEL1, actualLevels[0]);
+ assertEquals(TEST_LEVEL2, actualLevels[1]);
+ assertEquals(LoggingManagementMBean.INHERITED_PSUEDO_LOG_LEVEL, actualLevels[2]);
}
+ public void testViewEffectiveRuntimeLoggerLevels() throws Exception
+ {
+ Map<String, String> loggerLevels = new TreeMap<String, String>();
+ loggerLevels.put("a.b.D", TEST_LEVEL2);
+ loggerLevels.put("a.b.C", TEST_LEVEL1);
+ loggerLevels.put("a.b.c.E", TEST_LEVEL2);
+
+ when(_mockLoggingFacade.retrieveRuntimeLoggersLevels()).thenReturn(loggerLevels );
+
+ TabularData table = _loggingMBean.viewEffectiveRuntimeLoggerLevels();
+ assertEquals(3, table.size());
+
+ final CompositeData row1 = table.get(new String[] {"a.b.C"} );
+ final CompositeData row2 = table.get(new String[] {"a.b.D"} );
+ final CompositeData row3 = table.get(new String[] {"a.b.c.E"} );
+ assertChannelRow(row1, "a.b.C", TEST_LEVEL1);
+ assertChannelRow(row2, "a.b.D", TEST_LEVEL2);
+ assertChannelRow(row3, "a.b.c.E", TEST_LEVEL2);
+ }
+ public void testGetRuntimeRootLoggerLevel() throws Exception
+ {
+ when(_mockLoggingFacade.retrieveRuntimeRootLoggerLevel()).thenReturn(TEST_LEVEL1);
- //******* Test Methods ******* //
+ assertEquals(TEST_LEVEL1, _loggingMBean.getRuntimeRootLoggerLevel());
+ }
- public void testSetRuntimeLoggerLevel()
+ public void testSetRuntimeRootLoggerLevel() throws Exception
{
- LoggingManagementMBean lm = null;
- try
- {
- lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0, _registry);
- }
- catch (JMException e)
- {
- fail("Could not create test LoggingManagementMBean");
- }
-
- //create a parent test logger, set its level explicitly
- Logger log = Logger.getLogger(TEST_LOGGER);
- log.setLevel(Level.toLevel("info"));
-
- //create child1 test logger, check its *effective* level is the same as the parent, "info"
- Logger log1 = Logger.getLogger(TEST_LOGGER_CHILD1);
- assertTrue("Test logger's level was not the expected value",
- log1.getEffectiveLevel().toString().equalsIgnoreCase("info"));
-
- //now change its level to "warn"
- assertTrue("Failed to set logger level", lm.setRuntimeLoggerLevel(TEST_LOGGER_CHILD1, "warn"));
-
- //check the change, see its actual level is "warn
- assertTrue("Test logger's level was not the expected value",
- log1.getLevel().toString().equalsIgnoreCase("warn"));
-
- //try an invalid level
- assertFalse("Trying to set an invalid level succeded", lm.setRuntimeLoggerLevel(TEST_LOGGER_CHILD1, "made.up.level"));
+ _loggingMBean.setRuntimeRootLoggerLevel(TEST_LEVEL1);
+ verify(_mockLoggingFacade).setRuntimeRootLoggerLevel(TEST_LEVEL1);
}
- public void testSetRuntimeRootLoggerLevel()
+ public void testSetRuntimeRootLoggerLevelWhenLoggingLevelUnknown() throws Exception
{
- LoggingManagementMBean lm = null;
- try
- {
- lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0, _registry);
- }
- catch (JMException e)
- {
- fail("Could not create test LoggingManagementMBean");
- }
-
- Logger log = Logger.getRootLogger();
-
- //get current root logger level
- Level origLevel = log.getLevel();
-
- //change level twice to ensure a new level is actually selected
-
- //set root loggers level to info
- assertTrue("Failed to set root logger level", lm.setRuntimeRootLoggerLevel("debug"));
- //check it is now actually info
- Level currentLevel = log.getLevel();
- assertTrue("Logger level was not expected value", currentLevel.equals(Level.toLevel("debug")));
-
- //try an invalid level
- assertFalse("Trying to set an invalid level succeded", lm.setRuntimeRootLoggerLevel("made.up.level"));
-
- //set root loggers level to warn
- assertTrue("Failed to set logger level", lm.setRuntimeRootLoggerLevel("info"));
- //check it is now actually warn
- currentLevel = log.getLevel();
- assertTrue("Logger level was not expected value", currentLevel.equals(Level.toLevel("info")));
-
- //restore original level
- log.setLevel(origLevel);
+ boolean result = _loggingMBean.setRuntimeRootLoggerLevel("unknown");
+ assertFalse(result);
+ verify(_mockLoggingFacade, never()).setRuntimeRootLoggerLevel("unknown");
}
- public void testGetRuntimeRootLoggerLevel()
+ public void testSetRuntimeRootLoggerLevelWhenLoggingLevelInherited() throws Exception
{
- LoggingManagementMBean lm = null;
- try
- {
- lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0, _registry);
- }
- catch (JMException e)
- {
- fail("Could not create test LoggingManagementMBean");
- }
-
- Logger log = Logger.getRootLogger();
+ boolean result = _loggingMBean.setRuntimeRootLoggerLevel(LoggingManagementMBean.INHERITED_PSUEDO_LOG_LEVEL);
+ assertFalse(result);
+ verify(_mockLoggingFacade, never()).setRuntimeRootLoggerLevel(anyString());
+ }
- //get current root logger level
- Level origLevel = log.getLevel();
+ public void testSetRuntimeLoggerLevel() throws Exception
+ {
+ _loggingMBean.setRuntimeLoggerLevel("a.b.c.D", TEST_LEVEL1);
+ verify(_mockLoggingFacade).setRuntimeLoggerLevel("a.b.c.D", TEST_LEVEL1);
+ }
- //change level twice to ensure a new level is actually selected
+ public void testSetRuntimeLoggerLevelWhenLoggingLevelUnknown() throws Exception
+ {
+ boolean result = _loggingMBean.setRuntimeLoggerLevel("a.b.c.D", "unknown");
+ assertFalse(result);
+ verify(_mockLoggingFacade, never()).setRuntimeLoggerLevel(anyString(), anyString());
+ }
- //set root loggers level to debug
- log.setLevel(Level.toLevel("debug"));
- //check it is now actually debug
- assertTrue("Logger level was not expected value", lm.getRuntimeRootLoggerLevel().equalsIgnoreCase("debug"));
+ public void testSetRuntimeLoggerLevelWhenLoggingLevelInherited() throws Exception
+ {
+ boolean result = _loggingMBean.setRuntimeLoggerLevel("a.b.c.D", LoggingManagementMBean.INHERITED_PSUEDO_LOG_LEVEL);
+ assertTrue(result);
+ verify(_mockLoggingFacade).setRuntimeLoggerLevel("a.b.c.D", null);
+ }
+ public void testViewEffectiveConfigFileLoggerLevels() throws Exception
+ {
+ Map<String, String> loggerLevels = new TreeMap<String, String>();
+ loggerLevels.put("a.b.D", "level2");
+ loggerLevels.put("a.b.C", TEST_LEVEL1);
+ loggerLevels.put("a.b.c.E", "level2");
+
+ when(_mockLoggingFacade.retrieveConfigFileLoggersLevels()).thenReturn(loggerLevels );
+
+ TabularData table = _loggingMBean.viewConfigFileLoggerLevels();
+ assertEquals(3, table.size());
+
+ final CompositeData row1 = table.get(new String[] {"a.b.C"} );
+ final CompositeData row2 = table.get(new String[] {"a.b.D"} );
+ final CompositeData row3 = table.get(new String[] {"a.b.c.E"} );
+ assertChannelRow(row1, "a.b.C", TEST_LEVEL1);
+ assertChannelRow(row2, "a.b.D", TEST_LEVEL2);
+ assertChannelRow(row3, "a.b.c.E", TEST_LEVEL2);
+ }
- //set root loggers level to warn
- log.setLevel(Level.toLevel("info"));
- //check it is now actually warn
- assertTrue("Logger level was not expected value", lm.getRuntimeRootLoggerLevel().equalsIgnoreCase("info"));
+ public void testGetConfigFileRootLoggerLevel() throws Exception
+ {
+ when(_mockLoggingFacade.retrieveConfigFileRootLoggerLevel()).thenReturn(TEST_LEVEL1);
- //restore original level
- log.setLevel(origLevel);
+ assertEquals(TEST_LEVEL1, _loggingMBean.getConfigFileRootLoggerLevel());
}
- public void testViewEffectiveRuntimeLoggerLevels()
+ public void testSetConfigFileRootLoggerLevel() throws Exception
{
- LoggingManagementMBean lm = null;
- try
- {
- lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0, _registry);
- }
- catch (JMException e)
- {
- fail("Could not create test LoggingManagementMBean");
- }
-
- //(re)create a parent test logger, set its level explicitly
- Logger log = Logger.getLogger(TEST_LOGGER);
- log.setLevel(Level.toLevel("info"));
-
- //retrieve the current effective runtime logger level values
- TabularDataSupport levels = (TabularDataSupport) lm.viewEffectiveRuntimeLoggerLevels();
- Collection<Object> records = levels.values();
- Map<String,String> list = new HashMap<String,String>();
- for (Object o : records)
- {
- CompositeData data = (CompositeData) o;
- list.put(data.get(LOGGER_NAME).toString(), data.get(LOGGER_LEVEL).toString());
- }
-
- //check child2 does not exist already
- assertFalse("Did not expect this logger to exist already", list.containsKey(TEST_LOGGER_CHILD2));
-
- //create child2 test logger
- Logger log2 = Logger.getLogger(TEST_LOGGER_CHILD2);
-
- //retrieve the current effective runtime logger level values
- levels = (TabularDataSupport) lm.viewEffectiveRuntimeLoggerLevels();
- records = levels.values();
- list = new HashMap<String,String>();
- for (Object o : records)
- {
- CompositeData data = (CompositeData) o;
- list.put(data.get(LOGGER_NAME).toString(), data.get(LOGGER_LEVEL).toString());
- }
-
- //verify the parent and child2 loggers are present in returned values
- assertTrue(TEST_LOGGER + " logger was not in the returned list", list.containsKey(TEST_LOGGER));
- assertTrue(TEST_LOGGER_CHILD2 + " logger was not in the returned list", list.containsKey(TEST_LOGGER_CHILD2));
-
- //check child2's effective level is the same as the parent, "info"
- assertTrue("Test logger's level was not the expected value",
- list.get(TEST_LOGGER_CHILD2).equalsIgnoreCase("info"));
-
- //now change its level explicitly to "warn"
- log2.setLevel(Level.toLevel("warn"));
-
- //retrieve the current effective runtime logger level values
- levels = (TabularDataSupport) lm.viewEffectiveRuntimeLoggerLevels();
- records = levels.values();
- list = new HashMap<String,String>();
- for (Object o : records)
- {
- CompositeData data = (CompositeData) o;
- list.put(data.get(LOGGER_NAME).toString(), data.get(LOGGER_LEVEL).toString());
- }
-
- //check child2's effective level is now "warn"
- assertTrue("Test logger's level was not the expected value",
- list.get(TEST_LOGGER_CHILD2).equalsIgnoreCase("warn"));
+ when(_mockLoggingFacade.getAvailableLoggerLevels()).thenReturn(Collections.singletonList(TEST_LEVEL1));
+ _loggingMBean.setConfigFileRootLoggerLevel(TEST_LEVEL1);
+ verify(_mockLoggingFacade).setConfigFileRootLoggerLevel(TEST_LEVEL1);
}
- public void testViewAndSetConfigFileLoggerLevel() throws Exception
+ public void testSetConfigFileRootLoggerLevelWhenLoggingLevelUnknown() throws Exception
{
- LoggingManagementMBean lm =null;
- try
- {
- lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0, _registry);
- }
- catch (JMException e)
- {
- fail("Could not create test LoggingManagementMBean");
- }
-
- //retrieve the current values
- TabularDataSupport levels = (TabularDataSupport) lm.viewConfigFileLoggerLevels();
- Collection<Object> records = levels.values();
- Map<String,String> list = new HashMap<String,String>();
- for (Object o : records)
- {
- CompositeData data = (CompositeData) o;
- list.put(data.get(LOGGER_NAME).toString(), data.get(LOGGER_LEVEL).toString());
- }
-
- //check the 3 different types of logger definition are successfully retrieved before update
- assertTrue("Wrong number of items in returned list", list.size() == 3);
- assertTrue(TEST_CATEGORY_PRIORITY + " logger was not in the returned list", list.containsKey(TEST_CATEGORY_PRIORITY));
- assertTrue(TEST_CATEGORY_LEVEL + " logger was not in the returned list", list.containsKey(TEST_CATEGORY_LEVEL));
- assertTrue(TEST_LOGGER_LEVEL + " logger was not in the returned list", list.containsKey(TEST_LOGGER_LEVEL));
-
- //check that their level is as expected
- assertTrue(TEST_CATEGORY_PRIORITY + " logger's level was incorrect", list.get(TEST_CATEGORY_PRIORITY).equalsIgnoreCase("info"));
- assertTrue(TEST_CATEGORY_LEVEL + " logger's level was incorrect", list.get(TEST_CATEGORY_LEVEL).equalsIgnoreCase("warn"));
- assertTrue(TEST_LOGGER_LEVEL + " logger's level was incorrect", list.get(TEST_LOGGER_LEVEL).equalsIgnoreCase("error"));
-
- //increase their levels a notch to test the 3 different types of logger definition are successfully updated
- //change the category+priority to warn
- assertTrue("failed to set new level", lm.setConfigFileLoggerLevel(TEST_CATEGORY_PRIORITY, "warn"));
- //change the category+level to error
- assertTrue("failed to set new level", lm.setConfigFileLoggerLevel(TEST_CATEGORY_LEVEL, "error"));
- //change the logger+level to trace
- assertTrue("failed to set new level", lm.setConfigFileLoggerLevel(TEST_LOGGER_LEVEL, "trace"));
-
- //try an invalid level
- assertFalse("Use of an invalid logger level was successfull", lm.setConfigFileLoggerLevel(TEST_LOGGER_LEVEL, "made.up.level"));
-
- //try an invalid logger name
- assertFalse("Use of an invalid logger name was successfull", lm.setConfigFileLoggerLevel("made.up.logger.name", "info"));
-
- //retrieve the new values from the file and check them
- levels = (TabularDataSupport) lm.viewConfigFileLoggerLevels();
- records = levels.values();
- list = new HashMap<String,String>();
- for (Object o : records)
- {
- CompositeData data = (CompositeData) o;
- list.put(data.get(LOGGER_NAME).toString(), data.get(LOGGER_LEVEL).toString());
- }
-
- //check the 3 different types of logger definition are successfully retrieved after update
- assertTrue("Wrong number of items in returned list", list.size() == 3);
- assertTrue(TEST_CATEGORY_PRIORITY + " logger was not in the returned list", list.containsKey(TEST_CATEGORY_PRIORITY));
- assertTrue(TEST_CATEGORY_LEVEL + " logger was not in the returned list", list.containsKey(TEST_CATEGORY_LEVEL));
- assertTrue(TEST_LOGGER_LEVEL + " logger was not in the returned list", list.containsKey(TEST_LOGGER_LEVEL));
-
- //check that their level is as expected after the changes
- assertTrue(TEST_CATEGORY_PRIORITY + " logger's level was incorrect", list.get(TEST_CATEGORY_PRIORITY).equalsIgnoreCase("warn"));
- assertTrue(TEST_CATEGORY_LEVEL + " logger's level was incorrect", list.get(TEST_CATEGORY_LEVEL).equalsIgnoreCase("error"));
- assertTrue(TEST_LOGGER_LEVEL + " logger's level was incorrect", list.get(TEST_LOGGER_LEVEL).equalsIgnoreCase("trace"));
+ when(_mockLoggingFacade.getAvailableLoggerLevels()).thenReturn(Collections.singletonList(TEST_LEVEL1));
+ boolean result = _loggingMBean.setConfigFileRootLoggerLevel("unknown");
+ assertFalse(result);
+ verify(_mockLoggingFacade, never()).setConfigFileRootLoggerLevel("unknown");
}
- public void testGetAndSetConfigFileRootLoggerLevel() throws Exception
+ public void testSetConfigFileRootLoggerLevelWhenLoggingLevelInherited() throws Exception
{
- LoggingManagementMBean lm =null;
- try
- {
- lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 0, _registry);
- }
- catch (JMException e)
- {
- fail("Could not create test LoggingManagementMBean");
- }
-
- //retrieve the current value
- String level = lm.getConfigFileRootLoggerLevel();
+ when(_mockLoggingFacade.getAvailableLoggerLevels()).thenReturn(Collections.singletonList(TEST_LEVEL1));
+ boolean result = _loggingMBean.setConfigFileRootLoggerLevel(LoggingManagementMBean.INHERITED_PSUEDO_LOG_LEVEL);
+ assertFalse(result);
+ verify(_mockLoggingFacade, never()).setConfigFileRootLoggerLevel(anyString());
+ }
- //check the value was successfully retrieved before update
- assertTrue("Retrieved RootLogger level was incorrect", level.equalsIgnoreCase("info"));
+ public void testSetConfigFileLoggerLevel() throws Exception
+ {
+ when(_mockLoggingFacade.getAvailableLoggerLevels()).thenReturn(Collections.singletonList(TEST_LEVEL1));
+ _loggingMBean.setConfigFileLoggerLevel("a.b.c.D", TEST_LEVEL1);
+ verify(_mockLoggingFacade).setConfigFileLoggerLevel("a.b.c.D", TEST_LEVEL1);
+ }
- //try an invalid level
- assertFalse("Use of an invalid RootLogger level was successfull", lm.setConfigFileRootLoggerLevel("made.up.level"));
+ public void testSetConfigFileLoggerLevelWhenLoggingLevelUnknown() throws Exception
+ {
+ when(_mockLoggingFacade.getAvailableLoggerLevels()).thenReturn(Collections.singletonList(TEST_LEVEL1));
+ boolean result = _loggingMBean.setConfigFileLoggerLevel("a.b.c.D", "unknown");
+ assertFalse(result);
+ verify(_mockLoggingFacade, never()).setConfigFileLoggerLevel("a.b.c.D", "unknown");
+ }
- //change the level to warn
- assertTrue("Failed to set new RootLogger level", lm.setConfigFileRootLoggerLevel("warn"));
+ public void testSetConfigFileLoggerLevelWhenLoggingLevelInherited() throws Exception
+ {
+ when(_mockLoggingFacade.getAvailableLoggerLevels()).thenReturn(Collections.singletonList(TEST_LEVEL1));
+ boolean result = _loggingMBean.setConfigFileLoggerLevel("a.b.c.D", LoggingManagementMBean.INHERITED_PSUEDO_LOG_LEVEL);
+ assertTrue(result);
+ verify(_mockLoggingFacade).setConfigFileLoggerLevel("a.b.c.D", null);
+ }
- //retrieve the current value
- level = lm.getConfigFileRootLoggerLevel();
+ public void testReloadConfigFile() throws Exception
+ {
+ _loggingMBean.reloadConfigFile();
- //check the value was successfully retrieved after update
- assertTrue("Retrieved RootLogger level was incorrect", level.equalsIgnoreCase("warn"));
+ verify(_mockLoggingFacade).reload();
}
- public void testGetLog4jLogWatchInterval()
+ private void assertChannelRow(final CompositeData row, String logger, String level)
{
- LoggingManagementMBean lm =null;
- try
- {
- lm = new LoggingManagementMBean(_testConfigFile.getAbsolutePath(), 5000, _registry);
- }
- catch (JMException e)
- {
- fail("Could not create test LoggingManagementMBean");
- }
-
- assertTrue("Wrong value returned for logWatch period", lm.getLog4jLogWatchInterval() == 5000);
+ assertNotNull("No row for " + logger, row);
+ assertEquals("Unexpected logger name", logger, row.get(LoggingManagement.LOGGER_NAME));
+ assertEquals("Unexpected level", level, row.get(LoggingManagement.LOGGER_LEVEL));
}
-
}
diff --git a/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/LoggingManagementTest.java b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/LoggingManagementTest.java
new file mode 100644
index 0000000000..ac6730638e
--- /dev/null
+++ b/qpid/java/broker-plugins/jmx/src/test/java/org/apache/qpid/systest/management/jmx/LoggingManagementTest.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.qpid.systest.management.jmx;
+
+import java.io.File;
+import java.util.List;
+
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.TabularData;
+
+import org.apache.qpid.management.common.mbeans.LoggingManagement;
+import org.apache.qpid.server.jmx.mbeans.LoggingManagementMBeanTest;
+import org.apache.qpid.server.logging.log4j.LoggingFacadeTest;
+import org.apache.qpid.test.utils.JMXTestUtils;
+import org.apache.qpid.test.utils.QpidBrokerTestCase;
+import org.apache.qpid.util.FileUtils;
+import org.apache.qpid.util.LogMonitor;
+
+/**
+ * System test for Logging Management. <b>These tests rely on value set within
+ * test-profiles/log4j-test.xml</b>.
+ *
+ * @see LoggingManagementMBeanTest
+ * @see LoggingFacadeTest
+ *
+ */
+public class LoggingManagementTest extends QpidBrokerTestCase
+{
+ private JMXTestUtils _jmxUtils;
+ private LoggingManagement _loggingManagement;
+ private LogMonitor _monitor;
+
+ public void setUp() throws Exception
+ {
+ _jmxUtils = new JMXTestUtils(this);
+ _jmxUtils.setUp();
+
+ // System test normally run with log for4j test config from beneath test-profiles. We need to
+ // copy it as some of our tests write to this file.
+
+ File tmpLogFile = File.createTempFile("log4j" + "." + getName(), ".xml");
+ tmpLogFile.deleteOnExit();
+ FileUtils.copy(_logConfigFile, tmpLogFile);
+
+ _logConfigFile = tmpLogFile;
+
+ super.setUp();
+ _jmxUtils.open();
+
+ _loggingManagement = _jmxUtils.getLoggingManagement();
+ _monitor = new LogMonitor(_outputFile);
+ }
+
+ public void tearDown() throws Exception
+ {
+ try
+ {
+ if (_jmxUtils != null)
+ {
+ _jmxUtils.close();
+ }
+ }
+ finally
+ {
+ super.tearDown();
+ }
+ }
+
+ public void testViewEffectiveRuntimeLoggerLevels() throws Exception
+ {
+ final String qpidMainLogger = "org.apache.qpid";
+
+ TabularData table = _loggingManagement.viewEffectiveRuntimeLoggerLevels();
+ final CompositeData row = table.get(new String[] {qpidMainLogger} );
+ assertChannelRow(row, qpidMainLogger, "DEBUG");
+ }
+
+ public void testViewConfigFileLoggerLevels() throws Exception
+ {
+ final String operationalLoggingLogger = "qpid.message";
+
+ TabularData table = _loggingManagement.viewConfigFileLoggerLevels();
+ final CompositeData row = table.get(new String[] {operationalLoggingLogger} );
+ assertChannelRow(row, operationalLoggingLogger, "INFO");
+ }
+
+ public void testTurnOffOrgApacheQpidAtRuntime() throws Exception
+ {
+ final String logger = "org.apache.qpid";
+ _monitor.markDiscardPoint();
+ _loggingManagement.setRuntimeLoggerLevel(logger, "OFF");
+
+ List<String> matches = _monitor.findMatches("Setting level to OFF for logger 'org.apache.qpid'");
+ assertEquals(1, matches.size());
+
+ TabularData table = _loggingManagement.viewEffectiveRuntimeLoggerLevels();
+ final CompositeData row1 = table.get(new String[] {logger} );
+ assertChannelRow(row1, logger, "OFF");
+ }
+
+ public void testChangesToConfigFileBecomeEffectiveAfterReload() throws Exception
+ {
+ final String operationalLoggingLogger = "qpid.message";
+ assertEffectiveLoggingLevel(operationalLoggingLogger, "INFO");
+
+ _monitor.markDiscardPoint();
+ _loggingManagement.setConfigFileLoggerLevel(operationalLoggingLogger, "OFF");
+
+ List<String> matches = _monitor.findMatches("Setting level to OFF for logger 'qpid.message'");
+ assertEquals(1, matches.size());
+
+ assertEffectiveLoggingLevel(operationalLoggingLogger, "INFO");
+
+ _loggingManagement.reloadConfigFile();
+
+ assertEffectiveLoggingLevel(operationalLoggingLogger, "OFF");
+ }
+
+ private void assertEffectiveLoggingLevel(String operationalLoggingLogger, String expectedLevel)
+ {
+ TabularData table = _loggingManagement.viewEffectiveRuntimeLoggerLevels();
+ final CompositeData row1 = table.get(new String[] {operationalLoggingLogger} );
+ assertChannelRow(row1, operationalLoggingLogger, expectedLevel);
+ }
+
+ private void assertChannelRow(final CompositeData row, String logger, String level)
+ {
+ assertNotNull("No row for " + logger, row);
+ assertEquals("Unexpected logger name", logger, row.get(LoggingManagement.LOGGER_NAME));
+ assertEquals("Unexpected level", level, row.get(LoggingManagement.LOGGER_LEVEL));
+ }
+
+}
diff --git a/qpid/java/broker/src/main/java/org/apache/log4j/xml/QpidLog4JConfigurator.java b/qpid/java/broker/src/main/java/org/apache/log4j/xml/QpidLog4JConfigurator.java
deleted file mode 100644
index 79bedb2a7e..0000000000
--- a/qpid/java/broker/src/main/java/org/apache/log4j/xml/QpidLog4JConfigurator.java
+++ /dev/null
@@ -1,246 +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.xml;
-
-import org.apache.log4j.Level;
-import org.apache.log4j.Logger;
-import org.xml.sax.ErrorHandler;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import java.io.File;
-import java.io.IOException;
-import java.util.Map;
-import java.util.concurrent.locks.ReentrantLock;
-
-/**
- * Substitute for the Log4J XMLWatchdog (as used by DOMConfigurator.configureAndWatch)
- *
- * Extends the default behaviour with a strict parser check on the XML file before allowing the reconfiguration to proceed,
- * ensuring that any parser error or warning prevents initiation of a configuration update by Log4J, which aborts mid-update
- * upon fatal errors from the parser and proceeds in the event of 'regular' parser errors and warnings, in all cases allowing
- * startup to proceed with whatever half-baked configuration then exists.
- */
-public class QpidLog4JConfigurator
-{
- //lock to protect access to the configuration file
- //shared with LoggingManagementMBean
- public static final ReentrantLock LOCK = new ReentrantLock();
- private static Logger _logger;
- private static DOMConfigurator domConfig = new DOMConfigurator();
-
- private QpidLog4JConfigurator()
- {
- //no instances
- }
-
- public static void configure(String filename) throws IOException, ParserConfigurationException,
- SAXException, IllegalLoggerLevelException
- {
- try
- {
- LOCK.lock();
-
- parseXMLConfigFile(filename);
-
- DOMConfigurator.configure(filename);
-
- if(_logger == null)
- {
- _logger = Logger.getLogger(QpidLog4JConfigurator.class);
- }
- }
- finally
- {
- LOCK.unlock();
- }
- }
-
- public static void configureAndWatch(String filename, long delay) throws IOException, ParserConfigurationException,
- SAXException, IllegalLoggerLevelException
- {
- parseXMLConfigFile(filename);
-
- QpidLog4JXMLWatchdog watchdog = new QpidLog4JXMLWatchdog(filename);
- watchdog.setDelay(delay);
- watchdog.start();
- }
-
- private static void parseXMLConfigFile(String fileName) throws IOException, SAXException,
- ParserConfigurationException
- {
- try
- {
- LOCK.lock();
-
- //check file was specified, exists, and is readable
- if(fileName == null)
- {
- throw new IOException("Provided log4j XML configuration filename was null");
- }
-
- File configFile = new File(fileName);
-
- if (!configFile.exists())
- {
- throw new IOException("The log4j XML configuration file does not exist: " + fileName);
- }
- else if (!configFile.canRead())
- {
- throw new IOException("The log4j XML configuration file is not readable: " + fileName);
- }
-
- //parse it
- DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
- DocumentBuilder docBuilder;
-
- ErrorHandler errHandler = new QpidLog4JSaxErrorHandler();
-
- docFactory.setValidating(true);
- docBuilder = docFactory.newDocumentBuilder();
- docBuilder.setErrorHandler(errHandler);
- docBuilder.setEntityResolver(new Log4jEntityResolver());
- docBuilder.parse(fileName);
- }
- finally
- {
- LOCK.unlock();
- }
- }
-
- public static class QpidLog4JSaxErrorHandler implements ErrorHandler
- {
- public void error(SAXParseException e) throws SAXException
- {
- if(_logger != null)
- {
- _logger.warn(constructMessage("Error parsing XML file", e));
- }
- else
- {
- System.err.println(constructMessage("Error parsing XML file", e));
- }
- }
-
- public void fatalError(SAXParseException e) throws SAXException
- {
- throw new SAXException(constructMessage("Fatal error parsing XML file", e));
- }
-
- public void warning(SAXParseException e) throws SAXException
- {
- if(_logger != null)
- {
- _logger.warn(constructMessage("Warning parsing XML file", e));
- }
- else
- {
- System.err.println(constructMessage("Warning parsing XML file", e));
- }
- }
-
- private static String constructMessage(final String msg, final SAXParseException ex)
- {
- return msg + ": Line " + ex.getLineNumber()+" column " +ex.getColumnNumber() + ": " + ex.getMessage();
- }
- }
-
- private static class QpidLog4JXMLWatchdog extends XMLWatchdog
- {
- public QpidLog4JXMLWatchdog(String filename)
- {
- super(filename);
- }
-
- public void doOnChange()
- {
- try
- {
- LOCK.lock();
-
- try
- {
- parseXMLConfigFile(filename);
- }
- catch (Exception e)
- {
- //logger will be instantiated following first configuration success, which has been pre-validated
- //and so the null check should never actually be required.
- if(_logger != null)
- {
- _logger.warn("Parsing the log4j XML configuration file generated errors/warnings. " +
- "The new configuration was not applied. Correct the issues to prompt " +
- "another update attempt: " + e.getMessage());
- }
- return;
- }
-
- //everything checked was ok, let the normal update process proceed
- super.doOnChange();
-
- //a configuration has now been applied, enable logging for future attempts
- if(_logger == null)
- {
- _logger = Logger.getLogger(QpidLog4JConfigurator.class);
- }
-
- _logger.info("Applied log4j configuration from: " + filename);
- }
- finally
- {
- LOCK.unlock();
- }
-
- }
- }
-
- private static void checkLevel(String loggerName, String levelString) throws IllegalLoggerLevelException
- {
- if("null".equalsIgnoreCase(levelString) || "inherited".equalsIgnoreCase(levelString))
- {
- //the string "null" signals to inherit from a parent logger
- return;
- }
-
- Level level = Level.toLevel(levelString);
-
- //above Level.toLevel call returns a DEBUG Level if the request fails. Check the result.
- if (level.equals(Level.DEBUG) && !(levelString.equalsIgnoreCase("debug")))
- {
- //received DEBUG but we did not ask for it, the Level request failed.
- throw new IllegalLoggerLevelException("Level '" + levelString + "' specified for Logger '" + loggerName + "' is invalid");
- }
- }
-
- public static class IllegalLoggerLevelException extends Exception
- {
- private static final long serialVersionUID = 1L;
-
- public IllegalLoggerLevelException(String msg)
- {
- super(msg);
- }
- }
-}
-
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/Broker.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/Broker.java
index c843ce6a23..d58a0d5bb4 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/Broker.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/Broker.java
@@ -29,13 +29,13 @@ import java.util.*;
import javax.net.ssl.SSLContext;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
-import org.apache.log4j.xml.QpidLog4JConfigurator;
import org.apache.qpid.server.configuration.ServerConfiguration;
import org.apache.qpid.server.configuration.ServerNetworkTransportConfiguration;
import org.apache.qpid.server.logging.SystemOutMessageLogger;
import org.apache.qpid.server.logging.actors.BrokerActor;
import org.apache.qpid.server.logging.actors.CurrentActor;
import org.apache.qpid.server.logging.actors.GenericActor;
+import org.apache.qpid.server.logging.log4j.LoggingFacade;
import org.apache.qpid.server.logging.messages.BrokerMessages;
import org.apache.qpid.server.protocol.AmqpProtocolVersion;
import org.apache.qpid.server.protocol.MultiVersionProtocolEngineFactory;
@@ -430,7 +430,7 @@ public class Broker
}
}
- private void configureLogging(File logConfigFile, long logWatchTime) throws InitException, IOException
+ private void configureLogging(File logConfigFile, int logWatchTime) throws InitException, IOException
{
if (logConfigFile.exists() && logConfigFile.canRead())
{
@@ -443,7 +443,7 @@ public class Broker
// log4j expects the watch interval in milliseconds
try
{
- QpidLog4JConfigurator.configureAndWatch(logConfigFile.getPath(), logWatchTime * 1000);
+ LoggingFacade.configureAndWatch(logConfigFile.getPath(), logWatchTime * 1000);
}
catch (Exception e)
{
@@ -454,7 +454,7 @@ public class Broker
{
try
{
- QpidLog4JConfigurator.configure(logConfigFile.getPath());
+ LoggingFacade.configure(logConfigFile.getPath());
}
catch (Exception e)
{
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/log4j/LoggingFacade.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/log4j/LoggingFacade.java
new file mode 100644
index 0000000000..931171b175
--- /dev/null
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/log4j/LoggingFacade.java
@@ -0,0 +1,579 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.logging.log4j;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.xml.DOMConfigurator;
+import org.apache.log4j.xml.Log4jEntityResolver;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * A facade over log4j that allows both the control of the runtime logging behaviour (that is, the ability to
+ * turn {@link Logger} on, off and control their {@link Level}, and the manipulation and reload
+ * of the log4j configuration file.
+ */
+public class LoggingFacade
+{
+ private static Logger LOGGER;
+ private static transient LoggingFacade _instance;
+ private final String _filename;
+ private final int _delay;
+
+ public static LoggingFacade configure(String filename) throws LoggingFacadeException
+ {
+ _instance = new LoggingFacade(filename);
+ return _instance;
+ }
+
+ public static LoggingFacade configureAndWatch(String filename, int delay) throws LoggingFacadeException
+ {
+ _instance = new LoggingFacade(filename, delay);
+ return _instance;
+ }
+
+ public static LoggingFacade getCurrentInstance()
+ {
+ return _instance;
+ }
+
+ private LoggingFacade(String filename)
+ {
+ DOMConfigurator.configure(filename);
+
+ if(LOGGER == null)
+ {
+ LOGGER = Logger.getLogger(LoggingFacade.class);
+ }
+ _filename = filename;
+ _delay = 0;
+ }
+
+ private LoggingFacade(String filename, int delay)
+ {
+ DOMConfigurator.configureAndWatch(filename, delay);
+
+ if(LOGGER == null)
+ {
+ LOGGER = Logger.getLogger(LoggingFacade.class);
+ }
+
+ _filename = filename;
+ _delay = delay;
+ }
+
+ public int getLog4jLogWatchInterval()
+ {
+ return _delay;
+ }
+
+ public synchronized void reload() throws LoggingFacadeException
+ {
+ DOMConfigurator.configure(_filename);
+ }
+
+ /** The log4j XML configuration file DTD defines three possible element
+ * combinations for specifying optional logger+level settings.
+ * Must account for the following:
+ *
+ * <category name="x"> <priority value="y"/> </category> OR
+ * <category name="x"> <level value="y"/> </category> OR
+ * <logger name="x"> <level value="y"/> </logger>
+ *
+ * Noting also that the level/priority child element is optional too,
+ * and not the only possible child element.
+ */
+ public synchronized Map<String,String> retrieveConfigFileLoggersLevels() throws LoggingFacadeException
+ {
+ try
+ {
+ Map<String,String> loggerLevelList = new HashMap<String,String>();
+ LOGGER.info("Getting logger levels from log4j configuration file");
+
+ Document doc = parseConfigFile(_filename);
+ List<Element> categoryOrLoggerElements = buildListOfCategoryOrLoggerElements(doc);
+
+ for (Element categoryOrLogger : categoryOrLoggerElements)
+ {
+
+ Element priorityOrLevelElement;
+ try
+ {
+ priorityOrLevelElement = getPriorityOrLevelElement(categoryOrLogger);
+ }
+ catch (LoggingFacadeException lfe)
+ {
+ //there is no exiting priority or level to view, move onto next category/logger
+ continue;
+ }
+
+ String categoryName = categoryOrLogger.getAttribute("name");
+ String priorityOrLevelValue = priorityOrLevelElement.getAttribute("value");
+ loggerLevelList.put(categoryName, priorityOrLevelValue);
+ }
+
+ return loggerLevelList;
+ }
+ catch (IOException e)
+ {
+ throw new LoggingFacadeException(e);
+ }
+ }
+
+ /**
+ * The log4j XML configuration file DTD defines 2 possible element
+ * combinations for specifying the optional root logger level settings
+ * Must account for the following:
+ *
+ * <root> <priority value="y"/> </root> OR
+ * <root> <level value="y"/> </root>
+ *
+ * Noting also that the level/priority child element is optional too,
+ * and not the only possible child element.
+ */
+ public synchronized String retrieveConfigFileRootLoggerLevel() throws LoggingFacadeException
+ {
+ try
+ {
+ Document doc = parseConfigFile(_filename);
+
+ //retrieve the optional 'root' element node
+ NodeList rootElements = doc.getElementsByTagName("root");
+
+ if (rootElements.getLength() == 0)
+ {
+ //there is no root logger definition
+ return "N/A";
+ }
+
+ Element rootElement = (Element) rootElements.item(0);
+ Element levelElement = getPriorityOrLevelElement(rootElement);
+
+ if(levelElement != null)
+ {
+ return levelElement.getAttribute("value");
+ }
+ else
+ {
+ return "N/A";
+ }
+ }
+ catch (IOException e)
+ {
+ throw new LoggingFacadeException(e);
+ }
+ }
+
+ public synchronized void setConfigFileLoggerLevel(String logger, String level) throws LoggingFacadeException
+ {
+ LOGGER.info("Setting level to " + level + " for logger '" + logger
+ + "' in log4j xml configuration file: " + _filename);
+
+ try
+ {
+ Document doc = parseConfigFile(_filename);
+
+ List<Element> logElements = buildListOfCategoryOrLoggerElements(doc);
+
+ //try to locate the specified logger/category in the elements retrieved
+ Element logElement = null;
+ for (Element e : logElements)
+ {
+ if (e.getAttribute("name").equals(logger))
+ {
+ logElement = e;
+ break;
+ }
+ }
+
+ if (logElement == null)
+ {
+ throw new LoggingFacadeException("Can't find logger " + logger);
+ }
+
+ Element levelElement = getPriorityOrLevelElement(logElement);
+
+ //update the element with the new level/priority
+ levelElement.setAttribute("value", level);
+
+ //output the new file
+ writeUpdatedConfigFile(_filename, doc);
+ }
+ catch (IOException ioe)
+ {
+ throw new LoggingFacadeException(ioe);
+ }
+ catch (TransformerConfigurationException e)
+ {
+ throw new LoggingFacadeException(e);
+ }
+ }
+
+ public synchronized void setConfigFileRootLoggerLevel(String level) throws LoggingFacadeException
+ {
+ try
+ {
+ LOGGER.info("Setting level to " + level + " for the Root logger in " +
+ "log4j xml configuration file: " + _filename);
+
+ Document doc = parseConfigFile(_filename);
+
+ //retrieve the optional 'root' element node
+ NodeList rootElements = doc.getElementsByTagName("root");
+
+ if (rootElements.getLength() == 0)
+ {
+ throw new LoggingFacadeException("Configuration contains no root element");
+ }
+
+ Element rootElement = (Element) rootElements.item(0);
+ Element levelElement = getPriorityOrLevelElement(rootElement);
+
+ //update the element with the new level/priority
+ levelElement.setAttribute("value", level);
+
+ //output the new file
+ writeUpdatedConfigFile(_filename, doc);
+ }
+ catch (IOException e)
+ {
+ throw new LoggingFacadeException(e);
+ }
+ catch (TransformerConfigurationException e)
+ {
+ throw new LoggingFacadeException(e);
+ }
+ }
+
+ public List<String> getAvailableLoggerLevels()
+ {
+ return new ArrayList<String>()
+ {{
+ add(Level.ALL.toString());
+ add(Level.TRACE.toString());
+ add(Level.DEBUG.toString());
+ add(Level.INFO.toString());
+ add(Level.WARN.toString());
+ add(Level.ERROR.toString());
+ add(Level.FATAL.toString());
+ add(Level.OFF.toString());
+ }};
+ }
+
+ public String retrieveRuntimeRootLoggerLevel()
+ {
+ Logger rootLogger = Logger.getRootLogger();
+ return rootLogger.getLevel().toString();
+ }
+
+ public void setRuntimeRootLoggerLevel(String level)
+ {
+ Level newLevel = Level.toLevel(level);
+
+ LOGGER.info("Setting RootLogger level to " + level);
+
+ Logger log = Logger.getRootLogger();
+ log.setLevel(newLevel);
+ }
+
+ public void setRuntimeLoggerLevel(String loggerName, String level) throws LoggingFacadeException
+ {
+ Level newLevel = level == null ? null : Level.toLevel(level);
+
+ Logger targetLogger = findRuntimeLogger(loggerName);
+
+ if(targetLogger == null)
+ {
+ throw new LoggingFacadeException("Can't find logger " + loggerName);
+ }
+
+ LOGGER.info("Setting level to " + newLevel + " for logger '" + targetLogger.getName() + "'");
+
+ targetLogger.setLevel(newLevel);
+ }
+
+ public Map<String,String> retrieveRuntimeLoggersLevels()
+ {
+ LOGGER.info("Getting levels for currently active log4j loggers");
+
+ Map<String, String> levels = new HashMap<String, String>();
+ @SuppressWarnings("unchecked")
+ Enumeration<Logger> loggers = LogManager.getCurrentLoggers();
+
+ while (loggers.hasMoreElements())
+ {
+ Logger logger = loggers.nextElement();
+ levels.put(logger.getName(), logger.getEffectiveLevel().toString());
+ }
+
+ return levels;
+ }
+
+ private void writeUpdatedConfigFile(String log4jConfigFileName, Document doc) throws IOException, TransformerConfigurationException
+ {
+ File log4jConfigFile = new File(log4jConfigFileName);
+
+ if (!log4jConfigFile.canWrite())
+ {
+ LOGGER.warn("Specified log4j XML configuration file is not writable: " + log4jConfigFile);
+ throw new IOException("Specified log4j XML configuration file is not writable");
+ }
+
+ Transformer transformer = null;
+ transformer = TransformerFactory.newInstance().newTransformer();
+
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "log4j.dtd");
+ DOMSource source = new DOMSource(doc);
+
+ File tmp;
+ Random r = new Random();
+
+ do
+ {
+ tmp = new File(log4jConfigFile.getAbsolutePath() + r.nextInt() + ".tmp");
+ }
+ while(tmp.exists());
+
+ tmp.deleteOnExit();
+
+ try
+ {
+ StreamResult result = new StreamResult(new FileOutputStream(tmp));
+ transformer.transform(source, result);
+ }
+ catch (TransformerException e)
+ {
+ LOGGER.warn("Could not transform the XML into new file: ", e);
+ throw new IOException("Could not transform the XML into new file: ", e);
+ }
+
+ // Swap temp file in to replace existing configuration file.
+ File old = new File(log4jConfigFile.getAbsoluteFile() + ".old");
+ if (old.exists())
+ {
+ old.delete();
+ }
+
+ if(!log4jConfigFile.renameTo(old))
+ {
+ //unable to rename the existing file to the backup name
+ LOGGER.error("Could not backup the existing log4j XML file");
+ throw new IOException("Could not backup the existing log4j XML file");
+ }
+
+ if(!tmp.renameTo(log4jConfigFile))
+ {
+ //failed to rename the new file to the required filename
+
+ if(!old.renameTo(log4jConfigFile))
+ {
+ //unable to return the backup to required filename
+ LOGGER.error("Could not rename the new log4j configuration file into place, and unable to restore original file");
+ throw new IOException("Could not rename the new log4j configuration file into place, and unable to restore original file");
+ }
+
+ LOGGER.error("Could not rename the new log4j configuration file into place");
+ throw new IOException("Could not rename the new log4j configuration file into place");
+ }
+ }
+
+ //method to parse the XML configuration file, validating it in the process, and returning a DOM Document of the content.
+ private static Document parseConfigFile(String fileName) throws IOException
+ {
+ //check file was specified, exists, and is readable
+ if(fileName == null)
+ {
+ LOGGER.warn("Provided log4j XML configuration filename is null");
+ throw new IOException("Provided log4j XML configuration filename is null");
+ }
+
+ File configFile = new File(fileName);
+
+ if (!configFile.exists())
+ {
+ LOGGER.warn("The log4j XML configuration file could not be found: " + fileName);
+ throw new IOException("The log4j XML configuration file could not be found");
+ }
+ else if (!configFile.canRead())
+ {
+ LOGGER.warn("The log4j XML configuration file is not readable: " + fileName);
+ throw new IOException("The log4j XML configuration file is not readable");
+ }
+
+ //parse it
+ DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder docBuilder;
+ Document doc;
+
+ ErrorHandler errHandler = new QpidLog4JSaxErrorHandler();
+ try
+ {
+ docFactory.setValidating(true);
+ docBuilder = docFactory.newDocumentBuilder();
+ docBuilder.setErrorHandler(errHandler);
+ docBuilder.setEntityResolver(new Log4jEntityResolver());
+ doc = docBuilder.parse(fileName);
+ }
+ catch (ParserConfigurationException e)
+ {
+ LOGGER.warn("Unable to parse the log4j XML file due to possible configuration error: ", e);
+ throw new IOException("Unable to parse the log4j XML file due to possible configuration error: ", e);
+ }
+ catch (SAXException e)
+ {
+ LOGGER.warn("The specified log4j XML file is invalid: ", e);
+ throw new IOException("The specified log4j XML file is invalid: ", e);
+ }
+ catch (IOException e)
+ {
+ LOGGER.warn("Unable to parse the specified log4j XML file", e);
+ throw new IOException("Unable to parse the specified log4j XML file: ", e);
+ }
+
+ return doc;
+ }
+
+ private Logger findRuntimeLogger(String loggerName)
+ {
+ Logger targetLogger = null;
+ @SuppressWarnings("unchecked")
+ Enumeration<Logger> loggers = LogManager.getCurrentLoggers();
+ while(loggers.hasMoreElements())
+ {
+ targetLogger = loggers.nextElement();
+ if (targetLogger.getName().equals(loggerName))
+ {
+ return targetLogger;
+ }
+ }
+ return null;
+ }
+
+ private List<Element> buildListOfCategoryOrLoggerElements(Document doc)
+ {
+ //retrieve the 'category' and 'logger' element nodes
+ NodeList categoryElements = doc.getElementsByTagName("category");
+ NodeList loggerElements = doc.getElementsByTagName("logger");
+
+ //collect them into a single elements list
+ List<Element> logElements = new ArrayList<Element>();
+
+ for (int i = 0; i < categoryElements.getLength(); i++)
+ {
+ logElements.add((Element) categoryElements.item(i));
+ }
+ for (int i = 0; i < loggerElements.getLength(); i++)
+ {
+ logElements.add((Element) loggerElements.item(i));
+ }
+ return logElements;
+ }
+
+ private Element getPriorityOrLevelElement(Element categoryOrLogger) throws LoggingFacadeException
+ {
+ //retrieve the optional 'priority' or 'level' sub-element value.
+ //It may not be the only child node, so request by tag name.
+ NodeList priorityElements = categoryOrLogger.getElementsByTagName("priority");
+ NodeList levelElements = categoryOrLogger.getElementsByTagName("level");
+
+ Element levelElement = null;
+ if (priorityElements.getLength() != 0)
+ {
+ levelElement = (Element) priorityElements.item(0);
+ }
+ else if (levelElements.getLength() != 0)
+ {
+ levelElement = (Element) levelElements.item(0);
+ }
+ else
+ {
+ throw new LoggingFacadeException("Configuration " + categoryOrLogger.getNodeName()
+ + " element contains neither priority nor level child");
+ }
+ return levelElement;
+ }
+
+ private static class QpidLog4JSaxErrorHandler implements ErrorHandler
+ {
+ public void error(SAXParseException e) throws SAXException
+ {
+ if(LOGGER != null)
+ {
+ LOGGER.warn(constructMessage("Error parsing XML file", e));
+ }
+ else
+ {
+ System.err.println(constructMessage("Error parsing XML file", e));
+ }
+ }
+
+ public void fatalError(SAXParseException e) throws SAXException
+ {
+ throw new SAXException(constructMessage("Fatal error parsing XML file", e));
+ }
+
+ public void warning(SAXParseException e) throws SAXException
+ {
+ if(LOGGER != null)
+ {
+ LOGGER.warn(constructMessage("Warning parsing XML file", e));
+ }
+ else
+ {
+ System.err.println(constructMessage("Warning parsing XML file", e));
+ }
+ }
+
+ private static String constructMessage(final String msg, final SAXParseException ex)
+ {
+ return msg + ": Line " + ex.getLineNumber()+" column " +ex.getColumnNumber() + ": " + ex.getMessage();
+ }
+ }
+}
+
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/log4j/LoggingFacadeException.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/log4j/LoggingFacadeException.java
new file mode 100644
index 0000000000..468b06be34
--- /dev/null
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/log4j/LoggingFacadeException.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.logging.log4j;
+
+public class LoggingFacadeException extends Exception
+{
+
+ public LoggingFacadeException()
+ {
+ super();
+ }
+
+ public LoggingFacadeException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+
+ public LoggingFacadeException(String message)
+ {
+ super(message);
+ }
+
+ public LoggingFacadeException(Throwable cause)
+ {
+ super(cause);
+ }
+
+}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/OsgiSystemPackages.properties b/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/OsgiSystemPackages.properties
index 4677d4b9e6..48c33821ee 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/OsgiSystemPackages.properties
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/OsgiSystemPackages.properties
@@ -105,6 +105,7 @@ org.apache.qpid.server.configuration.management=0.0.0
org.apache.qpid.server.connection=0.0.0
org.apache.qpid.server.exchange=0.0.0
org.apache.qpid.server.logging=0.0.0
+org.apache.qpid.server.logging.log4j=0.0.0
org.apache.qpid.server.logging.actors=0.0.0
org.apache.qpid.server.logging.messages=0.0.0
org.apache.qpid.server.logging.subjects=0.0.0
@@ -127,3 +128,4 @@ org.apache.qpid.server.virtualhost.plugins=0.0.0
org.apache.qpid.util=0.0.0
org.apache.qpid.server.store.berkeleydb=0.0.0
+
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/log4j/LoggingFacadeTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/log4j/LoggingFacadeTest.java
new file mode 100644
index 0000000000..f871baffe6
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/log4j/LoggingFacadeTest.java
@@ -0,0 +1,245 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.logging.log4j;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Level;
+import org.apache.qpid.util.FileUtils;
+
+import junit.framework.TestCase;
+
+public class LoggingFacadeTest extends TestCase
+{
+ private LoggingFacade _loggingFacade;
+ private String _log4jXmlFile;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ _log4jXmlFile = createTestLog4jXml();
+ _loggingFacade = LoggingFacade.configure(_log4jXmlFile);
+ }
+
+ public void testGetAvailableLoggerLevels() throws Exception
+ {
+ List<String> levels = _loggingFacade.getAvailableLoggerLevels();
+ assertTrue(levels.contains("ALL"));
+ assertTrue(levels.contains("TRACE"));
+ assertTrue(levels.contains("DEBUG"));
+ assertTrue(levels.contains("INFO"));
+ assertTrue(levels.contains("WARN"));
+ assertTrue(levels.contains("ERROR"));
+ assertTrue(levels.contains("FATAL"));
+ assertTrue(levels.contains("OFF"));
+ assertEquals(8, levels.size());
+ }
+
+ public void testRetrieveConfigFileRootLoggerLevel() throws Exception
+ {
+ String level = _loggingFacade.retrieveConfigFileRootLoggerLevel();
+ assertEquals(Level.WARN.toString(), level);
+ }
+
+ public void testSetConfigFileRootLoggerLevel() throws Exception
+ {
+ String oldLevel = _loggingFacade.retrieveConfigFileRootLoggerLevel();
+ assertEquals("WARN", oldLevel);
+
+ _loggingFacade.setConfigFileRootLoggerLevel("INFO");
+
+ String level = _loggingFacade.retrieveConfigFileRootLoggerLevel();
+ assertEquals("INFO", level);
+ }
+
+ public void testRetrieveConfigFileLoggerLevels() throws Exception
+ {
+ Map<String, String> levels = _loggingFacade.retrieveConfigFileLoggersLevels();
+ assertEquals(3, levels.size());
+ String abcLevel = levels.get("a.b.c");
+ String abc1Level = levels.get("a.b.c.1");
+ String abc2Level = levels.get("a.b.c.2");
+ assertEquals("INFO", abcLevel);
+ assertEquals("DEBUG", abc1Level);
+ assertEquals("TRACE", abc2Level);
+ }
+
+ public void testSetConfigFileLoggerLevels() throws Exception
+ {
+ final String loggerName = "a.b.c";
+
+ assertConfigFileLoggingLevel(loggerName, "INFO");
+
+ _loggingFacade.setConfigFileLoggerLevel(loggerName, "WARN");
+
+ Map<String, String> levels = _loggingFacade.retrieveConfigFileLoggersLevels();
+ String abcLevel = levels.get(loggerName);
+ assertEquals("WARN", abcLevel);
+ }
+
+ public void testSetConfigFileLoggerLevelsWhereLoggerDoesNotExist() throws Exception
+ {
+ try
+ {
+ _loggingFacade.setConfigFileLoggerLevel("does.not.exist", "WARN");
+ fail("Exception not thrown");
+ }
+ catch (LoggingFacadeException lfe)
+ {
+ // PASS
+ assertEquals("Can't find logger does.not.exist", lfe.getMessage());
+ }
+ }
+
+ public void testRetrieveRuntimeRootLoggerLevel() throws Exception
+ {
+ String level = _loggingFacade.retrieveRuntimeRootLoggerLevel();
+ assertEquals(Level.WARN.toString(), level);
+ }
+
+ public void testSetRuntimeRootLoggerLevel() throws Exception
+ {
+ String oldLevel = _loggingFacade.retrieveRuntimeRootLoggerLevel();
+ assertEquals("WARN", oldLevel);
+
+ _loggingFacade.setRuntimeRootLoggerLevel("INFO");
+
+ String level = _loggingFacade.retrieveRuntimeRootLoggerLevel();
+ assertEquals("INFO", level);
+ }
+
+ public void testRetrieveRuntimeLoggersLevels() throws Exception
+ {
+ Map<String, String> levels = _loggingFacade.retrieveRuntimeLoggersLevels();
+ // Don't assert size as implementation itself uses logging and we'd count its loggers too
+ String abcLevel = levels.get("a.b.c");
+ String abc1Level = levels.get("a.b.c.1");
+ String abc2Level = levels.get("a.b.c.2");
+ assertEquals("INFO", abcLevel);
+ assertEquals("DEBUG", abc1Level);
+ assertEquals("TRACE", abc2Level);
+ }
+
+ public void testSetRuntimeLoggerLevel() throws Exception
+ {
+ final String loggerName = "a.b.c";
+
+ assertRuntimeLoggingLevel(loggerName, "INFO");
+
+ _loggingFacade.setRuntimeLoggerLevel(loggerName, "WARN");
+
+ assertRuntimeLoggingLevel(loggerName, "WARN");
+ }
+
+ public void testSetRuntimeLoggerToInheritFromParent() throws Exception
+ {
+ final String parentLoggerName = "a.b.c";
+ final String childLoggerName = "a.b.c.1";
+
+ assertRuntimeLoggingLevel(parentLoggerName, "INFO");
+ assertRuntimeLoggingLevel(childLoggerName, "DEBUG");
+
+ _loggingFacade.setRuntimeLoggerLevel(childLoggerName, null);
+
+ assertRuntimeLoggingLevel(parentLoggerName, "INFO");
+ assertRuntimeLoggingLevel(childLoggerName, "INFO");
+ }
+
+ public void testSetRuntimeLoggerLevelsWhereLoggerDoesNotExist() throws Exception
+ {
+ final String loggerName = "does.not.exist2";
+
+ Map<String, String> oldLevels = _loggingFacade.retrieveRuntimeLoggersLevels();
+ assertFalse(oldLevels.containsKey(loggerName));
+
+ try
+ {
+ _loggingFacade.setRuntimeLoggerLevel(loggerName, "WARN");
+ fail("Exception not thrown");
+ }
+ catch (LoggingFacadeException lfe)
+ {
+ // PASS
+ assertEquals("Can't find logger " + loggerName, lfe.getMessage());
+ }
+
+ Map<String, String> levels = _loggingFacade.retrieveRuntimeLoggersLevels();
+ assertFalse(levels.containsKey(loggerName));
+ }
+
+ public void testReloadOfChangedLog4JFileUpdatesRuntimeLogLevel() throws Exception
+ {
+ final String loggerName = "a.b.c";
+
+ assertRuntimeLoggingLevel(loggerName, "INFO");
+ assertConfigFileLoggingLevel(loggerName, "INFO");
+
+ _loggingFacade.setConfigFileLoggerLevel(loggerName, "WARN");
+
+ assertRuntimeLoggingLevel(loggerName, "INFO");
+
+ _loggingFacade.reload();
+
+ assertRuntimeLoggingLevel(loggerName, "WARN");
+ }
+
+
+ public void testReloadOfLog4JFileRevertsRuntimeChanges() throws Exception
+ {
+ final String loggerName = "a.b.c";
+
+ assertRuntimeLoggingLevel(loggerName, "INFO");
+ assertConfigFileLoggingLevel(loggerName, "INFO");
+
+ _loggingFacade.setRuntimeLoggerLevel(loggerName, "WARN");
+
+ assertRuntimeLoggingLevel(loggerName, "WARN");
+
+ _loggingFacade.reload();
+
+ assertRuntimeLoggingLevel(loggerName, "INFO");
+ }
+
+ private void assertConfigFileLoggingLevel(final String loggerName, String expectedLevel) throws Exception
+ {
+ Map<String, String> levels = _loggingFacade.retrieveConfigFileLoggersLevels();
+ String actualLevel = levels.get(loggerName);
+ assertEquals(expectedLevel, actualLevel);
+ }
+
+ private void assertRuntimeLoggingLevel(final String loggerName, String expectedLevel) throws Exception
+ {
+ Map<String, String> levels = _loggingFacade.retrieveRuntimeLoggersLevels();
+ String actualLevel = levels.get(loggerName);
+ assertEquals(expectedLevel, actualLevel);
+ }
+
+ private String createTestLog4jXml() throws Exception
+ {
+ File dst = File.createTempFile("log4j." + getName(), "xml");
+ File filename = new File(getClass().getResource("LoggingFacadeTest.log4j.xml").toURI());
+ FileUtils.copy(filename, dst);
+ dst.deleteOnExit();
+ return dst.getAbsolutePath();
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/log4j/LoggingFacadeTest.log4j.xml b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/log4j/LoggingFacadeTest.log4j.xml
new file mode 100644
index 0000000000..62ec877d3d
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/log4j/LoggingFacadeTest.log4j.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ -
+ - Licensed to the Apache Software Foundation (ASF) under one
+ - or more contributor license agreements. See the NOTICE file
+ - distributed with this work for additional information
+ - regarding copyright ownership. The ASF licenses this file
+ - to you under the Apache License, Version 2.0 (the
+ - "License"); you may not use this file except in compliance
+ - with the License. You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing,
+ - software distributed under the License is distributed on an
+ - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ - KIND, either express or implied. See the License for the
+ - specific language governing permissions and limitations
+ - under the License.
+ -
+ --><!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="null" threshold="null">
+
+ <category additivity="true" name="a.b.c">
+ <priority value="INFO"/>
+ </category>
+
+ <logger additivity="true" name="a.b.c.1">
+ <level value="DEBUG"/>
+ </logger>
+
+ <logger additivity="true" name="a.b.c.2">
+ <level value="TRACE"/>
+ </logger>
+
+ <root>
+ <priority value="WARN"/>
+ </root>
+
+</log4j:configuration>
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java
index 056356cad7..aa909a6674 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java
@@ -89,6 +89,7 @@ public class QpidBrokerTestCase extends QpidTestCase
protected final static String QpidHome = System.getProperty("QPID_HOME");
protected File _configFile = new File(System.getProperty("broker.config"));
+ protected File _logConfigFile = new File(System.getProperty("log4j.configuration"));
protected static final Logger _logger = Logger.getLogger(QpidBrokerTestCase.class);
protected static final int LOGMONITOR_TIMEOUT = 5000;
@@ -364,6 +365,7 @@ public class QpidBrokerTestCase extends QpidTestCase
.replace("@SSL_PORT", "" + sslPort)
.replace("@MPORT", "" + getManagementPort(port))
.replace("@CONFIG_FILE", _configFile.toString())
+ .replace("@LOG_CONFIG_FILE", _logConfigFile.toString())
.replace("@EXCLUDES", protocolExcludesList)
.replace("@INCLUDES", protocolIncludesList);
}
@@ -413,7 +415,7 @@ public class QpidBrokerTestCase extends QpidTestCase
//Set the log config file, relying on the log4j.configuration system property
//set on the JVM by the JUnit runner task in module.xml.
- options.setLogConfigFile(new URL(System.getProperty("log4j.configuration")).getFile());
+ options.setLogConfigFile(_logConfigFile.getAbsolutePath());
Broker broker = new Broker();
_logger.info("starting internal broker (same JVM)");
diff --git a/qpid/java/test-profiles/java-bdb-spawn.0-10.testprofile b/qpid/java/test-profiles/java-bdb-spawn.0-10.testprofile
index 5795f11da9..35991645d8 100644
--- a/qpid/java/test-profiles/java-bdb-spawn.0-10.testprofile
+++ b/qpid/java/test-profiles/java-bdb-spawn.0-10.testprofile
@@ -19,7 +19,7 @@
broker.language=java
broker.type=spawned
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.protocol.excludes=--exclude-1-0 @PORT --exclude-1-0 @SSL_PORT
diff --git a/qpid/java/test-profiles/java-bdb-spawn.0-8.testprofile b/qpid/java/test-profiles/java-bdb-spawn.0-8.testprofile
index 853f1b8cc0..a4b74748e6 100644
--- a/qpid/java/test-profiles/java-bdb-spawn.0-8.testprofile
+++ b/qpid/java/test-profiles/java-bdb-spawn.0-8.testprofile
@@ -19,7 +19,7 @@
broker.language=java
broker.type=spawned
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.config=build/etc/config-systests-bdb.xml
diff --git a/qpid/java/test-profiles/java-bdb-spawn.0-9-1.testprofile b/qpid/java/test-profiles/java-bdb-spawn.0-9-1.testprofile
index 6c7dc37f7f..9cd70a4ea7 100644
--- a/qpid/java/test-profiles/java-bdb-spawn.0-9-1.testprofile
+++ b/qpid/java/test-profiles/java-bdb-spawn.0-9-1.testprofile
@@ -19,7 +19,7 @@
broker.language=java
broker.type=spawned
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.config=build/etc/config-systests-bdb.xml
diff --git a/qpid/java/test-profiles/java-bdb-spawn.0-9.testprofile b/qpid/java/test-profiles/java-bdb-spawn.0-9.testprofile
index 7d7c9b085b..5c0ad2baf3 100644
--- a/qpid/java/test-profiles/java-bdb-spawn.0-9.testprofile
+++ b/qpid/java/test-profiles/java-bdb-spawn.0-9.testprofile
@@ -19,7 +19,7 @@
broker.language=java
broker.type=spawned
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.config=build/etc/config-systests-bdb.xml
diff --git a/qpid/java/test-profiles/java-bdb.0-10.testprofile b/qpid/java/test-profiles/java-bdb.0-10.testprofile
index ac90050a3a..892188aa24 100644
--- a/qpid/java/test-profiles/java-bdb.0-10.testprofile
+++ b/qpid/java/test-profiles/java-bdb.0-10.testprofile
@@ -20,7 +20,7 @@
broker.language=java
broker.type=internal
#broker.command only used for the second broker during failover tests in this profile
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.protocol.excludes=--exclude-1-0 @PORT --exclude-1-0 @SSL_PORT
diff --git a/qpid/java/test-profiles/java-bdb.0-8.testprofile b/qpid/java/test-profiles/java-bdb.0-8.testprofile
index 41dba2910a..87eea96dda 100644
--- a/qpid/java/test-profiles/java-bdb.0-8.testprofile
+++ b/qpid/java/test-profiles/java-bdb.0-8.testprofile
@@ -20,7 +20,7 @@
broker.language=java
broker.type=internal
#broker.command only used for the second broker during failover tests in this profile
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.config=build/etc/config-systests-bdb.xml
diff --git a/qpid/java/test-profiles/java-bdb.0-9-1.testprofile b/qpid/java/test-profiles/java-bdb.0-9-1.testprofile
index 49dd6db0e0..1339dc1dc7 100644
--- a/qpid/java/test-profiles/java-bdb.0-9-1.testprofile
+++ b/qpid/java/test-profiles/java-bdb.0-9-1.testprofile
@@ -20,7 +20,7 @@
broker.language=java
broker.type=internal
#broker.command only used for the second broker during failover tests in this profile
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.config=build/etc/config-systests-bdb.xml
diff --git a/qpid/java/test-profiles/java-bdb.0-9.testprofile b/qpid/java/test-profiles/java-bdb.0-9.testprofile
index 76bda49808..c097d53c85 100644
--- a/qpid/java/test-profiles/java-bdb.0-9.testprofile
+++ b/qpid/java/test-profiles/java-bdb.0-9.testprofile
@@ -20,7 +20,7 @@
broker.language=java
broker.type=internal
#broker.command only used for the second broker during failover tests in this profile
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.config=build/etc/config-systests-bdb.xml
diff --git a/qpid/java/test-profiles/java-dby-mem.0-10.testprofile b/qpid/java/test-profiles/java-dby-mem.0-10.testprofile
index a1452b4986..33f9527c86 100644
--- a/qpid/java/test-profiles/java-dby-mem.0-10.testprofile
+++ b/qpid/java/test-profiles/java-dby-mem.0-10.testprofile
@@ -20,7 +20,7 @@ broker.language=java
broker.version=v0_10
broker.type=internal
#broker.command only used for the second broker during failover tests in this profile
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.protocol.excludes=--exclude-1-0 @PORT --exclude-1-0 @SSL_PORT
diff --git a/qpid/java/test-profiles/java-dby-mem.0-8.testprofile b/qpid/java/test-profiles/java-dby-mem.0-8.testprofile
index 9b6b413e81..89bad84769 100644
--- a/qpid/java/test-profiles/java-dby-mem.0-8.testprofile
+++ b/qpid/java/test-profiles/java-dby-mem.0-8.testprofile
@@ -20,7 +20,7 @@ broker.version=v0_8
broker.language=java
broker.type=internal
#broker.command only used for the second broker during failover tests in this profile
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.config=build/etc/config-systests-derby-mem.xml
diff --git a/qpid/java/test-profiles/java-dby-mem.0-9-1.testprofile b/qpid/java/test-profiles/java-dby-mem.0-9-1.testprofile
index 36bcfc4c1d..8deea281a4 100644
--- a/qpid/java/test-profiles/java-dby-mem.0-9-1.testprofile
+++ b/qpid/java/test-profiles/java-dby-mem.0-9-1.testprofile
@@ -20,7 +20,7 @@ broker.version=v0_9_1
broker.language=java
broker.type=internal
#broker.command only used for the second broker during failover tests in this profile
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.config=build/etc/config-systests-derby-mem.xml
diff --git a/qpid/java/test-profiles/java-dby-mem.0-9.testprofile b/qpid/java/test-profiles/java-dby-mem.0-9.testprofile
index cfc7ad203f..b691a7d153 100644
--- a/qpid/java/test-profiles/java-dby-mem.0-9.testprofile
+++ b/qpid/java/test-profiles/java-dby-mem.0-9.testprofile
@@ -20,7 +20,7 @@ broker.version=v0_9
broker.language=java
broker.type=internal
#broker.command only used for the second broker during failover tests in this profile
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.config=build/etc/config-systests-derby-mem.xml
diff --git a/qpid/java/test-profiles/java-dby-spawn.0-10.testprofile b/qpid/java/test-profiles/java-dby-spawn.0-10.testprofile
index 5a8594599b..e2e2b44dae 100644
--- a/qpid/java/test-profiles/java-dby-spawn.0-10.testprofile
+++ b/qpid/java/test-profiles/java-dby-spawn.0-10.testprofile
@@ -19,7 +19,7 @@
broker.language=java
broker.version=v0_10
broker.type=spawned
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.protocol.excludes=--exclude-1-0 @PORT --exclude-1-0 @SSL_PORT
diff --git a/qpid/java/test-profiles/java-dby-spawn.0-8.testprofile b/qpid/java/test-profiles/java-dby-spawn.0-8.testprofile
index 2febd51e1c..3f609226e3 100644
--- a/qpid/java/test-profiles/java-dby-spawn.0-8.testprofile
+++ b/qpid/java/test-profiles/java-dby-spawn.0-8.testprofile
@@ -19,7 +19,7 @@
broker.version=v0_8
broker.language=java
broker.type=spawned
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.config=build/etc/config-systests-derby.xml
diff --git a/qpid/java/test-profiles/java-dby-spawn.0-9-1.testprofile b/qpid/java/test-profiles/java-dby-spawn.0-9-1.testprofile
index 133796becd..80d40458fd 100644
--- a/qpid/java/test-profiles/java-dby-spawn.0-9-1.testprofile
+++ b/qpid/java/test-profiles/java-dby-spawn.0-9-1.testprofile
@@ -19,7 +19,7 @@
broker.version=v0_9_1
broker.language=java
broker.type=spawned
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.config=build/etc/config-systests-derby.xml
diff --git a/qpid/java/test-profiles/java-dby-spawn.0-9.testprofile b/qpid/java/test-profiles/java-dby-spawn.0-9.testprofile
index 5bafa83221..122eccdca1 100644
--- a/qpid/java/test-profiles/java-dby-spawn.0-9.testprofile
+++ b/qpid/java/test-profiles/java-dby-spawn.0-9.testprofile
@@ -19,7 +19,7 @@
broker.version=v0_9
broker.language=java
broker.type=spawned
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.config=build/etc/config-systests-derby.xml
diff --git a/qpid/java/test-profiles/java-dby.0-10.testprofile b/qpid/java/test-profiles/java-dby.0-10.testprofile
index 8d1ab9b015..d3b03054de 100644
--- a/qpid/java/test-profiles/java-dby.0-10.testprofile
+++ b/qpid/java/test-profiles/java-dby.0-10.testprofile
@@ -20,7 +20,7 @@ broker.language=java
broker.version=v0_10
broker.type=internal
#broker.command only used for the second broker during failover tests in this profile
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.protocol.excludes=--exclude-1-0 @PORT --exclude-1-0 @SSL_PORT
diff --git a/qpid/java/test-profiles/java-dby.0-8.testprofile b/qpid/java/test-profiles/java-dby.0-8.testprofile
index ccb355842a..51580e6c7a 100644
--- a/qpid/java/test-profiles/java-dby.0-8.testprofile
+++ b/qpid/java/test-profiles/java-dby.0-8.testprofile
@@ -20,7 +20,7 @@ broker.version=v0_8
broker.language=java
broker.type=internal
#broker.command only used for the second broker during failover tests in this profile
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.config=build/etc/config-systests-derby.xml
diff --git a/qpid/java/test-profiles/java-dby.0-9-1.testprofile b/qpid/java/test-profiles/java-dby.0-9-1.testprofile
index 9e822deba5..b92397eae7 100644
--- a/qpid/java/test-profiles/java-dby.0-9-1.testprofile
+++ b/qpid/java/test-profiles/java-dby.0-9-1.testprofile
@@ -20,7 +20,7 @@ broker.version=v0_9_1
broker.language=java
broker.type=internal
#broker.command only used for the second broker during failover tests in this profile
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.config=build/etc/config-systests-derby.xml
diff --git a/qpid/java/test-profiles/java-dby.0-9.testprofile b/qpid/java/test-profiles/java-dby.0-9.testprofile
index 3926618f42..17363b7e47 100644
--- a/qpid/java/test-profiles/java-dby.0-9.testprofile
+++ b/qpid/java/test-profiles/java-dby.0-9.testprofile
@@ -20,7 +20,7 @@ broker.version=v0_9
broker.language=java
broker.type=internal
#broker.command only used for the second broker during failover tests in this profile
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.config=build/etc/config-systests-derby.xml
diff --git a/qpid/java/test-profiles/java-mms-spawn.0-10.testprofile b/qpid/java/test-profiles/java-mms-spawn.0-10.testprofile
index 2978fe6882..57af5c4a41 100644
--- a/qpid/java/test-profiles/java-mms-spawn.0-10.testprofile
+++ b/qpid/java/test-profiles/java-mms-spawn.0-10.testprofile
@@ -19,7 +19,7 @@
broker.version=v0_10
broker.language=java
broker.type=spawned
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.protocol.excludes=--exclude-1-0 @PORT --exclude-1-0 @SSL_PORT
diff --git a/qpid/java/test-profiles/java-mms-spawn.0-8.testprofile b/qpid/java/test-profiles/java-mms-spawn.0-8.testprofile
index 9d3e78fb11..24f088e0c5 100644
--- a/qpid/java/test-profiles/java-mms-spawn.0-8.testprofile
+++ b/qpid/java/test-profiles/java-mms-spawn.0-8.testprofile
@@ -19,7 +19,7 @@
broker.version=v0_8
broker.language=java
broker.type=spawned
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.protocol.excludes=--exclude-0-10 @PORT --exclude-0-10 @SSL_PORT --exclude-1-0 @PORT --exclude-1-0 @SSL_PORT --exclude-0-9-1 @PORT --exclude-0-9-1 @SSL_PORT --exclude-0-9 @PORT --exclude-0-9 @SSL_PORT
diff --git a/qpid/java/test-profiles/java-mms-spawn.0-9-1.testprofile b/qpid/java/test-profiles/java-mms-spawn.0-9-1.testprofile
index 54d845f391..c1f6d10675 100644
--- a/qpid/java/test-profiles/java-mms-spawn.0-9-1.testprofile
+++ b/qpid/java/test-profiles/java-mms-spawn.0-9-1.testprofile
@@ -19,7 +19,7 @@
broker.version=v0_9_1
broker.language=java
broker.type=spawned
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.protocol.excludes=--exclude-0-10 @PORT --exclude-0-10 @SSL_PORT --exclude-1-0 @PORT --exclude-1-0 @SSL_PORT
diff --git a/qpid/java/test-profiles/java-mms-spawn.0-9.testprofile b/qpid/java/test-profiles/java-mms-spawn.0-9.testprofile
index 6f76b699fa..421ae7476e 100644
--- a/qpid/java/test-profiles/java-mms-spawn.0-9.testprofile
+++ b/qpid/java/test-profiles/java-mms-spawn.0-9.testprofile
@@ -19,7 +19,7 @@
broker.version=v0_9
broker.language=java
broker.type=spawned
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.protocol.excludes=--exclude-0-10 @PORT --exclude-0-10 @SSL_PORT --exclude-1-0 @PORT --exclude-1-0 @SSL_PORT --exclude-0-9-1 @PORT --exclude-0-9-1 @SSL_PORT
diff --git a/qpid/java/test-profiles/java-mms.0-10.testprofile b/qpid/java/test-profiles/java-mms.0-10.testprofile
index d286450660..6dd40cff47 100644
--- a/qpid/java/test-profiles/java-mms.0-10.testprofile
+++ b/qpid/java/test-profiles/java-mms.0-10.testprofile
@@ -20,7 +20,7 @@ broker.language=java
broker.version=v0_10
broker.type=internal
#broker.command only used for the second broker during failover tests in this profile
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.protocol.excludes=--exclude-1-0 @PORT --exclude-1-0 @SSL_PORT
diff --git a/qpid/java/test-profiles/java-mms.0-8.testprofile b/qpid/java/test-profiles/java-mms.0-8.testprofile
index 5a425740fb..f82bf8c473 100644
--- a/qpid/java/test-profiles/java-mms.0-8.testprofile
+++ b/qpid/java/test-profiles/java-mms.0-8.testprofile
@@ -20,7 +20,7 @@ broker.language=java
broker.version=v0_8
broker.type=internal
#broker.command only used for the second broker during failover tests in this profile
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.protocol.excludes=--exclude-0-10 @PORT --exclude-0-10 @SSL_PORT --exclude-1-0 @PORT --exclude-1-0 @SSL_PORT --exclude-0-9-1 @PORT --exclude-0-9-1 @SSL_PORT --exclude-0-9 @PORT --exclude-0-9 @SSL_PORT
diff --git a/qpid/java/test-profiles/java-mms.0-9-1.testprofile b/qpid/java/test-profiles/java-mms.0-9-1.testprofile
index 007529ddc8..9b8baaa5a3 100644
--- a/qpid/java/test-profiles/java-mms.0-9-1.testprofile
+++ b/qpid/java/test-profiles/java-mms.0-9-1.testprofile
@@ -20,7 +20,7 @@ broker.language=java
broker.version=v0_9_1
broker.type=internal
#broker.command only used for the second broker during failover tests in this profile
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.protocol.excludes=--exclude-0-10 @PORT --exclude-0-10 @SSL_PORT --exclude-1-0 @PORT --exclude-1-0 @SSL_PORT
diff --git a/qpid/java/test-profiles/java-mms.0-9.testprofile b/qpid/java/test-profiles/java-mms.0-9.testprofile
index 3f70be7639..56ace6d9e1 100644
--- a/qpid/java/test-profiles/java-mms.0-9.testprofile
+++ b/qpid/java/test-profiles/java-mms.0-9.testprofile
@@ -20,7 +20,7 @@ broker.language=java
broker.version=v0_9
broker.type=internal
#broker.command only used for the second broker during failover tests in this profile
-broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l test-profiles/log4j-test.xml
+broker.command=build/bin/qpid-server -p @PORT -m @MPORT @EXCLUDES @INCLUDES -c @CONFIG_FILE -l @LOG_CONFIG_FILE
broker.ready=BRK-1004
broker.stopped=Exception
broker.protocol.excludes=--exclude-0-10 @PORT --exclude-0-10 @SSL_PORT --exclude-1-0 @PORT --exclude-1-0 @SSL_PORT --exclude-0-9-1 @PORT --exclude-0-9-1 @SSL_PORT
diff --git a/qpid/java/test-profiles/testprofile.defaults b/qpid/java/test-profiles/testprofile.defaults
index b0c1aea661..47992b9334 100644
--- a/qpid/java/test-profiles/testprofile.defaults
+++ b/qpid/java/test-profiles/testprofile.defaults
@@ -33,7 +33,7 @@ amqj.logging.level=${log}
amqj.server.logging.level=${log}
amqj.protocol.logging.level=${log}
root.logging.level=warn
-log4j.configuration=file:///${test.profiles}/log4j-test.xml
+log4j.configuration=test-profiles/log4j-test.xml
log4j.debug=false
# Note test-provider.properties also has variables of same name.