diff options
| author | Keith Wall <kwall@apache.org> | 2012-07-05 09:40:06 +0000 |
|---|---|---|
| committer | Keith Wall <kwall@apache.org> | 2012-07-05 09:40:06 +0000 |
| commit | 8b555d057f483874d9384c15bd989c975d18e0b0 (patch) | |
| tree | b5fdf00f4a10c465da038df03d6cdeb357501881 /qpid/java/broker-plugins | |
| parent | 179f46270e539569e7c57e763ccd8a49ccf09a84 (diff) | |
| download | qpid-python-8b555d057f483874d9384c15bd989c975d18e0b0.tar.gz | |
QPID-4109: Re-enable LoggingManagement MBean
* Re-wire up LoggingManagementMBean.
* Centralise log4j specific functionality into LoggingFacade class (moving implementation from LoggingManagementMBean and QpidLog4JConfigurator
together).
* Implement unit-tests for MBean and Facade levels. Reenforce units tests with system tests testing logging management end to end.
* Changed QpidBrokerTestCase so that log4j.configuration is _always_ used to obtain the log4j config file regardless of whether test type
is spawned or internal (previously log4j.configuration was respected only for internal tests). This was require to be able to
write a logging management system test that could safely change the contents of the log4j config without running the risk of
effecting other tests.
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1357528 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/java/broker-plugins')
5 files changed, 473 insertions, 1016 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)); + } + +} |
