From: Tomas Olvecky Date: Fri, 4 Oct 2013 14:29:41 +0000 (+0200) Subject: Inital code drop of logback-config. X-Git-Tag: jenkins-controller-bulk-release-prepare-only-2-1~663 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=655dffff17a7c45d181d0b15decf627fdd1238eb Inital code drop of logback-config. This module bridges logback configuration over config-subsystem, allowing to reconfigure logback via JMX. Change-Id: I52644b628d384d8ca015885cc9f016f4429238d4 Signed-off-by: Tomas Olvecky --- diff --git a/opendaylight/config/logback-config/pom.xml b/opendaylight/config/logback-config/pom.xml new file mode 100644 index 0000000000..6b79e7cd27 --- /dev/null +++ b/opendaylight/config/logback-config/pom.xml @@ -0,0 +1,121 @@ + + + 4.0.0 + + org.opendaylight.controller + config-subsystem + 0.2.1-SNAPSHOT + + logback-config + ${project.artifactId} + bundle + + 3.0.4 + + + + + ${project.groupId} + config-api + 0.2.1-SNAPSHOT + + + org.opendaylight.bgpcep + concepts + 0.2.0-SNAPSHOT + + + org.slf4j + slf4j-api + + + ch.qos.logback + logback-classic + ${logback.version} + + + ch.qos.logback + logback-core + ${logback.version} + + + + com.google.guava + guava + + + commons-io + commons-io + + + + + + ${project.groupId} + config-manager + 0.2.1-SNAPSHOT + test + test-jar + + + ${project.groupId} + config-manager + 0.2.1-SNAPSHOT + test + + + ${project.groupId} + config-util + 0.2.1-SNAPSHOT + test + + + org.opendaylight.bgpcep + mockito-configuration + 0.2.0-SNAPSHOT + test + + + + + + org.apache.felix + maven-bundle-plugin + + + + + + ch.qos.logback.classic, + ch.qos.logback.classic.encoder, + ch.qos.logback.classic.filter, + ch.qos.logback.classic.spi, + ch.qos.logback.core, + ch.qos.logback.core.status, + ch.qos.logback.core.encoder, + ch.qos.logback.core.rolling, + org.opendaylight.protocol.concepts, + org.opendaylight.controller.config.api, + org.opendaylight.controller.config.api.runtime, + org.opendaylight.controller.config.api.annotations, + org.opendaylight.controller.config.spi, + com.google.common.base, + com.google.common.collect, + org.apache.commons.lang3, + org.slf4j + + + org.opendaylight.controller.config.yang.logback.config, + + + + + + org.opendaylight.yangtools + yang-maven-plugin + + + + diff --git a/opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/ContextSetter.java b/opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/ContextSetter.java new file mode 100644 index 0000000000..3241fb01af --- /dev/null +++ b/opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/ContextSetter.java @@ -0,0 +1,19 @@ +/** + * Generated file + + * Generated from: yang module name: config-test yang module local name: testing + * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator + * Generated at: Fri Sep 27 14:06:33 CEST 2013 + * + * Do not modify this file unless it is present under src/main directory + */ +package org.opendaylight.controller.config.yang.logback.config; + +/** + * Updates current state of Logback configuration. + */ +public interface ContextSetter { + + public void updateContext(LogbackModule module); + +} diff --git a/opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/ContextSetterImpl.java b/opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/ContextSetterImpl.java new file mode 100644 index 0000000000..7e4095ff9c --- /dev/null +++ b/opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/ContextSetterImpl.java @@ -0,0 +1,221 @@ +/** + * Generated file + + * Generated from: yang module name: config-test yang module local name: testing + * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator + * Generated at: Fri Sep 27 14:06:33 CEST 2013 + * + * Do not modify this file unless it is present under src/main directory + */ +package org.opendaylight.controller.config.yang.logback.config; + +import java.io.Closeable; +import java.io.IOException; +import java.util.*; + +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.encoder.PatternLayoutEncoder; +import ch.qos.logback.classic.filter.ThresholdFilter; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.rolling.FixedWindowRollingPolicy; +import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.Sets; + +/** + * Implementation of {@link ContextSetter}. Resets running logback + * configuration. + */ +public class ContextSetterImpl implements ContextSetter, Closeable { + + private final LogbackStatusListener statusListener; + private static final org.slf4j.Logger classLogger = LoggerFactory + .getLogger(ContextSetterImpl.class); + + public ContextSetterImpl( + LogbackRuntimeRegistrator rootRuntimeBeanRegistratorWrapper) { + statusListener = new LogbackStatusListener( + rootRuntimeBeanRegistratorWrapper); + statusListener.register(); + } + + public void updateContext(LogbackModule module) { + LoggerContext context = (LoggerContext) LoggerFactory + .getILoggerFactory(); + + List loggersBefore = context + .getLoggerList(); + + createLoggers(context, module, Sets.newHashSet(loggersBefore)); + } + + private Map> createConsoleAppenders( + LoggerContext context, LogbackModule module) { + Map> appendersMap = new HashMap<>(); + for (ConsoleAppenderTO appender : module.getConsoleAppenderTO()) { + Preconditions.checkState( + appendersMap.containsKey(appender.getName()) == false, + "Duplicate appender name %s", appender.getName()); + ch.qos.logback.core.ConsoleAppender app = new ch.qos.logback.core.ConsoleAppender(); + app.setContext(context); + PatternLayoutEncoder encoder = new PatternLayoutEncoder(); + encoder.setContext(context); + encoder.setPattern(appender.getEncoderPattern()); + encoder.start(); + app.setEncoder(encoder); + ThresholdFilter filter = new ThresholdFilter(); + filter.setContext(context); + filter.setLevel(appender.getThresholdFilter()); + filter.start(); + app.getCopyOfAttachedFiltersList().add(filter); + app.setName(appender.getName()); + app.start(); + appendersMap.put(app.getName(), app); + } + return appendersMap; + } + + private void createLoggers(LoggerContext context, LogbackModule module, + Set loggersBefore) { + + Map> appendersMap = getAppenders( + module, context); + + for (LoggerTO logger : module.getLoggerTO()) { + classLogger.trace("Setting configuration for logger {}", + logger.getLoggerName()); + final ch.qos.logback.classic.Logger logbackLogger = context + .getLogger(logger.getLoggerName()); + + Optional>> appendersBefore = getAppendersBefore( + loggersBefore, logbackLogger); + classLogger.trace("Logger {}: Appenders registered before: {}", + logger.getLoggerName(), + appendersBefore.isPresent() ? appendersBefore.get() + : "NO APPENDERS BEFORE"); + + logbackLogger.setLevel(Level.toLevel(logger.getLevel())); + + addNewAppenders(appendersMap, logger, logbackLogger, + appendersBefore); + removeBeforeAppenders(loggersBefore, logger, logbackLogger, + appendersBefore); + } + } + + private void addNewAppenders( + Map> appendersMap, LoggerTO logger, + ch.qos.logback.classic.Logger logbackLogger, + Optional>> appendersBefore) { + for (String appenderName : logger.getAppenders()) { + if (appendersMap.containsKey(appenderName)) { + logbackLogger.addAppender(appendersMap.get(appenderName)); + classLogger.trace("Logger {}: Adding new appender: {}", + logger.getLoggerName(), appenderName); + } else { + throw new IllegalStateException( + "No appender " + + appenderName + + " found. This error should have been discovered by validation"); + } + } + } + + private void removeBeforeAppenders( + Set loggersBefore, LoggerTO logger, + ch.qos.logback.classic.Logger logbackLogger, + Optional>> appendersBefore) { + if (appendersBefore.isPresent()) { + for (Appender appenderBefore : appendersBefore.get()) { + logbackLogger.detachAppender(appenderBefore); + appenderBefore.stop(); + classLogger.trace("Logger {}: Removing old appender: {}", + logger.getLoggerName(), appenderBefore.getName()); + } + loggersBefore.remove(logbackLogger); + } + } + + private Optional>> getAppendersBefore( + Set loggersBefore, + ch.qos.logback.classic.Logger logbackLogger) { + if (loggersBefore.contains(logbackLogger)) { + Iterator> appenderIt = logbackLogger + .iteratorForAppenders(); + Set> appendersBefore = Sets.newHashSet(); + while (appenderIt.hasNext()) { + appendersBefore.add(appenderIt.next()); + } + return Optional.of(appendersBefore); + } else + return Optional.absent(); + + } + + private Map> getAppenders( + LogbackModule module, LoggerContext context) { + Map> appendersMap = new HashMap<>(); + addAllAppenders(appendersMap, createRollingAppenders(context, module)); + addAllAppenders(appendersMap, createConsoleAppenders(context, module)); + + return appendersMap; + } + + private void addAllAppenders( + Map> allAppenders, + Map> appendersToAdd) { + for (String appenderName : appendersToAdd.keySet()) { + Preconditions.checkState( + allAppenders.containsKey(appenderName) == false, + "Duplicate appender name %s", appenderName); + allAppenders.put(appenderName, appendersToAdd.get(appenderName)); + } + } + + private Map> createRollingAppenders( + LoggerContext context, LogbackModule module) { + Map> appendersMap = new HashMap<>(); + for (RollingFileAppenderTO appender : module.getRollingFileAppenderTO()) { + Preconditions.checkState( + appendersMap.containsKey(appender.getName()) == false, + "Duplicate appender name %s", appender.getName()); + ch.qos.logback.core.rolling.RollingFileAppender app = new ch.qos.logback.core.rolling.RollingFileAppender<>(); + app.setAppend(appender.getAppend()); + app.setContext(context); + PatternLayoutEncoder encoder = new PatternLayoutEncoder(); + encoder.setContext(context); + encoder.setPattern(appender.getEncoderPattern()); + encoder.start(); + app.setEncoder(encoder); + app.setFile(appender.getFileName()); + FixedWindowRollingPolicy policy = new FixedWindowRollingPolicy(); + policy.setContext(context); + policy.setMaxIndex(appender.getMaxIndex()); + policy.setMinIndex(appender.getMinIndex()); + policy.setFileNamePattern(appender.getFileNamePattern()); + policy.setParent(app); + policy.start(); + app.setRollingPolicy(policy); + SizeBasedTriggeringPolicy triggeringPolicy = new SizeBasedTriggeringPolicy(); + triggeringPolicy.setContext(context); + triggeringPolicy.setMaxFileSize(appender.getMaxFileSize()); + triggeringPolicy.start(); + app.setTriggeringPolicy(triggeringPolicy); + app.setName(appender.getName()); + app.start(); + appendersMap.put(app.getName(), app); + } + return appendersMap; + } + + @Override + public void close() throws IOException { + statusListener.close(); + } +} diff --git a/opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/LogbackModule.java b/opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/LogbackModule.java new file mode 100644 index 0000000000..9b5cbfc7f9 --- /dev/null +++ b/opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/LogbackModule.java @@ -0,0 +1,152 @@ +/** + * Generated file + + * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator + * Generated at: Wed Jul 17 15:26:45 CEST 2013 + * + * Do not modifiy this file unless it is present under src/main directory + */ +package org.opendaylight.controller.config.yang.logback.config; + +import java.util.Set; + +import org.opendaylight.controller.config.api.JmxAttribute; +import org.opendaylight.controller.config.api.JmxAttributeValidationException; + +import com.google.common.collect.Sets; + +/** +* +*/ +public final class LogbackModule + extends + org.opendaylight.controller.config.yang.logback.config.AbstractLogbackModule { + + public LogbackModule( + org.opendaylight.controller.config.api.ModuleIdentifier name, + org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + super(name, dependencyResolver); + } + + public LogbackModule( + org.opendaylight.controller.config.api.ModuleIdentifier name, + org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, + org.opendaylight.controller.config.yang.logback.config.AbstractLogbackModule oldModule, + java.lang.AutoCloseable oldInstance) { + super(name, dependencyResolver, oldModule, oldInstance); + } + + @Override + public void validate() { + super.validate(); + Set appenderNames = Sets.newHashSet(); + validateRollingObjects(appenderNames); + validateConsoleObjects(appenderNames); + validateLoggersObjects(appenderNames); + } + + private void validateLoggersObjects(Set appenderNames) { + JmxAttributeValidationException.checkNotNull(getLoggerTO(), + loggersJmxAttribute); + + for (LoggerTO loggerToValidate : getLoggerTO()) { + JmxAttributeValidationException.checkNotNull( + loggerToValidate.getLoggerName(), "LoggerName is null", + loggersJmxAttribute); + JmxAttributeValidationException.checkNotNull( + loggerToValidate.getLevel(), "Level is null", + loggersJmxAttribute); + JmxAttributeValidationException.checkCondition(!loggerToValidate + .getLoggerName().isEmpty(), "LoggerName needs to be set", + loggersJmxAttribute); + JmxAttributeValidationException.checkCondition(!loggerToValidate + .getLevel().isEmpty(), "Level needs to be set", + loggersJmxAttribute); + + for (String appenderName : loggerToValidate.getAppenders()) { + JmxAttributeValidationException + .checkCondition( + appenderNames.contains(appenderName), + "Appender " + + appenderName + + " referenced by logger " + + loggerToValidate.getLoggerName() + + " not present in configuration, present appenders: " + + appenderNames, loggersJmxAttribute); + } + + } + } + + private void validateConsoleObjects(Set appenderNames) { + + JmxAttributeValidationException.checkNotNull(getConsoleAppenderTO(), + consoleAppendersJmxAttribute); + for (ConsoleAppenderTO object : getConsoleAppenderTO()) { + JmxAttributeValidationException.checkNotNull( + object.getEncoderPattern(), "EncoderPattern is null", + consoleAppendersJmxAttribute); + + validateAppenderName(appenderNames, object.getName(), + consoleAppendersJmxAttribute); + + JmxAttributeValidationException.checkNotNull( + object.getThresholdFilter(), "Filterlevel is null", + consoleAppendersJmxAttribute); + } + } + + private void validateRollingObjects(Set appenderNames) { + + JmxAttributeValidationException.checkNotNull(getRollingFileAppenderTO(), + rollingAppendersJmxAttribute); + for (RollingFileAppenderTO object : getRollingFileAppenderTO()) { + JmxAttributeValidationException.checkNotNull( + object.getEncoderPattern(), "EncoderPattern is null", + rollingAppendersJmxAttribute); + + validateAppenderName(appenderNames, object.getName(), + rollingAppendersJmxAttribute); + + JmxAttributeValidationException.checkNotNull(object.getFileName(), + "FileName is null", rollingAppendersJmxAttribute); + JmxAttributeValidationException.checkNotNull( + object.getMaxFileSize(), "MaxFileSize is null", + rollingAppendersJmxAttribute); + JmxAttributeValidationException.checkNotNull(object.getMinIndex(), + "MinIndex is null", rollingAppendersJmxAttribute); + JmxAttributeValidationException.checkNotNull(object.getMaxIndex(), + "MaxIndex is null", rollingAppendersJmxAttribute); + JmxAttributeValidationException.checkCondition(!object + .getEncoderPattern().isEmpty(), + "EncoderPattern needs to be set", + rollingAppendersJmxAttribute); + JmxAttributeValidationException.checkCondition(!object + .getFileName().isEmpty(), "FileName needs to be set", + rollingAppendersJmxAttribute); + + } + } + + private void validateAppenderName(Set appenderNames, + String appenderName, JmxAttribute jmxAttribute) { + JmxAttributeValidationException.checkNotNull(appenderName, + "Name is null", jmxAttribute); + JmxAttributeValidationException.checkCondition( + appenderNames.contains(appenderName) == false, + "Duplicate appender name " + appenderName, jmxAttribute); + appenderNames.add(appenderName); + JmxAttributeValidationException.checkCondition(!appenderName.isEmpty(), + "Name needs to be set", jmxAttribute); + } + + @Override + public java.lang.AutoCloseable createInstance() { + ContextSetterImpl setter = new ContextSetterImpl( + getRootRuntimeBeanRegistratorWrapper()); + + setter.updateContext(this); + + return setter; + } +} diff --git a/opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/LogbackModuleFactory.java b/opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/LogbackModuleFactory.java new file mode 100644 index 0000000000..eb0c7da482 --- /dev/null +++ b/opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/LogbackModuleFactory.java @@ -0,0 +1,213 @@ +/** + * Generated file + + * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator + * Generated at: Wed Jul 17 15:26:45 CEST 2013 + * + * Do not modifiy this file unless it is present under src/main directory + */ +package org.opendaylight.controller.config.yang.logback.config; + +import java.util.*; +import java.util.Map.Entry; + +import org.apache.commons.lang3.StringUtils; +import org.opendaylight.controller.config.api.DependencyResolver; +import org.opendaylight.controller.config.api.ModuleIdentifier; +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.encoder.PatternLayoutEncoder; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggerComparator; +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.rolling.FixedWindowRollingPolicy; +import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; + +/** +* +*/ +public class LogbackModuleFactory + extends + org.opendaylight.controller.config.yang.logback.config.AbstractLogbackModuleFactory { + + private static final String INSTANCE_NAME = "singleton"; + private Map loggersDTOs; + private Map rollingDTOs; + private Map consoleDTOs; + + @Override + public LogbackModule instantiateModule(String instanceName, + DependencyResolver dependencyResolver) { + Preconditions.checkArgument(instanceName.equals(INSTANCE_NAME), + "There should be just one instance of logback, named " + + INSTANCE_NAME); + prepareDTOs(); + LogbackModule module = new LogbackModule(new ModuleIdentifier( + getImplementationName(), INSTANCE_NAME), dependencyResolver); + module.setConsoleAppenderTO(Lists.newArrayList(consoleDTOs.values())); + module.setRollingFileAppenderTO(Lists.newArrayList(rollingDTOs.values())); + module.setLoggerTO(Lists.newArrayList(loggersDTOs.values())); + return module; + } + + @Override + public LogbackModule instantiateModule(String instanceName, + DependencyResolver dependencyResolver, LogbackModule oldModule, + AutoCloseable oldInstance) { + Preconditions.checkArgument(instanceName.equals(INSTANCE_NAME), + "There should be just one instance of logback, named " + + INSTANCE_NAME); + prepareDTOs(); + LogbackModule module = new LogbackModule(new ModuleIdentifier( + getImplementationName(), INSTANCE_NAME), dependencyResolver, + oldModule, oldInstance); + module.setConsoleAppenderTO(Lists.newArrayList(consoleDTOs.values())); + /* + * module.setJCloudsAppender(Lists.newArrayList(jcloudsDTOs.values())); + */ + module.setRollingFileAppenderTO(Lists.newArrayList(rollingDTOs.values())); + module.setLoggerTO(Lists.newArrayList(loggersDTOs.values())); + return module; + } + + private void prepareDTOs() { + LoggerContext context = (LoggerContext) LoggerFactory + .getILoggerFactory(); + this.loggersDTOs = prepareLoggersDTOs(context); + prepareAppendersDTOs(context); + } + + private void prepareAppendersDTOs(LoggerContext context) { + this.rollingDTOs = new HashMap<>(); + this.consoleDTOs = new HashMap<>(); + ch.qos.logback.core.rolling.RollingFileAppender rollingApp; + ch.qos.logback.core.ConsoleAppender consoleApp; + Map>> appendersAll = new HashMap<>(); + for (Logger log : context.getLoggerList()) { + List> appenders = new ArrayList<>(); + Iterator> iter = log.iteratorForAppenders(); + while (iter.hasNext()) { + Appender element = iter.next(); + appenders.add(element); + } + appendersAll.put(log, appenders); + } + for (List> appEntry : appendersAll.values()) { + for (ch.qos.logback.core.Appender appender : appEntry) { + if (appender instanceof ch.qos.logback.core.rolling.RollingFileAppender) { + RollingFileAppenderTO app = new RollingFileAppenderTO(); + rollingApp = (ch.qos.logback.core.rolling.RollingFileAppender< ILoggingEvent >) appender; + app.setAppend(rollingApp.isAppend()); + PatternLayoutEncoder encoder = (PatternLayoutEncoder) rollingApp + .getEncoder(); + app.setEncoderPattern(encoder.getPattern()); + app.setFileName(rollingApp.getFile()); + FixedWindowRollingPolicy rollingPolicy = (FixedWindowRollingPolicy) rollingApp + .getRollingPolicy(); + app.setMaxIndex(rollingPolicy.getMaxIndex()); + app.setMinIndex(rollingPolicy.getMinIndex()); + SizeBasedTriggeringPolicy triggeringPolicy = (SizeBasedTriggeringPolicy) rollingApp + .getTriggeringPolicy(); + app.setMaxFileSize(triggeringPolicy.getMaxFileSize()); + app.setFileNamePattern(rollingPolicy.getFileNamePattern()); + app.setName(rollingApp.getName()); + this.rollingDTOs.put(rollingApp.getName(), app); + } + if (appender instanceof ch.qos.logback.core.ConsoleAppender) { + ConsoleAppenderTO app = new ConsoleAppenderTO(); + consoleApp = (ch.qos.logback.core.ConsoleAppender) appender; + consoleApp.getCopyOfAttachedFiltersList(); + PatternLayoutEncoder encoder = (PatternLayoutEncoder) consoleApp + .getEncoder(); + app.setEncoderPattern(encoder.getPattern()); + app.setName(consoleApp.getName()); + app.setThresholdFilter(context.getLogger( + Logger.ROOT_LOGGER_NAME).getEffectiveLevel().levelStr); + this.consoleDTOs.put(consoleApp.getName(), app); + } + } + } + } + + private Map prepareLoggersDTOs( + LoggerContext context) { + Map DTOs = new HashMap<>(); + List appenders = new ArrayList<>(); + List loggersToBeAdd = removeUnusableLoggers( + context.getLoggerList(), + context.getLogger(Logger.ROOT_LOGGER_NAME)); + for (org.slf4j.Logger log : loggersToBeAdd) { + LoggerTO logger = new LoggerTO(); + if (((Logger) log).getLevel() != null) + logger.setLevel(((Logger) log).getLevel().levelStr); + else + logger.setLevel(((Logger) log).getEffectiveLevel().levelStr); + logger.setLoggerName(log.getName()); + Iterator> iter = ((Logger) log) + .iteratorForAppenders(); + while (iter.hasNext()) { + Appender element = iter.next(); + appenders.add(element.getName()); + } + logger.setAppenders(appenders); + DTOs.put(log.getName(), logger); + appenders = new ArrayList<>(); + + } + return DTOs; + } + + private List removeUnusableLoggers( + List loggerList, Logger rootLogger) { + Collections.sort(loggerList, new LoggerComparator()); + Map loggersToReturn = new HashMap<>(); + + for (org.slf4j.Logger log : loggerList) { + boolean shouldAdd = true; + for (Entry entry : loggersToReturn + .entrySet()) { + if (StringUtils.contains(log.getName(), entry.getKey())) { + if (((Logger) log).getLevel() != null + && ((Logger) log).getLevel().equals( + ((Logger) entry.getValue()).getLevel()) + && !((Logger) log).iteratorForAppenders().hasNext()) { + shouldAdd = false; + break; + } + if (((Logger) log).getLevel() == null + && ((Logger) log).getEffectiveLevel().equals( + ((Logger) entry.getValue()) + .getEffectiveLevel()) + && !((Logger) log).iteratorForAppenders().hasNext()) { + shouldAdd = false; + break; + } + } + if (((Logger) log).getLevel() != null + && ((Logger) log).getLevel().equals( + rootLogger.getLevel()) + && !((Logger) log).iteratorForAppenders().hasNext()) { + shouldAdd = false; + break; + } + if (((Logger) log).getLevel() == null + && ((Logger) log).getEffectiveLevel().equals( + rootLogger.getEffectiveLevel()) + && !((Logger) log).iteratorForAppenders().hasNext()) { + shouldAdd = false; + break; + } + } + if (shouldAdd) { + loggersToReturn.put(log.getName(), log); + } + } + return Lists.newArrayList(loggersToReturn.values()); + } + +} diff --git a/opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/LogbackStatusListener.java b/opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/LogbackStatusListener.java new file mode 100644 index 0000000000..dc2677951f --- /dev/null +++ b/opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/LogbackStatusListener.java @@ -0,0 +1,121 @@ +/** + * Generated file + + * Generated from: yang module name: config-test yang module local name: testing + * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator + * Generated at: Fri Sep 27 14:06:33 CEST 2013 + * + * Do not modify this file unless it is present under src/main directory + */ +package org.opendaylight.controller.config.yang.logback.config; + +import java.io.Closeable; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import ch.qos.logback.core.status.Status; +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.core.status.StatusBase; +import ch.qos.logback.core.status.StatusListener; +import ch.qos.logback.core.status.StatusManager; + +public class LogbackStatusListener implements StatusListener, + LogbackRuntimeMXBean, Closeable { + + private final List receivedStatuses; + private final LogbackRuntimeRegistrator rootRuntimeBeanRegistratorWrapper; + private LogbackRuntimeRegistration reg; + + public LogbackStatusListener( + LogbackRuntimeRegistrator rootRuntimeBeanRegistratorWrapper) { + receivedStatuses = new ArrayList<>(); + this.rootRuntimeBeanRegistratorWrapper = rootRuntimeBeanRegistratorWrapper; + } + + @Override + public synchronized List getStatusTO() { + return Collections.unmodifiableList(receivedStatuses); + } + + @Override + public synchronized void reset() { + receivedStatuses.clear(); + } + + public LogbackRuntimeRegistration register() { + reg = registerToJMX(rootRuntimeBeanRegistratorWrapper); + registerToLogback(); + return reg; + } + + private LogbackRuntimeRegistration registerToJMX( + LogbackRuntimeRegistrator rootRuntimeBeanRegistratorWrapper) { + return rootRuntimeBeanRegistratorWrapper.register(this); + } + + private synchronized void registerToLogback() { + LoggerContext context = (LoggerContext) LoggerFactory + .getILoggerFactory(); + final StatusManager statusManager = context.getStatusManager(); + + statusManager.remove(this); + reset(); + + statusManager.add(this); + addInitialStatuses(statusManager); + } + + private void addInitialStatuses(StatusManager statusManager) { + for (ch.qos.logback.core.status.Status status : statusManager + .getCopyOfStatusList()) { + addStatusEvent(status); + } + } + + @Override + public synchronized void addStatusEvent( + ch.qos.logback.core.status.Status status) { + receivedStatuses.add(transformStatus(status)); + } + + private StatusTO transformStatus(ch.qos.logback.core.status.Status status) { + StatusTO transformed = new StatusTO(); + + transformed.setDate(status.getDate()); + transformed.setLevel(transformStatusLevel(status.getLevel())); + transformed.setMessage(status.getMessage()); + + return transformed; + } + + private String transformStatusLevel(int status) { + switch (status) { + case StatusBase.INFO: + return "INFO"; + case StatusBase.WARN: + return "WARN"; + case StatusBase.ERROR: + return "ERROR"; + default: + throw new IllegalStateException("Unknown status level " + status); + } + } + + @Override + public void close() throws IOException { + if (reg != null) + reg.close(); + unregisterFromLogback(); + } + + private void unregisterFromLogback() { + LoggerContext context = (LoggerContext) LoggerFactory + .getILoggerFactory(); + final StatusManager statusManager = context.getStatusManager(); + statusManager.remove(this); + } +} diff --git a/opendaylight/config/logback-config/src/main/yang/config-logging.yang b/opendaylight/config/logback-config/src/main/yang/config-logging.yang new file mode 100644 index 0000000000..d0307f6194 --- /dev/null +++ b/opendaylight/config/logback-config/src/main/yang/config-logging.yang @@ -0,0 +1,152 @@ +// vi: set smarttab et sw=4 tabstop=4: +module config-logging { + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:controller:logback:config"; + prefix "logging"; + + import config { prefix config; revision-date 2013-04-05; } + import rpc-context { prefix rpcx; revision-date 2013-06-17; } + + organization "Cisco Systems, Inc."; + + description + "This module contains the base YANG definitions for NS-OS + logging module."; + + revision "2013-07-16" { + description + "Initial revision."; + } + + identity logback { + description + "Actual state of logback configuration."; + base config:module-type; + config:java-name-prefix Logback; + } + + identity logback-rpc; + + augment "/config:modules/config:module/config:configuration" { + case logback { + when "/config:modules/config:module/config:type = 'logback'"; + + list rolling-appenders { + leaf append { + type boolean; + mandatory false; + } + + leaf file-name { + type string; + mandatory true; + } + + leaf encoder-pattern { + type string; + mandatory true; + } + + leaf min-index { + type int32; + mandatory true; + } + + leaf max-index { + type int32; + mandatory true; + } + + leaf max-file-size { + type string; + mandatory true; + } + + leaf name { + type string; + mandatory true; + } + + leaf file-name-pattern { + type string; + mandatory true; + } + + config:java-name-prefix RollingFileAppenderTO; + } + + list console-appenders { + + leaf encoder-pattern { + type string; + mandatory true; + } + + leaf threshold-filter { + type string; + default 'ALL'; + } + + leaf name { + type string; + mandatory true; + } + config:java-name-prefix ConsoleAppenderTO; + } + + list loggers { + leaf logger-name { + type string; + mandatory true; + } + + leaf level { + type string; + mandatory true; + } + + leaf-list appenders { + type string; + } + config:java-name-prefix LoggerTO; + } + } + } + + + augment "/config:modules/config:module/config:state" { + case logback { + when "/config:modules/config:module/config:type = 'logback'"; + + rpcx:rpc-context-instance "logback-rpc"; + + list status { + config:java-name-prefix StatusTO; + + leaf level { + type string; + } + + leaf message { + type string; + } + + leaf date { + type uint32; + } + } + } + } + + rpc reset { + input { + uses rpcx:rpc-context-ref { + refine context-instance { + rpcx:rpc-context-instance logback-rpc; + } + } + } + } + +} + diff --git a/opendaylight/config/logback-config/src/test/java/org/opendaylight/controller/config/yang/logback/config/ContextSetterImplTest.java b/opendaylight/config/logback-config/src/test/java/org/opendaylight/controller/config/yang/logback/config/ContextSetterImplTest.java new file mode 100644 index 0000000000..7ba7a24d48 --- /dev/null +++ b/opendaylight/config/logback-config/src/test/java/org/opendaylight/controller/config/yang/logback/config/ContextSetterImplTest.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.config.yang.logback.config; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.config.api.DependencyResolver; +import org.opendaylight.controller.config.api.ModuleIdentifier; +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.Appender; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; + +public class ContextSetterImplTest { + + @Mock + private LogbackRuntimeRegistrator runtimeRegistratorMock; + @Mock + private DependencyResolver dependencyResolverMock; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + LogbackRuntimeRegistration reg = mock(LogbackRuntimeRegistration.class); + doReturn(reg).when(runtimeRegistratorMock).register( + any(LogbackRuntimeMXBean.class)); + } + + @Test + public void testUpdate() throws Exception { + Multimap loggersToAppenders = HashMultimap.create(); + loggersToAppenders.put("l1", "a1"); + loggersToAppenders.put("l1", "a2"); + createContextSetter(loggersToAppenders); + + assertLoggerWithAppenders("l1", "a1", "a2"); + } + + @Test + public void testUpdateTwice() throws Exception { + Multimap loggersToAppenders = HashMultimap.create(); + loggersToAppenders.put("l1", "a1"); + loggersToAppenders.put("l1", "a2"); + createContextSetter(loggersToAppenders); + + loggersToAppenders.clear(); + loggersToAppenders.put("l1", "a3"); + loggersToAppenders.put("l1", "a2"); + loggersToAppenders.put("l1", "a4"); + createContextSetter(loggersToAppenders); + + assertLoggerWithAppenders("l1", "a2", "a3", "a4"); + } + + @Test + public void testKeepOtherLoggers() throws Exception { + Multimap loggersToAppenders = HashMultimap.create(); + loggersToAppenders.put("l1", "a1"); + loggersToAppenders.put("l1", "a2"); + loggersToAppenders.put("l2", "a22"); + createContextSetter(loggersToAppenders); + + loggersToAppenders.clear(); + loggersToAppenders.put("l1", "a3"); + createContextSetter(loggersToAppenders); + + assertLoggerWithAppenders("l1", "a3"); + assertLoggerWithAppenders("l2", "a22"); + } + + private void createContextSetter(Multimap loggersToAppenders) { + ContextSetterImpl setter = new ContextSetterImpl(runtimeRegistratorMock); + + List logger = Lists.newArrayList(); + List consoleAppenders = Lists.newArrayList(); + + for (String loggerName : loggersToAppenders.keySet()) { + LoggerTO l1 = createLogger(loggerName, + loggersToAppenders.get(loggerName)); + logger.add(l1); + for (String appenderName : loggersToAppenders.get(loggerName)) { + consoleAppenders.add(createConsoleAppender(appenderName)); + } + + } + + LogbackModule logbackModule = createLogbackModule(logger, + consoleAppenders); + setter.updateContext(logbackModule); + } + + private void assertLoggerWithAppenders(String name, String... appenders) { + LoggerContext context = (LoggerContext) LoggerFactory + .getILoggerFactory(); + ch.qos.logback.classic.Logger logger = context.getLogger(name); + Iterator> it = logger.iteratorForAppenders(); + + Multimap> foundAppenders = HashMultimap.create(); + while (it.hasNext()) { + final Appender app = it.next(); + foundAppenders.put(app.getName(), app); + } + + if (appenders.length == 0) { + assertEquals(0, foundAppenders.values().size()); + } + + for (String appender : appenders) { + boolean isPresent = foundAppenders.get(appender).isEmpty(); + assertFalse( + "Appender " + appender + " for logger " + name + + " was not present, present appenders: " + + foundAppenders.keys(), isPresent); + } + + } + + private LogbackModule createLogbackModule(List logger, + List consoleAppenders) { + LogbackModule logbackModule = new LogbackModule(new ModuleIdentifier( + "fact", "first"), dependencyResolverMock); + logbackModule.setLoggerTO(logger); + logbackModule.setConsoleAppenderTO(consoleAppenders); + logbackModule.setRollingFileAppenderTO(Lists + . newArrayList()); + return logbackModule; + } + + private LoggerTO createLogger(String name, Collection appenders) { + LoggerTO l1 = new LoggerTO(); + l1.setAppenders(Lists.newArrayList(appenders)); + l1.setLoggerName(name); + l1.setLevel("INFO"); + return l1; + } + + private ConsoleAppenderTO createConsoleAppender(String name) { + ConsoleAppenderTO a = new ConsoleAppenderTO(); + a.setName(name); + a.setEncoderPattern("%-4relative [%thread] %-5level %logger{35} - %msg%n"); + return a; + } +} diff --git a/opendaylight/config/logback-config/src/test/java/org/opendaylight/controller/config/yang/logback/config/LogbackModuleTest.java b/opendaylight/config/logback-config/src/test/java/org/opendaylight/controller/config/yang/logback/config/LogbackModuleTest.java new file mode 100644 index 0000000000..1e2327eb4a --- /dev/null +++ b/opendaylight/config/logback-config/src/test/java/org/opendaylight/controller/config/yang/logback/config/LogbackModuleTest.java @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.config.yang.logback.config; + +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.junit.matchers.JUnitMatchers.containsString; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.ObjectName; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.opendaylight.controller.config.api.ConflictingVersionException; +import org.opendaylight.controller.config.api.ValidationException; +import org.opendaylight.controller.config.api.jmx.CommitStatus; +import org.opendaylight.controller.config.manager.impl.AbstractConfigTest; +import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver; +import org.opendaylight.controller.config.util.ConfigTransactionJMXClient; + +public class LogbackModuleTest extends AbstractConfigTest { + + private static final String INSTANCE_NAME = "singleton"; + + private LogbackModuleFactory factory; + + @Before + public void setUp() throws IOException, ClassNotFoundException, + InterruptedException { + + factory = new LogbackModuleFactory(); + super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver( + factory)); + } + + @Test + public void testCreateBean() throws InstanceAlreadyExistsException { + + CommitStatus status = createBeans( + true, "target/rollingApp", + "%-4relative [%thread] %-5level %logger{35} - %msg%n", "30MB", + 1, 5, "target/%i.log", "rolling", "consoleName", "ALL", + "logger1", "DEBUG").commit(); + + assertBeanCount(1, factory.getImplementationName()); + assertStatus(status, 1, 0, 0); + } + + @Test + public void testReusingInstance() throws InstanceAlreadyExistsException { + createBeans( + true, "target/rollingApp", + "%-4relative [%thread] %-5level %logger{35} - %msg%n", "30MB", + 1, 5, "target/%i.log", "rolling", "consoleName", "ALL", + "logger1", "DEBUG").commit(); + + assertBeanCount(1, factory.getImplementationName()); + + ConfigTransactionJMXClient transaction = configRegistryClient + .createTransaction(); + CommitStatus status = transaction.commit(); + + assertBeanCount(1, factory.getImplementationName()); + assertStatus(status, 0, 0, 1); + } + + @Test + public void testRecreateInstance() throws InstanceAlreadyExistsException, + ValidationException, ConflictingVersionException, + InstanceNotFoundException { + createBeans( + true, "target/rollingApp", + "%-4relative [%thread] %-5level %logger{35} - %msg%n", "30MB", + 1, 5, "target/%i.log", "rolling", "consoleName", "ALL", + "logger1", "DEBUG").commit(); + + assertBeanCount(1, LogbackModuleFactory.NAME); + ConfigTransactionJMXClient transaction = configRegistryClient + .createTransaction(); + + ObjectName logback = transaction.lookupConfigBean( + LogbackModuleFactory.NAME, "singleton"); + LogbackModuleMXBean nwBean = transaction.newMXBeanProxy(logback, + LogbackModuleMXBean.class); + CommitStatus status = transaction.commit(); + assertBeanCount(1, LogbackModuleFactory.NAME); + + assertStatus(status, 0, 0, 1); + } + + @Test + public void testDestroyInstance() throws InstanceNotFoundException, + InstanceAlreadyExistsException { + createBeans( + true, "target/rollingApp", + "%-4relative [%thread] %-5level %logger{35} - %msg%n", "30MB", + 1, 5, "target/%i.log", "rolling", "consoleName", "ALL", + "logger1", "DEBUG").commit(); + assertBeanCount(1, factory.getImplementationName()); + + ConfigTransactionJMXClient transaction = configRegistryClient + .createTransaction(); + transaction.destroyConfigBean(factory.getImplementationName(), + INSTANCE_NAME); + CommitStatus status = transaction.commit(); + + assertBeanCount(0, factory.getImplementationName()); + assertStatus(status, 0, 0, 0); + } + + @Ignore + @Test + public void testValidation1() throws InstanceAlreadyExistsException { + try { + createBeans( + true, "target/rollingApp", + "%-4relative [%thread] %-5level %logger{35} - %msg%n", + "30MB", 1, 5, "target/%i.log", "rolling", "consoleName", + "ALL", "logger1", "DEBUG").commit(); + fail(); + } catch (ValidationException e) { + assertThat(e.getFailedValidations().toString(), + containsString("FileName is null")); + } + } + + @Test + public void testValidation2() throws InstanceAlreadyExistsException { + try { + createBeans( + true, "target/rollingApp", null, "30MB", 1, 5, "target/%i.log", + "rolling", "consoleName", "ALL", "logger1", "DEBUG") + .commit(); + fail(); + } catch (ValidationException e) { + assertThat(e.getFailedValidations().toString(), + containsString("EncoderPattern is null")); + } + } + + @Test + public void testValidation4() throws InstanceAlreadyExistsException { + try { + createBeans( + true, "target/rollingApp", + "%-4relative [%thread] %-5level %logger{35} - %msg%n", + null, 1, 5, "target/%i.log", "rolling", "consoleName", + "ALL", "logger1", "DEBUG").commit(); + fail(); + } catch (ValidationException e) { + assertThat(e.getFailedValidations().toString(), + containsString("MaxFileSize is null")); + } + } + + @Test + public void testValidation6() throws InstanceAlreadyExistsException { + try { + createBeans( + true, "", "%-4relative [%thread] %-5level %logger{35} - %msg%n", + "30MB", 1, 5, "target/%i.log", "rolling", "consoleName", + "ALL", "logger1", "DEBUG").commit(); + fail(); + } catch (ValidationException e) { + assertThat(e.getFailedValidations().toString(), + containsString("FileName needs to be set")); + } + } + + @Test + public void testValidation7() throws InstanceAlreadyExistsException { + try { + createBeans( + + true, "target/rollingApp", "", "30MB", 1, 5, "target/%i.log", + "rolling", "consoleName", "ALL", "logger1", "DEBUG") + .commit(); + fail(); + } catch (ValidationException e) { + assertThat(e.getFailedValidations().toString(), + containsString("EncoderPattern needs to be set")); + } + } + + @Test + public void testValidation8() throws InstanceAlreadyExistsException { + try { + createBeans( + true, "target/rollingApp", + "%-4relative [%thread] %-5level %logger{35} - %msg%n", + "30MB", 1, 5, "target/%i.log", "rolling", "consoleName", + "ALL", null, "DEBUG").commit(); + fail(); + } catch (ValidationException e) { + assertThat(e.getFailedValidations().toString(), + containsString("LoggerName is null")); + } + } + + @Test + public void testValidation9() throws InstanceAlreadyExistsException { + try { + createBeans( + true, "target/rollingApp", + "%-4relative [%thread] %-5level %logger{35} - %msg%n", + "30MB", 1, 5, "target/%i.log", "rolling", "consoleName", + "ALL", "", "DEBUG").commit(); + fail(); + } catch (ValidationException e) { + assertThat(e.getFailedValidations().toString(), + containsString("LoggerName needs to be set")); + } + } + + @Test + public void testValidation10() throws InstanceAlreadyExistsException { + try { + createBeans( + true, "target/rollingApp", + "%-4relative [%thread] %-5level %logger{35} - %msg%n", + "30MB", null, 5, "target/%i.log", "rolling", "consoleName", + "ALL", "logger1", "DEBUG").commit(); + fail(); + } catch (ValidationException e) { + assertThat(e.getFailedValidations().toString(), + containsString("MinIndex is null")); + } + } + + @Test + public void testValidation11() throws InstanceAlreadyExistsException { + try { + createBeans( + true, "target/rollingApp", + "%-4relative [%thread] %-5level %logger{35} - %msg%n", + "30MB", 1, null, "target/%i.log", "rolling", "consoleName", + "ALL", "logger1", "DEBUG").commit(); + fail(); + } catch (ValidationException e) { + assertThat(e.getFailedValidations().toString(), + containsString("MaxIndex is null")); + } + } + + private ConfigTransactionJMXClient createBeans( + Boolean isAppend, String rollingFileName, String encoderPattern, + String maxFileSize, Integer minIndex, Integer maxIndex, + String fileNamePattern, String rollingName, String consoleName, + String thresholdFilter, String loggerName, String level ) + throws InstanceAlreadyExistsException { + ConfigTransactionJMXClient transaction = configRegistryClient + .createTransaction(); + ObjectName nameCreated = transaction.createModule( + factory.getImplementationName(), INSTANCE_NAME); + LogbackModuleMXBean bean = transaction.newMXBeanProxy(nameCreated, + LogbackModuleMXBean.class); + + List rollingAppenders = new ArrayList<>(); + RollingFileAppenderTO rollingAppender = new RollingFileAppenderTO(); + rollingAppender.setAppend(isAppend); + rollingAppender.setEncoderPattern(encoderPattern); + rollingAppender.setFileName(rollingFileName); + rollingAppender.setMaxFileSize(maxFileSize); + rollingAppender.setMaxIndex(maxIndex); + rollingAppender.setMinIndex(minIndex); + rollingAppender.setFileNamePattern(fileNamePattern); + rollingAppender.setName(rollingName); + rollingAppenders.add(rollingAppender); + + List consoleAppenders = new ArrayList<>(); + ConsoleAppenderTO consoleAppender = new ConsoleAppenderTO(); + consoleAppender.setEncoderPattern(encoderPattern); + consoleAppender.setName(consoleName); + consoleAppender.setThresholdFilter(thresholdFilter); + consoleAppenders.add(consoleAppender); + + List loggers = new ArrayList<>(); + + LoggerTO logger = new LoggerTO(); + + logger.setAppenders(Arrays. asList()); + + logger.setLevel(level); + logger.setLoggerName(loggerName); + loggers.add(logger); + bean.setLoggerTO(loggers); + bean.setRollingFileAppenderTO(rollingAppenders); + bean.setConsoleAppenderTO(consoleAppenders); + + transaction.validateConfig(); + + return transaction; + } + +} diff --git a/opendaylight/config/logback-config/src/test/java/org/opendaylight/controller/config/yang/logback/config/LogbackModuleWithInitialConfigurationTest.java b/opendaylight/config/logback-config/src/test/java/org/opendaylight/controller/config/yang/logback/config/LogbackModuleWithInitialConfigurationTest.java new file mode 100644 index 0000000000..2bdfa47b54 --- /dev/null +++ b/opendaylight/config/logback-config/src/test/java/org/opendaylight/controller/config/yang/logback/config/LogbackModuleWithInitialConfigurationTest.java @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.config.yang.logback.config; + +import static org.junit.Assert.assertEquals; + +import java.io.File; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.management.InstanceAlreadyExistsException; +import javax.management.JMX; +import javax.management.ObjectName; + +import org.apache.commons.io.FileUtils; +import org.junit.Before; +import org.junit.Test; +import org.opendaylight.controller.config.api.jmx.ObjectNameUtil; +import org.opendaylight.controller.config.manager.impl.AbstractConfigTest; +import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver; +import org.opendaylight.controller.config.util.ConfigTransactionClient; +import org.opendaylight.controller.config.util.ConfigTransactionJMXClient; +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.encoder.PatternLayoutEncoder; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.rolling.FixedWindowRollingPolicy; +import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy; + +import com.google.common.collect.Lists; + +public class LogbackModuleWithInitialConfigurationTest extends + AbstractConfigTest { + + private LogbackModuleFactory factory; + + @Before + public void setUp() throws IOException, ClassNotFoundException { + + factory = new LogbackModuleFactory(); + super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver( + factory)); + } + + /** + * Tests that initial configuration was changed. Changed attributes: + * location, fileName, duplicateInsertTries + * + */ + @Test + public void test() throws Exception { + + createBeans(); + + ConfigTransactionClient transaction = configRegistryClient + .createTransaction(); + + LogbackModuleMXBean bean = JMX.newMXBeanProxy( + ManagementFactory.getPlatformMBeanServer(), + transaction.lookupConfigBean("logback", "singleton"), + LogbackModuleMXBean.class); + assertEquals(1, bean.getConsoleAppenderTO().size()); + assertEquals(1, bean.getRollingFileAppenderTO().size()); + assertEquals(1, bean.getLoggerTO().size()); + + RollingFileAppenderTO rolling = new RollingFileAppenderTO(); + RollingFileAppenderTO old = bean + .getRollingFileAppenderTO().get(0); + rolling.setAppend(old.getAppend()); + rolling.setEncoderPattern(old.getEncoderPattern()); + rolling.setFileName("target/logFile1.log"); + rolling.setFileNamePattern("target/%i.log"); + rolling.setMaxFileSize(old.getMaxFileSize()); + rolling.setMinIndex(old.getMinIndex()); + rolling.setMaxIndex(old.getMaxIndex()); + rolling.setName("FILE"); + + ConsoleAppenderTO console = new ConsoleAppenderTO(); + console.setEncoderPattern("%date %level [%thread] %logger{10} %msg%n"); + console.setName("SYSTEM"); + console.setThresholdFilter("DEBUG"); + + bean.setConsoleAppenderTO(Lists.newArrayList(console)); + bean.setRollingFileAppenderTO(Lists.newArrayList(rolling)); + + LoggerTO logger = new LoggerTO(); + logger.setLevel("INFO"); + logger.setLoggerName("logger"); + logger.setAppenders(Lists.newArrayList("SYSTEM")); + List loggers = Lists + .newArrayList(logger); + bean.setLoggerTO(loggers); + + transaction.commit(); + + LogbackModuleMXBean logback = configRegistryClient.newMXBeanProxy( + ObjectNameUtil.createReadOnlyModuleON("logback", "singleton"), + LogbackModuleMXBean.class); + + + List rollingList = logback + .getRollingFileAppenderTO(); + assertEquals(1, rollingList.size()); + + RollingFileAppenderTO rollingApp = rollingList + .get(0); + assertEquals(rollingApp.getFileName(), "target/logFile1.log"); + assertEquals(rollingApp.getName(), "FILE"); + + List consoleList = logback + .getConsoleAppenderTO(); + assertEquals(1, consoleList.size()); + + ConsoleAppenderTO consoleApp = consoleList + .get(0); + assertEquals(consoleApp.getThresholdFilter(), "DEBUG"); + assertEquals(consoleApp.getName(), "SYSTEM"); + + loggers = logback.getLoggerTO(); + assertEquals(1, loggers.size()); + + } + + public ObjectName createBeans() throws JoranException, + InstanceAlreadyExistsException, IOException { + + LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); + + JoranConfigurator configurator = new JoranConfigurator(); + configurator.setContext(lc); + configurator + .doConfigure("src/test/resources/simple_config_logback.xml"); + File f = new File("target/it"); + if (f.exists()) + FileUtils.cleanDirectory(f); + ch.qos.logback.classic.Logger logger = lc.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME); + ch.qos.logback.core.rolling.RollingFileAppender fileAppender = (ch.qos.logback.core.rolling.RollingFileAppender) logger + .getAppender("VARLOGFILE"); + fileAppender.start(); + + ch.qos.logback.core.ConsoleAppender consoleAppender = (ch.qos.logback.core.ConsoleAppender) logger + .getAppender("STDOUT"); + consoleAppender.start(); + List rollingAppenders = new ArrayList<>(); + RollingFileAppenderTO rollingApp = new RollingFileAppenderTO(); + rollingApp.setAppend(fileAppender.isAppend()); + PatternLayoutEncoder enc = (PatternLayoutEncoder) fileAppender + .getEncoder(); + rollingApp.setEncoderPattern(enc.getPattern()); + rollingApp.setFileName(fileAppender.getFile()); + FixedWindowRollingPolicy rollingPolicy = (FixedWindowRollingPolicy) fileAppender + .getRollingPolicy(); + rollingApp.setMaxIndex(rollingPolicy.getMaxIndex()); + rollingApp.setMinIndex(rollingPolicy.getMinIndex()); + SizeBasedTriggeringPolicy triggeringPolicy = (SizeBasedTriggeringPolicy) fileAppender + .getTriggeringPolicy(); + rollingApp.setMaxFileSize(triggeringPolicy.getMaxFileSize()); + rollingApp.setName(fileAppender.getName()); + rollingApp.setFileNamePattern(rollingPolicy.getFileNamePattern()); + rollingAppenders.add(rollingApp); + + assertEquals(rollingApp.getFileName(), "target/osgi.log"); + assertEquals(rollingApp.getMaxFileSize(), "50MB"); + assertEquals(rollingApp.getName(), "VARLOGFILE"); + + List consoleAppenders = new ArrayList<>(); + ConsoleAppenderTO consoleApp = new ConsoleAppenderTO(); + enc = (PatternLayoutEncoder) consoleAppender.getEncoder(); + consoleApp.setEncoderPattern(enc.getPattern()); + consoleApp.setName(consoleAppender.getName()); + consoleApp.setThresholdFilter("ALL"); + consoleAppenders.add(consoleApp); + + List loggersDTOs = new ArrayList<>(); + LoggerTO log = new LoggerTO(); + log.setAppenders(Arrays.asList(fileAppender.getName(), + consoleApp.getName())); + + log.setLevel(logger.getLevel().toString()); + log.setLoggerName(logger.getName()); + loggersDTOs.add(log); + + ConfigTransactionJMXClient transaction = configRegistryClient + .createTransaction(); + ObjectName nameCreated = transaction.createModule( + factory.getImplementationName(), "singleton"); + LogbackModuleMXBean bean = transaction.newMXBeanProxy(nameCreated, + LogbackModuleMXBean.class); + + bean.setLoggerTO(loggersDTOs); + bean.setRollingFileAppenderTO(rollingAppenders); + bean.setConsoleAppenderTO(consoleAppenders); + + transaction.commit(); + + return nameCreated; + } +} diff --git a/opendaylight/config/logback-config/src/test/java/org/opendaylight/controller/config/yang/logback/config/LogbackWithXmlConfigModuleTest.java b/opendaylight/config/logback-config/src/test/java/org/opendaylight/controller/config/yang/logback/config/LogbackWithXmlConfigModuleTest.java new file mode 100644 index 0000000000..ce3a019410 --- /dev/null +++ b/opendaylight/config/logback-config/src/test/java/org/opendaylight/controller/config/yang/logback/config/LogbackWithXmlConfigModuleTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.config.yang.logback.config; + +import static org.junit.Assert.assertEquals; + +import java.io.File; +import java.io.IOException; +import java.lang.management.ManagementFactory; + +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.JMX; +import javax.management.ObjectName; + +import org.apache.commons.io.FileUtils; +import org.junit.Before; +import org.junit.Test; +import org.opendaylight.controller.config.manager.impl.AbstractConfigTest; +import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver; +import org.opendaylight.controller.config.util.ConfigTransactionJMXClient; +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.core.joran.spi.JoranException; + +public class LogbackWithXmlConfigModuleTest extends AbstractConfigTest { + + private LogbackModuleFactory factory; + private LoggerContext lc; + + @Before + public void setUp() throws JoranException, IOException { + + factory = new LogbackModuleFactory(); + super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver( + factory)); + + lc = (LoggerContext) LoggerFactory.getILoggerFactory(); + JoranConfigurator configurator = new JoranConfigurator(); + lc.reset(); + configurator.setContext(lc); + configurator + .doConfigure("src/test/resources/simple_config_logback.xml"); + File f = new File("target/it"); + if (f.exists()) + FileUtils.cleanDirectory(f); + } + + /** + * Tests configuration of Logger factory. + */ + @Test + public void test() throws InstanceAlreadyExistsException, + InstanceNotFoundException { + + ConfigTransactionJMXClient transaction = configRegistryClient + .createTransaction(); + ObjectName nameCreated = transaction.createModule( + factory.getImplementationName(), "singleton"); + + LogbackModuleMXBean bean = transaction.newMXBeanProxy(nameCreated, + LogbackModuleMXBean.class); + + assertEquals(1, bean.getConsoleAppenderTO().size()); + + assertEquals(1, bean.getRollingFileAppenderTO().size()); + + transaction.commit(); + + transaction = configRegistryClient.createTransaction(); + + nameCreated = transaction.lookupConfigBean( + factory.getImplementationName(), "singleton"); + + bean = JMX.newMXBeanProxy(platformMBeanServer, nameCreated, + LogbackModuleMXBean.class); + + assertEquals(1, bean.getConsoleAppenderTO().size()); + assertEquals(1, bean.getRollingFileAppenderTO().size()); + + } + + /** + * Tests filtering loggers. Loggers inherited from ROOT logger and duplicate + * loggers should be removed. + */ + @Test + public void testAllLoggers() throws InstanceAlreadyExistsException, + InstanceNotFoundException { + ConfigTransactionJMXClient transaction = configRegistryClient + .createTransaction(); + transaction.createModule(factory.getImplementationName(), "singleton"); + + transaction.commit(); + + transaction = configRegistryClient.createTransaction(); + + LogbackModuleMXBean bean = JMX.newMXBeanProxy( + ManagementFactory.getPlatformMBeanServer(), + transaction.lookupConfigBean("logback", "singleton"), + LogbackModuleMXBean.class); + + assertEquals(5, bean.getLoggerTO().size()); + } + +} diff --git a/opendaylight/config/logback-config/src/test/resources/simple_config_logback.xml b/opendaylight/config/logback-config/src/test/resources/simple_config_logback.xml new file mode 100644 index 0000000000..f4535de73f --- /dev/null +++ b/opendaylight/config/logback-config/src/test/resources/simple_config_logback.xml @@ -0,0 +1,76 @@ + + + + true + + + + + INFO + + + [%d{HH:mm:ss.SSS}] [%thread] %-5level %logger - %msg%n + + + + + + target/osgi.log + true + + [%d{HH:mm:ss.SSS}] [%thread] %-5level %logger - %msg%n + + + /opt/Demo1/logs/osgi.log.%i.gz + 1 + 5 + + + + 50MB + + + + + + target/bgp.log + false + + [%d{HH:mm:ss.SSS}] [%thread] %-5level %logger - %msg%n + + + /opt/Demo1/logs/bgp.log.%i.gz + 1 + 5 + + + + 5MB + + + + + + + + + + + + + + + + + + + diff --git a/opendaylight/config/pom.xml b/opendaylight/config/pom.xml index 7607fb7793..3ebfad3f30 100755 --- a/opendaylight/config/pom.xml +++ b/opendaylight/config/pom.xml @@ -28,6 +28,7 @@ yang-store-api yang-store-impl yang-test + logback-config UTF-8