diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/ExecutorBeanPostProcessor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/ExecutorBeanPostProcessor.java index 3e7db1624..391374201 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/ExecutorBeanPostProcessor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/ExecutorBeanPostProcessor.java @@ -21,6 +21,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; @@ -80,6 +81,14 @@ class ExecutorBeanPostProcessor implements BeanPostProcessor { log.info("Not instrumenting bean " + beanName); } } + else if (bean instanceof ScheduledExecutorService && !alreadyTraced) { + if (isProxyNeeded(beanName)) { + return wrapScheduledExecutorService(bean); + } + else { + log.info("Not instrumenting bean " + beanName); + } + } else if (bean instanceof ExecutorService && !alreadyTraced) { if (isProxyNeeded(beanName)) { return wrapExecutorService(bean); @@ -104,6 +113,7 @@ class ExecutorBeanPostProcessor implements BeanPostProcessor { private boolean alreadyTraced(Object bean) { return bean instanceof LazyTraceThreadPoolTaskExecutor + || bean instanceof TraceableScheduledExecutorService || bean instanceof TraceableExecutorService || bean instanceof LazyTraceAsyncTaskExecutor || bean instanceof LazyTraceExecutor; @@ -148,6 +158,14 @@ class ExecutorBeanPostProcessor implements BeanPostProcessor { return createExecutorServiceProxy(bean, cglibProxy, executor); } + private Object wrapScheduledExecutorService(Object bean) { + ScheduledExecutorService executor = (ScheduledExecutorService) bean; + boolean classFinal = Modifier.isFinal(bean.getClass().getModifiers()); + boolean methodFinal = anyFinalMethods(executor, ExecutorService.class); + boolean cglibProxy = !classFinal && !methodFinal; + return createScheduledExecutorServiceProxy(bean, cglibProxy, executor); + } + private Object wrapAsyncTaskExecutor(Object bean) { AsyncTaskExecutor executor = (AsyncTaskExecutor) bean; boolean classFinal = Modifier.isFinal(bean.getClass().getModifiers()); @@ -188,6 +206,12 @@ class ExecutorBeanPostProcessor implements BeanPostProcessor { () -> new TraceableExecutorService(this.beanFactory, executor)); } + Object createScheduledExecutorServiceProxy(Object bean, boolean cglibProxy, + ScheduledExecutorService executor) { + return getProxiedObject(bean, cglibProxy, executor, + () -> new TraceableScheduledExecutorService(this.beanFactory, executor)); + } + Object createAsyncTaskExecutorProxy(Object bean, boolean cglibProxy, AsyncTaskExecutor executor) { return getProxiedObject(bean, cglibProxy, executor, () -> { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java index 3028c6f90..8a004930c 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java @@ -160,7 +160,7 @@ public class TraceWebClientAutoConfiguration { static class NettyConfiguration { @Bean - public HttpClientBeanPostProcessor httpClientBeanPostProcessor( + static HttpClientBeanPostProcessor httpClientBeanPostProcessor( BeanFactory beanFactory) { return new HttpClientBeanPostProcessor(beanFactory); } @@ -173,14 +173,14 @@ public class TraceWebClientAutoConfiguration { protected static class TraceOAuthConfiguration { @Bean - UserInfoRestTemplateCustomizerBPP userInfoRestTemplateCustomizerBeanPostProcessor( + static UserInfoRestTemplateCustomizerBPP userInfoRestTemplateCustomizerBeanPostProcessor( BeanFactory beanFactory) { return new UserInfoRestTemplateCustomizerBPP(beanFactory); } @Bean @ConditionalOnMissingBean - UserInfoRestTemplateCustomizer traceUserInfoRestTemplateCustomizer( + static UserInfoRestTemplateCustomizer traceUserInfoRestTemplateCustomizer( BeanFactory beanFactory) { return new TraceUserInfoRestTemplateCustomizer(beanFactory); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/AsyncDefaultAutoConfigurationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/AsyncDefaultAutoConfigurationTests.java new file mode 100644 index 000000000..0177d66a4 --- /dev/null +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/AsyncDefaultAutoConfigurationTests.java @@ -0,0 +1,56 @@ +/* + * Copyright 2013-2020 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.async; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +import org.assertj.core.api.BDDAssertions; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = AsyncDefaultAutoConfigurationTests.Config.class) +public class AsyncDefaultAutoConfigurationTests { + + @Autowired + ScheduledExecutorService executor; + + @Test + public void should_work_with_proxies() { + BDDAssertions.then(this.executor).isNotNull(); + } + + @Configuration + @EnableAutoConfiguration + static class Config { + + @Bean + public ScheduledExecutorService createExecutorService() { + return Executors.newSingleThreadScheduledExecutor(); + } + + } + +}