├── .gitignore ├── .settings └── .gitignore ├── .travis.yml ├── 3rdparty_license.txt ├── LICENSE.txt ├── README.md ├── jdbc-perf-logger-agent ├── .gitignore ├── .settings │ ├── .gitignore │ ├── org.eclipse.jdt.core.prefs │ └── org.eclipse.jdt.ui.prefs ├── pom.xml └── src │ ├── main │ └── java │ │ └── ch │ │ └── sla │ │ └── jdbcperflogger │ │ └── agent │ │ ├── Agent.java │ │ ├── DriverInterceptor.java │ │ └── package-info.java │ └── test │ └── java │ └── ch │ └── sla │ └── jdbcperflogger │ └── agent │ └── AgentIT.java ├── jdbc-perf-logger-driver ├── .gitignore ├── .settings │ ├── .gitignore │ ├── org.eclipse.jdt.core.prefs │ └── org.eclipse.jdt.ui.prefs ├── pom.xml └── src │ ├── main │ ├── java │ │ └── ch │ │ │ └── sla │ │ │ └── jdbcperflogger │ │ │ ├── DatabaseType.java │ │ │ ├── DriverConfig.java │ │ │ ├── Logger.java │ │ │ ├── PerfLoggerConstants.java │ │ │ ├── StatementType.java │ │ │ ├── TxCompletionType.java │ │ │ ├── driver │ │ │ ├── LoggingConnectionInvocationHandler.java │ │ │ ├── LoggingPreparedStatementInvocationHandler.java │ │ │ ├── LoggingResultSetInvocationHandler.java │ │ │ ├── LoggingStatementInvocationHandler.java │ │ │ ├── Utils.java │ │ │ ├── WrappingDriver.java │ │ │ └── package-info.java │ │ │ ├── logger │ │ │ ├── LogSender.java │ │ │ ├── PerfLogger.java │ │ │ ├── PerfLoggerClientThread.java │ │ │ ├── PerfLoggerRemoting.java │ │ │ ├── PerfLoggerServerThread.java │ │ │ ├── RecordingLogSender.java │ │ │ ├── SocketLogSender.java │ │ │ └── package-info.java │ │ │ ├── model │ │ │ ├── AbstractBeforeStatementExecutionLog.java │ │ │ ├── BatchedNonPreparedStatementsLog.java │ │ │ ├── BatchedPreparedStatementsLog.java │ │ │ ├── BufferFullLogMessage.java │ │ │ ├── ConnectionInfo.java │ │ │ ├── LogMessage.java │ │ │ ├── PreparedStatementValuesHolder.java │ │ │ ├── ResultSetLog.java │ │ │ ├── SqlTypedValue.java │ │ │ ├── StatementExecutedLog.java │ │ │ ├── StatementLog.java │ │ │ ├── TxCompleteLog.java │ │ │ └── package-info.java │ │ │ └── package-info.java │ └── resources │ │ ├── .gitignore │ │ ├── META-INF │ │ └── services │ │ │ └── java.sql.Driver │ │ └── jdbcperflogger-fallback.xml │ └── test │ ├── java │ └── ch │ │ └── sla │ │ └── jdbcperflogger │ │ ├── DriverConfigTest.java │ │ ├── driver │ │ ├── UtilsTest.java │ │ ├── WrappingDriverJava8Test.java │ │ ├── WrappingDriverTest.java │ │ └── WrappingDriverUnloadTest.java │ │ └── logger │ │ ├── MyClassLoader.java │ │ ├── PerfLoggerClientThreadTest.java │ │ ├── PerfLoggerServerThreadTest.java │ │ └── PerfLoggerTest.java │ ├── resources │ ├── jdbcperflogger.xml │ ├── log4j.dtd │ ├── log4j.xml │ └── logging.properties │ └── resourcesNonClasspath │ └── test.txt ├── jdbc-perf-logger-gui ├── .gitignore ├── .settings │ ├── .gitignore │ ├── org.eclipse.jdt.core.prefs │ └── org.eclipse.jdt.ui.prefs ├── pom.xml └── src │ ├── main │ ├── appassembler │ │ └── unixBinTemplate │ ├── assembly │ │ └── distrib.xml │ ├── config │ │ ├── example-jdbcperflogger.xml │ │ └── log4j.xml │ ├── java │ │ └── ch │ │ │ └── sla │ │ │ └── jdbcperflogger │ │ │ └── console │ │ │ ├── db │ │ │ ├── DetailedViewStatementLog.java │ │ │ ├── LogRepositoryConstants.java │ │ │ ├── LogRepositoryRead.java │ │ │ ├── LogRepositoryReadJdbc.java │ │ │ ├── LogRepositoryUpdate.java │ │ │ ├── LogRepositoryUpdateJdbc.java │ │ │ ├── LogSearchCriteria.java │ │ │ ├── ResultSetAnalyzer.java │ │ │ ├── StatementFullyExecutedLog.java │ │ │ └── package-info.java │ │ │ ├── net │ │ │ ├── AbstractLogReceiver.java │ │ │ ├── ClientLogReceiver.java │ │ │ ├── LogPersister.java │ │ │ ├── ServerLogReceiver.java │ │ │ └── package-info.java │ │ │ └── ui │ │ │ ├── CustomTable.java │ │ │ ├── CustomTableCellRenderer.java │ │ │ ├── CustomTableRowSorter.java │ │ │ ├── GuiUtils.java │ │ │ ├── HostPort.java │ │ │ ├── IClientConnectionDelegate.java │ │ │ ├── LogExporter.java │ │ │ ├── PerfLoggerController.java │ │ │ ├── PerfLoggerGuiMain.java │ │ │ ├── PerfLoggerGuiMainFrame.java │ │ │ ├── PerfLoggerPanel.java │ │ │ ├── ResultSetDataModel.java │ │ │ ├── StatementTimestampTableCellRenderer.java │ │ │ ├── WelcomePanel.java │ │ │ └── package-info.java │ └── resources │ │ ├── .gitignore │ │ ├── icons │ │ ├── 32px-Edit-clear.png │ │ ├── 32px-Edit-copy_purple.png │ │ ├── 32px-Media-playback-pause.png │ │ ├── 32px-Media-record.png │ │ ├── forkme_right_green_007200.png │ │ ├── network-offline.png │ │ └── network-transmit-receive.png │ │ └── initdb.sql │ └── test │ ├── java │ └── ch │ │ └── sla │ │ └── jdbcperflogger │ │ └── console │ │ ├── db │ │ ├── AbstractLogRepositoryTest.java │ │ ├── LogRepositoryReadJdbcTest.java │ │ └── LogRepositoryUpdateJdbcTest.java │ │ └── net │ │ └── ServerLogReceiverTest.java │ └── resources │ ├── log4j.dtd │ └── log4j.xml ├── lombok.config └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .DS_Store 3 | .idea 4 | .project 5 | *.iml 6 | *.log 7 | pom.xml.* 8 | target 9 | /.java-version 10 | /logdb 11 | /release.properties 12 | /PerfLoggerGuiMain*.launch 13 | -------------------------------------------------------------------------------- /.settings/.gitignore: -------------------------------------------------------------------------------- 1 | !/org.eclipse.jdt.core.prefs 2 | !/org.eclipse.jdt.ui.prefs 3 | /* -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: xenial 2 | language: java 3 | jdk: 4 | - openjdk8 5 | - openjdk11 6 | script: mvn clean verify 7 | 8 | env: 9 | global: 10 | # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created 11 | # via the "travis encrypt" command using the project repo's public key 12 | - secure: "lsTdhAiNAYTfpi14mofp3W8HauFE5tj40TOuyGgNGfNNCuk0dktAuAnBVZyb/ylaRqedzMJ7LCBjFZFkycMdh0nQO9TgEav0WaFjflDIuUg6BhWi+sV/uNVUBUtUC5eXYEZbBlhJ+a2h4/oCSgPdRVrNdPQxunMZHegrFYzKWk4=" 13 | 14 | addons: 15 | coverity_scan: 16 | project: 17 | name: "sylvainlaurent/JDBC-Performance-Logger" 18 | description: "A JDBC driver wrapper and GUI to analyze statement performance" 19 | notification_email: slaurent@apache.org 20 | build_command_prepend: "mvn clean" 21 | build_command: "mvn -DskipTests=true compile" 22 | branch_pattern: master 23 | cache: 24 | directories: 25 | - $HOME/.m2 26 | -------------------------------------------------------------------------------- /3rdparty_license.txt: -------------------------------------------------------------------------------- 1 | This project uses the following third-party libraries/resources : 2 | 3 | - H2Database : dual licensed and available under a modified version of the MPL 1.1 (Mozilla Public License) or under the (unmodified) EPL 1.0 (Eclipse Public License). An original copy of the license agreement can be found at: http://www.h2database.com/html/license.html 4 | - JUnit : Eclipse Public License 1.0. https://github.com/junit-team/junit 5 | - Log4J : The Apache Software License, Version 2.0. http://logging.apache.org/log4j/1.2/ 6 | - Findbugs / JSR305 : The Apache Software License, Version 2.0. http://findbugs.sourceforge.net/ 7 | - Firesoft RSyntaxArea : LGPL 2.1. http://fifesoft.com/rsyntaxtextarea/ 8 | - SLF4J : QOS.ch license, identical to MIT. http://www.slf4j.org/ 9 | - Icons from the Tango Desktop Project : licenses under the Creative Commons License. http://en.wikipedia.org/wiki/Tango_Desktop_Project 10 | 11 | Among those, the binary distribution redistributes the following : 12 | - H2Database 13 | - Log4J 14 | - Findbugs / JSR305 15 | - Firesoft RSyntaxArea 16 | - SLF4J 17 | - Icons from the Tango Desktop Project 18 | - Byte Buddy 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/sylvainlaurent/JDBC-Performance-Logger.svg?branch=master)](https://travis-ci.org/sylvainlaurent/JDBC-Performance-Logger) 2 | 3 | # JDBC Performance Logger 4 | 5 | ## Purpose 6 | Measuring performance of SQL statements executed through JDBC. 7 | 8 | (click on the image below for an overview of the features) 9 | [![Click here for an overview of the features](http://s159433608.onlinehome.fr/overview.png)](https://www.thinglink.com/scene/512018881544454146) 10 | 11 | 12 | ## Why yet another project? 13 | Although other tools already exist around JDBC performance monitoring ([log4jdbc](http://code.google.com/p/log4jdbc/), [P6Spy](http://sourceforge.net/projects/p6spy/), [JDbMonitor](http://www.jdbmonitor.com/)...), I did not find the features I was looking for : a GUI, measurement of statement execution and ResultSet iteration, cumulative measures, commit duration... 14 | 15 | ## Features 16 | - Graphical console (Swing-based) with analysis feature 17 | - filter/highlight based on statement text, minimum execution time 18 | - advanced filtering using custom SQL WHERE clause against the embedded H2 DB 19 | - group statements to count executions of identical statements and measure cumulated time 20 | - support for multiple connections 21 | - the connection between the monitored java application (JDBC proxy driver) and the console can be initiated from either side 22 | - Logging of bound values of prepared statements, including the name of the set* method called to bind the value (very helpful to distinguish setDate and setTimestamp to understand [why Oracle does not use an index](http://docs.oracle.com/cd/E16655_01/java.121/e17657/apxref.htm#JJDBC28919) ) 23 | - Separate measure of statement execution time, results fetch time and result set usage time (includes result processing like creation of java object) 24 | - Measures connection creation and commit/rollback durations 25 | - Handling of batched statements 26 | - Logging of SQLExceptions 27 | - Displays the `queryTimeout` of each statement (no value means 0 or no timeout) (since 0.5.0) 28 | - Displays the `autoCommit` status of each statement (since 0.6.0) 29 | - Supports new java 8 methods like `executeLargeUpdate` (since 0.6.2) 30 | - Java agent (since 0.8.0) 31 | 32 | ## Requirements 33 | - java 8 (since 0.9) 34 | 35 | ## How to download 36 | - The package containing both the console and driver is available here : https://github.com/sylvainlaurent/JDBC-Performance-Logger/releases 37 | - The driver is also available on Maven Central : 38 | 39 | ```xml 40 | 41 | com.github.sylvainlaurent.jdbcperflogger 42 | jdbc-perf-logger-agent 43 | ... 44 | 45 | ``` 46 | or 47 | 48 | ```xml 49 | 50 | com.github.sylvainlaurent.jdbcperflogger 51 | jdbc-perf-logger-driver 52 | ... 53 | 54 | ``` 55 | 56 | ## How to setup the JDBC Driver 57 | The driver can be setup in different ways: 58 | 59 | ### As a java agent (recommended/most simple way) 60 | Just launch the JVM with `-javaagent:path/to/jdbc-perf-logger-agent-x.y.z.jar` 61 | 62 | The agent jar file is available in the `lib` directory of the zip or tar.gz distribution or as a maven artifact as shown above. 63 | 64 | ### Manual configuration 65 | - If using maven, add the `` snippet above (replacing the version with the latest one) to your `pom.xml` 66 | - If NOT using maven, add `jdbc-perf-logger-driver` jar file to the classpath of the JDBC-client application (the file can be found in the `lib` directory of the binary distribution) 67 | - Change the driver class name to `ch.sla.jdbcperflogger.driver.WrappingDriver` 68 | - Prefix your current JDBC URL with `jdbcperflogger:`, example: `jdbcperflogger:jdbc:h2:mem:` or `jdbcperflogger:jdbc:oracle:thin:@myhost:1521:orcl` 69 | 70 | ### Advanced configuration 71 | - (optional) add a `jdbcperflogger.xml` file to the classpath (see the [example file](/jdbc-perf-logger-gui/src/main/config/example-jdbcperflogger.xml/) for indications). If both the driver and console are used on the same machine, there's nothing to do: the driver will try to connect to the console on localhost:4561. 72 | - (optional) the location of the config file can be overriden with the System property `jdbcperflogger.config.location`. Example : `java -Djdbcperflogger.config.location=/Users/me/myjdbcperflogger.xml ....` 73 | 74 | ## How to use the graphical console 75 | - launch `bin/jdbc-performance-logger-gui.sh` (unix/MacOS) or `bin\jdbc-performance-logger-gui.bat` (requires java 8 JRE) 76 | - by default the console waits for connections from jdbc-logger-drivers on port 4561. All statements will be logged to the same tab 77 | - The console can also connect to a jdbc-perf-logger-driver instance on a specific host and port. A tab is created for each host/port combination. 78 | - Once a tab is opened, the status of the connection is indicated at the bottom of the panel. If the connection is broken and was initiated by the console, the console will try to reconnect regularly. If the connection was initiated by the driver, the latter will try to reconnect regularly. 79 | - by default the console only keeps the last 20'000 statements. The number can be changed by adding the System property `maxLoggedStatements` when launching the console. 80 | 81 | ## Tested databases 82 | - H2 (lightly, used for our own unit tests) 83 | - Oracle 10.2/11.2/12.1 84 | - MySQL 5.1 85 | 86 | ## Current limitations 87 | - No DataSource nor XADataSource class provided 88 | - Only the first ResultSet of queries is logged (`Statement.getMoreResults()` works but no effort has been put to log the fetching of those ResultSets) 89 | 90 | ## Potential ClassLoader issues 91 | **There should not be any problem when using the "java agent way".** 92 | 93 | For the "driver way", here are the rules to observe : 94 | - The jdbc-perf-logger-driver must be able to use your bare JDBC driver 95 | - The DataSource you use must be able to use the jdbc-perf-logger-driver classes 96 | 97 | Here are the most common cases with Tomcat : 98 | - Tomcat _shared.loader_ includes 99 | - the DataSource implementation (which is by default for tomcat 7 DataSource) 100 | - the bare JDBC driver 101 | - the jdbc-perf-logger driver 102 | This is the case if you place your JDBC driver and jdbc-perf-logger driver in the `lib` directory of `CATALINA_BASE` or `CATALINA_HOME`. 103 | This can also be done by configuring the `catalina.properties` file. 104 | - your WAR file embeds the jars for 105 | - the DataSource implementation 106 | - the bare JDBC driver 107 | - the jdbc-perf-logger driver 108 | - the JVM classpath includes the bare JDBC driver and the jdbc-perf-logger driver and the Tomcat _shared.loader_ includes the DataSource implementation. 109 | 110 | ## Source code 111 | The source code is available on GitHub : https://github.com/sylvainlaurent/JDBC-Performance-Logger 112 | 113 | ### How to build source 114 | Use Maven and a JDK >=8, and run `mvn clean verify` in the root directory of the git repository. The binary distribution is then available in `jdbc-perf-logger-gui`. 115 | 116 | ### How to create a release 117 | `mvn release:prepare release:perform` and answer the questions about version number. 118 | 119 | Then push the commits and tags to github. 120 | 121 | ## License 122 | This software is licensed under the Apache Sotware License version 2.0, see [LICENSE.txt](LICENSE.txt). 123 | 124 | This software uses and redistributes third-party software, see [3rdparty_license.txt](3rdparty_license.txt). 125 | -------------------------------------------------------------------------------- /jdbc-perf-logger-agent/.gitignore: -------------------------------------------------------------------------------- 1 | /dependency-reduced-pom.xml 2 | -------------------------------------------------------------------------------- /jdbc-perf-logger-agent/.settings/.gitignore: -------------------------------------------------------------------------------- 1 | !/org.eclipse.jdt.core.prefs 2 | !/org.eclipse.jdt.ui.prefs 3 | /* -------------------------------------------------------------------------------- /jdbc-perf-logger-agent/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | com.github.sylvainlaurent.jdbcperflogger 5 | jdbc-perf-logger 6 | 0.9.1-SNAPSHOT 7 | 8 | jdbc-perf-logger-agent 9 | 10 | 11 | 12 | com.github.sylvainlaurent.jdbcperflogger 13 | jdbc-perf-logger-driver 14 | 15 | 16 | org.eclipse.jdt 17 | org.eclipse.jdt.annotation 18 | 19 | provided 20 | 21 | 22 | net.bytebuddy 23 | byte-buddy 24 | 1.10.9 25 | 26 | 27 | junit 28 | junit 29 | test 30 | 31 | 32 | org.mockito 33 | mockito-core 34 | test 35 | 36 | 37 | com.h2database 38 | h2 39 | test 40 | 41 | 42 | org.apache.derby 43 | derby 44 | 10.13.1.1 45 | 46 | 47 | 48 | 49 | 50 | 51 | org.apache.maven.plugins 52 | maven-jar-plugin 53 | 54 | 55 | true 56 | 57 | true 58 | 59 | 60 | ch.sla.jdbcperflogger.agent.Agent 61 | ch.sla.jdbcperflogger.agent.Agent 62 | true 63 | 64 | 65 | 66 | 67 | 68 | org.apache.maven.plugins 69 | maven-shade-plugin 70 | 3.2.3 71 | 72 | 73 | 74 | com.github.sylvainlaurent.jdbcperflogger:jdbc-perf-logger-driver 75 | net.bytebuddy:byte-buddy 76 | 77 | 78 | 79 | 80 | net.bytebuddy 81 | ch.sla.jdbcperflogger.agent.net.bytebuddy 82 | 83 | 84 | 85 | 86 | 87 | package 88 | 89 | shade 90 | 91 | 92 | 93 | 94 | 95 | maven-dependency-plugin 96 | 3.1.2 97 | 98 | 99 | 100 | properties 101 | 102 | 103 | 104 | 105 | 106 | org.apache.maven.plugins 107 | maven-failsafe-plugin 108 | 109 | -javaagent:${project.build.directory}/${project.build.finalName}.jar 110 | -Dwrapped.driver.url="${org.apache.derby:derby:jar}" 111 | 112 | 113 | com.github.sylvainlaurent.jdbcperflogger:jdbc-perf-logger-driver 114 | net.bytebuddy:byte-buddy 115 | org.apache.derby:derby 116 | 117 | 118 | 119 | 120 | 121 | 122 | 124 | 125 | org.eclipse.m2e 126 | lifecycle-mapping 127 | 1.0.0 128 | 129 | 130 | 131 | 132 | 133 | org.apache.maven.plugins 134 | maven-dependency-plugin 135 | [3.0.2,) 136 | 137 | properties 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /jdbc-perf-logger-agent/src/main/java/ch/sla/jdbcperflogger/agent/Agent.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.agent; 2 | 3 | import static net.bytebuddy.matcher.ElementMatchers.named; 4 | 5 | import java.io.IOException; 6 | import java.lang.instrument.Instrumentation; 7 | import java.sql.Driver; 8 | 9 | import org.eclipse.jdt.annotation.Nullable; 10 | 11 | import ch.sla.jdbcperflogger.driver.WrappingDriver; 12 | import net.bytebuddy.agent.builder.AgentBuilder; 13 | import net.bytebuddy.description.type.TypeDescription; 14 | import net.bytebuddy.dynamic.DynamicType.Builder; 15 | import net.bytebuddy.implementation.MethodDelegation; 16 | import net.bytebuddy.matcher.ElementMatchers; 17 | import net.bytebuddy.utility.JavaModule; 18 | 19 | public class Agent { 20 | protected static final String PREFIX = "[jdbc-perf-logger-agent]"; 21 | private static boolean loaded = false; 22 | 23 | public static void premain(final String agentArgs, final Instrumentation inst) throws IOException { 24 | installAgent(agentArgs, inst); 25 | } 26 | 27 | public static void agentmain(final String agentArgs, final Instrumentation inst) { 28 | installAgent(agentArgs, inst); 29 | } 30 | 31 | private static void installAgent(final String agentArgs, final Instrumentation inst) { 32 | System.out.print(PREFIX + " Loading..."); 33 | // final ByteBuddy byteBuddy = new ByteBuddy().with(Implementation.Context.Disabled.Factory.INSTANCE); 34 | 35 | new AgentBuilder.Default()// 36 | .with(new AgentBuilder.Listener.Adapter() { 37 | @Override 38 | public void onError(final String typeName, final @Nullable ClassLoader classLoader, 39 | final @Nullable JavaModule module, 40 | final boolean loaded1, final Throwable throwable) { 41 | System.err.println(PREFIX + " ERROR " + typeName); 42 | throwable.printStackTrace(System.err); 43 | } 44 | 45 | })// 46 | // .with(byteBuddy)// 47 | // .with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)// 48 | // .with(AgentBuilder.RedefinitionStrategy.REDEFINITION)// 49 | // .with(AgentBuilder.TypeStrategy.Default.REBASE)// 50 | .type(ElementMatchers.isSubTypeOf(Driver.class).and(ElementMatchers.noneOf(WrappingDriver.class)))// 51 | .transform(new AgentBuilder.Transformer() { 52 | 53 | @Override 54 | public Builder transform(final Builder builder, final TypeDescription typeDescription, 55 | final @Nullable ClassLoader classLoader, final @Nullable JavaModule module) { 56 | System.out.println(PREFIX + " Transforming " + typeDescription + " for interception"); 57 | return builder// 58 | .method(named("connect"))// 59 | .intercept(MethodDelegation.withDefaultConfiguration() 60 | .filter(ElementMatchers.isMethod().and(named("connect"))) 61 | .to(new DriverInterceptor()))// 62 | ; 63 | } 64 | })// 65 | .installOn(inst); 66 | 67 | // TODO: intercept javax.sql.DataSource, javax.sql.PooledConnection.getConnection()... 68 | 69 | loaded = true; 70 | System.out.println("OK"); 71 | } 72 | 73 | public static boolean isLoaded() { 74 | return loaded; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /jdbc-perf-logger-agent/src/main/java/ch/sla/jdbcperflogger/agent/DriverInterceptor.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.agent; 2 | 3 | import java.sql.Connection; 4 | import java.util.concurrent.Callable; 5 | 6 | import org.eclipse.jdt.annotation.Nullable; 7 | 8 | import ch.sla.jdbcperflogger.driver.WrappingDriver; 9 | import net.bytebuddy.implementation.bind.annotation.SuperCall; 10 | 11 | public class DriverInterceptor { 12 | public @Nullable Connection connect(final String url, final java.util.Properties info, 13 | @SuperCall final Callable<@Nullable Connection> originalConnectionCreator) throws Exception { 14 | 15 | return WrappingDriver.INSTANCE.wrapConnection(url, info, originalConnectionCreator); 16 | } 17 | } -------------------------------------------------------------------------------- /jdbc-perf-logger-agent/src/main/java/ch/sla/jdbcperflogger/agent/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @NonNullByDefault 17 | package ch.sla.jdbcperflogger.agent; 18 | 19 | import org.eclipse.jdt.annotation.NonNullByDefault; 20 | 21 | -------------------------------------------------------------------------------- /jdbc-perf-logger-agent/src/test/java/ch/sla/jdbcperflogger/agent/AgentIT.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.agent; 2 | 3 | import static java.util.Objects.requireNonNull; 4 | import static org.junit.Assert.assertEquals; 5 | import static org.junit.Assert.assertTrue; 6 | 7 | import java.io.File; 8 | import java.lang.reflect.Proxy; 9 | import java.net.URL; 10 | import java.net.URLClassLoader; 11 | import java.sql.Connection; 12 | import java.sql.Driver; 13 | import java.sql.DriverManager; 14 | import java.sql.SQLException; 15 | import java.util.Properties; 16 | 17 | import org.junit.Before; 18 | import org.junit.Test; 19 | 20 | import ch.sla.jdbcperflogger.driver.LoggingConnectionInvocationHandler; 21 | import ch.sla.jdbcperflogger.driver.WrappingDriver; 22 | 23 | public class AgentIT { 24 | 25 | private static final String WRAPPED_DRIVER = "org.apache.derby.jdbc.EmbeddedDriver"; 26 | 27 | @Before 28 | public void setup() { 29 | // assertTrue(Agent.isLoaded()); 30 | } 31 | 32 | @Test 33 | public void returns_a_wrapped_connection_when_using_normal_url() throws SQLException { 34 | final Connection connection = DriverManager.getConnection("jdbc:h2:mem:", "sa", ""); 35 | assertTrue("is proxy", Proxy.isProxyClass(connection.getClass())); 36 | assertEquals(LoggingConnectionInvocationHandler.class, Proxy.getInvocationHandler(connection).getClass()); 37 | } 38 | 39 | @Test 40 | public void returns_a_wrapped_connection_when_using_jdbcperf_url() throws SQLException { 41 | final Connection connection = DriverManager.getConnection(WrappingDriver.URL_PREFIX + "jdbc:h2:mem:", "sa", ""); 42 | assertTrue("is proxy", Proxy.isProxyClass(connection.getClass())); 43 | assertEquals(LoggingConnectionInvocationHandler.class, Proxy.getInvocationHandler(connection).getClass()); 44 | } 45 | 46 | @Test(expected = ClassNotFoundException.class) 47 | public void wrapped_driver_is_not_in_classpath() throws ClassNotFoundException { 48 | Class.forName(WRAPPED_DRIVER); 49 | } 50 | 51 | @Test() 52 | public void load_wrapped_from_sub_classloader() throws Exception { 53 | final File wrappedDriverJarFile = new File(System.getProperty("wrapped.driver.url")); 54 | final URLClassLoader childClassLoader = new URLClassLoader(new URL[] { wrappedDriverJarFile.toURI().toURL() }); 55 | // register the driver in the DriverManager 56 | @SuppressWarnings("unchecked") 57 | final Class driverClass = (Class) Class.forName(WRAPPED_DRIVER, true, 58 | childClassLoader); 59 | final Driver driver = driverClass.newInstance(); 60 | 61 | final Connection connection = requireNonNull( 62 | driver.connect("jdbc:derby:memory:myDB;create=true", new Properties())); 63 | assertTrue("is proxy", Proxy.isProxyClass(connection.getClass())); 64 | assertEquals(LoggingConnectionInvocationHandler.class, Proxy.getInvocationHandler(connection).getClass()); 65 | 66 | boolean foundInterfaceLoadedBySubClassLoader = false; 67 | for (final Class itf : connection.getClass().getInterfaces()) { 68 | if (itf.getClassLoader() == childClassLoader) { 69 | foundInterfaceLoadedBySubClassLoader = true; 70 | } 71 | } 72 | assertTrue("Should have found an interface loaded by the subclassloader", foundInterfaceLoadedBySubClassLoader); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/.gitignore: -------------------------------------------------------------------------------- 1 | /dependency-reduced-pom.xml 2 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/.settings/.gitignore: -------------------------------------------------------------------------------- 1 | !/org.eclipse.jdt.core.prefs 2 | !/org.eclipse.jdt.ui.prefs 3 | /* -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | com.github.sylvainlaurent.jdbcperflogger 5 | jdbc-perf-logger 6 | 0.9.1-SNAPSHOT 7 | 8 | 9 | jdbc-perf-logger-driver 10 | jar 11 | 12 | JDBC Performance Logger Driver 13 | 14 | 15 | 16 | com.h2database 17 | h2 18 | test 19 | 20 | 26 | 27 | org.eclipse.jdt 28 | org.eclipse.jdt.annotation 29 | 30 | true 31 | 32 | 33 | org.hsqldb 34 | hsqldb 35 | 2.5.0 36 | test 37 | 38 | 39 | junit 40 | junit 41 | test 42 | 43 | 44 | org.mockito 45 | mockito-core 46 | test 47 | 48 | 49 | org.slf4j 50 | jul-to-slf4j 51 | test 52 | 53 | 54 | org.slf4j 55 | slf4j-log4j12 56 | test 57 | 58 | 59 | org.apache.derby 60 | derby 61 | 10.10.1.1 62 | test 63 | 64 | 65 | org.awaitility 66 | awaitility 67 | test 68 | 69 | 70 | 71 | 72 | 73 | org.codehaus.mojo 74 | animal-sniffer-maven-plugin 75 | 76 | 77 | org.codehaus.mojo.signature 78 | java18 79 | 1.0 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/DatabaseType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger; 17 | 18 | 19 | public enum DatabaseType { 20 | ORACLE, GENERIC 21 | } 22 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/DriverConfig.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger; 2 | 3 | import java.io.FileInputStream; 4 | import java.io.FileNotFoundException; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.net.InetSocketAddress; 8 | import java.util.ArrayList; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.Map.Entry; 13 | 14 | import javax.xml.parsers.DocumentBuilder; 15 | import javax.xml.parsers.DocumentBuilderFactory; 16 | import javax.xml.parsers.ParserConfigurationException; 17 | 18 | import org.eclipse.jdt.annotation.NonNull; 19 | import org.eclipse.jdt.annotation.Nullable; 20 | import org.w3c.dom.Document; 21 | import org.w3c.dom.Element; 22 | import org.w3c.dom.NamedNodeMap; 23 | import org.w3c.dom.NodeList; 24 | import org.xml.sax.SAXException; 25 | 26 | public class DriverConfig { 27 | private final static Logger LOGGER = Logger.getLogger(DriverConfig.class); 28 | 29 | public final static DriverConfig INSTANCE; 30 | 31 | @Nullable 32 | private Integer serverPort; 33 | private final List clientAddresses = new ArrayList(); 34 | private final Map driverPrefixToClassName = new HashMap(); 35 | 36 | static { 37 | 38 | final InputStream configFileStream = openConfigFile(); 39 | INSTANCE = parseConfig(configFileStream); 40 | } 41 | 42 | static DriverConfig parseConfig(final InputStream configFileStream) { 43 | try { 44 | final DriverConfig config = new DriverConfig(); 45 | final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 46 | final DocumentBuilder docBuilder = dbf.newDocumentBuilder(); 47 | final Document doc = docBuilder.parse(configFileStream); 48 | final Element root = (Element) doc.getElementsByTagName("jdbc-perf-logger").item(0); 49 | 50 | { 51 | final NodeList localServersList = root.getElementsByTagName("local-server"); 52 | for (int i = 0; i < localServersList.getLength(); i++) { 53 | final String port = localServersList.item(i).getAttributes().getNamedItem("port").getTextContent(); 54 | config.serverPort = Integer.parseInt(port); 55 | } 56 | } 57 | 58 | { 59 | final NodeList targetClientList = root.getElementsByTagName("target-console"); 60 | for (int i = 0; i < targetClientList.getLength(); i++) { 61 | final NamedNodeMap attributes = targetClientList.item(i).getAttributes(); 62 | @NonNull 63 | final String host = attributes.getNamedItem("host").getTextContent(); 64 | @NonNull 65 | final String port = attributes.getNamedItem("port").getTextContent(); 66 | config.clientAddresses.add(InetSocketAddress.createUnresolved(host, Integer.parseInt(port))); 67 | } 68 | } 69 | 70 | final NodeList jdbcDriversRootNodesList = doc.getElementsByTagName("jdbc-drivers"); 71 | if (jdbcDriversRootNodesList.getLength() > 0) { 72 | final NodeList jdbcDriversNodeList = ((Element) jdbcDriversRootNodesList.item(0)) 73 | .getElementsByTagName("jdbc-driver"); 74 | for (int i = 0; i < jdbcDriversNodeList.getLength(); i++) { 75 | final Element jdbcDriverNode = ((Element) jdbcDriversNodeList.item(i)); 76 | final NodeList prefixNode = jdbcDriverNode.getElementsByTagName("prefix"); 77 | final NodeList classNameNode = jdbcDriverNode.getElementsByTagName("driver-class-name"); 78 | if (prefixNode.getLength() > 0 && classNameNode.getLength() > 0) { 79 | final String prefix = prefixNode.item(0).getTextContent(); 80 | final String className = classNameNode.item(0).getTextContent(); 81 | if (prefix != null && className != null) { 82 | config.driverPrefixToClassName.put(prefix.trim(), className.trim()); 83 | } 84 | } 85 | } 86 | } 87 | 88 | return config; 89 | } catch (final ParserConfigurationException e) { 90 | throw new RuntimeException(e); 91 | } catch (final IOException e) { 92 | throw new RuntimeException(e); 93 | } catch (final SAXException e) { 94 | throw new RuntimeException(e); 95 | } 96 | } 97 | 98 | static InputStream openConfigFile() { 99 | String location = System.getProperty(PerfLoggerConstants.CONFIG_FILE_LOCATION_PROP_KEY); 100 | if (location == null) { 101 | LOGGER.debug("No System property " + PerfLoggerConstants.CONFIG_FILE_LOCATION_PROP_KEY 102 | + " defined, looking for config at " + PerfLoggerConstants.CONFIG_FILE_DEFAULT_LOCATION); 103 | location = PerfLoggerConstants.CONFIG_FILE_DEFAULT_LOCATION; 104 | } 105 | 106 | InputStream configFileStream = openConfigFile(location); 107 | if (configFileStream == null) { 108 | location = PerfLoggerConstants.CONFIG_FILE_FALLBACK_LOCATION; 109 | configFileStream = openConfigFile(location); 110 | if (configFileStream == null) { 111 | throw new RuntimeException( 112 | "Unexpected: cannot find " + PerfLoggerConstants.CONFIG_FILE_FALLBACK_LOCATION); 113 | } 114 | } 115 | LOGGER.info("Using config file " + location); 116 | 117 | return configFileStream; 118 | } 119 | 120 | @Nullable 121 | static InputStream openConfigFile(final String location) { 122 | InputStream configFileStream = PerfLoggerConstants.class.getResourceAsStream("/" + location); 123 | if (configFileStream == null) { 124 | LOGGER.debug("Cannot find config file " + location + " in the classpath, trying on filesystem"); 125 | 126 | try { 127 | configFileStream = new FileInputStream(location); 128 | } catch (final FileNotFoundException e) { 129 | LOGGER.debug("Cannot find config file " + location + " on the filesystem"); 130 | // not found, just return null 131 | } 132 | } 133 | return configFileStream; 134 | 135 | } 136 | 137 | @Nullable 138 | public Integer getServerPort() { 139 | return serverPort; 140 | } 141 | 142 | public List getClientAddresses() { 143 | return clientAddresses; 144 | } 145 | 146 | @Nullable 147 | public String getClassNameForJdbcUrl(final String jdbcUrl) { 148 | for (final Entry driver : driverPrefixToClassName.entrySet()) { 149 | if (jdbcUrl.startsWith(driver.getKey())) { 150 | return driver.getValue(); 151 | } 152 | } 153 | return null; 154 | } 155 | 156 | } 157 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/Logger.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger; 2 | 3 | import java.util.logging.Level; 4 | 5 | import org.eclipse.jdt.annotation.Nullable; 6 | 7 | /** 8 | * Abstraction for logging. 9 | * 10 | * @author slaurent 11 | */ 12 | public class Logger { 13 | private final java.util.logging.Logger julLogger; 14 | 15 | private Logger(final java.util.logging.Logger julLogger) { 16 | this.julLogger = julLogger; 17 | } 18 | 19 | public static Logger getLogger(final Class clazz) { 20 | return new Logger(java.util.logging.Logger.getLogger(clazz.getName())); 21 | } 22 | 23 | public static Logger getLogger(final String loggerName) { 24 | return new Logger(java.util.logging.Logger.getLogger(loggerName)); 25 | } 26 | 27 | public void debug(final String msg) { 28 | julLogger.log(Level.FINE, msg); 29 | } 30 | 31 | public void debug(final String msg, final @Nullable Throwable exc) { 32 | julLogger.log(Level.FINE, msg, exc); 33 | 34 | } 35 | 36 | public void info(final String msg) { 37 | julLogger.log(Level.INFO, msg); 38 | 39 | } 40 | 41 | public void info(final String msg, final Throwable e) { 42 | julLogger.log(Level.INFO, msg, e); 43 | } 44 | 45 | public void warn(final String msg) { 46 | julLogger.log(Level.WARNING, msg); 47 | } 48 | 49 | public void warn(final String msg, final @Nullable Throwable e) { 50 | julLogger.log(Level.WARNING, msg, e); 51 | } 52 | 53 | public void error(final String msg) { 54 | julLogger.log(Level.SEVERE, msg); 55 | } 56 | 57 | public void error(final String msg, final Throwable e) { 58 | julLogger.log(Level.SEVERE, msg, e); 59 | } 60 | 61 | public boolean isDebugEnabled() { 62 | return julLogger.isLoggable(Level.FINE); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/PerfLoggerConstants.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger; 2 | 3 | public class PerfLoggerConstants { 4 | public final static String CONFIG_FILE_DEFAULT_LOCATION = "jdbcperflogger.xml"; 5 | public final static String CONFIG_FILE_FALLBACK_LOCATION = "jdbcperflogger-fallback.xml"; 6 | public final static String CONFIG_FILE_LOCATION_PROP_KEY = "jdbcperflogger.config.location"; 7 | 8 | private PerfLoggerConstants() { 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/StatementType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ch.sla.jdbcperflogger; 18 | 19 | import java.util.EnumSet; 20 | 21 | public enum StatementType { 22 | BASE_NON_PREPARED_STMT(1), // 23 | BASE_PREPARED_STMT(2), // 24 | NON_PREPARED_QUERY_STMT(3), // 25 | PREPARED_QUERY_STMT(4), // 26 | PREPARED_BATCH_EXECUTION(5), // 27 | NON_PREPARED_BATCH_EXECUTION(6), // 28 | TRANSACTION(7); 29 | 30 | private static StatementType[] vals; 31 | 32 | private final int id; 33 | 34 | StatementType(final int id) { 35 | this.id = id; 36 | } 37 | 38 | static { 39 | vals = new StatementType[8]; 40 | for (final StatementType type : EnumSet.allOf(StatementType.class)) { 41 | vals[type.id] = type; 42 | } 43 | } 44 | 45 | public int getId() { 46 | return id; 47 | } 48 | 49 | public static StatementType fromId(final int id) { 50 | final StatementType statementType = vals[id]; 51 | if (statementType == null) { 52 | throw new IllegalArgumentException("unhandled ID for StatementType: " + id); 53 | } 54 | return statementType; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/TxCompletionType.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger; 2 | 3 | public enum TxCompletionType { 4 | COMMIT, ROLLBACK, SET_SAVE_POINT, ROLLBACK_TO_SAVEPOINT 5 | } -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/driver/LoggingConnectionInvocationHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.driver; 17 | 18 | import java.lang.reflect.InvocationHandler; 19 | import java.lang.reflect.Method; 20 | import java.lang.reflect.Proxy; 21 | import java.sql.Connection; 22 | import java.sql.PreparedStatement; 23 | import java.sql.Savepoint; 24 | import java.sql.Statement; 25 | import java.util.Properties; 26 | import java.util.UUID; 27 | 28 | import org.eclipse.jdt.annotation.Nullable; 29 | 30 | import ch.sla.jdbcperflogger.DatabaseType; 31 | import ch.sla.jdbcperflogger.TxCompletionType; 32 | import ch.sla.jdbcperflogger.logger.PerfLogger; 33 | 34 | public class LoggingConnectionInvocationHandler implements InvocationHandler { 35 | private final UUID connectionUuid; 36 | private final int connectionId; 37 | private final Connection wrappedConnection; 38 | private final DatabaseType databaseType; 39 | private final String url; 40 | private final Properties connectionProperties; 41 | 42 | LoggingConnectionInvocationHandler(final int connectionId, final Connection wrappedConnection, final String url, 43 | final Properties connectionProperties) { 44 | connectionUuid = UUID.randomUUID(); 45 | this.connectionId = connectionId; 46 | this.wrappedConnection = wrappedConnection; 47 | databaseType = Utils.getDatabaseType(wrappedConnection); 48 | this.url = url; 49 | this.connectionProperties = connectionProperties; 50 | } 51 | 52 | @Override 53 | @Nullable 54 | public Object invoke(@Nullable final Object proxy, final Method method, final Object @Nullable [] args) 55 | throws Throwable { 56 | 57 | final String methodName = method.getName(); 58 | 59 | TxCompletionType txCompletionType = null; 60 | String savePointDescription = null; 61 | if ("commit".equals(methodName)) { 62 | txCompletionType = TxCompletionType.COMMIT; 63 | } else if ("rollback".equals(methodName)) { 64 | if (args == null) { 65 | txCompletionType = TxCompletionType.ROLLBACK; 66 | } else { 67 | txCompletionType = TxCompletionType.ROLLBACK_TO_SAVEPOINT; 68 | final Savepoint savepoint = (Savepoint) args[0]; 69 | savePointDescription = savepoint.toString(); 70 | } 71 | } else if ("setSavepoint".equals(methodName)) { 72 | txCompletionType = TxCompletionType.SET_SAVE_POINT; 73 | } 74 | final long startTimeStamp = System.currentTimeMillis(); 75 | long startNanos = -1; 76 | if (txCompletionType != null) { 77 | startNanos = System.nanoTime(); 78 | } 79 | 80 | final Object result = Utils.invokeUnwrapException(wrappedConnection, method, args); 81 | if (result != null) { 82 | if ("createStatement".equals(methodName)) { 83 | return Proxy.newProxyInstance(result.getClass().getClassLoader(), 84 | Utils.extractAllInterfaces(result.getClass()), 85 | new LoggingStatementInvocationHandler(connectionUuid, (Statement) result, databaseType)); 86 | } else if (("prepareStatement".equals(methodName) || "prepareCall".equals(methodName)) && args != null) { 87 | return Proxy.newProxyInstance(result.getClass().getClassLoader(), 88 | Utils.extractAllInterfaces(result.getClass()), new LoggingPreparedStatementInvocationHandler( 89 | connectionUuid, (PreparedStatement) result, (String) args[0], databaseType)); 90 | } 91 | 92 | } 93 | 94 | if (txCompletionType != null) { 95 | if (txCompletionType == TxCompletionType.SET_SAVE_POINT && result != null) { 96 | final Savepoint savepoint = (Savepoint) result; 97 | savePointDescription = savepoint.toString(); 98 | } 99 | PerfLogger.logTransactionComplete(connectionUuid, startTimeStamp, txCompletionType, 100 | System.nanoTime() - startNanos, savePointDescription); 101 | } 102 | 103 | return result; 104 | } 105 | 106 | public UUID getConnectionUuid() { 107 | return connectionUuid; 108 | } 109 | 110 | public int getConnectionId() { 111 | return connectionId; 112 | } 113 | 114 | public String getUrl() { 115 | return url; 116 | } 117 | 118 | public Properties getConnectionProperties() { 119 | return connectionProperties; 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/driver/LoggingResultSetInvocationHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.driver; 17 | 18 | import java.lang.reflect.InvocationHandler; 19 | import java.lang.reflect.Method; 20 | import java.sql.ResultSet; 21 | import java.util.UUID; 22 | 23 | import org.eclipse.jdt.annotation.Nullable; 24 | 25 | import ch.sla.jdbcperflogger.logger.PerfLogger; 26 | 27 | public class LoggingResultSetInvocationHandler implements InvocationHandler { 28 | private final ResultSet wrappedResultSet; 29 | private final UUID logId; 30 | private final long fetchStartTime; 31 | private boolean closed; 32 | private int nbRowsIterated; 33 | private long fetchDurationNanos; 34 | 35 | LoggingResultSetInvocationHandler(final ResultSet rset, final UUID logId) { 36 | wrappedResultSet = rset; 37 | this.logId = logId; 38 | fetchStartTime = System.nanoTime(); 39 | } 40 | 41 | @Override 42 | @Nullable 43 | public Object invoke(@Nullable final Object proxy, final Method method, final Object @Nullable [] args) 44 | throws Throwable { 45 | final long methodStartTime = System.nanoTime(); 46 | final Object result = Utils.invokeUnwrapException(wrappedResultSet, method, args); 47 | final long oneRowFetchDurationNanos = System.nanoTime() - methodStartTime; 48 | 49 | final String methodName = method.getName(); 50 | if (args == null || args.length == 0) { 51 | if ("close".equals(methodName)) { 52 | if (!closed) { 53 | closed = true; 54 | PerfLogger.logClosedResultSet(logId, System.nanoTime() - fetchStartTime, fetchDurationNanos, 55 | nbRowsIterated); 56 | } 57 | } else if ("next".equals(methodName)) { 58 | fetchDurationNanos += oneRowFetchDurationNanos; 59 | if (Boolean.TRUE.equals(result)) { 60 | nbRowsIterated++; 61 | } 62 | } 63 | } 64 | return result; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/driver/Utils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.driver; 17 | 18 | import java.lang.reflect.InvocationTargetException; 19 | import java.lang.reflect.Method; 20 | import java.sql.Connection; 21 | import java.sql.SQLException; 22 | import java.util.Collections; 23 | import java.util.HashSet; 24 | import java.util.Set; 25 | 26 | import org.eclipse.jdt.annotation.Nullable; 27 | 28 | import ch.sla.jdbcperflogger.DatabaseType; 29 | import ch.sla.jdbcperflogger.Logger; 30 | 31 | public final class Utils { 32 | private final static Logger LOGGER = Logger.getLogger(Utils.class); 33 | 34 | private Utils() { 35 | 36 | } 37 | 38 | static DatabaseType getDatabaseType(final Connection connection) { 39 | String dbProduct; 40 | try { 41 | dbProduct = connection.getMetaData().getDatabaseProductName(); 42 | } catch (final SQLException e) { 43 | LOGGER.error("cannot get db product name"); 44 | return DatabaseType.GENERIC; 45 | } 46 | if ("Oracle".equals(dbProduct)) { 47 | return DatabaseType.ORACLE; 48 | } 49 | return DatabaseType.GENERIC; 50 | } 51 | 52 | @Nullable 53 | static Object invokeUnwrapException(final Object target, final Method method, final Object @Nullable [] args) 54 | throws Throwable { 55 | try { 56 | return method.invoke(target, args); 57 | } catch (final InvocationTargetException e) { 58 | throw e.getCause(); 59 | } 60 | } 61 | 62 | static Object invokeUnwrapExceptionReturnNonNull(final Object target, final Method method, 63 | final Object @Nullable [] args) throws Throwable { 64 | try { 65 | final Object result = method.invoke(target, args); 66 | assert result != null; 67 | return result; 68 | } catch (final InvocationTargetException e) { 69 | throw e.getCause(); 70 | } 71 | } 72 | 73 | static Class[] extractAllInterfaces(final Class clazz) { 74 | final Set> interfaces = new HashSet>(); 75 | for (Class currClazz = clazz; currClazz != null; currClazz = currClazz.getSuperclass()) { 76 | Collections.addAll(interfaces, currClazz.getInterfaces()); 77 | } 78 | 79 | return interfaces.toArray(new Class[interfaces.size()]); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/driver/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @NonNullByDefault 17 | package ch.sla.jdbcperflogger.driver; 18 | 19 | import org.eclipse.jdt.annotation.NonNullByDefault; 20 | 21 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/logger/LogSender.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.logger; 2 | 3 | import ch.sla.jdbcperflogger.model.LogMessage; 4 | 5 | public interface LogSender { 6 | 7 | void postLog(LogMessage log); 8 | 9 | } -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/logger/PerfLoggerClientThread.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.logger; 17 | 18 | import java.io.Closeable; 19 | import java.io.IOException; 20 | import java.net.InetSocketAddress; 21 | import java.net.Socket; 22 | import java.security.AccessController; 23 | import java.security.PrivilegedAction; 24 | import java.util.concurrent.TimeUnit; 25 | 26 | import ch.sla.jdbcperflogger.Logger; 27 | 28 | class PerfLoggerClientThread extends Thread implements Closeable { 29 | private final static Logger LOGGER = Logger.getLogger(PerfLoggerClientThread.class); 30 | 31 | private static final int CONNECT_TIMEOUT_MS = 30000; 32 | 33 | volatile boolean done; 34 | 35 | private final InetSocketAddress socketAddress; 36 | 37 | private Socket socket; 38 | 39 | static PerfLoggerClientThread spawn(final InetSocketAddress socketAddress) { 40 | // avoid Classloader leaks 41 | 42 | return AccessController.doPrivileged(new PrivilegedAction() { 43 | @Override 44 | public PerfLoggerClientThread run() { 45 | final ClassLoader savedClassLoader = Thread.currentThread().getContextClassLoader(); 46 | try { 47 | Thread.currentThread().setContextClassLoader(null); 48 | 49 | final PerfLoggerClientThread thread = new PerfLoggerClientThread(socketAddress); 50 | thread.start(); 51 | return thread; 52 | } finally { 53 | Thread.currentThread().setContextClassLoader(savedClassLoader); 54 | } 55 | 56 | } 57 | }); 58 | } 59 | 60 | private PerfLoggerClientThread(final InetSocketAddress socketAddress) { 61 | this.setDaemon(true); 62 | this.setName("PerfLoggerClient " + socketAddress); 63 | this.socketAddress = socketAddress; 64 | } 65 | 66 | @Override 67 | public void run() { 68 | while (!done) { 69 | try { 70 | final InetSocketAddress resolvedAddress = new InetSocketAddress(socketAddress.getHostName(), 71 | socketAddress.getPort()); 72 | socket = new Socket(); 73 | socket.connect(resolvedAddress, CONNECT_TIMEOUT_MS); 74 | } catch (final IOException e) { 75 | LOGGER.debug("Unable to connect to " + socketAddress + ", will try again later", e); 76 | quietSleep(30); 77 | continue; 78 | } 79 | LOGGER.debug("Connected to " + socketAddress); 80 | try { 81 | final SocketLogSender sender = new SocketLogSender(socket); 82 | PerfLoggerRemoting.senders.add(sender); 83 | sender.run(); 84 | } catch (final IOException e) { 85 | LOGGER.info("Error in connection with " + socketAddress + ", will try again later", e); 86 | } 87 | } 88 | LOGGER.debug("Client for " + socketAddress + "closed"); 89 | } 90 | 91 | @Override 92 | public void close() { 93 | done = true; 94 | try { 95 | socket.close(); 96 | } catch (final IOException e) { 97 | LOGGER.info("Error closing socket at port" + socket.getLocalPort()); 98 | } 99 | } 100 | 101 | private void quietSleep(final int seconds) { 102 | try { 103 | Thread.sleep(TimeUnit.SECONDS.toMillis(seconds)); 104 | } catch (final InterruptedException e) { 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/logger/PerfLoggerRemoting.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.logger; 17 | 18 | import java.io.Closeable; 19 | import java.io.IOException; 20 | import java.net.InetSocketAddress; 21 | import java.util.ArrayList; 22 | import java.util.Date; 23 | import java.util.List; 24 | import java.util.Map; 25 | import java.util.Set; 26 | import java.util.WeakHashMap; 27 | import java.util.concurrent.CopyOnWriteArraySet; 28 | 29 | import ch.sla.jdbcperflogger.DriverConfig; 30 | import ch.sla.jdbcperflogger.driver.LoggingConnectionInvocationHandler; 31 | import ch.sla.jdbcperflogger.model.ConnectionInfo; 32 | import ch.sla.jdbcperflogger.model.LogMessage; 33 | 34 | public class PerfLoggerRemoting { 35 | 36 | final static Set senders = new CopyOnWriteArraySet(); 37 | final static Map connectionToInfo = new WeakHashMap(); 38 | final static List remotingThreads = new ArrayList(); 39 | 40 | public static synchronized void start() { 41 | final Integer serverPort = DriverConfig.INSTANCE.getServerPort(); 42 | if (serverPort != null) { 43 | remotingThreads.add(PerfLoggerServerThread.spawn(serverPort)); 44 | } 45 | for (final InetSocketAddress clientAddress : DriverConfig.INSTANCE.getClientAddresses()) { 46 | remotingThreads.add(PerfLoggerClientThread.spawn(clientAddress)); 47 | } 48 | } 49 | 50 | public static synchronized void stop() { 51 | for (final Closeable thread : remotingThreads) { 52 | try { 53 | thread.close(); 54 | } catch (final IOException e) { 55 | // ignore 56 | } 57 | } 58 | remotingThreads.clear(); 59 | } 60 | 61 | private PerfLoggerRemoting() { 62 | } 63 | 64 | public static void connectionCreated(final LoggingConnectionInvocationHandler connectionHandler, 65 | final long connectionCreationDuration) { 66 | final ConnectionInfo info = new ConnectionInfo(connectionHandler.getConnectionUuid(), 67 | connectionHandler.getConnectionId(), connectionHandler.getUrl(), new Date(), connectionCreationDuration, 68 | connectionHandler.getConnectionProperties()); 69 | synchronized (connectionToInfo) { 70 | connectionToInfo.put(connectionHandler, info); 71 | postLog(info); 72 | } 73 | } 74 | 75 | static void postLog(final LogMessage log) { 76 | for (final LogSender sender : senders) { 77 | sender.postLog(log); 78 | } 79 | } 80 | 81 | public static void addSender(final LogSender sender) { 82 | senders.add(sender); 83 | } 84 | 85 | public static void removeSender(final LogSender sender) { 86 | senders.remove(sender); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/logger/PerfLoggerServerThread.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.logger; 17 | 18 | import java.io.Closeable; 19 | import java.io.IOException; 20 | import java.net.ServerSocket; 21 | import java.net.Socket; 22 | import java.security.AccessController; 23 | import java.security.PrivilegedAction; 24 | 25 | import ch.sla.jdbcperflogger.Logger; 26 | 27 | class PerfLoggerServerThread extends Thread implements Closeable { 28 | private final static Logger LOGGER = Logger.getLogger(PerfLoggerServerThread.class); 29 | 30 | ServerSocket serverSocket; 31 | volatile boolean done; 32 | 33 | static PerfLoggerServerThread spawn(final int serverPort) { 34 | // avoid Classloader leaks 35 | 36 | return AccessController.doPrivileged(new PrivilegedAction() { 37 | @Override 38 | public PerfLoggerServerThread run() { 39 | final ClassLoader savedClassLoader = Thread.currentThread().getContextClassLoader(); 40 | try { 41 | Thread.currentThread().setContextClassLoader(null); 42 | 43 | final PerfLoggerServerThread thread = new PerfLoggerServerThread(serverPort); 44 | thread.start(); 45 | return thread; 46 | } finally { 47 | Thread.currentThread().setContextClassLoader(savedClassLoader); 48 | } 49 | 50 | } 51 | }); 52 | } 53 | 54 | private PerfLoggerServerThread(final int serverPort) { 55 | this.setDaemon(true); 56 | this.setName("PerfLoggerServer acceptor port " + serverPort); 57 | try { 58 | serverSocket = new ServerSocket(serverPort); 59 | } catch (final IOException e) { 60 | throw new RuntimeException(e); 61 | } 62 | } 63 | 64 | @Override 65 | public void run() { 66 | try { 67 | while (!done) { 68 | try { 69 | final Socket socket = serverSocket.accept(); 70 | LOGGER.debug("Got client connection from " + socket); 71 | final SocketLogSender sender = new SocketLogSender(socket); 72 | final Thread logSenderThread = new Thread(sender, "PerfLoggerServer " + socket.getInetAddress() 73 | + ":" + socket.getPort()); 74 | logSenderThread.setDaemon(true); 75 | logSenderThread.start(); 76 | PerfLoggerRemoting.senders.add(sender); 77 | } catch (final IOException e) { 78 | LOGGER.error("error while accepting socket", e); 79 | } 80 | } 81 | } finally { 82 | try { 83 | serverSocket.close(); 84 | } catch (final IOException e) { 85 | LOGGER.error("error while closing socket", e); 86 | } 87 | } 88 | } 89 | 90 | @Override 91 | public void close() { 92 | done = true; 93 | try { 94 | serverSocket.close(); 95 | } catch (final IOException e) { 96 | LOGGER.error("error closing socket at " + serverSocket.getLocalPort(), e); 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/logger/RecordingLogSender.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.logger; 2 | 3 | import java.util.concurrent.LinkedBlockingDeque; 4 | 5 | import org.eclipse.jdt.annotation.Nullable; 6 | 7 | import ch.sla.jdbcperflogger.model.LogMessage; 8 | 9 | public class RecordingLogSender implements LogSender { 10 | private static final int DEFAULT_RETAINED_LOG_MESSAGES_COUNT = 50; 11 | private final LinkedBlockingDeque queue; 12 | 13 | public RecordingLogSender() { 14 | this(DEFAULT_RETAINED_LOG_MESSAGES_COUNT); 15 | } 16 | 17 | public RecordingLogSender(final int retainedLogMessagesCount) { 18 | queue = new LinkedBlockingDeque(retainedLogMessagesCount); 19 | 20 | } 21 | 22 | @Override 23 | public void postLog(final LogMessage log) { 24 | while (!queue.offerFirst(log)) { 25 | queue.pollLast(); 26 | } 27 | } 28 | 29 | public LogMessage[] getRecordedLogMessages() { 30 | return queue.toArray(new LogMessage[0]); 31 | } 32 | 33 | public void clearLogs() { 34 | queue.clear(); 35 | } 36 | 37 | public @Nullable LogMessage lastLogMessage(final int index) { 38 | final LogMessage[] logs = getRecordedLogMessages(); 39 | if (logs.length <= index) { 40 | return null; 41 | } 42 | return logs[index]; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/logger/SocketLogSender.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.logger; 2 | 3 | import java.io.IOException; 4 | import java.io.ObjectOutputStream; 5 | import java.net.Socket; 6 | import java.net.SocketException; 7 | import java.util.concurrent.BlockingQueue; 8 | import java.util.concurrent.LinkedBlockingQueue; 9 | import java.util.concurrent.TimeUnit; 10 | import java.util.concurrent.atomic.AtomicBoolean; 11 | 12 | import ch.sla.jdbcperflogger.Logger; 13 | import ch.sla.jdbcperflogger.model.BufferFullLogMessage; 14 | import ch.sla.jdbcperflogger.model.ConnectionInfo; 15 | import ch.sla.jdbcperflogger.model.LogMessage; 16 | 17 | // public for tests 18 | public class SocketLogSender implements Runnable, LogSender { 19 | private final static Logger LOGGER2 = Logger.getLogger(SocketLogSender.class); 20 | 21 | private final BlockingQueue logsToSend = new LinkedBlockingQueue(10000); 22 | private final Socket socket; 23 | private final AtomicBoolean queueFull = new AtomicBoolean(); 24 | 25 | SocketLogSender(final Socket socket) throws SocketException { 26 | this.socket = socket; 27 | socket.setKeepAlive(true); 28 | socket.setSoTimeout((int) TimeUnit.SECONDS.toMillis(10)); 29 | } 30 | 31 | /* (non-Javadoc) 32 | * @see ch.sla.jdbcperflogger.logger.LogSender#postLog(ch.sla.jdbcperflogger.model.LogMessage) 33 | */ 34 | @Override 35 | public void postLog(final LogMessage log) { 36 | final boolean posted = logsToSend.offer(log); 37 | if (!posted) { 38 | queueFull.set(true); 39 | LOGGER2.warn("queue full, dropping remote log of statement"); 40 | } 41 | } 42 | 43 | @Override 44 | public void run() { 45 | // first send all current connections information to the socket 46 | synchronized (PerfLoggerRemoting.connectionToInfo) { 47 | for (final ConnectionInfo connectionInfo : PerfLoggerRemoting.connectionToInfo.values()) { 48 | logsToSend.offer(connectionInfo); 49 | } 50 | } 51 | 52 | ObjectOutputStream oos = null; 53 | try { 54 | oos = new ObjectOutputStream(socket.getOutputStream()); 55 | int cnt = 0; 56 | while (true) { 57 | try { 58 | if (queueFull.compareAndSet(true, false)) { 59 | oos.writeUnshared(new BufferFullLogMessage(System.currentTimeMillis())); 60 | } 61 | 62 | final LogMessage log = logsToSend.poll(10, TimeUnit.SECONDS); 63 | if (log != null) { 64 | oos.writeUnshared(log); 65 | } else { 66 | // check the socket state 67 | if (socket.isClosed() || !socket.isConnected()) { 68 | // client disconnected 69 | break; 70 | } 71 | oos.writeUnshared(null); 72 | } 73 | cnt = (cnt + 1) % 10; 74 | if (cnt == 0) { 75 | // avoid mem leak when the stream keeps back 76 | // references to serialized objects 77 | oos.reset(); 78 | } 79 | } catch (final InterruptedException e) { 80 | break; 81 | } 82 | } 83 | } catch (final IOException e) { 84 | LOGGER2.warn("socket error", e); 85 | } finally { 86 | LOGGER2.info("closing connection with " + socket); 87 | PerfLoggerRemoting.senders.remove(this); 88 | if (oos != null) { 89 | try { 90 | oos.close(); 91 | } catch (final IOException ignored) { 92 | } 93 | } 94 | try { 95 | socket.close(); 96 | } catch (final IOException e) { 97 | LOGGER2.error("error while closing socket", e); 98 | } 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/logger/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @NonNullByDefault 17 | package ch.sla.jdbcperflogger.logger; 18 | 19 | import org.eclipse.jdt.annotation.NonNullByDefault; 20 | 21 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/model/AbstractBeforeStatementExecutionLog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.model; 17 | 18 | import java.util.UUID; 19 | 20 | import ch.sla.jdbcperflogger.StatementType; 21 | 22 | public class AbstractBeforeStatementExecutionLog implements LogMessage { 23 | 24 | private static final long serialVersionUID = 1L; 25 | 26 | private final UUID connectionUuid; 27 | private final UUID logId; 28 | private final long timestamp; 29 | private final StatementType statementType; 30 | private final String threadName; 31 | private final int timeout; 32 | private final boolean autoCommit; 33 | private final int transactionIsolation; 34 | 35 | public AbstractBeforeStatementExecutionLog(final UUID connectionId, final UUID logId, final long timestamp, 36 | final StatementType statementType, final String threadName, final int timeout, final boolean autoCommit, int transactionIsolation) { 37 | connectionUuid = connectionId; 38 | this.logId = logId; 39 | this.timestamp = timestamp; 40 | this.statementType = statementType; 41 | this.threadName = threadName; 42 | this.timeout = timeout; 43 | this.autoCommit = autoCommit; 44 | this.transactionIsolation = transactionIsolation; 45 | } 46 | 47 | public UUID getConnectionUuid() { 48 | return connectionUuid; 49 | } 50 | 51 | public UUID getLogId() { 52 | return logId; 53 | } 54 | 55 | public long getTimestamp() { 56 | return timestamp; 57 | } 58 | 59 | public StatementType getStatementType() { 60 | return statementType; 61 | } 62 | 63 | public String getThreadName() { 64 | return threadName; 65 | } 66 | 67 | public int getTimeout() { 68 | return timeout; 69 | } 70 | 71 | public boolean isAutoCommit() { 72 | return autoCommit; 73 | } 74 | 75 | public int getTransactionIsolation() { 76 | return transactionIsolation; 77 | } 78 | } -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/model/BatchedNonPreparedStatementsLog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.model; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Collections; 20 | import java.util.List; 21 | import java.util.UUID; 22 | 23 | import ch.sla.jdbcperflogger.StatementType; 24 | 25 | public class BatchedNonPreparedStatementsLog extends AbstractBeforeStatementExecutionLog { 26 | 27 | private static final long serialVersionUID = 1L; 28 | 29 | private final List sqlList; 30 | 31 | public BatchedNonPreparedStatementsLog(final UUID connectionId, final UUID logId, final long timestamp, 32 | final List sqlList, final String threadName, final int timeout, final boolean autoCommit, 33 | final int transactionIsolation) { 34 | super(connectionId, logId, timestamp, StatementType.NON_PREPARED_BATCH_EXECUTION, threadName, timeout, 35 | autoCommit, transactionIsolation); 36 | this.sqlList = Collections.unmodifiableList(new ArrayList(sqlList)); 37 | } 38 | 39 | public List getSqlList() { 40 | return sqlList; 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | return "BatchedNonPreparedStatementsLog["// 46 | + "logId=" + getLogId()// 47 | + ", timestamp=" + getTimestamp()// 48 | + ", statementType=" + getStatementType()// 49 | + ", threadName=" + getThreadName()// 50 | + ", timeout=" + getTimeout()// 51 | + ", autocommit=" + isAutoCommit()// 52 | + ", getTransactionIsolation=" + getTransactionIsolation()// 53 | + ", sqlList=" + sqlList// 54 | + "]"; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/model/BatchedPreparedStatementsLog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.model; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Collections; 20 | import java.util.List; 21 | import java.util.UUID; 22 | 23 | import ch.sla.jdbcperflogger.StatementType; 24 | 25 | public class BatchedPreparedStatementsLog extends AbstractBeforeStatementExecutionLog { 26 | 27 | private static final long serialVersionUID = 1L; 28 | 29 | private final String rawSql; 30 | private final List sqlList; 31 | 32 | public BatchedPreparedStatementsLog(final UUID connectionId, final UUID logId, final long timestamp, 33 | final String rawSql, final List sqlList, final String threadName, final int timeout, 34 | final boolean autoCommit, final int transactionIsolation) { 35 | super(connectionId, logId, timestamp, StatementType.PREPARED_BATCH_EXECUTION, threadName, timeout, autoCommit, transactionIsolation); 36 | this.rawSql = rawSql; 37 | this.sqlList = Collections.unmodifiableList(new ArrayList(sqlList)); 38 | } 39 | 40 | public String getRawSql() { 41 | return rawSql; 42 | } 43 | 44 | public List getSqlList() { 45 | return sqlList; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return "BatchedPreparedStatementsLog["// 51 | + "logId=" + getLogId()// 52 | + ", rawSql=" + rawSql// 53 | + ", timestamp=" + getTimestamp()// 54 | + ", statementType=" + getStatementType()// 55 | + ", threadName=" + getThreadName()// 56 | + ", timeout=" + getTimeout()// 57 | + ", autocommit=" + isAutoCommit()// 58 | + ", transactionIsolation=" + getTransactionIsolation()// 59 | + "]"; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/model/BufferFullLogMessage.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.model; 2 | 3 | public class BufferFullLogMessage implements LogMessage { 4 | private static final long serialVersionUID = 1L; 5 | private final long timestamp; 6 | 7 | public BufferFullLogMessage(final long tstamp) { 8 | this.timestamp = tstamp; 9 | } 10 | 11 | public long getTimestamp() { 12 | return timestamp; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/model/ConnectionInfo.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.model; 2 | 3 | import java.util.Date; 4 | import java.util.Properties; 5 | import java.util.UUID; 6 | 7 | public class ConnectionInfo implements LogMessage { 8 | private static final long serialVersionUID = 1L; 9 | 10 | private final UUID uuid; 11 | private final int connectionNumber; 12 | private final String url; 13 | private final Date creationDate; 14 | private final long connectionCreationDuration; 15 | /** 16 | * Connection props without password 17 | */ 18 | private final Properties connectionProperties; 19 | 20 | public ConnectionInfo(final UUID uuid, final int connectionNumber, final String url, final Date creationDate, 21 | final long connectionCreationDuration, final Properties connectionProperties) { 22 | this.uuid = uuid; 23 | this.connectionNumber = connectionNumber; 24 | this.url = url; 25 | this.creationDate = creationDate; 26 | this.connectionCreationDuration = connectionCreationDuration; 27 | this.connectionProperties = connectionProperties; 28 | } 29 | 30 | public UUID getUuid() { 31 | return uuid; 32 | } 33 | 34 | public int getConnectionNumber() { 35 | return connectionNumber; 36 | } 37 | 38 | public String getUrl() { 39 | return url; 40 | } 41 | 42 | public Date getCreationDate() { 43 | return creationDate; 44 | } 45 | 46 | public Properties getConnectionProperties() { 47 | return connectionProperties; 48 | } 49 | 50 | public long getConnectionCreationDuration() { 51 | return connectionCreationDuration; 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | return "ConnectionInfo["// 57 | + "connectionId=" + uuid// 58 | + ", connectionNumber=" + connectionNumber// 59 | + ", url=" + url// 60 | + ", creationDate=" + creationDate// 61 | + ", connectionCreationDuration=" + connectionCreationDuration// 62 | + "]"; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/model/LogMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.model; 17 | 18 | import java.io.Serializable; 19 | 20 | public interface LogMessage extends Serializable { 21 | 22 | } 23 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/model/PreparedStatementValuesHolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.model; 17 | 18 | import java.io.Serializable; 19 | import java.util.HashMap; 20 | 21 | /** 22 | * Map from parameter index or name to value. 23 | * 24 | * @author slaurent 25 | * 26 | */ 27 | public class PreparedStatementValuesHolder extends HashMap { 28 | private static final long serialVersionUID = 1L; 29 | 30 | public PreparedStatementValuesHolder() { 31 | super(10); 32 | } 33 | 34 | public PreparedStatementValuesHolder(final PreparedStatementValuesHolder original) { 35 | super(original); 36 | } 37 | 38 | public PreparedStatementValuesHolder copy() { 39 | return new PreparedStatementValuesHolder(this); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/model/ResultSetLog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.model; 17 | 18 | import java.util.UUID; 19 | 20 | public class ResultSetLog implements LogMessage { 21 | 22 | private static final long serialVersionUID = 1L; 23 | 24 | private final UUID logId; 25 | 26 | private final long resultSetUsageDurationNanos; 27 | private final long fetchDurationNanos; 28 | private final int nbRowsIterated; 29 | 30 | public ResultSetLog(final UUID logId, final long resultSetIterationTimeNanos, final long fetchDurationNanos, 31 | final int nbRowsIterated) { 32 | this.logId = logId; 33 | this.resultSetUsageDurationNanos = resultSetIterationTimeNanos; 34 | this.fetchDurationNanos = fetchDurationNanos; 35 | this.nbRowsIterated = nbRowsIterated; 36 | } 37 | 38 | public UUID getLogId() { 39 | return logId; 40 | } 41 | 42 | public long getResultSetUsageDurationNanos() { 43 | return resultSetUsageDurationNanos; 44 | } 45 | 46 | public long getFetchDurationNanos() { 47 | return fetchDurationNanos; 48 | } 49 | 50 | public int getNbRowsIterated() { 51 | return nbRowsIterated; 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | return "ResultSetLog["// 57 | + "logId=" + logId// 58 | + ", resultSetUsageDurationNanos=" + resultSetUsageDurationNanos// 59 | + ", fetchDurationNanos=" + fetchDurationNanos// 60 | + ", nbRowsIterated=" + nbRowsIterated// 61 | + "]"; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/model/SqlTypedValue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.model; 17 | 18 | import org.eclipse.jdt.annotation.Nullable; 19 | 20 | public class SqlTypedValue { 21 | @Nullable 22 | public final Object value; 23 | @Nullable 24 | public final Integer sqlType; 25 | @Nullable 26 | public final String setter; 27 | 28 | public SqlTypedValue(@Nullable final Object value, final @Nullable Integer sqlType) { 29 | this.value = value; 30 | this.sqlType = sqlType; 31 | setter = null; 32 | } 33 | 34 | public SqlTypedValue(final Object value, final String setter) { 35 | this.value = value; 36 | sqlType = -1; 37 | this.setter = setter; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/model/StatementExecutedLog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.model; 17 | 18 | import java.util.UUID; 19 | 20 | import org.eclipse.jdt.annotation.Nullable; 21 | 22 | public class StatementExecutedLog implements LogMessage { 23 | 24 | private static final long serialVersionUID = 1L; 25 | 26 | private final UUID logId; 27 | private final long executionTimeNanos; 28 | @Nullable 29 | private final Long updateCount; 30 | @Nullable 31 | private final String sqlException; 32 | 33 | public StatementExecutedLog(final UUID logId, final long executionTimeNanos, @Nullable final Long updateCount, 34 | @Nullable final String sqlException) { 35 | this.logId = logId; 36 | this.executionTimeNanos = executionTimeNanos; 37 | this.updateCount = updateCount; 38 | this.sqlException = sqlException; 39 | } 40 | 41 | public UUID getLogId() { 42 | return logId; 43 | } 44 | 45 | public long getExecutionTimeNanos() { 46 | return executionTimeNanos; 47 | } 48 | 49 | @Nullable 50 | public Long getUpdateCount() { 51 | return updateCount; 52 | } 53 | 54 | @Nullable 55 | public String getSqlException() { 56 | return sqlException; 57 | } 58 | 59 | @Override 60 | public String toString() { 61 | return "StatementExecutedLog["// 62 | + "logId=" + logId// 63 | + ", executionTimeNanos=" + executionTimeNanos// 64 | + ", updateCount=" + updateCount// 65 | + "]"; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/model/StatementLog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.model; 17 | 18 | import java.util.UUID; 19 | 20 | import ch.sla.jdbcperflogger.StatementType; 21 | 22 | public class StatementLog extends AbstractBeforeStatementExecutionLog { 23 | 24 | private static final long serialVersionUID = 1L; 25 | 26 | private final String rawSql; 27 | private final String filledSql; 28 | private final boolean preparedStatement; 29 | 30 | public StatementLog(final UUID connectionId, final UUID logId, final long timestamp, 31 | final StatementType statementType, final String sql, final String threadName, final int timeout, 32 | final boolean autoCommit, final int transactionIsolation) { 33 | super(connectionId, logId, timestamp, statementType, threadName, timeout, autoCommit, transactionIsolation); 34 | rawSql = sql; 35 | filledSql = sql; 36 | preparedStatement = false; 37 | } 38 | 39 | public StatementLog(final UUID connectionId, final UUID logId, final long timestamp, 40 | final StatementType statementType, final String rawSql, final String filledSql, final String threadName, 41 | final int timeout, final boolean autoCommit, int transactionIsolation) { 42 | super(connectionId, logId, timestamp, statementType, threadName, timeout, autoCommit, transactionIsolation); 43 | this.rawSql = rawSql; 44 | this.filledSql = filledSql; 45 | preparedStatement = true; 46 | } 47 | 48 | public String getRawSql() { 49 | return rawSql; 50 | } 51 | 52 | public String getFilledSql() { 53 | return filledSql; 54 | } 55 | 56 | public boolean isPreparedStatement() { 57 | return preparedStatement; 58 | } 59 | 60 | @Override 61 | public String toString() { 62 | return "StatementLog["// 63 | + "logId=" + getLogId()// 64 | + ", timestamp=" + getTimestamp()// 65 | + ", statementType=" + getStatementType()// 66 | + ", threadName=" + getThreadName()// 67 | + ", timeout=" + getTimeout()// 68 | + ", autocommit=" + isAutoCommit()// 69 | + ", getTransactionIsolation=" + getTransactionIsolation()// 70 | + ", rawSql=" + rawSql// 71 | + ", filledSql=" + filledSql// 72 | + ", preparedStatement=" + preparedStatement// 73 | + "]"; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/model/TxCompleteLog.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.model; 2 | 3 | import java.util.UUID; 4 | 5 | import org.eclipse.jdt.annotation.Nullable; 6 | 7 | import ch.sla.jdbcperflogger.TxCompletionType; 8 | 9 | public class TxCompleteLog implements LogMessage { 10 | private static final long serialVersionUID = 1L; 11 | 12 | private final UUID connectionUuid; 13 | private final long timestamp; 14 | private final TxCompletionType completionType; 15 | private final long executionTimeNanos; 16 | private final String threadName; 17 | @Nullable 18 | private final String savePointDescription; 19 | 20 | public TxCompleteLog(final UUID connectionUuid, final long timestamp, final TxCompletionType completionType, 21 | final long executionTimeNanos, final String threadName, final @Nullable String savePointDescription) { 22 | this.connectionUuid = connectionUuid; 23 | this.timestamp = timestamp; 24 | this.completionType = completionType; 25 | this.executionTimeNanos = executionTimeNanos; 26 | this.threadName = threadName; 27 | this.savePointDescription = savePointDescription; 28 | } 29 | 30 | public UUID getConnectionUuid() { 31 | return connectionUuid; 32 | } 33 | 34 | public long getTimestamp() { 35 | return timestamp; 36 | } 37 | 38 | public TxCompletionType getCompletionType() { 39 | return completionType; 40 | } 41 | 42 | public long getExecutionTimeNanos() { 43 | return executionTimeNanos; 44 | } 45 | 46 | public String getThreadName() { 47 | return threadName; 48 | } 49 | 50 | @Nullable 51 | public String getSavePointDescription() { 52 | return savePointDescription; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return "TxCompleteLog["// 58 | + "connectionUuid=" + connectionUuid// 59 | + ", timestamp=" + timestamp// 60 | + ", completionType=" + completionType// 61 | + ", executionTimeNanos=" + executionTimeNanos// 62 | + ", threadName=" + threadName// 63 | + ", savePointDescription=" + savePointDescription// 64 | + "]"; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/model/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @NonNullByDefault 17 | package ch.sla.jdbcperflogger.model; 18 | 19 | import org.eclipse.jdt.annotation.NonNullByDefault; 20 | 21 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/java/ch/sla/jdbcperflogger/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @NonNullByDefault 17 | package ch.sla.jdbcperflogger; 18 | 19 | import org.eclipse.jdt.annotation.NonNullByDefault; 20 | 21 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/resources/.gitignore: -------------------------------------------------------------------------------- 1 | /log4j.xml 2 | /jdbcperflogger.xml 3 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/resources/META-INF/services/java.sql.Driver: -------------------------------------------------------------------------------- 1 | ch.sla.jdbcperflogger.driver.WrappingDriver -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/main/resources/jdbcperflogger-fallback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | jdbc:oracle: 8 | oracle.jdbc.OracleDriver 9 | 10 | 11 | jdbc:h2: 12 | org.h2.Driver 13 | 14 | 15 | jdbc:hsqldb: 16 | org.hsqldb.jdbc.JDBCDriver 17 | 18 | 19 | jdbc:mysql: 20 | com.mysql.jdbc.Driver 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/test/java/ch/sla/jdbcperflogger/DriverConfigTest.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertNotNull; 5 | import static org.junit.Assert.assertNull; 6 | import static org.junit.Assert.assertTrue; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.io.InputStreamReader; 12 | import java.net.InetSocketAddress; 13 | 14 | import org.junit.Test; 15 | 16 | public class DriverConfigTest { 17 | 18 | private static final String TEST_TXT = "src/test/resourcesNonClasspath/test.txt"; 19 | 20 | @SuppressWarnings("null") 21 | @Test 22 | public void testDefaultConfig() throws Exception { 23 | assertEquals(8889, DriverConfig.INSTANCE.getServerPort().intValue()); 24 | assertEquals(1, DriverConfig.INSTANCE.getClientAddresses().size()); 25 | final InetSocketAddress defaultClientAddress = DriverConfig.INSTANCE.getClientAddresses().get(0); 26 | assertEquals("localhost", defaultClientAddress.getHostName()); 27 | assertEquals(4561, defaultClientAddress.getPort()); 28 | 29 | assertEquals("oracle.jdbc.OracleDriver", DriverConfig.INSTANCE.getClassNameForJdbcUrl("jdbc:oracle:")); 30 | assertEquals("oracle.jdbc.OracleDriver", DriverConfig.INSTANCE.getClassNameForJdbcUrl("jdbc:oracle:thin:toto")); 31 | assertEquals("com.MyDriver", DriverConfig.INSTANCE.getClassNameForJdbcUrl("jdbc:mydriver:")); 32 | assertNull(DriverConfig.INSTANCE.getClassNameForJdbcUrl("jdbc:mynonexisting:")); 33 | } 34 | 35 | @Test 36 | public void testOpenFallbackConfigFile() throws Exception { 37 | final InputStream is = DriverConfig.openConfigFile(PerfLoggerConstants.CONFIG_FILE_FALLBACK_LOCATION); 38 | assertNotNull(is); 39 | is.close(); 40 | } 41 | 42 | @Test 43 | public void testOpenFileNotInClasspath() throws Exception { 44 | InputStream is = DriverConfig.openConfigFile(TEST_TXT); 45 | assertNotNull(is); 46 | is.close(); 47 | 48 | is = DriverConfig.openConfigFile("src/test/resourcesNonClasspath/notfound.txt"); 49 | assertNull(is); 50 | } 51 | 52 | @Test 53 | public void testOpenDefaultConfigFile() throws Exception { 54 | final InputStream is = DriverConfig.openConfigFile(); 55 | assertNotNull(is); 56 | is.close(); 57 | } 58 | 59 | @Test 60 | public void testOpenSystemSpecifiedInexistentConfigFile() throws Exception { 61 | System.setProperty(PerfLoggerConstants.CONFIG_FILE_LOCATION_PROP_KEY, "dummy"); 62 | final InputStream is = DriverConfig.openConfigFile(); 63 | assertNotNull(is); 64 | 65 | final String line = readFirstLine(is); 66 | assertTrue(line.contains("fallback")); 67 | is.close(); 68 | } 69 | 70 | @Test 71 | public void testOpenSystemSpecifiedConfigFileMalformed() throws Exception { 72 | System.setProperty(PerfLoggerConstants.CONFIG_FILE_LOCATION_PROP_KEY, TEST_TXT); 73 | final InputStream is = DriverConfig.openConfigFile(); 74 | assertNotNull(is); 75 | 76 | final String line = readFirstLine(is); 77 | assertTrue(line.contains("HELLO")); 78 | is.close(); 79 | } 80 | 81 | private String readFirstLine(final InputStream is) throws IOException { 82 | return new BufferedReader(new InputStreamReader(is)).readLine(); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/test/java/ch/sla/jdbcperflogger/driver/UtilsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.driver; 17 | 18 | import java.io.Serializable; 19 | import java.util.Collection; 20 | import java.util.Collections; 21 | import java.util.HashSet; 22 | import java.util.Set; 23 | 24 | import org.junit.Assert; 25 | import org.junit.Test; 26 | 27 | public class UtilsTest { 28 | 29 | @Test 30 | public void testExtractAllInterfaces() { 31 | final Class[] intfs = Utils.extractAllInterfaces(HashSet.class); 32 | final Set> coll = new HashSet>(); 33 | Collections.addAll(coll, intfs); 34 | Assert.assertTrue(coll.contains(Collection.class)); 35 | Assert.assertTrue(coll.contains(Serializable.class)); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/test/java/ch/sla/jdbcperflogger/driver/WrappingDriverUnloadTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Matthias Mueller 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.driver; 17 | 18 | import static org.awaitility.Awaitility.await; 19 | 20 | import java.io.IOException; 21 | import java.net.ServerSocket; 22 | import java.net.Socket; 23 | import java.util.concurrent.Callable; 24 | import java.util.concurrent.TimeUnit; 25 | 26 | import org.junit.Test; 27 | 28 | import ch.sla.jdbcperflogger.DriverConfig; 29 | 30 | public class WrappingDriverUnloadTest { 31 | 32 | private static final int WAIT_TIME = 10; 33 | 34 | @SuppressWarnings("null") 35 | @Test 36 | public void testUnload() throws Exception { 37 | final Integer serverPort = DriverConfig.INSTANCE.getServerPort(); 38 | final int clientPort = DriverConfig.INSTANCE.getClientAddresses().get(0).getPort(); 39 | final ServerSocket logReceiver = new ServerSocket(clientPort); 40 | 41 | await().atMost(WAIT_TIME, TimeUnit.SECONDS).until(portInUse(clientPort)); 42 | 43 | WrappingDriver.load(); 44 | 45 | try { 46 | await().atMost(WAIT_TIME, TimeUnit.SECONDS).until(portInUse(serverPort)); 47 | 48 | final Socket client = logReceiver.accept(); 49 | final int remotePort = client.getPort(); 50 | 51 | // following line is commented, not working on macOS 52 | // await().atMost(WAIT_TIME, TimeUnit.SECONDS).until(portInUse(remotePort)); 53 | 54 | client.close(); 55 | logReceiver.close(); 56 | 57 | WrappingDriver.unload(); 58 | await().atMost(WAIT_TIME, TimeUnit.SECONDS).until(portNotInUse(clientPort)); 59 | await().atMost(WAIT_TIME, TimeUnit.SECONDS).until(portNotInUse(serverPort)); 60 | await().atMost(WAIT_TIME, TimeUnit.SECONDS).until(portNotInUse(remotePort)); 61 | } finally { 62 | WrappingDriver.load(); 63 | await().atMost(WAIT_TIME, TimeUnit.SECONDS).until(portInUse(serverPort)); 64 | } 65 | } 66 | 67 | protected Callable portInUse(final int port) { 68 | return () -> !canOpenPort(port); 69 | } 70 | 71 | protected Callable portNotInUse(final int port) { 72 | return () -> canOpenPort(port); 73 | } 74 | 75 | protected boolean canOpenPort(final int port) { 76 | try ( 77 | ServerSocket serverSocket = new ServerSocket(port)) { 78 | return true; 79 | } catch (final IOException e) { 80 | return false; 81 | } 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/test/java/ch/sla/jdbcperflogger/logger/MyClassLoader.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.logger; 2 | 3 | import java.net.URL; 4 | import java.net.URLClassLoader; 5 | 6 | class MyClassLoader extends URLClassLoader { 7 | 8 | public MyClassLoader() { 9 | super(new URL[0]); 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/test/java/ch/sla/jdbcperflogger/logger/PerfLoggerClientThreadTest.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.logger; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | public class PerfLoggerClientThreadTest { 10 | 11 | @Test 12 | public void testCCL() throws InterruptedException, IOException { 13 | final ClassLoader oldCcl = currentCcl(); 14 | try { 15 | final MyClassLoader myClassLoader = new MyClassLoader(); 16 | Thread.currentThread().setContextClassLoader(myClassLoader); 17 | 18 | final PerfLoggerClientThread thread = PerfLoggerClientThread.spawn(new InetSocketAddress("localhost", 0)); 19 | Thread.sleep(1000); 20 | final ClassLoader classLoaderInsideThread = thread.getContextClassLoader(); 21 | thread.done = true; 22 | thread.interrupt(); 23 | thread.join(); 24 | 25 | Assert.assertNotSame(myClassLoader, classLoaderInsideThread); 26 | } finally { 27 | Thread.currentThread().setContextClassLoader(oldCcl); 28 | } 29 | } 30 | 31 | private static ClassLoader currentCcl() { 32 | return Thread.currentThread().getContextClassLoader(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/test/java/ch/sla/jdbcperflogger/logger/PerfLoggerServerThreadTest.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.logger; 2 | 3 | import java.io.IOException; 4 | 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | public class PerfLoggerServerThreadTest { 9 | 10 | @Test 11 | public void testCCL() throws InterruptedException, IOException { 12 | final ClassLoader oldCcl = currentCcl(); 13 | try { 14 | final MyClassLoader myClassLoader = new MyClassLoader(); 15 | Thread.currentThread().setContextClassLoader(myClassLoader); 16 | 17 | final PerfLoggerServerThread thread = PerfLoggerServerThread.spawn(0); 18 | Thread.sleep(1000); 19 | final ClassLoader classLoaderInsideThread = thread.getContextClassLoader(); 20 | thread.serverSocket.close(); 21 | thread.done = true; 22 | thread.interrupt(); 23 | thread.join(); 24 | 25 | Assert.assertNotSame(myClassLoader, classLoaderInsideThread); 26 | } finally { 27 | Thread.currentThread().setContextClassLoader(oldCcl); 28 | } 29 | } 30 | 31 | private static ClassLoader currentCcl() { 32 | return Thread.currentThread().getContextClassLoader(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/test/resources/jdbcperflogger.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | jdbc:oracle: 7 | oracle.jdbc.OracleDriver 8 | 9 | 10 | jdbc:mydriver: 11 | com.MyDriver 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/test/resources/log4j.dtd: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 58 | 59 | 60 | 61 | 62 | 65 | 69 | 70 | 71 | 74 | 75 | 76 | 79 | 80 | 81 | 82 | 83 | 84 | 87 | 88 | 89 | 90 | 91 | 94 | 95 | 96 | 100 | 101 | 102 | 103 | 104 | 108 | 109 | 110 | 111 | 115 | 116 | 117 | 118 | 119 | 120 | 125 | 126 | 127 | 128 | 129 | 133 | 134 | 135 | 136 | 138 | 139 | 140 | 142 | 143 | 144 | 147 | 148 | 149 | 150 | 154 | 155 | 156 | 159 | 160 | 161 | 164 | 165 | 166 | 170 | 171 | 172 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 193 | 194 | 195 | 196 | 198 | 199 | 200 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 220 | 221 | 222 | 223 | 224 | 228 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/test/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/test/resources/logging.properties: -------------------------------------------------------------------------------- 1 | # since the driver uses java.util.logging, for tests we bridge it to slf4j 2 | handlers = org.slf4j.bridge.SLF4JBridgeHandler 3 | -------------------------------------------------------------------------------- /jdbc-perf-logger-driver/src/test/resourcesNonClasspath/test.txt: -------------------------------------------------------------------------------- 1 | HELLO -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/.gitignore: -------------------------------------------------------------------------------- 1 | /logdb 2 | /PerfLoggerGuiMain.launch 3 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/.settings/.gitignore: -------------------------------------------------------------------------------- 1 | !/org.eclipse.jdt.core.prefs 2 | !/org.eclipse.jdt.ui.prefs 3 | /* -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | com.github.sylvainlaurent.jdbcperflogger 5 | jdbc-perf-logger 6 | 0.9.1-SNAPSHOT 7 | 8 | 9 | jdbc-perf-logger-gui 10 | jar 11 | 12 | jdbc-perf-logger-gui 13 | 14 | 15 | 16 | org.projectlombok 17 | lombok 18 | 1.18.12 19 | provided 20 | 21 | 22 | junit 23 | junit 24 | test 25 | 26 | 27 | com.github.sylvainlaurent.jdbcperflogger 28 | jdbc-perf-logger-agent 29 | 30 | provided 31 | 32 | 33 | com.github.sylvainlaurent.jdbcperflogger 34 | jdbc-perf-logger-driver 35 | 36 | 37 | org.slf4j 38 | slf4j-api 39 | 40 | 41 | org.slf4j 42 | slf4j-log4j12 43 | 44 | 45 | log4j 46 | log4j 47 | runtime 48 | 49 | 50 | com.h2database 51 | h2 52 | 53 | 54 | com.fifesoft 55 | rsyntaxtextarea 56 | 3.1.1 57 | 58 | 59 | org.eclipse.jdt 60 | org.eclipse.jdt.annotation 61 | provided 62 | 63 | 64 | org.mockito 65 | mockito-core 66 | test 67 | 68 | 69 | 70 | 71 | 72 | 73 | org.codehaus.mojo 74 | appassembler-maven-plugin 75 | 2.1.0 76 | 77 | 78 | 79 | ch.sla.jdbcperflogger.console.ui.PerfLoggerGuiMain 80 | jdbc-perf-logger-gui 81 | 82 | 20m 83 | 256m 84 | 85 | 86 | 87 | 88 | .sh 89 | 90 | flat 91 | lib 92 | true 93 | 94 | 95 | ${project.basedir}/src/main/appassembler/unixBinTemplate 96 | false 97 | 98 | 99 | 100 | package 101 | 102 | assemble 103 | 104 | 105 | 106 | 107 | 108 | maven-assembly-plugin 109 | 3.2.0 110 | 111 | 112 | 113 | src/main/assembly/distrib.xml 114 | 115 | 116 | 117 | 118 | package 119 | 120 | single 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/appassembler/unixBinTemplate: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | @LICENSE_HEADER@ 3 | 4 | # resolve links - $0 may be a softlink 5 | PRG="$0" 6 | 7 | while [ -h "$PRG" ]; do 8 | ls=`ls -ld "$PRG"` 9 | link=`expr "$ls" : '.*-> \(.*\)$'` 10 | if expr "$link" : '/.*' > /dev/null; then 11 | PRG="$link" 12 | else 13 | PRG=`dirname "$PRG"`/"$link" 14 | fi 15 | done 16 | 17 | PRGDIR=`dirname "$PRG"` 18 | BASEDIR=`cd "$PRGDIR/.." >/dev/null; pwd` 19 | cd "$BASEDIR" 20 | 21 | @ENV_SETUP@ 22 | 23 | # OS specific support. $var _must_ be set to either true or false. 24 | cygwin=false; 25 | darwin=false; 26 | case "`uname`" in 27 | CYGWIN*) cygwin=true ;; 28 | Darwin*) darwin=true 29 | if [ -z "$JAVA_VERSION" ] ; then 30 | JAVA_VERSION="CurrentJDK" 31 | else 32 | echo "Using Java version: $JAVA_VERSION" 33 | fi 34 | if [ -z "$JAVA_HOME" ]; then 35 | if [ -x "/usr/libexec/java_home" ]; then 36 | JAVA_HOME=`/usr/libexec/java_home` 37 | else 38 | JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/${JAVA_VERSION}/Home 39 | fi 40 | fi 41 | ;; 42 | esac 43 | 44 | if [ -z "$JAVA_HOME" ] ; then 45 | if [ -r /etc/gentoo-release ] ; then 46 | JAVA_HOME=`java-config --jre-home` 47 | fi 48 | fi 49 | 50 | # For Cygwin, ensure paths are in UNIX format before anything is touched 51 | if $cygwin ; then 52 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 53 | [ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 54 | fi 55 | 56 | # If a specific java binary isn't specified search for the standard 'java' binary 57 | if [ -z "$JAVACMD" ] ; then 58 | if [ -n "$JAVA_HOME" ] ; then 59 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 60 | # IBM's JDK on AIX uses strange locations for the executables 61 | JAVACMD="$JAVA_HOME/jre/sh/java" 62 | else 63 | JAVACMD="$JAVA_HOME/bin/java" 64 | fi 65 | else 66 | JAVACMD=`which java` 67 | fi 68 | fi 69 | 70 | if [ ! -x "$JAVACMD" ] ; then 71 | echo "Error: JAVA_HOME is not defined correctly." 1>&2 72 | echo " We cannot execute $JAVACMD" 1>&2 73 | exit 1 74 | fi 75 | 76 | if [ -z "$REPO" ] 77 | then 78 | REPO="$BASEDIR"/@REPO@ 79 | fi 80 | 81 | CLASSPATH=$CLASSPATH_PREFIX:@CLASSPATH@ 82 | 83 | # For Cygwin, switch paths to Windows format before running java 84 | if $cygwin; then 85 | [ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 86 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 87 | [ -n "$HOME" ] && HOME=`cygpath --path --windows "$HOME"` 88 | [ -n "$BASEDIR" ] && BASEDIR=`cygpath --path --windows "$BASEDIR"` 89 | [ -n "$REPO" ] && REPO=`cygpath --path --windows "$REPO"` 90 | fi 91 | 92 | exec "$JAVACMD" $JAVA_OPTS @EXTRA_JVM_ARGUMENTS@ \ 93 | -classpath "$CLASSPATH" \ 94 | -Dapp.name="@APP_NAME@" \ 95 | -Dapp.pid="$$" \ 96 | -Dapp.repo="$REPO" \ 97 | -Dapp.home="$BASEDIR" \ 98 | -Dbasedir="$BASEDIR" \ 99 | @MAINCLASS@ \ 100 | @APP_ARGUMENTS@"$@"@UNIX_BACKGROUND@ 101 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/assembly/distrib.xml: -------------------------------------------------------------------------------- 1 | 3 | distrib 4 | 5 | zip 6 | tar.gz 7 | 8 | 9 | 10 | target/appassembler 11 | . 12 | 13 | **/*.sh 14 | 15 | 16 | 17 | target/appassembler 18 | . 19 | 20 | **/*.sh 21 | 22 | 744 23 | 24 | 25 | ../ 26 | . 27 | 28 | README.md 29 | LICENSE.txt 30 | 3rdparty_license.txt 31 | 32 | 33 | 34 | ../jdbc-perf-logger-agent/target/ 35 | lib 36 | 37 | jdbc-perf-logger-agent-*.jar 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/config/example-jdbcperflogger.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | jdbc:oracle: 9 | oracle.jdbc.OracleDriver 10 | 11 | 12 | jdbc:h2: 13 | org.h2.Driver 14 | 15 | 16 | jdbc:hsqldb: 17 | org.hsqldb.jdbc.JDBCDriver 18 | 19 | 20 | jdbc:mysql: 21 | com.mysql.jdbc.Driver 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/config/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/db/DetailedViewStatementLog.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.console.db; 2 | 3 | import java.util.UUID; 4 | 5 | import lombok.Getter; 6 | import lombok.ToString; 7 | import org.eclipse.jdt.annotation.Nullable; 8 | 9 | import ch.sla.jdbcperflogger.StatementType; 10 | import ch.sla.jdbcperflogger.model.ConnectionInfo; 11 | 12 | @Getter 13 | @ToString 14 | public class DetailedViewStatementLog { 15 | private final UUID logId; 16 | private final long timestamp; 17 | @Nullable 18 | private final StatementType statementType; 19 | private final String rawSql; 20 | private final String filledSql; 21 | private final String threadName; 22 | @Nullable 23 | private final String sqlException; 24 | private final ConnectionInfo connectionInfo; 25 | 26 | public DetailedViewStatementLog(final UUID logId, final ConnectionInfo connectionInfo, final long timestamp, 27 | @Nullable final StatementType statementType, final String rawSql, final String filledSql, 28 | final String threadName, @Nullable final String exception) { 29 | this.logId = logId; 30 | this.connectionInfo = connectionInfo; 31 | this.timestamp = timestamp; 32 | this.statementType = statementType; 33 | this.rawSql = rawSql; 34 | this.filledSql = filledSql; 35 | this.threadName = threadName; 36 | sqlException = exception; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/db/LogRepositoryConstants.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.console.db; 2 | 3 | public class LogRepositoryConstants { 4 | 5 | public static final String AUTOCOMMIT_COLUMN = "AUTOCOMMIT"; 6 | public static final String AVG_EXEC_PLUS_RSET_USAGE_TIME_COLUMN = "AVG_EXEC_PLUS_RSET_USAGE_TIME"; 7 | public static final String BATCHED_STMT_ORDER = "BATCHED_STMT_ORDER"; 8 | public static final String CONNECTION_NUMBER_COLUMN = "connectionNumber"; 9 | public static final String ERROR_COLUMN = "ERROR"; 10 | public static final String EXEC_COUNT_COLUMN = "EXEC_COUNT"; 11 | public static final String EXEC_PLUS_RSET_USAGE_TIME = "EXEC_PLUS_RSET_USAGE_TIME"; 12 | public static final String EXEC_TIME_COLUMN = "execution_time"; 13 | public static final String FETCH_TIME_COLUMN = "fetch_time"; 14 | public static final String FILLED_SQL_COLUMN = "FILLEDSQL"; 15 | public static final String ID_COLUMN = "ID"; 16 | public static final String MAX_EXEC_PLUS_RSET_USAGE_TIME_COLUMN = "MAX_EXEC_PLUS_RSET_USAGE_TIME"; 17 | public static final String MIN_EXEC_PLUS_RSET_USAGE_TIME_COLUMN = "MIN_EXEC_PLUS_RSET_USAGE_TIME"; 18 | public static final String NB_ROWS_COLUMN = "NBROWS"; 19 | public static final String RAW_SQL_COLUMN = "RAWSQL"; 20 | public static final String RSET_USAGE_TIME = "RSET_USAGE_TIME"; 21 | public static final String STMT_TYPE_COLUMN = "STATEMENTTYPE"; 22 | public static final String THREAD_NAME_COLUMN = "threadName"; 23 | public static final String TIMEOUT_COLUMN = "TIMEOUT"; 24 | public static final String TOTAL_EXEC_PLUS_RSET_USAGE_TIME_COLUMN = "TOTAL_EXEC_PLUS_RSET_USAGE_TIME"; 25 | public static final String TRANSACTION_ISOLATION_COLUMN = "TRANSACTION_ISOLATION"; 26 | public static final String TSTAMP_COLUMN = "TSTAMP"; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/db/LogRepositoryRead.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.console.db; 2 | 3 | import java.util.UUID; 4 | 5 | import org.eclipse.jdt.annotation.Nullable; 6 | 7 | public interface LogRepositoryRead { 8 | 9 | void getStatements(LogSearchCriteria searchCriteria, ResultSetAnalyzer analyzer, boolean withFilledSql); 10 | 11 | void getStatementsGroupByRawSQL(LogSearchCriteria searchCriteria, ResultSetAnalyzer analyzer); 12 | 13 | void getStatementsGroupByFilledSQL(LogSearchCriteria searchCriteria, ResultSetAnalyzer analyzer); 14 | 15 | void getBatchStatementExecutions(UUID logId, ResultSetAnalyzer analyzer); 16 | 17 | @Nullable 18 | DetailedViewStatementLog getStatementLog(long id); 19 | 20 | int countStatements(); 21 | 22 | long getTotalExecAndFetchTimeNanos(); 23 | 24 | long getTotalExecAndFetchTimeNanos(LogSearchCriteria searchCriteria); 25 | 26 | void dispose(); 27 | } -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/db/LogRepositoryUpdate.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.console.db; 2 | 3 | import java.util.Collection; 4 | 5 | import org.eclipse.jdt.annotation.Nullable; 6 | 7 | import ch.sla.jdbcperflogger.model.BatchedNonPreparedStatementsLog; 8 | import ch.sla.jdbcperflogger.model.BatchedPreparedStatementsLog; 9 | import ch.sla.jdbcperflogger.model.ConnectionInfo; 10 | import ch.sla.jdbcperflogger.model.ResultSetLog; 11 | import ch.sla.jdbcperflogger.model.StatementExecutedLog; 12 | import ch.sla.jdbcperflogger.model.StatementLog; 13 | import ch.sla.jdbcperflogger.model.TxCompleteLog; 14 | 15 | public interface LogRepositoryUpdate { 16 | 17 | void addConnection(ConnectionInfo connectionInfo); 18 | 19 | void addStatementLog(StatementLog log); 20 | 21 | void addStatementFullyExecutedLog(final Collection logs); 22 | 23 | void updateLogAfterExecution(StatementExecutedLog log); 24 | 25 | void updateLogWithResultSetLog(ResultSetLog log); 26 | 27 | void addBatchedPreparedStatementsLog(BatchedPreparedStatementsLog log); 28 | 29 | void addBatchedNonPreparedStatementsLog(BatchedNonPreparedStatementsLog log); 30 | 31 | void addTxCompletionLog(final TxCompleteLog log); 32 | 33 | void clear(); 34 | 35 | void deleteStatementLog(final long... logIds); 36 | 37 | void dispose(); 38 | 39 | long getLastModificationTime(); 40 | 41 | void setLastLostMessageTime(@Nullable Long timestamp); 42 | 43 | @Nullable 44 | Long getLastLostMessageTime(); 45 | } -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/db/LogSearchCriteria.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.console.db; 2 | 3 | import org.eclipse.jdt.annotation.NonNullByDefault; 4 | 5 | @NonNullByDefault({}) 6 | public class LogSearchCriteria { 7 | private String filter; 8 | private Long minDurationNanos; 9 | private boolean removeTransactionCompletions; 10 | private String sqlPassThroughFilter; 11 | 12 | public String getFilter() { 13 | return filter; 14 | } 15 | 16 | public void setFilter(final String filter) { 17 | this.filter = filter; 18 | } 19 | 20 | public Long getMinDurationNanos() { 21 | return minDurationNanos; 22 | } 23 | 24 | public void setMinDurationNanos(final Long minDurationNanos) { 25 | this.minDurationNanos = minDurationNanos; 26 | } 27 | 28 | public boolean isRemoveTransactionCompletions() { 29 | return removeTransactionCompletions; 30 | } 31 | 32 | public void setRemoveTransactionCompletions(final boolean removeTransactionCompletions) { 33 | this.removeTransactionCompletions = removeTransactionCompletions; 34 | } 35 | 36 | public boolean atLeastOneFilterApplied() { 37 | return (filter != null && !filter.isEmpty()) || minDurationNanos != null || removeTransactionCompletions; 38 | } 39 | 40 | public String getSqlPassThroughFilter() { 41 | return sqlPassThroughFilter; 42 | } 43 | 44 | public void setSqlPassThroughFilter(final String sqlPassThroughFilter) { 45 | this.sqlPassThroughFilter = sqlPassThroughFilter; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/db/ResultSetAnalyzer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.console.db; 17 | 18 | import java.sql.ResultSet; 19 | import java.sql.SQLException; 20 | 21 | public interface ResultSetAnalyzer { 22 | void analyze(ResultSet resultSet) throws SQLException; 23 | } 24 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/db/StatementFullyExecutedLog.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.console.db; 2 | 3 | import java.util.UUID; 4 | 5 | import org.eclipse.jdt.annotation.Nullable; 6 | 7 | import ch.sla.jdbcperflogger.StatementType; 8 | import ch.sla.jdbcperflogger.model.ResultSetLog; 9 | import ch.sla.jdbcperflogger.model.StatementExecutedLog; 10 | import ch.sla.jdbcperflogger.model.StatementLog; 11 | 12 | public class StatementFullyExecutedLog { 13 | private final StatementLog statementLog; 14 | private final StatementExecutedLog statementExecutedLog; 15 | @Nullable 16 | private final ResultSetLog resultSetLog; 17 | 18 | public StatementFullyExecutedLog(final StatementLog statementLog, final StatementExecutedLog statementExecutedLog, 19 | @Nullable final ResultSetLog resultSetLog) { 20 | this.statementLog = statementLog; 21 | this.statementExecutedLog = statementExecutedLog; 22 | this.resultSetLog = resultSetLog; 23 | } 24 | 25 | public UUID getConnectionUuid() { 26 | return statementLog.getConnectionUuid(); 27 | } 28 | 29 | public UUID getLogId() { 30 | return statementLog.getLogId(); 31 | } 32 | 33 | public long getTimestamp() { 34 | return statementLog.getTimestamp(); 35 | } 36 | 37 | public StatementType getStatementType() { 38 | return statementLog.getStatementType(); 39 | } 40 | 41 | public String getThreadName() { 42 | return statementLog.getThreadName(); 43 | } 44 | 45 | public int getTimeout() { 46 | return statementLog.getTimeout(); 47 | } 48 | 49 | public boolean isAutoCommit() { 50 | return statementLog.isAutoCommit(); 51 | } 52 | 53 | public int getTransactionIsolation() { 54 | return statementLog.getTransactionIsolation(); 55 | } 56 | 57 | public String getRawSql() { 58 | return statementLog.getRawSql(); 59 | } 60 | 61 | public String getFilledSql() { 62 | return statementLog.getFilledSql(); 63 | } 64 | 65 | public boolean isPreparedStatement() { 66 | return statementLog.isPreparedStatement(); 67 | } 68 | 69 | public long getExecutionTimeNanos() { 70 | return statementExecutedLog.getExecutionTimeNanos(); 71 | } 72 | 73 | public long getExecutionPlusResultSetUsageTimeNanos() { 74 | return statementExecutedLog.getExecutionTimeNanos() + getResultSetUsageDurationNanosDefault0(); 75 | } 76 | 77 | @Nullable 78 | public Long getUpdateCount() { 79 | return statementExecutedLog.getUpdateCount(); 80 | } 81 | 82 | @Nullable 83 | public String getSqlException() { 84 | return statementExecutedLog.getSqlException(); 85 | } 86 | 87 | @Nullable 88 | public Long getResultSetUsageDurationNanos() { 89 | // extracted to local variable to make eclipse null-analysis happy... 90 | final ResultSetLog resultSetLog2 = resultSetLog; 91 | return resultSetLog2 != null ? resultSetLog2.getResultSetUsageDurationNanos() : null; 92 | } 93 | 94 | public long getResultSetUsageDurationNanosDefault0() { 95 | final Long nanos = getResultSetUsageDurationNanos(); 96 | if (nanos != null) { 97 | return nanos.longValue(); 98 | } else { 99 | return 0L; 100 | } 101 | } 102 | 103 | @Nullable 104 | public Long getFetchDurationNanos() { 105 | // extracted to local variable to make eclipse null-analysis happy... 106 | final ResultSetLog resultSetLog2 = resultSetLog; 107 | return resultSetLog2 != null ? resultSetLog2.getFetchDurationNanos() : null; 108 | } 109 | 110 | @Nullable 111 | public Integer getNbRowsIterated() { 112 | // extracted to local variable to make eclipse null-analysis happy... 113 | final ResultSetLog resultSetLog2 = resultSetLog; 114 | return resultSetLog2 != null ? resultSetLog2.getNbRowsIterated() : null; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/db/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @NonNullByDefault 17 | package ch.sla.jdbcperflogger.console.db; 18 | 19 | import org.eclipse.jdt.annotation.NonNullByDefault; 20 | 21 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/net/AbstractLogReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.console.net; 17 | 18 | import java.io.EOFException; 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.io.ObjectInputStream; 22 | import java.net.Socket; 23 | import java.net.SocketTimeoutException; 24 | 25 | import org.eclipse.jdt.annotation.Nullable; 26 | 27 | import org.slf4j.Logger; 28 | import org.slf4j.LoggerFactory; 29 | 30 | import ch.sla.jdbcperflogger.model.LogMessage; 31 | 32 | public abstract class AbstractLogReceiver extends Thread { 33 | private final static Logger LOGGER = LoggerFactory.getLogger(AbstractLogReceiver.class); 34 | 35 | // Visible for testing 36 | protected int SOCKET_TIMEOUT = 60 * 1000; 37 | 38 | protected volatile boolean connected; 39 | protected volatile boolean paused = false; 40 | protected volatile boolean disposed = false; 41 | @Nullable 42 | protected Throwable lastConnectionError; 43 | 44 | public AbstractLogReceiver() { 45 | this.setDaemon(true); 46 | } 47 | 48 | /** 49 | * @return the current number of connections with this log receiver 50 | */ 51 | public int getConnectionsCount() { 52 | return connected ? 1 : 0; 53 | } 54 | 55 | public void pauseReceivingLogs() { 56 | paused = true; 57 | } 58 | 59 | public void resumeReceivingLogs() { 60 | paused = false; 61 | } 62 | 63 | public boolean isPaused() { 64 | return paused; 65 | } 66 | 67 | public void dispose() { 68 | disposed = true; 69 | } 70 | 71 | protected void handleConnection(final Socket socket, final LogPersister logPersister) throws IOException { 72 | socket.setKeepAlive(true); 73 | socket.setSoTimeout(SOCKET_TIMEOUT); 74 | 75 | final InputStream is = socket.getInputStream(); 76 | 77 | try (ObjectInputStream ois = new ObjectInputStream(is)) { 78 | connected = true; 79 | while (!disposed) { 80 | Object o; 81 | try { 82 | o = ois.readObject(); 83 | } catch (final ClassNotFoundException e) { 84 | LOGGER.error( 85 | "unknown class, maybe the client is not compatible with the GUI? the msg will be skipped", 86 | e); 87 | continue; 88 | } catch (final EOFException e) { 89 | LOGGER.debug("The remote closed its connection"); 90 | lastConnectionError = e; 91 | break; 92 | } catch (final SocketTimeoutException e) { 93 | LOGGER.debug("timeout while reading socket"); 94 | lastConnectionError = e; 95 | continue; 96 | } 97 | if (o == null || paused || disposed) { 98 | continue; 99 | } 100 | 101 | logPersister.putMessage((LogMessage) o); 102 | 103 | } 104 | } finally { 105 | connected = false; 106 | LOGGER.debug("Closing socket " + socket); 107 | socket.close(); 108 | } 109 | 110 | } 111 | 112 | public abstract boolean isServerMode(); 113 | 114 | public @Nullable Throwable getLastConnectionError() { 115 | return lastConnectionError; 116 | } 117 | 118 | } -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/net/ClientLogReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.console.net; 17 | 18 | import java.io.IOException; 19 | import java.net.InetSocketAddress; 20 | import java.net.Socket; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | import ch.sla.jdbcperflogger.console.db.LogRepositoryUpdate; 27 | 28 | public class ClientLogReceiver extends AbstractLogReceiver { 29 | private final static Logger LOGGER = LoggerFactory.getLogger(ClientLogReceiver.class); 30 | 31 | final InetSocketAddress targetRemoteAddress; 32 | private final LogRepositoryUpdate logRepository; 33 | 34 | public ClientLogReceiver(final String targetHost, final int targetPort, final LogRepositoryUpdate logRepository) { 35 | targetRemoteAddress = InetSocketAddress.createUnresolved(targetHost, targetPort); 36 | this.logRepository = logRepository; 37 | } 38 | 39 | @Override 40 | public void run() { 41 | try (LogPersister logPersister = new LogPersister(logRepository)) { 42 | logPersister.start(); 43 | while (!disposed) { 44 | try { 45 | LOGGER.debug("Trying to connect to {}:{}", targetRemoteAddress.getHostName(), 46 | targetRemoteAddress, targetRemoteAddress.getPort()); 47 | final Socket socket = new Socket(targetRemoteAddress.getHostName(), targetRemoteAddress.getPort()); 48 | LOGGER.info("Connected to remote {}", targetRemoteAddress); 49 | handleConnection(socket, logPersister); 50 | } catch (final IOException e) { 51 | lastConnectionError = e; 52 | LOGGER.debug("expected error", e); 53 | } 54 | LOGGER.debug("Sleeping before trying to connect again to remote {}", targetRemoteAddress); 55 | try { 56 | Thread.sleep(TimeUnit.SECONDS.toMillis(10)); 57 | } catch (final InterruptedException ignored) { 58 | } 59 | 60 | } 61 | } 62 | 63 | } 64 | 65 | @Override 66 | public boolean isServerMode() { 67 | return false; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/net/LogPersister.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.console.net; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.concurrent.ArrayBlockingQueue; 6 | import java.util.concurrent.BlockingQueue; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | import org.eclipse.jdt.annotation.Nullable; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import ch.sla.jdbcperflogger.console.db.LogRepositoryUpdate; 14 | import ch.sla.jdbcperflogger.console.db.StatementFullyExecutedLog; 15 | import ch.sla.jdbcperflogger.model.BatchedNonPreparedStatementsLog; 16 | import ch.sla.jdbcperflogger.model.BatchedPreparedStatementsLog; 17 | import ch.sla.jdbcperflogger.model.BufferFullLogMessage; 18 | import ch.sla.jdbcperflogger.model.ConnectionInfo; 19 | import ch.sla.jdbcperflogger.model.LogMessage; 20 | import ch.sla.jdbcperflogger.model.ResultSetLog; 21 | import ch.sla.jdbcperflogger.model.StatementExecutedLog; 22 | import ch.sla.jdbcperflogger.model.StatementLog; 23 | import ch.sla.jdbcperflogger.model.TxCompleteLog; 24 | 25 | class LogPersister extends Thread implements AutoCloseable { 26 | final static Logger LOGGER = LoggerFactory.getLogger(LogPersister.class); 27 | 28 | private volatile boolean disposed = false; 29 | protected final LogRepositoryUpdate logRepository; 30 | private final BlockingQueue logs = new ArrayBlockingQueue<>(10000); 31 | 32 | LogPersister(final LogRepositoryUpdate logRepository) { 33 | this.logRepository = logRepository; 34 | this.setName("LogPersister"); 35 | } 36 | 37 | void putMessage(final LogMessage msg) { 38 | try { 39 | logs.put(msg); 40 | } catch (final InterruptedException e) { 41 | LOGGER.warn("interrupted", e); 42 | } 43 | } 44 | 45 | @Override 46 | public void close() { 47 | disposed = true; 48 | try { 49 | this.join(); 50 | } catch (final InterruptedException e) { 51 | LOGGER.error("error while waiting for LogPersister thread to finish", e); 52 | } 53 | } 54 | 55 | @Override 56 | public void run() { 57 | final List drainedLogs = new ArrayList<>(1000); 58 | final List statementFullyExecutedLogs = new ArrayList<>( 59 | 100); 60 | 61 | while (!disposed) { 62 | @Nullable 63 | LogMessage logMessage; 64 | try { 65 | logMessage = logs.poll(1, TimeUnit.SECONDS); 66 | } catch (final InterruptedException e) { 67 | LOGGER.warn("interrupted", e); 68 | continue; 69 | } 70 | 71 | if (logMessage == null) { 72 | continue; 73 | } 74 | drainedLogs.clear(); 75 | drainedLogs.add(logMessage); 76 | logs.drainTo(drainedLogs); 77 | 78 | for (int i = 0; i < drainedLogs.size(); i++) { 79 | logMessage = drainedLogs.get(i); 80 | if (i < drainedLogs.size() - 2 && logMessage instanceof StatementLog 81 | && drainedLogs.get(i + 1) instanceof StatementExecutedLog) { 82 | final StatementExecutedLog statementExecutedLog = (StatementExecutedLog) drainedLogs.get(i + 1); 83 | ResultSetLog resultSetLog = null; 84 | if (drainedLogs.get(i + 2) instanceof ResultSetLog) { 85 | resultSetLog = (ResultSetLog) drainedLogs.get(i + 2); 86 | } 87 | final StatementFullyExecutedLog statementFullyExecutedLog = new StatementFullyExecutedLog( 88 | (StatementLog) logMessage, statementExecutedLog, resultSetLog); 89 | statementFullyExecutedLogs.add(statementFullyExecutedLog); 90 | 91 | i += 1 + (resultSetLog != null ? 1 : 0); 92 | continue; 93 | } 94 | 95 | if (!statementFullyExecutedLogs.isEmpty()) { 96 | logRepository.addStatementFullyExecutedLog(statementFullyExecutedLogs); 97 | statementFullyExecutedLogs.clear(); 98 | } 99 | 100 | if (logMessage instanceof ConnectionInfo) { 101 | logRepository.addConnection((ConnectionInfo) logMessage); 102 | } else if (logMessage instanceof StatementLog) { 103 | logRepository.addStatementLog((StatementLog) logMessage); 104 | } else if (logMessage instanceof StatementExecutedLog) { 105 | logRepository.updateLogAfterExecution((StatementExecutedLog) logMessage); 106 | } else if (logMessage instanceof ResultSetLog) { 107 | logRepository.updateLogWithResultSetLog((ResultSetLog) logMessage); 108 | } else if (logMessage instanceof BatchedNonPreparedStatementsLog) { 109 | logRepository.addBatchedNonPreparedStatementsLog((BatchedNonPreparedStatementsLog) logMessage); 110 | } else if (logMessage instanceof BatchedPreparedStatementsLog) { 111 | logRepository.addBatchedPreparedStatementsLog((BatchedPreparedStatementsLog) logMessage); 112 | } else if (logMessage instanceof TxCompleteLog) { 113 | logRepository.addTxCompletionLog((TxCompleteLog) logMessage); 114 | } else if (logMessage instanceof BufferFullLogMessage) { 115 | logRepository.setLastLostMessageTime(((BufferFullLogMessage) logMessage).getTimestamp()); 116 | } else { 117 | throw new IllegalArgumentException("unexpected log, class=" + logMessage.getClass()); 118 | } 119 | } 120 | 121 | if (!statementFullyExecutedLogs.isEmpty()) { 122 | logRepository.addStatementFullyExecutedLog(statementFullyExecutedLogs); 123 | statementFullyExecutedLogs.clear(); 124 | } 125 | 126 | } 127 | } 128 | 129 | } -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/net/ServerLogReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.console.net; 17 | 18 | import java.io.IOException; 19 | import java.net.ServerSocket; 20 | import java.net.Socket; 21 | import java.net.SocketTimeoutException; 22 | import java.util.Set; 23 | import java.util.concurrent.CopyOnWriteArraySet; 24 | import java.util.concurrent.CountDownLatch; 25 | import java.util.concurrent.TimeUnit; 26 | 27 | import org.eclipse.jdt.annotation.NonNull; 28 | import org.eclipse.jdt.annotation.Nullable; 29 | 30 | import org.slf4j.Logger; 31 | import org.slf4j.LoggerFactory; 32 | 33 | import ch.sla.jdbcperflogger.console.db.LogRepositoryUpdate; 34 | 35 | public class ServerLogReceiver extends AbstractLogReceiver { 36 | final static Logger LOGGER = LoggerFactory.getLogger(ServerLogReceiver.class); 37 | 38 | private final Set childReceivers = new CopyOnWriteArraySet<>(); 39 | private final LogRepositoryUpdate logRepository; 40 | private int listenPort; 41 | @Nullable 42 | private volatile ServerSocket serverSocket; 43 | private final CountDownLatch serverStartedLatch = new CountDownLatch(1); 44 | 45 | public ServerLogReceiver(final int listenPort, final LogRepositoryUpdate logRepository) { 46 | this.listenPort = listenPort; 47 | this.logRepository = logRepository; 48 | } 49 | 50 | @Override 51 | public void dispose() { 52 | super.dispose(); 53 | try { 54 | final ServerSocket serverSocketLocalVar = serverSocket; 55 | if (serverSocketLocalVar != null) { 56 | serverSocketLocalVar.close(); 57 | } 58 | } catch (final IOException e) { 59 | LOGGER.error("error while closing socket", e); 60 | } 61 | try { 62 | this.join(); 63 | } catch (final InterruptedException e) { 64 | // ignore 65 | } 66 | } 67 | 68 | // visible for testing 69 | protected int getListenPort() { 70 | return listenPort; 71 | } 72 | 73 | @Override 74 | public int getConnectionsCount() { 75 | int cnt = 0; 76 | // it's thread-safe to iterate over childReceivers because it's a CopyOnWriteArraySet 77 | for (final AbstractLogReceiver receiver : childReceivers) { 78 | cnt += receiver.getConnectionsCount(); 79 | } 80 | return cnt; 81 | } 82 | 83 | @Override 84 | public void run() { 85 | try (ServerSocket serverSocketLocalVar = new ServerSocket(listenPort)) { 86 | // if listenPort is 0, a port has been chosen by the OS 87 | listenPort = serverSocketLocalVar.getLocalPort(); 88 | serverSocketLocalVar.setSoTimeout((int) TimeUnit.MINUTES.toMillis(5)); 89 | serverSocket = serverSocketLocalVar; 90 | this.setName("ServerLogReceiver " + listenPort); 91 | 92 | try (LogPersister logPersister = new LogPersister(logRepository)) { 93 | logPersister.start(); 94 | 95 | // signal threads that might be waiting for the server to be ready 96 | serverStartedLatch.countDown(); 97 | 98 | while (!disposed) { 99 | try { 100 | LOGGER.debug("Waiting for client connections on " + serverSocketLocalVar); 101 | @NonNull 102 | final Socket socket = serverSocketLocalVar.accept(); 103 | LOGGER.debug("Got client connection from " + socket); 104 | 105 | final AbstractLogReceiver logReceiver = new AbstractLogReceiver() { 106 | @Override 107 | public void run() { 108 | try { 109 | handleConnection(socket, logPersister); 110 | } catch (final IOException e) { 111 | LOGGER.error("error while receiving logs from " + socket.getRemoteSocketAddress(), 112 | e); 113 | } finally { 114 | childReceivers.remove(this); 115 | } 116 | } 117 | 118 | @Override 119 | public boolean isServerMode() { 120 | return true; 121 | } 122 | 123 | }; 124 | logReceiver.setName("LogReceiver " + socket.getRemoteSocketAddress()); 125 | if (isPaused()) { 126 | logReceiver.pauseReceivingLogs(); 127 | } 128 | childReceivers.add(logReceiver); 129 | logReceiver.start(); 130 | } catch (final SocketTimeoutException e) { 131 | LOGGER.debug("timeout while accepting socket", e); 132 | } catch (final IOException e) { 133 | if (!disposed) { 134 | LOGGER.error("error while accepting socket", e); 135 | } else { 136 | LOGGER.debug("error while accepting socket, the server has been closed", e); 137 | } 138 | } 139 | } 140 | } finally { 141 | LOGGER.debug("Closing server socket " + serverSocketLocalVar); 142 | if (!serverSocketLocalVar.isClosed()) { 143 | try { 144 | serverSocketLocalVar.close(); 145 | } catch (final IOException e) { 146 | LOGGER.error("error while closing socket", e); 147 | } 148 | } 149 | } 150 | } catch (final IOException e) { 151 | lastConnectionError = e; 152 | throw new RuntimeException(e); 153 | } 154 | 155 | } 156 | 157 | @Override 158 | public boolean isServerMode() { 159 | return true; 160 | } 161 | 162 | @Override 163 | public void pauseReceivingLogs() { 164 | super.pauseReceivingLogs(); 165 | for (final AbstractLogReceiver child : childReceivers) { 166 | child.pauseReceivingLogs(); 167 | } 168 | } 169 | 170 | @Override 171 | public void resumeReceivingLogs() { 172 | super.resumeReceivingLogs(); 173 | for (final AbstractLogReceiver child : childReceivers) { 174 | child.resumeReceivingLogs(); 175 | } 176 | } 177 | 178 | // visible for testing 179 | void waitUntilServerIsReady() throws InterruptedException { 180 | serverStartedLatch.await(); 181 | } 182 | 183 | } 184 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/net/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @NonNullByDefault 17 | package ch.sla.jdbcperflogger.console.net; 18 | 19 | import org.eclipse.jdt.annotation.NonNullByDefault; 20 | 21 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/ui/CustomTable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.console.ui; 17 | 18 | import ch.sla.jdbcperflogger.StatementType; 19 | import ch.sla.jdbcperflogger.console.db.LogRepositoryConstants; 20 | 21 | import org.eclipse.jdt.annotation.Nullable; 22 | import javax.swing.*; 23 | import javax.swing.table.JTableHeader; 24 | import javax.swing.table.TableCellRenderer; 25 | import java.awt.*; 26 | import java.awt.event.MouseEvent; 27 | import java.math.BigDecimal; 28 | 29 | public class CustomTable extends JTable { 30 | private static final long serialVersionUID = 1L; 31 | 32 | private static final Color ERROR_COLOR = Color.RED; 33 | private static final Color DEFAULT_BG_COLOR = Color.WHITE; 34 | private static final Color HIGHLIGHT_COLOR = Color.ORANGE; 35 | private static final Color COMMIT_COLOR = new Color(204, 255, 102); 36 | private static final Color ROLLBACK_COLOR = Color.PINK; 37 | 38 | @Nullable 39 | private String txtToHighlightUpper; 40 | @Nullable 41 | private Long minDurationNanoToHighlight; 42 | 43 | CustomTable(final ResultSetDataModel tm) { 44 | super(tm); 45 | } 46 | 47 | // Implement table header tool tips. 48 | @Override 49 | protected JTableHeader createDefaultTableHeader() { 50 | return new JTableHeader(columnModel) { 51 | private static final long serialVersionUID = 1L; 52 | 53 | @Override 54 | public String getToolTipText(@Nullable final MouseEvent e) { 55 | assert e != null; 56 | final java.awt.Point p = e.getPoint(); 57 | final int index = columnModel.getColumnIndexAtX(p.x); 58 | if (index >= 0) { 59 | return columnModel.getColumn(index).getHeaderValue().toString(); 60 | } else { 61 | return ""; 62 | } 63 | } 64 | }; 65 | } 66 | 67 | @Override 68 | public Component prepareRenderer(@Nullable final TableCellRenderer renderer, final int row, final int column) { 69 | assert renderer != null; 70 | final Component component = super.prepareRenderer(renderer, row, column); 71 | 72 | if (!this.getSelectionModel().isSelectedIndex(row)) { 73 | final ResultSetDataModel model = (ResultSetDataModel) getModel(); 74 | final int modelIndex = convertRowIndexToModel(row); 75 | 76 | Color bgColor = DEFAULT_BG_COLOR; 77 | final StatementType statementType = (StatementType) model.getValueAt(modelIndex, 78 | LogRepositoryConstants.STMT_TYPE_COLUMN); 79 | final String sql = (String) model.getValueAt(modelIndex, LogRepositoryConstants.RAW_SQL_COLUMN); 80 | 81 | if (statementType == StatementType.TRANSACTION && sql != null) { 82 | if (sql.contains("COMMIT")) { 83 | bgColor = COMMIT_COLOR; 84 | } else if (sql.contains("ROLLBACK")) { 85 | bgColor = ROLLBACK_COLOR; 86 | } 87 | } 88 | final Integer error = (Integer) model.getValueAt(modelIndex, LogRepositoryConstants.ERROR_COLUMN); 89 | if (error != null && error.intValue() != 0) { 90 | bgColor = ERROR_COLOR; 91 | } else if (txtToHighlightUpper != null) { 92 | if (sql != null && sql.toUpperCase().contains(txtToHighlightUpper)) { 93 | bgColor = HIGHLIGHT_COLOR; 94 | } 95 | } else { 96 | final Long minDurationNanoToHighlight2 = minDurationNanoToHighlight; 97 | if (minDurationNanoToHighlight2 != null) { 98 | Long duration = (Long) model.getValueAt(modelIndex, 99 | LogRepositoryConstants.EXEC_PLUS_RSET_USAGE_TIME); 100 | if (duration == null) { 101 | // in case we are in group by mode 102 | final BigDecimal val = (BigDecimal) model.getValueAt(modelIndex, 103 | LogRepositoryConstants.TOTAL_EXEC_PLUS_RSET_USAGE_TIME_COLUMN); 104 | if (val != null) { 105 | duration = val.longValue(); 106 | } 107 | } 108 | if (duration != null && duration.longValue() >= minDurationNanoToHighlight2.longValue()) { 109 | bgColor = HIGHLIGHT_COLOR; 110 | } 111 | } 112 | } 113 | component.setBackground(bgColor); 114 | } 115 | return component; 116 | } 117 | 118 | public void setTxtToHighlight(@Nullable final String txtToHighlight) { 119 | if (txtToHighlight != null) { 120 | txtToHighlightUpper = txtToHighlight.toUpperCase(); 121 | } else { 122 | txtToHighlightUpper = null; 123 | } 124 | } 125 | 126 | public void setMinDurationNanoToHighlight(@Nullable final Long minDurationNanoToHighlight) { 127 | this.minDurationNanoToHighlight = minDurationNanoToHighlight; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/ui/CustomTableCellRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.console.ui; 17 | 18 | import java.awt.Color; 19 | import java.awt.Component; 20 | import java.util.function.BiFunction; 21 | import java.util.function.Function; 22 | 23 | import org.eclipse.jdt.annotation.Nullable; 24 | import javax.swing.JTable; 25 | import javax.swing.table.DefaultTableCellRenderer; 26 | 27 | import ch.sla.jdbcperflogger.StatementType; 28 | import ch.sla.jdbcperflogger.console.db.LogRepositoryConstants; 29 | 30 | import static java.sql.Connection.*; 31 | 32 | @SuppressWarnings("serial") 33 | public class CustomTableCellRenderer extends DefaultTableCellRenderer { 34 | 35 | @Override 36 | public Component getTableCellRendererComponent(@Nullable final JTable table, @Nullable final Object value, 37 | final boolean isSelected, final boolean hasFocus, final int row, final int column) { 38 | assert table != null; 39 | 40 | final CustomTableCellRenderer component = (CustomTableCellRenderer) super.getTableCellRendererComponent(table, 41 | value, isSelected, hasFocus, row, column); 42 | 43 | final int columnModelIndex = table.convertColumnIndexToModel(column); 44 | 45 | final ResultSetDataModel dataModel = (ResultSetDataModel) table.getModel(); 46 | if (LogRepositoryConstants.STMT_TYPE_COLUMN.equals(dataModel.getColumnName(columnModelIndex))) { 47 | final int modelRowIndex = table.convertRowIndexToModel(row); 48 | final StatementType statementType = (StatementType) dataModel.getValueAt(modelRowIndex, columnModelIndex); 49 | assert statementType != null; 50 | switch (statementType) { 51 | case BASE_NON_PREPARED_STMT: 52 | component.setForeground(Color.ORANGE); 53 | component.setText("S"); 54 | break; 55 | case NON_PREPARED_QUERY_STMT: 56 | component.setForeground(Color.RED); 57 | component.setText("Q"); 58 | break; 59 | case NON_PREPARED_BATCH_EXECUTION: 60 | component.setForeground(Color.MAGENTA); 61 | component.setText("B"); 62 | break; 63 | case PREPARED_BATCH_EXECUTION: 64 | component.setForeground(Color.BLUE); 65 | component.setText("PB"); 66 | break; 67 | case BASE_PREPARED_STMT: 68 | component.setForeground(Color.CYAN); 69 | component.setText("PS"); 70 | break; 71 | case PREPARED_QUERY_STMT: 72 | component.setForeground(Color.GREEN); 73 | component.setText("PQ"); 74 | break; 75 | case TRANSACTION: 76 | component.setForeground(Color.DARK_GRAY); 77 | component.setText("TX"); 78 | break; 79 | } 80 | 81 | component.setToolTipText(value + " (use STATEMENTTYPE=" + statementType.getId() 82 | + " in the \"Advanced filter\")"); 83 | } else if (LogRepositoryConstants.TRANSACTION_ISOLATION_COLUMN.equals(dataModel.getColumnName(columnModelIndex))) { 84 | final int modelRowIndex = table.convertRowIndexToModel(row); 85 | final Integer transactionIsolation = (Integer) dataModel.getValueAt(modelRowIndex, columnModelIndex); 86 | if (transactionIsolation != null ) { 87 | final Function toDescription = text -> text + " (" + transactionIsolation + ")"; 88 | switch (transactionIsolation) { 89 | case TRANSACTION_READ_UNCOMMITTED: 90 | component.setText("RU"); 91 | component.setToolTipText(toDescription.apply("Read Uncommitted")); 92 | break; 93 | case TRANSACTION_READ_COMMITTED: 94 | component.setText("RC"); 95 | component.setToolTipText(toDescription.apply("Read Committed")); 96 | break; 97 | case TRANSACTION_REPEATABLE_READ: 98 | component.setText("RR"); 99 | component.setToolTipText(toDescription.apply("Repeatable Read")); 100 | break; 101 | case TRANSACTION_SERIALIZABLE: 102 | component.setText("SE"); 103 | component.setToolTipText(toDescription.apply("Serializable")); 104 | break; 105 | } 106 | } 107 | 108 | } else if (value != null) { 109 | component.setToolTipText(value.toString()); 110 | } 111 | 112 | return component; 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/ui/CustomTableRowSorter.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.console.ui; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import javax.swing.SortOrder; 7 | import javax.swing.table.TableRowSorter; 8 | 9 | import ch.sla.jdbcperflogger.console.db.LogRepositoryConstants; 10 | 11 | /** 12 | * Custom {@link TableRowSorter} that allows to sort on statement ID when the user wants to sort on the timestamp. This 13 | * allows to have a correct order even if 2 statements occurred at the same millisecond. 14 | * 15 | * @author slaurent 16 | * 17 | */ 18 | public class CustomTableRowSorter extends TableRowSorter { 19 | public CustomTableRowSorter(final ResultSetDataModel datamodel) { 20 | super(datamodel); 21 | } 22 | 23 | @Override 24 | public void toggleSortOrder(final int column) { 25 | if (LogRepositoryConstants.TSTAMP_COLUMN.equals(getModel().getColumnName(column))) { 26 | // sort on ID instead 27 | setMaxSortKeys(2); 28 | final List keys = new ArrayList<>(getSortKeys()); 29 | if (keys.size() <= 1) { 30 | keys.clear(); 31 | keys.add(new SortKey(column, SortOrder.DESCENDING)); 32 | keys.add(new SortKey(0, SortOrder.DESCENDING)); 33 | } else { 34 | final SortOrder previousOrder = keys.get(0).getSortOrder(); 35 | final SortOrder newOrder = previousOrder == SortOrder.ASCENDING ? SortOrder.DESCENDING 36 | : SortOrder.ASCENDING; 37 | keys.set(0, new SortKey(column, newOrder)); 38 | keys.set(1, new SortKey(0, newOrder)); 39 | } 40 | setSortKeys(keys); 41 | } else { 42 | setMaxSortKeys(1); 43 | super.toggleSortOrder(column); 44 | } 45 | 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/ui/GuiUtils.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.console.ui; 2 | 3 | import java.awt.Desktop; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.net.URI; 7 | import java.net.URISyntaxException; 8 | import java.util.Properties; 9 | 10 | import org.eclipse.jdt.annotation.Nullable; 11 | 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | public class GuiUtils { 16 | private final static Logger LOGGER = LoggerFactory.getLogger(WelcomePanel.class); 17 | 18 | static String getAppVersion() { 19 | final Properties mavenProps = new Properties(); 20 | try (@Nullable 21 | InputStream pomPropsFile = WelcomePanel.class 22 | .getResourceAsStream("/META-INF/maven/com.github.sylvainlaurent.jdbcperflogger/jdbc-perf-logger-gui/pom.properties")) { 23 | if (pomPropsFile != null) { 24 | mavenProps.load(pomPropsFile); 25 | } 26 | return mavenProps.getProperty("version"); 27 | } catch (final IOException e) { 28 | LOGGER.warn("", e); 29 | return "unknown"; 30 | } 31 | } 32 | 33 | static void openWebSite(final String address) { 34 | if (Desktop.isDesktopSupported()) { 35 | try { 36 | final URI uri = new URI(address); 37 | Desktop.getDesktop().browse(uri); 38 | } catch (URISyntaxException | IOException e) { 39 | throw new RuntimeException("unexpected", e); 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/ui/HostPort.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.console.ui; 2 | 3 | import org.eclipse.jdt.annotation.Nullable; 4 | 5 | public class HostPort implements Comparable { 6 | private final String host; 7 | private final int port; 8 | 9 | public HostPort(final String host, final int port) { 10 | this.host = host; 11 | this.port = port; 12 | } 13 | 14 | public String getHost() { 15 | return host; 16 | } 17 | 18 | public int getPort() { 19 | return port; 20 | } 21 | 22 | @Override 23 | public String toString() { 24 | return host + ":" + port; 25 | } 26 | 27 | @Override 28 | public int compareTo( final HostPort o) { 29 | int cmp = host.compareToIgnoreCase(o.host); 30 | if (cmp != 0) { 31 | return cmp; 32 | } 33 | cmp = port - o.port; 34 | return cmp; 35 | } 36 | 37 | @Override 38 | public int hashCode() { 39 | final int prime = 31; 40 | int result = 1; 41 | result = prime * result + host.hashCode(); 42 | result = prime * result + port; 43 | return result; 44 | } 45 | 46 | @Override 47 | public boolean equals(@Nullable final Object obj) { 48 | if (this == obj) { 49 | return true; 50 | } 51 | if (obj == null) { 52 | return false; 53 | } 54 | if (getClass() != obj.getClass()) { 55 | return false; 56 | } 57 | final HostPort other = (HostPort) obj; 58 | return compareTo(other) == 0; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/ui/IClientConnectionDelegate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.console.ui; 17 | 18 | public interface IClientConnectionDelegate { 19 | void createClientConnection(String host, int port); 20 | 21 | void close(PerfLoggerController perLoggercController); 22 | } 23 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/ui/PerfLoggerGuiMain.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.console.ui; 17 | 18 | import java.awt.Dimension; 19 | import java.awt.Frame; 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | import java.util.prefs.BackingStoreException; 23 | import java.util.prefs.Preferences; 24 | 25 | import org.eclipse.jdt.annotation.Nullable; 26 | import javax.swing.JPanel; 27 | import javax.swing.SwingUtilities; 28 | import javax.swing.ToolTipManager; 29 | import javax.swing.UIManager; 30 | import javax.swing.UnsupportedLookAndFeelException; 31 | 32 | import org.slf4j.Logger; 33 | import org.slf4j.LoggerFactory; 34 | 35 | import ch.sla.jdbcperflogger.console.db.LogRepositoryRead; 36 | import ch.sla.jdbcperflogger.console.db.LogRepositoryReadJdbc; 37 | import ch.sla.jdbcperflogger.console.db.LogRepositoryUpdate; 38 | import ch.sla.jdbcperflogger.console.db.LogRepositoryUpdateJdbc; 39 | import ch.sla.jdbcperflogger.console.net.AbstractLogReceiver; 40 | import ch.sla.jdbcperflogger.console.net.ClientLogReceiver; 41 | import ch.sla.jdbcperflogger.console.net.ServerLogReceiver; 42 | 43 | public class PerfLoggerGuiMain implements IClientConnectionDelegate { 44 | private static final String LOOK_AND_FEEL_CLASS_NAME_PREF_KEY = "lookAndFeelClassName"; 45 | 46 | private final static Logger LOGGER = LoggerFactory.getLogger(PerfLoggerGuiMain.class); 47 | 48 | private final PerfLoggerGuiMainFrame frmJdbcPerformanceLogger; 49 | 50 | private final Map connectionsToLogController = new HashMap<>(); 51 | private static final Preferences prefs = Preferences.userNodeForPackage(PerfLoggerGuiMain.class); 52 | 53 | /** 54 | * Launch the application. 55 | */ 56 | public static void main(final String[] args) { 57 | LOGGER.debug("PerfLoggerGuiMain starting..."); 58 | SwingUtilities.invokeLater(() -> { 59 | installLookAndFeel(); 60 | 61 | try { 62 | final PerfLoggerGuiMain window = new PerfLoggerGuiMain(); 63 | window.frmJdbcPerformanceLogger.setVisible(true); 64 | } catch (final Exception e) { 65 | e.printStackTrace(); 66 | } 67 | }); 68 | } 69 | 70 | private static void installLookAndFeel() { 71 | final String lfClassName = getPreferredLookAndFeel(); 72 | try { 73 | if (lfClassName != null) { 74 | UIManager.setLookAndFeel(lfClassName); 75 | } else { 76 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 77 | } 78 | } catch (ClassNotFoundException | InstantiationException | IllegalAccessException 79 | | UnsupportedLookAndFeelException e) { 80 | LOGGER.warn("Error setting LookAndFeel", e); 81 | } 82 | } 83 | 84 | @Nullable 85 | static String getPreferredLookAndFeel() { 86 | return prefs.get(LOOK_AND_FEEL_CLASS_NAME_PREF_KEY, null); 87 | } 88 | 89 | static void savePreferredLookAndFeel(final @Nullable String lfClassName) { 90 | if (lfClassName == null) { 91 | prefs.remove(LOOK_AND_FEEL_CLASS_NAME_PREF_KEY); 92 | } else { 93 | prefs.put(LOOK_AND_FEEL_CLASS_NAME_PREF_KEY, lfClassName); 94 | } 95 | try { 96 | prefs.sync(); 97 | } catch (final BackingStoreException e) { 98 | throw new IllegalArgumentException(e); 99 | } 100 | } 101 | 102 | /** 103 | * Create the application. 104 | */ 105 | public PerfLoggerGuiMain() { 106 | ToolTipManager.sharedInstance().setInitialDelay(500); 107 | 108 | frmJdbcPerformanceLogger = new PerfLoggerGuiMainFrame(); 109 | 110 | final JPanel welcomePanel = new WelcomePanel(this); 111 | frmJdbcPerformanceLogger.addTab("Welcome", welcomePanel); 112 | 113 | // TODO make server port configurable 114 | final PerfLoggerController serverPerfLoggerController = createServer(4561); 115 | frmJdbcPerformanceLogger.addTab("*:4561", serverPerfLoggerController.getPanel()); 116 | 117 | frmJdbcPerformanceLogger.pack(); 118 | frmJdbcPerformanceLogger.setExtendedState(Frame.MAXIMIZED_BOTH); 119 | frmJdbcPerformanceLogger.setMinimumSize(new Dimension(600, 500)); 120 | 121 | } 122 | 123 | @Override 124 | public void createClientConnection(final String host, final int port) { 125 | final PerfLoggerController clientPerfLoggerController = connectToClient(host, port); 126 | frmJdbcPerformanceLogger.addTab(host + ":" + port, clientPerfLoggerController.getPanel()); 127 | } 128 | 129 | @Override 130 | public void close(final PerfLoggerController perfLoggerController) { 131 | frmJdbcPerformanceLogger.removeTab(perfLoggerController.getPanel()); 132 | connectionsToLogController.entrySet().removeIf(entry -> entry.getValue() == perfLoggerController); 133 | } 134 | 135 | private PerfLoggerController connectToClient(final String targetHost, final int targetPort) { 136 | final String hostAndPort = targetHost + "_" + targetPort; 137 | PerfLoggerController perfLoggerController = connectionsToLogController.get(hostAndPort); 138 | if (perfLoggerController == null) { 139 | final LogRepositoryUpdate logRepositoryUpdate = new LogRepositoryUpdateJdbc(hostAndPort); 140 | final LogRepositoryRead logRepositoryRead = new LogRepositoryReadJdbc(hostAndPort); 141 | final AbstractLogReceiver logReceiver = new ClientLogReceiver(targetHost, targetPort, logRepositoryUpdate); 142 | logReceiver.start(); 143 | 144 | perfLoggerController = new PerfLoggerController(this, logReceiver, logRepositoryUpdate, logRepositoryRead); 145 | connectionsToLogController.put(hostAndPort, perfLoggerController); 146 | } 147 | return perfLoggerController; 148 | } 149 | 150 | private PerfLoggerController createServer(final int listeningPort) { 151 | final LogRepositoryUpdate logRepositoryUpdate = new LogRepositoryUpdateJdbc("server_" + listeningPort); 152 | final LogRepositoryRead logRepositoryRead = new LogRepositoryReadJdbc("server_" + listeningPort); 153 | 154 | final AbstractLogReceiver logReceiver = new ServerLogReceiver(listeningPort, logRepositoryUpdate); 155 | logReceiver.start(); 156 | 157 | return new PerfLoggerController(this, logReceiver, logRepositoryUpdate, logRepositoryRead); 158 | } 159 | 160 | } 161 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/ui/PerfLoggerGuiMainFrame.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.console.ui; 17 | 18 | import java.awt.BorderLayout; 19 | import java.awt.Component; 20 | 21 | import javax.swing.*; 22 | 23 | @SuppressWarnings("serial") 24 | public class PerfLoggerGuiMainFrame extends JFrame { 25 | 26 | private final JTabbedPane tabbedPane; 27 | 28 | /** 29 | * Create the frame. 30 | */ 31 | public PerfLoggerGuiMainFrame() { 32 | this.setTitle("JDBC Performance Logger"); 33 | // TODO handle clean exit 34 | this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 35 | 36 | tabbedPane = new JTabbedPane(SwingConstants.TOP); 37 | this.getContentPane().add(tabbedPane, BorderLayout.CENTER); 38 | 39 | this.pack(); 40 | // this.setMinimumSize(this.getSize()); 41 | // this.setBounds(100, 100, 900, 600); 42 | } 43 | 44 | void addTab(final String tabName, final Component tab) { 45 | tabbedPane.add(tabName, tab); 46 | tabbedPane.setSelectedComponent(tab); 47 | } 48 | 49 | void removeTab(final Component tab) { 50 | tabbedPane.remove(tab); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/ui/ResultSetDataModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.console.ui; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | import org.eclipse.jdt.annotation.Nullable; 23 | import javax.swing.table.AbstractTableModel; 24 | 25 | import ch.sla.jdbcperflogger.StatementType; 26 | import ch.sla.jdbcperflogger.console.db.LogRepositoryConstants; 27 | 28 | class ResultSetDataModel extends AbstractTableModel { 29 | 30 | private static final long serialVersionUID = 1L; 31 | private List columnNames = new ArrayList<>(); 32 | private List> columnTypes = new ArrayList<>(); 33 | private List rows = new ArrayList<>(); 34 | 35 | /** 36 | * Must be called in EDT 37 | * 38 | * @param rows 39 | * @param columnNames 40 | * @param columnTypes 41 | */ 42 | void setNewData(final List rows, final List columnNames, final List> columnTypes) { 43 | final boolean columnsChanged = !this.columnNames.equals(columnNames); 44 | if (columnsChanged) { 45 | // fireTableStructureChanged twice to force the clearing of the current selection 46 | ResultSetDataModel.this.fireTableStructureChanged(); 47 | } 48 | 49 | this.rows = rows; 50 | this.columnNames = columnNames; 51 | this.columnTypes = columnTypes; 52 | 53 | if (columnsChanged) { 54 | ResultSetDataModel.this.fireTableStructureChanged(); 55 | } else { 56 | ResultSetDataModel.this.fireTableDataChanged(); 57 | } 58 | } 59 | 60 | @Override 61 | public int getRowCount() { 62 | return rows.size(); 63 | } 64 | 65 | @Override 66 | public int getColumnCount() { 67 | return columnNames.size(); 68 | } 69 | 70 | @Override 71 | @Nullable 72 | public Object getValueAt(final int rowIndex, final int columnIndex) { 73 | Object o = rows.get(rowIndex)[columnIndex]; 74 | if (o == null) { 75 | return null; 76 | } 77 | final String col = getColumnName(columnIndex); 78 | if (LogRepositoryConstants.STMT_TYPE_COLUMN.equals(col)) { 79 | o = StatementType.fromId(((Byte) o).byteValue()); 80 | } else if (col.endsWith("TIME")) { 81 | // all time retrieved from the DB is in ns, we just convert it for display to have the best perf 82 | o = TimeUnit.NANOSECONDS.toMillis(((Number) o).longValue()); 83 | } 84 | return o; 85 | 86 | } 87 | 88 | @Nullable 89 | public Object getRawValueAt(final int rowIndex, final int columnIndex) { 90 | return rows.get(rowIndex)[columnIndex]; 91 | } 92 | 93 | @Override 94 | public String getColumnName(final int columnIndex) { 95 | return columnNames.get(columnIndex); 96 | } 97 | 98 | @Override 99 | public Class getColumnClass(final int columnIndex) { 100 | return columnTypes.get(columnIndex); 101 | } 102 | 103 | public long getIdAtRow(final int rowIndex) { 104 | return (Long) rows.get(rowIndex)[0]; 105 | } 106 | 107 | @Nullable 108 | public Object getValueAt(final int rowIndex, final String columnName) { 109 | final int columnIndex = columnNames.indexOf(columnName); 110 | if (columnIndex < 0) { 111 | return null; 112 | } 113 | Object o = rows.get(rowIndex)[columnIndex]; 114 | if (LogRepositoryConstants.STMT_TYPE_COLUMN.equals(columnName)) { 115 | o = StatementType.fromId(((Byte) o).byteValue()); 116 | } 117 | return o; 118 | } 119 | } -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/ui/StatementTimestampTableCellRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ch.sla.jdbcperflogger.console.ui; 17 | 18 | import java.awt.Component; 19 | import java.sql.Timestamp; 20 | import java.text.SimpleDateFormat; 21 | 22 | import org.eclipse.jdt.annotation.Nullable; 23 | import javax.swing.JTable; 24 | import javax.swing.table.DefaultTableCellRenderer; 25 | 26 | import ch.sla.jdbcperflogger.console.db.LogRepositoryConstants; 27 | 28 | public class StatementTimestampTableCellRenderer extends DefaultTableCellRenderer { 29 | 30 | private static final long serialVersionUID = 1L; 31 | private static final SimpleDateFormat tstampFormat = new SimpleDateFormat(/* "yyyy-MM-dd "+ */"HH:mm:ss.SSS"); 32 | private long deltaTimestampBaseMillis; 33 | 34 | @Override 35 | public Component getTableCellRendererComponent(@Nullable final JTable table, @Nullable final Object value, 36 | final boolean isSelected, final boolean hasFocus, final int row, final int column) { 37 | assert table != null; 38 | 39 | final StatementTimestampTableCellRenderer component = (StatementTimestampTableCellRenderer) super 40 | .getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 41 | 42 | final ResultSetDataModel dataModel = (ResultSetDataModel) table.getModel(); 43 | if (value != null && LogRepositoryConstants.TSTAMP_COLUMN.equals(dataModel.getColumnName(column))) { 44 | final Timestamp tstamp = (Timestamp) value; 45 | String str = tstampFormat.format(tstamp); 46 | if (deltaTimestampBaseMillis != 0) { 47 | str += " ("; 48 | final long delta = tstamp.getTime() - deltaTimestampBaseMillis; 49 | if (delta > 0) { 50 | str += "+"; 51 | } 52 | str += delta + "ms)"; 53 | } 54 | component.setText(str); 55 | } 56 | if (value != null) { 57 | component.setToolTipText(value.toString()); 58 | } 59 | 60 | return component; 61 | } 62 | 63 | public void setDeltaTimestampBaseMillis(final long deltaTimestampBaseMillis) { 64 | this.deltaTimestampBaseMillis = deltaTimestampBaseMillis; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/java/ch/sla/jdbcperflogger/console/ui/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Sylvain LAURENT 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @NonNullByDefault 17 | package ch.sla.jdbcperflogger.console.ui; 18 | 19 | import org.eclipse.jdt.annotation.NonNullByDefault; 20 | 21 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/resources/.gitignore: -------------------------------------------------------------------------------- 1 | /log4j.xml 2 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/resources/icons/32px-Edit-clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sylvainlaurent/JDBC-Performance-Logger/6c6b34c2e75a3b7ab7a815cbe982b83c417e255e/jdbc-perf-logger-gui/src/main/resources/icons/32px-Edit-clear.png -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/resources/icons/32px-Edit-copy_purple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sylvainlaurent/JDBC-Performance-Logger/6c6b34c2e75a3b7ab7a815cbe982b83c417e255e/jdbc-perf-logger-gui/src/main/resources/icons/32px-Edit-copy_purple.png -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/resources/icons/32px-Media-playback-pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sylvainlaurent/JDBC-Performance-Logger/6c6b34c2e75a3b7ab7a815cbe982b83c417e255e/jdbc-perf-logger-gui/src/main/resources/icons/32px-Media-playback-pause.png -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/resources/icons/32px-Media-record.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sylvainlaurent/JDBC-Performance-Logger/6c6b34c2e75a3b7ab7a815cbe982b83c417e255e/jdbc-perf-logger-gui/src/main/resources/icons/32px-Media-record.png -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/resources/icons/forkme_right_green_007200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sylvainlaurent/JDBC-Performance-Logger/6c6b34c2e75a3b7ab7a815cbe982b83c417e255e/jdbc-perf-logger-gui/src/main/resources/icons/forkme_right_green_007200.png -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/resources/icons/network-offline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sylvainlaurent/JDBC-Performance-Logger/6c6b34c2e75a3b7ab7a815cbe982b83c417e255e/jdbc-perf-logger-gui/src/main/resources/icons/network-offline.png -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/resources/icons/network-transmit-receive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sylvainlaurent/JDBC-Performance-Logger/6c6b34c2e75a3b7ab7a815cbe982b83c417e255e/jdbc-perf-logger-gui/src/main/resources/icons/network-transmit-receive.png -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/main/resources/initdb.sql: -------------------------------------------------------------------------------- 1 | set log 0; 2 | SET LOCK_MODE 0; 3 | SET UNDO_LOG 0; 4 | 5 | -- drop everything to be sure it works even if the schema changed... 6 | --drop all objects; 7 | 8 | create table if not exists connection_info 9 | (id identity, connectionId UUID not null, connectionNumber int not null, 10 | url varchar not null, creationDate timestamp not null, connectionCreationDurationNanos bigInt, connectionProperties other); 11 | 12 | create table if not exists statement_log 13 | (id identity, connectionId UUID not null, logId UUID not null, tstamp timestamp not null, statementType tinyInt not null, 14 | rawSql varchar not null, filledSql varchar not null, 15 | executionDurationNanos bigInt, fetchDurationNanos bigInt, rsetUsageDurationNanos bigInt, nbRows int, 16 | threadName varchar, exception varchar, timeout int, autoCommit boolean, transaction_Isolation int); 17 | 18 | create index if not exists idx_logId on statement_log(logId); 19 | create index if not exists idx_duration on statement_log(executionDurationNanos desc); 20 | create index if not exists idx_rawSql on statement_log(rawSql); 21 | create index if not exists idx_tstamp on statement_log(tstamp); 22 | create index if not exists idx_tstamp_desc on statement_log(tstamp desc); 23 | 24 | create table if not exists batched_statement_log 25 | (id identity, logId UUID not null, batched_stmt_order int not null, filledSql varchar not null); 26 | 27 | create index if not exists idx_batched_logId on batched_statement_log(logId); 28 | 29 | create or replace view v_statement_log 30 | (id, tstamp, statementType, rawSql, filledSql, EXEC_PLUS_RSET_USAGE_TIME, execution_time, fetch_time, RSET_USAGE_TIME, nbRows, threadName, timeout, autoCommit, transaction_Isolation, error, connectionNumber) 31 | as select statement_log.id, statement_log.tstamp, statement_log.statementType, statement_log.rawSql, statement_log.filledSql, 32 | statement_log.executionDurationNanos+coalesce(statement_log.rsetUsageDurationNanos,0) as EXEC_PLUS_RSET_USAGE_TIME, 33 | statement_log.executionDurationNanos as execution_time, 34 | statement_log.fetchDurationNanos as fetch_time, 35 | statement_log.rsetUsageDurationNanos as rset_usage_time, 36 | statement_log.nbRows, 37 | statement_log.threadName, 38 | case when statement_log.timeout = 0 then null else statement_log.timeout end as timeout, 39 | case when statement_log.autoCommit then 'Y' else null end as autoCommit, 40 | statement_log.transaction_Isolation, 41 | NVL2(statement_log.exception, 1, null) as exception, 42 | connection_info.connectionNumber 43 | from statement_log join connection_info on (connection_info.connectionId=statement_log.connectionId); 44 | 45 | --insert into statement_log (id, tstamp, rawSql, filledSql, durationNanos) values(1, sysdate, 'raw', 'filled', 456); 46 | --insert into statement_log (id, tstamp, rawSql, filledSql, durationNanos) values(2, sysdate, 'raw', 'filled2', 456098); 47 | --insert into statement_log (id, tstamp, rawSql, filledSql, durationNanos) values(3, sysdate, 'raw', 'filled2', 456098); 48 | --insert into statement_log (id, tstamp, rawSql, filledSql, durationNanos) values(4, sysdate, 'raw', 'filled2', 456098); 49 | --delete from statement_log where id=3; 50 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/test/java/ch/sla/jdbcperflogger/console/db/AbstractLogRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.console.db; 2 | 3 | import static java.sql.Connection.TRANSACTION_NONE; 4 | import static java.util.UUID.randomUUID; 5 | import static org.eclipse.jdt.annotation.DefaultLocation.PARAMETER; 6 | import static org.eclipse.jdt.annotation.DefaultLocation.RETURN_TYPE; 7 | 8 | import java.sql.Connection; 9 | import java.sql.ResultSet; 10 | import java.sql.SQLException; 11 | import java.sql.Statement; 12 | import java.util.Date; 13 | import java.util.Properties; 14 | 15 | import org.eclipse.jdt.annotation.NonNullByDefault; 16 | import org.junit.After; 17 | import org.junit.Before; 18 | 19 | import ch.sla.jdbcperflogger.StatementType; 20 | import ch.sla.jdbcperflogger.model.ConnectionInfo; 21 | import ch.sla.jdbcperflogger.model.StatementLog; 22 | 23 | @NonNullByDefault({ PARAMETER, RETURN_TYPE }) 24 | public class AbstractLogRepositoryTest { 25 | 26 | protected LogRepositoryUpdateJdbc repositoryUpdate; 27 | protected LogRepositoryRead repositoryRead; 28 | 29 | @Before 30 | public void setup() { 31 | repositoryUpdate = new LogRepositoryUpdateJdbc("test"); 32 | repositoryRead = new LogRepositoryReadJdbc("test"); 33 | } 34 | 35 | @After 36 | public void tearDown() { 37 | repositoryUpdate.dispose(); 38 | repositoryRead.dispose(); 39 | } 40 | 41 | protected ConnectionInfo insert1Connection() { 42 | final Properties connProps = new Properties(); 43 | connProps.setProperty("myprop", "myval"); 44 | final ConnectionInfo connectionInfo = new ConnectionInfo(randomUUID(), 12, "jdbc:toto", new Date(), 12, 45 | connProps); 46 | repositoryUpdate.addConnection(connectionInfo); 47 | return connectionInfo; 48 | } 49 | 50 | protected StatementLog insert1Log(final ConnectionInfo connectionInfo) { 51 | 52 | final StatementLog log = new StatementLog(connectionInfo.getUuid(), randomUUID(), System.currentTimeMillis(), 53 | StatementType.BASE_NON_PREPARED_STMT, "myrawsql", Thread.currentThread().getName(), 123, true, TRANSACTION_NONE); 54 | repositoryUpdate.addStatementLog(log); 55 | return log; 56 | } 57 | 58 | protected StatementLog insert1Log() { 59 | final ConnectionInfo connectionInfo = insert1Connection(); 60 | return insert1Log(connectionInfo); 61 | } 62 | 63 | protected int countRowsInTable(final String table) { 64 | try (Statement stmt = repositoryUpdate.connectionUpdate.createStatement()) { 65 | try (ResultSet rset = stmt.executeQuery("select count(1) from " + table)) { 66 | rset.next(); 67 | return rset.getInt(1); 68 | } 69 | } catch (final SQLException e) { 70 | throw new RuntimeException(e); 71 | } 72 | } 73 | 74 | } -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/test/java/ch/sla/jdbcperflogger/console/net/ServerLogReceiverTest.java: -------------------------------------------------------------------------------- 1 | package ch.sla.jdbcperflogger.console.net; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertFalse; 5 | import static org.junit.Assert.assertTrue; 6 | 7 | import java.io.IOException; 8 | import java.io.ObjectOutputStream; 9 | import java.net.Socket; 10 | import java.net.UnknownHostException; 11 | 12 | import org.junit.After; 13 | import org.junit.Before; 14 | import org.junit.Test; 15 | import org.mockito.Mock; 16 | 17 | import ch.sla.jdbcperflogger.console.db.LogRepositoryUpdateJdbc; 18 | 19 | @SuppressWarnings("null") 20 | public class ServerLogReceiverTest { 21 | private ServerLogReceiver receiver; 22 | @Mock 23 | private LogRepositoryUpdateJdbc repository; 24 | private ObjectOutputStream oos; 25 | private Socket clientSocket; 26 | 27 | @Before 28 | public void setUp() throws Exception { 29 | receiver = new ServerLogReceiver(0, repository); 30 | receiver.start(); 31 | receiver.waitUntilServerIsReady(); 32 | } 33 | 34 | @After 35 | public void teardown() throws Exception { 36 | if (oos != null) { 37 | oos.close(); 38 | } 39 | if (clientSocket != null) { 40 | clientSocket.close(); 41 | } 42 | receiver.dispose(); 43 | } 44 | 45 | @Test 46 | public void testGetConnectionsCount() throws Exception { 47 | assertEquals(0, receiver.getConnectionsCount()); 48 | connectToServer(); 49 | Thread.sleep(1000); 50 | assertEquals(1, receiver.getConnectionsCount()); 51 | } 52 | 53 | @Test 54 | public void testPauseReceivingLogs() throws Exception { 55 | assertFalse(receiver.isPaused()); 56 | receiver.pauseReceivingLogs(); 57 | assertTrue(receiver.isPaused()); 58 | receiver.resumeReceivingLogs(); 59 | assertFalse(receiver.isPaused()); 60 | } 61 | 62 | private Socket connectToServer() throws UnknownHostException, IOException { 63 | clientSocket = new Socket("localhost", receiver.getListenPort()); 64 | oos = new ObjectOutputStream(clientSocket.getOutputStream()); 65 | oos.writeObject(null); 66 | return clientSocket; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/test/resources/log4j.dtd: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 58 | 59 | 60 | 61 | 62 | 65 | 69 | 70 | 71 | 74 | 75 | 76 | 79 | 80 | 81 | 82 | 83 | 84 | 87 | 88 | 89 | 90 | 91 | 94 | 95 | 96 | 100 | 101 | 102 | 103 | 104 | 108 | 109 | 110 | 111 | 115 | 116 | 117 | 118 | 119 | 120 | 125 | 126 | 127 | 128 | 129 | 133 | 134 | 135 | 136 | 138 | 139 | 140 | 142 | 143 | 144 | 147 | 148 | 149 | 150 | 154 | 155 | 156 | 159 | 160 | 161 | 164 | 165 | 166 | 170 | 171 | 172 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 193 | 194 | 195 | 196 | 198 | 199 | 200 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 220 | 221 | 222 | 223 | 224 | 228 | -------------------------------------------------------------------------------- /jdbc-perf-logger-gui/src/test/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /lombok.config: -------------------------------------------------------------------------------- 1 | config.stopBubbling = true 2 | lombok.addLombokGeneratedAnnotation = true --------------------------------------------------------------------------------