[logback-dev] svn commit: r1790 - in logback/trunk/logback-classic/src: main/java/ch/qos/logback/classic/pattern main/java/ch/qos/logback/classic/spi test/java/ch/qos/logback/classic/pattern test/java/ch/qos/logback/classic/pattern/lru

noreply.ceki at qos.ch noreply.ceki at qos.ch
Tue Sep 2 18:34:48 CEST 2008


Author: ceki
Date: Tue Sep  2 18:34:48 2008
New Revision: 1790

Added:
   logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LRUCache.java
   logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/PackageInfo.java
   logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/LRUCacheTest.java
   logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/Simulator.java
   logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/UtilTest.java
   logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/lru/
   logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/lru/Event.java
   logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/lru/T_Entry.java
   logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/lru/T_LRUCache.java
Modified:
   logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/Util.java
   logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableInformation.java

Log:
LBGENERAL-23

Extracting package information is a time consuming process. 
Improve performance by keeping previously found results in a cache.
Added an LRUCache with accompanying test cases for this purpose. 

Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LRUCache.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LRUCache.java	Tue Sep  2 18:34:48 2008
@@ -0,0 +1,156 @@
+package ch.qos.logback.classic.pattern;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+public class LRUCache<K, V> {
+
+  Map<K, Entry> map = new HashMap<K, Entry>();
+  Entry head;
+  Entry tail;
+
+  int limit;
+
+  LRUCache(int limit) {
+    if(limit < 1) {
+       throw new IllegalArgumentException("limit cannnot be smaller than 1");
+    } 
+    
+    this.limit = limit;
+ 
+    head = new Entry(null, null);
+    tail = head;
+  }
+
+  public void put(K key, V value) {
+    Entry entry = map.get(key);
+    if (entry == null) {
+      entry = new Entry(key, value);
+      map.put(key, entry);
+    } 
+    moveToTail(entry);
+    while(map.size() > limit) {
+      removeHead();
+    }
+  }
+
+  public V get(K key) {
+    Entry existing = map.get(key);
+    if (existing == null) {
+      return null;
+    } else {
+      moveToTail(existing);
+      return existing.value;
+    }
+  }
+
+  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();
+  }
+
+  List<K> keyList() {
+    List<K> result = new LinkedList<K>();
+    Entry e = head;
+    while (e != tail) {
+      result.add(e.key);
+      e = e.next;
+    }
+    return result;
+  }
+  
+  // ================================================================ 
+  private class Entry {
+    Entry next;
+    Entry prev;
+    K key;
+    V value;
+
+    Entry(K k, V v) {
+      this.key = k;
+      this.value = v;
+    }
+
+    @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-classic/src/main/java/ch/qos/logback/classic/pattern/PackageInfo.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/PackageInfo.java	Tue Sep  2 18:34:48 2008
@@ -0,0 +1,13 @@
+package ch.qos.logback.classic.pattern;
+
+public class PackageInfo {
+
+  
+  String jarName;
+  String version;
+  
+  PackageInfo(String jarName, String version) {
+    this.jarName = jarName;
+    this.version = version;
+  }
+}

Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/Util.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/Util.java	(original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/Util.java	Tue Sep  2 18:34:48 2008
@@ -1,33 +1,128 @@
 /**
- * LOGBack: the reliable, fast and flexible logging library for Java.
- *
- * Copyright (C) 1999-2006, 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.
+ * 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.pattern;
 
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
 import org.slf4j.Marker;
 
+/**
+ * 
+ * @author James Strachan
+ * @author Ceki Gulcu
+ */
 public class Util {
 
+  static Map<String, PackageInfo> cache = new HashMap<String, PackageInfo>();
+
   static public boolean match(Marker marker, Marker[] markerArray) {
-    if(markerArray == null) {
+    if (markerArray == null) {
       throw new IllegalArgumentException("markerArray should not be null");
     }
-    
-    //System.out.println("event marker="+marker);
-    
+
+    // System.out.println("event marker="+marker);
+
     final int size = markerArray.length;
-    for(int i = 0; i < size; i++) {
-      //System.out.println("other:"+markerArray[i]);
-      
-      if(marker.contains(markerArray[i])) {
+    for (int i = 0; i < size; i++) {
+      // System.out.println("other:"+markerArray[i]);
+
+      if (marker.contains(markerArray[i])) {
         return true;
       }
     }
     return false;
   }
+
+  static String getVersion(String className) {
+    String packageName = getPackageName(className);
+    Package aPackage = Package.getPackage(packageName);
+    if (aPackage != null) {
+      String v = aPackage.getImplementationVersion();
+      if (v == null) {
+        return "na";
+      } else {
+        return v;
+      }
+    }
+    return "na";
+  }
+
+  static public PackageInfo getPackageInfo(String className) {
+    PackageInfo pi = cache.get(className);
+    if(pi != null) {
+      return pi;
+    }
+    String version = getVersion(className);
+    String jarname = getJarNameOfClass(className);
+    pi = new PackageInfo(jarname, version);
+    //cache.put(className, pi);
+    return pi;
+  }
+
+  static String getPackageName(String className) {
+    int j = className.lastIndexOf('.');
+    return className.substring(0, j);
+  }
+
+  /**
+   * Uses the context class path or the current global class loader to deduce
+   * the file that the given class name comes from
+   */
+  static String getJarNameOfClass(String className) {
+    try {
+      Class type = findClass(className);
+      if (type != null) {
+        URL resource = type.getClassLoader().getResource(
+            type.getName().replace('.', '/') + ".class");
+        // "jar:file:/C:/java/../repo/groupId/artifact/1.3/artifact-1.3.jar!/com/some/package/Some.class
+        if (resource != null) {
+          String text = resource.toString();
+          int idx = text.lastIndexOf('!');
+          if (idx > 0) {
+            text = text.substring(0, idx);
+            // now lets remove all but the file name
+            idx = text.lastIndexOf('/');
+            if (idx > 0) {
+              text = text.substring(idx + 1);
+            }
+            idx = text.lastIndexOf('\\');
+            if (idx > 0) {
+              text = text.substring(idx + 1);
+            }
+            return text;
+          }
+        }
+      }
+    } catch (Exception e) {
+      // ignore
+    }
+    return "na";
+  }
+
+  static private Class findClass(String className) {
+    try {
+      return Thread.currentThread().getContextClassLoader()
+          .loadClass(className);
+    } catch (ClassNotFoundException e) {
+      try {
+        return Class.forName(className);
+      } catch (ClassNotFoundException e1) {
+        try {
+          return Util.class.getClassLoader().loadClass(className);
+        } catch (ClassNotFoundException e2) {
+          return null;
+        }
+      }
+    }
+  }
+
 }

Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableInformation.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableInformation.java	(original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableInformation.java	Tue Sep  2 18:34:48 2008
@@ -29,7 +29,7 @@
   }
   
   /**
-   * The string representation of the exceptopn (throwable) that this object
+   * The string representation of the throwable  that this object
    * represents.
    */
   public String[] getThrowableStrRep() {

Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/LRUCacheTest.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/LRUCacheTest.java	Tue Sep  2 18:34:48 2008
@@ -0,0 +1,71 @@
+package ch.qos.logback.classic.pattern;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import ch.qos.logback.classic.pattern.lru.Event;
+import ch.qos.logback.classic.pattern.lru.T_LRUCache;
+
+public class LRUCacheTest {
+
+  @Before
+  public void setUp() throws Exception {
+  }
+
+  @After
+  public void tearDown() throws Exception {
+
+  }
+
+  @Test
+  public void smoke() {
+    LRUCache<String, String> cache = new LRUCache<String, String>(2);
+    cache.put("a", "a");
+    cache.put("b", "b");
+    cache.put("c", "c");
+    List<String> witness = new LinkedList<String>();
+    witness.add("b");
+    witness.add("c");
+    assertEquals(witness, cache.keyList());
+  }
+
+  @Test
+  public void typicalScenarioTest() {
+    int simulationLen = 1000 * 20;
+    int cacheSize = 500;
+    int worldSize = 10000;
+    doScenario(simulationLen, cacheSize, worldSize);
+  }
+
+  @Test
+  public void scenarioCoverageTest() {
+    int simulationLen = 1000 * 20;
+    int[] cacheSizes = new int[] {1,5,10,100,1000,5000,10000};
+    int[] worldSizes = new int[] {1,10,100,1000,20000};
+    for (int i = 0; i < cacheSizes.length; i++) {
+      for (int j = 0; j < worldSizes.length; j++) {
+        System.out.println("cacheSize="+cacheSizes[i]+", worldSize="+worldSizes[j]);
+        doScenario(simulationLen, cacheSizes[i], worldSizes[j]);
+      }
+    }
+  }
+
+  void doScenario(int simulationLen, int chacheSize, int worldSize) {
+    int cacheSize = 500;
+    int get2PutRatio = 10;
+
+    Simulator simulator = new Simulator(worldSize, get2PutRatio);
+    List<Event> scenario = simulator.generateScenario(simulationLen);
+    LRUCache<String, String> lruCache = new LRUCache<String, String>(cacheSize);
+    T_LRUCache<String> tlruCache = new T_LRUCache<String>(cacheSize);
+    simulator.simulate(scenario, lruCache, tlruCache);
+    assertEquals(tlruCache.ketList(), lruCache.keyList());
+  }
+}

Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/Simulator.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/Simulator.java	Tue Sep  2 18:34:48 2008
@@ -0,0 +1,70 @@
+package ch.qos.logback.classic.pattern;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import ch.qos.logback.classic.pattern.lru.Event;
+import ch.qos.logback.classic.pattern.lru.T_LRUCache;
+
+public class Simulator {
+
+  
+  Random random;
+  
+  int worldSize;
+  int get2PutRatio;
+  
+  public Simulator(int worldSize, int get2PutRatio) {
+    this.worldSize = worldSize;
+    this.get2PutRatio = get2PutRatio;
+    long seed = System.nanoTime();
+    System.out.println("seed is "+seed);
+    random = new Random(seed);
+  }
+  
+  public List<Event> generateScenario(int len) {
+    List<Event> scenario = new ArrayList<Event>();
+    
+    for(int i = 0; i < len; i++) {
+      
+      int r = random.nextInt(get2PutRatio);
+      boolean put = false;
+      if(r == 0) {
+        put = true;
+      }
+      r = random.nextInt(worldSize);
+      Event<String> e = new Event<String>(put, String.valueOf(r));
+      scenario.add(e);
+    }
+    return scenario;
+  }
+  
+  public void simulate(List<Event> scenario, LRUCache<String, String> lruCache, T_LRUCache<String> tlruCache) {
+    for(Event<String> e: scenario) {
+      if(e.put) {
+        lruCache.put(e.k, e.k);
+        tlruCache.put(e.k);
+      } else {
+        String r0 = lruCache.get(e.k);
+        String r1 = tlruCache.get(e.k);
+        if(r0 != null) {
+          assertEquals(r0, e.k);
+        }
+        assertEquals(r0, r1);
+      }
+    }
+  }
+  
+//  void compareAndDumpIfDifferent(LRUCache<String, String> lruCache, T_LRUCache<String> tlruCache) {
+//    lruCache.dump();
+//    tlruCache.dump();
+//    if(!lruCache.keyList().equals(tlruCache.ketList())) {
+//      lruCache.dump();
+//      tlruCache.dump();
+//      throw new AssertionFailedError("s");
+//    }
+//  }
+}

Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/UtilTest.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/UtilTest.java	Tue Sep  2 18:34:48 2008
@@ -0,0 +1,84 @@
+package ch.qos.logback.classic.pattern;
+
+import java.util.Random;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.icegreen.greenmail.util.GreenMail;
+import com.icegreen.greenmail.util.ServerSetup;
+
+public class UtilTest {
+
+  int diff = 1024 + new Random().nextInt(10000);
+
+  @Before
+  public void setUp() throws Exception {
+  }
+
+  @After
+  public void tearDown() throws Exception {
+  }
+
+  @Test
+  public void withGreenMail() {
+    try {
+      ServerSetup serverSetup = new ServerSetup(-1, "localhost",
+          ServerSetup.PROTOCOL_SMTP);
+      GreenMail greenMail = new GreenMail((ServerSetup) null);
+      // greenMail.start();
+    } catch (Throwable e) {
+      // e.printStackTrace();
+      StackTraceElement[] stea = e.getStackTrace();
+      for (StackTraceElement ste : stea) {
+        String className = ste.getClassName();
+        PackageInfo pi = Util.getPackageInfo(className);
+        System.out.println("  at " + className + "." + ste.getMethodName()
+            + "(" + ste.getFileName() + ":" + ste.getLineNumber() + ") ["
+            + pi.jarName + ":" + pi.version + "]");
+      }
+    }
+  }
+
+  public void doPerf(boolean versionExtraction) {
+    try {
+      ServerSetup serverSetup = new ServerSetup(-1, "localhost",
+          ServerSetup.PROTOCOL_SMTP);
+      GreenMail greenMail = new GreenMail((ServerSetup) null);
+      // greenMail.start();
+    } catch (Throwable e) {
+      StackTraceElement[] stea = e.getStackTrace();
+      if (versionExtraction) {
+        for (StackTraceElement ste : stea) {
+          String className = ste.getClassName();
+          PackageInfo pi = Util.getPackageInfo(className);
+        }
+      }
+    }
+  }
+
+  double loop(int len, boolean ve) {
+    long start = System.nanoTime();
+    for (int i = 0; i < len; i++) {
+      doPerf(ve);
+    }
+    return (1.0*System.nanoTime() - start)/len/1000;
+  }
+
+  @Test
+  @Ignore
+  public void perfTest() {
+    int len = 1000;
+    loop(len, false);
+    double d0 = loop(len, false);
+
+    System.out.println("false " + d0);
+
+    loop(len, true);
+    double d1 = loop(len, true);
+
+    System.out.println("false " + d1);
+  }
+}

Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/lru/Event.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/lru/Event.java	Tue Sep  2 18:34:48 2008
@@ -0,0 +1,20 @@
+package ch.qos.logback.classic.pattern.lru;
+
+public class Event<K> {
+
+  final public boolean put;
+  final public K k;
+  
+  public Event(boolean put, K k) {
+    this.put = put;
+    this.k = k;
+  }
+  
+  public String toString() {
+    if(put) {
+      return "Event: put, "+k;
+    } else {
+      return "Event: get, "+k;
+    }
+  }
+}

Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/lru/T_Entry.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/lru/T_Entry.java	Tue Sep  2 18:34:48 2008
@@ -0,0 +1,32 @@
+package ch.qos.logback.classic.pattern.lru;
+
+public class T_Entry<K> implements Comparable {
+
+  K k;
+  long sequenceNumber;
+  
+  T_Entry(K k, long sn) {
+    this.k = k;
+    this.sequenceNumber = sn;
+  }
+
+  public int compareTo(Object o) {
+    if(!(o instanceof T_Entry)) {
+      throw new IllegalArgumentException("arguments must be of type "+T_Entry.class);
+    }
+    
+    T_Entry other = (T_Entry) o;
+    if(sequenceNumber > other.sequenceNumber) {
+      return 1;
+    }
+    if(sequenceNumber == other.sequenceNumber) {
+      return 0;
+    }
+    return -1;
+  }
+  @Override
+  public String toString() {
+    return "("+k+","+sequenceNumber+")";
+    //return "("+k+")";
+  }
+}

Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/lru/T_LRUCache.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/lru/T_LRUCache.java	Tue Sep  2 18:34:48 2008
@@ -0,0 +1,88 @@
+/**
+ * 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.pattern.lru;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * This is an alternative (slower) implementation of LRUCache for testing
+ * purposes.
+ * 
+ * @author Ceki Gulcu
+ */
+public class T_LRUCache<K> {
+
+  int sequenceNumber;
+  final int cacheSize;
+  List<T_Entry<K>> cacheList = new LinkedList<T_Entry<K>>();
+
+  public T_LRUCache(int size) {
+    this.cacheSize = size;
+  }
+
+  @SuppressWarnings("unchecked")
+  public void put(K k) {
+    sequenceNumber++;
+    T_Entry<K> te = getEntry(k);
+    if (te != null) {
+      te.sequenceNumber = sequenceNumber;
+    } else {
+      te = new T_Entry<K>(k, sequenceNumber);
+      cacheList.add(te);
+    }
+    Collections.sort(cacheList);
+    while(cacheList.size() > cacheSize) {
+      cacheList.remove(0);    
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  public K get(K k) {
+    T_Entry<K> te = getEntry(k);
+    if (te == null) {
+      return null;
+    } else {
+      te.sequenceNumber = ++sequenceNumber;
+      Collections.sort(cacheList);
+      return te.k;
+    }
+  }
+
+  public List<K> ketList() {
+    List<K> keyList = new ArrayList<K>();
+    for (T_Entry<K> e : cacheList) {
+      keyList.add(e.k);
+    }
+    return keyList;
+  }
+
+  private T_Entry<K> getEntry(K k) {
+    for (int i = 0; i < cacheList.size(); i++) {
+      T_Entry<K> te = cacheList.get(i);
+      if (te.k.equals(k)) {
+        return te;
+      }
+    }
+    return null;
+  }
+  
+  public void dump() {
+    System.out.print("T:");
+    for (T_Entry<K> te : cacheList) {
+      //System.out.print(te.toString()+"->");
+      System.out.print(te.k+", ");
+    }
+    System.out.println();
+  }
+
+}


More information about the logback-dev mailing list