The branch, master has been updated
via 3f49ee7c4b0e444dc6748509442a1ea1e6f4c5e5 (commit)
from 6c5ba501831d19879e6865f795a1c294ad25bf7d (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
http://git.qos.ch/gitweb/?p=logback.git;a=commit;h=3f49ee7c4b0e444dc6748509442a1ea1e6f4c5e5
http://github.com/ceki/logback/commit/3f49ee7c4b0e444dc6748509442a1ea1e6f4c5e5
commit 3f49ee7c4b0e444dc6748509442a1ea1e6f4c5e5
Author: Ceki Gulcu <[email protected]>
Date: Thu Dec 3 16:17:14 2009 +0100
- ongoing work on LBCLASSIC-154 and LBCLASSIC-153
diff --git
a/logback-classic/src/main/java/ch/qos/logback/classic/turbo/ReconfigureOnChangeFilter.java
b/logback-classic/src/main/java/ch/qos/logback/classic/turbo/ReconfigureOnChangeFilter.java
index af5bc6a..fe36372 100644
---
a/logback-classic/src/main/java/ch/qos/logback/classic/turbo/ReconfigureOnChangeFilter.java
+++
b/logback-classic/src/main/java/ch/qos/logback/classic/turbo/ReconfigureOnChangeFilter.java
@@ -47,7 +47,9 @@ public class ReconfigureOnChangeFilter extends TurboFilter {
long refreshPeriod = DEFAULT_REFRESH_PERIOD;
File fileToScan;
protected long nextCheck;
- long lastModified;
+ volatile long lastModified;
+
+ Object lock = new Object();
@Override
public void start() {
@@ -56,7 +58,7 @@ public class ReconfigureOnChangeFilter extends TurboFilter {
if (url != null) {
fileToScan = convertToFile(url);
if (fileToScan != null) {
- synchronized (context.getConfigurationLock()) {
+ synchronized (lock) {
long inSeconds = refreshPeriod / 1000;
addInfo("Will scan for changes in file [" + fileToScan + "] every "
+ inSeconds + " seconds. ");
@@ -102,27 +104,35 @@ public class ReconfigureOnChangeFilter extends
TurboFilter {
return FilterReply.NEUTRAL;
}
- synchronized (context.getConfigurationLock()) {
+ synchronized (lock) {
boolean changed = changeDetected();
if (changed) {
- addInfo("Detected change in [" + fileToScan + "]");
- addInfo("Resetting and reconfiguring context [" + context.getName()
- + "]");
- reconfigure();
+ detachReconfigurationToNewThread();
+
}
}
+
return FilterReply.NEUTRAL;
}
+ // by detaching reconfiguration to a new thread, we release the various
+ // locks held by the current thread, in particular, the AppenderAttachable
+ // reader lock.
+ private void detachReconfigurationToNewThread() {
+ // Even though this method resets the loggerContext, which clears the
+ // list of turbo filters including this instance, it is still possible
+ // for this instance to be subsequently invoked by another thread if it
+ // was already executing when the context was reset.
+ disableSubsequentRecofiguration();
+ addInfo("Detected change in [" + fileToScan + "]");
+ addInfo("Resetting and reconfiguring context [" + context.getName() + "]");
+ new ReconfiguringThread().start();
+ }
+
void updateNextCheck(long now) {
nextCheck = now + refreshPeriod;
}
-// String stem() {
-// return currentThreadName() + ", context " + context.getName()
-// + ", nextCheck=" + (nextCheck - INIT);
-// }
-
// This method is synchronized to prevent near-simultaneous re-configurations
protected boolean changeDetected() {
long now = System.currentTimeMillis();
@@ -141,25 +151,6 @@ public class ReconfigureOnChangeFilter extends TurboFilter
{
lastModified = SENTINEL;
}
- protected void reconfigure() {
- // Even though this method resets the loggerContext, which clears the list
- // of turbo filters including this instance, it is still possible for this
- // instance to be subsequently invoked by another thread if it was already
- // executing when the context was reset.
- // We prevent multiple reconfigurations (for the same file change event) by
- // setting an appropriate sentinel value for lastMofidied field.
- disableSubsequentRecofiguration();
- JoranConfigurator jc = new JoranConfigurator();
- jc.setContext(context);
- LoggerContext lc = (LoggerContext) context;
- lc.reset();
- try {
- jc.doConfigure(fileToScan);
- } catch (JoranException e) {
- addError("Failure during reconfiguration", e);
- }
- }
-
public long getRefreshPeriod() {
return refreshPeriod;
}
@@ -167,4 +158,18 @@ public class ReconfigureOnChangeFilter extends TurboFilter
{
public void setRefreshPeriod(long refreshPeriod) {
this.refreshPeriod = refreshPeriod;
}
+
+ class ReconfiguringThread extends Thread {
+ public void run() {
+ JoranConfigurator jc = new JoranConfigurator();
+ jc.setContext(context);
+ LoggerContext lc = (LoggerContext) context;
+ lc.reset();
+ try {
+ jc.doConfigure(fileToScan);
+ } catch (JoranException e) {
+ addError("Failure during reconfiguration", e);
+ }
+ }
+ }
}
diff --git a/logback-classic/src/test/input/turbo/scan 1.xml
b/logback-classic/src/test/input/turbo/scan 1.xml
index dc3d0fe..39878d3 100644
--- a/logback-classic/src/test/input/turbo/scan 1.xml
+++ b/logback-classic/src/test/input/turbo/scan 1.xml
@@ -2,6 +2,9 @@
<!DOCTYPE configuration>
<configuration scan="true" scanPeriod="50 millisecond">
- <root level="ERROR"/>
+
+ <root level="ERROR">
+ </root>
+
</configuration>
\ No newline at end of file
diff --git a/logback-classic/src/test/input/turbo/scan_lbclassic154.xml
b/logback-classic/src/test/input/turbo/scan_lbclassic154.xml
new file mode 100644
index 0000000..7997d8a
--- /dev/null
+++ b/logback-classic/src/test/input/turbo/scan_lbclassic154.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE configuration>
+
+<configuration scan="true" scanPeriod="50 millisecond">
+
+ <appender name="default"
class="ch.qos.logback.classic.issue.lbclassic154.LoggingAppender">
+ <layout class="ch.qos.logback.classic.PatternLayout">
+ <pattern>%msg%n"</pattern>
+ </layout>
+ </appender>
+
+ <logger name="Ignore" level="ERROR" additivity="false"/>
+
+ <root level="INFO">
+ <appender-ref ref="default"/>
+ </root>
+
+</configuration>
+
\ No newline at end of file
diff --git a/logback-classic/src/test/input/turbo/scan_perf.xml
b/logback-classic/src/test/input/turbo/scan_perf.xml
new file mode 100644
index 0000000..7ef7c83
--- /dev/null
+++ b/logback-classic/src/test/input/turbo/scan_perf.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE configuration>
+
+<configuration scan="true" scanPeriod="50 millisecond">
+
+ <appender name="FILE" class="ch.qos.logback.core.FileAppender">
+ <File>testFile.log</File>
+ <Append>true</Append>
+
+ <layout class="ch.qos.logback.classic.PatternLayout">
+ <Pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</Pattern>
+ </layout>
+ </appender>
+
+
+ <root level="DEBUG">
+ <appender-ref ref="FILE"/>
+ </root>
+
+</configuration>
+
\ No newline at end of file
diff --git
a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic154/LoggingAppender.java
b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic154/LoggingAppender.java
new file mode 100644
index 0000000..a95b2b5
--- /dev/null
+++
b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic154/LoggingAppender.java
@@ -0,0 +1,41 @@
+/**
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * Copyright (C) 1999-2009, QOS.ch. All rights reserved.
+ *
+ * This program and the accompanying materials are dual-licensed under
+ * either the terms of the Eclipse Public License v1.0 as published by
+ * the Eclipse Foundation
+ *
+ * or (per the licensee's choosing)
+ *
+ * under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation.
+ */
+package ch.qos.logback.classic.issue.lbclassic154;
+
+import org.slf4j.Logger;
+
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.AppenderBase;
+
+/**
+ *
+ * An appender which calls logback recursively
+ *
+ * @author Ralph Goers
+ */
+
+public class LoggingAppender extends AppenderBase<ILoggingEvent> {
+
+ Logger logger;
+
+ public void start() {
+ super.start();
+ logger = ((LoggerContext) getContext()).getLogger("Ignore");
+ }
+
+ protected void append(ILoggingEvent eventObject) {
+ logger.debug("Ignore this");
+ }
+}
diff --git
a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/ReconfigureOnChangeTest.java
b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/ReconfigureOnChangeTest.java
index 6c6f52f..6c21e35 100644
---
a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/ReconfigureOnChangeTest.java
+++
b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/ReconfigureOnChangeTest.java
@@ -38,6 +38,7 @@ import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.status.InfoStatus;
import ch.qos.logback.core.status.StatusChecker;
import ch.qos.logback.core.testUtil.Env;
+import ch.qos.logback.core.util.StatusPrinter;
public class ReconfigureOnChangeTest {
final static int THREAD_COUNT = 5;
@@ -48,10 +49,13 @@ public class ReconfigureOnChangeTest {
final static String SCAN1_FILE_AS_STR = TeztConstants.TEST_DIR_PREFIX
+ "input/turbo/scan 1.xml";
+ final static String SCAN_LBCLASSIC_154_FILE_AS_STR =
TeztConstants.TEST_DIR_PREFIX
+ + "input/turbo/scan_lbclassic154.xml";
+
// it actually takes time for Windows to propagate file modification changes
- // values below 100 milliseconds can be problematic
- // the same propagation latency occurs in Linux but is even larger (>600 ms)
- final static int DEFAULT_SLEEP_BETWEEN_UPDATES = 250;
+ // values below 100 milliseconds can be problematic the same propagation
+ // latency occurs in Linux but is even larger (>600 ms)
+ final static int DEFAULT_SLEEP_BETWEEN_UPDATES = 110;
int sleepBetweenUpdates = DEFAULT_SLEEP_BETWEEN_UPDATES;
@@ -113,8 +117,25 @@ public class ReconfigureOnChangeTest {
verify(expectedRreconfigurations);
}
+ // check for deadlocks
+ @Test(timeout = 20000)
+ public void scan_lbclassic154() throws JoranException, IOException,
+ InterruptedException {
+ File file = new File(SCAN_LBCLASSIC_154_FILE_AS_STR);
+ configure(file);
+ RunnableWithCounterAndDone[] runnableArray = buildRunnableArray(file);
+ harness.execute(runnableArray);
+
+ loggerContext.getStatusManager().add(
+ new InfoStatus("end of execution ", this));
+
+ long expectedRreconfigurations = runnableArray[0].getCounter();
+ verify(expectedRreconfigurations);
+ }
+
void verify(long expectedRreconfigurations) {
StatusChecker checker = new StatusChecker(loggerContext);
+ StatusPrinter.print(loggerContext);
assertTrue(checker.isErrorFree());
int effectiveResets = checker
.matchCount("Resetting and reconfiguring context");
diff --git
a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/ReconfigurePerf.java
b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/ReconfigurePerf.java
new file mode 100644
index 0000000..e4165b9
--- /dev/null
+++
b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/ReconfigurePerf.java
@@ -0,0 +1,126 @@
+/**
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * Copyright (C) 1999-2009, QOS.ch. All rights reserved.
+ *
+ * This program and the accompanying materials are dual-licensed under
+ * either the terms of the Eclipse Public License v1.0 as published by
+ * the Eclipse Foundation
+ *
+ * or (per the licensee's choosing)
+ *
+ * under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation.
+ */
+package ch.qos.logback.classic.turbo;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.issue.lbclassic135.LoggingRunnable;
+import ch.qos.logback.classic.joran.JoranConfigurator;
+import ch.qos.logback.classic.util.TeztConstants;
+import ch.qos.logback.core.CoreConstants;
+import ch.qos.logback.core.contention.MultiThreadedHarness;
+import ch.qos.logback.core.contention.RunnableWithCounterAndDone;
+import ch.qos.logback.core.joran.spi.JoranException;
+import ch.qos.logback.core.status.InfoStatus;
+import ch.qos.logback.core.testUtil.Env;
+
+public class ReconfigurePerf {
+ final static int THREAD_COUNT = 5;
+ final static int LOOP_LEN = 1000 * 1000;
+
+ // the space in the file name mandated by
+ // http://jira.qos.ch/browse/LBCORE-119
+ final static String CONF_FILE_AS_STR = TeztConstants.TEST_DIR_PREFIX
+ + "input/turbo/scan_perf.xml";
+
+ // it actually takes time for Windows to propagate file modification changes
+ // values below 100 milliseconds can be problematic the same propagation
+ // latency occurs in Linux but is even larger (>600 ms)
+ final static int DEFAULT_SLEEP_BETWEEN_UPDATES = 110;
+
+ int sleepBetweenUpdates = DEFAULT_SLEEP_BETWEEN_UPDATES;
+
+ static int totalTestDuration;
+
+ LoggerContext loggerContext = new LoggerContext();
+ Logger logger = loggerContext.getLogger(this.getClass());
+ MultiThreadedHarness harness;
+
+ @Before
+ public void setUp() {
+ // take into account propagation latency occurs on Linux
+ if (Env.isLinux()) {
+ sleepBetweenUpdates = 850;
+ totalTestDuration = sleepBetweenUpdates * 5;
+ } else {
+ totalTestDuration = sleepBetweenUpdates * 10;
+ }
+ harness = new MultiThreadedHarness(totalTestDuration);
+ }
+
+ void configure(File file) throws JoranException {
+ JoranConfigurator jc = new JoranConfigurator();
+ jc.setContext(loggerContext);
+ jc.doConfigure(file);
+ }
+
+ RunnableWithCounterAndDone[] buildRunnableArray() {
+ RunnableWithCounterAndDone[] rArray = new
RunnableWithCounterAndDone[THREAD_COUNT];
+ for (int i = 0; i < THREAD_COUNT; i++) {
+ rArray[i] = new LoggingRunnable(logger);
+ }
+ return rArray;
+ }
+
+ // Tests whether ConfigurationAction is installing ReconfigureOnChangeFilter
+ @Test
+ public void scan1() throws JoranException, IOException, InterruptedException
{
+ File file = new File(CONF_FILE_AS_STR);
+ configure(file);
+ doRun();
+ doRun();
+ doRun();
+ System.out.println("ENTER :");
+ System.in.read();
+ doRun();
+ }
+
+ void doRun() throws InterruptedException {
+ RunnableWithCounterAndDone[] runnableArray = buildRunnableArray();
+ harness.execute(runnableArray);
+ }
+
+ ReconfigureOnChangeFilter initROCF() throws MalformedURLException {
+ ReconfigureOnChangeFilter rocf = new ReconfigureOnChangeFilter();
+ rocf.setContext(loggerContext);
+ File file = new File(CONF_FILE_AS_STR);
+ loggerContext.putObject(CoreConstants.URL_OF_LAST_CONFIGURATION_VIA_JORAN,
+ file.toURI().toURL());
+ rocf.start();
+ return rocf;
+ }
+
+
+ public double directLoop(ReconfigureOnChangeFilter rocf) {
+ long start = System.nanoTime();
+ for (int i = 0; i < LOOP_LEN; i++) {
+ rocf.decide(null, logger, Level.DEBUG, " ", null, null);
+ }
+ long end = System.nanoTime();
+ return (end - start) / (1.0d * LOOP_LEN);
+ }
+
+ void addInfo(String msg, Object o) {
+ loggerContext.getStatusManager().add(new InfoStatus(msg, o));
+ }
+
+}
-----------------------------------------------------------------------
Summary of changes:
.../classic/turbo/ReconfigureOnChangeFilter.java | 67 ++++++-----
logback-classic/src/test/input/turbo/scan 1.xml | 5 +-
.../src/test/input/turbo/scan_lbclassic154.xml | 19 +++
logback-classic/src/test/input/turbo/scan_perf.xml | 21 ++++
.../issue/lbclassic154/LoggingAppender.java | 32 +++---
.../classic/turbo/ReconfigureOnChangeTest.java | 27 ++++-
.../qos/logback/classic/turbo/ReconfigurePerf.java | 126 ++++++++++++++++++++
7 files changed, 247 insertions(+), 50 deletions(-)
create mode 100644 logback-classic/src/test/input/turbo/scan_lbclassic154.xml
create mode 100644 logback-classic/src/test/input/turbo/scan_perf.xml
copy logback-examples/src/main/java/chapter11/TrivialLogbackAppender.java =>
logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic154/LoggingAppender.java
(55%)
create mode 100644
logback-classic/src/test/java/ch/qos/logback/classic/turbo/ReconfigurePerf.java
hooks/post-receive
--
Logback: the generic, reliable, fast and flexible logging framework.
_______________________________________________
logback-dev mailing list
[email protected]
http://qos.ch/mailman/listinfo/logback-dev