All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.jmatrix.db.common.PerfTrack Maven / Gradle / Ivy

The newest version!
package net.jmatrix.db.common;

import java.util.*;
import java.lang.reflect.Method;

//import org.apache.commons.logging.Log;

/**
 * PerfTrack tracks performance through multiple operations within
 * a Thread.  Functionally it works with static method calls, that can 
 * be called from anywhere within the code.  Primary methods are start/stop - 
 * which adds a tracking of the performance (or wall clock time) it takes 
 * to execute the operation in question.  
 * 
 * The PerfTrack builds a tree-like structure showing the amounts of time taken
 * for each sub-operation.  
 * 
 * This will be useful in tracking performance issues in the web application, 
 * and the tree based performance output allows us to focus in rapidly on 
 * problem areas of the code and its interactions with external system.
 * 
 * This code was Modelled on the Log4J MDC (Mapped Diagnostic Context).
 * 
 * @author Paul Bemowski
 */
public final class PerfTrack {
   //static Log log=ClassLogFactory.getLog();
   
   // FIXME: 
   //   - Change to InheritableThreadLocal??
   //   - Ensure thread safety - I think its threadsafe now.
   static ThreadLocal threadLocalCurrent=new ThreadLocal();
   
   /** */
   public static void start(Method m) {
      String s=getMethodString(m);
      start(s);
   }
   
   /** */
   public static void start(String x) {
      if (x == null)
         throw new NullPointerException("Null PerfTrack name.  Cannot perftrack null.");
      
      Item current=threadLocalCurrent.get();
      
      // fixme: remove - verbose PerfTrack Debug.
      //log.debug("PerfTrack.start("+x+"), current: "+current);
      
      if (current == null) {
         current=new Item(x, null);
         current.start();
         threadLocalCurrent.set(current);
      } else {
         Item child=new Item(x, current);  // new sub-item
         child.start();
         threadLocalCurrent.set(child);
      }
   }
   
   /** */
   public static void stop(Method m) {
      String s=getMethodString(m);
      stop(s);
   }
   
   /** Returns a string representing a method. */
   private static final String getMethodString(Method m) {
      String classname=m.getDeclaringClass().getName();
      classname=classname.substring(classname.lastIndexOf(".")+1);
      return classname+"."+m.getName();
   }
   
   /** */
   public static void stop(String x) {
      if (x == null)
         throw new NullPointerException("Null PerfTrack name.  Cannot perftrack null.");
      
      Item current=threadLocalCurrent.get();
      
      // fixme: remove - verbose PerfTrack Debug.
      //log.debug("PerfTrack.stop("+x+"), current: "+current);
      
      if (current==null) {
         System.out.println("Stopping, but current is null??");
      } else {
         if (current.getName() != null && current.getName().equals(x)) {
            current.stop();
            
            Item parent=current.getParent();
            if (parent != null) {
               threadLocalCurrent.set(parent);
            } else {
               // leave current as root...
               //threadLocalCurrent.set(null);
            }
         } else {
            // stopping item that is not current.  likely because 
            // someone forgot to stop a PerfTrack with try/finally
            // or other programming error/typo.
            System.out.println("Stopping '"+x+"' but current is '"+current.getName()+"'");
            
         }
      }
   }
   
   /** */
   public static String reportAndClear() {
      String s=toString(0);
      clear();
      return s;
   }
   
   public static final boolean hasData() {
      return threadLocalCurrent.get() != null;
   }
   
   /** */
   public static void clear() {
      //log.debug("PerfTrack.clear()");
      threadLocalCurrent.remove();
   }
   
   /** */
   public static boolean isCurrentRootAndComplete() {
      Item current=threadLocalCurrent.get();
      if (current != null ) {
         if (current.getParent() == null) {
            if (current.isDone()) {
               return true;
            }
         }
      } 
//      else {
//         return true;
//      }
      return false;
   }

   public static String toString(int depth) {
      Item current=threadLocalCurrent.get();
      if (current != null ) {
         if (current.getParent() == null) {
            // this is the root.
            return current.toString(depth);
         } else {
            // this is more common now.  adding perftrack to syslog.  
            // we just append UF (unfinished) to current time.
            //log.warn("toString() called, but current is not root.  Unfinished tree items.");
            Item root=current.findRoot();
            return root.toString(depth);
         }
      }
      else 
         return "PerfTrack: no data?";
   }
   
   /**  */
   static final class Item {
      String name;
      int callCount=0;
      Item parent=null;
      long start=0;
      long stop=0;
      long et=0;
      List children=new ArrayList();
      
      /** */
      public Item(String n, Item p) {
         name=n;
         parent=p;
         callCount++;
         if (parent != null) 
            parent.addChild(this);
      }
      
      public void start() {start=System.currentTimeMillis();}
      public void stop() {stop=System.currentTimeMillis(); et=stop-start;}
      public List getChildren() {return children;}
      public String getName() {return name;}
      public Item getParent() {return parent;}
      
      public String toString() {
         return "PTItem("+name+", start="+start+", stop="+stop+", parent="+parent+")";
      }
      
      public boolean hasChildren() {
         if (children.size() > 0)
            return true;
         return false;
      }
      
      public final boolean isDone() {
         if (stop == 0)
            return false;
         return true;
      }
      
      public final long getEt() {
         if (stop == 0) {
            return System.currentTimeMillis()-start;
         }
         return et;
      }
      
      public void addChild(Item i) {
         if (children.size() > 0) {
            Item lastChild=children.get(children.size()-1);
            if (i.getName().equals(lastChild.getName())) {
               // just add count and time.
               lastChild.callCount++;
               lastChild.et=i.et;
            } else {
               children.add(i);
            }
         } else {
            children.add(i);
         }
      }
      
      public Item findRoot() {
         if (parent == null)
            return this;
         else
            return parent.findRoot();
      }
      
      public String toString(int depth) {
         StringBuilder sb=new StringBuilder();
         for (int i=0; i 1?"("+callCount+") ":"")+ (localstop-start)+
               (timesuffix == null?"":"(UF)")+"ms\n");
         
         if (children.size() > 0) {
            long unaccounted=et;
            
            for (Item child:children) {
               unaccounted=unaccounted-child.getEt();
               
               sb.append(child.toString(depth+1));
            }
            
            // unaccounted
            for (int i=0; i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy