You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by fh...@apache.org on 2008/12/04 08:07:25 UTC

svn commit: r723228 - /tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReport.java

Author: fhanik
Date: Wed Dec  3 23:07:23 2008
New Revision: 723228

URL: http://svn.apache.org/viewvc?rev=723228&view=rev
Log:
start working on the interceptor for query stats

Modified:
    tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReport.java

Modified: tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReport.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReport.java?rev=723228&r1=723227&r2=723228&view=diff
==============================================================================
--- tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReport.java (original)
+++ tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReport.java Wed Dec  3 23:07:23 2008
@@ -21,6 +21,10 @@
 import java.lang.reflect.Proxy;
 import java.sql.CallableStatement;
 import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
+import java.util.Map.Entry;
 
 import org.apache.tomcat.jdbc.pool.ConnectionPool;
 import org.apache.tomcat.jdbc.pool.JdbcInterceptor;
@@ -30,78 +34,173 @@
  * @author Filip Hanik
  * @version 1.0
  */
-public class SlowQueryReport extends JdbcInterceptor {
+public class SlowQueryReport extends AbstractCreateStatementInterceptor {
     protected final String[] statements = {"createStatement","prepareStatement","prepareCall"};
     protected final String[] executes = {"execute","executeQuery","executeUpdate","executeBatch"};
 
+    protected static IdentityHashMap<ConnectionPool,HashMap<String,QueryStats>> perPoolStats = 
+        new IdentityHashMap<ConnectionPool,HashMap<String,QueryStats>>();
+    
+    protected HashMap<String,QueryStats> queries = null;
+    
+    protected long threshold = 100; //don't report queries less than this
+    protected int  maxQueries= 1000; //don't store more than this amount of queries
+
+    
+    
     public SlowQueryReport() {
         super();
     }
 
-    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-        boolean process = false;
-        process = process(statements, method, process);
-        if (process) {
-            Object statement = super.invoke(proxy,method,args);
-            CallableStatement measuredStatement =
-                (CallableStatement)Proxy.newProxyInstance(SlowQueryReport.class.getClassLoader(),
-                    new Class[] {java.sql.CallableStatement.class,
-                                 java.sql.PreparedStatement.class,
-                                 java.sql.Statement.class},
-                    new StatementProxy(statement, args));
-
-            return measuredStatement;
-        } else {
-            return super.invoke(proxy,method,args);
+    public long getThreshold() {
+        return threshold;
+    }
+
+    public void setThreshold(long threshold) {
+        this.threshold = threshold;
+    }
+
+    @Override
+    public void closeInvoked() {
+        // TODO Auto-generated method stub
+        
+    }
+
+    @Override
+    public Object createStatement(Object proxy, Method method, Object[] args, Object statement) {
+        // TODO Auto-generated method stub
+        String sql = null;
+        if (method.getName().startsWith("prepare")) {
+            sql = (args.length>0 && (args[0] instanceof String))?(String)args[0]:null;
         }
+        return new StatementProxy(statement,sql);
     }
 
-    protected boolean process(String[] names, Method method, boolean process) {
+    protected boolean process(final String[] names, Method method, boolean process) {
         for (int i=0; (!process) && i<names.length; i++) {
-            process = (method.getName()==names[i]);
+            process = compare(method.getName(),names[i]);
         }
         return process;
     }
 
+    protected class QueryStats {
+        private final String query;
+        private int nrOfInvocations;
+        private long maxInvocationTime;
+        private long maxInvocationDate;
+        private long minInvocationTime;
+        private long minInvocationDate;
+        private long totalInvocationTime;
+        
+        public QueryStats(String query) {
+            this.query = query;
+        }
+        
+        public void add(long invocationTime) {
+            long now = -1;
+            //not thread safe, but don't sacrifice performance for this kind of stuff
+            maxInvocationTime = Math.max(invocationTime, maxInvocationTime);
+            if (maxInvocationTime == invocationTime) {
+                now = System.currentTimeMillis();
+                maxInvocationDate = now;
+            }
+            minInvocationTime = Math.min(invocationTime, minInvocationTime);
+            if (minInvocationTime==invocationTime) {
+                now = (now==-1)?System.currentTimeMillis():now;
+                minInvocationDate = now;
+            }
+            nrOfInvocations++;
+            totalInvocationTime+=invocationTime;
+        }
+        
+        public String getQuery() {
+            return query;
+        }
+
+        public int getNrOfInvocations() {
+            return nrOfInvocations;
+        }
+
+        public long getMaxInvocationTime() {
+            return maxInvocationTime;
+        }
+
+        public long getMaxInvocationDate() {
+            return maxInvocationDate;
+        }
+
+        public long getMinInvocationTime() {
+            return minInvocationTime;
+        }
+
+        public long getMinInvocationDate() {
+            return minInvocationDate;
+        }
+
+        public long getTotalInvocationTime() {
+            return totalInvocationTime;
+        }
+
+        public int hashCode() {
+            return query.hashCode();
+        }
+        
+        public boolean equals(Object other) {
+            if (other instanceof QueryStats) {
+                QueryStats qs = (QueryStats)other;
+                return SlowQueryReport.this.compare(qs.query,this.query);
+            } 
+            return false;
+        }
+    }
+    
     protected class StatementProxy implements InvocationHandler {
-        protected Object parent;
-        protected Object[] args;
-        public StatementProxy(Object parent, Object[] args) {
-            this.parent = parent;
-            this.args = args;
+        protected boolean closed = false;
+        protected Object delegate;
+        protected final String query;
+        public StatementProxy(Object parent, String query) {
+            this.delegate = parent;
+            this.query = query;
         }
+        
         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-            if (this.parent == null ) throw new SQLException("Statement has been closed.");
+            final String name = method.getName();
+            boolean close = compare(JdbcInterceptor.CLOSE_VAL,name);
+            if (close && closed) return null; //allow close to be called multiple times
+            if (closed) throw new SQLException("Statement closed.");
             boolean process = false;
             process = process(executes, method, process);
             long start = (process)?System.currentTimeMillis():0;
             //execute the query
-            Object result =  method.invoke(parent,args);
+            Object result =  method.invoke(delegate,args);
             long delta = (process)?(System.currentTimeMillis()-start):0;
-            if (delta>10) {
-                StringBuffer out = new StringBuffer("\n\tType:");
-                out.append(parent.getClass().getName());
-                out.append("\n\tCreate/Prepare args:");
-                for (int i=0; this.args!=null && i<this.args.length;i++) {
-                    out.append(this.args[i]!=null?this.args[i]:"null");
-                    out.append("; ");
+            if (delta>threshold) {
+                String sql = null;//TODO
+                QueryStats qs = SlowQueryReport.this.queries.get(sql);
+                if (qs == null) {
+                    qs = new QueryStats(sql);
+                    SlowQueryReport.this.queries.put((String)sql,qs);
                 }
-                out.append("\n\tExecute args:");
-                for (int i=0; args!=null && i<args.length;i++) {
-                    out.append(args[i]!=null?args[i]:"null");
-                    out.append("; ");
-                }
-                System.out.println("Slow query:"+out+"\nTime to execute:"+(delta)+" ms.");
+                qs.add(delta);
+                return qs;
             }
-            if (JdbcInterceptor.CLOSE_VAL==method.getName()) {
-                this.parent = null;
-                this.args = null;
+            if (close) {
+                closed=true;
+                delegate = null;
             }
             return result;
         }
     }
 
     public void reset(ConnectionPool parent, PooledConnection con) {
+        if (queries==null && SlowQueryReport.perPoolStats.get(parent)==null) {
+            queries = new LinkedHashMap<String,QueryStats>() {
+                @Override
+                protected boolean removeEldestEntry(Entry<String, QueryStats> eldest) {
+                    return size()>maxQueries;
+                }
 
+            };
+        }
     }
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org