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