Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
import net.ttddyy.dsproxy.listener.logging.CommonsLogLevel;
import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel;
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
import org.springframework.boot.convert.DurationUnit;

import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;

Expand Down Expand Up @@ -51,7 +54,7 @@ public class DataSourceProxyProperties {
/**
* Use formatted SQL for logging query.
*
* @see ProxyDataSourceBuilder#formatQuery(ProxyDataSourceBuilder.FormatQueryCallback)
* @see ProxyDataSourceBuilder#formatQuery(ProxyDataSourceBuilder.FormatQueryCallback)
*/
private boolean formatSql = false;

Expand Down Expand Up @@ -194,9 +197,10 @@ public static class SlowQuery {
*/
private String logLevel = "WARN";
/**
* Number of seconds to consider query as slow.
* Query duration to consider the query slow and log it.
*/
private long threshold = 300;
@DurationUnit(value = ChronoUnit.SECONDS)
private Duration threshold = Duration.ofSeconds(300);

public boolean isEnableLogging() {
return this.enableLogging;
Expand All @@ -210,7 +214,15 @@ public String getLogLevel() {
return this.logLevel;
}

/**
* @deprecated Use {@link #getThresholdDuration()} instead.
*/
@Deprecated(since = "1.11.0", forRemoval = true)
public long getThreshold() {
return this.threshold.toSeconds();
}

public Duration getThresholdDuration() {
return this.threshold;
}

Expand All @@ -226,7 +238,7 @@ public void setLogLevel(String logLevel) {
this.logLevel = logLevel;
}

public void setThreshold(long threshold) {
public void setThreshold(Duration threshold) {
this.threshold = threshold;
}
}
Expand All @@ -237,4 +249,4 @@ public enum DataSourceProxyLogging {
COMMONS,
JUL
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,43 +71,49 @@ public class ProxyDataSourceBuilderConfigurer {
ProxyDataSourceBuilder.FormatQueryCallback formatQueryCallback;

public void configure(ProxyDataSourceBuilder proxyDataSourceBuilder, DataSourceProxyProperties datasourceProxy) {
var query = datasourceProxy.getQuery();
var slowQuery = datasourceProxy.getSlowQuery();
switch (datasourceProxy.getLogging()) {
case SLF4J: {
if (datasourceProxy.getQuery().isEnableLogging()) {
proxyDataSourceBuilder.logQueryBySlf4j(toSlf4JLogLevel(datasourceProxy.getQuery().getLogLevel()), datasourceProxy.getQuery().getLoggerName());
if (query.isEnableLogging()) {
proxyDataSourceBuilder.logQueryBySlf4j(toSlf4JLogLevel(query.getLogLevel()), query.getLoggerName());
}
if (datasourceProxy.getSlowQuery().isEnableLogging()) {
proxyDataSourceBuilder.logSlowQueryBySlf4j(datasourceProxy.getSlowQuery().getThreshold(), TimeUnit.SECONDS,
toSlf4JLogLevel(datasourceProxy.getSlowQuery().getLogLevel()), datasourceProxy.getSlowQuery().getLoggerName());
if (slowQuery.isEnableLogging()) {
proxyDataSourceBuilder.logSlowQueryBySlf4j(
slowQuery.getThresholdDuration().toMillis(), TimeUnit.MILLISECONDS,
toSlf4JLogLevel(slowQuery.getLogLevel()), slowQuery.getLoggerName());
}
break;
}
case JUL: {
if (datasourceProxy.getQuery().isEnableLogging()) {
proxyDataSourceBuilder.logQueryByJUL(toJULLogLevel(datasourceProxy.getQuery().getLogLevel()), datasourceProxy.getQuery().getLoggerName());
if (query.isEnableLogging()) {
proxyDataSourceBuilder.logQueryByJUL(toJULLogLevel(query.getLogLevel()), query.getLoggerName());
}
if (datasourceProxy.getSlowQuery().isEnableLogging()) {
proxyDataSourceBuilder.logSlowQueryByJUL(datasourceProxy.getSlowQuery().getThreshold(), TimeUnit.SECONDS,
toJULLogLevel(datasourceProxy.getSlowQuery().getLogLevel()), datasourceProxy.getSlowQuery().getLoggerName());
if (slowQuery.isEnableLogging()) {
proxyDataSourceBuilder.logSlowQueryByJUL(
slowQuery.getThresholdDuration().toMillis(), TimeUnit.MILLISECONDS,
toJULLogLevel(slowQuery.getLogLevel()), slowQuery.getLoggerName());
}
break;
}
case COMMONS: {
if (datasourceProxy.getQuery().isEnableLogging()) {
proxyDataSourceBuilder.logQueryByCommons(toCommonsLogLevel(datasourceProxy.getQuery().getLogLevel()), datasourceProxy.getQuery().getLoggerName());
if (query.isEnableLogging()) {
proxyDataSourceBuilder.logQueryByCommons(toCommonsLogLevel(query.getLogLevel()), query.getLoggerName());
}
if (datasourceProxy.getSlowQuery().isEnableLogging()) {
proxyDataSourceBuilder.logSlowQueryByCommons(datasourceProxy.getSlowQuery().getThreshold(), TimeUnit.SECONDS,
toCommonsLogLevel(datasourceProxy.getSlowQuery().getLogLevel()), datasourceProxy.getSlowQuery().getLoggerName());
if (slowQuery.isEnableLogging()) {
proxyDataSourceBuilder.logSlowQueryByCommons(
slowQuery.getThresholdDuration().toMillis(), TimeUnit.MILLISECONDS,
toCommonsLogLevel(slowQuery.getLogLevel()), slowQuery.getLoggerName());
}
break;
}
case SYSOUT: {
if (datasourceProxy.getQuery().isEnableLogging()) {
if (query.isEnableLogging()) {
proxyDataSourceBuilder.logQueryToSysOut();
}
if (datasourceProxy.getSlowQuery().isEnableLogging()) {
proxyDataSourceBuilder.logSlowQueryToSysOut(datasourceProxy.getSlowQuery().getThreshold(), TimeUnit.SECONDS);
if (slowQuery.isEnableLogging()) {
proxyDataSourceBuilder.logSlowQueryToSysOut(
slowQuery.getThresholdDuration().toMillis(), TimeUnit.MILLISECONDS);
}
break;
}
Expand Down Expand Up @@ -201,14 +207,4 @@ private CommonsLogLevel toCommonsLogLevel(String logLevel) {
throw new IllegalArgumentException("Unresolved log level " + logLevel + " for apache commons logger, " +
"known levels " + Arrays.toString(CommonsLogLevel.values()));
}

private static boolean classExists(String fullQualifiedClassName) {

try {
Class.forName(fullQualifiedClassName);
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import javax.sql.DataSource;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

import static org.assertj.core.api.Assertions.assertThat;

Expand All @@ -68,8 +69,10 @@ void testRegisterLogAndSlowQueryLogByDefaultToSlf4j() {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DecoratedDataSource) dataSource).getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(SLF4JSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(SLF4JQueryLoggingListener.class);
assertThat(chainListener.getListeners()).hasExactlyElementsOfTypes(
SLF4JQueryLoggingListener.class,
SLF4JSlowQueryListener.class
);
});
}

Expand All @@ -81,8 +84,10 @@ void testRegisterLogAndSlowQueryLogByUsingSlf4j() {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DecoratedDataSource) dataSource).getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(SLF4JSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(SLF4JQueryLoggingListener.class);
assertThat(chainListener.getListeners()).hasExactlyElementsOfTypes(
SLF4JQueryLoggingListener.class,
SLF4JSlowQueryListener.class
);
});
}

Expand All @@ -94,8 +99,10 @@ void testRegisterLogAndSlowQueryLogUsingSystemOut() {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DecoratedDataSource) dataSource).getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(SystemOutSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(SystemOutQueryLoggingListener.class);
assertThat(chainListener.getListeners()).hasExactlyElementsOfTypes(
SystemOutQueryLoggingListener.class,
SystemOutSlowQueryListener.class
);
});
}

Expand All @@ -107,8 +114,10 @@ void testRegisterLogAndSlowQueryLogUsingJUL() {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DecoratedDataSource) dataSource).getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(JULSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(JULQueryLoggingListener.class);
assertThat(chainListener.getListeners()).hasExactlyElementsOfTypes(
JULQueryLoggingListener.class,
JULSlowQueryListener.class
);
});
}

Expand All @@ -120,11 +129,54 @@ void testRegisterLogAndSlowQueryLogUsingApacheCommons() {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DecoratedDataSource) dataSource).getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(CommonsSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(CommonsQueryLoggingListener.class);
assertThat(chainListener.getListeners()).hasExactlyElementsOfTypes(
CommonsQueryLoggingListener.class,
CommonsSlowQueryListener.class
);
});
}

@Test
void testSlowQueryWithoutTimeUnit() {
ApplicationContextRunner contextRunner = this.contextRunner.withPropertyValues(
"decorator.datasource.datasource-proxy.slow-query.threshold:50"
);

contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DecoratedDataSource) dataSource).getDecoratedDataSource();
var slowQueryListener = findListener(proxyDataSource, SLF4JSlowQueryListener.class);
assertThat(slowQueryListener.getThreshold()).isEqualTo(50000);
assertThat(slowQueryListener.getThresholdTimeUnit()).isEqualTo(TimeUnit.MILLISECONDS);
});
}

@Test
void testSlowQueryMilliseconds() {
ApplicationContextRunner contextRunner = this.contextRunner.withPropertyValues(
"decorator.datasource.datasource-proxy.slow-query.threshold:50ms"
);

contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DecoratedDataSource) dataSource).getDecoratedDataSource();
var slowQueryListener = findListener(proxyDataSource, SLF4JSlowQueryListener.class);
assertThat(slowQueryListener.getThreshold()).isEqualTo(50);
assertThat(slowQueryListener.getThresholdTimeUnit()).isEqualTo(TimeUnit.MILLISECONDS);
});
}

private static <T> T findListener(ProxyDataSource proxyDataSource, Class<T> clazz) {
return proxyDataSource.getProxyConfig()
.getQueryListener()
.getListeners()
.stream()
.filter(clazz::isInstance)
.map(clazz::cast)
.findFirst()
.orElseThrow(() -> new IllegalStateException("Listener of type " + clazz.getName() + " not found"));
}

@Test
void testCustomParameterAndQueryTransformer() {
ApplicationContextRunner contextRunner = this.contextRunner.withUserConfiguration(CustomDataSourceProxyConfiguration.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@
package com.github.gavlyukovskiy.sample;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.sql.DataSource;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
Expand All @@ -38,6 +41,25 @@ public class SampleController {

public SampleController(DataSource dataSource) {
this.dataSource = dataSource;
prepareFunctions(dataSource);
}

private static void prepareFunctions(DataSource dataSource) {
try (
Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement(
"""
CREATE ALIAS sleep AS '
void sleep(int millis) throws Exception {
Thread.sleep(millis);
}
';
""")
) {
statement.execute();
} catch (SQLException e) {
throw new IllegalStateException(e);
}
}

@RequestMapping("/commit")
Expand Down Expand Up @@ -95,4 +117,16 @@ public void error() {
catch (Exception ignored) {
}
}

@RequestMapping("/sleep")
public void sleep(@RequestParam(defaultValue = "3000") int millis) {
try (Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement("SELECT SLEEP(?)")) {
statement.setInt(1, millis);
statement.execute();
}
catch (Exception e) {
throw new IllegalStateException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@ decorator:
datasource-proxy:
format-sql: true
query:
log-level: INFO
log-level: INFO
slow-query:
threshold: 100ms
Loading