Moved tests to common

This commit is contained in:
Marcin Grzejszczak
2021-05-27 13:41:44 +02:00
parent 682398daec
commit 8991b4d3cd
26 changed files with 3481 additions and 3238 deletions

View File

@@ -1,119 +1,112 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import java.util.ArrayList;
import java.util.List;
import net.ttddyy.dsproxy.listener.MethodExecutionListener;
import net.ttddyy.dsproxy.listener.QueryCountStrategy;
import net.ttddyy.dsproxy.listener.QueryExecutionListener;
import net.ttddyy.dsproxy.listener.SingleQueryCountHolder;
import net.ttddyy.dsproxy.proxy.GlobalConnectionIdManager;
import net.ttddyy.dsproxy.proxy.ResultSetProxyLogicFactory;
import net.ttddyy.dsproxy.proxy.SimpleResultSetProxyLogicFactory;
import net.ttddyy.dsproxy.support.ProxyDataSource;
import net.ttddyy.dsproxy.transform.ParameterTransformer;
import net.ttddyy.dsproxy.transform.QueryTransformer;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceNameResolver;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceProxyBuilderCustomizer;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceProxyConnectionIdManagerProvider;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceProxyDataSourceDecorator;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceProxyProperties;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceListenerStrategySpanCustomizer;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceQueryExecutionListener;
import org.springframework.context.annotation.Bean;
/**
* Configuration for integration with datasource-proxy, allows to use define custom
* {@link QueryExecutionListener}, {@link ParameterTransformer} and
* {@link QueryTransformer}.
*
* @author Arthur Gavlyukovskiy
*/
@ConditionalOnClass(ProxyDataSource.class)
class DataSourceProxyConfiguration {
@Bean
@ConditionalOnMissingBean
DataSourceProxyConnectionIdManagerProvider traceConnectionIdManagerProvider() {
return GlobalConnectionIdManager::new;
}
@Bean
DataSourceProxyBuilderCustomizer proxyDataSourceBuilderConfigurer(
ObjectProvider<QueryCountStrategy> queryCountStrategy,
ObjectProvider<List<QueryExecutionListener>> listeners,
ObjectProvider<List<MethodExecutionListener>> methodExecutionListeners,
ObjectProvider<ParameterTransformer> parameterTransformer,
ObjectProvider<QueryTransformer> queryTransformer,
ObjectProvider<ResultSetProxyLogicFactory> resultSetProxyLogicFactory,
ObjectProvider<DataSourceProxyConnectionIdManagerProvider> dataSourceProxyConnectionIdManagerProvider,
TraceDataSourceDecoratorProperties dataSourceDecoratorProperties) {
return new DataSourceProxyBuilderCustomizer(queryCountStrategy.getIfAvailable(() -> null),
listeners.getIfAvailable(() -> null), methodExecutionListeners.getIfAvailable(() -> null),
parameterTransformer.getIfAvailable(() -> null), queryTransformer.getIfAvailable(() -> null),
resultSetProxyLogicFactory.getIfAvailable(() -> null),
dataSourceProxyConnectionIdManagerProvider.getIfAvailable(() -> null),
props(dataSourceDecoratorProperties));
}
private DataSourceProxyProperties props(TraceDataSourceDecoratorProperties dataSourceDecoratorProperties) {
TraceDataSourceDecoratorProperties.DataSourceProxyProperties originalProxy = dataSourceDecoratorProperties
.getDatasourceProxy();
DataSourceProxyProperties props = new DataSourceProxyProperties();
BeanUtils.copyProperties(originalProxy, props);
props.setLogging(DataSourceProxyProperties.DataSourceProxyLogging.valueOf(originalProxy.getLogging().name()));
return props;
}
@Bean
DataSourceProxyDataSourceDecorator proxyDataSourceDecorator(
DataSourceProxyBuilderCustomizer dataSourceProxyBuilderCustomizer,
DataSourceNameResolver dataSourceNameResolver) {
return new DataSourceProxyDataSourceDecorator(dataSourceProxyBuilderCustomizer, dataSourceNameResolver);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.sleuth.jdbc.datasource-proxy.count-query", havingValue = "true")
QueryCountStrategy queryCountStrategy() {
return new SingleQueryCountHolder();
}
@Bean
TraceQueryExecutionListener traceQueryExecutionListener(Tracer tracer,
TraceDataSourceDecoratorProperties dataSourceDecoratorProperties,
ObjectProvider<List<TraceListenerStrategySpanCustomizer>> customizers) {
return new TraceQueryExecutionListener(tracer, dataSourceDecoratorProperties.getIncludes(),
customizers.getIfAvailable(ArrayList::new));
}
@Bean
@ConditionalOnMissingBean
ResultSetProxyLogicFactory traceResultSetProxyLogicFactory() {
return new SimpleResultSetProxyLogicFactory();
}
}
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import java.util.ArrayList;
import java.util.List;
import net.ttddyy.dsproxy.listener.MethodExecutionListener;
import net.ttddyy.dsproxy.listener.QueryCountStrategy;
import net.ttddyy.dsproxy.listener.QueryExecutionListener;
import net.ttddyy.dsproxy.proxy.GlobalConnectionIdManager;
import net.ttddyy.dsproxy.proxy.ResultSetProxyLogicFactory;
import net.ttddyy.dsproxy.proxy.SimpleResultSetProxyLogicFactory;
import net.ttddyy.dsproxy.support.ProxyDataSource;
import net.ttddyy.dsproxy.transform.ParameterTransformer;
import net.ttddyy.dsproxy.transform.QueryTransformer;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceNameResolver;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceProxyBuilderCustomizer;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceProxyConnectionIdManagerProvider;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceProxyDataSourceDecorator;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceProxyProperties;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceListenerStrategySpanCustomizer;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceQueryExecutionListener;
import org.springframework.context.annotation.Bean;
/**
* Configuration for integration with datasource-proxy, allows to use define custom
* {@link QueryExecutionListener}, {@link ParameterTransformer} and
* {@link QueryTransformer}.
*
* @author Arthur Gavlyukovskiy
*/
@ConditionalOnClass(ProxyDataSource.class)
@ConditionalOnProperty(name = "spring.sleuth.jdbc.datasource-proxy.enabled", havingValue = "true", matchIfMissing = true)
class DataSourceProxyConfiguration {
@Bean
@ConditionalOnMissingBean
DataSourceProxyConnectionIdManagerProvider traceConnectionIdManagerProvider() {
return GlobalConnectionIdManager::new;
}
@Bean
DataSourceProxyBuilderCustomizer proxyDataSourceBuilderConfigurer(
ObjectProvider<QueryCountStrategy> queryCountStrategy,
ObjectProvider<List<QueryExecutionListener>> listeners,
ObjectProvider<List<MethodExecutionListener>> methodExecutionListeners,
ObjectProvider<ParameterTransformer> parameterTransformer,
ObjectProvider<QueryTransformer> queryTransformer,
ObjectProvider<ResultSetProxyLogicFactory> resultSetProxyLogicFactory,
ObjectProvider<DataSourceProxyConnectionIdManagerProvider> dataSourceProxyConnectionIdManagerProvider,
TraceJdbcProperties traceJdbcProperties) {
return new DataSourceProxyBuilderCustomizer(queryCountStrategy.getIfAvailable(() -> null),
listeners.getIfAvailable(() -> null), methodExecutionListeners.getIfAvailable(() -> null),
parameterTransformer.getIfAvailable(() -> null), queryTransformer.getIfAvailable(() -> null),
resultSetProxyLogicFactory.getIfAvailable(() -> null),
dataSourceProxyConnectionIdManagerProvider.getIfAvailable(() -> null),
props(traceJdbcProperties));
}
private DataSourceProxyProperties props(TraceJdbcProperties traceJdbcProperties) {
TraceJdbcProperties.DataSourceProxyProperties originalProxy = traceJdbcProperties
.getDatasourceProxy();
DataSourceProxyProperties props = new DataSourceProxyProperties();
BeanUtils.copyProperties(originalProxy, props);
props.setLogging(DataSourceProxyProperties.DataSourceProxyLogging.valueOf(originalProxy.getLogging().name()));
return props;
}
@Bean
DataSourceProxyDataSourceDecorator proxyDataSourceDecorator(
DataSourceProxyBuilderCustomizer dataSourceProxyBuilderCustomizer,
DataSourceNameResolver dataSourceNameResolver) {
return new DataSourceProxyDataSourceDecorator(dataSourceProxyBuilderCustomizer, dataSourceNameResolver);
}
@Bean
TraceQueryExecutionListener traceQueryExecutionListener(Tracer tracer,
TraceJdbcProperties dataSourceDecoratorProperties,
ObjectProvider<List<TraceListenerStrategySpanCustomizer>> customizers) {
return new TraceQueryExecutionListener(tracer, dataSourceDecoratorProperties.getIncludes(),
customizers.getIfAvailable(ArrayList::new));
}
@Bean
@ConditionalOnMissingBean
ResultSetProxyLogicFactory traceResultSetProxyLogicFactory() {
return new SimpleResultSetProxyLogicFactory();
}
}

View File

@@ -1,76 +1,78 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import java.util.ArrayList;
import java.util.List;
import com.p6spy.engine.event.JdbcEventListener;
import com.p6spy.engine.spy.DefaultJdbcEventListenerFactory;
import com.p6spy.engine.spy.JdbcEventListenerFactory;
import com.p6spy.engine.spy.P6DataSource;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceNameResolver;
import org.springframework.cloud.sleuth.instrument.jdbc.P6SpyContextJdbcEventListenerFactory;
import org.springframework.cloud.sleuth.instrument.jdbc.P6SpyDataSourceDecorator;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceJdbcEventListener;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceListenerStrategySpanCustomizer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
/**
* Configuration for integration with p6spy, allows to define custom
* {@link JdbcEventListener}.
*
* @author Arthur Gavlyukovskiy
*/
@ConditionalOnClass(P6DataSource.class)
class P6SpyConfiguration {
@Bean
static P6SpyPropertiesSetter p6SpyPropertiesSetter(ConfigurableApplicationContext context) {
return new P6SpyPropertiesSetter(context);
}
@Bean
@ConditionalOnMissingBean
JdbcEventListenerFactory traceJdbcEventListenerFactory(ObjectProvider<List<JdbcEventListener>> listeners) {
JdbcEventListenerFactory jdbcEventListenerFactory = new DefaultJdbcEventListenerFactory();
List<JdbcEventListener> listenerList = listeners.getIfAvailable(() -> null);
return listenerList != null ? new P6SpyContextJdbcEventListenerFactory(jdbcEventListenerFactory, listenerList)
: jdbcEventListenerFactory;
}
@Bean
P6SpyDataSourceDecorator p6SpyDataSourceDecorator(JdbcEventListenerFactory jdbcEventListenerFactory) {
return new P6SpyDataSourceDecorator(jdbcEventListenerFactory);
}
@Bean
TraceJdbcEventListener tracingJdbcEventListener(Tracer tracer, DataSourceNameResolver dataSourceNameResolver,
TraceDataSourceDecoratorProperties dataSourceDecoratorProperties,
ObjectProvider<List<TraceListenerStrategySpanCustomizer>> customizers) {
return new TraceJdbcEventListener(tracer, dataSourceNameResolver, dataSourceDecoratorProperties.getIncludes(),
dataSourceDecoratorProperties.getP6spy().getTracing().isIncludeParameterValues(),
customizers.getIfAvailable(ArrayList::new));
}
}
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import java.util.ArrayList;
import java.util.List;
import com.p6spy.engine.event.JdbcEventListener;
import com.p6spy.engine.spy.DefaultJdbcEventListenerFactory;
import com.p6spy.engine.spy.JdbcEventListenerFactory;
import com.p6spy.engine.spy.P6DataSource;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceNameResolver;
import org.springframework.cloud.sleuth.instrument.jdbc.P6SpyContextJdbcEventListenerFactory;
import org.springframework.cloud.sleuth.instrument.jdbc.P6SpyDataSourceDecorator;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceJdbcEventListener;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceListenerStrategySpanCustomizer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
/**
* Configuration for integration with p6spy, allows to define custom
* {@link JdbcEventListener}.
*
* @author Arthur Gavlyukovskiy
*/
@ConditionalOnClass(P6DataSource.class)
@ConditionalOnProperty(name = "spring.sleuth.jdbc.p6spy.enabled", havingValue = "true", matchIfMissing = true)
class P6SpyConfiguration {
@Bean
static P6SpyPropertiesSetter p6SpyPropertiesSetter(ConfigurableApplicationContext context) {
return new P6SpyPropertiesSetter(context);
}
@Bean
@ConditionalOnMissingBean
JdbcEventListenerFactory traceJdbcEventListenerFactory(ObjectProvider<List<JdbcEventListener>> listeners) {
JdbcEventListenerFactory jdbcEventListenerFactory = new DefaultJdbcEventListenerFactory();
List<JdbcEventListener> listenerList = listeners.getIfAvailable(() -> null);
return listenerList != null ? new P6SpyContextJdbcEventListenerFactory(jdbcEventListenerFactory, listenerList)
: jdbcEventListenerFactory;
}
@Bean
P6SpyDataSourceDecorator p6SpyDataSourceDecorator(JdbcEventListenerFactory jdbcEventListenerFactory) {
return new P6SpyDataSourceDecorator(jdbcEventListenerFactory);
}
@Bean
TraceJdbcEventListener tracingJdbcEventListener(Tracer tracer, DataSourceNameResolver dataSourceNameResolver,
TraceJdbcProperties traceJdbcProperties,
ObjectProvider<List<TraceListenerStrategySpanCustomizer>> customizers) {
return new TraceJdbcEventListener(tracer, dataSourceNameResolver, traceJdbcProperties.getIncludes(),
traceJdbcProperties.getP6spy().getTracing().isIncludeParameterValues(),
customizers.getIfAvailable(ArrayList::new));
}
}

View File

@@ -1,187 +1,187 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.p6spy.engine.logging.P6LogFactory;
import com.p6spy.engine.spy.P6ModuleManager;
import com.p6spy.engine.spy.P6SpyFactory;
import com.p6spy.engine.spy.option.EnvironmentVariables;
import com.p6spy.engine.spy.option.P6OptionsSource;
import com.p6spy.engine.spy.option.SpyDotProperties;
import com.p6spy.engine.spy.option.SystemProperties;
import org.slf4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Sets p6spy properties to / from system properties.
*
* @author Arthur Gavlyukovskiy
* @since 3.1.0
*/
class P6SpyPropertiesSetter implements BeanDefinitionRegistryPostProcessor, Closeable {
private static final Logger log = getLogger(P6SpyPropertiesSetter.class);
private final ConfigurableApplicationContext context;
private final Map<String, String> initialP6SpyOptions;
P6SpyPropertiesSetter(ConfigurableApplicationContext context) {
this.context = context;
this.initialP6SpyOptions = findDefinedOptions();
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
ConfigurableEnvironment environment = this.context.getEnvironment();
String customModuleList = initialP6SpyOptions.get("modulelist");
boolean isEnableLogging = environment.getProperty("spring.sleuth.jdbc.p6spy.enable-logging", Boolean.class,
true);
if (customModuleList != null) {
log.info("P6Spy modulelist is overridden, some p6spy configuration features will not be applied");
}
else {
List<String> moduleList = new ArrayList<>();
// default factory, holds P6Spy configuration
moduleList.add(P6SpyFactory.class.getName());
if (isEnableLogging) {
moduleList.add(P6LogFactory.class.getName());
}
System.setProperty("p6spy.config.modulelist", String.join(",", moduleList));
}
if (!initialP6SpyOptions.containsKey("logMessageFormat")) {
String logFormat = logFormat(environment);
boolean isMultiline = multiLine(environment);
if (logFormat != null) {
System.setProperty("p6spy.config.logMessageFormat", "com.p6spy.engine.spy.appender.CustomLineFormat");
System.setProperty("p6spy.config.customLogMessageFormat", logFormat);
}
else if (isMultiline) {
System.setProperty("p6spy.config.logMessageFormat", "com.p6spy.engine.spy.appender.MultiLineFormat");
}
}
if (isEnableLogging && !initialP6SpyOptions.containsKey("appender")) {
TraceDataSourceDecoratorProperties.P6SpyProperties.P6SpyLogging logging = TraceDataSourceDecoratorProperties.P6SpyProperties.P6SpyLogging
.valueOf(environment
.getProperty("spring.sleuth.jdbc.p6spy.logging", String.class,
TraceDataSourceDecoratorProperties.P6SpyProperties.P6SpyLogging.SLF4J.toString())
.toUpperCase());
switch (logging) {
case SYSOUT:
System.setProperty("p6spy.config.appender", "com.p6spy.engine.spy.appender.StdoutLogger");
break;
case SLF4J:
System.setProperty("p6spy.config.appender", "com.p6spy.engine.spy.appender.Slf4JLogger");
break;
case FILE:
System.setProperty("p6spy.config.appender", "com.p6spy.engine.spy.appender.FileLogger");
break;
case CUSTOM:
String customAppender = environment.getProperty("spring.sleuth.jdbc.p6spy.custom-appender-class",
String.class, "");
System.setProperty("p6spy.config.appender", customAppender);
break;
}
}
if (!initialP6SpyOptions.containsKey("logfile")) {
String logFile = environment.getProperty("spring.sleuth.jdbc.p6spy.log-file", String.class, "spy.log");
System.setProperty("p6spy.config.logfile", logFile);
}
String pattern = environment.getProperty("spring.sleuth.jdbc.p6spy.log-filter.pattern", String.class);
if (pattern != null) {
System.setProperty("p6spy.config.filter", "true");
System.setProperty("p6spy.config.sqlexpression", pattern);
}
// If factories were loaded before this method is initialized changing properties
// will not be applied
// Changes done in this method could not override anything user specified,
// therefore it is safe to call reload
P6ModuleManager.getInstance().reload();
}
private Boolean multiLine(ConfigurableEnvironment environment) {
return environment.getProperty("spring.sleuth.jdbc.p6spy.multiline", Boolean.class, true);
}
private String logFormat(ConfigurableEnvironment environment) {
return environment.getProperty("spring.sleuth.jdbc.p6spy.log-format", String.class);
}
@Override
public void close() throws IOException {
if (!initialP6SpyOptions.containsKey("modulelist")) {
System.clearProperty("p6spy.config.modulelist");
}
if (!initialP6SpyOptions.containsKey("logMessageFormat")) {
ConfigurableEnvironment environment = this.context.getEnvironment();
String logFormat = logFormat(environment);
boolean isMultiline = multiLine(environment);
if (logFormat != null) {
System.clearProperty("p6spy.config.logMessageFormat");
System.clearProperty("p6spy.config.customLogMessageFormat");
}
else if (isMultiline) {
System.clearProperty("p6spy.config.logMessageFormat");
}
}
if (!initialP6SpyOptions.containsKey("appender")) {
System.clearProperty("p6spy.config.appender");
}
if (!initialP6SpyOptions.containsKey("logfile")) {
System.clearProperty("p6spy.config.logfile");
}
P6ModuleManager.getInstance().reload();
}
private Map<String, String> findDefinedOptions() {
SpyDotProperties spyDotProperties = null;
try {
spyDotProperties = new SpyDotProperties();
}
catch (IOException ignored) {
}
return Stream.of(spyDotProperties, new EnvironmentVariables(), new SystemProperties()).filter(Objects::nonNull)
.map(P6OptionsSource::getOptions).filter(Objects::nonNull)
.flatMap(options -> options.entrySet().stream())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
// always using value from the first P6OptionsSource
(value1, value2) -> value1));
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
}
}
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.p6spy.engine.logging.P6LogFactory;
import com.p6spy.engine.spy.P6ModuleManager;
import com.p6spy.engine.spy.P6SpyFactory;
import com.p6spy.engine.spy.option.EnvironmentVariables;
import com.p6spy.engine.spy.option.P6OptionsSource;
import com.p6spy.engine.spy.option.SpyDotProperties;
import com.p6spy.engine.spy.option.SystemProperties;
import org.slf4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Sets p6spy properties to / from system properties.
*
* @author Arthur Gavlyukovskiy
* @since 3.1.0
*/
class P6SpyPropertiesSetter implements BeanDefinitionRegistryPostProcessor, Closeable {
private static final Logger log = getLogger(P6SpyPropertiesSetter.class);
private final ConfigurableApplicationContext context;
private final Map<String, String> initialP6SpyOptions;
P6SpyPropertiesSetter(ConfigurableApplicationContext context) {
this.context = context;
this.initialP6SpyOptions = findDefinedOptions();
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
ConfigurableEnvironment environment = this.context.getEnvironment();
String customModuleList = initialP6SpyOptions.get("modulelist");
boolean isEnableLogging = environment.getProperty("spring.sleuth.jdbc.p6spy.enable-logging", Boolean.class,
true);
if (customModuleList != null) {
log.info("P6Spy modulelist is overridden, some p6spy configuration features will not be applied");
}
else {
List<String> moduleList = new ArrayList<>();
// default factory, holds P6Spy configuration
moduleList.add(P6SpyFactory.class.getName());
if (isEnableLogging) {
moduleList.add(P6LogFactory.class.getName());
}
System.setProperty("p6spy.config.modulelist", String.join(",", moduleList));
}
if (!initialP6SpyOptions.containsKey("logMessageFormat")) {
String logFormat = logFormat(environment);
boolean isMultiline = multiLine(environment);
if (logFormat != null) {
System.setProperty("p6spy.config.logMessageFormat", "com.p6spy.engine.spy.appender.CustomLineFormat");
System.setProperty("p6spy.config.customLogMessageFormat", logFormat);
}
else if (isMultiline) {
System.setProperty("p6spy.config.logMessageFormat", "com.p6spy.engine.spy.appender.MultiLineFormat");
}
}
if (isEnableLogging && !initialP6SpyOptions.containsKey("appender")) {
TraceJdbcProperties.P6SpyProperties.P6SpyLogging logging = TraceJdbcProperties.P6SpyProperties.P6SpyLogging
.valueOf(environment
.getProperty("spring.sleuth.jdbc.p6spy.logging", String.class,
TraceJdbcProperties.P6SpyProperties.P6SpyLogging.SLF4J.toString())
.toUpperCase());
switch (logging) {
case SYSOUT:
System.setProperty("p6spy.config.appender", "com.p6spy.engine.spy.appender.StdoutLogger");
break;
case SLF4J:
System.setProperty("p6spy.config.appender", "com.p6spy.engine.spy.appender.Slf4JLogger");
break;
case FILE:
System.setProperty("p6spy.config.appender", "com.p6spy.engine.spy.appender.FileLogger");
break;
case CUSTOM:
String customAppender = environment.getProperty("spring.sleuth.jdbc.p6spy.custom-appender-class",
String.class, "");
System.setProperty("p6spy.config.appender", customAppender);
break;
}
}
if (!initialP6SpyOptions.containsKey("logfile")) {
String logFile = environment.getProperty("spring.sleuth.jdbc.p6spy.log-file", String.class, "spy.log");
System.setProperty("p6spy.config.logfile", logFile);
}
String pattern = environment.getProperty("spring.sleuth.jdbc.p6spy.log-filter.pattern", String.class);
if (pattern != null) {
System.setProperty("p6spy.config.filter", "true");
System.setProperty("p6spy.config.sqlexpression", pattern);
}
// If factories were loaded before this method is initialized changing properties
// will not be applied
// Changes done in this method could not override anything user specified,
// therefore it is safe to call reload
P6ModuleManager.getInstance().reload();
}
private Boolean multiLine(ConfigurableEnvironment environment) {
return environment.getProperty("spring.sleuth.jdbc.p6spy.multiline", Boolean.class, true);
}
private String logFormat(ConfigurableEnvironment environment) {
return environment.getProperty("spring.sleuth.jdbc.p6spy.log-format", String.class);
}
@Override
public void close() throws IOException {
if (!initialP6SpyOptions.containsKey("modulelist")) {
System.clearProperty("p6spy.config.modulelist");
}
if (!initialP6SpyOptions.containsKey("logMessageFormat")) {
ConfigurableEnvironment environment = this.context.getEnvironment();
String logFormat = logFormat(environment);
boolean isMultiline = multiLine(environment);
if (logFormat != null) {
System.clearProperty("p6spy.config.logMessageFormat");
System.clearProperty("p6spy.config.customLogMessageFormat");
}
else if (isMultiline) {
System.clearProperty("p6spy.config.logMessageFormat");
}
}
if (!initialP6SpyOptions.containsKey("appender")) {
System.clearProperty("p6spy.config.appender");
}
if (!initialP6SpyOptions.containsKey("logfile")) {
System.clearProperty("p6spy.config.logfile");
}
P6ModuleManager.getInstance().reload();
}
private Map<String, String> findDefinedOptions() {
SpyDotProperties spyDotProperties = null;
try {
spyDotProperties = new SpyDotProperties();
}
catch (IOException ignored) {
}
return Stream.of(spyDotProperties, new EnvironmentVariables(), new SystemProperties()).filter(Objects::nonNull)
.map(P6OptionsSource::getOptions).filter(Objects::nonNull)
.flatMap(options -> options.entrySet().stream())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
// always using value from the first P6OptionsSource
(value1, value2) -> value1));
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
}
}

View File

@@ -1,140 +1,140 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.sql.DataSource;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.scope.ScopedProxyUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceDecorator;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceNameResolver;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceWrapper;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.util.ClassUtils;
/**
* {@link BeanPostProcessor} that wraps all data source beans in {@link DataSource}
* proxies specified in property 'spring.datasource.type'.
*
* @author Arthur Gavlyukovskiy
* @since 3.1.0
*/
public class TraceDataSourceDecoratorBeanPostProcessor implements BeanPostProcessor, Ordered, ApplicationContextAware {
private static final Log log = LogFactory.getLog(TraceDataSourceDecoratorBeanPostProcessor.class);
private final static boolean HIKARI_AVAILABLE = ClassUtils.isPresent("com.zaxxer.hikari.HikariDataSource",
DataSourceNameResolver.class.getClassLoader());
private ApplicationContext applicationContext;
private DataSourceNameResolver dataSourceNameResolver;
private final Collection<String> excludedBeans;
public TraceDataSourceDecoratorBeanPostProcessor(Collection<String> excludedBeans) {
this.excludedBeans = excludedBeans;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof DataSource && !ScopedProxyUtils.isScopedTarget(beanName)
&& !this.excludedBeans.contains(beanName)) {
// TODO: This might be a problem
Map<String, DataSourceDecorator> decorators = this.applicationContext
.getBeansOfType(DataSourceDecorator.class).entrySet().stream()
.sorted(Entry.comparingByValue(AnnotationAwareOrderComparator.INSTANCE))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, (v1, v2) -> v2, LinkedHashMap::new));
return decorate((DataSource) bean, getDataSourceName(bean, beanName), decorators);
}
else {
return bean;
}
}
private String getDataSourceName(Object bean, String beanName) {
if (HIKARI_AVAILABLE && bean instanceof HikariDataSource) {
HikariDataSource hikariDataSource = (HikariDataSource) bean;
if (hikariDataSource.getPoolName() != null && !hikariDataSource.getPoolName().startsWith("HikariPool-")) {
return hikariDataSource.getPoolName();
}
}
return beanName;
}
private DataSource decorate(DataSource dataSource, String name, Map<String, DataSourceDecorator> decorators) {
getDataSourceNameResolver().addDataSource(name, dataSource);
DataSource decoratedDataSource = dataSource;
for (Entry<String, DataSourceDecorator> decoratorEntry : decorators.entrySet()) {
String decoratorBeanName = decoratorEntry.getKey();
DataSourceDecorator decorator = decoratorEntry.getValue();
DataSource dataSourceBeforeDecorating = decoratedDataSource;
decoratedDataSource = Objects.requireNonNull(decorator.decorate(name, decoratedDataSource),
"DataSourceDecorator (" + decoratorBeanName + ", " + decorator + ") should not return null");
if (dataSourceBeforeDecorating != decoratedDataSource) {
getDataSourceNameResolver().addDataSource(name, decoratedDataSource);
}
}
if (dataSource != decoratedDataSource) {
if (log.isDebugEnabled()) {
log.debug("The decorated data source [" + decoratedDataSource + "] will replace the original one ["
+ dataSource + "]");
}
decoratedDataSource = new DataSourceWrapper(dataSource, decoratedDataSource);
getDataSourceNameResolver().addDataSource(name, decoratedDataSource);
}
return decoratedDataSource;
}
private DataSourceNameResolver getDataSourceNameResolver() {
if (this.dataSourceNameResolver == null) {
this.dataSourceNameResolver = this.applicationContext.getBean(DataSourceNameResolver.class);
}
return this.dataSourceNameResolver;
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE - 10;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.sql.DataSource;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.scope.ScopedProxyUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceDecorator;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceNameResolver;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceWrapper;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.util.ClassUtils;
/**
* {@link BeanPostProcessor} that wraps all data source beans in {@link DataSource}
* proxies specified in property 'spring.datasource.type'.
*
* @author Arthur Gavlyukovskiy
* @since 3.1.0
*/
public class TraceDataSourceDecoratorBeanPostProcessor implements BeanPostProcessor, Ordered, ApplicationContextAware {
private static final Log log = LogFactory.getLog(TraceDataSourceDecoratorBeanPostProcessor.class);
private final static boolean HIKARI_AVAILABLE = ClassUtils.isPresent("com.zaxxer.hikari.HikariDataSource",
DataSourceNameResolver.class.getClassLoader());
private ApplicationContext applicationContext;
private DataSourceNameResolver dataSourceNameResolver;
private final Collection<String> excludedBeans;
public TraceDataSourceDecoratorBeanPostProcessor(Collection<String> excludedBeans) {
this.excludedBeans = excludedBeans;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof DataSource && !ScopedProxyUtils.isScopedTarget(beanName)
&& !this.excludedBeans.contains(beanName)) {
// TODO: This might be a problem - it can lead to eager bean init
Map<String, DataSourceDecorator> decorators = this.applicationContext
.getBeansOfType(DataSourceDecorator.class).entrySet().stream()
.sorted(Entry.comparingByValue(AnnotationAwareOrderComparator.INSTANCE))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, (v1, v2) -> v2, LinkedHashMap::new));
return decorate((DataSource) bean, getDataSourceName(bean, beanName), decorators);
}
else {
return bean;
}
}
private String getDataSourceName(Object bean, String beanName) {
if (HIKARI_AVAILABLE && bean instanceof HikariDataSource) {
HikariDataSource hikariDataSource = (HikariDataSource) bean;
if (hikariDataSource.getPoolName() != null && !hikariDataSource.getPoolName().startsWith("HikariPool-")) {
return hikariDataSource.getPoolName();
}
}
return beanName;
}
private DataSource decorate(DataSource dataSource, String name, Map<String, DataSourceDecorator> decorators) {
getDataSourceNameResolver().addDataSource(name, dataSource);
DataSource decoratedDataSource = dataSource;
for (Entry<String, DataSourceDecorator> decoratorEntry : decorators.entrySet()) {
String decoratorBeanName = decoratorEntry.getKey();
DataSourceDecorator decorator = decoratorEntry.getValue();
DataSource dataSourceBeforeDecorating = decoratedDataSource;
decoratedDataSource = Objects.requireNonNull(decorator.decorate(name, decoratedDataSource),
"DataSourceDecorator (" + decoratorBeanName + ", " + decorator + ") should not return null");
if (dataSourceBeforeDecorating != decoratedDataSource) {
getDataSourceNameResolver().addDataSource(name, decoratedDataSource);
}
}
if (dataSource != decoratedDataSource) {
if (log.isDebugEnabled()) {
log.debug("The decorated data source [" + decoratedDataSource + "] will replace the original one ["
+ dataSource + "]");
}
decoratedDataSource = new DataSourceWrapper(dataSource, decoratedDataSource);
getDataSourceNameResolver().addDataSource(name, decoratedDataSource);
}
return decoratedDataSource;
}
private DataSourceNameResolver getDataSourceNameResolver() {
if (this.dataSourceNameResolver == null) {
this.dataSourceNameResolver = this.applicationContext.getBean(DataSourceNameResolver.class);
}
return this.dataSourceNameResolver;
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE - 10;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}

View File

@@ -1,70 +1,70 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceDecorator;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceNameResolver;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceHikariListenerStrategySpanCustomizer;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceListenerStrategySpanCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* {@link EnableAutoConfiguration Auto-configuration} for proxying DataSource.
*
* @author Arthur Gavlyukovskiy
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(TraceDataSourceDecoratorProperties.class)
@ConditionalOnProperty(name = "spring.sleuth.jdbc.enabled", havingValue = "true", matchIfMissing = true)
@ConditionalOnBean({ DataSource.class, Tracer.class })
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
@Import({ P6SpyConfiguration.class, DataSourceProxyConfiguration.class })
public class TraceDataSourceDecoratorAutoConfiguration {
@Bean
@ConditionalOnBean(DataSourceDecorator.class)
static TraceDataSourceDecoratorBeanPostProcessor traceDataSourceDecoratorBeanPostProcessor(
TraceDataSourceDecoratorProperties dataSourceDecoratorProperties) {
return new TraceDataSourceDecoratorBeanPostProcessor(dataSourceDecoratorProperties.getExcludedBeans());
}
@Bean
@ConditionalOnMissingBean
DataSourceNameResolver traceDataSourceNameResolver() {
return new DataSourceNameResolver();
}
@Bean
@ConditionalOnClass(name = "com.zaxxer.hikari.HikariDataSource")
TraceListenerStrategySpanCustomizer hikariTraceListenerStrategySpanCustomizer() {
return new TraceHikariListenerStrategySpanCustomizer();
}
}
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceDecorator;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceNameResolver;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceHikariListenerStrategySpanCustomizer;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceListenerStrategySpanCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* {@link EnableAutoConfiguration Auto-configuration} for proxying DataSource.
*
* @author Arthur Gavlyukovskiy
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(TraceJdbcProperties.class)
@ConditionalOnProperty(name = "spring.sleuth.jdbc.enabled", havingValue = "true", matchIfMissing = true)
@ConditionalOnBean({ DataSource.class, Tracer.class })
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
@Import({ P6SpyConfiguration.class, DataSourceProxyConfiguration.class })
public class TraceJdbcAutoConfiguration {
@Bean
@ConditionalOnBean(DataSourceDecorator.class)
static TraceDataSourceDecoratorBeanPostProcessor traceDataSourceDecoratorBeanPostProcessor(
TraceJdbcProperties dataSourceDecoratorProperties) {
return new TraceDataSourceDecoratorBeanPostProcessor(dataSourceDecoratorProperties.getExcludedDataSourceBeanNames());
}
@Bean
@ConditionalOnMissingBean
DataSourceNameResolver traceDataSourceNameResolver() {
return new DataSourceNameResolver();
}
@Bean
@ConditionalOnClass(name = "com.zaxxer.hikari.HikariDataSource")
TraceListenerStrategySpanCustomizer hikariTraceListenerStrategySpanCustomizer() {
return new TraceHikariListenerStrategySpanCustomizer();
}
}

View File

@@ -1,485 +1,511 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceType;
/**
* Properties for configuring proxy providers.
*
* @author Arthur Gavlyukovskiy
* @since 3.1.0
*/
@ConfigurationProperties(prefix = "spring.sleuth.jdbc")
public class TraceDataSourceDecoratorProperties {
/**
* Enables data source decorating.
*/
private boolean enabled = true;
/**
* Beans that won't be decorated.
*/
private Collection<String> excludedBeans = Collections.emptyList();
/**
* Which types of tracing we would like to include.
*/
private List<TraceType> includes = Arrays.asList(TraceType.CONNECTION, TraceType.QUERY, TraceType.FETCH);
private DataSourceProxyProperties datasourceProxy = new DataSourceProxyProperties();
private P6SpyProperties p6spy = new P6SpyProperties();
public boolean isEnabled() {
return enabled;
}
public Collection<String> getExcludedBeans() {
return excludedBeans;
}
public List<TraceType> getIncludes() {
return includes;
}
public void setIncludes(List<TraceType> includes) {
this.includes = includes;
}
public DataSourceProxyProperties getDatasourceProxy() {
return datasourceProxy;
}
public P6SpyProperties getP6spy() {
return p6spy;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void setExcludedBeans(Collection<String> excludedBeans) {
this.excludedBeans = excludedBeans;
}
public void setDatasourceProxy(DataSourceProxyProperties datasourceProxy) {
this.datasourceProxy = datasourceProxy;
}
public void setP6spy(P6SpyProperties p6spy) {
this.p6spy = p6spy;
}
/**
* Properties for datasource-proxy.
*/
public static class DataSourceProxyProperties {
/**
* Logging to use for logging queries.
*/
private DataSourceProxyLogging logging = DataSourceProxyLogging.SLF4J;
/**
* Query configuration.
*/
private Query query = new Query();
/**
* Slow query configuration.
*/
private SlowQuery slowQuery = new SlowQuery();
/**
* Use multiline output for logging query.
*
* @see ProxyDataSourceBuilder#multiline()
*/
private boolean multiline = true;
/**
* Use json output for logging query.
*
* @see ProxyDataSourceBuilder#asJson()
*/
private boolean jsonFormat = false;
public DataSourceProxyLogging getLogging() {
return logging;
}
public void setLogging(DataSourceProxyLogging logging) {
this.logging = logging;
}
public Query getQuery() {
return query;
}
public void setQuery(Query query) {
this.query = query;
}
public SlowQuery getSlowQuery() {
return slowQuery;
}
public void setSlowQuery(SlowQuery slowQuery) {
this.slowQuery = slowQuery;
}
public boolean isMultiline() {
return multiline;
}
public void setMultiline(boolean multiline) {
this.multiline = multiline;
}
public boolean isJsonFormat() {
return jsonFormat;
}
public void setJsonFormat(boolean jsonFormat) {
this.jsonFormat = jsonFormat;
}
/**
* Properties to configure query logging listener.
*/
public static class Query {
/**
* Enable logging all queries to the log.
*/
private boolean enableLogging = true;
/**
* Name of query logger.
*/
private String loggerName;
/**
* Severity of query logger.
*/
private String logLevel = "DEBUG";
public boolean isEnableLogging() {
return enableLogging;
}
public void setEnableLogging(boolean enableLogging) {
this.enableLogging = enableLogging;
}
public String getLoggerName() {
return loggerName;
}
public void setLoggerName(String loggerName) {
this.loggerName = loggerName;
}
public String getLogLevel() {
return logLevel;
}
public void setLogLevel(String logLevel) {
this.logLevel = logLevel;
}
}
/**
* Properties to configure slow query logging listener.
*/
public static class SlowQuery {
/**
* Enable logging slow queries to the log.
*/
private boolean enableLogging = true;
/**
* Name of slow query logger.
*/
private String loggerName;
/**
* Severity of slow query logger.
*/
private String logLevel = "WARN";
/**
* Number of seconds to consider query as slow.
*/
private long threshold = 300;
boolean isEnableLogging() {
return enableLogging;
}
public void setEnableLogging(boolean enableLogging) {
this.enableLogging = enableLogging;
}
public String getLoggerName() {
return loggerName;
}
public void setLoggerName(String loggerName) {
this.loggerName = loggerName;
}
public String getLogLevel() {
return logLevel;
}
public void setLogLevel(String logLevel) {
this.logLevel = logLevel;
}
public long getThreshold() {
return threshold;
}
public void setThreshold(long threshold) {
this.threshold = threshold;
}
}
/**
* Query logging listener is the most used listener that logs executing query with
* actual parameters to. You can pick one of the following proxy logging
* mechanisms.
*/
public enum DataSourceProxyLogging {
/**
* Log using System.out.
*/
SYSOUT,
/**
* Log using SLF4J.
*/
SLF4J,
/**
* Log using Commons.
*/
COMMONS,
/**
* Log using Java Util Logging.
*/
JUL
}
}
/**
* Properties for configuring p6spy.
*/
public static class P6SpyProperties {
/**
* Enables logging JDBC events.
*/
private boolean enableLogging = true;
/**
* Enables multiline output.
*/
private boolean multiline = true;
/**
* Logging to use for logging queries.
*/
private P6SpyLogging logging = P6SpyLogging.SLF4J;
/**
* Name of log file to use (only with logging=file).
*/
private String logFile = "spy.log";
/**
* Custom log format.
*/
private String logFormat;
/**
* Tracing related properties.
*/
private P6SpyTracing tracing = new P6SpyTracing();
/**
* Class file to use (only with logging=custom). The class must implement
* {@link com.p6spy.engine.spy.appender.FormattedLogger}.
*/
private String customAppenderClass;
/**
* Log filtering related properties.
*/
private P6SpyLogFilter logFilter = new P6SpyLogFilter();
public boolean isEnableLogging() {
return enableLogging;
}
public void setEnableLogging(boolean enableLogging) {
this.enableLogging = enableLogging;
}
public boolean isMultiline() {
return multiline;
}
public void setMultiline(boolean multiline) {
this.multiline = multiline;
}
public P6SpyLogging getLogging() {
return logging;
}
public void setLogging(P6SpyLogging logging) {
this.logging = logging;
}
public String getLogFile() {
return logFile;
}
public void setLogFile(String logFile) {
this.logFile = logFile;
}
public String getLogFormat() {
return logFormat;
}
public void setLogFormat(String logFormat) {
this.logFormat = logFormat;
}
public P6SpyTracing getTracing() {
return tracing;
}
public void setTracing(P6SpyTracing tracing) {
this.tracing = tracing;
}
public String getCustomAppenderClass() {
return customAppenderClass;
}
public void setCustomAppenderClass(String customAppenderClass) {
this.customAppenderClass = customAppenderClass;
}
public P6SpyLogFilter getLogFilter() {
return logFilter;
}
public void setLogFilter(P6SpyLogFilter logFilter) {
this.logFilter = logFilter;
}
/**
* P6Spy logging options.
*/
public enum P6SpyLogging {
/**
* Log using System.out.
*/
SYSOUT,
/**
* Log using SLF4J.
*/
SLF4J,
/**
* Log to file.
*/
FILE,
/**
* Custom logging.
*/
CUSTOM
}
public static class P6SpyTracing {
/**
* Report the effective sql string (with '?' replaced with real values) to
* tracing systems.
* <p>
* NOTE this setting does not affect the logging message.
*/
private boolean includeParameterValues = true;
public boolean isIncludeParameterValues() {
return includeParameterValues;
}
public void setIncludeParameterValues(boolean includeParameterValues) {
this.includeParameterValues = includeParameterValues;
}
}
public static class P6SpyLogFilter {
/**
* Use regex pattern to filter log messages. Only matched messages will be
* logged.
*/
private Pattern pattern;
public Pattern getPattern() {
return pattern;
}
public void setPattern(Pattern pattern) {
this.pattern = pattern;
}
}
}
}
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceType;
/**
* Properties for JDBC instrumentation.
*
* @author Arthur Gavlyukovskiy
* @since 3.1.0
*/
@ConfigurationProperties(prefix = "spring.sleuth.jdbc")
public class TraceJdbcProperties {
/**
* Enables JDBC instrumentation.
*/
private boolean enabled = true;
/**
* List of DataSource bean names that will not be decorated.
*/
private Collection<String> excludedDataSourceBeanNames = Collections.emptyList();
/**
* Which types of tracing we would like to include.
*/
private List<TraceType> includes = Arrays.asList(TraceType.CONNECTION, TraceType.QUERY, TraceType.FETCH);
private DataSourceProxyProperties datasourceProxy = new DataSourceProxyProperties();
private P6SpyProperties p6spy = new P6SpyProperties();
public boolean isEnabled() {
return this.enabled;
}
public Collection<String> getExcludedDataSourceBeanNames() {
return this.excludedDataSourceBeanNames;
}
public List<TraceType> getIncludes() {
return this.includes;
}
public void setIncludes(List<TraceType> includes) {
this.includes = includes;
}
public DataSourceProxyProperties getDatasourceProxy() {
return datasourceProxy;
}
public P6SpyProperties getP6spy() {
return this.p6spy;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void setExcludedDataSourceBeanNames(Collection<String> excludedDataSourceBeanNames) {
this.excludedDataSourceBeanNames = excludedDataSourceBeanNames;
}
public void setDatasourceProxy(DataSourceProxyProperties datasourceProxy) {
this.datasourceProxy = datasourceProxy;
}
public void setP6spy(P6SpyProperties p6spy) {
this.p6spy = p6spy;
}
/**
* Properties for datasource-proxy.
*/
public static class DataSourceProxyProperties {
/**
* Should the datasource-proxy tracing be enabled?
*/
private boolean enabled = true;
/**
* Logging to use for logging queries.
*/
private DataSourceProxyLogging logging = DataSourceProxyLogging.SLF4J;
/**
* Query configuration.
*/
private Query query = new Query();
/**
* Slow query configuration.
*/
private SlowQuery slowQuery = new SlowQuery();
/**
* Use multiline output for logging query.
*
* @see ProxyDataSourceBuilder#multiline()
*/
private boolean multiline = true;
/**
* Use json output for logging query.
*
* @see ProxyDataSourceBuilder#asJson()
*/
private boolean jsonFormat = false;
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public DataSourceProxyLogging getLogging() {
return this.logging;
}
public void setLogging(DataSourceProxyLogging logging) {
this.logging = logging;
}
public Query getQuery() {
return this.query;
}
public void setQuery(Query query) {
this.query = query;
}
public SlowQuery getSlowQuery() {
return this.slowQuery;
}
public void setSlowQuery(SlowQuery slowQuery) {
this.slowQuery = slowQuery;
}
public boolean isMultiline() {
return this.multiline;
}
public void setMultiline(boolean multiline) {
this.multiline = multiline;
}
public boolean isJsonFormat() {
return this.jsonFormat;
}
public void setJsonFormat(boolean jsonFormat) {
this.jsonFormat = jsonFormat;
}
/**
* Properties to configure query logging listener.
*/
public static class Query {
/**
* Enable logging all queries to the log.
*/
private boolean enableLogging = true;
/**
* Name of query logger.
*/
private String loggerName;
/**
* Severity of query logger.
*/
private String logLevel = "DEBUG";
public boolean isEnableLogging() {
return this.enableLogging;
}
public void setEnableLogging(boolean enableLogging) {
this.enableLogging = enableLogging;
}
public String getLoggerName() {
return this.loggerName;
}
public void setLoggerName(String loggerName) {
this.loggerName = loggerName;
}
public String getLogLevel() {
return this.logLevel;
}
public void setLogLevel(String logLevel) {
this.logLevel = logLevel;
}
}
/**
* Properties to configure slow query logging listener.
*/
public static class SlowQuery {
/**
* Enable logging slow queries to the log.
*/
private boolean enableLogging = true;
/**
* Name of slow query logger.
*/
private String loggerName;
/**
* Severity of slow query logger.
*/
private String logLevel = "WARN";
/**
* Number of seconds to consider query as slow.
*/
private long threshold = 300;
boolean isEnableLogging() {
return enableLogging;
}
public void setEnableLogging(boolean enableLogging) {
this.enableLogging = enableLogging;
}
public String getLoggerName() {
return loggerName;
}
public void setLoggerName(String loggerName) {
this.loggerName = loggerName;
}
public String getLogLevel() {
return logLevel;
}
public void setLogLevel(String logLevel) {
this.logLevel = logLevel;
}
public long getThreshold() {
return threshold;
}
public void setThreshold(long threshold) {
this.threshold = threshold;
}
}
/**
* Query logging listener is the most used listener that logs executing query with
* actual parameters to. You can pick one of the following proxy logging
* mechanisms.
*/
public enum DataSourceProxyLogging {
/**
* Log using System.out.
*/
SYSOUT,
/**
* Log using SLF4J.
*/
SLF4J,
/**
* Log using Commons.
*/
COMMONS,
/**
* Log using Java Util Logging.
*/
JUL
}
}
/**
* Properties for configuring p6spy.
*/
public static class P6SpyProperties {
/**
* Should the p6spy tracing be enabled?
*/
private boolean enabled = true;
/**
* Enables logging JDBC events.
*/
private boolean enableLogging = true;
/**
* Enables multiline output.
*/
private boolean multiline = true;
/**
* Logging to use for logging queries.
*/
private P6SpyLogging logging = P6SpyLogging.SLF4J;
/**
* Name of log file to use (only with logging=file).
*/
private String logFile = "spy.log";
/**
* Custom log format.
*/
private String logFormat;
/**
* Tracing related properties.
*/
private P6SpyTracing tracing = new P6SpyTracing();
/**
* Class file to use (only with logging=custom). The class must implement
* {@link com.p6spy.engine.spy.appender.FormattedLogger}.
*/
private String customAppenderClass;
/**
* Log filtering related properties.
*/
private P6SpyLogFilter logFilter = new P6SpyLogFilter();
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isEnableLogging() {
return this.enableLogging;
}
public void setEnableLogging(boolean enableLogging) {
this.enableLogging = enableLogging;
}
public boolean isMultiline() {
return this.multiline;
}
public void setMultiline(boolean multiline) {
this.multiline = multiline;
}
public P6SpyLogging getLogging() {
return this.logging;
}
public void setLogging(P6SpyLogging logging) {
this.logging = logging;
}
public String getLogFile() {
return this.logFile;
}
public void setLogFile(String logFile) {
this.logFile = logFile;
}
public String getLogFormat() {
return this.logFormat;
}
public void setLogFormat(String logFormat) {
this.logFormat = logFormat;
}
public P6SpyTracing getTracing() {
return this.tracing;
}
public void setTracing(P6SpyTracing tracing) {
this.tracing = tracing;
}
public String getCustomAppenderClass() {
return this.customAppenderClass;
}
public void setCustomAppenderClass(String customAppenderClass) {
this.customAppenderClass = customAppenderClass;
}
public P6SpyLogFilter getLogFilter() {
return this.logFilter;
}
public void setLogFilter(P6SpyLogFilter logFilter) {
this.logFilter = logFilter;
}
/**
* P6Spy logging options.
*/
public enum P6SpyLogging {
/**
* Log using System.out.
*/
SYSOUT,
/**
* Log using SLF4J.
*/
SLF4J,
/**
* Log to file.
*/
FILE,
/**
* Custom logging.
*/
CUSTOM
}
public static class P6SpyTracing {
/**
* Report the effective sql string (with '?' replaced with real values) to
* tracing systems.
* <p>
* NOTE this setting does not affect the logging message.
*/
private boolean includeParameterValues = true;
public boolean isIncludeParameterValues() {
return this.includeParameterValues;
}
public void setIncludeParameterValues(boolean includeParameterValues) {
this.includeParameterValues = includeParameterValues;
}
}
public static class P6SpyLogFilter {
/**
* Use regex pattern to filter log messages. Only matched messages will be
* logged.
*/
private Pattern pattern;
public Pattern getPattern() {
return this.pattern;
}
public void setPattern(Pattern pattern) {
this.pattern = pattern;
}
}
}
}

View File

@@ -9,7 +9,7 @@ org.springframework.cloud.sleuth.autoconfig.instrument.batch.TraceBatchAutoConfi
org.springframework.cloud.sleuth.autoconfig.instrument.config.TraceSpringCloudConfigAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.circuitbreaker.TraceCircuitBreakerAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.deployer.TraceDeployerAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.jdbc.TraceDataSourceDecoratorAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.jdbc.TraceJdbcAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.rxjava.TraceRxJavaAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.quartz.TraceQuartzAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.task.TraceTaskAutoConfiguration,\

View File

@@ -1,88 +1,104 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.CurrentTraceContext;
import org.springframework.cloud.sleuth.SpanCustomizer;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.autoconfig.instrument.web.ConditionalOnSleuthWeb;
import org.springframework.cloud.sleuth.autoconfig.instrument.web.client.ConditionalnOnSleuthWebClient;
import org.springframework.cloud.sleuth.http.HttpClientHandler;
import org.springframework.cloud.sleuth.http.HttpServerHandler;
import org.springframework.cloud.sleuth.propagation.Propagator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} to enable tracing via Spring Cloud Sleuth.
*
* @author Spencer Gibb
* @author Marcin Grzejszczak
* @author Tim Ysewyn
* @since 2.0.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty("spring.sleuth.noop.enabled")
@AutoConfigureBefore(BraveAutoConfiguration.class)
@Import(TraceConfiguration.class)
public class TraceNoOpAutoConfiguration {
@Bean
@ConditionalOnMissingBean
Tracer defaultTracer() {
return new NoOpTracer();
}
@Bean
Propagator defaultPropagator() {
return new NoOpPropagator();
}
@Bean
CurrentTraceContext defaultCurrentTraceContext() {
return new NoOpCurrentTraceContext();
}
@Bean
SpanCustomizer defaultSpanCustomizer() {
return new NoOpSpanCustomizer();
}
@Configuration(proxyBeanMethods = false)
static class TraceHttpConfiguration {
@Bean
@ConditionalnOnSleuthWebClient
HttpClientHandler defaultHttpClientHandler() {
return new NoOpHttpClientHandler();
}
@Bean
@ConditionalOnSleuthWeb
HttpServerHandler defaultHttpServerHandler() {
return new NoOpHttpServerHandler();
}
}
}
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.CurrentTraceContext;
import org.springframework.cloud.sleuth.SpanCustomizer;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.autoconfig.instrument.web.ConditionalOnSleuthWeb;
import org.springframework.cloud.sleuth.autoconfig.instrument.web.client.ConditionalnOnSleuthWebClient;
import org.springframework.cloud.sleuth.http.HttpClientHandler;
import org.springframework.cloud.sleuth.http.HttpServerHandler;
import org.springframework.cloud.sleuth.propagation.Propagator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} to enable tracing via Spring Cloud Sleuth.
*
* @author Spencer Gibb
* @author Marcin Grzejszczak
* @author Tim Ysewyn
* @since 2.0.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnSleuth
@ConditionalOnProperty("spring.sleuth.noop.enabled")
@AutoConfigureBefore(BraveAutoConfiguration.class)
@Import(TraceConfiguration.class)
public class TraceNoOpAutoConfiguration {
@Bean
@ConditionalOnMissingBean
Tracer defaultTracer() {
return new NoOpTracer();
}
@Bean
Propagator defaultPropagator() {
return new NoOpPropagator();
}
@Bean
CurrentTraceContext defaultCurrentTraceContext() {
return new NoOpCurrentTraceContext();
}
@Bean
SpanCustomizer defaultSpanCustomizer() {
return new NoOpSpanCustomizer();
}
@Configuration(proxyBeanMethods = false)
static class TraceHttpConfiguration {
@Bean
@ConditionalnOnSleuthWebClient
HttpClientHandler defaultHttpClientHandler() {
return new NoOpHttpClientHandler();
}
@Bean
@ConditionalOnSleuthWeb
HttpServerHandler defaultHttpServerHandler() {
return new NoOpHttpServerHandler();
}
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@ConditionalOnProperty(value = "spring.sleuth.enabled", matchIfMissing = true)
@interface ConditionalOnSleuth {
}

View File

@@ -1,370 +1,371 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import java.io.PrintWriter;
import java.sql.Connection;
import java.util.concurrent.ThreadLocalRandom;
import java.util.logging.Logger;
import javax.sql.DataSource;
import com.p6spy.engine.spy.P6DataSource;
import com.zaxxer.hikari.HikariDataSource;
import net.ttddyy.dsproxy.support.ProxyDataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.junit.jupiter.api.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceDecorator;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceWrapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
class DataSourceDecoratorAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TraceDataSourceDecoratorAutoConfiguration.class, BraveAutoConfiguration.class,
TestSpanHandlerConfiguration.class, PropertyPlaceholderAutoConfiguration.class))
.withPropertyValues("spring.datasource.initialization-mode=never",
"spring.datasource.url:jdbc:h2:mem:testdb-" + ThreadLocalRandom.current().nextInt());
@Test
void testDecoratingInDefaultOrder() {
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
assertThat(dataSource).isInstanceOf(DataSourceWrapper.class);
DataSourceWrapper DataSourceWrapper = (DataSourceWrapper) dataSource;
assertThat(DataSourceWrapper.getDecoratedDataSource()).isInstanceOf(P6DataSource.class);
P6DataSource p6DataSource = (P6DataSource) DataSourceWrapper.getDecoratedDataSource();
DataSource p6WrappedDataSource = (DataSource) ReflectionTestUtils.getField(p6DataSource, "realDataSource");
assertThat(p6WrappedDataSource).isInstanceOf(ProxyDataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) p6WrappedDataSource;
});
}
@Test
void testNoDecoratingForExcludeBeans() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.excluded-beans:dataSource");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
assertThat(dataSource).isInstanceOf(HikariDataSource.class);
});
}
@Test
void testDecoratingWhenDefaultProxyProviderNotAvailable() {
ApplicationContextRunner contextRunner = this.contextRunner;
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
assertThat(((DataSourceWrapper) dataSource).getOriginalDataSource()).isInstanceOf(HikariDataSource.class);
DataSourceWrapper DataSourceWrapper = (DataSourceWrapper) dataSource;
assertThat(DataSourceWrapper.getDecoratedDataSource()).isInstanceOf(P6DataSource.class);
P6DataSource p6DataSource = (P6DataSource) DataSourceWrapper.getDecoratedDataSource();
DataSource p6WrappedDataSource = (DataSource) ReflectionTestUtils.getField(p6DataSource, "realDataSource");
assertThat(p6WrappedDataSource).isInstanceOf(ProxyDataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) p6WrappedDataSource;
DataSource dsProxyWrappedDataSource = (DataSource) ReflectionTestUtils.getField(proxyDataSource,
"dataSource");
assertThat(dsProxyWrappedDataSource).isEqualTo(DataSourceWrapper.getOriginalDataSource());
});
}
@Test
void testDecoratedHikariSpecificPropertiesIsSet() {
ApplicationContextRunner contextRunner = this.contextRunner.withPropertyValues(
"spring.datasource.type:" + HikariDataSource.class.getName(),
"spring.datasource.hikari.catalog:test_catalog");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
assertThat(dataSource).isNotNull();
assertThat(dataSource).isInstanceOf(DataSourceWrapper.class);
DataSource realDataSource = ((DataSourceWrapper) dataSource).getOriginalDataSource();
assertThat(realDataSource).isInstanceOf(HikariDataSource.class);
assertThat(((HikariDataSource) realDataSource).getCatalog()).isEqualTo("test_catalog");
});
}
@Test
void testCustomDataSourceIsDecorated() {
ApplicationContextRunner contextRunner = this.contextRunner
.withUserConfiguration(TestDataSourceConfiguration.class);
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
assertThat(dataSource).isInstanceOf(DataSourceWrapper.class);
DataSource realDataSource = ((DataSourceWrapper) dataSource).getOriginalDataSource();
assertThat(realDataSource).isInstanceOf(BasicDataSource.class);
});
}
@Test
void testScopedDataSourceIsNotDecorated() {
ApplicationContextRunner contextRunner = this.contextRunner
.withUserConfiguration(TestScopedDataSourceConfiguration.class);
contextRunner.run(context -> {
assertThat(context).getBeanNames(DataSource.class).containsOnly("dataSource", "scopedTarget.dataSource");
assertThat(context).getBean("dataSource").isInstanceOf(DataSourceWrapper.class);
assertThat(context).getBean("scopedTarget.dataSource").isNotInstanceOf(DataSourceWrapper.class);
});
}
@Test
void testCustomDataSourceDecoratorApplied() {
ApplicationContextRunner contextRunner = this.contextRunner
.withUserConfiguration(TestDataSourceDecoratorConfiguration.class);
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
assertThat(dataSource).isNotNull();
DataSource customDataSource = ((DataSourceWrapper) dataSource).getDecoratedDataSource();
assertThat(customDataSource).isInstanceOf(CustomDataSourceProxy.class);
DataSource realDataSource = ((DataSourceWrapper) dataSource).getOriginalDataSource();
assertThat(realDataSource).isInstanceOf(HikariDataSource.class);
assertThat(dataSource).isInstanceOf(DataSourceWrapper.class);
DataSourceWrapper DataSourceWrapper = (DataSourceWrapper) dataSource;
assertThat(DataSourceWrapper.getDecoratedDataSource()).isInstanceOf(CustomDataSourceProxy.class);
CustomDataSourceProxy customDataSourceProxy = (CustomDataSourceProxy) DataSourceWrapper
.getDecoratedDataSource();
assertThat(customDataSourceProxy.delegate).isInstanceOf(P6DataSource.class);
P6DataSource p6DataSource = (P6DataSource) customDataSourceProxy.delegate;
DataSource p6WrappedDataSource = (DataSource) ReflectionTestUtils.getField(p6DataSource, "realDataSource");
assertThat(p6WrappedDataSource).isInstanceOf(ProxyDataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) p6WrappedDataSource;
});
}
@Test
void testDecoratingCanBeDisabled() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.enabled:false");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
assertThat(dataSource).isInstanceOf(HikariDataSource.class);
});
}
@Test
void testDecoratingCanBeDisabledForSpecificBeans() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.excluded-beans:secondDataSource")
.withUserConfiguration(TestMultiDataSourceConfiguration.class);
contextRunner.run(context -> {
DataSource dataSource = context.getBean("dataSource", DataSource.class);
assertThat(dataSource).isInstanceOf(DataSourceWrapper.class);
DataSource secondDataSource = context.getBean("secondDataSource", DataSource.class);
assertThat(secondDataSource).isInstanceOf(BasicDataSource.class);
});
}
@Test
void testDecoratingChainBuiltCorrectly() {
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
DataSourceWrapper dataSource1 = context.getBean(DataSourceWrapper.class);
assertThat(dataSource1).isNotNull();
DataSource p6DataSource = dataSource1.getDecoratedDataSource();
assertThat(p6DataSource).isNotNull();
assertThat(p6DataSource).isInstanceOf(P6DataSource.class);
DataSource proxyDataSource = (DataSource) new DirectFieldAccessor(p6DataSource)
.getPropertyValue("realDataSource");
assertThat(proxyDataSource).isNotNull();
assertThat(proxyDataSource).isInstanceOf(ProxyDataSource.class);
});
}
@Test
void testDecorateDynamicallyRegisteredBeans() {
ApplicationContextRunner contextRunner = this.contextRunner.withInitializer(context -> {
GenericApplicationContext gac = (GenericApplicationContext) context;
gac.registerBean("ds1", DataSource.class, () -> new HikariDataSource());
gac.registerBean("ds2", DataSource.class, () -> new HikariDataSource());
});
contextRunner.run(context -> {
DataSource dataSource1 = context.getBean("ds1", DataSource.class);
assertThat(dataSource1).isNotNull();
assertThat(dataSource1).isInstanceOf(DataSourceWrapper.class);
DataSource dataSource2 = context.getBean("ds2", DataSource.class);
assertThat(dataSource2).isNotNull();
assertThat(dataSource2).isInstanceOf(DataSourceWrapper.class);
});
}
@Configuration
static class TestDataSourceConfiguration {
@Bean
public DataSource dataSource() {
BasicDataSource pool = new BasicDataSource();
pool.setDriverClassName("org.hsqldb.jdbcDriver");
pool.setUrl("jdbc:hsqldb:target/overridedb");
pool.setUsername("sa");
return pool;
}
}
@Configuration
static class TestDataSourceDecoratorConfiguration {
@Bean
public DataSourceDecorator customDataSourceDecorator() {
return (beanName, dataSource) -> new CustomDataSourceProxy(dataSource);
}
}
@Configuration
static class TestMultiDataSourceConfiguration {
@Bean
@Primary
public DataSource dataSource() {
BasicDataSource pool = new BasicDataSource();
pool.setDriverClassName("org.hsqldb.jdbcDriver");
pool.setUrl("jdbc:hsqldb:target/db");
pool.setUsername("sa");
return pool;
}
@Bean
public DataSource secondDataSource() {
BasicDataSource pool = new BasicDataSource();
pool.setDriverClassName("org.hsqldb.jdbcDriver");
pool.setUrl("jdbc:hsqldb:target/db2");
pool.setUsername("sa");
return pool;
}
}
@Configuration
static class TestScopedDataSourceConfiguration {
@Bean
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public DataSource dataSource() {
BasicDataSource pool = new BasicDataSource();
pool.setDriverClassName("org.hsqldb.jdbcDriver");
pool.setUrl("jdbc:hsqldb:target/overridedb");
pool.setUsername("sa");
return pool;
}
}
/**
* Custom proxy data source for tests.
*
* @author Arthur Gavlyukovskiy
*/
static class CustomDataSourceProxy implements DataSource {
private final DataSource delegate;
CustomDataSourceProxy(DataSource delegate) {
this.delegate = delegate;
}
@Override
public Connection getConnection() {
return null;
}
@Override
public Connection getConnection(String username, String password) {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) {
return false;
}
@Override
public PrintWriter getLogWriter() {
return null;
}
@Override
public void setLogWriter(PrintWriter out) {
}
@Override
public void setLoginTimeout(int seconds) {
}
@Override
public int getLoginTimeout() {
return 0;
}
@Override
public Logger getParentLogger() {
return null;
}
}
}
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import java.io.PrintWriter;
import java.sql.Connection;
import java.util.concurrent.ThreadLocalRandom;
import java.util.logging.Logger;
import javax.sql.DataSource;
import com.p6spy.engine.spy.P6DataSource;
import com.zaxxer.hikari.HikariDataSource;
import net.ttddyy.dsproxy.support.ProxyDataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.junit.jupiter.api.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.autoconfig.TraceNoOpAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceDecorator;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceWrapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
class DataSourceDecoratorAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TraceJdbcAutoConfiguration.class, TraceNoOpAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class))
.withPropertyValues("spring.datasource.initialization-mode=never",
"spring.sleuth.noop.enabled=true",
"spring.datasource.url:jdbc:h2:mem:testdb-" + ThreadLocalRandom.current().nextInt());
@Test
void testDecoratingInDefaultOrder() {
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
assertThat(dataSource).isInstanceOf(DataSourceWrapper.class);
DataSourceWrapper DataSourceWrapper = (DataSourceWrapper) dataSource;
assertThat(DataSourceWrapper.getDecoratedDataSource()).isInstanceOf(P6DataSource.class);
P6DataSource p6DataSource = (P6DataSource) DataSourceWrapper.getDecoratedDataSource();
DataSource p6WrappedDataSource = (DataSource) ReflectionTestUtils.getField(p6DataSource, "realDataSource");
assertThat(p6WrappedDataSource).isInstanceOf(ProxyDataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) p6WrappedDataSource;
});
}
@Test
void testNoDecoratingForExcludeBeans() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.excluded-data-source-bean-names:dataSource");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
assertThat(dataSource).isInstanceOf(HikariDataSource.class);
});
}
@Test
void testDecoratingWhenDefaultProxyProviderNotAvailable() {
ApplicationContextRunner contextRunner = this.contextRunner;
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
assertThat(((DataSourceWrapper) dataSource).getOriginalDataSource()).isInstanceOf(HikariDataSource.class);
DataSourceWrapper DataSourceWrapper = (DataSourceWrapper) dataSource;
assertThat(DataSourceWrapper.getDecoratedDataSource()).isInstanceOf(P6DataSource.class);
P6DataSource p6DataSource = (P6DataSource) DataSourceWrapper.getDecoratedDataSource();
DataSource p6WrappedDataSource = (DataSource) ReflectionTestUtils.getField(p6DataSource, "realDataSource");
assertThat(p6WrappedDataSource).isInstanceOf(ProxyDataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) p6WrappedDataSource;
DataSource dsProxyWrappedDataSource = (DataSource) ReflectionTestUtils.getField(proxyDataSource,
"dataSource");
assertThat(dsProxyWrappedDataSource).isEqualTo(DataSourceWrapper.getOriginalDataSource());
});
}
@Test
void testDecoratedHikariSpecificPropertiesIsSet() {
ApplicationContextRunner contextRunner = this.contextRunner.withPropertyValues(
"spring.datasource.type:" + HikariDataSource.class.getName(),
"spring.datasource.hikari.catalog:test_catalog");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
assertThat(dataSource).isNotNull();
assertThat(dataSource).isInstanceOf(DataSourceWrapper.class);
DataSource realDataSource = ((DataSourceWrapper) dataSource).getOriginalDataSource();
assertThat(realDataSource).isInstanceOf(HikariDataSource.class);
assertThat(((HikariDataSource) realDataSource).getCatalog()).isEqualTo("test_catalog");
});
}
@Test
void testCustomDataSourceIsDecorated() {
ApplicationContextRunner contextRunner = this.contextRunner
.withUserConfiguration(TestDataSourceConfiguration.class);
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
assertThat(dataSource).isInstanceOf(DataSourceWrapper.class);
DataSource realDataSource = ((DataSourceWrapper) dataSource).getOriginalDataSource();
assertThat(realDataSource).isInstanceOf(BasicDataSource.class);
});
}
@Test
void testScopedDataSourceIsNotDecorated() {
ApplicationContextRunner contextRunner = this.contextRunner
.withUserConfiguration(TestScopedDataSourceConfiguration.class);
contextRunner.run(context -> {
assertThat(context).getBeanNames(DataSource.class).containsOnly("dataSource", "scopedTarget.dataSource");
assertThat(context).getBean("dataSource").isInstanceOf(DataSourceWrapper.class);
assertThat(context).getBean("scopedTarget.dataSource").isNotInstanceOf(DataSourceWrapper.class);
});
}
@Test
void testCustomDataSourceDecoratorApplied() {
ApplicationContextRunner contextRunner = this.contextRunner
.withUserConfiguration(TestDataSourceDecoratorConfiguration.class);
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
assertThat(dataSource).isNotNull();
DataSource customDataSource = ((DataSourceWrapper) dataSource).getDecoratedDataSource();
assertThat(customDataSource).isInstanceOf(CustomDataSourceProxy.class);
DataSource realDataSource = ((DataSourceWrapper) dataSource).getOriginalDataSource();
assertThat(realDataSource).isInstanceOf(HikariDataSource.class);
assertThat(dataSource).isInstanceOf(DataSourceWrapper.class);
DataSourceWrapper DataSourceWrapper = (DataSourceWrapper) dataSource;
assertThat(DataSourceWrapper.getDecoratedDataSource()).isInstanceOf(CustomDataSourceProxy.class);
CustomDataSourceProxy customDataSourceProxy = (CustomDataSourceProxy) DataSourceWrapper
.getDecoratedDataSource();
assertThat(customDataSourceProxy.delegate).isInstanceOf(P6DataSource.class);
P6DataSource p6DataSource = (P6DataSource) customDataSourceProxy.delegate;
DataSource p6WrappedDataSource = (DataSource) ReflectionTestUtils.getField(p6DataSource, "realDataSource");
assertThat(p6WrappedDataSource).isInstanceOf(ProxyDataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) p6WrappedDataSource;
});
}
@Test
void testDecoratingCanBeDisabled() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.enabled:false");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
assertThat(dataSource).isInstanceOf(HikariDataSource.class);
});
}
@Test
void testDecoratingCanBeDisabledForSpecificBeans() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.excluded-data-source-bean-names:secondDataSource")
.withUserConfiguration(TestMultiDataSourceConfiguration.class);
contextRunner.run(context -> {
DataSource dataSource = context.getBean("dataSource", DataSource.class);
assertThat(dataSource).isInstanceOf(DataSourceWrapper.class);
DataSource secondDataSource = context.getBean("secondDataSource", DataSource.class);
assertThat(secondDataSource).isInstanceOf(BasicDataSource.class);
});
}
@Test
void testDecoratingChainBuiltCorrectly() {
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
DataSourceWrapper dataSource1 = context.getBean(DataSourceWrapper.class);
assertThat(dataSource1).isNotNull();
DataSource p6DataSource = dataSource1.getDecoratedDataSource();
assertThat(p6DataSource).isNotNull();
assertThat(p6DataSource).isInstanceOf(P6DataSource.class);
DataSource proxyDataSource = (DataSource) new DirectFieldAccessor(p6DataSource)
.getPropertyValue("realDataSource");
assertThat(proxyDataSource).isNotNull();
assertThat(proxyDataSource).isInstanceOf(ProxyDataSource.class);
});
}
@Test
void testDecorateDynamicallyRegisteredBeans() {
ApplicationContextRunner contextRunner = this.contextRunner.withInitializer(context -> {
GenericApplicationContext gac = (GenericApplicationContext) context;
gac.registerBean("ds1", DataSource.class, () -> new HikariDataSource());
gac.registerBean("ds2", DataSource.class, () -> new HikariDataSource());
});
contextRunner.run(context -> {
DataSource dataSource1 = context.getBean("ds1", DataSource.class);
assertThat(dataSource1).isNotNull();
assertThat(dataSource1).isInstanceOf(DataSourceWrapper.class);
DataSource dataSource2 = context.getBean("ds2", DataSource.class);
assertThat(dataSource2).isNotNull();
assertThat(dataSource2).isInstanceOf(DataSourceWrapper.class);
});
}
@Configuration
static class TestDataSourceConfiguration {
@Bean
public DataSource dataSource() {
BasicDataSource pool = new BasicDataSource();
pool.setDriverClassName("org.hsqldb.jdbcDriver");
pool.setUrl("jdbc:hsqldb:target/overridedb");
pool.setUsername("sa");
return pool;
}
}
@Configuration
static class TestDataSourceDecoratorConfiguration {
@Bean
public DataSourceDecorator customDataSourceDecorator() {
return (beanName, dataSource) -> new CustomDataSourceProxy(dataSource);
}
}
@Configuration
static class TestMultiDataSourceConfiguration {
@Bean
@Primary
public DataSource dataSource() {
BasicDataSource pool = new BasicDataSource();
pool.setDriverClassName("org.hsqldb.jdbcDriver");
pool.setUrl("jdbc:hsqldb:target/db");
pool.setUsername("sa");
return pool;
}
@Bean
public DataSource secondDataSource() {
BasicDataSource pool = new BasicDataSource();
pool.setDriverClassName("org.hsqldb.jdbcDriver");
pool.setUrl("jdbc:hsqldb:target/db2");
pool.setUsername("sa");
return pool;
}
}
@Configuration
static class TestScopedDataSourceConfiguration {
@Bean
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public DataSource dataSource() {
BasicDataSource pool = new BasicDataSource();
pool.setDriverClassName("org.hsqldb.jdbcDriver");
pool.setUrl("jdbc:hsqldb:target/overridedb");
pool.setUsername("sa");
return pool;
}
}
/**
* Custom proxy data source for tests.
*
* @author Arthur Gavlyukovskiy
*/
static class CustomDataSourceProxy implements DataSource {
private final DataSource delegate;
CustomDataSourceProxy(DataSource delegate) {
this.delegate = delegate;
}
@Override
public Connection getConnection() {
return null;
}
@Override
public Connection getConnection(String username, String password) {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) {
return false;
}
@Override
public PrintWriter getLogWriter() {
return null;
}
@Override
public void setLogWriter(PrintWriter out) {
}
@Override
public void setLoginTimeout(int seconds) {
}
@Override
public int getLoginTimeout() {
return 0;
}
@Override
public Logger getParentLogger() {
return null;
}
}
}

View File

@@ -1,280 +1,280 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import javax.sql.DataSource;
import com.p6spy.engine.common.ConnectionInformation;
import com.p6spy.engine.common.P6LogQuery;
import com.p6spy.engine.event.CompoundJdbcEventListener;
import com.p6spy.engine.event.JdbcEventListener;
import com.p6spy.engine.logging.Category;
import com.p6spy.engine.logging.LoggingEventListener;
import com.p6spy.engine.spy.JdbcEventListenerFactory;
import com.p6spy.engine.spy.P6DataSource;
import com.p6spy.engine.spy.appender.CustomLineFormat;
import com.p6spy.engine.spy.appender.FormattedLogger;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceWrapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
class P6SpyConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TraceDataSourceDecoratorAutoConfiguration.class, BraveAutoConfiguration.class,
TestSpanHandlerConfiguration.class, PropertyPlaceholderAutoConfiguration.class))
.withPropertyValues("spring.datasource.initialization-mode=never",
"spring.datasource.url:jdbc:h2:mem:testdb-" + ThreadLocalRandom.current().nextInt())
.withClassLoader(new FilteredClassLoader("net.ttddyy.dsproxy"));
@BeforeEach
@AfterEach
void resetLogAccumulator() {
LogAccumulator.reset();
}
@Test
void testCustomListeners() {
ApplicationContextRunner contextRunner = this.contextRunner
.withUserConfiguration(CustomListenerConfiguration.class);
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
JdbcEventListenerFactory jdbcEventListenerFactory = context.getBean(JdbcEventListenerFactory.class);
GetCountingListener getCountingListener = context.getBean(GetCountingListener.class);
ClosingCountingListener closingCountingListener = context.getBean(ClosingCountingListener.class);
P6DataSource p6DataSource = (P6DataSource) ((DataSourceWrapper) dataSource).getDecoratedDataSource();
assertThat(p6DataSource).extracting("jdbcEventListenerFactory").isEqualTo(jdbcEventListenerFactory);
CompoundJdbcEventListener jdbcEventListener = (CompoundJdbcEventListener) jdbcEventListenerFactory
.createJdbcEventListener();
assertThat(jdbcEventListener.getEventListeners()).contains(getCountingListener, closingCountingListener);
assertThat(getCountingListener.connectionCount).isEqualTo(0);
Connection connection1 = p6DataSource.getConnection();
assertThat(getCountingListener.connectionCount).isEqualTo(1);
assertThat(closingCountingListener.connectionCount).isEqualTo(0);
Connection connection2 = p6DataSource.getConnection();
assertThat(getCountingListener.connectionCount).isEqualTo(2);
// order matters!
connection2.close();
assertThat(closingCountingListener.connectionCount).isEqualTo(1);
// order matters!
connection1.close();
assertThat(closingCountingListener.connectionCount).isEqualTo(2);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testDoesNotRegisterLoggingListenerIfDisabled() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.p6spy.enable-logging:false");
contextRunner.run(context -> {
JdbcEventListenerFactory jdbcEventListenerFactory = context.getBean(JdbcEventListenerFactory.class);
CompoundJdbcEventListener jdbcEventListener = (CompoundJdbcEventListener) jdbcEventListenerFactory
.createJdbcEventListener();
assertThat(jdbcEventListener.getEventListeners()).extracting("class")
.doesNotContain(LoggingEventListener.class);
});
}
@Test
void testCanSetCustomLoggingFormat() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.p6spy.log-format:test %{connectionId}");
contextRunner.run(context -> {
JdbcEventListenerFactory jdbcEventListenerFactory = context.getBean(JdbcEventListenerFactory.class);
CompoundJdbcEventListener jdbcEventListener = (CompoundJdbcEventListener) jdbcEventListenerFactory
.createJdbcEventListener();
assertThat(jdbcEventListener.getEventListeners()).extracting("class").contains(LoggingEventListener.class);
assertThat(P6LogQuery.getLogger()).extracting("strategy").extracting("class")
.isEqualTo(CustomLineFormat.class);
});
}
@Test
void testMultilineShouldNotOverrideCustomProperties() {
System.setProperty("p6spy.config.logMessageFormat", "com.p6spy.engine.spy.appender.CustomLineFormat");
System.setProperty("p6spy.config.excludecategories", "debug");
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.p6spy.multiline:true");
contextRunner.run(context -> {
JdbcEventListenerFactory jdbcEventListenerFactory = context.getBean(JdbcEventListenerFactory.class);
CompoundJdbcEventListener jdbcEventListener = (CompoundJdbcEventListener) jdbcEventListenerFactory
.createJdbcEventListener();
assertThat(jdbcEventListener.getEventListeners()).extracting("class").contains(LoggingEventListener.class);
assertThat(P6LogQuery.getLogger()).extracting("strategy").extracting("class")
.isEqualTo(CustomLineFormat.class);
});
}
@Test
void testUseCustomLogger() {
ApplicationContextRunner contextRunner = this.contextRunner.withPropertyValues(
"spring.sleuth.jdbc.p6spy.logging:custom",
"spring.sleuth.jdbc.p6spy.custom-appender-class:" + LogAccumulator.class.getName());
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
dataSource.getConnection().close();
assertThat(P6LogQuery.getLogger()).isInstanceOf(LogAccumulator.class);
});
}
@Test
void testLogFilterPattern() {
ApplicationContextRunner contextRunner = this.contextRunner.withPropertyValues(
"spring.sleuth.jdbc.p6spy.logging:custom",
"spring.sleuth.jdbc.p6spy.custom-appender-class:" + LogAccumulator.class.getName(),
"spring.sleuth.jdbc.p6spy.log-filter.pattern:.*table1.*");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
try (Connection connection = dataSource.getConnection();
PreparedStatement ps1 = connection.prepareStatement("select 1 /* from table1 */");
PreparedStatement ps2 = connection.prepareStatement("select 1 /* from table2 */")) {
ps1.execute();
ps2.execute();
}
assertThat(LogAccumulator.MESSAGES).hasSize(1);
assertThat(LogAccumulator.MESSAGES).allMatch(message -> message.contains("table1"));
});
}
@Test
void testLogFilterPatternMatchAll() {
ApplicationContextRunner contextRunner = this.contextRunner.withPropertyValues(
"spring.sleuth.jdbc.p6spy.logging:custom",
"spring.sleuth.jdbc.p6spy.custom-appender-class:" + LogAccumulator.class.getName(),
"spring.sleuth.jdbc.p6spy.log-filter.pattern:.*");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
try (Connection connection = dataSource.getConnection();
PreparedStatement ps1 = connection.prepareStatement("select 1 /* from table1 */");
PreparedStatement ps2 = connection.prepareStatement("select 1 /* from table2 */")) {
ps1.execute();
ps2.execute();
}
assertThat(LogAccumulator.MESSAGES).hasSize(2);
});
}
@Configuration
static class CustomListenerConfiguration {
@Bean
public GetCountingListener wrappingCountingListener() {
return new GetCountingListener();
}
@Bean
public ClosingCountingListener closingCountingListener() {
return new ClosingCountingListener();
}
}
static class GetCountingListener extends JdbcEventListener {
int connectionCount = 0;
@Override
public void onAfterGetConnection(ConnectionInformation connectionInformation, SQLException e) {
connectionCount++;
}
}
static class ClosingCountingListener extends JdbcEventListener {
int connectionCount = 0;
@Override
public void onAfterConnectionClose(ConnectionInformation connectionInformation, SQLException e) {
connectionCount++;
}
}
public static class LogAccumulator extends FormattedLogger {
static final List<String> MESSAGES = new ArrayList<>();
static final List<Exception> EXCEPTIONS = new ArrayList<>();
public static void reset() {
MESSAGES.clear();
EXCEPTIONS.clear();
}
@Override
public void logException(Exception e) {
EXCEPTIONS.add(e);
}
@Override
public void logText(String text) {
MESSAGES.add(text);
}
@Override
public boolean isCategoryEnabled(Category category) {
return true;
}
}
}
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import javax.sql.DataSource;
import com.p6spy.engine.common.ConnectionInformation;
import com.p6spy.engine.common.P6LogQuery;
import com.p6spy.engine.event.CompoundJdbcEventListener;
import com.p6spy.engine.event.JdbcEventListener;
import com.p6spy.engine.logging.Category;
import com.p6spy.engine.logging.LoggingEventListener;
import com.p6spy.engine.spy.JdbcEventListenerFactory;
import com.p6spy.engine.spy.P6DataSource;
import com.p6spy.engine.spy.appender.CustomLineFormat;
import com.p6spy.engine.spy.appender.FormattedLogger;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.TraceNoOpAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceWrapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
class P6SpyConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TraceJdbcAutoConfiguration.class, TraceNoOpAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class))
.withPropertyValues("spring.datasource.initialization-mode=never",
"spring.sleuth.noop.enabled=true",
"spring.datasource.url:jdbc:h2:mem:testdb-" + ThreadLocalRandom.current().nextInt())
.withClassLoader(new FilteredClassLoader("net.ttddyy.dsproxy"));
@BeforeEach
@AfterEach
void resetLogAccumulator() {
LogAccumulator.reset();
}
@Test
void testCustomListeners() {
ApplicationContextRunner contextRunner = this.contextRunner
.withUserConfiguration(CustomListenerConfiguration.class);
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
JdbcEventListenerFactory jdbcEventListenerFactory = context.getBean(JdbcEventListenerFactory.class);
GetCountingListener getCountingListener = context.getBean(GetCountingListener.class);
ClosingCountingListener closingCountingListener = context.getBean(ClosingCountingListener.class);
P6DataSource p6DataSource = (P6DataSource) ((DataSourceWrapper) dataSource).getDecoratedDataSource();
assertThat(p6DataSource).extracting("jdbcEventListenerFactory").isEqualTo(jdbcEventListenerFactory);
CompoundJdbcEventListener jdbcEventListener = (CompoundJdbcEventListener) jdbcEventListenerFactory
.createJdbcEventListener();
assertThat(jdbcEventListener.getEventListeners()).contains(getCountingListener, closingCountingListener);
assertThat(getCountingListener.connectionCount).isEqualTo(0);
Connection connection1 = p6DataSource.getConnection();
assertThat(getCountingListener.connectionCount).isEqualTo(1);
assertThat(closingCountingListener.connectionCount).isEqualTo(0);
Connection connection2 = p6DataSource.getConnection();
assertThat(getCountingListener.connectionCount).isEqualTo(2);
// order matters!
connection2.close();
assertThat(closingCountingListener.connectionCount).isEqualTo(1);
// order matters!
connection1.close();
assertThat(closingCountingListener.connectionCount).isEqualTo(2);
});
}
@Test
void testDoesNotRegisterLoggingListenerIfDisabled() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.p6spy.enable-logging=false");
contextRunner.run(context -> {
JdbcEventListenerFactory jdbcEventListenerFactory = context.getBean(JdbcEventListenerFactory.class);
CompoundJdbcEventListener jdbcEventListener = (CompoundJdbcEventListener) jdbcEventListenerFactory
.createJdbcEventListener();
assertThat(jdbcEventListener.getEventListeners()).extracting("class")
.doesNotContain(LoggingEventListener.class);
});
}
@Test
void testCanSetCustomLoggingFormat() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.p6spy.log-format:test %{connectionId}");
contextRunner.run(context -> {
JdbcEventListenerFactory jdbcEventListenerFactory = context.getBean(JdbcEventListenerFactory.class);
CompoundJdbcEventListener jdbcEventListener = (CompoundJdbcEventListener) jdbcEventListenerFactory
.createJdbcEventListener();
assertThat(jdbcEventListener.getEventListeners()).extracting("class").contains(LoggingEventListener.class);
assertThat(P6LogQuery.getLogger()).extracting("strategy").extracting("class")
.isEqualTo(CustomLineFormat.class);
});
}
@Test
void testMultilineShouldNotOverrideCustomProperties() {
System.setProperty("p6spy.config.logMessageFormat", "com.p6spy.engine.spy.appender.CustomLineFormat");
System.setProperty("p6spy.config.excludecategories", "debug");
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.p6spy.multiline:true");
contextRunner.run(context -> {
JdbcEventListenerFactory jdbcEventListenerFactory = context.getBean(JdbcEventListenerFactory.class);
CompoundJdbcEventListener jdbcEventListener = (CompoundJdbcEventListener) jdbcEventListenerFactory
.createJdbcEventListener();
assertThat(jdbcEventListener.getEventListeners()).extracting("class").contains(LoggingEventListener.class);
assertThat(P6LogQuery.getLogger()).extracting("strategy").extracting("class")
.isEqualTo(CustomLineFormat.class);
});
}
@Test
void testUseCustomLogger() {
ApplicationContextRunner contextRunner = this.contextRunner.withPropertyValues(
"spring.sleuth.jdbc.p6spy.logging=custom",
"spring.sleuth.jdbc.p6spy.custom-appender-class:" + LogAccumulator.class.getName());
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
dataSource.getConnection().close();
assertThat(P6LogQuery.getLogger()).isInstanceOf(LogAccumulator.class);
});
}
@Test
void testLogFilterPattern() {
ApplicationContextRunner contextRunner = this.contextRunner.withPropertyValues(
"spring.sleuth.jdbc.p6spy.logging=custom",
"spring.sleuth.jdbc.p6spy.custom-appender-class:" + LogAccumulator.class.getName(),
"spring.sleuth.jdbc.p6spy.log-filter.pattern:.*table1.*");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
try (Connection connection = dataSource.getConnection();
PreparedStatement ps1 = connection.prepareStatement("select 1 /* from table1 */");
PreparedStatement ps2 = connection.prepareStatement("select 1 /* from table2 */")) {
ps1.execute();
ps2.execute();
}
assertThat(LogAccumulator.MESSAGES).hasSize(1);
assertThat(LogAccumulator.MESSAGES).allMatch(message -> message.contains("table1"));
});
}
@Test
void testLogFilterPatternMatchAll() {
ApplicationContextRunner contextRunner = this.contextRunner.withPropertyValues(
"spring.sleuth.jdbc.p6spy.logging=custom",
"spring.sleuth.jdbc.p6spy.custom-appender-class:" + LogAccumulator.class.getName(),
"spring.sleuth.jdbc.p6spy.log-filter.pattern:.*");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
try (Connection connection = dataSource.getConnection();
PreparedStatement ps1 = connection.prepareStatement("select 1 /* from table1 */");
PreparedStatement ps2 = connection.prepareStatement("select 1 /* from table2 */")) {
ps1.execute();
ps2.execute();
}
assertThat(LogAccumulator.MESSAGES).hasSize(2);
});
}
@Configuration
static class CustomListenerConfiguration {
@Bean
public GetCountingListener wrappingCountingListener() {
return new GetCountingListener();
}
@Bean
public ClosingCountingListener closingCountingListener() {
return new ClosingCountingListener();
}
}
static class GetCountingListener extends JdbcEventListener {
int connectionCount = 0;
@Override
public void onAfterGetConnection(ConnectionInformation connectionInformation, SQLException e) {
connectionCount++;
}
}
static class ClosingCountingListener extends JdbcEventListener {
int connectionCount = 0;
@Override
public void onAfterConnectionClose(ConnectionInformation connectionInformation, SQLException e) {
connectionCount++;
}
}
public static class LogAccumulator extends FormattedLogger {
static final List<String> MESSAGES = new ArrayList<>();
static final List<Exception> EXCEPTIONS = new ArrayList<>();
public static void reset() {
MESSAGES.clear();
EXCEPTIONS.clear();
}
@Override
public void logException(Exception e) {
EXCEPTIONS.add(e);
}
@Override
public void logText(String text) {
MESSAGES.add(text);
}
@Override
public boolean isCategoryEnabled(Category category) {
return true;
}
}
}

View File

@@ -1,246 +1,239 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import javax.sql.DataSource;
import net.ttddyy.dsproxy.ExecutionInfo;
import net.ttddyy.dsproxy.QueryInfo;
import net.ttddyy.dsproxy.listener.ChainListener;
import net.ttddyy.dsproxy.listener.QueryExecutionListener;
import net.ttddyy.dsproxy.listener.logging.CommonsQueryLoggingListener;
import net.ttddyy.dsproxy.listener.logging.CommonsSlowQueryListener;
import net.ttddyy.dsproxy.listener.logging.JULQueryLoggingListener;
import net.ttddyy.dsproxy.listener.logging.JULSlowQueryListener;
import net.ttddyy.dsproxy.listener.logging.SLF4JQueryLoggingListener;
import net.ttddyy.dsproxy.listener.logging.SLF4JSlowQueryListener;
import net.ttddyy.dsproxy.listener.logging.SystemOutQueryLoggingListener;
import net.ttddyy.dsproxy.listener.logging.SystemOutSlowQueryListener;
import net.ttddyy.dsproxy.proxy.DefaultConnectionIdManager;
import net.ttddyy.dsproxy.proxy.GlobalConnectionIdManager;
import net.ttddyy.dsproxy.support.ProxyDataSource;
import net.ttddyy.dsproxy.transform.ParameterTransformer;
import net.ttddyy.dsproxy.transform.QueryTransformer;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceProxyConnectionIdManagerProvider;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceWrapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import static org.assertj.core.api.Assertions.assertThat;
class ProxyDataSourceConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TraceDataSourceDecoratorAutoConfiguration.class, BraveAutoConfiguration.class,
TestSpanHandlerConfiguration.class, PropertyPlaceholderAutoConfiguration.class))
.withPropertyValues("spring.datasource.initialization-mode=never",
"spring.datasource.url:jdbc:h2:mem:testdb-" + ThreadLocalRandom.current().nextInt())
.withClassLoader(new FilteredClassLoader("com.p6spy"));
@Test
void testRegisterLogAndSlowQueryLogByDefaultToSlf4j() {
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(SLF4JSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(SLF4JQueryLoggingListener.class);
});
}
@Test
void testRegisterLogAndSlowQueryLogByUsingSlf4j() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.datasource-proxy.logging:slf4j");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(SLF4JSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(SLF4JQueryLoggingListener.class);
});
}
@Test
void testRegisterLogAndSlowQueryLogUsingSystemOut() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.datasource-proxy.logging:sysout");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(SystemOutSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(SystemOutQueryLoggingListener.class);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testRegisterLogAndSlowQueryLogUsingJUL() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.datasourceProxy.logging:jul");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(JULSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(JULQueryLoggingListener.class);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testRegisterLogAndSlowQueryLogUsingApacheCommons() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.datasourceProxy.logging:commons");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(CommonsSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(CommonsQueryLoggingListener.class);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testCustomParameterAndQueryTransformer() {
ApplicationContextRunner contextRunner = this.contextRunner
.withUserConfiguration(CustomDataSourceProxyConfiguration.class);
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ParameterTransformer parameterTransformer = context.getBean(ParameterTransformer.class);
QueryTransformer queryTransformer = context.getBean(QueryTransformer.class);
assertThat(proxyDataSource.getProxyConfig().getParameterTransformer()).isSameAs(parameterTransformer);
assertThat(proxyDataSource.getProxyConfig().getQueryTransformer()).isSameAs(queryTransformer);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testCustomListeners() {
ApplicationContextRunner contextRunner = this.contextRunner
.withUserConfiguration(CustomListenerConfiguration.class);
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
QueryExecutionListener queryExecutionListener = context.getBean(QueryExecutionListener.class);
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).contains(queryExecutionListener);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testGlobalConnectionIdManagerByDefault() {
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
assertThat(proxyDataSource.getConnectionIdManager()).isInstanceOf(GlobalConnectionIdManager.class);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testCustomConnectionIdManager() {
ApplicationContextRunner contextRunner = this.contextRunner
.withUserConfiguration(CustomDataSourceProxyConfiguration.class);
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
assertThat(proxyDataSource.getConnectionIdManager()).isInstanceOf(DefaultConnectionIdManager.class);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Configuration
static class CustomDataSourceProxyConfiguration {
@Bean
public ParameterTransformer parameterTransformer() {
return (replacer, transformInfo) -> {
};
}
@Bean
public QueryTransformer queryTransformer() {
return (transformInfo) -> "TestQuery";
}
@Bean
public DataSourceProxyConnectionIdManagerProvider connectionIdManagerProvider() {
return DefaultConnectionIdManager::new;
}
}
@Configuration
static class CustomListenerConfiguration {
@Bean
@Primary
public QueryExecutionListener queryExecutionListener() {
return new QueryExecutionListener() {
@Override
public void beforeQuery(ExecutionInfo execInfo, List<QueryInfo> queryInfoList) {
System.out.println("beforeQuery");
}
@Override
public void afterQuery(ExecutionInfo execInfo, List<QueryInfo> queryInfoList) {
System.out.println("afterQuery");
}
};
}
}
}
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import javax.sql.DataSource;
import net.ttddyy.dsproxy.ExecutionInfo;
import net.ttddyy.dsproxy.QueryInfo;
import net.ttddyy.dsproxy.listener.ChainListener;
import net.ttddyy.dsproxy.listener.QueryExecutionListener;
import net.ttddyy.dsproxy.listener.logging.CommonsQueryLoggingListener;
import net.ttddyy.dsproxy.listener.logging.CommonsSlowQueryListener;
import net.ttddyy.dsproxy.listener.logging.JULQueryLoggingListener;
import net.ttddyy.dsproxy.listener.logging.JULSlowQueryListener;
import net.ttddyy.dsproxy.listener.logging.SLF4JQueryLoggingListener;
import net.ttddyy.dsproxy.listener.logging.SLF4JSlowQueryListener;
import net.ttddyy.dsproxy.listener.logging.SystemOutQueryLoggingListener;
import net.ttddyy.dsproxy.listener.logging.SystemOutSlowQueryListener;
import net.ttddyy.dsproxy.proxy.DefaultConnectionIdManager;
import net.ttddyy.dsproxy.proxy.GlobalConnectionIdManager;
import net.ttddyy.dsproxy.support.ProxyDataSource;
import net.ttddyy.dsproxy.transform.ParameterTransformer;
import net.ttddyy.dsproxy.transform.QueryTransformer;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.autoconfig.TraceNoOpAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceProxyConnectionIdManagerProvider;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceWrapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import static org.assertj.core.api.Assertions.assertThat;
class ProxyDataSourceConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TraceJdbcAutoConfiguration.class, TraceNoOpAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class))
.withPropertyValues("spring.datasource.initialization-mode=never",
"spring.sleuth.noop.enabled=true",
"spring.datasource.url:jdbc:h2:mem:testdb-" + ThreadLocalRandom.current().nextInt())
.withClassLoader(new FilteredClassLoader("com.p6spy"));
@Test
void testRegisterLogAndSlowQueryLogByDefaultToSlf4j() {
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(SLF4JSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(SLF4JQueryLoggingListener.class);
});
}
@Test
void testRegisterLogAndSlowQueryLogByUsingSlf4j() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.datasource-proxy.logging=slf4j");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(SLF4JSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(SLF4JQueryLoggingListener.class);
});
}
@Test
void testRegisterLogAndSlowQueryLogUsingSystemOut() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.datasource-proxy.logging=sysout");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(SystemOutSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(SystemOutQueryLoggingListener.class);
});
}
@Test
void testRegisterLogAndSlowQueryLogUsingJUL() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.datasourceProxy.logging=jul");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(JULSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(JULQueryLoggingListener.class);
});
}
@Test
void testRegisterLogAndSlowQueryLogUsingApacheCommons() {
ApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("spring.sleuth.jdbc.datasourceProxy.logging=commons");
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(CommonsSlowQueryListener.class);
assertThat(chainListener.getListeners()).extracting("class").contains(CommonsQueryLoggingListener.class);
});
}
@Test
void testCustomParameterAndQueryTransformer() {
ApplicationContextRunner contextRunner = this.contextRunner
.withUserConfiguration(CustomDataSourceProxyConfiguration.class);
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ParameterTransformer parameterTransformer = context.getBean(ParameterTransformer.class);
QueryTransformer queryTransformer = context.getBean(QueryTransformer.class);
assertThat(proxyDataSource.getProxyConfig().getParameterTransformer()).isSameAs(parameterTransformer);
assertThat(proxyDataSource.getProxyConfig().getQueryTransformer()).isSameAs(queryTransformer);
});
}
@Test
void testCustomListeners() {
ApplicationContextRunner contextRunner = this.contextRunner
.withUserConfiguration(CustomListenerConfiguration.class);
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
QueryExecutionListener queryExecutionListener = context.getBean(QueryExecutionListener.class);
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).contains(queryExecutionListener);
});
}
@Test
void testGlobalConnectionIdManagerByDefault() {
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
assertThat(proxyDataSource.getConnectionIdManager()).isInstanceOf(GlobalConnectionIdManager.class);
});
}
@Test
void testCustomConnectionIdManager() {
ApplicationContextRunner contextRunner = this.contextRunner
.withUserConfiguration(CustomDataSourceProxyConfiguration.class);
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
assertThat(proxyDataSource.getConnectionIdManager()).isInstanceOf(DefaultConnectionIdManager.class);
});
}
@Configuration
static class CustomDataSourceProxyConfiguration {
@Bean
public ParameterTransformer parameterTransformer() {
return (replacer, transformInfo) -> {
};
}
@Bean
public QueryTransformer queryTransformer() {
return (transformInfo) -> "TestQuery";
}
@Bean
public DataSourceProxyConnectionIdManagerProvider connectionIdManagerProvider() {
return DefaultConnectionIdManager::new;
}
}
@Configuration
static class CustomListenerConfiguration {
@Bean
@Primary
public QueryExecutionListener queryExecutionListener() {
return new QueryExecutionListener() {
@Override
public void beforeQuery(ExecutionInfo execInfo, List<QueryInfo> queryInfoList) {
System.out.println("beforeQuery");
}
@Override
public void afterQuery(ExecutionInfo execInfo, List<QueryInfo> queryInfoList) {
System.out.println("afterQuery");
}
};
}
}
}

View File

@@ -1,67 +1,66 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import java.util.concurrent.ThreadLocalRandom;
import com.p6spy.engine.event.CompoundJdbcEventListener;
import com.p6spy.engine.spy.JdbcEventListenerFactory;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceJdbcEventListener;
import static org.assertj.core.api.Assertions.assertThat;
class SleuthP6SpyListenerAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TraceDataSourceDecoratorAutoConfiguration.class, BraveAutoConfiguration.class,
TestSpanHandlerConfiguration.class, PropertyPlaceholderAutoConfiguration.class))
.withPropertyValues("spring.datasource.initialization-mode=never",
"spring.datasource.url:jdbc:h2:mem:testdb-" + ThreadLocalRandom.current().nextInt())
.withClassLoader(new FilteredClassLoader("net.ttddyy.dsproxy"));
@Test
void testAddsP6SpyListener() {
contextRunner.run(context -> {
JdbcEventListenerFactory jdbcEventListenerFactory = context.getBean(JdbcEventListenerFactory.class);
CompoundJdbcEventListener jdbcEventListener = (CompoundJdbcEventListener) jdbcEventListenerFactory
.createJdbcEventListener();
assertThat(jdbcEventListener.getEventListeners()).extracting("class")
.contains(TraceJdbcEventListener.class);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testDoesNotAddP6SpyListenerIfNoTracer() {
ApplicationContextRunner contextRunner = this.contextRunner.withPropertyValues("spring.sleuth.enabled:false");
contextRunner.run(context -> {
assertThat(context).doesNotHaveBean(JdbcEventListenerFactory.class);
});
}
}
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import java.util.concurrent.ThreadLocalRandom;
import com.p6spy.engine.event.CompoundJdbcEventListener;
import com.p6spy.engine.spy.JdbcEventListenerFactory;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.autoconfig.TraceNoOpAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceJdbcEventListener;
import static org.assertj.core.api.Assertions.assertThat;
class SleuthP6SpyListenerAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TraceJdbcAutoConfiguration.class, TraceNoOpAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class))
.withPropertyValues("spring.datasource.initialization-mode=never",
"spring.sleuth.noop.enabled=true",
"spring.datasource.url=jdbc:h2:mem:testdb-" + ThreadLocalRandom.current().nextInt())
.withClassLoader(new FilteredClassLoader("net.ttddyy.dsproxy"));
@Test
void testAddsP6SpyListener() {
contextRunner.run(context -> {
JdbcEventListenerFactory jdbcEventListenerFactory = context.getBean(JdbcEventListenerFactory.class);
CompoundJdbcEventListener jdbcEventListener = (CompoundJdbcEventListener) jdbcEventListenerFactory
.createJdbcEventListener();
assertThat(jdbcEventListener.getEventListeners()).extracting("class")
.contains(TraceJdbcEventListener.class);
});
}
@Test
void testDoesNotAddP6SpyListenerIfNoTracer() {
ApplicationContextRunner contextRunner = this.contextRunner.withPropertyValues("spring.sleuth.enabled=false");
contextRunner.run(context -> {
assertThat(context).doesNotHaveBean(JdbcEventListenerFactory.class);
});
}
}

View File

@@ -1,70 +1,69 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import java.util.concurrent.ThreadLocalRandom;
import javax.sql.DataSource;
import net.ttddyy.dsproxy.listener.ChainListener;
import net.ttddyy.dsproxy.support.ProxyDataSource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceWrapper;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceQueryExecutionListener;
import static org.assertj.core.api.Assertions.assertThat;
class SleuthProxyDataSourceListenerAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TraceDataSourceDecoratorAutoConfiguration.class, BraveAutoConfiguration.class,
TestSpanHandlerConfiguration.class, PropertyPlaceholderAutoConfiguration.class))
.withPropertyValues("spring.datasource.initialization-mode=never",
"spring.datasource.url:jdbc:h2:mem:testdb-" + ThreadLocalRandom.current().nextInt())
.withClassLoader(new FilteredClassLoader("com.p6spy"));
@Test
void testAddsDatasourceProxyListener() {
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(TraceQueryExecutionListener.class);
assertThat(context.getBean(Tracer.class).currentSpan()).isNull();
});
}
@Test
void testDoesNotAddDatasourceProxyListenerIfNoTracer() {
ApplicationContextRunner contextRunner = this.contextRunner.withPropertyValues("spring.sleuth.enabled:false");
contextRunner.run(context -> {
assertThat(context).doesNotHaveBean(DataSourceWrapper.class);
});
}
}
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import java.util.concurrent.ThreadLocalRandom;
import javax.sql.DataSource;
import net.ttddyy.dsproxy.listener.ChainListener;
import net.ttddyy.dsproxy.support.ProxyDataSource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.autoconfig.TraceNoOpAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.jdbc.DataSourceWrapper;
import org.springframework.cloud.sleuth.instrument.jdbc.TraceQueryExecutionListener;
import static org.assertj.core.api.Assertions.assertThat;
class SleuthProxyDataSourceListenerAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TraceJdbcAutoConfiguration.class, TraceNoOpAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class))
.withPropertyValues("spring.datasource.initialization-mode=never",
"spring.sleuth.noop.enabled=true",
"spring.datasource.url:jdbc:h2:mem:testdb-" + ThreadLocalRandom.current().nextInt())
.withClassLoader(new FilteredClassLoader("com.p6spy"));
@Test
void testAddsDatasourceProxyListener() {
contextRunner.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
ProxyDataSource proxyDataSource = (ProxyDataSource) ((DataSourceWrapper) dataSource)
.getDecoratedDataSource();
ChainListener chainListener = proxyDataSource.getProxyConfig().getQueryListener();
assertThat(chainListener.getListeners()).extracting("class").contains(TraceQueryExecutionListener.class);
});
}
@Test
void testDoesNotAddDatasourceProxyListenerIfNoTracer() {
ApplicationContextRunner contextRunner = this.contextRunner.withPropertyValues("spring.sleuth.enabled:false");
contextRunner.run(context -> {
assertThat(context).doesNotHaveBean(DataSourceWrapper.class);
});
}
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import brave.sampler.Sampler;
import brave.test.TestSpanHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
class TestSpanHandlerConfiguration {
@Bean
public TestSpanHandler spanHandler() {
return new TestSpanHandler();
}
@Bean
public Sampler sampler() {
return Sampler.ALWAYS_SAMPLE;
}
}

View File

@@ -1,43 +1,43 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.instrument.jdbc;
import javax.sql.CommonDataSource;
import org.springframework.cloud.sleuth.Span;
/**
* Customizer for {@link TraceListenerStrategy} client span.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public interface TraceListenerStrategySpanCustomizer<T extends CommonDataSource> {
/**
* Customizes the client database span.
* @param spanBuilder span builder
*/
void customizeConnectionSpan(T dataSource, Span.Builder spanBuilder);
/**
* @param dataSource data source for which we're building the span
* @return {@code true} when this customizer can be applied
*/
boolean isApplicable(CommonDataSource dataSource);
}
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.instrument.jdbc;
import javax.sql.CommonDataSource;
import org.springframework.cloud.sleuth.Span;
/**
* Customizer for {@link TraceListenerStrategy} client span.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public interface TraceListenerStrategySpanCustomizer<T extends CommonDataSource> {
/**
* Customizes the client database span.
* @param spanBuilder span builder
*/
void customizeConnectionSpan(T dataSource, Span.Builder spanBuilder);
/**
* @param dataSource data source for which we're building the span
* @return {@code true} when this customizer can be applied
*/
boolean isApplicable(CommonDataSource dataSource);
}

View File

@@ -1,39 +1,39 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.instrument.jdbc;
/**
* Trace types.
*/
public enum TraceType {
/**
* Connection.
*/
CONNECTION,
/**
* Query.
*/
QUERY,
/**
* Fetch.
*/
FETCH
}
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.instrument.jdbc;
/**
* Trace types.
*/
public enum TraceType {
/**
* Related to JDBC connections.
*/
CONNECTION,
/**
* Related to query executions.
*/
QUERY,
/**
* Related to ResultSets.
*/
FETCH
}

View File

@@ -45,6 +45,7 @@
<module>spring-cloud-sleuth-instrumentation-feign-tests</module>
<module>spring-cloud-sleuth-instrumentation-gateway-tests</module>
<module>spring-cloud-sleuth-instrumentation-grpc-tests</module>
<module>spring-cloud-sleuth-instrumentation-jdbc-tests</module>
<module>spring-cloud-sleuth-instrumentation-kafka-tests</module>
<module>spring-cloud-sleuth-instrumentation-lettuce-tests</module>
<module>spring-cloud-sleuth-instrumentation-messaging-tests</module>

View File

@@ -0,0 +1,114 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2013-2021 the original author or authors.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
~
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-sleuth-instrumentation-jdbc-tests</artifactId>
<packaging>jar</packaging>
<name>Spring Cloud Sleuth Brave Jdbc Instrumentation Tests</name>
<description>Spring Cloud Sleuth Brave Jdbc Instrumentation Tests</description>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-tests-brave</artifactId>
<version>3.1.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<properties>
<sonar.skip>true</sonar.skip>
</properties>
<build>
<plugins>
<plugin>
<!--skip deploy -->
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-tests-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>net.ttddyy</groupId>
<artifactId>datasource-proxy</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-tests</artifactId>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.brave.instrument.jdbc;
import brave.sampler.Sampler;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler;
import org.springframework.cloud.sleuth.test.TestSpanHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
public class TracingJdbcEventListenerTests
extends org.springframework.cloud.sleuth.instrument.jdbc.TracingJdbcEventListenerTests {
@Override
protected Class autoConfiguration() {
return BraveAutoConfiguration.class;
}
@Override
protected Class testConfiguration() {
return Config.class;
}
@Configuration(proxyBeanMethods = false)
static class Config {
@Bean
TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) {
return new BraveTestSpanHandler(testSpanHandler);
}
@Bean
Sampler alwaysSampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
brave.test.TestSpanHandler braveTestSpanHandler() {
return new brave.test.TestSpanHandler();
}
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.brave.instrument.jdbc;
import brave.sampler.Sampler;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler;
import org.springframework.cloud.sleuth.test.TestSpanHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
public class TracingQueryExecutionListenerTests
extends org.springframework.cloud.sleuth.instrument.jdbc.TracingQueryExecutionListenerTests {
@Override
protected Class autoConfiguration() {
return BraveAutoConfiguration.class;
}
@Override
protected Class testConfiguration() {
return Config.class;
}
@Configuration(proxyBeanMethods = false)
static class Config {
@Bean
TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) {
return new BraveTestSpanHandler(testSpanHandler);
}
@Bean
Sampler alwaysSampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
brave.test.TestSpanHandler braveTestSpanHandler() {
return new brave.test.TestSpanHandler();
}
}
}

View File

@@ -0,0 +1,5 @@
logging.level.org.springframework.cloud: DEBUG
logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR
logging.level.org.springframework.cloud.sleuth.brave.instrument.web.client.feign: TRACE
spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.cloud.gateway.config.GatewayAutoConfiguration, org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration, org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration

View File

@@ -1,59 +1,60 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.brave.instrument.web.view;
import brave.test.TestSpanHandler;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.Environment;
import org.springframework.test.context.TestPropertySource;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.BDDAssertions.then;
@SpringBootTest(classes = Issue469.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(properties = { "spring.mvc.view.prefix=/WEB-INF/jsp/", "spring.mvc.view.suffix=.jsp" })
public class Issue469Tests {
@Autowired
TestSpanHandler spans;
@Autowired
Environment environment;
RestTemplate restTemplate = new RestTemplate();
@Test
public void should_not_result_in_tracing_exceptions_when_using_view_controllers() {
try {
this.restTemplate.getForObject("http://localhost:" + port() + "/welcome", String.class);
}
catch (Exception e) {
// JSPs are not rendered
then(e).hasMessageContaining("404");
}
then(this.spans).isNotEmpty();
}
private int port() {
return this.environment.getProperty("local.server.port", Integer.class);
}
}
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.brave.instrument.web.view;
import brave.test.TestSpanHandler;
import org.awaitility.Awaitility;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.Environment;
import org.springframework.test.context.TestPropertySource;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.BDDAssertions.then;
@SpringBootTest(classes = Issue469.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(properties = { "spring.mvc.view.prefix=/WEB-INF/jsp/", "spring.mvc.view.suffix=.jsp" })
public class Issue469Tests {
@Autowired
TestSpanHandler spans;
@Autowired
Environment environment;
RestTemplate restTemplate = new RestTemplate();
@Test
public void should_not_result_in_tracing_exceptions_when_using_view_controllers() {
try {
this.restTemplate.getForObject("http://localhost:" + port() + "/welcome", String.class);
}
catch (Exception e) {
// JSPs are not rendered
then(e).hasMessageContaining("404");
}
Awaitility.await().untilAsserted(() -> then(this.spans).isNotEmpty());
}
private int port() {
return this.environment.getProperty("local.server.port", Integer.class);
}
}

View File

@@ -189,6 +189,16 @@
<artifactId>spring-boot-starter-rsocket</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>net.ttddyy</groupId>
<artifactId>datasource-proxy</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>

View File

@@ -1,77 +1,79 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import javax.sql.DataSource;
import brave.handler.MutableSpan;
import brave.test.TestSpanHandler;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
class TracingJdbcEventListenerTests extends TracingListenerStrategyTests {
private static final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TraceDataSourceDecoratorAutoConfiguration.class, BraveAutoConfiguration.class,
TestSpanHandlerConfiguration.class, PropertyPlaceholderAutoConfiguration.class))
.withPropertyValues("spring.datasource.initialization-mode=never",
"spring.datasource.url=jdbc:h2:mem:testdb-baz", "spring.datasource.hikari.pool-name=test")
.withClassLoader(new FilteredClassLoader("net.ttddyy.dsproxy"));
protected TracingJdbcEventListenerTests() {
super(contextRunner);
}
@Test
void testShouldUsePlaceholderInSqlTagOfSpansForPreparedStatementIfIncludeParameterValuesIsSetToFalse() {
contextRunner.withPropertyValues("spring.sleuth.jdbc.p6spy.tracing.include-parameter-values=false")
.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class);
Connection connection = dataSource.getConnection();
PreparedStatement preparedStatement = connection
.prepareStatement("UPDATE INFORMATION_SCHEMA.TABLES SET table_Name = ? WHERE 0 = ?");
preparedStatement.setString(1, "");
preparedStatement.setInt(2, 1);
preparedStatement.executeUpdate();
connection.close();
assertThat(spanReporter.spans()).hasSize(2);
MutableSpan connectionSpan = spanReporter.spans().get(1);
MutableSpan statementSpan = spanReporter.spans().get(0);
assertThat(connectionSpan.name()).isEqualTo("connection");
assertThat(statementSpan.name()).isEqualTo("update");
assertThat(statementSpan.tags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME,
"UPDATE INFORMATION_SCHEMA.TABLES SET table_Name = ? WHERE 0 = ?");
assertThat(statementSpan.tags()).containsEntry(SPAN_ROW_COUNT_TAG_NAME, "0");
});
}
}
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.instrument.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import javax.sql.DataSource;
import brave.handler.MutableSpan;
import brave.test.TestSpanHandler;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.autoconfig.instrument.jdbc.TraceJdbcAutoConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
public abstract class TracingJdbcEventListenerTests extends TracingListenerStrategyTests {
protected final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TraceJdbcAutoConfiguration.class, autoConfiguration(),
PropertyPlaceholderAutoConfiguration.class))
.withUserConfiguration(testConfiguration())
.withPropertyValues("spring.datasource.initialization-mode=never",
"spring.datasource.url=jdbc:h2:mem:testdb-baz", "spring.datasource.hikari.pool-name=test")
.withClassLoader(new FilteredClassLoader("net.ttddyy.dsproxy"));
@Override
ApplicationContextRunner parentContextRunner() {
return this.contextRunner;
}
@Test
void testShouldUsePlaceholderInSqlTagOfSpansForPreparedStatementIfIncludeParameterValuesIsSetToFalse() {
contextRunner.withPropertyValues("spring.sleuth.jdbc.p6spy.tracing.include-parameter-values=false")
.run(context -> {
DataSource dataSource = context.getBean(DataSource.class);
TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class);
Connection connection = dataSource.getConnection();
PreparedStatement preparedStatement = connection
.prepareStatement("UPDATE INFORMATION_SCHEMA.TABLES SET table_Name = ? WHERE 0 = ?");
preparedStatement.setString(1, "");
preparedStatement.setInt(2, 1);
preparedStatement.executeUpdate();
connection.close();
assertThat(spanReporter.spans()).hasSize(2);
MutableSpan connectionSpan = spanReporter.spans().get(1);
MutableSpan statementSpan = spanReporter.spans().get(0);
assertThat(connectionSpan.name()).isEqualTo("connection");
assertThat(statementSpan.name()).isEqualTo("update");
assertThat(statementSpan.tags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME,
"UPDATE INFORMATION_SCHEMA.TABLES SET table_Name = ? WHERE 0 = ?");
assertThat(statementSpan.tags()).containsEntry(SPAN_ROW_COUNT_TAG_NAME, "0");
});
}
}

View File

@@ -1,38 +1,39 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
class TracingQueryExecutionListenerTests extends TracingListenerStrategyTests {
TracingQueryExecutionListenerTests() {
super(new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TraceDataSourceDecoratorAutoConfiguration.class, BraveAutoConfiguration.class,
TestSpanHandlerConfiguration.class, PropertyPlaceholderAutoConfiguration.class))
.withPropertyValues("spring.datasource.initialization-mode=never",
"spring.datasource.url:jdbc:h2:mem:testdb-baz", "spring.datasource.hikari.pool-name=test")
.withClassLoader(new FilteredClassLoader("com.p6spy")));
}
}
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.instrument.jdbc;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.sleuth.autoconfig.instrument.jdbc.TraceJdbcAutoConfiguration;
public abstract class TracingQueryExecutionListenerTests extends TracingListenerStrategyTests {
@Override
ApplicationContextRunner parentContextRunner() {
return new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
TraceJdbcAutoConfiguration.class, autoConfiguration(), PropertyPlaceholderAutoConfiguration.class))
.withUserConfiguration(testConfiguration())
.withPropertyValues("spring.datasource.initialization-mode=never",
"spring.datasource.url:jdbc:h2:mem:testdb-baz", "spring.datasource.hikari.pool-name=test")
.withClassLoader(new FilteredClassLoader("com.p6spy"));
}
}