|
Installation
Installation instructions.
IntroductionCurrently, the installation of jordens-jdbcspy involves checking out source code and performing a maven build. Binary builds will be coming. Checkout
Build
IntelliJ IDEA is my editor of choice, the following should generate appropriate project files:
This will generate a multi-module IntelliJ project that should automatically download sources and setup library configurations. RunAfter a successful build, you should have a jordens-jdbcspy-core-1.0-SNAPSHOT.jar file in the modules/jdbcspy-core/target/ directory.
You're done. If you point jconsole at your application, you should see the JDBCSpyMBean and be able to query for some basic attributes and statistics. When running jconsole, please make sure that jordens-jdbcspy-core-1.0-SNAPSHOT.jar is on your classpath. If you've specified a valid JDBCSpyDriver.logFile, you should be able to 'tail -f' it and get a streaming update of JDBC activity. JDBCSpy ViewerThe code for the JDBCSpy Viewer resides in modules/jdbcspy-ui.
You're good to go. You can either File -> Connect To... or File -> Open Log File. |
Sign in to add a comment
INFO? ------------------------------------------------------------------------ ERROR? BUILD FAILURE INFO? ------------------------------------------------------------------------ INFO? Compilation failure /home/jamesr/Working/jordens-jdbcspy/modules/jdbcspy-core/src/main/java/org/jordens/jdbcspy/JDBCSpyConnection.java:15,7? org.jordens.jdbcspy.JDBCSpyConnection is not abstract and does not override abstract method createStruct(java.lang.String,java.lang.Object) in java.sql.Connection
:(
The following file contents for JDBCSpyConnection.java builds:
package org.jordens.jdbcspy; import org.jordens.jdbcspy.jmx.*; import java.lang.reflect.*; import java.sql.*; import java.util.*; /** * JDBC Connection implementation that wraps another JDBC connection and logs * Statement, PreparedStatement and CallableStatement executions. * * @author Adam Jordens */ public class JDBCSpyConnection implements Connection { private static final JDBCSpy jdbcSpy; private final Connection delegate; static { jdbcSpy = new JDBCSpy(); jdbcSpy.registerBean(); } /** * @param wrappedConnection JDBC connection to wrap */ public JDBCSpyConnection(Connection wrappedConnection) { this.delegate = wrappedConnection; } /** * {@inheritDoc} */ public Statement createStatement() throws SQLException { return createProxy(Statement.class, new GenericStatementHandler(delegate.createStatement())); } /** * {@inheritDoc} */ public PreparedStatement prepareStatement(String s) throws SQLException { return createProxy(PreparedStatement.class, new GenericStatementHandler(delegate.prepareStatement(s))); } /** * {@inheritDoc} */ public CallableStatement prepareCall(String s) throws SQLException { return createProxy(CallableStatement.class, new GenericStatementHandler(delegate.prepareCall(s))); } /** * {@inheritDoc} */ public Statement createStatement(int i, int i1) throws SQLException { return createProxy(Statement.class, new GenericStatementHandler(delegate.createStatement(i, i1))); } /** * {@inheritDoc} */ public PreparedStatement prepareStatement(String s, int i, int i1) throws SQLException { return createProxy(PreparedStatement.class, new GenericStatementHandler(delegate.prepareStatement(s, i, i1))); } /** * {@inheritDoc} */ public CallableStatement prepareCall(String s, int i, int i1) throws SQLException { return createProxy(CallableStatement.class, new GenericStatementHandler(delegate.prepareCall(s, i, i1))); } /** * {@inheritDoc} */ public Statement createStatement(int i, int i1, int i2) throws SQLException { return createProxy(Statement.class, new GenericStatementHandler(delegate.createStatement(i, i1, i2))); } /** * {@inheritDoc} */ public PreparedStatement prepareStatement(String s, int i, int i1, int i2) throws SQLException { return createProxy(PreparedStatement.class, new GenericStatementHandler(delegate.prepareStatement(s, i, i1, i2))); } /** * {@inheritDoc} */ public CallableStatement prepareCall(String s, int i, int i1, int i2) throws SQLException { return createProxy(CallableStatement.class, new GenericStatementHandler(delegate.prepareCall(s, i, i1, i2))); } /** * {@inheritDoc} */ public PreparedStatement prepareStatement(String s, int i) throws SQLException { return createProxy(PreparedStatement.class, new GenericStatementHandler(delegate.prepareStatement(s, i))); } /** * {@inheritDoc} */ public PreparedStatement prepareStatement(String s, int[] ints) throws SQLException { return createProxy(PreparedStatement.class, new GenericStatementHandler(delegate.prepareStatement(s, ints))); } /** * {@inheritDoc} */ public PreparedStatement prepareStatement(String s, String[] strings) throws SQLException { return createProxy(PreparedStatement.class, new GenericStatementHandler(delegate.prepareStatement(s, strings))); } /** * {@inheritDoc} */ public Struct createStruct(String typeName, Object[] attributes) throws SQLException { return delegate.createStruct(typeName, attributes); } /** * {@inheritDoc} */ public java.sql.Array createArrayOf(String typeName, Object[] elements) throws SQLException { return delegate.createArrayOf(typeName, elements); } /** * {@inheritDoc} */ public Properties getClientInfo() throws SQLException { return delegate.getClientInfo(); } /** * {@inheritDoc} */ public String getClientInfo(String name) throws SQLException { return delegate.getClientInfo(name); } /** * {@inheritDoc} */ public void setClientInfo(Properties props) throws SQLClientInfoException { delegate.setClientInfo(props); } /** * {@inheritDoc} */ public boolean isValid(int timeout) throws SQLException { return delegate.isValid(timeout); } /** * {@inheritDoc} */ public void setClientInfo(String name, String value) throws SQLClientInfoException { delegate.setClientInfo(name, value); } /** * {@inheritDoc} */ public Clob createClob() throws SQLException { return delegate.createClob(); } /** * {@inheritDoc} */ public Blob createBlob() throws SQLException { return delegate.createBlob(); } /** * {@inheritDoc} */ public NClob createNClob() throws SQLException { return delegate.createNClob(); } /** * {@inheritDoc} */ public SQLXML createSQLXML() throws SQLException { return delegate.createSQLXML(); } /** * {@inheritDoc} */ public boolean isWrapperFor(Class<?> iface) throws SQLException { return delegate.isWrapperFor(iface); } public <T> T unwrap(Class<T> iface) throws SQLException { return delegate.unwrap(iface); } /** * {@inheritDoc} */ public String nativeSQL(String s) throws SQLException { return delegate.nativeSQL(s); } /** * {@inheritDoc} */ public void setAutoCommit(boolean b) throws SQLException { delegate.setAutoCommit(b); } /** * {@inheritDoc} */ public boolean getAutoCommit() throws SQLException { return delegate.getAutoCommit(); } /** * {@inheritDoc} */ public void commit() throws SQLException { delegate.commit(); } /** * {@inheritDoc} */ public void rollback() throws SQLException { delegate.rollback(); } /** * {@inheritDoc} */ public void close() throws SQLException { delegate.close(); } /** * {@inheritDoc} */ public boolean isClosed() throws SQLException { return delegate.isClosed(); } /** * {@inheritDoc} */ public DatabaseMetaData getMetaData() throws SQLException { return delegate.getMetaData(); } /** * {@inheritDoc} */ public void setReadOnly(boolean b) throws SQLException { delegate.setReadOnly(b); } /** * {@inheritDoc} */ public boolean isReadOnly() throws SQLException { return delegate.isReadOnly(); } /** * {@inheritDoc} */ public void setCatalog(String s) throws SQLException { delegate.setCatalog(s); } /** * {@inheritDoc} */ public String getCatalog() throws SQLException { return delegate.getCatalog(); } /** * {@inheritDoc} */ public void setTransactionIsolation(int i) throws SQLException { delegate.setTransactionIsolation(i); } /** * {@inheritDoc} */ public int getTransactionIsolation() throws SQLException { return delegate.getTransactionIsolation(); } /** * {@inheritDoc} */ public SQLWarning getWarnings() throws SQLException { return delegate.getWarnings(); } /** * {@inheritDoc} */ public void clearWarnings() throws SQLException { delegate.clearWarnings(); } /** * {@inheritDoc} */ public Map<String, Class<?>> getTypeMap() throws SQLException { return delegate.getTypeMap(); } /** * {@inheritDoc} */ public void setTypeMap(Map<String, Class<?>> map) throws SQLException { delegate.setTypeMap(map); } /** * {@inheritDoc} */ public void setHoldability(int i) throws SQLException { delegate.setHoldability(i); } /** * {@inheritDoc} */ public int getHoldability() throws SQLException { return delegate.getHoldability(); } /** * {@inheritDoc} */ public Savepoint setSavepoint() throws SQLException { return delegate.setSavepoint(); } /** * {@inheritDoc} */ public Savepoint setSavepoint(String s) throws SQLException { return delegate.setSavepoint(s); } /** * {@inheritDoc} */ public void rollback(Savepoint savepoint) throws SQLException { delegate.rollback(savepoint); } /** * {@inheritDoc} */ public void releaseSavepoint(Savepoint savepoint) throws SQLException { delegate.releaseSavepoint(savepoint); } /** * @param clazz Statement class to proxy * @param handler Invocation handler * @return Dynamic proxy of a JDBC Statement */ <T extends Statement> T createProxy(Class<T> clazz, InvocationHandler handler) { return (T)Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] {clazz}, handler); } /** * For testing purposes only! * * @return The local reference to JDBCSpy */ JDBCSpy getJDBCSpy() { return jdbcSpy; } /** * A generic wrapper for JDBC Statements that will log query executions * and expose those statistics via the JDBCSpy MBean. */ private class GenericStatementHandler implements InvocationHandler { private final Statement delegate; /** * @param statement JDBC Statement to wrap and delegate to */ public GenericStatementHandler(Statement statement) { this.delegate = statement; } /** * {@inheritDoc} */ public Object invoke(Object o, Method method, Object[] objects) throws Throwable { long start = System.currentTimeMillis(); try { return method.invoke(delegate, objects); } finally { long end = System.currentTimeMillis(); if (method.getName().equals("executeQuery")) { if (objects == null) { jdbcSpy.logQuery(o.toString(), (end-start)); } else { jdbcSpy.logQuery(Arrays.asList(objects).toString(), (end-start)); } } } } } }I have modified to code to provide better logging for prepared statements. This code will print out the the prepared statement SQL and any arguments set on the prepared statement when executeQuery is called. This is much more useful for monitoring what hibernate is doing and how long it is taking.
I have modified the logging to the log file so that it now prints skeleton XML (without document elements). I expect this breaks the GUI tool however I haven't been able to build this in order to test.
This code is also Java 6 compatible - i.e. will build with Java 6 unlike trunk head.
The following is an SVN patch:
Index: modules/jdbcspy-core/src/test/java/org/jordens/jdbcspy/jmx/JDBCSpyTest.java =================================================================== --- modules/jdbcspy-core/src/test/java/org/jordens/jdbcspy/jmx/JDBCSpyTest.java (revision 35) +++ modules/jdbcspy-core/src/test/java/org/jordens/jdbcspy/jmx/JDBCSpyTest.java (working copy) @@ -109,13 +109,16 @@ String regex = "\\d+\\s-\\s.+\\s-\\s\\d+ms\\s-\\s"; try { - String line1 = reader.readLine(); - Assert.assertTrue(line1.matches(regex)); - Assert.assertTrue(line1.contains(query1)); - - String line2 = reader.readLine(); - Assert.assertTrue(line2.matches(regex)); - Assert.assertTrue(line2.contains(query2)); + StringBuilder line1 = new StringBuilder(); + String read = null; + + while ((read = reader.readLine()) != null) + { + line1.append(read); + } + + Assert.assertTrue(line1.toString().contains(query1)); + Assert.assertTrue(line1.toString().contains(query2)); } catch (IOException e) { @@ -166,9 +169,17 @@ String regex = "\\d+\\s-\\s.+"; try { - String line1 = reader.readLine(); - Assert.assertTrue(line1.matches(regex)); - Assert.assertTrue(line1.contains("[[ START ]]")); + StringBuilder line1 = new StringBuilder(); + String read = null; + + while ((read = reader.readLine()) != null) + { + line1.append(read); + } + + + //Assert.assertTrue(line1.matches(regex)); + Assert.assertTrue(line1.toString().contains("[[ START ]]")); } catch (IOException e) { Index: modules/jdbcspy-core/src/main/java/org/jordens/jdbcspy/jmx/JDBCSpy.java =================================================================== --- modules/jdbcspy-core/src/main/java/org/jordens/jdbcspy/jmx/JDBCSpy.java (revision 35) +++ modules/jdbcspy-core/src/main/java/org/jordens/jdbcspy/jmx/JDBCSpy.java (working copy) @@ -123,6 +123,23 @@ } } + private void logStats(String sql, long ms) + { + synchronized(executedQueries) + { + QueryStatistic statistic = executedQueries.get(sql); + if (statistic == null) + { + statistic = new QueryStatistic(sql, ms); + } + else + { + statistic.record(ms); + } + executedQueries.put(sql, statistic); + } + } + /** * Log the given query. * @@ -138,24 +155,46 @@ { return; } + + logStats(sql, ms); - synchronized(executedQueries) + writeLog("\t\t<sql><![CDATA[" + sql + "]]></sql>" + "\n\t\t<duration unit=\"ms\">" + ms + "</duration>"); + } + + public void log(String message) { + writeLog("\t\t<message>" + message + "</message>"); + } + + public void logQueryAndArguments(String sql, Map<Integer,Object> arguments, long ms) + { + if (!isEnabled) { - QueryStatistic statistic = executedQueries.get(sql); - if (statistic == null) - { - statistic = new QueryStatistic(sql, ms); - } - else - { - statistic.record(ms); - } - executedQueries.put(sql, statistic); + return; } - - writeLog(sql, ms + "ms"); + + logStats(sql, ms); + + StringBuilder builder = new StringBuilder(); + builder.append("\t\t<sql><![CDATA[\n\t\t"); + builder.append(sql); + builder.append("\n\t\t]]></sql>"); + + for (Integer key : arguments.keySet()) + { + builder.append("\n\t\t<arg index=\""); + builder.append(key); + builder.append("\">"); + builder.append(arguments.get(key)); + builder.append("</arg>"); + } + + builder.append("\n\t\t<duration unit=\"ms\">"); + builder.append(ms); + builder.append("</duration>"); + + writeLog(builder.toString()); } - + /** * {@inheritDoc} */ @@ -248,7 +287,7 @@ */ public boolean mark(String message) { - return writeLog(message); + return writeLog("\t\t<mark>" + message + "</mark>"); } /** @@ -263,13 +302,16 @@ if (logFileWriter != null) { StringBuilder builder = new StringBuilder(); + builder.append("<entry>\n\t<time>"); builder.append(System.currentTimeMillis()); - builder.append(" - "); + builder.append("</time>"); for (Object msg : msgs) { + builder.append("\n\t<message>\n"); builder.append(msg.toString()); - builder.append(" - "); + builder.append("\n\t</message>"); } + builder.append("\n</entry>"); logFileWriter.println(builder.toString()); logFileWriter.flush(); Index: modules/jdbcspy-core/src/main/java/org/jordens/jdbcspy/JDBCSpyConnection.java =================================================================== --- modules/jdbcspy-core/src/main/java/org/jordens/jdbcspy/JDBCSpyConnection.java (revision 35) +++ modules/jdbcspy-core/src/main/java/org/jordens/jdbcspy/JDBCSpyConnection.java (working copy) @@ -45,7 +45,7 @@ */ public PreparedStatement prepareStatement(String s) throws SQLException { - return createProxy(PreparedStatement.class, new GenericStatementHandler(delegate.prepareStatement(s))); + return createProxy(PreparedStatement.class, new PreparedStatementHandler(delegate.prepareStatement(s), s)); } /** @@ -69,7 +69,7 @@ */ public PreparedStatement prepareStatement(String s, int i, int i1) throws SQLException { - return createProxy(PreparedStatement.class, new GenericStatementHandler(delegate.prepareStatement(s, i, i1))); + return createProxy(PreparedStatement.class, new PreparedStatementHandler(delegate.prepareStatement(s, i, i1), s)); } /** @@ -93,7 +93,7 @@ */ public PreparedStatement prepareStatement(String s, int i, int i1, int i2) throws SQLException { - return createProxy(PreparedStatement.class, new GenericStatementHandler(delegate.prepareStatement(s, i, i1, i2))); + return createProxy(PreparedStatement.class, new PreparedStatementHandler(delegate.prepareStatement(s, i, i1, i2), s)); } /** @@ -109,7 +109,7 @@ */ public PreparedStatement prepareStatement(String s, int i) throws SQLException { - return createProxy(PreparedStatement.class, new GenericStatementHandler(delegate.prepareStatement(s, i))); + return createProxy(PreparedStatement.class, new PreparedStatementHandler(delegate.prepareStatement(s, i), s)); } /** @@ -117,7 +117,7 @@ */ public PreparedStatement prepareStatement(String s, int[] ints) throws SQLException { - return createProxy(PreparedStatement.class, new GenericStatementHandler(delegate.prepareStatement(s, ints))); + return createProxy(PreparedStatement.class, new PreparedStatementHandler(delegate.prepareStatement(s, ints), s)); } /** @@ -127,10 +127,111 @@ { return createProxy(PreparedStatement.class, new GenericStatementHandler(delegate.prepareStatement(s, strings))); } - + /** * {@inheritDoc} */ + public Struct createStruct(String typeName, Object[] attributes) throws SQLException + { + return delegate.createStruct(typeName, attributes); + } + + /** + * {@inheritDoc} + */ + public java.sql.Array createArrayOf(String typeName, Object[] elements) throws SQLException + { + return delegate.createArrayOf(typeName, elements); + } + + /** + * {@inheritDoc} + */ + public Properties getClientInfo() throws SQLException + { + return delegate.getClientInfo(); + } + + /** + * {@inheritDoc} + */ + public String getClientInfo(String name) throws SQLException + { + return delegate.getClientInfo(name); + } + + /** + * {@inheritDoc} + */ + public void setClientInfo(Properties props) throws SQLClientInfoException + { + delegate.setClientInfo(props); + } + + /** + * {@inheritDoc} + */ + public boolean isValid(int timeout) throws SQLException + { + return delegate.isValid(timeout); + } + + /** + * {@inheritDoc} + */ + public void setClientInfo(String name, String value) throws SQLClientInfoException + { + delegate.setClientInfo(name, value); + } + + /** + * {@inheritDoc} + */ + public Clob createClob() throws SQLException + { + return delegate.createClob(); + } + + /** + * {@inheritDoc} + */ + public Blob createBlob() throws SQLException + { + return delegate.createBlob(); + } + + /** + * {@inheritDoc} + */ + public NClob createNClob() throws SQLException + { + return delegate.createNClob(); + } + + /** + * {@inheritDoc} + */ + public SQLXML createSQLXML() throws SQLException + { + return delegate.createSQLXML(); + } + + /** + * {@inheritDoc} + */ + public boolean isWrapperFor(Class<?> iface) throws SQLException + { + return delegate.isWrapperFor(iface); + } + + public <T> T unwrap(Class<T> iface) throws SQLException + { + return delegate.unwrap(iface); + } + + /** + * {@inheritDoc} + */ public String nativeSQL(String s) throws SQLException { return delegate.nativeSQL(s); @@ -371,16 +472,58 @@ long end = System.currentTimeMillis(); if (method.getName().equals("executeQuery")) { - if (objects == null) + if (objects == null || objects.length == 0) { - jdbcSpy.logQuery(o.toString(), (end-start)); + jdbcSpy.log("Unexpected call to executeQuery() without arguments."); } else { - jdbcSpy.logQuery(Arrays.asList(objects).toString(), (end-start)); + jdbcSpy.logQuery((String) objects[0], (end-start)); } } } } } + + private class PreparedStatementHandler implements InvocationHandler + { + private final PreparedStatement delegate; + private final String sql; + + private final Map<Integer,Object> arguments = new HashMap<Integer,Object>(); + + /** + * @param statement JDBC Statement to wrap and delegate to + */ + public PreparedStatementHandler(PreparedStatement statement, String sql) + { + this.delegate = statement; + this.sql = sql; + } + + /** + * {@inheritDoc} + */ + public Object invoke(Object o, Method method, Object[] objects) throws Throwable + { + long start = System.currentTimeMillis(); + try + { + return method.invoke(delegate, objects); + } + finally + { + long end = System.currentTimeMillis(); + + if (method.getName().startsWith("set") && objects.length >= 2) + { + arguments.put((Integer) objects[0], objects[1]); + } + else if (method.getName().startsWith("execute")) + { + jdbcSpy.logQueryAndArguments(sql, arguments, (end-start)); + } + } + } + } }