From 685fb727b5798facd23d486ba9e111ff2c26eee1 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Thu, 27 May 2021 14:05:39 +0200 Subject: [PATCH] Polish --- .../TraceAsyncDefaultAutoConfiguration.java | 306 ++-- .../jdbc/DataSourceProxyConfiguration.java | 223 ++- .../instrument/jdbc/P6SpyConfiguration.java | 156 +- .../jdbc/P6SpyPropertiesSetter.java | 375 ++--- ...eDataSourceDecoratorBeanPostProcessor.java | 280 ++-- .../jdbc/TraceJdbcAutoConfiguration.java | 141 +- .../instrument/jdbc/TraceJdbcProperties.java | 1022 +++++------ .../TraceNoOpAutoConfiguration.java | 207 ++- ...SourceDecoratorAutoConfigurationTests.java | 741 ++++---- .../jdbc/P6SpyConfigurationTests.java | 558 +++---- .../ProxyDataSourceConfigurationTests.java | 477 +++--- ...thP6SpyListenerAutoConfigurationTests.java | 131 +- ...aSourceListenerAutoConfigurationTests.java | 137 +- .../TraceListenerStrategySpanCustomizer.java | 86 +- .../sleuth/instrument/jdbc/TraceType.java | 78 +- .../jdbc/TracingJdbcEventListenerTests.java | 120 +- .../TracingQueryExecutionListenerTests.java | 120 +- .../web/TraceAsyncIntegrationTests.java | 475 +++--- .../instrument/web/view/Issue469Tests.java | 120 +- .../jdbc/TracingJdbcEventListenerTests.java | 157 +- .../jdbc/TracingListenerStrategyTests.java | 1487 +++++++++-------- .../TracingQueryExecutionListenerTests.java | 79 +- 22 files changed, 3736 insertions(+), 3740 deletions(-) diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/async/TraceAsyncDefaultAutoConfiguration.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/async/TraceAsyncDefaultAutoConfiguration.java index 72feab40a..18fb2fbdf 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/async/TraceAsyncDefaultAutoConfiguration.java +++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/async/TraceAsyncDefaultAutoConfiguration.java @@ -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. - *

- * 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. + *

+ * 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(); + } + + } + +} diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/DataSourceProxyConfiguration.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/DataSourceProxyConfiguration.java index 944e74fa7..876efc38c 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/DataSourceProxyConfiguration.java +++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/DataSourceProxyConfiguration.java @@ -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, - ObjectProvider> listeners, - ObjectProvider> methodExecutionListeners, - ObjectProvider parameterTransformer, - ObjectProvider queryTransformer, - ObjectProvider resultSetProxyLogicFactory, - ObjectProvider 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> 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, + ObjectProvider> listeners, + ObjectProvider> methodExecutionListeners, + ObjectProvider parameterTransformer, + ObjectProvider queryTransformer, + ObjectProvider resultSetProxyLogicFactory, + ObjectProvider 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> customizers) { + return new TraceQueryExecutionListener(tracer, dataSourceDecoratorProperties.getIncludes(), + customizers.getIfAvailable(ArrayList::new)); + } + + @Bean + @ConditionalOnMissingBean + ResultSetProxyLogicFactory traceResultSetProxyLogicFactory() { + return new SimpleResultSetProxyLogicFactory(); + } + +} diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/P6SpyConfiguration.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/P6SpyConfiguration.java index b38bd2d85..68f05063e 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/P6SpyConfiguration.java +++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/P6SpyConfiguration.java @@ -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> listeners) { - JdbcEventListenerFactory jdbcEventListenerFactory = new DefaultJdbcEventListenerFactory(); - List 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> 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> listeners) { + JdbcEventListenerFactory jdbcEventListenerFactory = new DefaultJdbcEventListenerFactory(); + List 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> customizers) { + return new TraceJdbcEventListener(tracer, dataSourceNameResolver, traceJdbcProperties.getIncludes(), + traceJdbcProperties.getP6spy().getTracing().isIncludeParameterValues(), + customizers.getIfAvailable(ArrayList::new)); + } + +} diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/P6SpyPropertiesSetter.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/P6SpyPropertiesSetter.java index f12826584..b5633a019 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/P6SpyPropertiesSetter.java +++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/P6SpyPropertiesSetter.java @@ -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 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 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 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 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 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 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 { + + } + +} diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/TraceDataSourceDecoratorBeanPostProcessor.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/TraceDataSourceDecoratorBeanPostProcessor.java index 3c1a78142..1788fddeb 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/TraceDataSourceDecoratorBeanPostProcessor.java +++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/TraceDataSourceDecoratorBeanPostProcessor.java @@ -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 excludedBeans; - - public TraceDataSourceDecoratorBeanPostProcessor(Collection 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 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 decorators) { - getDataSourceNameResolver().addDataSource(name, dataSource); - DataSource decoratedDataSource = dataSource; - for (Entry 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 excludedBeans; + + public TraceDataSourceDecoratorBeanPostProcessor(Collection 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 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 decorators) { + getDataSourceNameResolver().addDataSource(name, dataSource); + DataSource decoratedDataSource = dataSource; + for (Entry 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; + } + +} diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/TraceJdbcAutoConfiguration.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/TraceJdbcAutoConfiguration.java index c8c385ba9..80233866e 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/TraceJdbcAutoConfiguration.java +++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/TraceJdbcAutoConfiguration.java @@ -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(); + } + +} diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/TraceJdbcProperties.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/TraceJdbcProperties.java index bd16574da..a0ffa1f4e 100644 --- a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/TraceJdbcProperties.java +++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/TraceJdbcProperties.java @@ -1,511 +1,511 @@ -/* - * Copyright 2013-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.sleuth.autoconfig.instrument.jdbc; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.regex.Pattern; - -import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.cloud.sleuth.instrument.jdbc.TraceType; - -/** - * Properties for JDBC instrumentation. - * - * @author Arthur Gavlyukovskiy - * @since 3.1.0 - */ -@ConfigurationProperties(prefix = "spring.sleuth.jdbc") -public class TraceJdbcProperties { - - /** - * Enables JDBC instrumentation. - */ - private boolean enabled = true; - - /** - * List of DataSource bean names that will not be decorated. - */ - private Collection excludedDataSourceBeanNames = Collections.emptyList(); - - /** - * Which types of tracing we would like to include. - */ - private List includes = Arrays.asList(TraceType.CONNECTION, TraceType.QUERY, TraceType.FETCH); - - private DataSourceProxyProperties datasourceProxy = new DataSourceProxyProperties(); - - private P6SpyProperties p6spy = new P6SpyProperties(); - - public boolean isEnabled() { - return this.enabled; - } - - public Collection getExcludedDataSourceBeanNames() { - return this.excludedDataSourceBeanNames; - } - - public List getIncludes() { - return this.includes; - } - - public void setIncludes(List includes) { - this.includes = includes; - } - - public DataSourceProxyProperties getDatasourceProxy() { - return datasourceProxy; - } - - public P6SpyProperties getP6spy() { - return this.p6spy; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public void setExcludedDataSourceBeanNames(Collection excludedDataSourceBeanNames) { - this.excludedDataSourceBeanNames = excludedDataSourceBeanNames; - } - - public void setDatasourceProxy(DataSourceProxyProperties datasourceProxy) { - this.datasourceProxy = datasourceProxy; - } - - public void setP6spy(P6SpyProperties p6spy) { - this.p6spy = p6spy; - } - - /** - * Properties for datasource-proxy. - */ - public static class DataSourceProxyProperties { - - /** - * Should the datasource-proxy tracing be enabled? - */ - private boolean enabled = true; - - /** - * Logging to use for logging queries. - */ - private DataSourceProxyLogging logging = DataSourceProxyLogging.SLF4J; - - /** - * Query configuration. - */ - private Query query = new Query(); - - /** - * Slow query configuration. - */ - private SlowQuery slowQuery = new SlowQuery(); - - /** - * Use multiline output for logging query. - * - * @see ProxyDataSourceBuilder#multiline() - */ - private boolean multiline = true; - - /** - * Use json output for logging query. - * - * @see ProxyDataSourceBuilder#asJson() - */ - private boolean jsonFormat = false; - - public boolean isEnabled() { - return this.enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public DataSourceProxyLogging getLogging() { - return this.logging; - } - - public void setLogging(DataSourceProxyLogging logging) { - this.logging = logging; - } - - public Query getQuery() { - return this.query; - } - - public void setQuery(Query query) { - this.query = query; - } - - public SlowQuery getSlowQuery() { - return this.slowQuery; - } - - public void setSlowQuery(SlowQuery slowQuery) { - this.slowQuery = slowQuery; - } - - public boolean isMultiline() { - return this.multiline; - } - - public void setMultiline(boolean multiline) { - this.multiline = multiline; - } - - public boolean isJsonFormat() { - return this.jsonFormat; - } - - public void setJsonFormat(boolean jsonFormat) { - this.jsonFormat = jsonFormat; - } - - /** - * Properties to configure query logging listener. - */ - public static class Query { - - /** - * Enable logging all queries to the log. - */ - private boolean enableLogging = true; - - /** - * Name of query logger. - */ - private String loggerName; - - /** - * Severity of query logger. - */ - private String logLevel = "DEBUG"; - - public boolean isEnableLogging() { - return this.enableLogging; - } - - public void setEnableLogging(boolean enableLogging) { - this.enableLogging = enableLogging; - } - - public String getLoggerName() { - return this.loggerName; - } - - public void setLoggerName(String loggerName) { - this.loggerName = loggerName; - } - - public String getLogLevel() { - return this.logLevel; - } - - public void setLogLevel(String logLevel) { - this.logLevel = logLevel; - } - - } - - /** - * Properties to configure slow query logging listener. - */ - public static class SlowQuery { - - /** - * Enable logging slow queries to the log. - */ - private boolean enableLogging = true; - - /** - * Name of slow query logger. - */ - private String loggerName; - - /** - * Severity of slow query logger. - */ - private String logLevel = "WARN"; - - /** - * Number of seconds to consider query as slow. - */ - private long threshold = 300; - - boolean isEnableLogging() { - return enableLogging; - } - - public void setEnableLogging(boolean enableLogging) { - this.enableLogging = enableLogging; - } - - public String getLoggerName() { - return loggerName; - } - - public void setLoggerName(String loggerName) { - this.loggerName = loggerName; - } - - public String getLogLevel() { - return logLevel; - } - - public void setLogLevel(String logLevel) { - this.logLevel = logLevel; - } - - public long getThreshold() { - return threshold; - } - - public void setThreshold(long threshold) { - this.threshold = threshold; - } - - } - - /** - * Query logging listener is the most used listener that logs executing query with - * actual parameters to. You can pick one of the following proxy logging - * mechanisms. - */ - public enum DataSourceProxyLogging { - - /** - * Log using System.out. - */ - SYSOUT, - - /** - * Log using SLF4J. - */ - SLF4J, - - /** - * Log using Commons. - */ - COMMONS, - - /** - * Log using Java Util Logging. - */ - JUL - - } - - } - - /** - * Properties for configuring p6spy. - */ - public static class P6SpyProperties { - - /** - * Should the p6spy tracing be enabled? - */ - private boolean enabled = true; - - /** - * Enables logging JDBC events. - */ - private boolean enableLogging = true; - - /** - * Enables multiline output. - */ - private boolean multiline = true; - - /** - * Logging to use for logging queries. - */ - private P6SpyLogging logging = P6SpyLogging.SLF4J; - - /** - * Name of log file to use (only with logging=file). - */ - private String logFile = "spy.log"; - - /** - * Custom log format. - */ - private String logFormat; - - /** - * Tracing related properties. - */ - private P6SpyTracing tracing = new P6SpyTracing(); - - /** - * Class file to use (only with logging=custom). The class must implement - * {@link com.p6spy.engine.spy.appender.FormattedLogger}. - */ - private String customAppenderClass; - - /** - * Log filtering related properties. - */ - private P6SpyLogFilter logFilter = new P6SpyLogFilter(); - - public boolean isEnabled() { - return this.enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public boolean isEnableLogging() { - return this.enableLogging; - } - - public void setEnableLogging(boolean enableLogging) { - this.enableLogging = enableLogging; - } - - public boolean isMultiline() { - return this.multiline; - } - - public void setMultiline(boolean multiline) { - this.multiline = multiline; - } - - public P6SpyLogging getLogging() { - return this.logging; - } - - public void setLogging(P6SpyLogging logging) { - this.logging = logging; - } - - public String getLogFile() { - return this.logFile; - } - - public void setLogFile(String logFile) { - this.logFile = logFile; - } - - public String getLogFormat() { - return this.logFormat; - } - - public void setLogFormat(String logFormat) { - this.logFormat = logFormat; - } - - public P6SpyTracing getTracing() { - return this.tracing; - } - - public void setTracing(P6SpyTracing tracing) { - this.tracing = tracing; - } - - public String getCustomAppenderClass() { - return this.customAppenderClass; - } - - public void setCustomAppenderClass(String customAppenderClass) { - this.customAppenderClass = customAppenderClass; - } - - public P6SpyLogFilter getLogFilter() { - return this.logFilter; - } - - public void setLogFilter(P6SpyLogFilter logFilter) { - this.logFilter = logFilter; - } - - /** - * P6Spy logging options. - */ - public enum P6SpyLogging { - - /** - * Log using System.out. - */ - SYSOUT, - - /** - * Log using SLF4J. - */ - SLF4J, - - /** - * Log to file. - */ - FILE, - - /** - * Custom logging. - */ - CUSTOM - - } - - public static class P6SpyTracing { - - /** - * Report the effective sql string (with '?' replaced with real values) to - * tracing systems. - *

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

+ * NOTE this setting does not affect the logging message. + */ + private boolean includeParameterValues = true; + + public boolean isIncludeParameterValues() { + return this.includeParameterValues; + } + + public void setIncludeParameterValues(boolean includeParameterValues) { + this.includeParameterValues = includeParameterValues; + } + + } + + public static class P6SpyLogFilter { + + /** + * Use regex pattern to filter log messages. Only matched messages will be + * logged. + */ + private Pattern pattern; + + public Pattern getPattern() { + return this.pattern; + } + + public void setPattern(Pattern pattern) { + this.pattern = pattern; + } + + } + + } + +} diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceNoOpAutoConfiguration.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceNoOpAutoConfiguration.java index 491a6ea42..c6ed16337 100644 --- a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceNoOpAutoConfiguration.java +++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/TraceNoOpAutoConfiguration.java @@ -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 { + +} diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/DataSourceDecoratorAutoConfigurationTests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/DataSourceDecoratorAutoConfigurationTests.java index f2a08a139..f9d79f63c 100644 --- a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/DataSourceDecoratorAutoConfigurationTests.java +++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/DataSourceDecoratorAutoConfigurationTests.java @@ -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 unwrap(Class 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 unwrap(Class 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; + } + + } + +} diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/P6SpyConfigurationTests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/P6SpyConfigurationTests.java index 0573e862d..2c5a9c551 100644 --- a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/P6SpyConfigurationTests.java +++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/P6SpyConfigurationTests.java @@ -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 MESSAGES = new ArrayList<>(); - static final List 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 MESSAGES = new ArrayList<>(); + static final List 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; + } + + } + +} diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/ProxyDataSourceConfigurationTests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/ProxyDataSourceConfigurationTests.java index 6b10c2d95..844f34a23 100644 --- a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/ProxyDataSourceConfigurationTests.java +++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/ProxyDataSourceConfigurationTests.java @@ -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 queryInfoList) { - System.out.println("beforeQuery"); - } - - @Override - public void afterQuery(ExecutionInfo execInfo, List 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 queryInfoList) { + System.out.println("beforeQuery"); + } + + @Override + public void afterQuery(ExecutionInfo execInfo, List queryInfoList) { + System.out.println("afterQuery"); + } + }; + } + + } + +} diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/SleuthP6SpyListenerAutoConfigurationTests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/SleuthP6SpyListenerAutoConfigurationTests.java index 0d1153ef0..af03d9335 100644 --- a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/SleuthP6SpyListenerAutoConfigurationTests.java +++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/SleuthP6SpyListenerAutoConfigurationTests.java @@ -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); + }); + } + +} diff --git a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/SleuthProxyDataSourceListenerAutoConfigurationTests.java b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/SleuthProxyDataSourceListenerAutoConfigurationTests.java index 9380ce316..1bc7cd052 100644 --- a/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/SleuthProxyDataSourceListenerAutoConfigurationTests.java +++ b/spring-cloud-sleuth-autoconfigure/src/test/java/org/springframework/cloud/sleuth/autoconfig/instrument/jdbc/SleuthProxyDataSourceListenerAutoConfigurationTests.java @@ -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); + }); + } + +} diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/jdbc/TraceListenerStrategySpanCustomizer.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/jdbc/TraceListenerStrategySpanCustomizer.java index 3fd40e9d5..52fe0ae6b 100644 --- a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/jdbc/TraceListenerStrategySpanCustomizer.java +++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/jdbc/TraceListenerStrategySpanCustomizer.java @@ -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 { - - /** - * 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 { + + /** + * 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); + +} diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/jdbc/TraceType.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/jdbc/TraceType.java index 2348ae3fc..cef75651a 100644 --- a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/jdbc/TraceType.java +++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/jdbc/TraceType.java @@ -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 + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-jdbc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/jdbc/TracingJdbcEventListenerTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-jdbc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/jdbc/TracingJdbcEventListenerTests.java index 96b6c08aa..0abe1b11e 100644 --- a/tests/brave/spring-cloud-sleuth-instrumentation-jdbc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/jdbc/TracingJdbcEventListenerTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-jdbc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/jdbc/TracingJdbcEventListenerTests.java @@ -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(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-jdbc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/jdbc/TracingQueryExecutionListenerTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-jdbc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/jdbc/TracingQueryExecutionListenerTests.java index c2a442054..e9bacd2b8 100644 --- a/tests/brave/spring-cloud-sleuth-instrumentation-jdbc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/jdbc/TracingQueryExecutionListenerTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-jdbc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/jdbc/TracingQueryExecutionListenerTests.java @@ -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(); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceAsyncIntegrationTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceAsyncIntegrationTests.java index 5ebdeb20d..e24da75ef 100644 --- a/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceAsyncIntegrationTests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/TraceAsyncIntegrationTests.java @@ -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 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 = 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 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 = 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); + } + + } + +} diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/view/Issue469Tests.java b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/view/Issue469Tests.java index 9f12664e7..530b1e496 100644 --- a/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/view/Issue469Tests.java +++ b/tests/brave/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/web/view/Issue469Tests.java @@ -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); + } + +} diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/jdbc/TracingJdbcEventListenerTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/jdbc/TracingJdbcEventListenerTests.java index ac0cf1b03..776dd5aa1 100644 --- a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/jdbc/TracingJdbcEventListenerTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/jdbc/TracingJdbcEventListenerTests.java @@ -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"); + }); + } + +} diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/jdbc/TracingListenerStrategyTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/jdbc/TracingListenerStrategyTests.java index de201d45d..a598e9504 100644 --- a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/jdbc/TracingListenerStrategyTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/jdbc/TracingListenerStrategyTests.java @@ -1,743 +1,744 @@ -/* - * 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 java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import javax.sql.DataSource; - -import com.zaxxer.hikari.HikariDataSource; -import org.junit.jupiter.api.Test; - -import org.springframework.boot.test.context.assertj.AssertableApplicationContext; -import org.springframework.boot.test.context.runner.ApplicationContextRunner; -import org.springframework.cloud.sleuth.Tracer; -import org.springframework.cloud.sleuth.exporter.FinishedSpan; -import org.springframework.cloud.sleuth.test.TestSpanHandler; -import org.springframework.context.annotation.Bean; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -abstract class TracingListenerStrategyTests { - - public static final String SPAN_SQL_QUERY_TAG_NAME = "jdbc.query"; - - public static final String SPAN_ROW_COUNT_TAG_NAME = "jdbc.row-count"; - - abstract ApplicationContextRunner parentContextRunner(); - - protected abstract Class autoConfiguration(); - - protected abstract Class testConfiguration(); - - @Test - void testShouldAddSpanForConnection() { - parentContextRunner().run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection = dataSource.getConnection(); - connection.commit(); - connection.rollback(); - connection.close(); - - assertThat(spanReporter.reportedSpans()).hasSize(1); - FinishedSpan connectionSpan = spanReporter.reportedSpans().get(0); - assertThat(connectionSpan.getName()).isEqualTo("connection"); - assertThat(connectionSpan.getRemoteServiceName()).isEqualTo("TESTDB-BAZ"); - assertThat(connectionSpan.getEvents()).extracting("value").contains("jdbc.commit"); - assertThat(connectionSpan.getEvents()).extracting("value").contains("jdbc.rollback"); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldAddSpanForConnectionWithFixedRemoteServiceName() { - parentContextRunner().withPropertyValues("spring.datasource.url:jdbc:h2:mem:testdb-baz?sleuthServiceName=aaaabbbb") - .run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection = dataSource.getConnection(); - connection.commit(); - connection.rollback(); - connection.close(); - - assertThat(spanReporter.reportedSpans()).hasSize(1); - FinishedSpan connectionSpan = spanReporter.reportedSpans().get(0); - assertThat(connectionSpan.getName()).isEqualTo("connection"); - assertThat(connectionSpan.getRemoteServiceName()).isEqualTo("aaaabbbb"); - assertThat(connectionSpan.getEvents()).extracting("value").contains("jdbc.commit"); - assertThat(connectionSpan.getEvents()).extracting("value").contains("jdbc.rollback"); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldAddSpanForPreparedStatementExecute() { - parentContextRunner().run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection = dataSource.getConnection(); - connection.prepareStatement("SELECT NOW()").execute(); - connection.close(); - - assertThat(spanReporter.reportedSpans()).hasSize(2); - FinishedSpan connectionSpan = spanReporter.reportedSpans().get(1); - FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); - assertThat(connectionSpan.getName()).isEqualTo("connection"); - assertThat(statementSpan.getName()).isEqualTo("select"); - assertThat(statementSpan.getRemoteServiceName()).isEqualTo("TESTDB-BAZ"); - assertThat(statementSpan.getTags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME, "SELECT NOW()"); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldAddSpanForPreparedStatementExecuteUpdate() { - parentContextRunner().run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection = dataSource.getConnection(); - connection.prepareStatement("UPDATE INFORMATION_SCHEMA.TABLES SET table_Name = '' WHERE 0 = 1") - .executeUpdate(); - connection.close(); - - assertThat(spanReporter.reportedSpans()).hasSize(2); - FinishedSpan connectionSpan = spanReporter.reportedSpans().get(1); - FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); - assertThat(connectionSpan.getName()).isEqualTo("connection"); - assertThat(statementSpan.getName()).isEqualTo("update"); - assertThat(statementSpan.getTags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME, - "UPDATE INFORMATION_SCHEMA.TABLES SET table_Name = '' WHERE 0 = 1"); - assertThat(statementSpan.getTags()).containsEntry(SPAN_ROW_COUNT_TAG_NAME, "0"); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldAddSpanForStatementExecuteUpdate() { - parentContextRunner().run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection = dataSource.getConnection(); - connection.createStatement() - .executeUpdate("UPDATE INFORMATION_SCHEMA.TABLES SET table_Name = '' WHERE 0 = 1"); - connection.close(); - - assertThat(spanReporter.reportedSpans()).hasSize(2); - FinishedSpan connectionSpan = spanReporter.reportedSpans().get(1); - FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); - assertThat(connectionSpan.getName()).isEqualTo("connection"); - assertThat(statementSpan.getName()).isEqualTo("update"); - assertThat(statementSpan.getTags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME, - "UPDATE INFORMATION_SCHEMA.TABLES SET table_Name = '' WHERE 0 = 1"); - assertThat(statementSpan.getTags()).containsEntry(SPAN_ROW_COUNT_TAG_NAME, "0"); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldAddSpanForPreparedStatementExecuteQueryIncludingTimeToCloseResultSet() { - parentContextRunner().run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection = dataSource.getConnection(); - ResultSet resultSet = connection.prepareStatement("SELECT NOW() UNION ALL select NOW()").executeQuery(); - resultSet.next(); - resultSet.next(); - resultSet.close(); - connection.close(); - - assertThat(spanReporter.reportedSpans()).hasSize(3); - FinishedSpan connectionSpan = spanReporter.reportedSpans().get(2); - FinishedSpan resultSetSpan = spanReporter.reportedSpans().get(1); - FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); - assertThat(connectionSpan.getName()).isEqualTo("connection"); - assertThat(statementSpan.getName()).isEqualTo("select"); - assertThat(statementSpan.getTags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME, - "SELECT NOW() UNION ALL select NOW()"); - assertThat(resultSetSpan.getName()).isEqualTo("result-set"); - assertThat(resultSetSpan.getRemoteServiceName()).isEqualTo("TESTDB-BAZ"); - if (isP6Spy(context)) { - assertThat(resultSetSpan.getTags()).containsEntry(SPAN_ROW_COUNT_TAG_NAME, "2"); - } - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldAddSpanForStatementAndResultSet() { - parentContextRunner().run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection = dataSource.getConnection(); - ResultSet resultSet = connection.createStatement().executeQuery("SELECT NOW()"); - resultSet.next(); - Thread.sleep(200L); - resultSet.close(); - connection.close(); - - assertThat(spanReporter.reportedSpans()).hasSize(3); - FinishedSpan connectionSpan = spanReporter.reportedSpans().get(2); - FinishedSpan resultSetSpan = spanReporter.reportedSpans().get(1); - FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); - assertThat(connectionSpan.getName()).isEqualTo("connection"); - assertThat(statementSpan.getName()).isEqualTo("select"); - assertThat(statementSpan.getTags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME, "SELECT NOW()"); - assertThat(resultSetSpan.getName()).isEqualTo("result-set"); - if (isP6Spy(context)) { - assertThat(resultSetSpan.getTags()).containsEntry(SPAN_ROW_COUNT_TAG_NAME, "1"); - } - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldNotFailWhenStatementIsClosedWihoutResultSet() { - parentContextRunner().run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection = dataSource.getConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SELECT NOW()"); - resultSet.next(); - statement.close(); - connection.close(); - - assertThat(spanReporter.reportedSpans()).hasSize(3); - FinishedSpan connectionSpan = spanReporter.reportedSpans().get(2); - FinishedSpan resultSetSpan = spanReporter.reportedSpans().get(1); - FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); - assertThat(connectionSpan.getName()).isEqualTo("connection"); - assertThat(statementSpan.getName()).isEqualTo("select"); - assertThat(resultSetSpan.getName()).isEqualTo("result-set"); - assertThat(statementSpan.getTags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME, "SELECT NOW()"); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldNotFailWhenConnectionIsClosedWihoutResultSet() { - parentContextRunner().run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection = dataSource.getConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SELECT NOW()"); - resultSet.next(); - connection.close(); - - assertThat(spanReporter.reportedSpans()).hasSize(3); - FinishedSpan connectionSpan = spanReporter.reportedSpans().get(2); - FinishedSpan resultSetSpan = spanReporter.reportedSpans().get(1); - FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); - assertThat(connectionSpan.getName()).isEqualTo("connection"); - assertThat(statementSpan.getName()).isEqualTo("select"); - assertThat(resultSetSpan.getName()).isEqualTo("result-set"); - assertThat(statementSpan.getTags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME, "SELECT NOW()"); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldNotFailWhenResultSetNextWasNotCalled() { - parentContextRunner().run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection = dataSource.getConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SELECT NOW()"); - resultSet.next(); - resultSet.close(); - statement.close(); - connection.close(); - - assertThat(spanReporter.reportedSpans()).hasSize(3); - FinishedSpan connectionSpan = spanReporter.reportedSpans().get(2); - FinishedSpan resultSetSpan = spanReporter.reportedSpans().get(1); - FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); - assertThat(connectionSpan.getName()).isEqualTo("connection"); - assertThat(statementSpan.getName()).isEqualTo("select"); - assertThat(resultSetSpan.getName()).isEqualTo("result-set"); - assertThat(statementSpan.getTags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME, "SELECT NOW()"); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldNotFailWhenResourceIsAlreadyClosed() { - parentContextRunner().run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection = dataSource.getConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SELECT NOW()"); - resultSet.next(); - resultSet.close(); - resultSet.close(); - statement.close(); - statement.close(); - connection.close(); - connection.close(); - - assertThat(spanReporter.reportedSpans()).hasSize(3); - FinishedSpan connectionSpan = spanReporter.reportedSpans().get(2); - FinishedSpan resultSetSpan = spanReporter.reportedSpans().get(1); - FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); - assertThat(connectionSpan.getName()).isEqualTo("connection"); - assertThat(statementSpan.getName()).isEqualTo("select"); - assertThat(resultSetSpan.getName()).isEqualTo("result-set"); - assertThat(statementSpan.getTags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME, "SELECT NOW()"); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldNotFailWhenResourceIsAlreadyClosed2() { - parentContextRunner().run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection = dataSource.getConnection(); - assertThatThrownBy(() -> { - connection.close(); - connection.prepareStatement("SELECT NOW()"); - }).isInstanceOf(SQLException.class); - - assertThat(spanReporter.reportedSpans()).hasSize(1); - FinishedSpan connectionSpan = spanReporter.reportedSpans().get(0); - assertThat(connectionSpan.getName()).isEqualTo("connection"); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldNotFailWhenResourceIsAlreadyClosed3() { - parentContextRunner().run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - - Connection connection = dataSource.getConnection(); - Statement statement = connection.createStatement(); - assertThatThrownBy(() -> { - statement.close(); - statement.executeQuery("SELECT NOW()"); - }).isInstanceOf(SQLException.class); - connection.close(); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldNotFailWhenResourceIsAlreadyClosed4() { - parentContextRunner().run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - - Connection connection = dataSource.getConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SELECT NOW()"); - assertThatThrownBy(() -> { - resultSet.close(); - resultSet.next(); - }).isInstanceOf(SQLException.class); - statement.close(); - connection.close(); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldNotFailToCloseSpanForTwoConsecutiveConnections() { - parentContextRunner().run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection1 = dataSource.getConnection(); - Connection connection2 = dataSource.getConnection(); - connection2.close(); - connection1.close(); - - assertThat(spanReporter.reportedSpans()).hasSize(2); - FinishedSpan connection1Span = spanReporter.reportedSpans().get(0); - FinishedSpan connection2Span = spanReporter.reportedSpans().get(1); - assertThat(connection1Span.getName()).isEqualTo("connection"); - assertThat(connection2Span.getName()).isEqualTo("connection"); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldNotFailWhenClosedInReversedOrder() { - parentContextRunner().run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection = dataSource.getConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SELECT NOW()"); - resultSet.next(); - connection.close(); - statement.close(); - resultSet.close(); - - assertThat(spanReporter.reportedSpans()).hasSize(3); - FinishedSpan connectionSpan = spanReporter.reportedSpans().get(2); - FinishedSpan resultSetSpan = spanReporter.reportedSpans().get(1); - FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); - assertThat(connectionSpan.getName()).isEqualTo("connection"); - assertThat(statementSpan.getName()).isEqualTo("select"); - assertThat(resultSetSpan.getName()).isEqualTo("result-set"); - assertThat(statementSpan.getTags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME, "SELECT NOW()"); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - @SuppressWarnings("unchecked") - void testShouldNotCauseMemoryLeakOnTomcatPool() { - parentContextRunner().withPropertyValues("spring.datasource.type:org.apache.tomcat.jdbc.pool.DataSource") - .run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - Object listener = isP6Spy(context) ? context.getBean(TraceJdbcEventListener.class) - : context.getBean(TraceQueryExecutionListener.class); - - Connection connection = dataSource.getConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("select 1 FROM dual"); - resultSet.next(); - resultSet.close(); - statement.close(); - connection.close(); - - assertThat(listener).extracting("strategy").extracting("openConnections") - .isInstanceOfSatisfying(Map.class, map -> assertThat(map).isEmpty()); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - private boolean isP6Spy(AssertableApplicationContext context) { - if (context.getBeansOfType(TraceJdbcEventListener.class).size() == 1) { - return true; - } - else if (context.getBeansOfType(TraceQueryExecutionListener.class).size() == 1) { - return false; - } - else { - throw new IllegalStateException("Expected exactly 1 tracing listener bean in the context."); - } - } - - @Test - void testSingleConnectionAcrossMultipleThreads() { - parentContextRunner().run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection = dataSource.getConnection(); - IntStream.range(0, 5).mapToObj(i -> CompletableFuture.runAsync(() -> { - try { - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SELECT NOW()"); - resultSet.next(); - statement.close(); - resultSet.close(); - } - catch (SQLException e) { - throw new IllegalStateException(e); - } - })).collect(Collectors.toList()).forEach(CompletableFuture::join); - connection.close(); - - assertThat(spanReporter.reportedSpans()).hasSize(1 + 2 * 5); - assertThat(spanReporter.reportedSpans()).extracting("name").contains("select", "result-set", "connection"); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldIncludeOnlyConnectionTraces() { - parentContextRunner().withPropertyValues("spring.sleuth.jdbc.includes: connection").run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection = dataSource.getConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("select 1 FROM dual"); - resultSet.next(); - resultSet.close(); - statement.close(); - connection.close(); - - assertThat(spanReporter.reportedSpans()).hasSize(1); - FinishedSpan connectionSpan = spanReporter.reportedSpans().get(0); - assertThat(connectionSpan.getName()).isEqualTo("connection"); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldIncludeOnlyQueryTraces() { - parentContextRunner().withPropertyValues("spring.sleuth.jdbc.includes: query").run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection = dataSource.getConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("select 1 FROM dual"); - resultSet.next(); - resultSet.close(); - statement.close(); - connection.close(); - - assertThat(spanReporter.reportedSpans()).hasSize(1); - FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); - assertThat(statementSpan.getName()).isEqualTo("select"); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldIncludeOnlyFetchTraces() { - parentContextRunner().withPropertyValues("spring.sleuth.jdbc.includes: fetch").run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection = dataSource.getConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("select 1 FROM dual"); - resultSet.next(); - resultSet.close(); - statement.close(); - connection.close(); - - assertThat(spanReporter.reportedSpans()).hasSize(1); - FinishedSpan resultSetSpan = spanReporter.reportedSpans().get(0); - assertThat(resultSetSpan.getName()).isEqualTo("result-set"); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldIncludeOnlyConnectionAndQueryTraces() { - parentContextRunner().withPropertyValues("spring.sleuth.jdbc.includes: connection, query").run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection = dataSource.getConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("select 1 FROM dual"); - resultSet.next(); - resultSet.close(); - statement.close(); - connection.close(); - - assertThat(spanReporter.reportedSpans()).hasSize(2); - FinishedSpan connectionSpan = spanReporter.reportedSpans().get(1); - FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); - assertThat(connectionSpan.getName()).isEqualTo("connection"); - assertThat(statementSpan.getName()).isEqualTo("select"); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldIncludeOnlyConnectionAndFetchTraces() { - parentContextRunner().withPropertyValues("spring.sleuth.jdbc.includes: connection, fetch").run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection = dataSource.getConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("select 1 FROM dual"); - resultSet.next(); - resultSet.close(); - statement.close(); - connection.close(); - - assertThat(spanReporter.reportedSpans()).hasSize(2); - FinishedSpan connectionSpan = spanReporter.reportedSpans().get(1); - FinishedSpan resultSetSpan = spanReporter.reportedSpans().get(0); - assertThat(connectionSpan.getName()).isEqualTo("connection"); - assertThat(resultSetSpan.getName()).isEqualTo("result-set"); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldIncludeOnlyQueryAndFetchTraces() { - parentContextRunner().withPropertyValues("spring.sleuth.jdbc.includes: query, fetch").run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection = dataSource.getConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("select 1 FROM dual"); - resultSet.next(); - resultSet.close(); - statement.close(); - connection.close(); - - assertThat(spanReporter.reportedSpans()).hasSize(2); - FinishedSpan resultSetSpan = spanReporter.reportedSpans().get(1); - FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); - assertThat(statementSpan.getName()).isEqualTo("select"); - assertThat(resultSetSpan.getName()).isEqualTo("result-set"); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldNotOverrideExceptionWhenConnectionWasClosedBeforeExecutingQuery() { - parentContextRunner().run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection = dataSource.getConnection(); - PreparedStatement statement = connection.prepareStatement("SELECT NOW()"); - connection.close(); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - - assertThatThrownBy(statement::executeQuery).isInstanceOf(SQLException.class); - - assertThat(spanReporter.reportedSpans()).hasSize(1); - FinishedSpan connectionSpan = spanReporter.reportedSpans().get(0); - assertThat(connectionSpan.getName()).isEqualTo("connection"); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldNotOverrideExceptionWhenStatementWasClosedBeforeExecutingQuery() { - parentContextRunner().run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection = dataSource.getConnection(); - PreparedStatement statement = connection.prepareStatement("SELECT NOW()"); - statement.close(); - assertThatThrownBy(statement::executeQuery).isInstanceOf(SQLException.class); - connection.close(); - - assertThat(spanReporter.reportedSpans()).hasSize(2); - FinishedSpan connectionSpan = spanReporter.reportedSpans().get(1); - FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); - assertThat(connectionSpan.getName()).isEqualTo("connection"); - assertThat(statementSpan.getName()).isEqualTo("select"); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldNotOverrideExceptionWhenResultSetWasClosedBeforeNext() { - parentContextRunner().run(context -> { - DataSource dataSource = context.getBean(DataSource.class); - TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); - - Connection connection = dataSource.getConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SELECT NOW()"); - resultSet.close(); - assertThatThrownBy(resultSet::next).isInstanceOf(SQLException.class); - statement.close(); - connection.close(); - - assertThat(spanReporter.reportedSpans()).hasSize(3); - FinishedSpan connectionSpan = spanReporter.reportedSpans().get(2); - FinishedSpan resultSetSpan = spanReporter.reportedSpans().get(1); - FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); - assertThat(connectionSpan.getName()).isEqualTo("connection"); - assertThat(statementSpan.getName()).isEqualTo("select"); - assertThat(resultSetSpan.getName()).isEqualTo("result-set"); - assertThat(statementSpan.getTags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME, "SELECT NOW()"); - assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); - }); - } - - @Test - void testShouldNotFailWhenClosingConnectionFromDifferentDataSource() { - ApplicationContextRunner contextRunner = parentContextRunner() - .withUserConfiguration(MultiDataSourceConfiguration.class); - - contextRunner.run(context -> { - DataSource dataSource1 = context.getBean("test1", DataSource.class); - DataSource dataSource2 = context.getBean("test2", DataSource.class); - - dataSource1.getConnection().close(); - dataSource2.getConnection().close(); - - CompletableFuture future = CompletableFuture.runAsync(() -> { - try { - Connection connection1 = dataSource1.getConnection(); - PreparedStatement statement = connection1.prepareStatement("SELECT NOW()"); - ResultSet resultSet = statement.executeQuery(); - Thread.sleep(200); - resultSet.close(); - statement.close(); - connection1.close(); - } - catch (SQLException | InterruptedException e) { - throw new IllegalStateException(e); - } - }); - Thread.sleep(100); - Connection connection2 = dataSource2.getConnection(); - Thread.sleep(300); - connection2.close(); - - future.join(); - }); - } - - private static class MultiDataSourceConfiguration { - - @Bean - public HikariDataSource test1() { - HikariDataSource dataSource = new HikariDataSource(); - dataSource.setJdbcUrl("jdbc:h2:mem:testdb-1-foo"); - dataSource.setPoolName("test1"); - return dataSource; - } - - @Bean - public HikariDataSource test2() { - HikariDataSource dataSource = new HikariDataSource(); - dataSource.setJdbcUrl("jdbc:h2:mem:testdb-2-bar"); - dataSource.setPoolName("test2"); - return 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 java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import javax.sql.DataSource; + +import com.zaxxer.hikari.HikariDataSource; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.test.context.assertj.AssertableApplicationContext; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.exporter.FinishedSpan; +import org.springframework.cloud.sleuth.test.TestSpanHandler; +import org.springframework.context.annotation.Bean; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +abstract class TracingListenerStrategyTests { + + public static final String SPAN_SQL_QUERY_TAG_NAME = "jdbc.query"; + + public static final String SPAN_ROW_COUNT_TAG_NAME = "jdbc.row-count"; + + abstract ApplicationContextRunner parentContextRunner(); + + protected abstract Class autoConfiguration(); + + protected abstract Class testConfiguration(); + + @Test + void testShouldAddSpanForConnection() { + parentContextRunner().run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection = dataSource.getConnection(); + connection.commit(); + connection.rollback(); + connection.close(); + + assertThat(spanReporter.reportedSpans()).hasSize(1); + FinishedSpan connectionSpan = spanReporter.reportedSpans().get(0); + assertThat(connectionSpan.getName()).isEqualTo("connection"); + assertThat(connectionSpan.getRemoteServiceName()).isEqualTo("TESTDB-BAZ"); + assertThat(connectionSpan.getEvents()).extracting("value").contains("jdbc.commit"); + assertThat(connectionSpan.getEvents()).extracting("value").contains("jdbc.rollback"); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldAddSpanForConnectionWithFixedRemoteServiceName() { + parentContextRunner() + .withPropertyValues("spring.datasource.url:jdbc:h2:mem:testdb-baz?sleuthServiceName=aaaabbbb") + .run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection = dataSource.getConnection(); + connection.commit(); + connection.rollback(); + connection.close(); + + assertThat(spanReporter.reportedSpans()).hasSize(1); + FinishedSpan connectionSpan = spanReporter.reportedSpans().get(0); + assertThat(connectionSpan.getName()).isEqualTo("connection"); + assertThat(connectionSpan.getRemoteServiceName()).isEqualTo("aaaabbbb"); + assertThat(connectionSpan.getEvents()).extracting("value").contains("jdbc.commit"); + assertThat(connectionSpan.getEvents()).extracting("value").contains("jdbc.rollback"); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldAddSpanForPreparedStatementExecute() { + parentContextRunner().run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection = dataSource.getConnection(); + connection.prepareStatement("SELECT NOW()").execute(); + connection.close(); + + assertThat(spanReporter.reportedSpans()).hasSize(2); + FinishedSpan connectionSpan = spanReporter.reportedSpans().get(1); + FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); + assertThat(connectionSpan.getName()).isEqualTo("connection"); + assertThat(statementSpan.getName()).isEqualTo("select"); + assertThat(statementSpan.getRemoteServiceName()).isEqualTo("TESTDB-BAZ"); + assertThat(statementSpan.getTags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME, "SELECT NOW()"); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldAddSpanForPreparedStatementExecuteUpdate() { + parentContextRunner().run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection = dataSource.getConnection(); + connection.prepareStatement("UPDATE INFORMATION_SCHEMA.TABLES SET table_Name = '' WHERE 0 = 1") + .executeUpdate(); + connection.close(); + + assertThat(spanReporter.reportedSpans()).hasSize(2); + FinishedSpan connectionSpan = spanReporter.reportedSpans().get(1); + FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); + assertThat(connectionSpan.getName()).isEqualTo("connection"); + assertThat(statementSpan.getName()).isEqualTo("update"); + assertThat(statementSpan.getTags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME, + "UPDATE INFORMATION_SCHEMA.TABLES SET table_Name = '' WHERE 0 = 1"); + assertThat(statementSpan.getTags()).containsEntry(SPAN_ROW_COUNT_TAG_NAME, "0"); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldAddSpanForStatementExecuteUpdate() { + parentContextRunner().run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection = dataSource.getConnection(); + connection.createStatement() + .executeUpdate("UPDATE INFORMATION_SCHEMA.TABLES SET table_Name = '' WHERE 0 = 1"); + connection.close(); + + assertThat(spanReporter.reportedSpans()).hasSize(2); + FinishedSpan connectionSpan = spanReporter.reportedSpans().get(1); + FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); + assertThat(connectionSpan.getName()).isEqualTo("connection"); + assertThat(statementSpan.getName()).isEqualTo("update"); + assertThat(statementSpan.getTags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME, + "UPDATE INFORMATION_SCHEMA.TABLES SET table_Name = '' WHERE 0 = 1"); + assertThat(statementSpan.getTags()).containsEntry(SPAN_ROW_COUNT_TAG_NAME, "0"); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldAddSpanForPreparedStatementExecuteQueryIncludingTimeToCloseResultSet() { + parentContextRunner().run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection = dataSource.getConnection(); + ResultSet resultSet = connection.prepareStatement("SELECT NOW() UNION ALL select NOW()").executeQuery(); + resultSet.next(); + resultSet.next(); + resultSet.close(); + connection.close(); + + assertThat(spanReporter.reportedSpans()).hasSize(3); + FinishedSpan connectionSpan = spanReporter.reportedSpans().get(2); + FinishedSpan resultSetSpan = spanReporter.reportedSpans().get(1); + FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); + assertThat(connectionSpan.getName()).isEqualTo("connection"); + assertThat(statementSpan.getName()).isEqualTo("select"); + assertThat(statementSpan.getTags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME, + "SELECT NOW() UNION ALL select NOW()"); + assertThat(resultSetSpan.getName()).isEqualTo("result-set"); + assertThat(resultSetSpan.getRemoteServiceName()).isEqualTo("TESTDB-BAZ"); + if (isP6Spy(context)) { + assertThat(resultSetSpan.getTags()).containsEntry(SPAN_ROW_COUNT_TAG_NAME, "2"); + } + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldAddSpanForStatementAndResultSet() { + parentContextRunner().run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection = dataSource.getConnection(); + ResultSet resultSet = connection.createStatement().executeQuery("SELECT NOW()"); + resultSet.next(); + Thread.sleep(200L); + resultSet.close(); + connection.close(); + + assertThat(spanReporter.reportedSpans()).hasSize(3); + FinishedSpan connectionSpan = spanReporter.reportedSpans().get(2); + FinishedSpan resultSetSpan = spanReporter.reportedSpans().get(1); + FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); + assertThat(connectionSpan.getName()).isEqualTo("connection"); + assertThat(statementSpan.getName()).isEqualTo("select"); + assertThat(statementSpan.getTags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME, "SELECT NOW()"); + assertThat(resultSetSpan.getName()).isEqualTo("result-set"); + if (isP6Spy(context)) { + assertThat(resultSetSpan.getTags()).containsEntry(SPAN_ROW_COUNT_TAG_NAME, "1"); + } + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldNotFailWhenStatementIsClosedWihoutResultSet() { + parentContextRunner().run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection = dataSource.getConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT NOW()"); + resultSet.next(); + statement.close(); + connection.close(); + + assertThat(spanReporter.reportedSpans()).hasSize(3); + FinishedSpan connectionSpan = spanReporter.reportedSpans().get(2); + FinishedSpan resultSetSpan = spanReporter.reportedSpans().get(1); + FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); + assertThat(connectionSpan.getName()).isEqualTo("connection"); + assertThat(statementSpan.getName()).isEqualTo("select"); + assertThat(resultSetSpan.getName()).isEqualTo("result-set"); + assertThat(statementSpan.getTags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME, "SELECT NOW()"); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldNotFailWhenConnectionIsClosedWihoutResultSet() { + parentContextRunner().run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection = dataSource.getConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT NOW()"); + resultSet.next(); + connection.close(); + + assertThat(spanReporter.reportedSpans()).hasSize(3); + FinishedSpan connectionSpan = spanReporter.reportedSpans().get(2); + FinishedSpan resultSetSpan = spanReporter.reportedSpans().get(1); + FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); + assertThat(connectionSpan.getName()).isEqualTo("connection"); + assertThat(statementSpan.getName()).isEqualTo("select"); + assertThat(resultSetSpan.getName()).isEqualTo("result-set"); + assertThat(statementSpan.getTags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME, "SELECT NOW()"); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldNotFailWhenResultSetNextWasNotCalled() { + parentContextRunner().run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection = dataSource.getConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT NOW()"); + resultSet.next(); + resultSet.close(); + statement.close(); + connection.close(); + + assertThat(spanReporter.reportedSpans()).hasSize(3); + FinishedSpan connectionSpan = spanReporter.reportedSpans().get(2); + FinishedSpan resultSetSpan = spanReporter.reportedSpans().get(1); + FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); + assertThat(connectionSpan.getName()).isEqualTo("connection"); + assertThat(statementSpan.getName()).isEqualTo("select"); + assertThat(resultSetSpan.getName()).isEqualTo("result-set"); + assertThat(statementSpan.getTags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME, "SELECT NOW()"); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldNotFailWhenResourceIsAlreadyClosed() { + parentContextRunner().run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection = dataSource.getConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT NOW()"); + resultSet.next(); + resultSet.close(); + resultSet.close(); + statement.close(); + statement.close(); + connection.close(); + connection.close(); + + assertThat(spanReporter.reportedSpans()).hasSize(3); + FinishedSpan connectionSpan = spanReporter.reportedSpans().get(2); + FinishedSpan resultSetSpan = spanReporter.reportedSpans().get(1); + FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); + assertThat(connectionSpan.getName()).isEqualTo("connection"); + assertThat(statementSpan.getName()).isEqualTo("select"); + assertThat(resultSetSpan.getName()).isEqualTo("result-set"); + assertThat(statementSpan.getTags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME, "SELECT NOW()"); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldNotFailWhenResourceIsAlreadyClosed2() { + parentContextRunner().run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection = dataSource.getConnection(); + assertThatThrownBy(() -> { + connection.close(); + connection.prepareStatement("SELECT NOW()"); + }).isInstanceOf(SQLException.class); + + assertThat(spanReporter.reportedSpans()).hasSize(1); + FinishedSpan connectionSpan = spanReporter.reportedSpans().get(0); + assertThat(connectionSpan.getName()).isEqualTo("connection"); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldNotFailWhenResourceIsAlreadyClosed3() { + parentContextRunner().run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + + Connection connection = dataSource.getConnection(); + Statement statement = connection.createStatement(); + assertThatThrownBy(() -> { + statement.close(); + statement.executeQuery("SELECT NOW()"); + }).isInstanceOf(SQLException.class); + connection.close(); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldNotFailWhenResourceIsAlreadyClosed4() { + parentContextRunner().run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + + Connection connection = dataSource.getConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT NOW()"); + assertThatThrownBy(() -> { + resultSet.close(); + resultSet.next(); + }).isInstanceOf(SQLException.class); + statement.close(); + connection.close(); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldNotFailToCloseSpanForTwoConsecutiveConnections() { + parentContextRunner().run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection1 = dataSource.getConnection(); + Connection connection2 = dataSource.getConnection(); + connection2.close(); + connection1.close(); + + assertThat(spanReporter.reportedSpans()).hasSize(2); + FinishedSpan connection1Span = spanReporter.reportedSpans().get(0); + FinishedSpan connection2Span = spanReporter.reportedSpans().get(1); + assertThat(connection1Span.getName()).isEqualTo("connection"); + assertThat(connection2Span.getName()).isEqualTo("connection"); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldNotFailWhenClosedInReversedOrder() { + parentContextRunner().run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection = dataSource.getConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT NOW()"); + resultSet.next(); + connection.close(); + statement.close(); + resultSet.close(); + + assertThat(spanReporter.reportedSpans()).hasSize(3); + FinishedSpan connectionSpan = spanReporter.reportedSpans().get(2); + FinishedSpan resultSetSpan = spanReporter.reportedSpans().get(1); + FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); + assertThat(connectionSpan.getName()).isEqualTo("connection"); + assertThat(statementSpan.getName()).isEqualTo("select"); + assertThat(resultSetSpan.getName()).isEqualTo("result-set"); + assertThat(statementSpan.getTags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME, "SELECT NOW()"); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + @SuppressWarnings("unchecked") + void testShouldNotCauseMemoryLeakOnTomcatPool() { + parentContextRunner().withPropertyValues("spring.datasource.type:org.apache.tomcat.jdbc.pool.DataSource") + .run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + Object listener = isP6Spy(context) ? context.getBean(TraceJdbcEventListener.class) + : context.getBean(TraceQueryExecutionListener.class); + + Connection connection = dataSource.getConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("select 1 FROM dual"); + resultSet.next(); + resultSet.close(); + statement.close(); + connection.close(); + + assertThat(listener).extracting("strategy").extracting("openConnections") + .isInstanceOfSatisfying(Map.class, map -> assertThat(map).isEmpty()); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + private boolean isP6Spy(AssertableApplicationContext context) { + if (context.getBeansOfType(TraceJdbcEventListener.class).size() == 1) { + return true; + } + else if (context.getBeansOfType(TraceQueryExecutionListener.class).size() == 1) { + return false; + } + else { + throw new IllegalStateException("Expected exactly 1 tracing listener bean in the context."); + } + } + + @Test + void testSingleConnectionAcrossMultipleThreads() { + parentContextRunner().run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection = dataSource.getConnection(); + IntStream.range(0, 5).mapToObj(i -> CompletableFuture.runAsync(() -> { + try { + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT NOW()"); + resultSet.next(); + statement.close(); + resultSet.close(); + } + catch (SQLException e) { + throw new IllegalStateException(e); + } + })).collect(Collectors.toList()).forEach(CompletableFuture::join); + connection.close(); + + assertThat(spanReporter.reportedSpans()).hasSize(1 + 2 * 5); + assertThat(spanReporter.reportedSpans()).extracting("name").contains("select", "result-set", "connection"); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldIncludeOnlyConnectionTraces() { + parentContextRunner().withPropertyValues("spring.sleuth.jdbc.includes: connection").run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection = dataSource.getConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("select 1 FROM dual"); + resultSet.next(); + resultSet.close(); + statement.close(); + connection.close(); + + assertThat(spanReporter.reportedSpans()).hasSize(1); + FinishedSpan connectionSpan = spanReporter.reportedSpans().get(0); + assertThat(connectionSpan.getName()).isEqualTo("connection"); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldIncludeOnlyQueryTraces() { + parentContextRunner().withPropertyValues("spring.sleuth.jdbc.includes: query").run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection = dataSource.getConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("select 1 FROM dual"); + resultSet.next(); + resultSet.close(); + statement.close(); + connection.close(); + + assertThat(spanReporter.reportedSpans()).hasSize(1); + FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); + assertThat(statementSpan.getName()).isEqualTo("select"); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldIncludeOnlyFetchTraces() { + parentContextRunner().withPropertyValues("spring.sleuth.jdbc.includes: fetch").run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection = dataSource.getConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("select 1 FROM dual"); + resultSet.next(); + resultSet.close(); + statement.close(); + connection.close(); + + assertThat(spanReporter.reportedSpans()).hasSize(1); + FinishedSpan resultSetSpan = spanReporter.reportedSpans().get(0); + assertThat(resultSetSpan.getName()).isEqualTo("result-set"); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldIncludeOnlyConnectionAndQueryTraces() { + parentContextRunner().withPropertyValues("spring.sleuth.jdbc.includes: connection, query").run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection = dataSource.getConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("select 1 FROM dual"); + resultSet.next(); + resultSet.close(); + statement.close(); + connection.close(); + + assertThat(spanReporter.reportedSpans()).hasSize(2); + FinishedSpan connectionSpan = spanReporter.reportedSpans().get(1); + FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); + assertThat(connectionSpan.getName()).isEqualTo("connection"); + assertThat(statementSpan.getName()).isEqualTo("select"); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldIncludeOnlyConnectionAndFetchTraces() { + parentContextRunner().withPropertyValues("spring.sleuth.jdbc.includes: connection, fetch").run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection = dataSource.getConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("select 1 FROM dual"); + resultSet.next(); + resultSet.close(); + statement.close(); + connection.close(); + + assertThat(spanReporter.reportedSpans()).hasSize(2); + FinishedSpan connectionSpan = spanReporter.reportedSpans().get(1); + FinishedSpan resultSetSpan = spanReporter.reportedSpans().get(0); + assertThat(connectionSpan.getName()).isEqualTo("connection"); + assertThat(resultSetSpan.getName()).isEqualTo("result-set"); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldIncludeOnlyQueryAndFetchTraces() { + parentContextRunner().withPropertyValues("spring.sleuth.jdbc.includes: query, fetch").run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection = dataSource.getConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("select 1 FROM dual"); + resultSet.next(); + resultSet.close(); + statement.close(); + connection.close(); + + assertThat(spanReporter.reportedSpans()).hasSize(2); + FinishedSpan resultSetSpan = spanReporter.reportedSpans().get(1); + FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); + assertThat(statementSpan.getName()).isEqualTo("select"); + assertThat(resultSetSpan.getName()).isEqualTo("result-set"); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldNotOverrideExceptionWhenConnectionWasClosedBeforeExecutingQuery() { + parentContextRunner().run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection = dataSource.getConnection(); + PreparedStatement statement = connection.prepareStatement("SELECT NOW()"); + connection.close(); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + + assertThatThrownBy(statement::executeQuery).isInstanceOf(SQLException.class); + + assertThat(spanReporter.reportedSpans()).hasSize(1); + FinishedSpan connectionSpan = spanReporter.reportedSpans().get(0); + assertThat(connectionSpan.getName()).isEqualTo("connection"); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldNotOverrideExceptionWhenStatementWasClosedBeforeExecutingQuery() { + parentContextRunner().run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection = dataSource.getConnection(); + PreparedStatement statement = connection.prepareStatement("SELECT NOW()"); + statement.close(); + assertThatThrownBy(statement::executeQuery).isInstanceOf(SQLException.class); + connection.close(); + + assertThat(spanReporter.reportedSpans()).hasSize(2); + FinishedSpan connectionSpan = spanReporter.reportedSpans().get(1); + FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); + assertThat(connectionSpan.getName()).isEqualTo("connection"); + assertThat(statementSpan.getName()).isEqualTo("select"); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldNotOverrideExceptionWhenResultSetWasClosedBeforeNext() { + parentContextRunner().run(context -> { + DataSource dataSource = context.getBean(DataSource.class); + TestSpanHandler spanReporter = context.getBean(TestSpanHandler.class); + + Connection connection = dataSource.getConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT NOW()"); + resultSet.close(); + assertThatThrownBy(resultSet::next).isInstanceOf(SQLException.class); + statement.close(); + connection.close(); + + assertThat(spanReporter.reportedSpans()).hasSize(3); + FinishedSpan connectionSpan = spanReporter.reportedSpans().get(2); + FinishedSpan resultSetSpan = spanReporter.reportedSpans().get(1); + FinishedSpan statementSpan = spanReporter.reportedSpans().get(0); + assertThat(connectionSpan.getName()).isEqualTo("connection"); + assertThat(statementSpan.getName()).isEqualTo("select"); + assertThat(resultSetSpan.getName()).isEqualTo("result-set"); + assertThat(statementSpan.getTags()).containsEntry(SPAN_SQL_QUERY_TAG_NAME, "SELECT NOW()"); + assertThat(context.getBean(Tracer.class).currentSpan()).isNull(); + }); + } + + @Test + void testShouldNotFailWhenClosingConnectionFromDifferentDataSource() { + ApplicationContextRunner contextRunner = parentContextRunner() + .withUserConfiguration(MultiDataSourceConfiguration.class); + + contextRunner.run(context -> { + DataSource dataSource1 = context.getBean("test1", DataSource.class); + DataSource dataSource2 = context.getBean("test2", DataSource.class); + + dataSource1.getConnection().close(); + dataSource2.getConnection().close(); + + CompletableFuture future = CompletableFuture.runAsync(() -> { + try { + Connection connection1 = dataSource1.getConnection(); + PreparedStatement statement = connection1.prepareStatement("SELECT NOW()"); + ResultSet resultSet = statement.executeQuery(); + Thread.sleep(200); + resultSet.close(); + statement.close(); + connection1.close(); + } + catch (SQLException | InterruptedException e) { + throw new IllegalStateException(e); + } + }); + Thread.sleep(100); + Connection connection2 = dataSource2.getConnection(); + Thread.sleep(300); + connection2.close(); + + future.join(); + }); + } + + private static class MultiDataSourceConfiguration { + + @Bean + public HikariDataSource test1() { + HikariDataSource dataSource = new HikariDataSource(); + dataSource.setJdbcUrl("jdbc:h2:mem:testdb-1-foo"); + dataSource.setPoolName("test1"); + return dataSource; + } + + @Bean + public HikariDataSource test2() { + HikariDataSource dataSource = new HikariDataSource(); + dataSource.setJdbcUrl("jdbc:h2:mem:testdb-2-bar"); + dataSource.setPoolName("test2"); + return dataSource; + } + + } + +} diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/jdbc/TracingQueryExecutionListenerTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/jdbc/TracingQueryExecutionListenerTests.java index b54480927..6897f4293 100644 --- a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/jdbc/TracingQueryExecutionListenerTests.java +++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/jdbc/TracingQueryExecutionListenerTests.java @@ -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")); + } + +}