
Author: ceki Date: Thu Dec 18 21:48:23 2008 New Revision: 2086 Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/sift/ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderFactory.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderTracker.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderTrackerImpl.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/sift/SiftingAppenderBase.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/sift/SiftingJoranConfigurator.java Log: - migrating SiftingAppender to logback-core from logback-classic Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderFactory.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderFactory.java Thu Dec 18 21:48:23 2008 @@ -0,0 +1,43 @@ +package ch.qos.logback.core.sift; + +import java.util.ArrayList; +import java.util.List; + +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.event.SaxEvent; +import ch.qos.logback.core.joran.spi.JoranException; + +public abstract class AppenderFactory<E, K> { + + final List<SaxEvent> eventList; + Context context; + + AppenderFactory(Context context, List<SaxEvent> eventList) { + this.context = context; + this.eventList = new ArrayList<SaxEvent>(eventList); + removeHoardElement(); + + } + + void removeHoardElement() { + eventList.remove(0); + eventList.remove(eventList.size() - 1); + System.out.println(eventList); + } + + + abstract SiftingJoranConfigurator<E> getSiftingJoranConfigurator(K k); + + Appender<E> buildAppender(Context context, K k) throws JoranException { + SiftingJoranConfigurator<E> sjc = getSiftingJoranConfigurator(k); + sjc.setContext(context); + sjc.doConfigure(eventList); + return sjc.getAppender(); + } + + public List<SaxEvent> getEventList() { + return eventList; + } + +} Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderTracker.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderTracker.java Thu Dec 18 21:48:23 2008 @@ -0,0 +1,28 @@ +/** + * 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.core.sift; + +import java.util.List; + +import ch.qos.logback.core.Appender; + +public interface AppenderTracker<E, K> { + + static int MILLIS_IN_ONE_SECOND = 1000; + static int THRESHOLD = 30 * 60 * MILLIS_IN_ONE_SECOND; // 30 minutes + + void put(K key, Appender<E> value, long timestamp); + Appender<E> get(String key, long timestamp); + void stopStaleAppenders(long timestamp); + List<K> keyList(); + List<Appender<E>> valueList(); + + +} \ No newline at end of file Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderTrackerImpl.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderTrackerImpl.java Thu Dec 18 21:48:23 2008 @@ -0,0 +1,207 @@ +/** + * 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.core.sift; + +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, K> implements AppenderTracker<E, K> { + + Map<K, Entry> map = new HashMap<K, 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; + } + + + public synchronized void put(K 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 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; + } + } + + + 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<K> keyList() { + List<K> result = new LinkedList<K>(); + 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; + + K key; + Appender<E> value; + long timestamp; + + Entry(K 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 + ")"; + } + } +} Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/sift/SiftingAppenderBase.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/sift/SiftingAppenderBase.java Thu Dec 18 21:48:23 2008 @@ -0,0 +1,112 @@ +/** + * 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.core.sift; + + +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 abstract class SiftingAppenderBase<E, K> extends UnsynchronizedAppenderBase<E> { + + AppenderTracker<E, K> appenderTracker = new AppenderTrackerImpl<E, K>(); + //Map<String, Appender<LoggingEvent>> appenderMap = new Hashtable<String, Appender<LoggingEvent>>(); + + String mdcKey; + String defaultValue; + + AppenderFactory<E, K> appenderFactory; + + void setAppenderFactory(AppenderFactory<E, K> appenderFactory) { + 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<E> appender : appenderTracker.valueList()) { + appender.stop(); + } + } + + abstract protected K getDiscriminatingValue(E event); + abstract protected long getTimestamp(E event); + + @Override + protected void append(E event) { + if (!isStarted()) { + return; + } + + + K value = getDiscriminatingValue(event); + long timestamp = getTimestamp(event); + + Appender<E> appender = appenderTracker.get(value, timestamp); + + if (appender == null) { + try { + appender = appenderFactory.buildAppender(context, value); + if (appender != null) { + appenderTracker.put(value, appender, timestamp); + } + } catch (JoranException e) { + addError("Failed to build appender for [" + value + "]", e); + return; + } + } + appenderTracker.stopStaleAppenders(timestamp); + appender.doAppend(event); + } + + public String getMdcKey() { + return mdcKey; + } + + public void setMdcKey(String mdcKey) { + this.mdcKey = mdcKey; + } + + /** + * @see #setDefaultValue(String) + * @return + */ + public String getDefaultValue() { + return defaultValue; + } + + +} Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/sift/SiftingJoranConfigurator.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/sift/SiftingJoranConfigurator.java Thu Dec 18 21:48:23 2008 @@ -0,0 +1,23 @@ +package ch.qos.logback.core.sift; + +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.joran.GenericConfigurator; +import ch.qos.logback.core.joran.spi.Interpreter; +import ch.qos.logback.core.joran.spi.RuleStore; + +public abstract class SiftingJoranConfigurator<E> extends GenericConfigurator { + + @Override + protected void addImplicitRules(Interpreter interpreter) { + // TODO Auto-generated method stub + + } + + @Override + protected void addInstanceRules(RuleStore rs) { + // TODO Auto-generated method stub + + } + + abstract Appender<E> getAppender(); +}