[logback-dev] svn commit: r2086 - logback/trunk/logback-core/src/main/java/ch/qos/logback/core/sift

noreply.ceki at qos.ch noreply.ceki at qos.ch
Thu Dec 18 21:48:23 CET 2008


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();
+}


More information about the logback-dev mailing list