spring-cloud-sleuth
spring-cloud-sleuth copied to clipboard
TraceAsyncCustomAutoConfiguration configures a BeanPostProcessor that changes the type of AsyncConfigurer beans and breaks dependency resolution
Describe the bug
Please see https://github.com/spring-projects/spring-boot/issues/29151 for details.
Sample
This zipped Maven project will reproduce the problem using Sleuth 3.0.5 without really involving Boot. Running the main method will fail:
12:12:49.395 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@2401f4c3
12:12:49.414 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
12:12:49.536 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
12:12:49.538 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
12:12:49.539 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
12:12:49.540 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
12:12:49.543 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'asyncBeanPostProcessor'
12:12:49.559 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'gh29151Application'
12:12:49.560 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'exampleConfigurer'
12:12:49.606 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@2401f4c3, started on Wed Jan 19 12:12:49 GMT 2022
Exception in thread "main" org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'exampleConfigurer' is expected to be of type 'com.example.demo.Gh29151Application$Example' but was actually of type 'org.springframework.cloud.sleuth.instrument.async.LazyTraceAsyncCustomizer'
at org.springframework.beans.factory.support.AbstractBeanFactory.adaptBeanInstance(AbstractBeanFactory.java:417)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:398)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:213)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1160)
at com.example.demo.Gh29151Application.main(Gh29151Application.java:14)
The failure does not occur without the TraceAsyncCustomAutoConfiguration bean post-processor.
Here is a workaround if you do not want to disable Sleuth for async (spring.sleuth.async.enabled=false). You have to exclude autoconfiguration TraceAsyncCustomAutoConfiguration and wrap your executor with LazyTraceExecutor:
spring:
autoconfigure:
exclude:
- org.springframework.cloud.sleuth.autoconfig.instrument.async.TraceAsyncCustomAutoConfiguration
@EnableAsync
@Configuration
public class AsyncConfig implements AsyncConfigurer {
private final BeanFactory beanFactory;
public AsyncConfig(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public Executor getAsyncExecutor() {
Executor executor = new ThreadPoolTaskExecutor(); // create or configure your executor
return new LazyTraceExecutor(beanFactory, executor);
}
}
This is blocked by https://github.com/spring-projects/spring-framework/pull/27953
Another workaround you can do without modifying any other settings is this.
before
@Configuration
@EnableAsync
class AsyncConfiguration : AsyncConfigurer {
override fun getAsyncExecutor(): Executor {
val executor = ThreadPoolTaskExecutor()
executor.initialize()
return executor
}
override fun getAsyncUncaughtExceptionHandler(): AsyncUncaughtExceptionHandler {
return YourCustomAsyncUncaughtExceptionHandler()
}
}
after
@Configuration
@EnableAsync
class AsyncConfiguration {
@Bean
fun asyncConfigurer(): AsyncConfigurer {
return object : AsyncConfigurer {
override fun getAsyncExecutor(): Executor {
val executor = ThreadPoolTaskExecutor()
executor.initialize()
return executor
}
override fun getAsyncUncaughtExceptionHandler(): AsyncUncaughtExceptionHandler {
return YourCustomAsyncUncaughtExceptionHandler()
}
}
}
}
Do we have an fix for this issue. We are still seeing the error with Spring boot 2.6.8 and 3.1.0 version of sleuth.
May I ask for any progresses or any better solutions?
Adding the following configuration into the application.properties and keeping the @Bean annotation above the override method getAsyncExecutor() can be a solution. spring.sleuth.async.enabled=false
My spring version is spring-boot:2.6.7 and spirng-cloud:3.1.x
keeping the @bean annotation above the override method getAsyncExecutor() can be a solution. spring.sleuth.async.enabled=false
this is not working for me
@Saljack @lette1394 The following solutions mentioned are not working for me. Here is the sample reproducible code https://github.com/maradanasai/otel-async-exper.git . Using spring boot 2.7.14 and spring cloud 2021.0.8 version with jdk 17. It is blocking migrating to latest versions.
There's a PR open in Framework that should be fixing this (https://github.com/spring-projects/spring-framework/pull/27953) you can write a comment there that you're being blocked because of this
Since Spring Framework won't merge this PR. The only thing we can do is to document that in cases of exceptions you must
- disable sleuth async
- manually instrument the executors using their trace representations
example:
spring.sleuth.async.enabled=false
and configuration example
@Configuration
@EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {
private final BeanFactory beanFactory;
public AsyncConfiguration(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
@Bean("AsyncTaskExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.initialize();
return new LazyTraceThreadPoolTaskExecutor(beanFactory, executor);
}
}
I will update the documentation