diff options
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. |
