[logback-dev] [GIT] Logback: the generic, reliable, fast and flexible logging framework. branch, master, updated. v_0.9.21-9-ga052f15

added by portage for gitosis-gentoo git-noreply at pixie.qos.ch
Thu Jun 17 10:19:15 CEST 2010


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Logback: the generic, reliable, fast and flexible logging framework.".

The branch, master has been updated
       via  a052f15d09636698c96fcdb753da3270f5c01b11 (commit)
      from  ee6dcb691896b71fcc425980f6f6f07aa7fcc611 (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=a052f15d09636698c96fcdb753da3270f5c01b11
http://github.com/ceki/logback/commit/a052f15d09636698c96fcdb753da3270f5c01b11

commit a052f15d09636698c96fcdb753da3270f5c01b11
Author: Ceki Gulcu <ceki at qos.ch>
Date:   Thu Jun 17 10:15:07 2010 +0200

    Added multiple buffer tracking capability to SMTPAppender

diff --git a/logback-access/src/main/java/ch/qos/logback/access/net/SMTPAppender.java b/logback-access/src/main/java/ch/qos/logback/access/net/SMTPAppender.java
index 13477e9..4d48cc8 100644
--- a/logback-access/src/main/java/ch/qos/logback/access/net/SMTPAppender.java
+++ b/logback-access/src/main/java/ch/qos/logback/access/net/SMTPAppender.java
@@ -34,9 +34,6 @@ import ch.qos.logback.core.net.SMTPAppenderBase;
 public class SMTPAppender extends SMTPAppenderBase<AccessEvent> {
 
   static final String DEFAULT_SUBJECT_PATTERN = "%m";
-  
-  private int bufferSize = 512;
-  protected CyclicBuffer<AccessEvent> cb = new CyclicBuffer<AccessEvent>(bufferSize);
 
   /**
    * The default constructor will instantiate the appender with a
@@ -56,15 +53,14 @@ public class SMTPAppender extends SMTPAppenderBase<AccessEvent> {
 
   /**
    * Perform SMTPAppender specific appending actions, mainly adding the event to
-   * a cyclic buffer.
+   * the appropriate cyclic buffer.
    */
-  protected void subAppend(AccessEvent event) {
+  protected void subAppend(CyclicBuffer<AccessEvent> cb, AccessEvent event) {
     cb.add(event);
-    // addInfo("Added event to the cyclic buffer: " + event.getMessage());
   }
 
   @Override
-  protected void fillBuffer(StringBuffer sbuf) {
+  protected void fillBuffer(CyclicBuffer<AccessEvent> cb, StringBuffer sbuf) {
     int len = cb.length();
     for (int i = 0; i < len; i++) {
       // sbuf.append(MimeUtility.encodeText(layout.format(cb.get())));
@@ -73,25 +69,6 @@ public class SMTPAppender extends SMTPAppenderBase<AccessEvent> {
     }
   }
 
-  /**
-   * The <b>BufferSize</b> option takes a positive integer representing the
-   * maximum number of logging events to collect in a cyclic buffer. When the
-   * <code>BufferSize</code> is reached, oldest events are deleted as new
-   * events are added to the buffer. By default the size of the cyclic buffer is
-   * 512 events.
-   */
-  public void setBufferSize(int bufferSize) {
-    this.bufferSize = bufferSize;
-    cb.resize(bufferSize);
-  }
-
-  /**
-   * Returns value of the <b>BufferSize</b> option.
-   */
-  public int getBufferSize() {
-    return bufferSize;
-  }
-
   @Override
   protected Layout<AccessEvent> makeSubjectLayout(String subjectStr) {
     if(subjectStr == null) {
diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/SMTPAppender.java b/logback-classic/src/main/java/ch/qos/logback/classic/net/SMTPAppender.java
index caf0036..c30e24c 100644
--- a/logback-classic/src/main/java/ch/qos/logback/classic/net/SMTPAppender.java
+++ b/logback-classic/src/main/java/ch/qos/logback/classic/net/SMTPAppender.java
@@ -38,7 +38,7 @@ public class SMTPAppender extends SMTPAppenderBase<ILoggingEvent> {
   static final String DEFAULT_SUBJECT_PATTERN = "%logger{20} - %m";
   
   private int bufferSize = 512;
-  protected CyclicBuffer<ILoggingEvent> cb = new CyclicBuffer<ILoggingEvent>(bufferSize);
+  //protected CyclicBuffer<ILoggingEvent> cb = new CyclicBuffer<ILoggingEvent>(bufferSize);
 
   /**
    * The default constructor will instantiate the appender with a
@@ -72,41 +72,20 @@ public class SMTPAppender extends SMTPAppenderBase<ILoggingEvent> {
    * Perform SMTPAppender specific appending actions, mainly adding the event to
    * a cyclic buffer.
    */
-  protected void subAppend(ILoggingEvent event) {
+  protected void subAppend(CyclicBuffer<ILoggingEvent> cb, ILoggingEvent event) {
     event.prepareForDeferredProcessing();
     cb.add(event);
-    // addInfo("Added event to the cyclic buffer: " + event.getMessage());
   }
 
   @Override
-  protected void fillBuffer(StringBuffer sbuf) {
+  protected void fillBuffer(CyclicBuffer<ILoggingEvent> cb, StringBuffer sbuf) {
     int len = cb.length();
     for (int i = 0; i < len; i++) {
-      // sbuf.append(MimeUtility.encodeText(layout.format(cb.get())));
       ILoggingEvent event = cb.get();
       sbuf.append(layout.doLayout(event));
     }
   }
 
-  /**
-   * The <b>BufferSize</b> option takes a positive integer representing the
-   * maximum number of logging events to collect in a cyclic buffer. When the
-   * <code>BufferSize</code> is reached, oldest events are deleted as new
-   * events are added to the buffer. By default the size of the cyclic buffer is
-   * 512 events.
-   */
-  public void setBufferSize(int bufferSize) {
-    this.bufferSize = bufferSize;
-    cb.resize(bufferSize);
-  }
-
-  /**
-   * Returns value of the <b>BufferSize</b> option.
-   */
-  public int getBufferSize() {
-    return bufferSize;
-  }
-
   @Override
   protected Layout<ILoggingEvent> makeSubjectLayout(String subjectStr) {
     if(subjectStr == null) {
diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/DilutedSMTPAppenderTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/DilutedSMTPAppenderTest.java
index 3e605fc..76b3ae0 100644
--- a/logback-classic/src/test/java/ch/qos/logback/classic/net/DilutedSMTPAppenderTest.java
+++ b/logback-classic/src/test/java/ch/qos/logback/classic/net/DilutedSMTPAppenderTest.java
@@ -20,6 +20,8 @@ import static org.junit.Assert.fail;
 import javax.mail.Address;
 import javax.mail.MessagingException;
 
+import ch.qos.logback.core.helpers.CyclicBuffer;
+import ch.qos.logback.core.spi.CyclicBufferTracker;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -34,11 +36,15 @@ import ch.qos.logback.core.Layout;
 public class DilutedSMTPAppenderTest {
 
   SMTPAppender appender;
+  CyclicBufferTracker cbTracker;
+  CyclicBuffer cb;
 
   @Before
   public void setUp() throws Exception {
     LoggerContext lc = new LoggerContext();
     appender = new SMTPAppender();
+    cbTracker = appender.getCyclicBufferTracker();
+    cb = cbTracker.get("", 0);
     appender.setContext(lc);
     appender.setName("smtp");
     appender.setFrom("user at host.dom");
@@ -92,8 +98,8 @@ public class DilutedSMTPAppenderTest {
     LoggingEvent event = new LoggingEvent();
     event.setThreadName("thead name");
     event.setLevel(Level.DEBUG);
-    appender.subAppend(event);
-    assertEquals(1, appender.cb.length());
+    appender.subAppend(cb, event);
+    assertEquals(1, cb.length());
   }
 
   @Test
diff --git a/logback-core/src/main/java/ch/qos/logback/core/ConsoleAppender.java b/logback-core/src/main/java/ch/qos/logback/core/ConsoleAppender.java
index 8b7661d..ef09428 100644
--- a/logback-core/src/main/java/ch/qos/logback/core/ConsoleAppender.java
+++ b/logback-core/src/main/java/ch/qos/logback/core/ConsoleAppender.java
@@ -43,7 +43,6 @@ public class ConsoleAppender<E> extends OutputStreamAppender<E> {
   public void setTarget(String value) {
     ConsoleTarget t = ConsoleTarget.findByName(value.trim());
     if (t == null) {
-      System.out.println("**************");
       targetWarn(value);
     } else {
       target = t;
diff --git a/logback-core/src/main/java/ch/qos/logback/core/CoreConstants.java b/logback-core/src/main/java/ch/qos/logback/core/CoreConstants.java
index 699483f..9d65b75 100644
--- a/logback-core/src/main/java/ch/qos/logback/core/CoreConstants.java
+++ b/logback-core/src/main/java/ch/qos/logback/core/CoreConstants.java
@@ -115,4 +115,5 @@ public class CoreConstants {
   
   
   public static int BYTES_PER_INT = 4;
+  public static final int MILLIS_IN_ONE_SECOND = 1000;
 }
diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/SMTPAppenderBase.java b/logback-core/src/main/java/ch/qos/logback/core/net/SMTPAppenderBase.java
index 7ca6684..43e34e6 100644
--- a/logback-core/src/main/java/ch/qos/logback/core/net/SMTPAppenderBase.java
+++ b/logback-core/src/main/java/ch/qos/logback/core/net/SMTPAppenderBase.java
@@ -34,6 +34,11 @@ import ch.qos.logback.core.CoreConstants;
 import ch.qos.logback.core.Layout;
 import ch.qos.logback.core.boolex.EvaluationException;
 import ch.qos.logback.core.boolex.EventEvaluator;
+import ch.qos.logback.core.helpers.CyclicBuffer;
+import ch.qos.logback.core.sift.DefaultDiscriminator;
+import ch.qos.logback.core.sift.Discriminator;
+import ch.qos.logback.core.spi.CyclicBufferTracker;
+import ch.qos.logback.core.spi.CyclicBufferTrackerImpl;
 import ch.qos.logback.core.util.ContentTypeUtil;
 import ch.qos.logback.core.util.OptionHelper;
 
@@ -43,20 +48,20 @@ import ch.qos.logback.core.util.OptionHelper;
 /**
  * An abstract class that provides support for sending events to an email
  * address.
- * 
- * <p>
+ * <p/>
+ * <p/>
  * See http://logback.qos.ch/manual/appenders.html#SMTPAppender for further
  * documentation.
- * 
+ *
  * @author Ceki G&uuml;lc&uuml;
  * @author S&eacute;bastien Pennec
  */
 public abstract class SMTPAppenderBase<E> extends AppenderBase<E> {
 
   protected Layout<E> subjectLayout;
-  
+
   protected Layout<E> layout;
-  
+
   private List<String> toList = new ArrayList<String>();
   private String from;
   private String subjectStr = null;
@@ -74,15 +79,17 @@ public abstract class SMTPAppenderBase<E> extends AppenderBase<E> {
 
   protected EventEvaluator<E> eventEvaluator;
 
+  protected Discriminator<E> discriminator = new DefaultDiscriminator<E>();
+  protected CyclicBufferTracker<E> cbTracker = new CyclicBufferTrackerImpl<E>();
+
   private int errorCount = 0;
 
   /**
    * return a layout for the subjet string as appropriate for the module. If the
    * subjectStr parameter is null, then a default value for subjectStr should be
    * used.
-   * 
+   *
    * @param subjectStr
-   * 
    * @return a layout as appropriate for the module
    */
   abstract protected Layout<E> makeSubjectLayout(String subjectStr);
@@ -152,26 +159,31 @@ public abstract class SMTPAppenderBase<E> extends AppenderBase<E> {
       return;
     }
 
-    subAppend(eventObject);
+
+    String key =  discriminator.getDiscriminatingValue(eventObject);
+
+    CyclicBuffer<E> cb = cbTracker.get(key, System.currentTimeMillis());
+
+    subAppend(cb, eventObject);
 
     try {
       if (eventEvaluator.evaluate(eventObject)) {
-        sendBuffer(eventObject);
+        sendBuffer(cb, eventObject);
       }
     } catch (EvaluationException ex) {
-      errorCount ++; 
+      errorCount++;
       if (errorCount < CoreConstants.MAX_ERROR_COUNT) {
         addError("SMTPAppender's EventEvaluator threw an Exception-", ex);
       }
     }
   }
 
-  abstract protected void subAppend(E eventObject);
+  abstract protected void subAppend(CyclicBuffer<E> cb, E eventObject);
 
   /**
    * This method determines if there is a sense in attempting to append.
-   * 
-   * <p>
+   * <p/>
+   * <p/>
    * It checks whether there is a set output target and also if there is a set
    * layout. If these checks fail, then the boolean value <code>false</code> is
    * returned.
@@ -179,7 +191,7 @@ public abstract class SMTPAppenderBase<E> extends AppenderBase<E> {
   public boolean checkEntryConditions() {
     if (!this.started) {
       addError("Attempting to append to a non-started appender: "
-          + this.getName());
+              + this.getName());
       return false;
     }
 
@@ -195,8 +207,8 @@ public abstract class SMTPAppenderBase<E> extends AppenderBase<E> {
 
     if (this.layout == null) {
       addError("No layout set for appender named ["
-          + name
-          + "]. For more information, please visit http://logback.qos.ch/codes.html#smtp_no_layout");
+              + name
+              + "]. For more information, please visit http://logback.qos.ch/codes.html#smtp_no_layout");
       return false;
     }
     return true;
@@ -243,7 +255,7 @@ public abstract class SMTPAppenderBase<E> extends AppenderBase<E> {
   /**
    * Send the contents of the cyclic buffer as an e-mail message.
    */
-  protected void sendBuffer(E lastEventObject) {
+  protected void sendBuffer(CyclicBuffer<E> cb, E lastEventObject) {
 
     // Note: this code already owns the monitor for this
     // appender. This frees us from needing to synchronize on 'cb'.
@@ -260,7 +272,7 @@ public abstract class SMTPAppenderBase<E> extends AppenderBase<E> {
       if (presentationHeader != null) {
         sbuf.append(presentationHeader);
       }
-      fillBuffer(sbuf);
+      fillBuffer(cb, sbuf);
       String presentationFooter = layout.getPresentationFooter();
       if (presentationFooter != null) {
         sbuf.append(presentationFooter);
@@ -272,14 +284,14 @@ public abstract class SMTPAppenderBase<E> extends AppenderBase<E> {
 
       if (subjectLayout != null) {
         mimeMsg.setSubject(subjectLayout.doLayout(lastEventObject),
-            charsetEncoding);
+                charsetEncoding);
       }
 
       String contentType = layout.getContentType();
 
       if (ContentTypeUtil.isTextual(contentType)) {
         part.setText(sbuf.toString(), charsetEncoding, ContentTypeUtil
-            .getSubType(contentType));
+                .getSubType(contentType));
       } else {
         part.setContent(sbuf.toString(), layout.getContentType());
       }
@@ -295,7 +307,7 @@ public abstract class SMTPAppenderBase<E> extends AppenderBase<E> {
     }
   }
 
-  abstract protected void fillBuffer(StringBuffer sbuf);
+  abstract protected void fillBuffer(CyclicBuffer<E> cb, StringBuffer sbuf);
 
   /**
    * Returns value of the <b>From</b> option.
@@ -344,7 +356,7 @@ public abstract class SMTPAppenderBase<E> extends AppenderBase<E> {
 
   /**
    * The port where the SMTP server is running. Default value is 25.
-   * 
+   *
    * @param port
    */
   public void setSMTPPort(int port) {
@@ -352,13 +364,29 @@ public abstract class SMTPAppenderBase<E> extends AppenderBase<E> {
   }
 
   /**
-   * @see #setSMTPPort(int)
    * @return
+   * @see #setSMTPPort(int)
    */
   public int getSMTPPort() {
     return smtpPort;
   }
 
+  public CyclicBufferTracker<E> getCyclicBufferTracker() {
+    return cbTracker;
+  }
+
+  public void setCyclicBufferTracker(CyclicBufferTracker<E> cbTracker) {
+    this.cbTracker = cbTracker;
+  }
+
+  public Discriminator<E> getDiscriminator() {
+    return discriminator;
+  }
+
+  public void setDiscriminator(Discriminator<E> discriminator) {
+    this.discriminator = discriminator;
+  }
+
   /**
    * The <b>To</b> option takes a string value which should be an e-mail address
    * of one of the recipients.
@@ -368,11 +396,13 @@ public abstract class SMTPAppenderBase<E> extends AppenderBase<E> {
   }
 
   // for testing purpose only
+
   public Message getMessage() {
     return mimeMsg;
   }
 
   // for testing purpose only
+
   public void setMessage(MimeMessage msg) {
     this.mimeMsg = msg;
   }
@@ -420,17 +450,17 @@ public abstract class SMTPAppenderBase<E> extends AppenderBase<E> {
   }
 
   /**
-   * @see #setCharsetEncoding(String)
    * @return the charset encoding value
+   * @see #setCharsetEncoding(String)
    */
   public String getCharsetEncoding() {
     return charsetEncoding;
-  } 
+  }
 
   /**
    * Set the character set encoding of the outgoing email messages. The default
    * encoding is "UTF-8" which usually works well for most purposes.
-   * 
+   *
    * @param charsetEncoding
    */
   public void setCharsetEncoding(String charsetEncoding) {
diff --git a/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderTracker.java b/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderTracker.java
index 6e75c85..fa6c5c5 100644
--- a/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderTracker.java
+++ b/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderTracker.java
@@ -16,11 +16,11 @@ package ch.qos.logback.core.sift;
 import java.util.List;
 
 import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.CoreConstants;
 
 public interface AppenderTracker<E> {
 
-  static int MILLIS_IN_ONE_SECOND = 1000;
-  static int THRESHOLD = 30 * 60 * MILLIS_IN_ONE_SECOND; // 30 minutes
+  static int THRESHOLD = 30 * 60 * CoreConstants.MILLIS_IN_ONE_SECOND; // 30 minutes
 
   void put(String key, Appender<E> value, long timestamp);
   Appender<E> get(String key, long timestamp);
diff --git a/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderTrackerImpl.java b/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderTrackerImpl.java
index 4e388b2..050acda 100644
--- a/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderTrackerImpl.java
+++ b/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderTrackerImpl.java
@@ -19,6 +19,7 @@ import java.util.List;
 import java.util.Map;
 
 import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.CoreConstants;
 
 /**
  * Track appenders by a key. When an appender is not used for
@@ -62,7 +63,7 @@ public class AppenderTrackerImpl<E> implements AppenderTracker<E> {
 
   
   public synchronized void stopStaleAppenders(long now) {
-    if (lastCheck + MILLIS_IN_ONE_SECOND > now) {
+    if (lastCheck + CoreConstants.MILLIS_IN_ONE_SECOND > now) {
       return;
     }
     lastCheck = now;
diff --git a/logback-core/src/main/java/ch/qos/logback/core/sift/DefaultDiscriminator.java b/logback-core/src/main/java/ch/qos/logback/core/sift/DefaultDiscriminator.java
new file mode 100644
index 0000000..a1354d9
--- /dev/null
+++ b/logback-core/src/main/java/ch/qos/logback/core/sift/DefaultDiscriminator.java
@@ -0,0 +1,46 @@
+/**
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * Copyright (C) 1999-2010, 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.core.sift;
+
+import ch.qos.logback.core.sift.Discriminator;
+
+/**
+ * @author Ceki G&uuml;c&uuml;
+ */
+public class DefaultDiscriminator<E> implements Discriminator<E> {
+
+  static public final String DEFAULT = "default";
+
+  boolean started = false;
+
+  public String getDiscriminatingValue(E e) {
+    return DEFAULT;
+  }
+
+  public String getKey() {
+    return DEFAULT;
+  }
+
+  public void start() {
+    started = true;
+  }
+
+  public void stop() {
+    started = false;
+  }
+
+  public boolean isStarted() {
+    return started;  
+  }
+}
diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/CyclicBufferTracker.java b/logback-core/src/main/java/ch/qos/logback/core/spi/CyclicBufferTracker.java
new file mode 100644
index 0000000..db82b74
--- /dev/null
+++ b/logback-core/src/main/java/ch/qos/logback/core/spi/CyclicBufferTracker.java
@@ -0,0 +1,65 @@
+/**
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * Copyright (C) 1999-2010, 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.core.spi;
+
+import ch.qos.logback.core.CoreConstants;
+import ch.qos.logback.core.helpers.CyclicBuffer;
+
+/**
+ * An interface for tracking cyclic buffers by key.
+ *
+ * @author Ceki G&uuml;c&uuml;
+ */
+public interface CyclicBufferTracker<E> {
+
+  public static int DEFAULT_BUFFER_SIZE = 256;
+  public static int DEFAULT_NUMBER_OF_BUFFERS = 64;
+
+  static int THRESHOLD = 30 * 60 * CoreConstants.MILLIS_IN_ONE_SECOND; // 30 minutes
+
+  public int getBufferSize();
+
+  public void setBufferSize(int size);
+
+
+  public int getMaxNumberOfBuffers();
+
+  /**
+   * Set the maximum number of tracked buffers. After reaching the maximum number of
+   * buffers, the creation of a new buffer implies the removal of the least recently
+   * used buffer.
+   *
+   * @param maxNumBuffers
+   */
+  public void setMaxNumberOfBuffers(int maxNumBuffers);
+
+
+  /**
+   * Get the cyclic buffer identified by 'key', updating its timestamp in the process.
+   * If there is no such buffer, create it. If the current number of buffers is
+   * above or equal to 'maxNumBuffers' then the least recently accessed buffer is removed.
+   *
+   * @param key
+   * @param timestamp
+   * @return
+   */
+  CyclicBuffer<E> get(String key, long timestamp);
+
+  /**
+   * Clear (and detach) buffers which are stale.
+   * 
+   * @param now
+   */
+  void clearStaleBuffers(long now);
+}
diff --git a/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderTrackerImpl.java b/logback-core/src/main/java/ch/qos/logback/core/spi/CyclicBufferTrackerImpl.java
similarity index 59%
copy from logback-core/src/main/java/ch/qos/logback/core/sift/AppenderTrackerImpl.java
copy to logback-core/src/main/java/ch/qos/logback/core/spi/CyclicBufferTrackerImpl.java
index 4e388b2..f71813c 100644
--- a/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderTrackerImpl.java
+++ b/logback-core/src/main/java/ch/qos/logback/core/spi/CyclicBufferTrackerImpl.java
@@ -1,6 +1,6 @@
 /**
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2009, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2010, 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
@@ -11,48 +11,57 @@
  * under the terms of the GNU Lesser General Public License version 2.1
  * as published by the Free Software Foundation.
  */
-package ch.qos.logback.core.sift;
+package ch.qos.logback.core.spi;
+
+import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.CoreConstants;
+import ch.qos.logback.core.helpers.CyclicBuffer;
 
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
-import ch.qos.logback.core.Appender;
-
 /**
- * Track appenders by a key. When an appender is not used for
- * longer than THRESHOLD, stop it.
- * @author Ceki Gulcu
+ * @author Ceki G&uuml;c&uuml;
  */
-public class AppenderTrackerImpl<E> implements AppenderTracker<E> {
+public class CyclicBufferTrackerImpl<E> implements CyclicBufferTracker<E> {
+
+  int bufferSize = DEFAULT_BUFFER_SIZE;
+  int maxNumBuffers = DEFAULT_NUMBER_OF_BUFFERS;
+  int bufferCount = 0;
 
-  Map<String, Entry> map = new HashMap<String, Entry>();
- 
-  Entry head; // least recently used entries are towards the head
-  Entry tail; // most recently used entries are towards the tail
+  private Map<String, Entry> map = new HashMap<String, Entry>();
 
+  private Entry head; // least recently used entries are towards the head
+  private Entry tail; // most recently used entries are towards the tail
   long lastCheck = 0;
 
-  AppenderTrackerImpl() {
+  public CyclicBufferTrackerImpl() {
     head = new Entry(null, null, 0);
     tail = head;
   }
 
+  public int getBufferSize() {
+    return bufferSize;
+  }
 
-  public synchronized void put(String key, Appender<E> value, long timestamp) {
-    Entry entry = map.get(key);
-    if (entry == null) {
-      entry = new Entry(key, value, timestamp);
-      map.put(key, entry);
-    }
-    moveToTail(entry);
+  public void setBufferSize(int size) {
+  }
+
+  public int getMaxNumberOfBuffers() {
+    return maxNumBuffers;
   }
 
-  public synchronized Appender<E> get(String key, long timestamp) {
+  public void setMaxNumberOfBuffers(int maxNumBuffers) {
+    this.maxNumBuffers = maxNumBuffers;
+  }
+
+  public CyclicBuffer<E> get(String key, long timestamp) {
     Entry existing = map.get(key);
     if (existing == null) {
-      return null;
+      CyclicBuffer<E> cb = processNewEntry(key, timestamp);
+      return cb;
     } else {
       existing.setTimestamp(timestamp);
       moveToTail(existing);
@@ -60,60 +69,25 @@ public class AppenderTrackerImpl<E> implements AppenderTracker<E> {
     }
   }
 
-  
-  public synchronized void stopStaleAppenders(long now) {
-    if (lastCheck + MILLIS_IN_ONE_SECOND > now) {
-      return;
-    }
-    lastCheck = now;
-    while (head.value != null && isEntryStale(head,now)) {
-      Appender<E> appender = head.value;
-      appender.stop();
+  private CyclicBuffer<E> processNewEntry(String key, long timestamp) {
+    CyclicBuffer<E> cb = new CyclicBuffer<E>(bufferSize);
+    Entry entry = new Entry(key, cb, timestamp);
+    map.put(key, entry);
+    bufferCount++;
+    rearrangeTailLinks(entry);
+    if (bufferCount >= maxNumBuffers) {
       removeHead();
     }
-  } 
-
-  /**
-   * @since 0.9.19
-   * @param key
-   */
-  public synchronized void stopAndRemoveNow(String key) {
-    Entry e = head;
-    Entry found = null;
-    while (e != tail) {
-      if(key.equals(e.key)) {
-        found = e;
-        break;
-      }
-      e = e.next;
-    }
-    if(found != null) {
-      rearrangePreexistingLinks(e);
-      map.remove(key);
-      Appender<E> appender = e.value;
-      appender.stop();
-    }
-  }
-  
-  public List<String> keyList() {
-    List<String> result = new LinkedList<String>();
-    Entry e = head;
-    while (e != tail) {
-      result.add(e.key);
-      e = e.next;
-    }
-    return result;
-  }
-  
-  
-  final private boolean isEntryStale(Entry entry, long now) {
-    return ((entry.timestamp + THRESHOLD) < now);
+    return cb;
   }
 
-  
   private void removeHead() {
-    // System.out.println("RemoveHead called");
+    CyclicBuffer cb = head.value;
+    if (cb != null) {
+      cb.clear();
+    }
     map.remove(head.key);
+    bufferCount--;
     head = head.next;
     head.prev = null;
   }
@@ -135,6 +109,32 @@ public class AppenderTrackerImpl<E> implements AppenderTracker<E> {
     }
   }
 
+
+  public synchronized void clearStaleBuffers(long now) {
+    if (lastCheck + CoreConstants.MILLIS_IN_ONE_SECOND > now) {
+      return;
+    }
+    lastCheck = now;
+    while (head.value != null && isEntryStale(head, now)) {
+      CyclicBuffer<E> cb = head.value;
+      cb.clear();
+      removeHead();
+    }
+  }
+
+  final private boolean isEntryStale(Entry entry, long now) {
+    return ((entry.timestamp + THRESHOLD) < now);
+  }
+
+   List<String> keyList() {
+    List<String> result = new LinkedList<String>();
+    Entry e = head;
+    while (e != tail) {
+      result.add(e.key);
+      e = e.next;
+    }
+    return result;
+  }
   private void rearrangeTailLinks(Entry e) {
     if (head == tail) {
       head = e;
@@ -148,48 +148,22 @@ public class AppenderTrackerImpl<E> implements AppenderTracker<E> {
     tail.prev = e;
   }
 
-  public void dump() {
-    Entry e = head;
-    System.out.print("N:");
-    while (e != null) {
-      // System.out.print(e+"->");
-      System.out.print(e.key + ", ");
-      e = e.next;
-    }
-    System.out.println();
-  }
-
-
-
-  public List<Appender<E>> valueList() {
-    List<Appender<E>> result = new LinkedList<Appender<E>>();
-    Entry e = head;
-    while (e != tail) {
-      result.add(e.value);
-      e = e.next;
-    }
-    return result;
-  }
-  
   // ================================================================
+
   private class Entry {
     Entry next;
     Entry prev;
 
     String key;
-    Appender<E> value;
+    CyclicBuffer<E> value;
     long timestamp;
 
-    Entry(String k, Appender<E> v, long timestamp) {
+    Entry(String k, CyclicBuffer<E> v, long timestamp) {
       this.key = k;
       this.value = v;
       this.timestamp = timestamp;
     }
 
-//    public long getTimestamp() {
-//      return timestamp;
-//    }
-
     public void setTimestamp(long timestamp) {
       this.timestamp = timestamp;
     }
diff --git a/logback-core/src/test/java/ch/qos/logback/core/sift/tracker/AppenderTrackerTImpl.java b/logback-core/src/test/java/ch/qos/logback/core/sift/tracker/AppenderTrackerTImpl.java
index 3b8e546..aa69266 100644
--- a/logback-core/src/test/java/ch/qos/logback/core/sift/tracker/AppenderTrackerTImpl.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/sift/tracker/AppenderTrackerTImpl.java
@@ -19,6 +19,7 @@ import java.util.LinkedList;
 import java.util.List;
 
 import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.CoreConstants;
 import ch.qos.logback.core.sift.AppenderTracker;
 
 /**
@@ -61,7 +62,7 @@ public class AppenderTrackerTImpl implements AppenderTracker<Object> {
   }
 
   synchronized public void stopStaleAppenders(long timestamp) {
-    if (lastCheck + MILLIS_IN_ONE_SECOND > timestamp) {
+    if (lastCheck + CoreConstants.MILLIS_IN_ONE_SECOND > timestamp) {
       return;
     }
     lastCheck = timestamp;
diff --git a/logback-core/src/test/java/ch/qos/logback/core/sift/tracker/TEntry.java b/logback-core/src/test/java/ch/qos/logback/core/sift/tracker/TEntry.java
index 600f41c..7f22b9e 100644
--- a/logback-core/src/test/java/ch/qos/logback/core/sift/tracker/TEntry.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/sift/tracker/TEntry.java
@@ -1,6 +1,6 @@
 /**
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2009, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2010, 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
diff --git a/logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTrackerImplTest.java b/logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTrackerImplTest.java
new file mode 100644
index 0000000..6b9ee28
--- /dev/null
+++ b/logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTrackerImplTest.java
@@ -0,0 +1,46 @@
+package ch.qos.logback.core.spi;
+
+import ch.qos.logback.core.helpers.CyclicBuffer;
+import ch.qos.logback.core.sift.AppenderTrackerImpl;
+import org.junit.Test;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertNotNull;
+
+/**
+ * @author Ceki G&uuml;c&uuml;
+ */
+public class CyclicBufferTrackerImplTest {
+
+
+  CyclicBufferTrackerImpl<Object> tracker = new CyclicBufferTrackerImpl<Object>();
+  String key = "a";
+
+  @Test
+  public void empty0() {
+    long now = 3000;
+    tracker.clearStaleBuffers(now);
+    assertEquals(0, tracker.keyList().size());
+  }
+
+    @Test
+  public void empty1() {
+    long now = 3000;
+    assertNotNull(tracker.get(key, now++));
+    now += CyclicBufferTracker.THRESHOLD+1000;
+    tracker.clearStaleBuffers(now);
+    assertEquals(0, tracker.keyList().size());
+    assertNotNull(tracker.get(key, now++));
+  }
+
+  @Test
+  public void smoke() {
+    long now = 3000;
+    CyclicBuffer<Object> cb = tracker.get(key, now);
+    assertEquals(cb, tracker.get(key, now++));
+    now += AppenderTrackerImpl.THRESHOLD+1000;
+    tracker.clearStaleBuffers(now);
+    assertEquals(0, tracker.keyList().size());
+  }
+}
diff --git a/logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTrackerSimulator.java b/logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTrackerSimulator.java
new file mode 100644
index 0000000..3d3e000
--- /dev/null
+++ b/logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTrackerSimulator.java
@@ -0,0 +1,100 @@
+/**
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * Copyright (C) 1999-2010, 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.core.spi;
+
+import java.util.*;
+
+/**
+ * @author Ceki G&uuml;c&uuml;
+ */
+public class CyclicBufferTrackerSimulator {
+
+  CyclicBufferTrackerImpl<Object> realAppenderTracker = new CyclicBufferTrackerImpl<Object>();
+  CyclicBufferTrackerImpl t_appenderTracker = new CyclicBufferTrackerImpl();
+
+  List<SimulationEvent> scenario = new ArrayList<SimulationEvent>();
+  List<String> keySpace = new ArrayList<String>();
+  int maxTimestampInc;
+  Random randomKeyGen = new Random(100);
+  Random random = new Random(11234);
+
+  CyclicBufferTrackerSimulator(int keySpaceLen, int maxTimestampInc) {
+    this.maxTimestampInc = maxTimestampInc;
+    Map<String, String> checkMap = new HashMap<String, String>();
+    for (int i = 0; i < keySpaceLen; i++) {
+      String k = getRandomKeyStr();
+      if (checkMap.containsKey(k)) {
+        System.out.println("random key collision occured");
+        k += "" + i;
+      }
+      keySpace.add(k);
+      checkMap.put(k, k);
+    }
+
+  }
+
+  private String getRandomKeyStr() {
+    int ri = randomKeyGen.nextInt();
+    String s = String.format("%X", ri);
+    return s;
+  }
+
+  void buildScenario(int simLen) {
+    long timestamp = 30000;
+    int keySpaceLen = keySpace.size();
+    for (int i = 0; i < simLen; i++) {
+      int index = random.nextInt(keySpaceLen);
+      timestamp += random.nextInt(maxTimestampInc);
+      String key = keySpace.get(index);
+      scenario.add(new SimulationEvent(key, timestamp));
+    }
+  }
+
+  public void dump() {
+    for (SimulationEvent simeEvent : scenario) {
+      System.out.println(simeEvent);
+    }
+  }
+
+
+  void play(SimulationEvent simulationEvent,
+            CyclicBufferTracker<Object> tracker) {
+    String key = simulationEvent.key;
+    long timestamp = simulationEvent.timestamp;
+    tracker.get(key, timestamp);
+  }
+
+  public void simulate() {
+    for (SimulationEvent simeEvent : scenario) {
+      play(simeEvent, realAppenderTracker);
+      play(simeEvent, t_appenderTracker);
+    }
+  }
+
+  // =========================================================================
+
+  class SimulationEvent {
+    final public String key;
+    final public long timestamp;
+
+    public SimulationEvent(String key, long timestamp) {
+      this.key = key;
+      this.timestamp = timestamp;
+    }
+
+    public String toString() {
+      return "Event: k=" + key + ", timestamp=" + timestamp;
+    }
+  }
+}
diff --git a/logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTracker_TImpl.java b/logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTracker_TImpl.java
new file mode 100644
index 0000000..fd56075
--- /dev/null
+++ b/logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTracker_TImpl.java
@@ -0,0 +1,150 @@
+package ch.qos.logback.core.spi;
+
+import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.CoreConstants;
+import ch.qos.logback.core.helpers.CyclicBuffer;
+import ch.qos.logback.core.sift.tracker.TEntry;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author Ceki G&uuml;c&uuml;
+ */
+public class CyclicBufferTracker_TImpl<E> implements CyclicBufferTracker<E> {
+
+  int bufferSize = DEFAULT_BUFFER_SIZE;
+  int maxNumBuffers = DEFAULT_NUMBER_OF_BUFFERS;
+  int bufferCount = 0;
+
+
+  List<TEntry> entryList = new LinkedList<TEntry>();
+  long lastCheck = 0;
+
+  public int getBufferSize() {
+    return bufferSize;
+  }
+
+  public void setBufferSize(int size) {
+  }
+
+  public int getMaxNumberOfBuffers() {
+    return maxNumBuffers;
+  }
+
+  public void setMaxNumberOfBuffers(int maxNumBuffers) {
+    this.maxNumBuffers = maxNumBuffers;
+  }
+
+  private TEntry getEntry(String k) {
+    for (int i = 0; i < entryList.size(); i++) {
+      TEntry te = entryList.get(i);
+      if (te.key.equals(k)) {
+        return te;
+      }
+    }
+    return null;
+  }
+
+  public CyclicBuffer<E> get(String key, long timestamp) {
+    TEntry te = getEntry(key);
+    if (te == null) {
+      CyclicBuffer<E> cb = new CyclicBuffer<E>(bufferSize);
+      te = new  TEntry<E>(key, cb, timestamp);
+      entryList.add(te);
+      return cb;
+    } else {
+      te.timestamp = timestamp;
+      Collections.sort(entryList);
+      return te.value;
+    }
+
+  }
+
+  final private boolean isEntryStale(TEntry entry, long now) {
+    return ((entry.timestamp + THRESHOLD) < now);
+  }
+
+  public void clearStaleBuffers(long now) {
+   if (lastCheck + CoreConstants.MILLIS_IN_ONE_SECOND > now) {
+      return;
+    }
+    lastCheck = now;
+    Collections.sort(entryList);
+    while (entryList.size() != 0 && isEntryStale(entryList.get(0), now)) {
+      entryList.remove(0);
+    }
+  }
+
+  // ==================================================================
+
+  private class TEntry<E> implements Comparable {
+    TEntry next;
+    TEntry prev;
+
+    String key;
+    CyclicBuffer<E> value;
+    long timestamp;
+
+    TEntry(String k, CyclicBuffer<E> v, long timestamp) {
+      this.key = k;
+      this.value = v;
+      this.timestamp = timestamp;
+    }
+
+    public void setTimestamp(long timestamp) {
+      this.timestamp = timestamp;
+    }
+
+    @Override
+    public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((key == null) ? 0 : key.hashCode());
+      return result;
+    }
+
+    public int compareTo(Object o) {
+      if (!(o instanceof TEntry)) {
+        throw new IllegalArgumentException("arguments must be of type " + TEntry.class);
+      }
+
+      TEntry other = (TEntry) o;
+      if (timestamp > other.timestamp) {
+        return 1;
+      }
+      if (timestamp == other.timestamp) {
+        return 0;
+      }
+      return -1;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj)
+        return true;
+      if (obj == null)
+        return false;
+      if (getClass() != obj.getClass())
+        return false;
+      final TEntry other = (TEntry) obj;
+      if (key == null) {
+        if (other.key != null)
+          return false;
+      } else if (!key.equals(other.key))
+        return false;
+      if (value == null) {
+        if (other.value != null)
+          return false;
+      } else if (!value.equals(other.value))
+        return false;
+      return true;
+    }
+
+    @Override
+    public String toString() {
+      return "(" + key + ", " + value + ")";
+    }
+  }
+}
diff --git a/logback-core/src/test/java/ch/qos/logback/core/spi/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/spi/PackageTest.java
index 991ffff..804ba0b 100644
--- a/logback-core/src/test/java/ch/qos/logback/core/spi/PackageTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/spi/PackageTest.java
@@ -18,7 +18,8 @@ import org.junit.runners.Suite;
 
 
 @RunWith(Suite.class)
- at Suite.SuiteClasses({AppenderAttachableImplTest.class ,AppenderAttachableImplLockTest.class})
+ at Suite.SuiteClasses({AppenderAttachableImplTest.class, AppenderAttachableImplLockTest.class,
+        CyclicBufferTrackerImplTest.class, ScenarioBasedCyclicBufferTrackerTest.class})
 
 public class PackageTest {
 }
diff --git a/logback-core/src/test/java/ch/qos/logback/core/spi/ScenarioBasedCyclicBufferTrackerTest.java b/logback-core/src/test/java/ch/qos/logback/core/spi/ScenarioBasedCyclicBufferTrackerTest.java
new file mode 100644
index 0000000..e48e4df
--- /dev/null
+++ b/logback-core/src/test/java/ch/qos/logback/core/spi/ScenarioBasedCyclicBufferTrackerTest.java
@@ -0,0 +1,58 @@
+/**
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * Copyright (C) 1999-2010, 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.core.spi;
+
+import org.junit.Test;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertNotNull;
+
+/**
+ * @author Ceki G&uuml;c&uuml;
+ */
+public class ScenarioBasedCyclicBufferTrackerTest {
+
+  CyclicBufferTrackerSimulator simulator;
+
+   void verify() {
+    CyclicBufferTrackerImpl<Object> at = simulator.realAppenderTracker;
+    CyclicBufferTrackerImpl<Object> t_at = simulator.t_appenderTracker;
+    assertEquals(t_at.keyList(), at.keyList());
+  }
+
+  @Test
+  public void shortTest() {
+    simulator = new CyclicBufferTrackerSimulator(64, 500);
+    simulator.buildScenario(200);
+    simulator.simulate();
+    verify();
+  }
+
+  @Test
+  public void mediumTest() {
+    simulator = new CyclicBufferTrackerSimulator(128, CyclicBufferTracker.THRESHOLD / 2);
+    simulator.buildScenario(20000);
+    simulator.simulate();
+    verify();
+  }
+
+    @Test
+  public void longTest() {
+    simulator = new CyclicBufferTrackerSimulator(128, CyclicBufferTracker.THRESHOLD / 2);
+    simulator.buildScenario(200000);
+    simulator.simulate();
+    verify();
+  }
+}

-----------------------------------------------------------------------

Summary of changes:
 .../ch/qos/logback/access/net/SMTPAppender.java    |   29 +---
 .../ch/qos/logback/classic/net/SMTPAppender.java   |   27 +---
 .../classic/net/DilutedSMTPAppenderTest.java       |   10 +-
 .../java/ch/qos/logback/core/ConsoleAppender.java  |    1 -
 .../java/ch/qos/logback/core/CoreConstants.java    |    1 +
 .../ch/qos/logback/core/net/SMTPAppenderBase.java  |   82 +++++++---
 .../ch/qos/logback/core/sift/AppenderTracker.java  |    4 +-
 .../qos/logback/core/sift/AppenderTrackerImpl.java |    3 +-
 .../logback/core/sift/DefaultDiscriminator.java}   |   26 +++-
 .../qos/logback/core/spi/CyclicBufferTracker.java  |   65 ++++++++
 .../CyclicBufferTrackerImpl.java}                  |  172 +++++++++-----------
 .../core/sift/tracker/AppenderTrackerTImpl.java    |    3 +-
 .../ch/qos/logback/core/sift/tracker/TEntry.java   |    2 +-
 .../core/spi/CyclicBufferTrackerImplTest.java      |   46 ++++++
 .../core/spi/CyclicBufferTrackerSimulator.java     |  100 ++++++++++++
 .../core/spi/CyclicBufferTracker_TImpl.java        |  150 +++++++++++++++++
 .../java/ch/qos/logback/core/spi/PackageTest.java  |    3 +-
 .../spi/ScenarioBasedCyclicBufferTrackerTest.java  |   58 +++++++
 18 files changed, 591 insertions(+), 191 deletions(-)
 copy logback-core/src/{test/java/ch/qos/logback/core/joran/spi/DoNotAutoStart.java => main/java/ch/qos/logback/core/sift/DefaultDiscriminator.java} (56%)
 create mode 100644 logback-core/src/main/java/ch/qos/logback/core/spi/CyclicBufferTracker.java
 copy logback-core/src/main/java/ch/qos/logback/core/{sift/AppenderTrackerImpl.java => spi/CyclicBufferTrackerImpl.java} (59%)
 create mode 100644 logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTrackerImplTest.java
 create mode 100644 logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTrackerSimulator.java
 create mode 100644 logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTracker_TImpl.java
 create mode 100644 logback-core/src/test/java/ch/qos/logback/core/spi/ScenarioBasedCyclicBufferTrackerTest.java


hooks/post-receive
-- 
Logback: the generic, reliable, fast and flexible logging framework.


More information about the logback-dev mailing list