summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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.