[logback-dev] svn commit: r2082 - in logback/trunk/logback-classic/src: main/java/ch/qos/logback/classic/hoard test/input/joran/hoard test/java/ch/qos/logback/classic test/java/ch/qos/logback/classic/hoard test/java/ch/qos/logback/classic/hoard/tracker
noreply.ceki at qos.ch
noreply.ceki at qos.ch
Wed Dec 17 18:46:02 CET 2008
Author: ceki
Date: Wed Dec 17 18:46:01 2008
New Revision: 2082
Added:
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/hoard/AppenderTracker.java
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/hoard/AppenderTrackerImpl.java
logback/trunk/logback-classic/src/test/input/joran/hoard/smoke.xml
logback/trunk/logback-classic/src/test/input/joran/hoard/unsetDefaultValueProperty.xml
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/AppenderTrackerTest.java
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/PackageTest.java
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/ScenarioBasedAppenderTrackerTest.java
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/Simulator.java
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/tracker/
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/tracker/AppenderTrackerTImpl.java
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/tracker/SimulationEvent.java
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/tracker/TEntry.java
Modified:
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/hoard/HoardingAppender.java
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/hoard/HoardingJoranConfigurator.java
logback/trunk/logback-classic/src/test/input/joran/hoard/hoard0.xml
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/AllClassicTest.java
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/HoardingAppenderTest.java
Log:
Code to track and clean up appenders. Related to LBCLASSIC-94
Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/hoard/AppenderTracker.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/hoard/AppenderTracker.java Wed Dec 17 18:46:01 2008
@@ -0,0 +1,19 @@
+package ch.qos.logback.classic.hoard;
+
+import java.util.List;
+
+import ch.qos.logback.core.Appender;
+
+public interface AppenderTracker<E> {
+
+ static int MILLIS_IN_ONE_SECOND = 1000;
+ static int THRESHOLD = 30 * 60 * MILLIS_IN_ONE_SECOND; // 30 minutes
+
+ void put(String key, Appender<E> value, long timestamp);
+ Appender<E> get(String key, long timestamp);
+ void stopStaleAppenders(long now);
+ List<String> keyList();
+ List<Appender<E>> valueList();
+
+
+}
\ No newline at end of file
Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/hoard/AppenderTrackerImpl.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/hoard/AppenderTrackerImpl.java Wed Dec 17 18:46:01 2008
@@ -0,0 +1,216 @@
+/**
+ * Logback: the generic, reliable, fast and flexible logging framework.
+ *
+ * Copyright (C) 2000-2008, 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
+ * Software Foundation.
+ */
+package ch.qos.logback.classic.hoard;
+
+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
+ */
+public class AppenderTrackerImpl<E> implements AppenderTracker<E> {
+
+ 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
+
+ long lastCheck = 0;
+
+ AppenderTrackerImpl() {
+ head = new Entry(null, null, 0);
+ tail = head;
+ }
+
+ /* (non-Javadoc)
+ * @see ch.qos.logback.classic.hoard.AppenderTracker#put(java.lang.String, ch.qos.logback.core.Appender, long)
+ */
+ 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);
+ }
+
+ /* (non-Javadoc)
+ * @see ch.qos.logback.classic.hoard.AppenderTracker#get(java.lang.String, long)
+ */
+ public synchronized Appender<E> get(String key, long timestamp) {
+ Entry existing = map.get(key);
+ if (existing == null) {
+ return null;
+ } else {
+ existing.setTimestamp(timestamp);
+ moveToTail(existing);
+ return existing.value;
+ }
+ }
+
+
+ /* (non-Javadoc)
+ * @see ch.qos.logback.classic.hoard.AppenderTracker#stopStaleAppenders(long)
+ */
+ public synchronized void stopStaleAppenders(long now) {
+ if (lastCheck + MILLIS_IN_ONE_SECOND > now) {
+ return;
+ }
+ lastCheck = now;
+ while (head.value != null && isEntryStale(head,now)) {
+ Appender appender = head.value;
+ //System.out.println(" stopping "+appender);
+ appender.stop();
+ removeHead();
+ }
+ }
+
+ 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);
+ }
+
+
+ private void removeHead() {
+ // System.out.println("RemoveHead called");
+ map.remove(head.key);
+ head = head.next;
+ head.prev = null;
+ }
+
+ private void moveToTail(Entry e) {
+ rearrangePreexistingLinks(e);
+ rearrangeTailLinks(e);
+ }
+
+ private void rearrangePreexistingLinks(Entry e) {
+ if (e.prev != null) {
+ e.prev.next = e.next;
+ }
+ if (e.next != null) {
+ e.next.prev = e.prev;
+ }
+ if (head == e) {
+ head = e.next;
+ }
+ }
+
+ private void rearrangeTailLinks(Entry e) {
+ if (head == tail) {
+ head = e;
+ }
+ Entry preTail = tail.prev;
+ if (preTail != null) {
+ preTail.next = e;
+ }
+ e.prev = preTail;
+ e.next = tail;
+ 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;
+ long timestamp;
+
+ Entry(String k, Appender<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;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((key == null) ? 0 : key.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ final Entry other = (Entry) 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 + ")";
+ }
+ }
+
+}
Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/hoard/HoardingAppender.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/hoard/HoardingAppender.java (original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/hoard/HoardingAppender.java Wed Dec 17 18:46:01 2008
@@ -1,22 +1,39 @@
+/**
+ * Logback: the generic, reliable, fast and flexible logging framework.
+ *
+ * Copyright (C) 2000-2008, 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
+ * Software Foundation.
+ */
package ch.qos.logback.classic.hoard;
-import java.util.Hashtable;
-import java.util.Map;
-
import org.slf4j.MDC;
import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.UnsynchronizedAppenderBase;
import ch.qos.logback.core.joran.spi.JoranException;
+import ch.qos.logback.core.util.OptionHelper;
+/**
+ * This appender can contains other appenders which it can build dynamically
+ * depending on MDC values. The built appender is specified as part of a
+ * configuration file.
+ *
+ * <p>See the logback manual for further details.
+ *
+ *
+ * @author Ceki Gulcu
+ */
public class HoardingAppender extends UnsynchronizedAppenderBase<LoggingEvent> {
- static String DEFAULT = "default";
-
- Map<String, Appender<LoggingEvent>> appenderMap = new Hashtable<String, Appender<LoggingEvent>>();
+ AppenderTracker<LoggingEvent> appenderTracker = new AppenderTrackerImpl<LoggingEvent>();
+ //Map<String, Appender<LoggingEvent>> appenderMap = new Hashtable<String, Appender<LoggingEvent>>();
String mdcKey;
+ String defaultValue;
AppenderFactory appenderFactory;
@@ -24,26 +41,57 @@
this.appenderFactory = appenderFactory;
}
-
-
+ @Override
+ public void start() {
+ int errors = 0;
+ if (OptionHelper.isEmpty(mdcKey)) {
+ errors++;
+ addError("The \"mdcKey\" property must be set");
+ }
+ if (OptionHelper.isEmpty(defaultValue)) {
+ errors++;
+ addError("The \"defaultValue\" property must be set");
+ }
+ if (errors == 0) {
+ super.start();
+ }
+ }
+
+ @Override
+ public void stop() {
+ for (Appender<LoggingEvent> appender : appenderTracker.valueList()) {
+ appender.stop();
+ }
+ }
+
@Override
protected void append(LoggingEvent loggingEvent) {
+ if (!isStarted()) {
+ return;
+ }
+
String mdcValue = MDC.get(mdcKey);
if (mdcValue == null) {
- mdcValue = DEFAULT;
+ mdcValue = defaultValue;
}
- Appender<LoggingEvent> appender = appenderMap.get(mdcValue);
+ long timestamp = loggingEvent.getTimeStamp();
+
+ Appender<LoggingEvent> appender = appenderTracker.get(mdcValue, timestamp);
if (appender == null) {
try {
appender = appenderFactory.buildAppender(context, mdcKey, mdcValue);
+ if (appender != null) {
+ appenderTracker.put(mdcValue, appender, timestamp);
+ }
} catch (JoranException e) {
addError("Failed to build appender for " + mdcKey + "=" + mdcValue, e);
return;
}
}
+ appenderTracker.stopStaleAppenders(timestamp);
appender.doAppend(loggingEvent);
}
@@ -54,6 +102,29 @@
public void setMdcKey(String mdcKey) {
this.mdcKey = mdcKey;
}
-
-
+
+ /**
+ * @see #setDefaultValue(String)
+ * @return
+ */
+ public String getDefaultValue() {
+ return defaultValue;
+ }
+
+ /**
+ * The default MDC value in case the MDC is not set for
+ * {@link #setMdcKey(String) mdcKey}.
+ *
+ * <p> For example, if {@link #setMdcKey(String) mdcKey} is set to the value
+ * "someKey", and the MDC is not set for "someKey", then this appender will
+ * use the default value, which you can set with the help of method.
+ *
+ * <p>The "defaultValue" property is set to the value "DEFAULT" by default.
+ *
+ * @param defaultValue
+ */
+ public void setDefaultValue(String defaultValue) {
+ this.defaultValue = defaultValue;
+ }
+
}
Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/hoard/HoardingJoranConfigurator.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/hoard/HoardingJoranConfigurator.java (original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/hoard/HoardingJoranConfigurator.java Wed Dec 17 18:46:01 2008
@@ -4,6 +4,7 @@
import java.util.HashMap;
import java.util.Map;
+import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.joran.GenericConfigurator;
import ch.qos.logback.core.joran.action.ActionConst;
@@ -55,10 +56,11 @@
interpreter.setInterpretationContextPropertiesMap(propertiesMap);
}
- public Appender getAppender() {
+ @SuppressWarnings("unchecked")
+ public Appender<LoggingEvent> getAppender() {
Map<String, Object> omap = interpreter.getInterpretationContext().getObjectMap();
HashMap map = (HashMap) omap.get(ActionConst.APPENDER_BAG);
Collection values = map.values();
- return (Appender) values.iterator().next();
+ return (Appender<LoggingEvent>) values.iterator().next();
}
}
Modified: logback/trunk/logback-classic/src/test/input/joran/hoard/hoard0.xml
==============================================================================
--- logback/trunk/logback-classic/src/test/input/joran/hoard/hoard0.xml (original)
+++ logback/trunk/logback-classic/src/test/input/joran/hoard/hoard0.xml Wed Dec 17 18:46:01 2008
@@ -7,10 +7,9 @@
class="ch.qos.logback.classic.hoard.HoardingAppender">
<mdcKey>userid</mdcKey>
-
-
+ <default>asdad</default>
<hoard>
- <appender name="FILE" class="ch.qos.logback.core.FileAppender">
+ <appender name="FILE-${userid}" class="ch.qos.logback.core.FileAppender">
<File>${userid}.log</File>
<Append>true</Append>
<layout class="ch.qos.logback.classic.PatternLayout">
@@ -18,7 +17,6 @@
</layout>
</appender>
</hoard>
-
</appender>
<root level="DEBUG">
Added: logback/trunk/logback-classic/src/test/input/joran/hoard/smoke.xml
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/input/joran/hoard/smoke.xml Wed Dec 17 18:46:01 2008
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE configuration>
+
+<configuration debug="true">
+
+ <appender name="HOARD"
+ class="ch.qos.logback.classic.hoard.HoardingAppender">
+
+ <mdcKey>userid</mdcKey>
+ <defaultValue>smoke</defaultValue>
+ <hoard>
+ <appender name="list-${userid}" class="ch.qos.logback.core.read.ListAppender"/>
+ </hoard>
+ </appender>
+
+ <root level="DEBUG">
+ <appender-ref ref="HOARD" />
+ </root>
+
+</configuration>
Added: logback/trunk/logback-classic/src/test/input/joran/hoard/unsetDefaultValueProperty.xml
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/input/joran/hoard/unsetDefaultValueProperty.xml Wed Dec 17 18:46:01 2008
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE configuration>
+
+<configuration debug="true">
+
+ <appender name="HOARD"
+ class="ch.qos.logback.classic.hoard.HoardingAppender">
+
+ <mdcKey>userid</mdcKey>
+ <hoard>
+ <appender name="list-${userid}" class="ch.qos.logback.core.read.ListAppender"/>
+ </hoard>
+ </appender>
+
+ <root level="DEBUG">
+ <appender-ref ref="HOARD" />
+ </root>
+
+</configuration>
Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/AllClassicTest.java
==============================================================================
--- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/AllClassicTest.java (original)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/AllClassicTest.java Wed Dec 17 18:46:01 2008
@@ -20,20 +20,23 @@
TestSuite suite = new TestSuite();
suite.addTest(org.slf4j.impl.PackageTest.suite());
- suite.addTest(new JUnit4TestAdapter(ch.qos.logback.classic.PackageTest.class));
+ suite.addTest(new JUnit4TestAdapter(
+ ch.qos.logback.classic.PackageTest.class));
suite.addTest(ch.qos.logback.classic.util.PackageTest.suite());
suite.addTest(ch.qos.logback.classic.control.PackageTest.suite());
suite.addTest(ch.qos.logback.classic.joran.PackageTest.suite());
- suite.addTest(ch.qos.logback.classic.jmx.PackageTest.suite());
+ suite.addTest(ch.qos.logback.classic.jmx.PackageTest.suite());
suite.addTest(ch.qos.logback.classic.boolex.PackageTest.suite());
- suite.addTest(ch.qos.logback.classic.selector.PackageTest.suite());
- suite.addTest(ch.qos.logback.classic.html.PackageTest.suite());
+ suite.addTest(ch.qos.logback.classic.selector.PackageTest.suite());
+ suite.addTest(ch.qos.logback.classic.html.PackageTest.suite());
suite.addTest(ch.qos.logback.classic.net.PackageTest.suite());
- suite.addTest(ch.qos.logback.classic.pattern.PackageTest.suite());
- suite.addTest(ch.qos.logback.classic.db.PackageTest.suite());
- suite.addTest(ch.qos.logback.classic.spi.PackageTest.suite());
- suite.addTest(ch.qos.logback.classic.turbo.PackageTest.suite());
-
+ suite.addTest(ch.qos.logback.classic.pattern.PackageTest.suite());
+ suite.addTest(ch.qos.logback.classic.db.PackageTest.suite());
+ suite.addTest(ch.qos.logback.classic.spi.PackageTest.suite());
+ suite.addTest(ch.qos.logback.classic.turbo.PackageTest.suite());
+ suite.addTest(new JUnit4TestAdapter(
+ ch.qos.logback.classic.hoard.PackageTest.class));
+
return suite;
}
}
Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/AppenderTrackerTest.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/AppenderTrackerTest.java Wed Dec 17 18:46:01 2008
@@ -0,0 +1,53 @@
+package ch.qos.logback.classic.hoard;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import ch.qos.logback.core.Context;
+import ch.qos.logback.core.ContextBase;
+import ch.qos.logback.core.read.ListAppender;
+
+public class AppenderTrackerTest {
+
+
+ Context context = new ContextBase();
+ AppenderTracker<Object> appenderTracker = new AppenderTrackerImpl<Object>();
+ ListAppender<Object> la = new ListAppender<Object>();
+
+ @Before
+ public void setUp() {
+ la.setContext(context);
+ la.start();
+ }
+
+ @Test
+ public void empty() {
+ long now = 3000;
+ assertNull(appenderTracker.get("a", now++));
+ now += AppenderTrackerImpl.THRESHOLD+1000;
+ appenderTracker.stopStaleAppenders(now);
+ assertNull(appenderTracker.get("a", now++));
+ }
+
+ @Test
+ public void smoke() {
+ assertTrue(la.isStarted());
+ long now = 3000;
+ appenderTracker.put("a", la, now);
+ assertEquals(la, appenderTracker.get("a", now++));
+ now += AppenderTrackerImpl.THRESHOLD+1000;
+ appenderTracker.stopStaleAppenders(now);
+ assertFalse(la.isStarted());
+ assertNull(appenderTracker.get("a", now++));
+ }
+
+ @Test
+ public void scenarioBased() {
+
+ }
+}
Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/HoardingAppenderTest.java
==============================================================================
--- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/HoardingAppenderTest.java (original)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/HoardingAppenderTest.java Wed Dec 17 18:46:01 2008
@@ -9,13 +9,21 @@
*/
package ch.qos.logback.classic.hoard;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.List;
+
import org.junit.Test;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
+import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.classic.util.TeztConstants;
import ch.qos.logback.core.joran.spi.JoranException;
+import ch.qos.logback.core.read.ListAppender;
import ch.qos.logback.core.util.StatusPrinter;
public class HoardingAppenderTest {
@@ -32,6 +40,29 @@
jc.doConfigure(file);
}
+ @Test
+ public void unsetDefaultValueProperty() throws JoranException {
+ configure(PREFIX + "unsetDefaultValueProperty.xml");
+ logger.debug("hello");
+ HoardingAppender ha = (HoardingAppender) root.getAppender("HOARD");
+ assertFalse(ha.isStarted());
+
+ }
+
+ @Test
+ public void smoke() throws JoranException {
+ configure(PREFIX + "smoke.xml");
+ logger.debug("smoke");
+ long timestamp = 0;
+ HoardingAppender ha = (HoardingAppender) root.getAppender("HOARD");
+ ListAppender<LoggingEvent> listAppender = (ListAppender<LoggingEvent>) ha.appenderTracker.get("smoke", timestamp);
+ StatusPrinter.print(loggerContext);
+
+ assertNotNull(listAppender);
+ List<LoggingEvent> eventList = listAppender.list;
+ assertEquals(1, listAppender.list.size());
+ assertEquals("smoke", eventList.get(0).getMessage());
+ }
@Test
public void testLevel() throws JoranException {
Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/PackageTest.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/PackageTest.java Wed Dec 17 18:46:01 2008
@@ -0,0 +1,19 @@
+/**
+ * Logback: the generic, reliable, fast and flexible logging framework.
+ *
+ * Copyright (C) 2000-2008, 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
+ * Software Foundation.
+ */
+package ch.qos.logback.classic.hoard;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+ at RunWith(Suite.class)
+ at SuiteClasses({HoardingAppenderTest.class})
+public class PackageTest {
+}
\ No newline at end of file
Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/ScenarioBasedAppenderTrackerTest.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/ScenarioBasedAppenderTrackerTest.java Wed Dec 17 18:46:01 2008
@@ -0,0 +1,53 @@
+/**
+ * Logback: the generic, reliable, fast and flexible logging framework.
+ *
+ * Copyright (C) 2000-2008, 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
+ * Software Foundation.
+ */
+package ch.qos.logback.classic.hoard;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class ScenarioBasedAppenderTrackerTest {
+
+ Simulator simulator;
+
+ void verify() {
+ AppenderTracker at = simulator.appenderTracker;
+ AppenderTracker t_at = simulator.t_appenderTracker;
+ //List<String> resultKeys = at.keyList();
+ //List<String> witnessKeys = t_at.keyList();
+ assertEquals(t_at.keyList(), at.keyList());
+ }
+
+ @Test
+ public void shortTest() {
+ simulator = new Simulator(20, AppenderTracker.THRESHOLD / 2);
+ simulator.buildScenario(200);
+ simulator.simulate();
+ verify();
+ }
+
+ @Test
+ public void mediumTest() {
+ simulator = new Simulator(100, AppenderTracker.THRESHOLD / 2);
+ simulator.buildScenario(20000);
+ simulator.simulate();
+ verify();
+ }
+
+ @Test
+ @Ignore
+ public void longetTest() {
+ simulator = new Simulator(100, AppenderTracker.THRESHOLD / 200);
+ simulator.buildScenario(2000000);
+ simulator.simulate();
+ verify();
+ }
+}
Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/Simulator.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/Simulator.java Wed Dec 17 18:46:01 2008
@@ -0,0 +1,92 @@
+/**
+ * Logback: the generic, reliable, fast and flexible logging framework.
+ *
+ * Copyright (C) 2000-2008, 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
+ * Software Foundation.
+ */
+package ch.qos.logback.classic.hoard;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+import ch.qos.logback.classic.hoard.tracker.SimulationEvent;
+import ch.qos.logback.classic.hoard.tracker.AppenderTrackerTImpl;
+import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.appender.NOPAppender;
+
+/**
+ * Simulate use of AppenderTracker by HoardAppender.
+ *
+ * @author ceki
+ *
+ */
+public class Simulator {
+
+ AppenderTrackerImpl<Object> appenderTracker = new AppenderTrackerImpl<Object>();
+ AppenderTrackerTImpl t_appenderTracker = new AppenderTrackerTImpl();
+
+ List<String> keySpace = new ArrayList<String>();
+ List<SimulationEvent> scenario = new ArrayList<SimulationEvent>();
+ Random randomKeyGen = new Random(100);
+
+ Random random = new Random(11234);
+
+ final int maxTimestampInc;
+ long timestamp = 30000;
+
+ Simulator(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) {
+ 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 simulate() {
+ for (SimulationEvent simeEvent : scenario) {
+ play(simeEvent, appenderTracker);
+ play(simeEvent, t_appenderTracker);
+ }
+ }
+
+ void play(SimulationEvent simulationEvent,
+ AppenderTracker<Object> appenderTracker) {
+ String mdcValue = simulationEvent.key;
+ long timestamp = simulationEvent.timestamp;
+ Appender<Object> appender = appenderTracker.get(mdcValue, timestamp);
+ if (appender == null) {
+ appender = new NOPAppender<Object>();
+ appenderTracker.put(mdcValue, appender, timestamp);
+ }
+ appenderTracker.stopStaleAppenders(timestamp);
+ }
+}
Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/tracker/AppenderTrackerTImpl.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/tracker/AppenderTrackerTImpl.java Wed Dec 17 18:46:01 2008
@@ -0,0 +1,98 @@
+/**
+ * Logback: the generic, reliable, fast and flexible logging framework.
+ *
+ * Copyright (C) 2000-2008, 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
+ * Software Foundation.
+ */
+package ch.qos.logback.classic.hoard.tracker;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import ch.qos.logback.classic.hoard.AppenderTracker;
+import ch.qos.logback.core.Appender;
+
+/**
+ * This is an alternative (slower) implementation of AppenderTracker for testing
+ * purposes.
+ *
+ * @author Ceki Gulcu
+ */
+public class AppenderTrackerTImpl implements AppenderTracker<Object> {
+
+ List<TEntry> entryList = new LinkedList<TEntry>();
+ long lastCheck = 0;
+
+ public AppenderTrackerTImpl() {
+ }
+
+ @SuppressWarnings("unchecked")
+ synchronized public void put(String k, Appender<Object> appender,
+ long timestamp) {
+ TEntry te = getEntry(k);
+ if (te != null) {
+ te.timestamp = timestamp;
+ } else {
+ te = new TEntry(k, appender, timestamp);
+ entryList.add(te);
+ }
+ Collections.sort(entryList);
+ }
+
+ @SuppressWarnings("unchecked")
+ synchronized public Appender<Object> get(String k, long timestamp) {
+ TEntry te = getEntry(k);
+ if (te == null) {
+ return null;
+ } else {
+ te.timestamp = timestamp;
+ Collections.sort(entryList);
+ return te.appender;
+ }
+ }
+
+ synchronized public void stopStaleAppenders(long timestamp) {
+ if (lastCheck + MILLIS_IN_ONE_SECOND > timestamp) {
+ return;
+ }
+ lastCheck = timestamp;
+ while (entryList.size() != 0 && isEntryStale(entryList.get(0), timestamp)) {
+ entryList.remove(0);
+ }
+ }
+
+ final private boolean isEntryStale(TEntry entry, long now) {
+ return ((entry.timestamp + THRESHOLD) < now);
+ }
+
+ synchronized public List<String> keyList() {
+ List<String> keyList = new ArrayList<String>();
+ for (TEntry e : entryList) {
+ keyList.add(e.key);
+ }
+ return keyList;
+ }
+
+ synchronized public List<Appender<Object>> valueList() {
+ List<Appender<Object>> appenderList = new ArrayList<Appender<Object>>();
+ for (TEntry e : entryList) {
+ appenderList.add(e.appender);
+ }
+ return appenderList;
+ }
+
+ 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;
+ }
+}
Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/tracker/SimulationEvent.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/tracker/SimulationEvent.java Wed Dec 17 18:46:01 2008
@@ -0,0 +1,17 @@
+package ch.qos.logback.classic.hoard.tracker;
+
+
+public class SimulationEvent {
+
+ public String key;
+ public long timestamp;
+
+ public SimulationEvent(String key, long timestamp) {
+ this.key = key;
+ this.timestamp = timestamp;
+ }
+
+ public String toString() {
+ return "Event: k=" + key +", timestamp=" + timestamp;
+ }
+}
Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/tracker/TEntry.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/tracker/TEntry.java Wed Dec 17 18:46:01 2008
@@ -0,0 +1,45 @@
+/**
+ * Logback: the generic, reliable, fast and flexible logging framework.
+ *
+ * Copyright (C) 2000-2008, 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
+ * Software Foundation.
+ */
+package ch.qos.logback.classic.hoard.tracker;
+
+import ch.qos.logback.core.Appender;
+
+public class TEntry implements Comparable {
+
+ String key;
+ long timestamp;
+ Appender<Object> appender;
+
+ TEntry(String key, Appender<Object> appender, long timestamp) {
+ this.key = key;
+ this.appender = appender;
+ this.timestamp = timestamp;
+ }
+
+ 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 String toString() {
+ return "("+key+","+timestamp+")";
+ }
+}
More information about the logback-dev
mailing list