[logback-dev] svn commit: r2344 - in logback/trunk/logback-classic/src: main/java/ch/qos/logback/classic/joran/action main/java/ch/qos/logback/classic/turbo test/input/joran test/java/ch/qos/logback/classic/joran
noreply.ceki at qos.ch
noreply.ceki at qos.ch
Tue Jul 14 16:59:25 CEST 2009
Author: ceki
Date: Tue Jul 14 16:59:25 2009
New Revision: 2344
Added:
logback/trunk/logback-classic/src/test/input/joran/scan1.xml
Modified:
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/ConfigurationAction.java
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/turbo/ReconfigureOnChangeFilter.java
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/joran/JoranConfiguratorTest.java
Log:
Adding support for automatic reconfiguration. Related to LBCORE-59
This implementation checks for changes in the config file
every N seconds, where N is a user specified parameter. However, as a
performance twist, the check is performed every 16 logging events.
It's still ongoing work
Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/ConfigurationAction.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/ConfigurationAction.java (original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/ConfigurationAction.java Tue Jul 14 16:59:25 2009
@@ -1,7 +1,7 @@
/**
- * LOGBack: the generic, reliable, fast and flexible logging framework.
+ * Logback: the generic, reliable, fast and flexible logging framework.
*
- * Copyright (C) 1999-2006, QOS.ch
+ * Copyright (C) 2000-2009, QOS.ch
*
* This library is free software, you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
@@ -13,42 +13,73 @@
import org.xml.sax.Attributes;
import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.turbo.ReconfigureOnChangeFilter;
import ch.qos.logback.core.joran.action.Action;
import ch.qos.logback.core.joran.spi.InterpretationContext;
+import ch.qos.logback.core.util.Duration;
+import ch.qos.logback.core.util.OptionHelper;
import ch.qos.logback.core.util.StatusPrinter;
-
-
public class ConfigurationAction extends Action {
static final String INTERNAL_DEBUG_ATTR = "debug";
+ static final String SCAN_ATTR = "scan";
+ static final String SCAN_PERIOD_ATTR = "scanPeriod";
+
boolean debugMode = false;
public void begin(InterpretationContext ec, String name, Attributes attributes) {
String debugAttrib = attributes.getValue(INTERNAL_DEBUG_ATTR);
- if (
- (debugAttrib == null) || debugAttrib.equals("")
- || debugAttrib.equals("false") || debugAttrib.equals("null")) {
+ if (OptionHelper.isEmpty(debugAttrib)
+ || debugAttrib.equalsIgnoreCase("false")
+ || debugAttrib.equalsIgnoreCase("null")) {
addInfo(INTERNAL_DEBUG_ATTR + " attribute not set");
} else {
- //LoggerContext loggerContext = (LoggerContext) context;
- //ConfiguratorBase.attachTemporaryConsoleAppender(context);
-
+ // LoggerContext loggerContext = (LoggerContext) context;
+ // ConfiguratorBase.attachTemporaryConsoleAppender(context);
+
debugMode = true;
}
-
- // the context is turbo filter attachable, so it is pushed on top of the stack
+
+ processScanAttrib(attributes);
+
+ // the context is turbo filter attachable, so it is pushed on top of the
+ // stack
ec.pushObject(getContext());
}
+ void processScanAttrib(Attributes attributes) {
+ String scanAttrib = attributes.getValue(SCAN_ATTR);
+ if (!OptionHelper.isEmpty(scanAttrib)
+ && !"false".equalsIgnoreCase(scanAttrib)) {
+ ReconfigureOnChangeFilter rocf = new ReconfigureOnChangeFilter();
+ rocf.setContext(context);
+ String scanPeriodAttrib = attributes.getValue(SCAN_PERIOD_ATTR);
+ if (!OptionHelper.isEmpty(scanPeriodAttrib)) {
+ try {
+ Duration duration = Duration.valueOf(scanPeriodAttrib);
+ rocf.setRefreshPeriod(duration.getMilliseconds());
+ addInfo("Setting ReconfigureOnChangeFilter scanning period to "
+ + duration);
+ } catch (NumberFormatException nfe) {
+ addError("Error while converting [" + scanAttrib + "] to long", nfe);
+ }
+ }
+ rocf.start();
+ LoggerContext lc = (LoggerContext) context;
+ addInfo("Adding ReconfigureOnChangeFilter as a turbo filter");
+ lc.addTurboFilter(rocf);
+ }
+ }
+
public void end(InterpretationContext ec, String name) {
if (debugMode) {
addInfo("End of configuration.");
LoggerContext loggerContext = (LoggerContext) context;
StatusPrinter.print(loggerContext);
-
- //LoggerContext loggerContext = (LoggerContext) context;
- //ConfiguratorBase.detachTemporaryConsoleAppender(repository, errorList);
+
+ // LoggerContext loggerContext = (LoggerContext) context;
+ // ConfiguratorBase.detachTemporaryConsoleAppender(repository, errorList);
}
ec.popObject();
}
Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/turbo/ReconfigureOnChangeFilter.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/turbo/ReconfigureOnChangeFilter.java (original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/turbo/ReconfigureOnChangeFilter.java Tue Jul 14 16:59:25 2009
@@ -36,10 +36,11 @@
protected long nextCheck;
long lastModified;
+
@Override
public void start() {
URL url = (URL) context.getObject(CoreConstants.URL_OF_LAST_CONFIGURATION_VIA_JORAN);
- if(url == null) {
+ if(url != null) {
fileToScan = convertToFile(url);
if(fileToScan != null) {
long inSeconds = refreshPeriod/1000;
@@ -64,14 +65,26 @@
}
}
+ // a counter of the number of time the decide method is called
+ private volatile int invocationCounter = 0;
+
@Override
public FilterReply decide(Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t) {
if(!isStarted()) {
return FilterReply.NEUTRAL;
}
+ System.out.println("counter="+invocationCounter+", format="+format);
+ // for performance reasons, check for changes every 16 invocations
+ if(((invocationCounter++) & 0xF) != 0xF) {
+ return FilterReply.NEUTRAL;
+ }
+
+
boolean changed = changeDetected();
if(changed) {
+ addInfo("["+fileToScan + "] change detected. Reconfiguring");
+ addInfo("Resetting and reconfiguring context ["+context.getName()+"]");
reconfigure();
}
return FilterReply.NEUTRAL;
@@ -102,4 +115,12 @@
addError("Failure during reconfiguration", e);
}
}
+
+ public long getRefreshPeriod() {
+ return refreshPeriod;
+ }
+
+ public void setRefreshPeriod(long refreshPeriod) {
+ this.refreshPeriod = refreshPeriod;
+ }
}
Added: logback/trunk/logback-classic/src/test/input/joran/scan1.xml
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/input/joran/scan1.xml Tue Jul 14 16:59:25 2009
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE configuration>
+
+<configuration scan="true" scanPeriod="1 millisecond">
+
+ <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+ <layout class="ch.qos.logback.classic.PatternLayout">
+ <Pattern>yx %d - %m%n"</Pattern>
+ </layout>
+ </appender>
+
+ <root level="TRACE">
+ <appender-ref ref="CONSOLE" />
+ </root>
+
+</configuration>
+
\ No newline at end of file
Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/joran/JoranConfiguratorTest.java
==============================================================================
--- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/joran/JoranConfiguratorTest.java (original)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/joran/JoranConfiguratorTest.java Tue Jul 14 16:59:25 2009
@@ -13,6 +13,9 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import java.io.File;
+import java.io.IOException;
+
import org.junit.Test;
import org.slf4j.MDC;
@@ -27,7 +30,9 @@
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.read.ListAppender;
+import ch.qos.logback.core.status.StatusChecker;
import ch.qos.logback.core.testUtil.StringListAppender;
+import ch.qos.logback.core.util.StatusPrinter;
public class JoranConfiguratorTest {
@@ -67,12 +72,14 @@
}
@Test
- public void testRootLoggerLevelSettingBySystemProperty() throws JoranException {
+ public void testRootLoggerLevelSettingBySystemProperty()
+ throws JoranException {
String propertyName = "logback.level";
-
+
System.setProperty(propertyName, "INFO");
- configure(TeztConstants.TEST_DIR_PREFIX + "input/joran/rootLevelByProperty.xml");
- //StatusPrinter.print(loggerContext);
+ configure(TeztConstants.TEST_DIR_PREFIX
+ + "input/joran/rootLevelByProperty.xml");
+ // StatusPrinter.print(loggerContext);
ListAppender listAppender = (ListAppender) root.getAppender("LIST");
assertEquals(0, listAppender.list.size());
String msg = "hello world";
@@ -80,14 +87,15 @@
assertEquals(0, listAppender.list.size());
System.clearProperty(propertyName);
}
-
+
@Test
public void testLoggerLevelSettingBySystemProperty() throws JoranException {
String propertyName = "logback.level";
-
+
System.setProperty(propertyName, "DEBUG");
- configure(TeztConstants.TEST_DIR_PREFIX + "input/joran/loggerLevelByProperty.xml");
- //StatusPrinter.print(loggerContext);
+ configure(TeztConstants.TEST_DIR_PREFIX
+ + "input/joran/loggerLevelByProperty.xml");
+ // StatusPrinter.print(loggerContext);
ListAppender listAppender = (ListAppender) root.getAppender("LIST");
assertEquals(0, listAppender.list.size());
String msg = "hello world";
@@ -95,11 +103,11 @@
assertEquals(1, listAppender.list.size());
System.clearProperty(propertyName);
}
-
+
@Test
public void testStatusListener() throws JoranException {
configure(TeztConstants.TEST_DIR_PREFIX + "input/joran/statusListener.xml");
- //StatusPrinter.print(loggerContext);
+ // StatusPrinter.print(loggerContext);
}
@Test
@@ -108,7 +116,7 @@
configure(TeztConstants.TEST_DIR_PREFIX + "input/joran/contextRename.xml");
assertEquals("wombat", loggerContext.getName());
}
-
+
@Test
public void testEval() throws JoranException {
configure(TeztConstants.TEST_DIR_PREFIX + "input/joran/callerData.xml");
@@ -122,7 +130,7 @@
assertNotNull(slAppender);
assertEquals(2, slAppender.strList.size());
assertTrue(slAppender.strList.get(0).contains(" DEBUG - toto"));
-
+
String str1 = slAppender.strList.get(1);
assertTrue(str1.contains("Caller+0"));
assertTrue(str1.contains(" DEBUG - hello world"));
@@ -159,7 +167,7 @@
public void testLevelFilter() throws JoranException {
configure(TeztConstants.TEST_DIR_PREFIX + "input/joran/levelFilter.xml");
- //StatusPrinter.print(loggerContext);
+ // StatusPrinter.print(loggerContext);
logger.warn("hello");
logger.error("to be ignored");
@@ -179,7 +187,7 @@
public void testEvaluatorFilter() throws JoranException {
configure(TeztConstants.TEST_DIR_PREFIX + "input/joran/evaluatorFilter.xml");
- //StatusPrinter.print(loggerContext);
+ // StatusPrinter.print(loggerContext);
logger.warn("hello");
logger.error("to be ignored");
@@ -239,4 +247,26 @@
le = (ILoggingEvent) listAppender.list.get(1);
assertEquals("hello user2", le.getMessage());
}
+
+ @Test
+ public void scan1() throws JoranException, IOException, InterruptedException {
+
+ String configFileAsStr = TeztConstants.TEST_DIR_PREFIX
+ + "input/joran/scan1.xml";
+ configure(configFileAsStr);
+
+ File file = new File(configFileAsStr);
+ file.setLastModified(System.currentTimeMillis());
+
+ // scanning requires 16 logs
+ for (int i = 0; i < 16; i++) {
+ logger.debug("after " + i);
+ }
+
+ StatusChecker checker = new StatusChecker(loggerContext);
+ assertTrue(checker.isErrorFree());
+ assertTrue(checker.containsMatch("Resetting and reconfiguring context"));
+ StatusPrinter.print(loggerContext);
+
+ }
}
More information about the logback-dev
mailing list