Git Product home page Git Product logo

java-spring-web's Introduction

Build Status Released Version

OpenTracing Spring Web Instrumentation

This library provides instrumentation for Spring Web applications (Boot, MVC and WebFlux). It creates tracing data for server requests and also client requests (RestTemplate, AsyncRestTemplate and WebClient).

Comparison with opentracing-spring-cloud

As it was mentioned above, this library traces only inbound/outbound HTTP requests. If you would like to get automatically traced different set of technologies e.g. spring-cloud-netflix, JMS or even more then use project opentracing-spring-cloud instead.

For reactive applications, it is especially recommended to use reactor tracing from opentracing-spring-cloud, as that will ensure that the Span is activated in reactor handler functions. (Without that, one would have to extract the Span from the subscriber context.)

How does the server tracing work?

Servlet

Server span is started in Web Servlet Filter, then tracing interceptor adds spring related tags and logs. There are use case when spring boot invokes a handler after a request processing in filter finished, in this case interceptor starts a new span as followsFrom which references the initial span created in the servlet filter.

Reactive

Server span is started in TracingWebFilter (upon subscription), then onNext(), onError(), etc. handlers add Spring WebFlux related tags and logs.

Library versions

Versions 1.x.y, 2.x.y, ... of the library are meant to target Spring Framework 5.x and Spring Boot 2.x while versions 0.x.y are meant to be used with Spring Framework 4.3 and Spring Boot 1.5

Configuration

Spring Boot Auto-configuration

If you are using Spring Boot the easiest way how to configure OpenTracing instrumentation is to use auto-configuration:

<dependency>
  <groupId>io.opentracing.contrib</groupId>
  <artifactId>opentracing-spring-web-starter</artifactId>
</dependency>

Just provide an OpenTracing tracer bean and all required configuration is automatically done for you. It also instruments all RestTemplate, AsyncRestTemplate, WebClient and WebClient.Builder beans.

Manual configuration

Servlet and MVC Server

Configuration needs to add TracingFilter and TracingHandlerInterceptor. Both of these classes are required!

Tracing interceptor can be instantiated manually or injected via CDI, but it needs bean of type Tracer configured.

Java based configuration:

@Configuration
@Import({TracingHandlerInterceptor.class})
public class MVCConfiguration extends WebMvcConfigurerAdapter {

    @Autowired
    private Tracer tracer;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TracingHandlerInterceptor(tracer));
    }

    @Bean
    public FilterRegistrationBean tracingFilter() {
        TracingFilter tracingFilter = new TracingFilter(tracer);

        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(tracingFilter);
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.setOrder(Integer.MIN_VALUE);
        filterRegistrationBean.setAsyncSupported(true);

        return filterRegistrationBean;
    }
}

XML based configuration can be used too. Filter can be also directly defined in web.xml.

Reactive Server

Configuration needs to add the TracingWebFilter bean.

@Configuration
class TracingConfiguration {
    @Bean
    public TracingWebFilter tracingWebFilter(Tracer tracer) {
        return new TracingWebFilter(
                tracer,
                Integer.MIN_VALUE,               // Order
                Pattern.compile(""),             // Skip pattern
                Collections.emptyList(),         // URL patterns, empty list means all
                Arrays.asList(new WebFluxSpanDecorator.StandardTags(), new WebFluxSpanDecorator.WebFluxTags())
        );
    }
}

Client

RestTemplate restTemplate = new RestTemplate();
restTemplate.setInterceptors(Collections.singletonList(new TracingRestTemplateInterceptor(tracer)));

// the same applies for AsyncRestTemplate 

Reactive Client

WebClient webClient = WebClient.builder()
        .filter(new TracingExchangeFilterFunction(tracer, Collections.singletonList(new WebClientSpanDecorator.StandardTags())))
        .build();

Access server span

@RequestMapping("/hello")
public String hello(HttpServletRequest request) {
    Span serverSpan = tracer.activeSpan();

    Span span = tracer.buildSpan("localSpan")
                      .asChildOf(serverSpan.context())
                      .start();
    try {
        // Traced work happens between start() and deactivate();
        return "Hello world!";
    } finally {
        span.finish();
    }
}

Development

./mvnw clean install

Release

Follow instructions in RELEASE

java-spring-web's People

Contributors

ahus1 avatar ask4gilles avatar bensigelman avatar csabakos avatar dependabot[bot] avatar eddumelendez avatar geoand avatar ledor473 avatar luramarchanjo avatar mdvorak avatar nhebrard avatar objectiser avatar pavolloffay avatar peterjurkovic avatar rbtcollins avatar serceman avatar sijuv avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

java-spring-web's Issues

Release for SB 2.x

We should probably also perform a release supporting SB 2.x (most likely SB 2.1.x).

@pavolloffay How would you like to organize the code for such a release? Would you like to have a separate branch for such a release?
Also, what would you like to do about the release versions?

Span finishes before webclient completes

Please take a look at https://github.com/winster/jaeger-trace-reactive

This is a spring boot application with opentracing. As per the doc, java-spring-cloud supports webflux and webclient. But the trace is finished before the reactive flow is completed when there is a webclient call.

Initial analysis shows that following method are invoked everytime webclient makes http request.

TracingExchangeFilterFunction::filter
TracingClientResponseMono::subscribe
TracingClientResponseSubscriber::onSubscribe

Also noted that SpanLogsAppender::append is called for every log with the same spanId.

It is observed that JaegerSpan::finish is called before missing logs are appended. But span is still not null.

I tried removing, @Async annotation in service method for which Jaeger shows only POST trace.

Is there a way to get the complete reactive flow in the trace?

Bug: Span has already been finished; will not be reported again

Hey team! We've recently stumbled on a bit of a contrived bug in this library when using it with Webflux.

We are subscribing to a Flux. This Flux makes requests to a paged API. Each element emitted by the Flux is a response (page).

In our situation, we'd like to perform a one-time action on the first page. We achieved this using take. When calling take(1) on the Flux, a "complete" event is fired for the first element emitted by the flux, which is captured in the TracingClientResponseSubscriber onComplete handler here. Shortly afterwards, however, a cancel event is fired for the Flux (this is the behavior of take). This cancel event is picked up in the TracingClientResponseSubscriber subscription here.

The end result is that the span is "finished" twice, and a warning is logged.

Expected Behavior

Span is only finished once

Actual Behavior

Spans are finished twice when using the take API- once during a complete event, and again when a cancel event is fired. When using the Flux.take API, a cancel event can follow a complete event.

The work around currently is to not use take() or anything that will emit a complete event followed by a cancel event.

Errors happening in the controller in Spring boot should not create follows-from span

This is related to this comment: #1 (comment) and this spring issue: https://jira.spring.io/browse/SPR-15265

Basically now in Spring Boot with embedded container the request which throws exception from the controller creates two spans, the timeline is following:

  1. span GET /url is started
  2. exception thrown inside controller
  3. span GET /url is finished
  4. dispatcher servlet forwards to /error, span GET /error is started as follows-from the GET /url span
  5. span GET /error is finished

I'd expect that there is a span which spans (duh!) the whole processing of the request - one that is started in 1) and finished in 5). Ideally the first span (GET /url) would not finish in 3) but in 5).

I did some debugging and it seems that the forward to /error happens in tomcat code - at the end of org.apache.catalina.core.StandardHostValve.throwable():

// A custom error-page has not been defined for the exception
// that was thrown during request processing. Check if an
// error-page for error code 500 was specified and if so,
// send that page back as the response.
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
// The response is an error
response.setError();

status(request, response);

It's similar in Jetty but in this case this behaviour is a part of spring code - the forward happens in org.eclipse.jetty.servlet.ServletHandler.doHandle():

if (!response.isCommitted())
{
    baseRequest.getResponse().getHttpFields().put(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE);
    if (th instanceof UnavailableException)
    {
        UnavailableException ue = (UnavailableException)th;
        if (ue.isPermanent())
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
        else
            response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
    }
    else
        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); // <----- HERE
}

Now - there are several cases where this behaviour is not present - e.g. if the response is committed (above code). But also (see org.springframework.web.servlet.DispatcherServlet.processDispatchResult):

  • if exception extends ModelAndViewDefiningException - it defines where to forward
  • if it can be resolved by any of registered HandlerExceptionResolver

The case of HandlerExceptionResolver is interesting because this the way to handle exceptions (http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-ann-exceptionhandler).

Registering such handler completely changes the order of span starts/stops to the expected one (A start, B start, B finish, A finish).
The forward to /error still happens (this time inside org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle) but before the original request is "completed". But only if the exception handler returned response text - if only response code is set then the forward won't happen.

I have hard time believing what's going on in this code...

But there's more. The above behaviour happens when I use @ResponseStatus:

@ResponseStatus(value = HttpStatus.I_AM_A_TEAPOT, reason = "Teapot failure")
@ExceptionHandler(Exception.class)
public void mapit() {}

But if instead I return ResponseEntity the forward is gone and there's only 1 span (see ServletInvocableHandlerMethod.setResponseStatus()):

@ExceptionHandler(Exception.class)
public ResponseEntity<String> mapit() {
    return ResponseEntity.status(418).body("rotfl");
}

So I guess this will be my new workaround.
Using @ResponseBody also works this way.

But now the exception is not really captured, it has to be extracted from request.getAttribute(DispatcherServlet.EXCEPTION_ATTRIBUTE).

TL,DR:
To avoid redirect to /error:

  • define @ExceptionHandler
  • don't use @ResponseStatus or make sure that reason is an empty string

See also: http://stackoverflow.com/questions/28902374/spring-boot-rest-service-exception-handling#answer-30193013 (I haven't checked if those properties work)

Rename opentracing-spring-web-handler-interceptor to something more appropriate

All the instrumentations are using org.springframework#spring-webmvc (spring web and http clients) and instrumentation artifactId is opentracing-spring-web-handler-interceptor and auto-configuration opentracing-spring-web-autoconfigure.

Should we change opentracing-spring-web-handler-interceptor to something like opentracing-spring-web/opentracing-spring-webmvc as it includes all instrumentations, both client and server side?

Unmatched dependencies between beans

ServerTracingAutoConfiguration has the following dependency:
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
but is also responsible for loading WebTracingProperties.
SkipPatternAutoConfiguration depends on WebTracingProperties being loaded, but is not conditional on Type.SERVLET. Thus, if you run a unittest using webEnvironment = SpringBootTest.WebEnvironment.NONE, Spring will try to instantiate SkipPatternAutoConfiguration, but it will fail since WebTracingProperties is not in the ApplicationContext.

I'm sorry that I don't have a suggested solution, but I don't have the full overview of what is actually required or not in a WebEnvironment.NONE situation.

A reproducer is available at https://github.com/scoof/opentracing-contrib-java-spring-web-reproducer/

Error when running @SpringBootTest with SpringBootTest.WebEnvironment.NONE

Running my project tests with

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)

results in

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of method defaultSkipPatternBean in io.opentracing.contrib.spring.web.starter.SkipPatternAutoConfiguration$DefaultSkipPatternConfig required a bean of type 'io.opentracing.contrib.spring.web.starter.WebTracingProperties' that could not be found.


Action:

Consider defining a bean of type 'io.opentracing.contrib.spring.web.starter.WebTracingProperties' in your configuration.

15:50:04.275 ERROR 30733 --- [           main] o.s.test.context.TestContextManager       : Caught exception while allowing TestExecutionListener [org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@130e116b] to prepare test instance [com.tui.destimo.microservice.contractedit.integrationtest.AddedValueOfferIT@3e881258]

java.lang.IllegalStateException: Failed to load ApplicationContext
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:125)
	at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:108)
	at org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener.postProcessFields(MockitoTestExecutionListener.java:95)
	at org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener.injectFields(MockitoTestExecutionListener.java:79)
	at org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener.prepareTestInstance(MockitoTestExecutionListener.java:54)
	at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:246)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'defaultSkipPatternBean' defined in class path resource [io/opentracing/contrib/spring/web/starter/SkipPatternAutoConfiguration$DefaultSkipPatternConfig.class]: Unsatisfied dependency expressed through method 'defaultSkipPatternBean' parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'io.opentracing.contrib.spring.web.starter.WebTracingProperties' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:769)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:509)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1321)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1160)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:845)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:743)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:390)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:312)
	at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:120)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:117)
	... 25 common frames omitted
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'io.opentracing.contrib.spring.web.starter.WebTracingProperties' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1658)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1217)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1171)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:857)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:760)
	... 43 common frames omitted

Disconnected from the target VM, address: '127.0.0.1:0', transport: 'socket'

Process finished with exit code 255

Using MOCK works fine.

Span object is null when passing RequestEntity<?> as a request parameter

I am using Webflux and Netty in my spring boot app. RequestEntity is the request parameter in my controller class. I am getting 500 'internal server error' for RequestEntity as a parameter. But this service is giving response status 200 for other inputs.

Controller class

@RestController
public class ReactiveController {

	@Autowired
	Tracer tracer;

	@GetMapping("/test")
	@ResponseBody
	public CompletableFuture<ResponseEntity<?>> test(RequestEntity<String> reqEntity,
			@RequestHeader Map<String, String> reqHeaders) {
		final CompletableFuture<ResponseEntity<?>> finalRspCompFuture = new CompletableFuture<>();
		Span span = tracer.activeSpan();
		span.setOperationName("www.google.com");
		ResponseEntity<?> rspEntity = new ResponseEntity<>(HttpStatus.OK);
		finalRspCompFuture.complete(rspEntity);
		return finalRspCompFuture;
	}
}	

Expected behavior

Http Status: 200(Ok)

Actual behavior

Http Status: 500(Internal server error)

Console log:

οΏ½[2m2021-07-22 09:36:27.083οΏ½[0;39m οΏ½[31mERRORοΏ½[0;39m οΏ½[35m25684οΏ½[0;39m οΏ½[2m---οΏ½[0;39m οΏ½[2m[ctor-http-nio-4]οΏ½[0;39m οΏ½[36ma.w.r.e.AbstractErrorWebExceptionHandlerοΏ½[0;39m οΏ½[2m:οΏ½[0;39m [de269c37-1]  500 Server Error for HTTP GET "/test"

java.lang.NullPointerException: Cannot invoke "io.opentracing.Span.setOperationName(String)" because "span" is null
	at com.example.reactiverestservice.controller.ReactiveController.test(ReactiveController.java:26) ~[main/:na]
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
	|_ checkpoint ? org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain]
	|_ checkpoint ? io.opentracing.contrib.spring.web.webfilter.TracingWebFilter [DefaultWebFilterChain]
	|_ checkpoint ? HTTP GET "/test" [ExceptionHandlingWebHandler]
Stack trace:
		at com.example.reactiverestservice.controller.ReactiveController.test(ReactiveController.java:26) ~[main/:na]
		at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
		at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78) ~[na:na]
		at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
		at java.base/java.lang.reflect.Method.invoke(Method.java:567) ~[na:na]
		at org.springframework.web.reactive.result.method.InvocableHandlerMethod.lambda$invoke$0(InvocableHandlerMethod.java:146) ~[spring-webflux-5.3.4.jar:5.3.4]
		at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:125) ~[reactor-core-3.4.3.jar:3.4.3]
		at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1815) ~[reactor-core-3.4.3.jar:3.4.3]
		at reactor.core.publisher.MonoZip$ZipCoordinator.signal(MonoZip.java:251) ~[reactor-core-3.4.3.jar:3.4.3]
		at reactor.core.publisher.MonoZip$ZipInner.onNext(MonoZip.java:336) ~[reactor-core-3.4.3.jar:3.4.3]
		at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:180) ~[reactor-core-3.4.3.jar:3.4.3]
		at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:100) ~[reactor-core-3.4.3.jar:3.4.3]
		at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1815) ~[reactor-core-3.4.3.jar:3.4.3]
		at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onComplete(FluxDefaultIfEmpty.java:108) ~[reactor-core-3.4.3.jar:3.4.3]
		at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142) ~[reactor-core-3.4.3.jar:3.4.3]
		at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onComplete(Operators.java:2057) ~[reactor-core-3.4.3.jar:3.4.3]
		at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onComplete(FluxMapFuseable.java:150) ~[reactor-core-3.4.3.jar:3.4.3]
		at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onComplete(FluxContextWrite.java:126) ~[reactor-core-3.4.3.jar:3.4.3]
		at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onComplete(FluxMapFuseable.java:344) ~[reactor-core-3.4.3.jar:3.4.3]
		at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onComplete(FluxFilterFuseable.java:391) ~[reactor-core-3.4.3.jar:3.4.3]
		at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1816) ~[reactor-core-3.4.3.jar:3.4.3]
		at reactor.core.publisher.MonoCollect$CollectSubscriber.onComplete(MonoCollect.java:159) ~[reactor-core-3.4.3.jar:3.4.3]
		at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142) ~[reactor-core-3.4.3.jar:3.4.3]
		at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:259) ~[reactor-core-3.4.3.jar:3.4.3]
		at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142) ~[reactor-core-3.4.3.jar:3.4.3]
		at reactor.netty.channel.FluxReceive.onInboundComplete(FluxReceive.java:389) ~[reactor-netty-core-1.0.4.jar:1.0.4]
		at reactor.netty.channel.ChannelOperations.onInboundComplete(ChannelOperations.java:396) ~[reactor-netty-core-1.0.4.jar:1.0.4]
		at reactor.netty.http.server.HttpServerOperations.onInboundNext(HttpServerOperations.java:555) ~[reactor-netty-http-1.0.4.jar:1.0.4]
		at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:94) ~[reactor-netty-core-1.0.4.jar:1.0.4]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.59.Final.jar:4.1.59.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.59.Final.jar:4.1.59.Final]
		at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.59.Final.jar:4.1.59.Final]
		at reactor.netty.http.server.HttpTrafficHandler.channelRead(HttpTrafficHandler.java:253) ~[reactor-netty-http-1.0.4.jar:1.0.4]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.59.Final.jar:4.1.59.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.59.Final.jar:4.1.59.Final]
		at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.59.Final.jar:4.1.59.Final]
		at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436) ~[netty-transport-4.1.59.Final.jar:4.1.59.Final]
		at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324) ~[netty-codec-4.1.59.Final.jar:4.1.59.Final]
		at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296) ~[netty-codec-4.1.59.Final.jar:4.1.59.Final]
		at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251) ~[netty-transport-4.1.59.Final.jar:4.1.59.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.59.Final.jar:4.1.59.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.59.Final.jar:4.1.59.Final]
		at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.59.Final.jar:4.1.59.Final]
		at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) ~[netty-transport-4.1.59.Final.jar:4.1.59.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.59.Final.jar:4.1.59.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.59.Final.jar:4.1.59.Final]
		at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) ~[netty-transport-4.1.59.Final.jar:4.1.59.Final]
		at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) ~[netty-transport-4.1.59.Final.jar:4.1.59.Final]
		at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:719) ~[netty-transport-4.1.59.Final.jar:4.1.59.Final]
		at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:655) ~[netty-transport-4.1.59.Final.jar:4.1.59.Final]
		at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:581) ~[netty-transport-4.1.59.Final.jar:4.1.59.Final]
		at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493) ~[netty-transport-4.1.59.Final.jar:4.1.59.Final]
		at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989) ~[netty-common-4.1.59.Final.jar:4.1.59.Final]
		at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.59.Final.jar:4.1.59.Final]
		at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.59.Final.jar:4.1.59.Final]
		at java.base/java.lang.Thread.run(Thread.java:831) ~[na:na]

Environment

SpringBootVersion: 2.4.3
java : 16

Source Code

reactive-rest-service.zip

TracingHandlerInterceptor closes the Scope, even if it hasn't been opened in preHandle()

I'm using a spring boot application that has several filters.

I observe the following behavior: going in with the request the scope was set by TracingFilter, processing the response the scope was already closed. Looking at TracingHandlerInterceptor it looks like it closes the scope too early after processing the request


Debuggig the situation I found that TracingHandlerInterceptor only sometimes opens a scope in preHandle()

Scope serverSpan = tracer.scopeManager().active();
if (serverSpan == null) {
        /* ... open scope ... */ 
}

but it will always close the scope in afterCompletion()

Deque<Scope> scopeStack = getScopeStack(httpServletRequest);
Scope scope = scopeStack.pop();
/* ... */
scope.close();

When you put a breakpoint in ThreadLocalScope you'll see that the following condition will be true when TracingFilter tries to close the scope again.

if (scopeManager.getTlsScope() != this) {
    // This shouldn't happen if users call methods in the expected order. Bail out.
    return;
}

I'm using opentracing-spring-jaeger-cloud-starter:0.2.1 that includes opentracing-web-servlet-filter:0.1.0 and opentracing-spring-web:0.3.3. This also happens with opentracing-web-servlet-filter:0.2.0

RestTemplate can't be autowired

With springBootVersion = '2.0.0.RELEASE'
I'm trying to use autoconfiguration, and using
@Autowired
RestTemplate restTemplate;

This fails with the following error:


APPLICATION FAILED TO START


Description:

Field restTemplate in oracle.igcs.tracer.controller.HelloController required a bean of type 'org.springframework.web.client.RestTemplate' that could not be found.

Action:

Consider defining a bean of type 'org.springframework.web.client.RestTemplate' in your configuration.

Run tests on Netty for WebFlux

WebFlux doesn't require jetty or tomcat.
It uses netty.
Is it possible to get rid of jetty from WebFlux integration tests?

upgrade to opentracing 0.33.0

Please upgrade the libs to use opentracing 0.33.0. This library currently breaks due to some incompatibilities between 0.32.0 and 0.33.0

`Scope` should be closed in the method of `afterConcurrentHandlingStarted` for async request

For async requests, thread-bound resources should be cleaned in method afterConcurrentHandlingStarted of TracingHandlerInterceptor interceptor as is suggested in the document AsyncHandlerInterceptor.

In this case, I suppose active scope should be closed since this specific thread will be released back to the dispatch pool and possibly used later for subsequent requests. If the scope is not closed properly, the following requests cannot be traced.

Simply adding the following code at the end of afterConcurrentHandlingStarted makes it work,

Deque<Scope> scopeStack = getScopeStack(httpServletRequest);
if (scopeStack.size() > 0) {
    Scope scope = scopeStack.pop();
    scope.close();
}

Span not finished when subscription is cancelled

Hello!

In the TracingOperator component is there any good reason the not finish the span when the wrapped source has been canceled?

I would change

class TracingOperator extends MonoOperator<Void, Void> {
    
    @Override
    public void subscribe(final CoreSubscriber<? super Void> subscriber) {
        // ...
        try (final Scope scope = tracer.scopeManager().activate(span)) {
            exchange.getAttributes().put(TracingWebFilter.SERVER_SPAN_CONTEXT, span.context());
            source.subscribe(new TracingSubscriber(subscriber, exchange, context, span, spanDecorators));
        }
    }

To

class TracingOperator extends MonoOperator<Void, Void> {
    
    @Override
    public void subscribe(final CoreSubscriber<? super Void> subscriber) {
        // ...
        try (final Scope scope = tracer.scopeManager().activate(span)) {
            exchange.getAttributes().put(TracingWebFilter.SERVER_SPAN_CONTEXT, span.context());
            source.doOnCancel(() -> span.finish())
               .subscribe(new TracingSubscriber(subscriber, exchange, context, span, spanDecorators));
        }
    }

Happy to contribute if needed!

Thanks.

TracingHandlerInterceptor order cannot be changed and even overridden

TracingHandlerInterceptor is created in that way there is no possibility of changing order of that interceptor like it's done for TracingFilter. (both beans are configured in ServerTracingAutoConfiguration)

IMO it should be registered using InterceptorRegistration in similar way to TracingFilter.
Also those configuration should be separated because if I want to register just filter then interceptor will be registered automatically. In my case for example I would want to be able to register TracingHandlerInterceptor by myself to change order, but I can't do that because if I turn opentracing.spring.web.enabled off then also TracingFilter wont be registered.

Bug: (java.lang.IllegalArgumentException) Service name must not be null or empty

In the Spring Boot project with version 2.3.7.RELEASE, when added the dependency below in pom.xml:

<dependency>
	<groupId>io.opentracing.contrib</groupId>
	<artifactId>opentracing-spring-jaeger-web-starter</artifactId>
	<version>3.3.1</version>
</dependency>

And configured the below properties in application.properties:

opentracing.jaeger.enabled=false
opentracing.jaeger.service-name=authorization-server
opentracing.jaeger.udp-sender.host=localhost
opentracing.jaeger.udp-sender.port=6831 

When running the application, It causes the below throwable in the console:

java.lang.IllegalArgumentException: Service name must not be null or empty
	at io.jaegertracing.internal.JaegerTracer$Builder.checkValidServiceName(JaegerTracer.java:712) ~[jaeger-core-1.3.2.jar:1.3.2]
	at io.jaegertracing.Configuration.<init>(Configuration.java:196) ~[jaeger-core-1.3.2.jar:1.3.2]
	at io.jaegertracing.Configuration.fromEnv(Configuration.java:208) ~[jaeger-core-1.3.2.jar:1.3.2]
	at io.jaegertracing.Configuration.fromEnv(Configuration.java:204) ~[jaeger-core-1.3.2.jar:1.3.2]
	at io.jaegertracing.tracerresolver.internal.JaegerTracerFactory.getTracer(JaegerTracerFactory.java:24) ~[jaeger-tracerresolver-1.3.2.jar:1.3.2]
	at io.jaegertracing.tracerresolver.internal.JaegerTracerFactory.getTracer(JaegerTracerFactory.java:21) ~[jaeger-tracerresolver-1.3.2.jar:1.3.2]
	at io.opentracing.contrib.tracerresolver.TracerResolver.getFromFactory(TracerResolver.java:190) [opentracing-tracerresolver-0.1.8.jar:na]
	at io.opentracing.contrib.tracerresolver.TracerResolver.resolveTracer(TracerResolver.java:123) [opentracing-tracerresolver-0.1.8.jar:na]
	at io.opentracing.contrib.tracerresolver.TracerResolver.resolveTracer(TracerResolver.java:79) [opentracing-tracerresolver-0.1.8.jar:na]
	at io.opentracing.contrib.spring.tracer.configuration.TracerAutoConfiguration.getTracer(TracerAutoConfiguration.java:53) [opentracing-spring-tracer-configuration-starter-0.4.0.jar:na]
	at io.opentracing.contrib.spring.tracer.configuration.TracerAutoConfiguration$$EnhancerBySpringCGLIB$$9eabe38e.CGLIB$getTracer$0(<generated>) [opentracing-spring-tracer-configuration-starter-0.4.0.jar:na]
	at io.opentracing.contrib.spring.tracer.configuration.TracerAutoConfiguration$$EnhancerBySpringCGLIB$$9eabe38e$$FastClassBySpringCGLIB$$6aa0e072.invoke(<generated>) [opentracing-spring-tracer-configuration-starter-0.4.0.jar:na]
	at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) [spring-core-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) [spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at io.opentracing.contrib.spring.tracer.configuration.TracerAutoConfiguration$$EnhancerBySpringCGLIB$$9eabe38e.getTracer(<generated>) [opentracing-spring-tracer-configuration-starter-0.4.0.jar:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_292]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_292]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_292]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_292]
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:652) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:485) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1176) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:556) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1307) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1227) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:886) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:790) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:540) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1176) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:556) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1525) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1489) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1378) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1265) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1227) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:714) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:409) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1176) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:556) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1307) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1227) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:714) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:409) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1176) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:556) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1307) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1227) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:886) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:790) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:228) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1356) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1203) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:556) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1525) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1489) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1378) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1265) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1227) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:409) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1176) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:556) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1307) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1227) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:886) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:790) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:228) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1356) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1203) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:556) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) [spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:897) ~[spring-beans-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:879) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:551) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:405) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.3.7.RELEASE.jar:2.3.7.RELEASE]
	at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:120) ~[spring-boot-test-2.3.7.RELEASE.jar:2.3.7.RELEASE]
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99) ~[spring-test-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124) ~[spring-test-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:123) ~[spring-test-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190) ~[spring-test-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132) ~[spring-test-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:244) ~[spring-test-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227) ~[spring-test-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289) ~[spring-test-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) ~[junit-4.13.1.jar:4.13.1]
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291) ~[spring-test-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246) ~[spring-test-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97) ~[spring-test-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331) ~[junit-4.13.1.jar:4.13.1]
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79) ~[junit-4.13.1.jar:4.13.1]
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329) ~[junit-4.13.1.jar:4.13.1]
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66) ~[junit-4.13.1.jar:4.13.1]
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293) ~[junit-4.13.1.jar:4.13.1]
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) ~[spring-test-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) ~[spring-test-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306) ~[junit-4.13.1.jar:4.13.1]
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413) ~[junit-4.13.1.jar:4.13.1]
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190) ~[spring-test-5.2.12.RELEASE.jar:5.2.12.RELEASE]
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137) ~[junit-4.13.1.jar:4.13.1]
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69) ~[junit-rt.jar:na]
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33) ~[junit-rt.jar:na]
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235) ~[junit-rt.jar:na]
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54) ~[junit-rt.jar:na]

Notice the property opentracing.jaeger.enabled is defined with false and the opentracing.jaeger.service-name property is populated.

If I enabling the jaeger this error does not occur, but I disable the jaeger this error is thrown in the console.

Spans are not connected after upgrading spring-web-starter

Dear Community,

Different services spans can not be connected after upgrade below old version

opentracing-spring-jaeger-starter:0.1.4
opentracing-spring-web-starter:0.3.3

to new version

opentracing-spring-jaeger-starter:2.0.0
opentracing-spring-web-starter:2.1.0

Before the upgrading, everything is ok.

For example, Service A call B and C, we inject the tracing context to the HTTP header when calling Service B and C. The sampler is ratelimiting 1. Span Calling Service B and Calling Service C are created in Service A and under the root span Service A. Span Service B is recorded in service B. Span Service C is recorded in service C.

Before upgrading, we got below three level tracing: one root span, two level 2 spans and two level 3 spans,

Service A
  Calling Service B
    Service B
  Calling Service C
    Service C

Even we changed the sampler to any, above tracing is working as expected.

After upgrading, we can only got 1 root span and two level 2 spans which all belong to service A:

Service A
  Calling Service B
  Calling Service C

But when we tried to change the sampler to const 1 in Service A. we got three individual tracing:
1:

Service A
  Calling Service B
  Calling Service C

2:

GET
  Service B

3:

POST
  Service C

in tracing 2 and 3, there has two unknown span: GET and POST. When looked at the span detail. I found the field Component has the value java-spring-webclient which is also not seen in old version. I am guessing the Spring Webclient created a root span solely. I didn't look at the http packet to see what exactly the header are, but from the result, I think Service B and Service C treat the span as the root span. Is our injected span got overwritten? and also sometimes the results related to the sampler?

I am not sure this is the changes to new version or other changes resulted in the weird behavior. Thanks for your information and opinion.

Should client interceptor use more information for generating operation name?

Currently client interceptor uses only method of request as an operation name for new spans:

ActiveSpan span = tracer.buildSpan(httpRequest.getMethod().toString())
                .withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).startActive();

Does it make sense to add a request path to operation name? Or maybe make an operation name generator to support customisation of that? Otherwise for many outgoing GET requests to different targets, spans appear with the same operation name GET.

Improve skip pattern

Skip pattern should include all static files and common health endpoints used in spring.

[Question] Example / how to use SpanDecorator?

It looks like use can supply their own span decorators with their tracer πŸ‘

public TracingHandlerInterceptor(Tracer tracer, List<HandlerInterceptorSpanDecorator> decorators) {

is there an example of how to do it and wire it up? It is not at all obvious how to do it.
Only got to the point of

``` and override onPreHandle and onAfterCompletion. but then what.

Clarify project status

On the README, it says

"Use opentracing-spring-cloud instead"

what does it really mean?

Is it indicating this project is inactive; so, don't use java-spring-web and move to opentracing-spring-cloud??

It is very confusing.

Appreciate clarifying the intention there.

Spring MVC integration example

Dear Sir/Madam,
I am trying to integrate my Spring MVC applications with Opentracing and Jaeger/Zipkin. I didn;t find any examples or API that can support Spring MVC.

As given in documentation FilterRegistrationBean is a SpringBoot API class, But I want to try to integrate with Spring MVC.

Can you please help me. Appreciate your help in advance.

No Service dependencies shown in Jaeger

Hello,

i have a minimal example with 2 Spring Boot Apps where one calls the other.
I can see all spans of the apps in the Jaeger UI, but no dependency between the apps. Shouldn't this already work by autoconfiguration?

Here is a link to the minimal example. If it's not a bug, maybe someone could push me into the right direction at Stackoverflow here :)

Thanks in advance

Disable tracerresolver configuration

Currently, if I want to use tracerresolver there is no way how to tell auto-config that I would like to use NoopTracer to quickly "disable the instrumentation". Probably we should provide a configuration property which would use Noop or resolve the tracer.

cc @objectiser @jpkrohling

Rename opentracing-spring-web-autoconfigure to opentracing-spring-web-starter

All the Spring Boot modules = starters containing the definition/list of the dependencies to be packaged like also the code to be used during bootstrap process and to configure the beans contains, part of their name, the word starter.

I propose to rename this project from opentracing-spring-web-autoconfigure to opentracing-spring-web-starter to adopt/follow the same convention. Make sense @geoand @pavolloffay @objectiser ?

Same convention should also be followed for the cloud module -> opentracing-spring-cloud-starter

Version 0.3.4 fails with OpenTracing API 0.33.0

Is it possible to have this commit (or at least the API bump part) integrated into the 0.3 branch? I'm stuck in an older Spring Boot version and right now 0.3.4 fails with the latest OpenTracing API because startActive() has been removed and not only deprecated.

Thanks.

Information in README.md is confusing regarding versions

The README.md says:

Versions 1.x.y of the library are meant to target Spring Framework 5.x and Spring Boot 2.x while versions 0.x.y are meant to be used with Spring Framework 4.3 and Spring Boot 1.5.

What "versions" is this referring to? Is this referring to versions of this Instrumentation Plugin? Because there are no versions 1.x.y, but instead there are versions 2.x.y.

Putting handlers into log fields makes ZipkinV2Reporter cause Gson errors

  1. TracingHandlerInterceptor puts a handler object into log fields. See this.
  2. ZipkinV2Reporter converts log fields to JSON via Gson to build JaegerSpan annotations. See this.
  3. Gson inspects private fields of the handler object.
  4. Some handlers have private fields that can not be handled by Gson.

This makes errors when I use TracingHandlerInterceptor and ZipkinV2Reporter together.

Do we need to put handler objects into log fields? I think class name and method name is enough.

Disable reporting spans for actuator endpoints

Hi guys,

Reposting this here.

When using Opentracing with Jaeger for Spring Cloud Gateway with actuator endpoints enabled (e.g. /actuator/health for K8s readiness probe), all calls to the actuator endpoints produce a span. Is there a way to disable this behaviour?

Config:

        <dependency>
            <groupId>io.opentracing.contrib</groupId>
            <artifactId>opentracing-spring-jaeger-cloud-starter</artifactId>
            <version>2.0.0</version>
        </dependency>

        <dependency>
            <groupId>io.opentracing.contrib</groupId>
            <artifactId>opentracing-spring-cloud-starter</artifactId>
            <version>0.3.2</version>
        </dependency>

Cheers,
Marius

Span customization - Extend WebFluxSpanDecorator

Hello!

I need to customize a span right after its creation and before its activation (injecting some baggage items).

I am encountering this need for both regular Servlet based applications and WebFlux applications.

In the Servlet world, it’s totally feasible thanks to a ServletFilterSpanDecorator.onRequest() which is invoked right after the span creation and before its activation.
See TracingFilter.

However, it’s not achievable in the reactor world as the WebFluxSpanDecorator.onRequest() is actually called at the subscription time occurring after the span activation.
See TracingOperator and TracingSubscriber.

Could we extend the WebFluxSpanDecorator by adding a new callback onCreate() that would be called right after the span creation?
I would also remove This is called right after span in created from the onRequest() documentation as it's not really the case.

Let me know if you agree with the proposal, I'll be happy to contribute! :)

Should new active span be created using current active span as a parent?

Hi! I'd like to discuss whether it makes sense to instantiate new active spans using possibly existing current active span as a parent. To illustrate this idea, here's an excerpt from TracingRestTemplateInterceptor.java

    @Override
    public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] body,
                                        ClientHttpRequestExecution execution) throws IOException {

        ActiveSpan span = tracer.buildSpan(httpRequest.getMethod().toString())
                .withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).startActive();
...

Which with described idea changes to (notice asChildOf call):

    @Override
    public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] body,
                                        ClientHttpRequestExecution execution) throws IOException {

        ActiveSpan span = tracer.buildSpan(httpRequest.getMethod().toString())
                .asChildOf(tracer.activeSpan())
                .withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).startActive();
...

What I'm currently trying to solve with this idea is creating a span that covers an operation that includes several outgoing HTTP requests.

TracingFilter and ServletFilterSpanDecorator

Hi, in this line https://github.com/opentracing-contrib/java-spring-web/blob/master/opentracing-spring-web-starter/src/main/java/io/opentracing/contrib/spring/web/starter/ServerTracingAutoConfiguration.java#L87 the lib is checking for some ServletFilterSpanDecorator decorators. Yesterday I had to create a custom decorator for me and I noticed that if I register my custom decorator, the STANDARD_TAGS is no longer registered due to this checking.

Would not be better if instead of manually checking and injecting we could not simply register it as a Bean and register all the beans?

The problem is that I don't to lose this default behavior, and if I register my custom bean I also have to define standard_tags as a bean to not lose it. So I have do to do something like this:

@Configuration
class DecoratorsConfig {
  @Bean
  public ServletFilterSpanDecorator myCustomDecorator() {
    return new MyCustomerServletFilterSpanDecorator();
  }
  
  @Bean
  public ServletFilterSpanDecorator defaultDecorator() { // this is actually undesired for me.
    return ServletFilterSpanDecorator.STANDARD_TAGS;
  }
}

If someone just don't want to use it, somehow, it could be annotated as @ConditionalOnProperty and be disabled through a property.

I didn't make a PR because I want to hear you guys before to understand this behavior.

FilterRegistrationBean not defined in non spring boot application

I have not spring mvc application NOT spring boot. In readme, example to extend WebMvcConfigurerAdapter shows use of FilterRegistrationBean which is spring boot class. What should I use for non spring boot application?

is below code fix the problem?
@OverRide
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TracingHandlerInterceptor(tracer)).addPathPatterns("/*");
}

Skip patterns does not apply to RestTemplate client requests

Hi, I've noticed that the skip url pattern configured by property opentracing.spring.web.skipPattern does not apply to RestTemplate requests.

My application uses RestTemplate to perform regular health check requests to other applications, which I need to stop tracing. Is there any way to achieve that?

I am looking into the code and I believe the TracingRestTemplateInterceptor may receive the Pattern with qualifier "skipPattern" and stop creating spans if it matches the URL in the request. It would be necessary to change SkipPatternAutoConfiguration to be loaded when opentracing.spring.web.client.enabled==true as well.

SkipPattern for Management Endpoints doesn't work with context-path

The skip pattern for actuator endpoints contains the context-path, but the TracingFilter tries to match the skip pattern without context-path.

The following is added to the configured default skip pattern:
/user/api/management/(health|health/.*|info|info/.*)

The method TracingFilter#isTraced removes the context-path from the request URI:
httpServletRequest.getRequestURI().substring(httpServletRequest.getContextPath().length())

Maybe I get something wrong, but I suspect that this filter never works when Spring has a context-path configured.

HandlerInterceptorSpanDecorator Not run

I am using https://github.com/spring-projects/spring-petclinic as a simple test example to test out how to use the library. A jaeger Tracer is configured and created. I only added PetClinicApplicationConfiguration that implements WebMvcConfigurer tow wireup a tracer and and TracingHandlerInterceptor. When I run the app, I can see the log message "TracingHandlerInterceptor added". But when I bring up the web and click on the front end . I am seeing the any of the HandlerInterceptorSpanDecorator being run. No log messages are printed from inside the Span decorator. .Below are the two files:

PetClinicApplicationConfiguraton.java

package org.springframework.samples.petclinic;

import org.springframework.context.annotation.Configuration;
import org.springframework.samples.tracing.TracerConfiguration;
import org.springframework.samples.petclinic.PetClinicApplication;
import org.springframework.samples.tracing.TracingHandlerDecorator;

import io.opentracing.Tracer;
import io.opentracing.util.GlobalTracer;
import io.opentracing.contrib.spring.web.interceptor.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.Arrays;
import java.util.List;

@Configuration
public class PetClinicApplicationConfiguration implements WebMvcConfigurer {
    private static Logger LOGGER = LogManager.getLogger();

    @Bean
    public Tracer tracer() {
        return (new TracerConfiguration()).getTracer(PetClinicApplication.SERVICE_NAME);
    }

    @Bean
    public List<HandlerInterceptorSpanDecorator> spanDecorators() {
        return Arrays.asList(new TracingHandlerDecorator());
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TracingHandlerInterceptor(GlobalTracer.get(), spanDecorators()));
        LOGGER.info("TracingHandlerInterceptor added");
    }

}

package org.springframework.samples.tracing;

import io.opentracing.Span;
import io.opentracing.contrib.spring.web.interceptor.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.web.method.HandlerMethod;
import org.bson.types.ObjectId;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Optional;

public class TracingHandlerDecorator implements HandlerInterceptorSpanDecorator {
private static Logger LOGGER = LogManager.getLogger();

@Override
public void onPreHandle(HttpServletRequest httpServletRequest,
                        Object handler,
                        Span span) {
    String metaData = HandlerUtils.methodName(handler);
    LOGGER.info("*************tracinghandledecorator***********");
    LOGGER.info("*******Global tracer*********** ={}", io.opentracing.util.GlobalTracer.get());
    if (metaData != null) {
        span.setOperationName(metaData);
        //SplunkUri uri = new SplunkUri(httpServletRequest.getRequestURI());
        //String tenantId = uri.getTenant();
        String requestId = getRequestId(httpServletRequest);
        span.setTag("requestId", requestId);
        //span.setTag("tenant", tenantId);
    }
}

@Override
public void onAfterCompletion(HttpServletRequest httpServletRequest,
                              HttpServletResponse httpServletResponse,
                              Object handler,
                              Exception ex,
                              Span span) {
    LOGGER.info("*************tracinghandledecorator ONAFTERCOMPLETION ***********");
}

@Override
public void onAfterConcurrentHandlingStarted(HttpServletRequest httpServletRequest,
                                             HttpServletResponse httpServletResponse,
                                             Object handler,
                                             Span span) {
    LOGGER.info("*************tracinghandledecorator AFTERCONCURRENTHANDLEING  STARTED***********");
}

private String getRequestId(HttpServletRequest request) {
    return Optional
        .ofNullable(request.getHeader("X-Request-Id"))
        .orElseGet(() -> ObjectId.get().toString());
}

/**
 * Helper class for deriving tags/logs from handler object.
 */
static class HandlerUtils {
    private HandlerUtils() {}

    /**
     * Class name of a handler serving request.
     */
    public static final String HANDLER_CLASS_NAME = "handler.class_simple_name";
    /**
     * Method name of handler serving request.
     */
    public static final String HANDLER_METHOD_NAME = "handler.method_name";
    /**
     * Spring handler object.
     */
    public static final String HANDLER = "handler";

    public static String methodName(Object handler) {
        return handler instanceof HandlerMethod
            ? ((HandlerMethod) handler).getMethod().getName() : null;
    }
}

}

Why is TracingHandlerInterceptor conditional on missing TracingFilter?

Code in question:

@Bean
@ConditionalOnMissingBean(TracingFilter.class)
public WebMvcConfigurer tracingHandlerInterceptor(final Tracer tracer) {

The source code of TracingHandlerInterceptor seems to suggest that it's meant to be used in conjunction with the TracingFilter:

* 1. check if there is an active span, it has been activated in servlet filter or in this interceptor (forward)

check if activeSpanStack is empty before pop

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.