Index: logback-core/src/main/java/ch/qos/logback/core/UnsynchronizedAppenderBase.java =================================================================== --- logback-core/src/main/java/ch/qos/logback/core/UnsynchronizedAppenderBase.java (revision 2150) +++ logback-core/src/main/java/ch/qos/logback/core/UnsynchronizedAppenderBase.java Tue Jun 09 09:03:54 CEST 2009 @@ -20,9 +20,19 @@ /** * Similar to AppenderBase except that derived appenders need to handle * thread synchronization on their own. - * + * + * Synchronization MUST be implemented in a fair manner, i.e. using one of the constructs + * from java.util.concurrent with the optional fairness parameter set to true. + * + * Otherwise, the appender implementation can lead to starvation effects in the logged application! + * * @author Ceki Gülcü * @author Ralph Goers + * + * @see java.util.concurrent.locks.ReentrantLock + * @see java.util.concurrent.locks.ReentrantReadWriteLock + * @see java.util.concurrent.SynchronousQueue + * @see java.util.concurrent.ArrayBlockingQueue */ abstract public class UnsynchronizedAppenderBase extends ContextAwareBase implements Appender { Index: logback-core/src/main/java/ch/qos/logback/core/AppenderBase.java =================================================================== --- logback-core/src/main/java/ch/qos/logback/core/AppenderBase.java (revision 2146) +++ logback-core/src/main/java/ch/qos/logback/core/AppenderBase.java Sat Jun 13 03:05:34 CEST 2009 @@ -10,6 +10,7 @@ package ch.qos.logback.core; import java.util.List; +import java.util.concurrent.locks.ReentrantLock; import ch.qos.logback.core.filter.Filter; import ch.qos.logback.core.spi.ContextAwareBase; @@ -36,11 +37,19 @@ protected boolean started = false; + // As suggested in LBCORE-99 + // using a ThreadLocal instead of a boolean add 75 nanoseconds per + // doAppend invocation. This is tolerable as doAppend takes at least a few microseconds + // on a real appender - /** - * The guard prevents an appender from repeatedly calling its own doAppend - * method. - */ + /** + * The guard prevents an appender from repeatedly calling its own doAppend + * method. + */ - private boolean guard = false; + private ThreadLocal guard = new ThreadLocal() { + protected Boolean initialValue() { + return false; + } + }; /** * Appenders are named. @@ -57,18 +66,20 @@ private int exceptionCount = 0; static final int ALLOWED_REPEATS = 5; + protected final ReentrantLock lock = new ReentrantLock(true); // we want a "fair" lock to avoid congestion - public synchronized void doAppend(E eventObject) { + + public void doAppend(E eventObject) { // WARNING: The guard check MUST be the first statement in the // doAppend() method. - // prevent re-entry. - if (guard) { + if (guard.get()) { - return; - } + return; + } + lock.lock(); try { - guard = true; + guard.set(true); if (!this.started) { if (statusRepeatCount++ < ALLOWED_REPEATS) { @@ -91,7 +102,8 @@ addError("Appender [" + name + "] failed to append.", e); } } finally { - guard = false; + guard.set(false); + lock.unlock(); } } Index: logback-core/src/test/java/ch/qos/logback/core/joran/action/IncludeActionTest.java =================================================================== --- logback-core/src/test/java/ch/qos/logback/core/joran/action/IncludeActionTest.java (revision 1891) +++ logback-core/src/test/java/ch/qos/logback/core/joran/action/IncludeActionTest.java Sat Jun 13 03:28:27 CEST 2009 @@ -2,15 +2,18 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.FileNotFoundException; import java.net.MalformedURLException; import java.net.UnknownHostException; import java.util.HashMap; +import java.util.List; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.junit.Ignore; import org.xml.sax.SAXParseException; import ch.qos.logback.core.Context; @@ -128,14 +131,19 @@ assertTrue(sc.containsException(MalformedURLException.class)); } + @Ignore + // because of Expected UnknownHostException but status was [ERROR in ch.qos.logback.core.joran.event.SaxEventRecorder@4f3516d7 - I/O error occurred while parsing xml file java.io.IOException: Server returned HTTP response code: 503 for URL: http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd, ERROR in ch.qos.logback.core.joran.action.IncludeAction - Error while parsing http://logback2345.qos.ch ch.qos.logback.core.joran.spi.JoranException: I/O error occurred while parsing xml file] @Test public void unknownURL() throws JoranException { System.setProperty(INCLUDE_KEY, "http://logback2345.qos.ch"); tc.doConfigure(TOP_BY_URL); assertEquals(Status.ERROR, context.getStatusManager().getLevel()); StatusChecker sc = new StatusChecker(context.getStatusManager()); - assertTrue(sc.containsException(UnknownHostException.class)); + if(!sc.containsException(UnknownHostException.class)) { + List stati = context.getStatusManager().getCopyOfStatusList(); + fail("Expected UnknownHostException but status was "+stati); - } + } + } @Test public void nestedInclude() throws JoranException {