diff options
author | Simon Brandhof <simon.brandhof@sonarsource.com> | 2015-10-06 00:21:11 +0200 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@sonarsource.com> | 2015-10-06 15:02:48 +0200 |
commit | f9a7660fc36358f43dba067230e4b39736e46f9b (patch) | |
tree | 2a629c800724a4026ebe2cdac1f7ab25eff4079f /sonar-db | |
parent | 248208188e2da2983dc6c615218a1dc32131ca56 (diff) | |
download | sonarqube-f9a7660fc36358f43dba067230e4b39736e46f9b.tar.gz sonarqube-f9a7660fc36358f43dba067230e4b39736e46f9b.zip |
SONAR-6830 ability to change level of SQL logs on the fly
Diffstat (limited to 'sonar-db')
9 files changed, 183 insertions, 32 deletions
diff --git a/sonar-db/src/main/java/org/sonar/db/Database.java b/sonar-db/src/main/java/org/sonar/db/Database.java index 6b747edee76..d2602312ddb 100644 --- a/sonar-db/src/main/java/org/sonar/db/Database.java +++ b/sonar-db/src/main/java/org/sonar/db/Database.java @@ -36,4 +36,6 @@ public interface Database extends Startable { * @return the dialect or null if start() has not been executed */ Dialect getDialect(); + + void enableSqlLogging(boolean enable); } diff --git a/sonar-db/src/main/java/org/sonar/db/DefaultDatabase.java b/sonar-db/src/main/java/org/sonar/db/DefaultDatabase.java index 7bb864230f2..b6a044bf3cd 100644 --- a/sonar-db/src/main/java/org/sonar/db/DefaultDatabase.java +++ b/sonar-db/src/main/java/org/sonar/db/DefaultDatabase.java @@ -36,8 +36,12 @@ import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.db.dialect.Dialect; import org.sonar.db.dialect.DialectUtils; +import org.sonar.db.profiling.NullConnectionInterceptor; +import org.sonar.db.profiling.ProfiledConnectionInterceptor; import org.sonar.db.profiling.ProfiledDataSource; +import static java.lang.String.format; + /** * @since 2.12 */ @@ -51,7 +55,7 @@ public class DefaultDatabase implements Database { private static final String SONAR_JDBC_URL = "sonar.jdbc.url"; private Settings settings; - private BasicDataSource datasource; + private ProfiledDataSource datasource; private Dialect dialect; private Properties properties; @@ -63,7 +67,7 @@ public class DefaultDatabase implements Database { public void start() { try { initSettings(); - initDatasource(); + initDataSource(); checkConnection(); } catch (Exception e) { @@ -82,15 +86,14 @@ public class DefaultDatabase implements Database { properties.setProperty(DatabaseProperties.PROP_DRIVER, dialect.getDefaultDriverClassName()); } - private void initDatasource() throws Exception {// NOSONAR this exception is thrown by BasicDataSourceFactory + private void initDataSource() throws Exception { // but it's correctly caught by start() - LOG.info("Create JDBC datasource for " + properties.getProperty(DatabaseProperties.PROP_URL, DEFAULT_URL)); - datasource = (BasicDataSource) BasicDataSourceFactory.createDataSource(extractCommonsDbcpProperties(properties)); + LOG.info("Create JDBC data source for {}", properties.getProperty(DatabaseProperties.PROP_URL, DEFAULT_URL)); + BasicDataSource basicDataSource = (BasicDataSource) BasicDataSourceFactory.createDataSource(extractCommonsDbcpProperties(properties)); + datasource = new ProfiledDataSource(basicDataSource, NullConnectionInterceptor.INSTANCE); datasource.setConnectionInitSqls(dialect.getConnectionInitStatements()); datasource.setValidationQuery(dialect.getValidationQuery()); - if ("TRACE".equals(settings.getString("sonar.log.level"))) { - datasource = new ProfiledDataSource(datasource); - } + enableSqlLogging(datasource, "TRACE".equals(settings.getString("sonar.log.level"))); } private void checkConnection() { @@ -129,6 +132,15 @@ public class DefaultDatabase implements Database { return properties; } + @Override + public void enableSqlLogging(boolean enable) { + enableSqlLogging(datasource, enable); + } + + private static void enableSqlLogging(ProfiledDataSource ds, boolean enable) { + ds.setConnectionInterceptor(enable ? ProfiledConnectionInterceptor.INSTANCE : NullConnectionInterceptor.INSTANCE); + } + /** * Override this method to add JDBC properties at runtime */ @@ -168,6 +180,6 @@ public class DefaultDatabase implements Database { @Override public String toString() { - return "Database[" + (properties != null ? properties.getProperty(SONAR_JDBC_URL) : "?") + "]"; + return format("Database[%s]", properties != null ? properties.getProperty(SONAR_JDBC_URL) : "?"); } } diff --git a/sonar-db/src/main/java/org/sonar/db/profiling/ConnectionInterceptor.java b/sonar-db/src/main/java/org/sonar/db/profiling/ConnectionInterceptor.java new file mode 100644 index 00000000000..559e953957d --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/profiling/ConnectionInterceptor.java @@ -0,0 +1,32 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.db.profiling; + +import java.sql.Connection; +import java.sql.SQLException; +import org.apache.commons.dbcp.BasicDataSource; + +public interface ConnectionInterceptor { + + Connection getConnection(BasicDataSource dataSource) throws SQLException; + + Connection getConnection(BasicDataSource dataSource, String login, String password) throws SQLException; + +} diff --git a/sonar-db/src/main/java/org/sonar/db/profiling/NullConnectionInterceptor.java b/sonar-db/src/main/java/org/sonar/db/profiling/NullConnectionInterceptor.java new file mode 100644 index 00000000000..0f9949983c3 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/profiling/NullConnectionInterceptor.java @@ -0,0 +1,38 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.db.profiling; + +import java.sql.Connection; +import java.sql.SQLException; +import org.apache.commons.dbcp.BasicDataSource; + +public enum NullConnectionInterceptor implements ConnectionInterceptor { + INSTANCE; + + @Override + public Connection getConnection(BasicDataSource dataSource) throws SQLException { + return dataSource.getConnection(); + } + + @Override + public Connection getConnection(BasicDataSource dataSource, String user, String password) throws SQLException { + return dataSource.getConnection(user, password); + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/profiling/ProfiledConnectionInterceptor.java b/sonar-db/src/main/java/org/sonar/db/profiling/ProfiledConnectionInterceptor.java new file mode 100644 index 00000000000..78220ebfa5a --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/profiling/ProfiledConnectionInterceptor.java @@ -0,0 +1,45 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.db.profiling; + +import java.lang.reflect.Proxy; +import java.sql.Connection; +import java.sql.SQLException; +import org.apache.commons.dbcp.BasicDataSource; + +public enum ProfiledConnectionInterceptor implements ConnectionInterceptor { + INSTANCE; + + @Override + public Connection getConnection(BasicDataSource dataSource) throws SQLException { + return buildConnectionProxy(new ProfilingConnectionHandler(dataSource.getConnection())); + } + + @Override + public Connection getConnection(BasicDataSource dataSource, String login, String password) throws SQLException { + return buildConnectionProxy(new ProfilingConnectionHandler(dataSource.getConnection(login, password))); + } + + private static Connection buildConnectionProxy(ProfilingConnectionHandler connectionHandler) { + ClassLoader classloader = ProfiledConnectionInterceptor.class.getClassLoader(); + return (Connection) Proxy.newProxyInstance(classloader, new Class[] {Connection.class}, connectionHandler); + } + +} diff --git a/sonar-db/src/main/java/org/sonar/db/profiling/ProfiledDataSource.java b/sonar-db/src/main/java/org/sonar/db/profiling/ProfiledDataSource.java index 2b2136a1ef8..396e2178a17 100644 --- a/sonar-db/src/main/java/org/sonar/db/profiling/ProfiledDataSource.java +++ b/sonar-db/src/main/java/org/sonar/db/profiling/ProfiledDataSource.java @@ -20,9 +20,9 @@ package org.sonar.db.profiling; import java.io.PrintWriter; -import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; import java.util.Collection; import org.apache.commons.dbcp.BasicDataSource; import org.sonar.api.utils.log.Logger; @@ -33,9 +33,19 @@ public class ProfiledDataSource extends BasicDataSource { static final Logger SQL_LOGGER = Loggers.get("sql"); private final BasicDataSource delegate; + private ConnectionInterceptor connectionInterceptor; - public ProfiledDataSource(BasicDataSource delegate) { + public ProfiledDataSource(BasicDataSource delegate, ConnectionInterceptor connectionInterceptor) { this.delegate = delegate; + this.connectionInterceptor = connectionInterceptor; + } + + public BasicDataSource getDelegate() { + return delegate; + } + + public synchronized void setConnectionInterceptor(ConnectionInterceptor ci) { + this.connectionInterceptor = ci; } @Override @@ -310,14 +320,12 @@ public class ProfiledDataSource extends BasicDataSource { @Override public Connection getConnection() throws SQLException { - return (Connection) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[] {Connection.class}, - new ProfilingConnectionHandler(delegate.getConnection())); + return connectionInterceptor.getConnection(delegate); } @Override - public Connection getConnection(String user, String pass) throws SQLException { - return (Connection) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[] {Connection.class}, - new ProfilingConnectionHandler(delegate.getConnection(user, pass))); + public Connection getConnection(String login, String password) throws SQLException { + return connectionInterceptor.getConnection(this, login, password); } @Override @@ -326,6 +334,11 @@ public class ProfiledDataSource extends BasicDataSource { } @Override + public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException { + return java.util.logging.Logger.getLogger(getClass().getName()); + } + + @Override public PrintWriter getLogWriter() throws SQLException { return delegate.getLogWriter(); } diff --git a/sonar-db/src/main/java/org/sonar/db/profiling/ProfilingConnectionHandler.java b/sonar-db/src/main/java/org/sonar/db/profiling/ProfilingConnectionHandler.java index 4446a47e7ee..b6e4c3566d6 100644 --- a/sonar-db/src/main/java/org/sonar/db/profiling/ProfilingConnectionHandler.java +++ b/sonar-db/src/main/java/org/sonar/db/profiling/ProfilingConnectionHandler.java @@ -40,17 +40,17 @@ class ProfilingConnectionHandler implements InvocationHandler { if ("prepareStatement".equals(method.getName())) { PreparedStatement statement = (PreparedStatement) result; String sql = (String) args[0]; - return Proxy.newProxyInstance(ProfilingConnectionHandler.class.getClassLoader(), new Class[] {PreparedStatement.class}, - new ProfilingPreparedStatementHandler(statement, sql)); - - } else if ("createStatement".equals(method.getName())) { + return buildStatementProxy(PreparedStatement.class, new ProfilingPreparedStatementHandler(statement, sql)); + } + if ("createStatement".equals(method.getName())) { Statement statement = (Statement) result; - return Proxy.newProxyInstance(ProfilingConnectionHandler.class.getClassLoader(), new Class[] {Statement.class}, - new ProfilingStatementHandler(statement)); - - } else { - return result; + return buildStatementProxy(Statement.class, new ProfilingStatementHandler(statement)); } + return result; + } + + private Object buildStatementProxy(Class<? extends Statement> stmtClass, InvocationHandler handler) { + return Proxy.newProxyInstance(ProfilingConnectionHandler.class.getClassLoader(), new Class[] {stmtClass}, handler); } } diff --git a/sonar-db/src/test/java/org/sonar/db/H2Database.java b/sonar-db/src/test/java/org/sonar/db/H2Database.java index 688211e31b2..b2ada1cc440 100644 --- a/sonar-db/src/test/java/org/sonar/db/H2Database.java +++ b/sonar-db/src/test/java/org/sonar/db/H2Database.java @@ -27,6 +27,8 @@ import org.apache.commons.dbutils.DbUtils; import org.sonar.db.dialect.Dialect; import org.sonar.db.dialect.H2; +import static java.lang.String.format; + /** * H2 in-memory database, used for unit tests only. * @@ -109,7 +111,12 @@ public class H2Database implements Database { } @Override + public void enableSqlLogging(boolean enable) { + throw new UnsupportedOperationException(); + } + + @Override public String toString() { - return "H2 Database[" + name + "]"; + return format("H2 Database[%s]", name); } } diff --git a/sonar-db/src/test/java/org/sonar/db/profiling/ProfiledDataSourceTest.java b/sonar-db/src/test/java/org/sonar/db/profiling/ProfiledDataSourceTest.java index d52e95531f0..d58c20e5d9c 100644 --- a/sonar-db/src/test/java/org/sonar/db/profiling/ProfiledDataSourceTest.java +++ b/sonar-db/src/test/java/org/sonar/db/profiling/ProfiledDataSourceTest.java @@ -31,6 +31,7 @@ import org.apache.commons.dbcp.BasicDataSource; import org.junit.Rule; import org.junit.Test; import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -42,7 +43,7 @@ public class ProfiledDataSourceTest { public LogTester logTester = new LogTester(); @Test - public void log_sql_requests() throws Exception { + public void execute_and_log_sql_requests() throws Exception { BasicDataSource originDataSource = mock(BasicDataSource.class); Connection connection = mock(Connection.class); @@ -64,7 +65,7 @@ public class ProfiledDataSourceTest { when(connection.createStatement()).thenReturn(statement); when(statement.execute(sql)).thenReturn(true); - ProfiledDataSource ds = new ProfiledDataSource(originDataSource); + ProfiledDataSource ds = new ProfiledDataSource(originDataSource, ProfiledConnectionInterceptor.INSTANCE); assertThat(ds.getUrl()).isNull(); assertThat(ds.getConnection().getClientInfo()).isNull(); @@ -80,14 +81,15 @@ public class ProfiledDataSourceTest { assertThat(statementProxy.getConnection()).isNull(); assertThat(statementProxy.execute(sql)).isTrue(); - assertThat(logTester.logs()).hasSize(2); - assertThat(logTester.logs().get(1)).contains(sql); + assertThat(logTester.logs(LoggerLevel.TRACE)).hasSize(2); + assertThat(logTester.logs(LoggerLevel.TRACE).get(0)).containsSequence("sql=insert into polop (col1, col2, col3, col4) values (?, ?, ?, ?, ?);"); + assertThat(logTester.logs(LoggerLevel.TRACE).get(1)).containsSequence("sql=select 'polop' from dual;"); } @Test - public void delegate_to_underlying_datasource() throws Exception { + public void delegate_to_underlying_data_source() throws Exception { BasicDataSource delegate = mock(BasicDataSource.class); - ProfiledDataSource proxy = new ProfiledDataSource(delegate); + ProfiledDataSource proxy = new ProfiledDataSource(delegate, ProfiledConnectionInterceptor.INSTANCE); // painful to call all methods // so using reflection to check that calls does not fail |