diff --git a/docs/src/main/asciidoc/spring-cloud-sleuth.adoc b/docs/src/main/asciidoc/spring-cloud-sleuth.adoc index ac7b75d08..4fd67f4eb 100644 --- a/docs/src/main/asciidoc/spring-cloud-sleuth.adoc +++ b/docs/src/main/asciidoc/spring-cloud-sleuth.adoc @@ -838,12 +838,38 @@ Sleuth will search for beans of those types and automatically apply customizatio === HTTP -If a customization of client / server parsing of the HTTP related spans is -required, just register a bean of type `brave.http.HttpClientParser` or -`brave.http.HttpServerParser`. If client /server sampling is required, just -register a bean of type `brave.sampler.SamplerFunction` and name -the bean `sleuthHttpClientSampler` for client sampler and -`sleuthHttpServerSampler` for server sampler. +==== Data Policy + +The default span data policy for HTTP requests is described in Brave: +https://github.com/openzipkin/brave/tree/master/instrumentation/http#span-data-policy + +To add different data to the span, you need to register a bean of type +`brave.http.HttpRequestParser` or `brave.http.HttpResponseParser` based on when +the data is collected. + +The bean names correspond to the request or response side, and whether it is +a client or server. For example, `sleuthHttpClientRequestParser` changes what +is collected before a client request is sent to the server. + +For your convenience `@HttpClientRequestParser`, `@HttpClientResponseParser` +and corresponding server annotations can be used to inject the proper beans +or to reference the bean names via their static String `NAME` fields. + +Here's an example adding the HTTP url in addition to defaults: +[source,java] +---- +@Configuration +class Config { +include::{project-root}/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterWebIntegrationTests.java[tags=custom_parser,indent=2] +} +---- + +==== Sampling + +If client /server sampling is required, just register a bean of type +`brave.sampler.SamplerFunction` and name the bean +`sleuthHttpClientSampler` for client sampler and `sleuthHttpServerSampler` +for server sampler. For your convenience the `@HttpClientSampler` and `@HttpServerSampler` annotations can be used to inject the proper beans or to reference the bean diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpClientRequestParser.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpClientRequestParser.java new file mode 100644 index 000000000..f79639f58 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpClientRequestParser.java @@ -0,0 +1,51 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.sleuth.instrument.web; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import brave.http.HttpRequestParser; +import brave.http.HttpTracing; + +import org.springframework.beans.factory.annotation.Qualifier; + +/** + * Annotate a client {@link HttpRequestParser} that should be injected to + * {@link HttpTracing.Builder#clientRequestParser(HttpRequestParser)}. + * + * @see Qualifier + * @since 2.2.2 + */ +@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, + ElementType.ANNOTATION_TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +@Qualifier(HttpClientRequestParser.NAME) +public @interface HttpClientRequestParser { + + /** + * Default name for Sleuth HTTP client request parser. + */ + String NAME = "sleuthHttpClientRequestParser"; + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpClientResponseParser.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpClientResponseParser.java new file mode 100644 index 000000000..a5ecc499c --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpClientResponseParser.java @@ -0,0 +1,51 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.sleuth.instrument.web; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import brave.http.HttpResponseParser; +import brave.http.HttpTracing; + +import org.springframework.beans.factory.annotation.Qualifier; + +/** + * Annotate a client {@link HttpResponseParser} that should be injected to + * {@link HttpTracing.Builder#clientResponseParser(HttpResponseParser)}. + * + * @see Qualifier + * @since 2.2.2 + */ +@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, + ElementType.ANNOTATION_TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +@Qualifier(HttpClientResponseParser.NAME) +public @interface HttpClientResponseParser { + + /** + * Default name for Sleuth HTTP client response parser. + */ + String NAME = "sleuthHttpClientResponseParser"; + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServerRequestParser.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServerRequestParser.java new file mode 100644 index 000000000..7ae20954f --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServerRequestParser.java @@ -0,0 +1,51 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.sleuth.instrument.web; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import brave.http.HttpRequestParser; +import brave.http.HttpTracing; + +import org.springframework.beans.factory.annotation.Qualifier; + +/** + * Annotate a server {@link HttpRequestParser} that should be injected to + * {@link HttpTracing.Builder#serverRequestParser(HttpRequestParser)}. + * + * @see Qualifier + * @since 2.2.2 + */ +@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, + ElementType.ANNOTATION_TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +@Qualifier(HttpServerRequestParser.NAME) +public @interface HttpServerRequestParser { + + /** + * Default name for Sleuth HTTP server request parser. + */ + String NAME = "sleuthHttpServerRequestParser"; + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServerResponseParser.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServerResponseParser.java new file mode 100644 index 000000000..1e19082cd --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServerResponseParser.java @@ -0,0 +1,51 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.sleuth.instrument.web; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import brave.http.HttpResponseParser; +import brave.http.HttpTracing; + +import org.springframework.beans.factory.annotation.Qualifier; + +/** + * Annotate a server {@link HttpResponseParser} that should be injected to + * {@link HttpTracing.Builder#serverResponseParser(HttpResponseParser)}. + * + * @see Qualifier + * @since 2.2.2 + */ +@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, + ElementType.ANNOTATION_TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +@Qualifier(HttpServerResponseParser.NAME) +public @interface HttpServerResponseParser { + + /** + * Default name for Sleuth HTTP server response parser. + */ + String NAME = "sleuthHttpServerResponseParser"; + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceHttpAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceHttpAutoConfiguration.java index 2391d609d..3ff0f98ba 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceHttpAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceHttpAutoConfiguration.java @@ -19,11 +19,10 @@ package org.springframework.cloud.sleuth.instrument.web; import java.util.ArrayList; import java.util.List; -import brave.ErrorParser; import brave.Tracing; -import brave.http.HttpClientParser; import brave.http.HttpRequest; -import brave.http.HttpServerParser; +import brave.http.HttpRequestParser; +import brave.http.HttpResponseParser; import brave.http.HttpTracing; import brave.http.HttpTracingCustomizer; import brave.sampler.SamplerFunction; @@ -61,17 +60,47 @@ public class TraceHttpAutoConfiguration { @ConditionalOnMissingBean // NOTE: stable bean name as might be used outside sleuth HttpTracing httpTracing(Tracing tracing, SkipPatternProvider provider, - HttpClientParser clientParser, HttpServerParser serverParser, + @Nullable @HttpClientRequestParser HttpRequestParser httpClientRequestParser, + @Nullable @HttpClientResponseParser HttpResponseParser httpClientResponseParser, + @Nullable brave.http.HttpClientParser clientParser, + @Nullable @HttpServerRequestParser HttpRequestParser httpServerRequestParser, + @Nullable @HttpServerResponseParser HttpResponseParser httpServerResponseParser, + @Nullable brave.http.HttpServerParser serverParser, @HttpClientSampler SamplerFunction httpClientSampler, @Nullable @HttpServerSampler SamplerFunction httpServerSampler) { SamplerFunction combinedSampler = combineUserProvidedSamplerWithSkipPatternSampler( httpServerSampler, provider); HttpTracing.Builder builder = HttpTracing.newBuilder(tracing) - .clientParser(clientParser).serverParser(serverParser) .clientSampler(httpClientSampler).serverSampler(combinedSampler); + + if (httpClientRequestParser != null || httpClientResponseParser != null) { + if (httpClientRequestParser != null) { + builder.clientRequestParser(httpClientRequestParser); + } + if (httpClientResponseParser != null) { + builder.clientResponseParser(httpClientResponseParser); + } + } + else if (clientParser != null) { // consider deprecated last + builder.clientParser(clientParser); + } + + if (httpServerRequestParser != null || httpServerResponseParser != null) { + if (httpServerRequestParser != null) { + builder.serverRequestParser(httpServerRequestParser); + } + if (httpServerResponseParser != null) { + builder.serverResponseParser(httpServerResponseParser); + } + } + else if (serverParser != null) { // consider deprecated last + builder.serverParser(serverParser); + } + for (HttpTracingCustomizer customizer : this.httpTracingCustomizers) { customizer.customize(builder); } + return builder.build(); } @@ -86,23 +115,6 @@ public class TraceHttpAutoConfiguration { return new CompositeHttpSampler(skipPatternSampler, serverSampler); } - @Bean - @ConditionalOnMissingBean - HttpClientParser httpClientParser(ErrorParser errorParser) { - return new HttpClientParser() { - @Override - protected ErrorParser errorParser() { - return errorParser; - } - }; - } - - @Bean - @ConditionalOnMissingBean - HttpServerParser defaultHttpServerParser() { - return new HttpServerParser(); - } - @Bean @ConditionalOnMissingBean(name = HttpClientSampler.NAME) SamplerFunction sleuthHttpClientSampler( diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/HttpClientBeanPostProcessor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/HttpClientBeanPostProcessor.java index 888377a63..45312ffa5 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/HttpClientBeanPostProcessor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/HttpClientBeanPostProcessor.java @@ -152,7 +152,7 @@ class HttpClientBeanPostProcessor implements BeanPostProcessor { // Start a new client span with the appropriate parent TraceContext parent = req.currentContext().getOrDefault(TraceContext.class, null); - WrappedHttpClientRequest request = new WrappedHttpClientRequest(req); + HttpClientRequestWrapper request = new HttpClientRequestWrapper(req); clientSpan = handler().handleSendWithParent(request, parent); parseConnectionAddress(connection, clientSpan); @@ -240,18 +240,18 @@ class HttpClientBeanPostProcessor implements BeanPostProcessor { if (clientSpan == null) { return; // Unexpected. In the handle method, without a span to finish! } - WrappedHttpClientResponse response = resp != null - ? new WrappedHttpClientResponse(resp) : null; + HttpClientResponseWrapper response = resp != null + ? new HttpClientResponseWrapper(resp) : null; handler().handleReceive(response, error, clientSpan); } } - static final class WrappedHttpClientRequest extends brave.http.HttpClientRequest { + static final class HttpClientRequestWrapper extends brave.http.HttpClientRequest { final HttpClientRequest delegate; - WrappedHttpClientRequest(HttpClientRequest delegate) { + HttpClientRequestWrapper(HttpClientRequest delegate) { this.delegate = delegate; } @@ -287,11 +287,11 @@ class HttpClientBeanPostProcessor implements BeanPostProcessor { } - static final class WrappedHttpClientResponse extends brave.http.HttpClientResponse { + static final class HttpClientResponseWrapper extends brave.http.HttpClientResponse { final HttpClientResponse delegate; - WrappedHttpClientResponse(HttpClientResponse delegate) { + HttpClientResponseWrapper(HttpClientResponse delegate) { this.delegate = delegate; } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientBeanPostProcessor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientBeanPostProcessor.java index 97faff492..600c35fe5 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientBeanPostProcessor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientBeanPostProcessor.java @@ -23,6 +23,8 @@ import java.util.function.Function; import brave.Span; import brave.http.HttpClientHandler; +import brave.http.HttpClientRequest; +import brave.http.HttpClientResponse; import brave.http.HttpTracing; import brave.propagation.CurrentTraceContext; import brave.propagation.CurrentTraceContext.Scope; @@ -122,7 +124,7 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction { final Function, ? extends Publisher> scopePassingTransformer; // Lazy initialized fields - HttpClientHandler handler; + HttpClientHandler handler; CurrentTraceContext currentTraceContext; @@ -148,7 +150,7 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction { return this.currentTraceContext; } - HttpClientHandler handler() { + HttpClientHandler handler() { if (this.handler == null) { this.handler = HttpClientHandler.create(this.httpTracing.get()); } @@ -161,7 +163,7 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction { final ClientRequest request; - final HttpClientHandler handler; + final HttpClientHandler handler; final CurrentTraceContext currentTraceContext; @@ -185,7 +187,7 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction { Context context = subscriber.currentContext(); - HttpClientRequest wrapper = new HttpClientRequest(request); + ClientRequestWrapper wrapper = new ClientRequestWrapper(request); Span span = handler.handleSendWithParent(wrapper, parent); if (log.isDebugEnabled()) { log.debug("HttpClientHandler::handleSend: " + span); @@ -209,7 +211,7 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction { final Span clientSpan; - final HttpClientHandler handler; + final HttpClientHandler handler; final Function, ? extends Publisher> scopePassingTransformer; @@ -308,20 +310,20 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction { } void handleReceive(@Nullable ClientResponse res, @Nullable Throwable error) { - HttpClientResponse response = res != null ? new HttpClientResponse(res) + ClientResponseWrapper response = res != null ? new ClientResponseWrapper(res) : null; this.handler.handleReceive(response, error, clientSpan); } } - private static final class HttpClientRequest extends brave.http.HttpClientRequest { + private static final class ClientRequestWrapper extends HttpClientRequest { final ClientRequest delegate; final ClientRequest.Builder builder; - HttpClientRequest(ClientRequest delegate) { + ClientRequestWrapper(ClientRequest delegate) { this.delegate = delegate; this.builder = ClientRequest.from(delegate); } @@ -362,11 +364,11 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction { } - static final class HttpClientResponse extends brave.http.HttpClientResponse { + static final class ClientResponseWrapper extends HttpClientResponse { final ClientResponse delegate; - HttpClientResponse(ClientResponse delegate) { + ClientResponseWrapper(ClientResponse delegate) { this.delegate = delegate; } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignObjectWrapper.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignObjectWrapper.java index 6fc4f51e9..e27043dd0 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignObjectWrapper.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignObjectWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 the original author or authors. + * Copyright 2013-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -76,7 +76,7 @@ final class TraceFeignObjectWrapper { private Object instrumentedFeignLoadBalancerClient(Object bean) { if (AopUtils.getTargetClass(bean).equals(FeignBlockingLoadBalancerClient.class)) { - FeignBlockingLoadBalancerClient client = ((FeignBlockingLoadBalancerClient) bean); + FeignBlockingLoadBalancerClient client = ProxyUtils.getTargetObject(bean); return new TraceFeignBlockingLoadBalancerClient( (Client) new TraceFeignObjectWrapper(this.beanFactory) .wrap(client.getDelegate()), diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TracingFeignClient.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TracingFeignClient.java index 58b15a604..ac6d69442 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TracingFeignClient.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TracingFeignClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 the original author or authors. + * Copyright 2013-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,10 +25,12 @@ import java.util.LinkedHashMap; import java.util.Map; import brave.Span; -import brave.Tracer; import brave.http.HttpClientHandler; +import brave.http.HttpClientRequest; +import brave.http.HttpClientResponse; import brave.http.HttpTracing; -import brave.propagation.Propagation; +import brave.propagation.CurrentTraceContext; +import brave.propagation.CurrentTraceContext.Scope; import feign.Client; import feign.Request; import feign.Response; @@ -36,6 +38,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.cloud.util.ProxyUtils; +import org.springframework.lang.Nullable; /** * Feign client wrapper. @@ -47,37 +50,14 @@ final class TracingFeignClient implements Client { private static final Log log = LogFactory.getLog(TracingFeignClient.class); - static final Propagation.Setter>, String> SETTER = new Propagation.Setter>, String>() { - @Override - public void put(Map> carrier, String key, - String value) { - if (!carrier.containsKey(key)) { - carrier.put(key, Collections.singletonList(value)); - if (log.isTraceEnabled()) { - log.trace("Added key [" + key + "] and header value [" + value + "]"); - } - } - else { - if (log.isTraceEnabled()) { - log.trace("Key [" + key + "] already there in the headers"); - } - } - } - - @Override - public String toString() { - return "Map::set"; - } - }; - - final Tracer tracer; + final CurrentTraceContext currentTraceContext; final Client delegate; - final HttpClientHandler handler; + final HttpClientHandler handler; TracingFeignClient(HttpTracing httpTracing, Client delegate) { - this.tracer = httpTracing.tracing().tracer(); + this.currentTraceContext = httpTracing.tracing().currentTraceContext(); this.handler = HttpClientHandler.create(httpTracing); Client delegateTarget = ProxyUtils.getTargetObject(delegate); this.delegate = delegateTarget instanceof TracingFeignClient @@ -90,29 +70,27 @@ final class TracingFeignClient implements Client { @Override public Response execute(Request req, Request.Options options) throws IOException { - HttpClientRequest request = new HttpClientRequest(req); + RequestWrapper request = new RequestWrapper(req); Span span = this.handler.handleSend(request); if (log.isDebugEnabled()) { log.debug("Handled send of " + span); } - HttpClientResponse response = null; + Response res = null; Throwable error = null; - try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) { - Response res = this.delegate.execute(request.build(), options); - if (res != null) { - response = new HttpClientResponse(res); - } - else { // possibly null on bad implementation or mocks - response = new HttpClientResponse( - Response.builder().request(req).build()); + try (Scope ws = this.currentTraceContext.newScope(span.context())) { + res = this.delegate.execute(request.build(), options); + if (res == null) { // possibly null on bad implementation or mocks + res = Response.builder().request(req).build(); } return res; } - catch (IOException | RuntimeException | Error e) { + catch (Throwable e) { error = e; throw e; } finally { + ResponseWrapper response = res != null + ? new ResponseWrapper(request, res, error) : null; this.handler.handleReceive(response, error, span); if (log.isDebugEnabled()) { @@ -121,20 +99,22 @@ final class TracingFeignClient implements Client { } } - void handleSendAndReceive(Span span, Request request, Response response, - Throwable error) { - this.handler.handleSend(new HttpClientRequest(request), span); - this.handler.handleReceive( - response != null ? new HttpClientResponse(response) : null, error, span); + void handleSendAndReceive(Span span, Request req, @Nullable Response res, + @Nullable Throwable error) { + RequestWrapper request = new RequestWrapper(req); + this.handler.handleSend(request, span); + ResponseWrapper response = res != null ? new ResponseWrapper(request, res, error) + : null; + this.handler.handleReceive(response, error, span); } - static final class HttpClientRequest extends brave.http.HttpClientRequest { + static final class RequestWrapper extends HttpClientRequest { final Request delegate; Map> headers; - HttpClientRequest(Request delegate) { + RequestWrapper(Request delegate) { this.delegate = delegate; } @@ -202,22 +182,41 @@ final class TracingFeignClient implements Client { } - static final class HttpClientResponse extends brave.http.HttpClientResponse { + static final class ResponseWrapper extends HttpClientResponse { - final Response delegate; + final RequestWrapper request; - HttpClientResponse(Response delegate) { - this.delegate = delegate; + final Response response; + + @Nullable + final Throwable error; + + ResponseWrapper(RequestWrapper request, Response response, + @Nullable Throwable error) { + this.request = request; + this.response = response; + this.error = error; } @Override public Object unwrap() { - return delegate; + return response; + } + + @Override + public RequestWrapper request() { + return request; + } + + @Override + @Nullable + public Throwable error() { + return error; } @Override public int statusCode() { - return delegate.status(); + return response.status(); } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceHttpAutoConfigurationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceHttpAutoConfigurationTests.java index 6258ab105..3607a6cfb 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceHttpAutoConfigurationTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceHttpAutoConfigurationTests.java @@ -17,6 +17,8 @@ package org.springframework.cloud.sleuth.instrument.web; import brave.http.HttpRequest; +import brave.http.HttpRequestParser; +import brave.http.HttpResponseParser; import brave.http.HttpTracing; import brave.sampler.SamplerFunction; import org.junit.Test; @@ -85,6 +87,86 @@ public class TraceHttpAutoConfigurationTests { }; } + @Test + public void defaultHttpClientParser() { + contextRunner().run((context) -> { + HttpRequestParser clientRequestParser = context.getBean(HttpTracing.class) + .clientRequestParser(); + HttpResponseParser clientResponseParser = context.getBean(HttpTracing.class) + .clientResponseParser(); + + then(clientRequestParser).isInstanceOf(HttpRequestParser.Default.class); + then(clientResponseParser).isInstanceOf(HttpResponseParser.Default.class); + }); + } + + @Test + public void configuresUserProvidedHttpClientParser() { + contextRunner().withUserConfiguration(HttpClientParserConfig.class) + .run((context) -> { + HttpRequestParser clientRequestParser = context + .getBean(HttpTracing.class).clientRequestParser(); + HttpResponseParser clientResponseParser = context + .getBean(HttpTracing.class).clientResponseParser(); + + then(clientRequestParser) + .isSameAs(HttpClientParserConfig.REQUEST_PARSER); + then(clientResponseParser) + .isSameAs(HttpClientParserConfig.RESPONSE_PARSER); + }); + } + + @Test + public void defaultHttpServerParser() { + contextRunner().run((context) -> { + HttpRequestParser serverRequestParser = context.getBean(HttpTracing.class) + .serverRequestParser(); + HttpResponseParser serverResponseParser = context.getBean(HttpTracing.class) + .serverResponseParser(); + + then(serverRequestParser).isInstanceOf(HttpRequestParser.Default.class); + then(serverResponseParser).isInstanceOf(HttpResponseParser.Default.class); + }); + } + + @Test + public void configuresUserProvidedHttpServerParser() { + contextRunner().withUserConfiguration(HttpServerParserConfig.class) + .run((context) -> { + HttpRequestParser serverRequestParser = context + .getBean(HttpTracing.class).serverRequestParser(); + HttpResponseParser serverResponseParser = context + .getBean(HttpTracing.class).serverResponseParser(); + + then(serverRequestParser) + .isSameAs(HttpServerParserConfig.REQUEST_PARSER); + then(serverResponseParser) + .isSameAs(HttpServerParserConfig.RESPONSE_PARSER); + }); + } + + /** + * Shows bean aliases work to configure the same instance for both client and server + */ + @Test + public void configuresUserProvidedHttpClientAndServerParser() { + contextRunner().withUserConfiguration(HttpParserConfig.class).run((context) -> { + HttpRequestParser serverRequestParser = context.getBean(HttpTracing.class) + .serverRequestParser(); + HttpResponseParser serverResponseParser = context.getBean(HttpTracing.class) + .serverResponseParser(); + HttpRequestParser clientRequestParser = context.getBean(HttpTracing.class) + .clientRequestParser(); + HttpResponseParser clientResponseParser = context.getBean(HttpTracing.class) + .clientResponseParser(); + + then(clientRequestParser).isSameAs(HttpParserConfig.REQUEST_PARSER); + then(clientResponseParser).isSameAs(HttpParserConfig.RESPONSE_PARSER); + then(serverRequestParser).isSameAs(HttpParserConfig.REQUEST_PARSER); + then(serverResponseParser).isSameAs(HttpParserConfig.RESPONSE_PARSER); + }); + } + private ApplicationContextRunner contextRunner(String... propertyValues) { return new ApplicationContextRunner().withPropertyValues(propertyValues) .withConfiguration(AutoConfigurations.of(TraceAutoConfiguration.class, @@ -117,3 +199,63 @@ class HttpServerSamplerConfig { } } + +@Configuration +class HttpClientParserConfig { + + static final HttpRequestParser REQUEST_PARSER = (r, c, s) -> { + }; + static final HttpResponseParser RESPONSE_PARSER = (r, c, s) -> { + }; + + @Bean(HttpClientRequestParser.NAME) + HttpRequestParser sleuthHttpClientRequestParser() { + return REQUEST_PARSER; + } + + @Bean(HttpClientResponseParser.NAME) + HttpResponseParser sleuthHttpClientResponseParser() { + return RESPONSE_PARSER; + } + +} + +@Configuration +class HttpServerParserConfig { + + static final HttpRequestParser REQUEST_PARSER = (r, c, s) -> { + }; + static final HttpResponseParser RESPONSE_PARSER = (r, c, s) -> { + }; + + @Bean(HttpServerRequestParser.NAME) + HttpRequestParser sleuthHttpServerRequestParser() { + return REQUEST_PARSER; + } + + @Bean(HttpServerResponseParser.NAME) + HttpResponseParser sleuthHttpServerResponseParser() { + return RESPONSE_PARSER; + } + +} + +@Configuration +class HttpParserConfig { + + static final HttpRequestParser REQUEST_PARSER = (r, c, s) -> { + }; + static final HttpResponseParser RESPONSE_PARSER = (r, c, s) -> { + }; + + @Bean(name = { HttpClientRequestParser.NAME, HttpServerRequestParser.NAME }) + HttpRequestParser sleuthHttpServerRequestParser() { + return REQUEST_PARSER; + } + + @Bean(name = { HttpClientResponseParser.NAME, HttpServerResponseParser.NAME }) + HttpResponseParser sleuthHttpServerResponseParser() { + return RESPONSE_PARSER; + } + +} diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceExchangeFilterFunctionHttpClientResponseTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceExchangeFilterFunctionHttpClientResponseTests.java index b194cf6ab..e615af73d 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceExchangeFilterFunctionHttpClientResponseTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceExchangeFilterFunctionHttpClientResponseTests.java @@ -20,7 +20,7 @@ import org.assertj.core.api.BDDAssertions; import org.junit.Test; import org.mockito.BDDMockito; -import org.springframework.cloud.sleuth.instrument.web.client.TraceExchangeFilterFunction.HttpClientResponse; +import org.springframework.cloud.sleuth.instrument.web.client.TraceExchangeFilterFunction.ClientResponseWrapper; import org.springframework.web.reactive.function.client.ClientResponse; public class TraceExchangeFilterFunctionHttpClientResponseTests { @@ -29,7 +29,7 @@ public class TraceExchangeFilterFunctionHttpClientResponseTests { public void should_return_0_when_invalid_status_code_is_returned() { ClientResponse clientResponse = BDDMockito.mock(ClientResponse.class); BDDMockito.given(clientResponse.rawStatusCode()).willReturn(-1); - HttpClientResponse response = new HttpClientResponse(clientResponse); + ClientResponseWrapper response = new ClientResponseWrapper(clientResponse); Integer statusCode = response.statusCode(); @@ -40,7 +40,7 @@ public class TraceExchangeFilterFunctionHttpClientResponseTests { public void should_return_status_code_when_valid_status_code_is_returned() { ClientResponse clientResponse = BDDMockito.mock(ClientResponse.class); BDDMockito.given(clientResponse.rawStatusCode()).willReturn(200); - HttpClientResponse response = new HttpClientResponse(clientResponse); + ClientResponseWrapper response = new ClientResponseWrapper(clientResponse); Integer statusCode = response.statusCode(); diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRetriesTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRetriesTests.java index 86c0e0776..9d1d366ea 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRetriesTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRetriesTests.java @@ -22,7 +22,6 @@ import java.util.HashMap; import java.util.concurrent.atomic.AtomicInteger; import brave.Tracing; -import brave.http.HttpClientParser; import brave.http.HttpTracing; import brave.propagation.StrictScopeDecorator; import brave.propagation.ThreadLocalCurrentTraceContext; @@ -69,8 +68,7 @@ public class FeignRetriesTests { .addScopeDecorator(StrictScopeDecorator.create()).build()) .spanReporter(this.reporter).build(); - HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing) - .clientParser(new HttpClientParser()).build(); + HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing).build(); @Before @After diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignAspectTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignAspectTests.java index 85da05f5c..0fe01d886 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignAspectTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignAspectTests.java @@ -19,7 +19,6 @@ package org.springframework.cloud.sleuth.instrument.web.client.feign; import java.io.IOException; import brave.Tracing; -import brave.http.HttpClientParser; import brave.http.HttpTracing; import brave.propagation.StrictScopeDecorator; import brave.propagation.ThreadLocalCurrentTraceContext; @@ -57,8 +56,7 @@ public class TraceFeignAspectTests { .addScopeDecorator(StrictScopeDecorator.create()).build()) .build(); - HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing) - .clientParser(new HttpClientParser()).build(); + HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing).build(); TraceFeignAspect traceFeignAspect; diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TracingFeignClientTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TracingFeignClientTests.java index 7c7b1312b..39a59c040 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TracingFeignClientTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TracingFeignClientTests.java @@ -24,7 +24,6 @@ import java.util.List; import brave.Span; import brave.Tracer; import brave.Tracing; -import brave.http.HttpClientParser; import brave.http.HttpTracing; import brave.propagation.StrictScopeDecorator; import brave.propagation.ThreadLocalCurrentTraceContext; @@ -59,8 +58,7 @@ public class TracingFeignClientTests { Tracer tracer = this.tracing.tracer(); - HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing) - .clientParser(new HttpClientParser()).build(); + HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing).build(); @Mock Client client; diff --git a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-websocket/src/main/java/sample/SampleWebsocketApplication.java b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-websocket/src/main/java/sample/SampleWebsocketApplication.java index e671eb9f7..37f92fe0f 100644 --- a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-websocket/src/main/java/sample/SampleWebsocketApplication.java +++ b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-websocket/src/main/java/sample/SampleWebsocketApplication.java @@ -19,13 +19,13 @@ package sample; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.messaging.simp.config.MessageBrokerRegistry; -import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; +import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; @SpringBootApplication @EnableWebSocketMessageBroker -public class SampleWebsocketApplication extends AbstractWebSocketMessageBrokerConfigurer { +public class SampleWebsocketApplication implements WebSocketMessageBrokerConfigurer { public static void main(String[] args) { SpringApplication.run(SampleWebsocketApplication.class, args); diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/pom.xml b/tests/spring-cloud-sleuth-instrumentation-messaging-tests/pom.xml index 9a0700f86..5e56dcf2b 100644 --- a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/pom.xml +++ b/tests/spring-cloud-sleuth-instrumentation-messaging-tests/pom.xml @@ -60,6 +60,18 @@ https://www.w3.org/2001/XMLSchema-instance "> org.springframework.cloud spring-cloud-starter-sleuth + + org.springframework.cloud + spring-cloud-stream + jar + + + org.springframework.cloud + spring-cloud-stream + test-jar + test + test-binder + org.springframework.boot spring-boot-starter-test diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceContextPropagationChannelInterceptorTests.java b/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceContextPropagationChannelInterceptorTests.java index e9588d4be..9cbd75c06 100644 --- a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceContextPropagationChannelInterceptorTests.java +++ b/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceContextPropagationChannelInterceptorTests.java @@ -65,6 +65,7 @@ public class TraceContextPropagationChannelInterceptorTests { public void testSpanPropagation() { Span span = this.tracing.tracer().nextSpan().name("http:testSendMessage").start(); String expectedSpanId = SpanUtil.idToHex(span.context().spanId()); + try (Tracer.SpanInScope ws = this.tracing.tracer().withSpanInScope(span)) { this.channel.send(MessageBuilder.withPayload("hi").build()); } @@ -72,6 +73,10 @@ public class TraceContextPropagationChannelInterceptorTests { span.finish(); } + assertThatNewSpanIdWasSetOnMessage(expectedSpanId); + } + + private void assertThatNewSpanIdWasSetOnMessage(String expectedSpanId) { Message message = this.channel.receive(0); assertThat(message).as("message was null").isNotNull(); @@ -88,7 +93,6 @@ public class TraceContextPropagationChannelInterceptorTests { String.class); assertThat(parentId).as("parentId was not equal to parent's id") .isEqualTo(this.reporter.getSpans().get(0).id()); - } @Configuration diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceStreamChannelInterceptorTests.java b/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceStreamChannelInterceptorTests.java new file mode 100644 index 000000000..b4cefbb70 --- /dev/null +++ b/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceStreamChannelInterceptorTests.java @@ -0,0 +1,120 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.sleuth.instrument.messaging; + +import brave.Span; +import brave.Tracer; +import brave.Tracing; +import brave.sampler.Sampler; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.instrument.util.SpanUtil; +import org.springframework.cloud.sleuth.util.ArrayListSpanReporter; +import org.springframework.cloud.stream.binder.test.OutputDestination; +import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration; +import org.springframework.cloud.stream.function.StreamBridge; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.Message; +import org.springframework.test.annotation.DirtiesContext; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Spencer Gibb + */ +@SpringBootTest(classes = TraceStreamChannelInterceptorTests.App.class, + properties = "spring.cloud.stream.source=testSupplier") +@DirtiesContext +public class TraceStreamChannelInterceptorTests { + + @Autowired + private OutputDestination channel; + + @Autowired + private Tracing tracing; + + @Autowired + private StreamBridge streamBridge; + + @Autowired + private ArrayListSpanReporter reporter; + + @AfterEach + public void close() { + this.reporter.clear(); + } + + @Test + public void testSpanPropagationViaBridge() { + Span span = this.tracing.tracer().nextSpan().name("http:testSendMessage").start(); + String expectedSpanId = SpanUtil.idToHex(span.context().spanId()); + + try (Tracer.SpanInScope ws = this.tracing.tracer().withSpanInScope(span)) { + this.streamBridge.send("testSupplier-out-0", "hi"); + } + finally { + span.finish(); + } + + assertThatNewSpanIdWasSetOnMessage(expectedSpanId); + } + + private void assertThatNewSpanIdWasSetOnMessage(String expectedSpanId) { + Message message = this.channel.receive(0); + assertThat(message).as("message was null").isNotNull(); + + String spanId = message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME, + String.class); + assertThat(spanId).as("spanId was equal to parent's id") + .isNotEqualTo(expectedSpanId); + + String traceId = message.getHeaders().get(TraceMessageHeaders.TRACE_ID_NAME, + String.class); + assertThat(traceId).as("traceId was null").isNotNull(); + + String parentId = message.getHeaders().get(TraceMessageHeaders.PARENT_ID_NAME, + String.class); + // [0] - producer + // [1] - http:testsendmessage + assertThat(parentId).as("parentId was not equal to parent's id") + .isEqualTo(this.reporter.getSpans().get(1).id()); + } + + @Configuration + @EnableAutoConfiguration + @ImportAutoConfiguration(TestChannelBinderConfiguration.class) + static class App { + + @Bean + Sampler testSampler() { + return Sampler.ALWAYS_SAMPLE; + } + + @Bean + ArrayListSpanReporter reporter() { + return new ArrayListSpanReporter(); + } + + } + +} diff --git a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloSpringIntegration.java b/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloSpringIntegration.java index 20fef6839..32b80db76 100644 --- a/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloSpringIntegration.java +++ b/tests/spring-cloud-sleuth-instrumentation-messaging-tests/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/issues/issue_943/HelloSpringIntegration.java @@ -19,7 +19,6 @@ package org.springframework.cloud.sleuth.instrument.messaging.issues.issue_943; import brave.sampler.Sampler; import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; @@ -30,8 +29,7 @@ import org.springframework.integration.config.EnableIntegration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.web.client.RestTemplate; -@SpringBootApplication -@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class, +@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class }) @ImportResource("classpath:beans/applicationContext.xml") @EnableIntegration diff --git a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterWebIntegrationTests.java b/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterWebIntegrationTests.java index 9dd9e2171..7bf0e6c3b 100644 --- a/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterWebIntegrationTests.java +++ b/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterWebIntegrationTests.java @@ -24,6 +24,7 @@ import java.util.stream.Collectors; import brave.Tracing; import brave.http.HttpRequest; +import brave.http.HttpRequestParser; import brave.sampler.Sampler; import brave.sampler.SamplerFunction; import org.assertj.core.api.BDDAssertions; @@ -66,9 +67,6 @@ public class TraceFilterWebIntegrationTests { @Rule public OutputCaptureRule capture = new OutputCaptureRule(); - @Autowired - Tracing tracer; - @Autowired ArrayListSpanReporter accumulator; @@ -85,6 +83,16 @@ public class TraceFilterWebIntegrationTests { this.accumulator.clear(); } + @Test + public void should_tag_url() { + new RestTemplate().getForObject("http://localhost:" + port() + "/good", + String.class); + + then(Tracing.current().tracer().currentSpan()).isNull(); + then(this.accumulator.getSpans()).hasSize(1); + then(this.accumulator.getSpans().get(0).tags()).containsKey("http.url"); + } + @Test public void should_not_create_a_span_for_error_controller() { try { @@ -146,7 +154,12 @@ public class TraceFilterWebIntegrationTests { public static class Config { @Bean - ExceptionThrowingController controller() { + GoodController goodController() { + return new GoodController(); + } + + @Bean + ExceptionThrowingController badController() { return new ExceptionThrowingController(); } @@ -160,6 +173,19 @@ public class TraceFilterWebIntegrationTests { return Sampler.ALWAYS_SAMPLE; } + // tag::custom_parser[] + @Bean(name = { HttpClientRequestParser.NAME, HttpServerRequestParser.NAME }) + HttpRequestParser sleuthHttpServerRequestParser() { + return (req, context, span) -> { + HttpRequestParser.DEFAULT.parse(req, context, span); + String url = req.url(); + if (url != null) { + span.tag("http.url", url); + } + }; + } + // end::custom_parser[] + // tag::custom_server_sampler[] @Bean(name = HttpServerSampler.NAME) SamplerFunction myHttpSampler(SkipPatternProvider provider) { @@ -188,6 +214,16 @@ public class TraceFilterWebIntegrationTests { } + @RestController + public static class GoodController { + + @RequestMapping("/good") + public String beGood() { + return "good"; + } + + } + @RestController public static class ExceptionThrowingController { diff --git a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/WebClientBraveTests.java b/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/WebClientBraveTests.java index d5859a3d5..e2b6833ed 100644 --- a/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/WebClientBraveTests.java +++ b/tests/spring-cloud-sleuth-instrumentation-reactor-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/WebClientBraveTests.java @@ -28,6 +28,7 @@ import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.reactive.ClientHttpConnector; import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.reactive.function.client.ClientResponse; @@ -118,9 +119,13 @@ public class WebClientBraveTests } @Bean - WebClient.Builder webClientBuilder(HttpClient httpClient) { - return WebClient.builder() - .clientConnector(new ReactorClientHttpConnector(httpClient)); + ClientHttpConnector clientHttpConnector(HttpClient httpClient) { + return new ReactorClientHttpConnector(httpClient); + } + + @Bean + WebClient.Builder webClientBuilder(ClientHttpConnector clientHttpConnector) { + return WebClient.builder().clientConnector(clientHttpConnector); } }