This commit is contained in:
Marcin Grzejszczak
2021-05-27 14:05:39 +02:00
parent 8991b4d3cd
commit 685fb727b5
22 changed files with 3736 additions and 3740 deletions

View File

@@ -1,153 +1,153 @@
/*
* 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.async;
import java.util.concurrent.Executor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.interceptor.AsyncExecutionAspectSupport;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
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.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.sleuth.SpanNamer;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.async.LazyTraceExecutor;
import org.springframework.cloud.sleuth.instrument.async.TraceAsyncAspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} enabling async related processing.
*
* @author Dave Syer
* @author Marcin Grzejszczak
* @since 1.0.0
* @see LazyTraceExecutor
* @see TraceAsyncAspect
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SleuthAsyncProperties.class)
@ConditionalOnProperty(value = "spring.sleuth.async.enabled", matchIfMissing = true)
@ConditionalOnBean(Tracer.class)
@AutoConfigureAfter(BraveAutoConfiguration.class)
public class TraceAsyncDefaultAutoConfiguration {
@Bean
@ConditionalOnProperty(value = "spring.sleuth.scheduled.enabled", matchIfMissing = true)
static ExecutorBeanPostProcessor executorBeanPostProcessor(BeanFactory beanFactory) {
return new ExecutorBeanPostProcessor(beanFactory);
}
@Bean
TraceAsyncAspect traceAsyncAspect(Tracer tracer, SpanNamer spanNamer) {
return new TraceAsyncAspect(tracer, spanNamer);
}
/**
* Wrapper for the async executor.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(AsyncConfigurer.class)
@ConditionalOnMissingBean(AsyncConfigurer.class)
@ConditionalOnProperty(value = "spring.sleuth.async.configurer.enabled", matchIfMissing = true)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static class DefaultAsyncConfigurerSupport extends AsyncConfigurerSupport {
private static final Log log = LogFactory.getLog(DefaultAsyncConfigurerSupport.class);
@Autowired
private BeanFactory beanFactory;
@Override
public Executor getAsyncExecutor() {
Executor delegate = getDefaultExecutor();
return new LazyTraceExecutor(this.beanFactory, delegate);
}
/**
* Retrieve or build a default executor for this advice instance. An executor
* returned from here will be cached for further use.
* <p>
* The default implementation searches for a unique {@link TaskExecutor} bean in
* the context, or for an {@link Executor} bean named "taskExecutor" otherwise. If
* neither of the two is resolvable, this implementation will return {@code null}.
* @return the default executor, or {@code null} if none available
* @see AsyncExecutionAspectSupport#getDefaultExecutor(org.springframework.beans.factory.BeanFactory)
*/
private Executor getDefaultExecutor() {
try {
// Search for TaskExecutor bean... not plain Executor since that would
// match with ScheduledExecutorService as well, which is unusable for
// our purposes here. TaskExecutor is more clearly designed for it.
return this.beanFactory.getBean(TaskExecutor.class);
}
catch (NoUniqueBeanDefinitionException ex) {
if (log.isDebugEnabled()) {
log.debug("Could not find unique TaskExecutor bean", ex);
}
try {
return this.beanFactory.getBean(AsyncExecutionAspectSupport.DEFAULT_TASK_EXECUTOR_BEAN_NAME,
Executor.class);
}
catch (NoSuchBeanDefinitionException ex2) {
if (log.isInfoEnabled()) {
log.info("More than one TaskExecutor bean found within the context, and none is named "
+ "'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly "
+ "as an alias) in order to use it for async processing: " + ex.getBeanNamesFound());
}
}
}
catch (NoSuchBeanDefinitionException ex) {
log.debug("Could not find default TaskExecutor bean", ex);
try {
return this.beanFactory.getBean(AsyncExecutionAspectSupport.DEFAULT_TASK_EXECUTOR_BEAN_NAME,
Executor.class);
}
catch (NoSuchBeanDefinitionException ex2) {
log.info("No task executor bean found for async processing: "
+ "no bean of type TaskExecutor and no bean named 'taskExecutor' either");
}
// Giving up -> either using local default executor or none at all...
}
// backward compatibility
if (log.isInfoEnabled()) {
log.info(
"For backward compatibility, will fallback to the default, SimpleAsyncTaskExecutor implementation");
}
return new SimpleAsyncTaskExecutor();
}
}
}
/*
* 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.async;
import java.util.concurrent.Executor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.interceptor.AsyncExecutionAspectSupport;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
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.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.sleuth.SpanNamer;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.async.LazyTraceExecutor;
import org.springframework.cloud.sleuth.instrument.async.TraceAsyncAspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} enabling async related processing.
*
* @author Dave Syer
* @author Marcin Grzejszczak
* @since 1.0.0
* @see LazyTraceExecutor
* @see TraceAsyncAspect
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SleuthAsyncProperties.class)
@ConditionalOnProperty(value = "spring.sleuth.async.enabled", matchIfMissing = true)
@ConditionalOnBean(Tracer.class)
@AutoConfigureAfter(BraveAutoConfiguration.class)
public class TraceAsyncDefaultAutoConfiguration {
@Bean
@ConditionalOnProperty(value = "spring.sleuth.scheduled.enabled", matchIfMissing = true)
static ExecutorBeanPostProcessor executorBeanPostProcessor(BeanFactory beanFactory) {
return new ExecutorBeanPostProcessor(beanFactory);
}
@Bean
TraceAsyncAspect traceAsyncAspect(Tracer tracer, SpanNamer spanNamer) {
return new TraceAsyncAspect(tracer, spanNamer);
}
/**
* Wrapper for the async executor.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(AsyncConfigurer.class)
@ConditionalOnMissingBean(AsyncConfigurer.class)
@ConditionalOnProperty(value = "spring.sleuth.async.configurer.enabled", matchIfMissing = true)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static class DefaultAsyncConfigurerSupport extends AsyncConfigurerSupport {
private static final Log log = LogFactory.getLog(DefaultAsyncConfigurerSupport.class);
@Autowired
private BeanFactory beanFactory;
@Override
public Executor getAsyncExecutor() {
Executor delegate = getDefaultExecutor();
return new LazyTraceExecutor(this.beanFactory, delegate);
}
/**
* Retrieve or build a default executor for this advice instance. An executor
* returned from here will be cached for further use.
* <p>
* The default implementation searches for a unique {@link TaskExecutor} bean in
* the context, or for an {@link Executor} bean named "taskExecutor" otherwise. If
* neither of the two is resolvable, this implementation will return {@code null}.
* @return the default executor, or {@code null} if none available
* @see AsyncExecutionAspectSupport#getDefaultExecutor(org.springframework.beans.factory.BeanFactory)
*/
private Executor getDefaultExecutor() {
try {
// Search for TaskExecutor bean... not plain Executor since that would
// match with ScheduledExecutorService as well, which is unusable for
// our purposes here. TaskExecutor is more clearly designed for it.
return this.beanFactory.getBean(TaskExecutor.class);
}
catch (NoUniqueBeanDefinitionException ex) {
if (log.isDebugEnabled()) {
log.debug("Could not find unique TaskExecutor bean", ex);
}
try {
return this.beanFactory.getBean(AsyncExecutionAspectSupport.DEFAULT_TASK_EXECUTOR_BEAN_NAME,
Executor.class);
}
catch (NoSuchBeanDefinitionException ex2) {
if (log.isInfoEnabled()) {
log.info("More than one TaskExecutor bean found within the context, and none is named "
+ "'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly "
+ "as an alias) in order to use it for async processing: " + ex.getBeanNamesFound());
}
}
}
catch (NoSuchBeanDefinitionException ex) {
log.debug("Could not find default TaskExecutor bean", ex);
try {
return this.beanFactory.getBean(AsyncExecutionAspectSupport.DEFAULT_TASK_EXECUTOR_BEAN_NAME,
Executor.class);
}
catch (NoSuchBeanDefinitionException ex2) {
log.info("No task executor bean found for async processing: "
+ "no bean of type TaskExecutor and no bean named 'taskExecutor' either");
}
// Giving up -> either using local default executor or none at all...
}
// backward compatibility
if (log.isInfoEnabled()) {
log.info(
"For backward compatibility, will fallback to the default, SimpleAsyncTaskExecutor implementation");
}
return new SimpleAsyncTaskExecutor();
}
}
}

View File

@@ -1,112 +1,111 @@
/*
* 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();
}
}
/*
* 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,78 +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.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));
}
}
/*
* 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,188 @@
/*
* 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 {
}
}
/*
* 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 - 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;
}
}
/*
* 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,71 @@
/*
* 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();
}
}
/*
* 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,104 +1,103 @@
/*
* 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 {
}
/*
* 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,371 +1,370 @@
/*
* 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;
}
}
}
/*
* 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,278 @@
/*
* 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;
}
}
}
/*
* 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.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,239 +1,238 @@
/*
* 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");
}
};
}
}
}
/*
* 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,66 +1,65 @@
/*
* 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);
});
}
}
/*
* 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,69 +1,68 @@
/*
* 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);
});
}
}
/*
* 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,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 {
/**
* Related to JDBC connections.
*/
CONNECTION,
/**
* Related to query executions.
*/
QUERY,
/**
* Related to ResultSets.
*/
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

@@ -1,60 +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();
}
}
}
/*
* 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

@@ -1,60 +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();
}
}
}
/*
* 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

@@ -1,237 +1,238 @@
/*
* 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;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import brave.Span;
import brave.Tracer;
import brave.handler.MutableSpan;
import brave.handler.SpanHandler;
import brave.sampler.Sampler;
import brave.test.TestSpanHandler;
import org.awaitility.Awaitility;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.SpanName;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.BDDAssertions.then;
@SpringBootTest(classes = { TraceAsyncIntegrationTests.TraceAsyncITestConfiguration.class })
public class TraceAsyncIntegrationTests {
@Autowired
ClassPerformingAsyncLogic classPerformingAsyncLogic;
@Autowired
Tracer tracer;
@Autowired
TestSpanHandler spans;
@BeforeEach
public void cleanup() {
this.spans.clear();
this.classPerformingAsyncLogic.clear();
}
@Test
public void should_set_span_on_an_async_annotated_method() {
whenAsyncProcessingTakesPlace();
thenANewAsyncSpanGetsCreated();
}
@Test
public void should_set_span_with_custom_method_on_an_async_annotated_method() {
whenAsyncProcessingTakesPlaceWithCustomSpanName();
thenAsyncSpanHasCustomName();
}
@Test
public void should_continue_a_span_on_an_async_annotated_method() {
Span span = givenASpanInCurrentThread();
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
whenAsyncProcessingTakesPlace();
}
finally {
span.finish();
}
thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOne(span);
}
@Test
public void should_continue_a_span_with_custom_method_on_an_async_annotated_method() {
Span span = givenASpanInCurrentThread();
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
whenAsyncProcessingTakesPlaceWithCustomSpanName();
}
finally {
span.finish();
}
thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOneAndSpanHasCustomName(span);
}
private Span givenASpanInCurrentThread() {
return this.tracer.nextSpan().name("http:existing");
}
private void whenAsyncProcessingTakesPlace() {
this.classPerformingAsyncLogic.invokeAsynchronousLogic();
}
private void whenAsyncProcessingTakesPlaceWithCustomSpanName() {
this.classPerformingAsyncLogic.customNameInvokeAsynchronousLogic();
}
private void thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOne(final Span span) {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
then(TraceAsyncIntegrationTests.this.classPerformingAsyncLogic.getSpan().context().traceId())
.isEqualTo(span.context().traceId());
List<MutableSpan> webSpans = this.spans.spans().stream().filter(mutableSpan -> mutableSpan.traceId().equalsIgnoreCase(span.context().traceIdString()))
.collect(Collectors.toList());
then(webSpans).hasSize(2);
// HTTP
then(webSpans.get(0).name()).isEqualTo("http:existing");
// ASYNC
then(webSpans.get(1).tags()).containsEntry("class", "ClassPerformingAsyncLogic").containsEntry("method",
"invokeAsynchronousLogic");
});
}
private void thenANewAsyncSpanGetsCreated() {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
then(this.spans).hasSize(1);
MutableSpan storedSpan = this.spans.get(0);
then(storedSpan.name()).isEqualTo("invoke-asynchronous-logic");
then(storedSpan.tags()).containsEntry("class", "ClassPerformingAsyncLogic").containsEntry("method",
"invokeAsynchronousLogic");
});
}
private void thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOneAndSpanHasCustomName(final Span span) {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
then(TraceAsyncIntegrationTests.this.classPerformingAsyncLogic.getSpan()).isNotNull();
then(TraceAsyncIntegrationTests.this.classPerformingAsyncLogic.getSpan().context().traceId())
.isEqualTo(span.context().traceId());
then(this.spans).hasSize(2);
// HTTP
then(this.spans.get(0).name()).isEqualTo("http:existing");
// ASYNC
then(this.spans.get(1).tags()).containsEntry("class", "ClassPerformingAsyncLogic").containsEntry("method",
"customNameInvokeAsynchronousLogic");
});
}
private void thenAsyncSpanHasCustomName() {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
then(this.spans).hasSize(1);
MutableSpan storedSpan = this.spans.get(0);
then(storedSpan.name()).isEqualTo("foo");
then(storedSpan.tags()).containsEntry("class", "ClassPerformingAsyncLogic").containsEntry("method",
"customNameInvokeAsynchronousLogic");
});
}
@AfterEach
public void cleanTrace() {
this.spans.clear();
}
@EnableAutoConfiguration
@EnableAsync
@Configuration(proxyBeanMethods = false)
static class TraceAsyncITestConfiguration {
@Bean
ClassPerformingAsyncLogic asyncClass(Tracer tracer) {
return new ClassPerformingAsyncLogic(tracer);
}
@Bean
Sampler defaultSampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
SpanHandler testSpanHandler() {
return new TestSpanHandler();
}
@Bean
Executor fooExecutor() {
return new SimpleAsyncTaskExecutor();
}
@Bean
Executor barExecutor() {
return new SimpleAsyncTaskExecutor();
}
}
static class ClassPerformingAsyncLogic {
private final Tracer tracer;
AtomicReference<Span> span = new AtomicReference<>();
ClassPerformingAsyncLogic(Tracer tracer) {
this.tracer = tracer;
}
@Async("fooExecutor")
public void invokeAsynchronousLogic() {
this.span.set(this.tracer.currentSpan());
}
@Async
@SpanName("foo")
public void customNameInvokeAsynchronousLogic() {
this.span.set(this.tracer.currentSpan());
}
public Span getSpan() {
return this.span.get();
}
public void clear() {
this.span.set(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.brave.instrument.web;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import brave.Span;
import brave.Tracer;
import brave.handler.MutableSpan;
import brave.handler.SpanHandler;
import brave.sampler.Sampler;
import brave.test.TestSpanHandler;
import org.awaitility.Awaitility;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.SpanName;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.BDDAssertions.then;
@SpringBootTest(classes = { TraceAsyncIntegrationTests.TraceAsyncITestConfiguration.class })
public class TraceAsyncIntegrationTests {
@Autowired
ClassPerformingAsyncLogic classPerformingAsyncLogic;
@Autowired
Tracer tracer;
@Autowired
TestSpanHandler spans;
@BeforeEach
public void cleanup() {
this.spans.clear();
this.classPerformingAsyncLogic.clear();
}
@Test
public void should_set_span_on_an_async_annotated_method() {
whenAsyncProcessingTakesPlace();
thenANewAsyncSpanGetsCreated();
}
@Test
public void should_set_span_with_custom_method_on_an_async_annotated_method() {
whenAsyncProcessingTakesPlaceWithCustomSpanName();
thenAsyncSpanHasCustomName();
}
@Test
public void should_continue_a_span_on_an_async_annotated_method() {
Span span = givenASpanInCurrentThread();
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
whenAsyncProcessingTakesPlace();
}
finally {
span.finish();
}
thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOne(span);
}
@Test
public void should_continue_a_span_with_custom_method_on_an_async_annotated_method() {
Span span = givenASpanInCurrentThread();
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
whenAsyncProcessingTakesPlaceWithCustomSpanName();
}
finally {
span.finish();
}
thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOneAndSpanHasCustomName(span);
}
private Span givenASpanInCurrentThread() {
return this.tracer.nextSpan().name("http:existing");
}
private void whenAsyncProcessingTakesPlace() {
this.classPerformingAsyncLogic.invokeAsynchronousLogic();
}
private void whenAsyncProcessingTakesPlaceWithCustomSpanName() {
this.classPerformingAsyncLogic.customNameInvokeAsynchronousLogic();
}
private void thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOne(final Span span) {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
then(TraceAsyncIntegrationTests.this.classPerformingAsyncLogic.getSpan().context().traceId())
.isEqualTo(span.context().traceId());
List<MutableSpan> webSpans = this.spans.spans().stream()
.filter(mutableSpan -> mutableSpan.traceId().equalsIgnoreCase(span.context().traceIdString()))
.collect(Collectors.toList());
then(webSpans).hasSize(2);
// HTTP
then(webSpans.get(0).name()).isEqualTo("http:existing");
// ASYNC
then(webSpans.get(1).tags()).containsEntry("class", "ClassPerformingAsyncLogic").containsEntry("method",
"invokeAsynchronousLogic");
});
}
private void thenANewAsyncSpanGetsCreated() {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
then(this.spans).hasSize(1);
MutableSpan storedSpan = this.spans.get(0);
then(storedSpan.name()).isEqualTo("invoke-asynchronous-logic");
then(storedSpan.tags()).containsEntry("class", "ClassPerformingAsyncLogic").containsEntry("method",
"invokeAsynchronousLogic");
});
}
private void thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOneAndSpanHasCustomName(final Span span) {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
then(TraceAsyncIntegrationTests.this.classPerformingAsyncLogic.getSpan()).isNotNull();
then(TraceAsyncIntegrationTests.this.classPerformingAsyncLogic.getSpan().context().traceId())
.isEqualTo(span.context().traceId());
then(this.spans).hasSize(2);
// HTTP
then(this.spans.get(0).name()).isEqualTo("http:existing");
// ASYNC
then(this.spans.get(1).tags()).containsEntry("class", "ClassPerformingAsyncLogic").containsEntry("method",
"customNameInvokeAsynchronousLogic");
});
}
private void thenAsyncSpanHasCustomName() {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
then(this.spans).hasSize(1);
MutableSpan storedSpan = this.spans.get(0);
then(storedSpan.name()).isEqualTo("foo");
then(storedSpan.tags()).containsEntry("class", "ClassPerformingAsyncLogic").containsEntry("method",
"customNameInvokeAsynchronousLogic");
});
}
@AfterEach
public void cleanTrace() {
this.spans.clear();
}
@EnableAutoConfiguration
@EnableAsync
@Configuration(proxyBeanMethods = false)
static class TraceAsyncITestConfiguration {
@Bean
ClassPerformingAsyncLogic asyncClass(Tracer tracer) {
return new ClassPerformingAsyncLogic(tracer);
}
@Bean
Sampler defaultSampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
SpanHandler testSpanHandler() {
return new TestSpanHandler();
}
@Bean
Executor fooExecutor() {
return new SimpleAsyncTaskExecutor();
}
@Bean
Executor barExecutor() {
return new SimpleAsyncTaskExecutor();
}
}
static class ClassPerformingAsyncLogic {
private final Tracer tracer;
AtomicReference<Span> span = new AtomicReference<>();
ClassPerformingAsyncLogic(Tracer tracer) {
this.tracer = tracer;
}
@Async("fooExecutor")
public void invokeAsynchronousLogic() {
this.span.set(this.tracer.currentSpan());
}
@Async
@SpanName("foo")
public void customNameInvokeAsynchronousLogic() {
this.span.set(this.tracer.currentSpan());
}
public Span getSpan() {
return this.span.get();
}
public void clear() {
this.span.set(null);
}
}
}

View File

@@ -1,60 +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.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);
}
}
/*
* 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

@@ -1,79 +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.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");
});
}
}
/*
* 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,39 +1,40 @@
/*
* 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"));
}
}
/*
* 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"));
}
}