diff --git a/docs/src/main/asciidoc/spring-cloud-sleuth.adoc b/docs/src/main/asciidoc/spring-cloud-sleuth.adoc index 665ceac4c..5cd9eb560 100644 --- a/docs/src/main/asciidoc/spring-cloud-sleuth.adoc +++ b/docs/src/main/asciidoc/spring-cloud-sleuth.adoc @@ -1023,6 +1023,15 @@ tracing context gets injected to the sent requests. To block these features, set `spring.sleuth.web.client.enabled` to `false`. +==== Netty `HttpClient` + +We instrument the Netty's `HttpClient`. + +To block this feature, set `spring.sleuth.web.client.enabled` to `false`. + +IMPORTANT: You have to register `HttpClient` as a bean so that the instrumentation happens. +If you create a `HttpClient` instance with a `new` keyword, the instrumentation does NOT work. + === Feign By default, Spring Cloud Sleuth provides integration with Feign through `TraceFeignClientAutoConfiguration`. diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAspect.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAspect.java index 32f889293..fbb89bda7 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAspect.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAspect.java @@ -16,8 +16,6 @@ package org.springframework.cloud.sleuth.instrument.web; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Field; import java.util.concurrent.Callable; @@ -32,8 +30,6 @@ import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.instrument.async.TraceCallable; import org.springframework.web.context.request.async.WebAsyncTask; -import brave.Span; - /** * Aspect that adds tracing to *
@@ -73,14 +69,12 @@ public class TraceWebAspect { private final Tracer tracer; private final SpanNamer spanNamer; - //private final TraceKeys traceKeys; private final ErrorParser errorParser; - public TraceWebAspect(Tracer tracer, SpanNamer spanNamer, //TraceKeys traceKeys, + public TraceWebAspect(Tracer tracer, SpanNamer spanNamer, ErrorParser errorParser) { this.tracer = tracer; this.spanNamer = spanNamer; - //this.traceKeys = traceKeys; this.errorParser = errorParser; } @@ -99,9 +93,6 @@ public class TraceWebAspect { @Pointcut("execution(public org.springframework.web.context.request.async.WebAsyncTask *(..))") private void anyPublicMethodReturningWebAsyncTask() { } // NOSONAR - @Pointcut("execution(public * org.springframework.web.servlet.HandlerExceptionResolver.resolveException(..)) && args(request, response, handler, ex)") - private void anyHandlerExceptionResolver(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { } // NOSONAR - @Pointcut("(anyRestControllerAnnotated() || anyControllerAnnotated()) && anyPublicMethodReturningWebAsyncTask()") private void anyControllerOrRestControllerWithPublicWebAsyncTaskMethod() { } // NOSONAR @@ -140,13 +131,4 @@ public class TraceWebAspect { return webAsyncTask; } - @Around("anyHandlerExceptionResolver(request, response, handler, ex)") - public Object markRequestForSpanClosing(ProceedingJoinPoint pjp, - HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Throwable { - Span currentSpan = this.tracer.currentSpan(); - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(currentSpan)){ - return pjp.proceed(); - } - } - } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java index f2d21cfdc..72c19ab33 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java @@ -19,13 +19,31 @@ package org.springframework.cloud.sleuth.instrument.web.client; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; +import brave.Span; +import brave.Tracer; +import brave.http.HttpClientHandler; import brave.http.HttpTracing; import brave.httpasyncclient.TracingHttpAsyncClientBuilder; import brave.httpclient.TracingHttpClientBuilder; +import brave.propagation.Propagation; +import brave.propagation.TraceContext; +import brave.propagation.TraceContextOrSamplingFlags; import brave.spring.web.TracingClientHttpRequestInterceptor; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpMethod; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.reactivestreams.Publisher; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.ListableBeanFactory; @@ -48,6 +66,10 @@ import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import org.springframework.web.client.RestTemplate; import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; +import reactor.ipc.netty.http.client.HttpClient; +import reactor.ipc.netty.http.client.HttpClientRequest; +import reactor.ipc.netty.http.client.HttpClientResponse; /** * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration @@ -117,6 +139,15 @@ public class TraceWebClientAutoConfiguration { return new TraceWebClientBeanPostProcessor(beanFactory); } } + + @Configuration + @ConditionalOnClass(HttpClient.class) + static class NettyConfiguration { + @Bean + public NettyAspect traceNetyAspect(HttpTracing httpTracing) { + return new NettyAspect(httpTracing); + } + } } class RestTemplateInterceptorInjector { @@ -209,4 +240,148 @@ class LazyTracingClientHttpRequestInterceptor implements ClientHttpRequestInterc } return this.interceptor; } -} \ No newline at end of file +} + +@Aspect +class NettyAspect { + + private final TracingHttpClientInstrumentation instrumentation; + + NettyAspect(HttpTracing httpTracing) { + this.instrumentation = TracingHttpClientInstrumentation.create(httpTracing); + } + + @Pointcut("execution(public * reactor.ipc.netty.http.client.HttpClient.request(..)) && args(method, url, handler)") + private void anyHttpClientRequestSending(HttpMethod method, + String url, Function super HttpClientRequest, ? extends Publisher