handler() {
if (this.handler == null) {
- this.handler = HttpClientHandler.create(httpTracing());
+ this.handler = HttpClientHandler.create(httpTracing.get());
}
return this.handler;
}
- protected void handle(HttpClientResponse httpClientResponse,
- Throwable throwable) {
- if (httpClientResponse == null) {
+ void handle(Context context, @Nullable HttpClientResponse resp,
+ @Nullable Throwable error) {
+ CurrentClientSpan ref = context.getOrDefault(CurrentClientSpan.class, null);
+ if (ref == null) { // Somehow TracingMapConnect was not invoked.. skip out
return;
}
- AtomicReference reference = httpClientResponse.currentContext()
- .getOrDefault(AtomicReference.class, null);
- if (reference == null || reference.get() == null) {
- return;
+
+ Span clientSpan = ref.getAndSet(null);
+ if (clientSpan == null) {
+ return; // Unexpected. In the handle method, without a span to finish!
}
- handler().handleReceive(new WrappedHttpClientResponse(httpClientResponse),
- throwable, (Span) reference.get());
+ WrappedHttpClientResponse response = resp != null
+ ? new WrappedHttpClientResponse(resp) : null;
+ handler().handleReceive(response, error, clientSpan);
}
}
@@ -260,12 +270,12 @@ class HttpClientBeanPostProcessor implements BeanPostProcessor {
@Override
public String path() {
- return delegate.path();
+ return "/" + delegate.path(); // TODO: reactor/reactor-netty#999
}
@Override
public String url() {
- return delegate.uri();
+ return delegate.resourceUrl();
}
@Override
@@ -288,6 +298,11 @@ class HttpClientBeanPostProcessor implements BeanPostProcessor {
this.delegate = delegate;
}
+ @Override
+ public String method() {
+ return delegate.method().name();
+ }
+
@Override
public Object unwrap() {
return delegate;
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 ef596dddf..bb59752b7 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
@@ -162,8 +162,8 @@ public class TraceWebClientAutoConfiguration {
@Bean
static HttpClientBeanPostProcessor httpClientBeanPostProcessor(
- BeanFactory beanFactory) {
- return new HttpClientBeanPostProcessor(beanFactory);
+ ConfigurableApplicationContext springContext) {
+ return new HttpClientBeanPostProcessor(springContext);
}
}
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 558f384cf..e50f31fc0 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
@@ -18,6 +18,7 @@ package org.springframework.cloud.sleuth.instrument.web.client;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.CancellationException;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -131,9 +132,14 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction {
}
};
- private static final String CLIENT_SPAN_KEY = "sleuth.webclient.clientSpan";
+ static final String CLIENT_SPAN_KEY = "sleuth.webclient.clientSpan";
- private static final String CANCELLED_SUBSCRIPTION_ERROR = "CANCELLED";
+ static final Exception CANCELLED_ERROR = new CancellationException("CANCELLED") {
+ @Override
+ public Throwable fillInStackTrace() {
+ return this; // stack trace doesn't add value here
+ }
+ };
final ConfigurableApplicationContext springContext;
@@ -170,6 +176,8 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction {
}
MonoWebClientTrace trace = new MonoWebClientTrace(next, wrapper.buildRequest(),
this, span);
+ // TODO: investigate why this commit leaks a scope:
+ // 8f5bcdabd7af23df443e771432eb85597f3b3076
tracer().withSpanInScope(parentSpan);
return trace;
}
@@ -356,13 +364,14 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction {
return this.context;
}
- void handleReceive(Span clientSpan, ClientResponse clientResponse,
- Throwable throwable) {
+ void handleReceive(Span clientSpan, @Nullable ClientResponse res,
+ @Nullable Throwable error) {
if (log.isTraceEnabled()) {
log.trace("Handling receive");
}
- this.handler.handleReceive(new HttpClientResponse(clientResponse),
- throwable, clientSpan);
+ HttpClientResponse response = res != null ? new HttpClientResponse(res)
+ : null;
+ this.handler.handleReceive(response, error, clientSpan);
if (log.isTraceEnabled()) {
log.trace("Closed scope");
}
@@ -374,32 +383,31 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction {
+ this.span + "]");
}
- this.span.tag("error", CANCELLED_SUBSCRIPTION_ERROR);
- handleReceive(this.span, null, null);
+ handleReceive(this.span, null, CANCELLED_ERROR);
}
void terminateSpan(@Nullable ClientResponse clientResponse,
- @Nullable Throwable throwable) {
+ @Nullable Throwable error) {
if (clientResponse == null) {
if (log.isDebugEnabled()) {
log.debug("No response was returned. Will close the span ["
+ this.span + "]");
}
- handleReceive(this.span, clientResponse, throwable);
+ handleReceive(this.span, null, error);
return;
}
int statusCode = clientResponse.rawStatusCode();
- boolean error = statusCode >= 400;
- if (error) {
+ boolean isHttpError = statusCode >= 400;
+ if (isHttpError) {
if (log.isDebugEnabled()) {
log.debug(
"Non positive status code was returned from the call. Will close the span ["
+ this.span + "]");
}
- throwable = new RestClientException(
+ error = new RestClientException(
"Status code of the response is [" + statusCode + "]");
}
- handleReceive(this.span, clientResponse, throwable);
+ handleReceive(this.span, clientResponse, error);
}
}
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 54cdc172a..58b15a604 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
@@ -99,9 +99,13 @@ final class TracingFeignClient implements Client {
Throwable error = null;
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
Response res = this.delegate.execute(request.build(), options);
- if (res != null) { // possibly null on bad implementation or mocks
+ if (res != null) {
response = new HttpClientResponse(res);
}
+ else { // possibly null on bad implementation or mocks
+ response = new HttpClientResponse(
+ Response.builder().request(req).build());
+ }
return res;
}
catch (IOException | RuntimeException | Error e) {
diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/LazyBean.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/internal/LazyBean.java
similarity index 83%
rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/LazyBean.java
rename to spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/internal/LazyBean.java
index 364d18648..8a55e0b31 100644
--- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/LazyBean.java
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/internal/LazyBean.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.sleuth.instrument.reactor;
+package org.springframework.cloud.sleuth.internal;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -25,8 +25,16 @@ import org.springframework.lang.Nullable;
/**
* Avoids calling the expensive {@link ConfigurableApplicationContext#getBean(Class)} many
* times or throwing an exception.
+ *
+ *
+ * Note: This is an internal class to sleuth and must not be used by external code.
*/
-final class LazyBean {
+public final class LazyBean {
+
+ public static LazyBean create(ConfigurableApplicationContext springContext,
+ Class requiredType) {
+ return new LazyBean<>(springContext, requiredType);
+ }
// spring-jcl uses commons-logging, so do we.
private static final Log log = LogFactory.getLog(LazyBean.class);
@@ -47,7 +55,7 @@ final class LazyBean {
* @return the bean value or null if there was an exception getting it.
*/
@Nullable
- T get() {
+ public T get() {
if (this.value != null) {
return this.value;
}
diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/ReactorNettyHttpClientSpringBootTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/ReactorNettyHttpClientSpringBootTests.java
new file mode 100644
index 000000000..efecdd11b
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/ReactorNettyHttpClientSpringBootTests.java
@@ -0,0 +1,206 @@
+/*
+ * 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.client;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import brave.propagation.B3SinglePropagation;
+import brave.propagation.CurrentTraceContext;
+import brave.propagation.CurrentTraceContext.Scope;
+import brave.propagation.Propagation;
+import brave.propagation.TraceContext;
+import brave.sampler.Sampler;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+import reactor.netty.DisposableServer;
+import reactor.netty.http.client.HttpClient;
+import reactor.netty.http.client.HttpClientResponse;
+import reactor.netty.http.client.PrematureCloseException;
+import reactor.netty.http.server.HttpServer;
+import zipkin2.Span;
+import zipkin2.reporter.Reporter;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/**
+ * This tests {@link HttpClient} instrumentation performed by
+ * {@link HttpClientBeanPostProcessor}, as wired by auto-configuration.
+ *
+ *
+ * Note: {@link HttpClient} can be an implementation of {@link WebClient}, so
+ * care should be taken to also test that integration. For example, it would be easy to
+ * create duplicate client spans for the same request.
+ */
+@SpringBootTest(classes = ReactorNettyHttpClientSpringBootTests.TestConfiguration.class,
+ webEnvironment = SpringBootTest.WebEnvironment.NONE)
+@RunWith(SpringRunner.class)
+public class ReactorNettyHttpClientSpringBootTests {
+
+ DisposableServer disposableServer;
+
+ @Autowired
+ HttpClient httpClient;
+
+ @Autowired
+ BlockingQueue spans;
+
+ @Autowired
+ CurrentTraceContext currentTraceContext;
+
+ TraceContext context = TraceContext.newBuilder().traceId(1).spanId(1).sampled(true)
+ .build();
+
+ @After
+ public void tearDown() {
+ if (disposableServer != null) {
+ disposableServer.disposeNow();
+ }
+ this.spans.clear();
+ }
+
+ @Test
+ public void shouldRecordRemoteEndpoint() throws Exception {
+ disposableServer = HttpServer.create().port(0)
+ .handle((in, out) -> out.sendString(Flux.just("foo"))).bindNow();
+
+ HttpClientResponse response = httpClient.port(disposableServer.port()).get()
+ .uri("/").response().block();
+
+ assertThat(response.status()).isEqualTo(HttpResponseStatus.OK);
+
+ Span clientSpan = takeClientSpan();
+
+ assertThat(clientSpan.remoteEndpoint()).satisfiesAnyOf(
+ ep -> assertThat(ep.ipv4()).isNotNull(),
+ ep -> assertThat(ep.ipv6()).isNotNull());
+ assertThat(clientSpan.remoteEndpoint().portAsInt()).isNotZero();
+ }
+
+ @Test
+ public void shouldUseInvocationContext() throws Exception {
+ disposableServer = HttpServer.create().port(0)
+ // this reads the trace context header, b3, returning it in the response
+ .handle((in, out) -> out
+ .sendString(Flux.just(in.requestHeaders().get("b3"))))
+ .bindNow();
+
+ String b3SingleHeaderReadByServer;
+ try (Scope ws = currentTraceContext.newScope(context)) {
+ b3SingleHeaderReadByServer = httpClient.port(disposableServer.port()).get()
+ .uri("/").responseContent().aggregate().asString().block();
+ }
+
+ Span clientSpan = takeClientSpan();
+
+ assertThat(b3SingleHeaderReadByServer).isEqualTo(context.traceIdString() + "-"
+ + clientSpan.id() + "-1-" + context.spanIdString());
+ }
+
+ @Test
+ public void shouldSendTraceContextToServer_rootSpan() throws Exception {
+ disposableServer = HttpServer.create().port(0)
+ // this reads the trace context header, b3, returning it in the response
+ .handle((in, out) -> out
+ .sendString(Flux.just(in.requestHeaders().get("b3"))))
+ .bindNow();
+
+ Mono request = httpClient.port(disposableServer.port()).get().uri("/")
+ .responseContent().aggregate().asString();
+
+ String b3SingleHeaderReadByServer = request.block();
+
+ Span clientSpan = takeClientSpan();
+
+ assertThat(b3SingleHeaderReadByServer)
+ .isEqualTo(clientSpan.traceId() + "-" + clientSpan.id() + "-1");
+ }
+
+ @Test
+ public void shouldTagOnRequestError() throws InterruptedException {
+ disposableServer = HttpServer.create().port(0).handle((req, resp) -> {
+ throw new RuntimeException("test");
+ }).bindNow();
+
+ Mono request = httpClient.port(disposableServer.port()).get().uri("/")
+ .responseContent().aggregate().asString();
+
+ assertThatThrownBy(request::block)
+ .hasCauseInstanceOf(PrematureCloseException.class);
+
+ Span clientSpan = takeClientSpan();
+
+ assertThat(clientSpan.tags()).containsKey("error");
+ }
+
+ /** Call this to block until a span was reported */
+ Span takeClientSpan() throws InterruptedException {
+ Span result = spans.poll(1, TimeUnit.SECONDS);
+ assertThat(result).withFailMessage("Span was not reported").isNotNull();
+ assertThat(result.kind()).isEqualTo(Span.Kind.CLIENT);
+ return result;
+ }
+
+ @Configuration
+ @EnableAutoConfiguration
+ static class TestConfiguration {
+
+ @Bean
+ Propagation.Factory propagationFactory() {
+ return B3SinglePropagation.FACTORY;
+ }
+
+ @Bean
+ Sampler sampler() {
+ return Sampler.ALWAYS_SAMPLE;
+ }
+
+ /**
+ * Use a blocking queue as it is simpler than wrapping everything in awaitility
+ */
+ @Bean
+ BlockingQueue spans() {
+ return new LinkedBlockingQueue<>();
+ }
+
+ @Bean
+ Reporter spanReporter(BlockingQueue spans) {
+ return spans::add;
+ }
+
+ @Bean
+ HttpClient reactorHttpClient() {
+ return HttpClient.create();
+ }
+
+ }
+
+}
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 d195b7bd0..53dc877cd 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
@@ -17,8 +17,9 @@
package org.springframework.cloud.sleuth.instrument.web.client.feign;
import java.io.IOException;
-import java.nio.charset.Charset;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import brave.Span;
import brave.Tracer;
@@ -36,9 +37,7 @@ import org.mockito.BDDMockito;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
-import org.springframework.beans.factory.BeanFactory;
import org.springframework.cloud.sleuth.instrument.web.SleuthHttpParserAccessor;
-import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import static org.assertj.core.api.BDDAssertions.then;
@@ -48,15 +47,16 @@ import static org.assertj.core.api.BDDAssertions.then;
@RunWith(MockitoJUnitRunner.class)
public class TracingFeignClientTests {
- ArrayListSpanReporter reporter = new ArrayListSpanReporter();
+ Request request = Request.create("GET", "https://foo", new HashMap<>(), null, null);
- @Mock
- BeanFactory beanFactory;
+ Request.Options options = new Request.Options();
+
+ List spans = new ArrayList<>();
Tracing tracing = Tracing.newBuilder()
.currentTraceContext(ThreadLocalCurrentTraceContext.newBuilder()
.addScopeDecorator(StrictScopeDecorator.create()).build())
- .spanReporter(this.reporter).build();
+ .spanReporter(spans::add).build();
Tracer tracer = this.tracing.tracer();
@@ -78,17 +78,13 @@ public class TracingFeignClientTests {
Span span = this.tracer.nextSpan().name("foo");
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) {
- this.traceFeignClient
- .execute(
- Request.create("GET", "https://foo", new HashMap<>(),
- "".getBytes(), Charset.defaultCharset()),
- new Request.Options());
+ this.traceFeignClient.execute(this.request, this.options);
}
finally {
span.finish();
}
- then(this.reporter.getSpans().get(0)).extracting("kind.ordinal")
+ then(spans.get(0)).extracting("kind.ordinal")
.isEqualTo(Span.Kind.CLIENT.ordinal());
}
@@ -99,11 +95,7 @@ public class TracingFeignClientTests {
.willThrow(new RuntimeException("exception has occurred"));
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) {
- this.traceFeignClient
- .execute(
- Request.create("GET", "https://foo", new HashMap<>(),
- "".getBytes(), Charset.defaultCharset()),
- new Request.Options());
+ this.traceFeignClient.execute(this.request, this.options);
BDDAssertions.fail("Exception should have been thrown");
}
catch (Exception e) {
@@ -112,21 +104,17 @@ public class TracingFeignClientTests {
span.finish();
}
- then(this.reporter.getSpans().get(0)).extracting("kind.ordinal")
+ then(this.spans.get(0)).extracting("kind.ordinal")
.isEqualTo(Span.Kind.CLIENT.ordinal());
- then(this.reporter.getSpans().get(0).tags()).containsEntry("error",
- "exception has occurred");
+ then(this.spans.get(0).tags()).containsEntry("error", "exception has occurred");
}
@Test
public void should_shorten_the_span_name() throws IOException {
- this.traceFeignClient
- .execute(
- Request.create("GET", "https://foo/" + bigName(), new HashMap<>(),
- "".getBytes(), Charset.defaultCharset()),
- new Request.Options());
+ this.traceFeignClient.execute(Request.create("GET", "https://foo/" + bigName(),
+ new HashMap<>(), null, null), this.options);
- then(this.reporter.getSpans().get(0).name()).hasSize(50);
+ then(this.spans.get(0).name()).hasSize(50);
}
private String bigName() {
diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/integration/WebClientTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/integration/WebClientTests.java
index b335b2443..f25ed70b5 100644
--- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/integration/WebClientTests.java
+++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/integration/WebClientTests.java
@@ -36,6 +36,7 @@ import brave.propagation.TraceContextOrSamplingFlags;
import brave.sampler.Sampler;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
+import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
@@ -51,8 +52,8 @@ import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import reactor.netty.http.client.HttpClient;
-import reactor.netty.http.client.HttpClientResponse;
+import org.reactivestreams.Subscription;
+import reactor.core.publisher.BaseSubscriber;
import zipkin2.Annotation;
import zipkin2.reporter.Reporter;
@@ -112,8 +113,7 @@ public class WebClientTests {
static final String SAMPLED_NAME = "X-B3-Sampled";
static final String PARENT_ID_NAME = "X-B3-ParentSpanId";
- private static final org.apache.commons.logging.Log log = LogFactory
- .getLog(WebClientTests.class);
+ private static final Log log = LogFactory.getLog(WebClientTests.class);
@Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
@@ -134,9 +134,6 @@ public class WebClientTests {
@Autowired
HttpClientBuilder httpClientBuilder; // #845
- @Autowired
- HttpClient nettyHttpClient;
-
@Autowired
HttpAsyncClientBuilder httpAsyncClientBuilder; // #845
@@ -276,30 +273,6 @@ public class WebClientTests {
then(this.reporter.getSpans()).isNotEmpty();
}
- @Test
- @SuppressWarnings("unchecked")
- public void shouldAttachTraceIdWhenCallingAnotherServiceForNettyHttpClient()
- throws Exception {
- Span span = this.tracer.nextSpan().name("foo").start();
-
- try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
- HttpClientResponse response = this.nettyHttpClient.get()
- .uri("http://localhost:" + this.port).response().block();
-
- then(response).isNotNull();
- }
-
- Awaitility.await().untilAsserted(() -> {
- then(this.tracer.currentSpan()).isNull();
- System.out.println("Collected span " + this.reporter.getSpans());
- then(this.reporter.getSpans()).isNotEmpty()
- .extracting("traceId", String.class)
- // we can have some bizarre spans popping up
- .contains(span.context().traceIdString());
- then(this.reporter.getSpans()).extracting("kind.name").contains("CLIENT");
- });
- }
-
@Test
@SuppressWarnings("unchecked")
public void shouldAttachTraceIdWhenCallingAnotherServiceForHttpClient()
@@ -378,7 +351,7 @@ public class WebClientTests {
@Test
@SuppressWarnings("unchecked")
- public void shouldWorkWhenCustomStatusCodeIsReturned() throws InterruptedException {
+ public void shouldWorkWhenCustomStatusCodeIsReturned() {
Span span = this.tracer.nextSpan().name("foo").start();
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
@@ -397,6 +370,21 @@ public class WebClientTests {
.contains("CLIENT");
}
+ @Test
+ public void shouldTagOnCancel() {
+ this.webClient.get().uri("http://localhost:" + this.port + "/doNotSkip")
+ .retrieve().bodyToMono(String.class)
+ .subscribe(new BaseSubscriber() {
+ @Override
+ protected void hookOnSubscribe(Subscription subscription) {
+ cancel();
+ }
+ });
+
+ then(this.reporter.getSpans()).isNotEmpty();
+ then(this.reporter.getSpans().get(0).tags()).containsEntry("error", "CANCELLED");
+ }
+
@Test
public void shouldRespectSkipPattern() {
this.webClient.get().uri("http://localhost:" + this.port + "/skip").retrieve()
@@ -599,11 +587,6 @@ public class WebClientTests {
return new MyRestTemplateCustomizer();
}
- @Bean
- HttpClient reactorHttpClient() {
- return HttpClient.create();
- }
-
}
static class MyRestTemplateCustomizer implements RestTemplateCustomizer {
diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/LazyBeanTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/internal/LazyBeanTests.java
similarity index 95%
rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/LazyBeanTests.java
rename to spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/internal/LazyBeanTests.java
index 3f9694ba6..db03fb410 100644
--- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/reactor/LazyBeanTests.java
+++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/internal/LazyBeanTests.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.sleuth.instrument.reactor;
+package org.springframework.cloud.sleuth.internal;
import brave.propagation.CurrentTraceContext;
import org.junit.Test;
diff --git a/spring-cloud-sleuth-dependencies/pom.xml b/spring-cloud-sleuth-dependencies/pom.xml
index 7b8985958..905e78763 100644
--- a/spring-cloud-sleuth-dependencies/pom.xml
+++ b/spring-cloud-sleuth-dependencies/pom.xml
@@ -31,7 +31,7 @@
spring-cloud-sleuth-dependencies
Spring Cloud Sleuth Dependencies
- 5.9.3
+ 5.9.5
0.35.1
3.4.1
diff --git a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinBackwardsCompatibilityAutoConfiguration.java b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinBackwardsCompatibilityAutoConfiguration.java
deleted file mode 100644
index e58326656..000000000
--- a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/ZipkinBackwardsCompatibilityAutoConfiguration.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * 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.zipkin2;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-import zipkin2.Span;
-import zipkin2.codec.BytesEncoder;
-import zipkin2.reporter.AsyncReporter;
-import zipkin2.reporter.InMemoryReporterMetrics;
-import zipkin2.reporter.Reporter;
-import zipkin2.reporter.ReporterMetrics;
-import zipkin2.reporter.Sender;
-
-import org.springframework.beans.factory.support.DefaultListableBeanFactory;
-import org.springframework.boot.autoconfigure.AutoConfigureBefore;
-import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
-import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.ConditionContext;
-import org.springframework.context.annotation.Conditional;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.ConfigurationCondition;
-import org.springframework.core.type.AnnotatedTypeMetadata;
-import org.springframework.util.Assert;
-
-/**
- * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
- * Auto-configuration} that will provide backwards compatibility to be able to support
- * multiple tracing systems on the classpath.
- *
- * Needs to be auto-configured before {@link ZipkinAutoConfiguration} in order to create a
- * {@link Reporter span reporter} if needed.
- *
- * @author Tim Ysewyn
- * @since 2.1.0
- * @see ZipkinAutoConfiguration
- * @deprecated left for backward compatibility
- */
-@Configuration(proxyBeanMethods = false)
-@ConditionalOnProperty(value = { "spring.sleuth.enabled", "spring.zipkin.enabled" },
- matchIfMissing = true)
-@AutoConfigureBefore(ZipkinAutoConfiguration.class)
-@Deprecated
-public class ZipkinBackwardsCompatibilityAutoConfiguration {
-
- /**
- * Reporter that is depending on a {@link Sender} bean which is created in another
- * auto-configuration than {@link ZipkinAutoConfiguration}.
- * @param reporterMetrics metrics
- * @param zipkin zipkin properties
- * @param spanBytesEncoder encoder
- * @param beanFactory Spring's Bean Factory
- * @return span reporter
- * @deprecated left for backwards compatibility
- */
- @Bean
- @Conditional(BackwardsCompatibilityCondition.class)
- @Deprecated
- Reporter reporter(ReporterMetrics reporterMetrics, ZipkinProperties zipkin,
- BytesEncoder spanBytesEncoder, DefaultListableBeanFactory beanFactory) {
- List beanNames = new ArrayList<>(
- Arrays.asList(beanFactory.getBeanNamesForType(Sender.class)));
- beanNames.remove(ZipkinAutoConfiguration.SENDER_BEAN_NAME);
- Sender sender = (Sender) beanFactory.getBean(beanNames.get(0));
- // historical constraint. Note: AsyncReporter supports memory bounds
- return AsyncReporter.builder(sender).queuedMaxSpans(1000)
- .messageTimeout(zipkin.getMessageTimeout(), TimeUnit.SECONDS)
- .metrics(reporterMetrics).build(spanBytesEncoder);
- }
-
- /**
- * Only used for creating a reporter bean with the method above.
- * @param zipkinProperties zipkin properties
- * @return bytes encoder
- * @deprecated left for backwards compatibility
- */
- @Bean
- @ConditionalOnMissingBean
- @Deprecated
- BytesEncoder spanBytesEncoder(ZipkinProperties zipkinProperties) {
- return zipkinProperties.getEncoder();
- }
-
- /**
- * Deprecated because this is moved to {@link TraceAutoConfiguration}. Left for
- * backwards compatibility reasons.
- * @return reporter metrics
- * @deprecated left for backwards compatibility
- */
- @Bean
- @ConditionalOnMissingBean
- @Deprecated
- ReporterMetrics zipkinReporterMetrics() {
- return new InMemoryReporterMetrics();
- }
-
- /**
- * Old approach: - one sender - one reporter
- *
- * This auto configuration verifies if we have the old approach. In which case we
- * define the missing beans.
- *
- * In case of having 0 or more than 1 sender and there is a reporter, we don't need to
- * use the backward compatibility bean setup.
- */
- static class BackwardsCompatibilityCondition extends SpringBootCondition
- implements ConfigurationCondition {
-
- @Override
- public ConfigurationPhase getConfigurationPhase() {
- return ConfigurationPhase.REGISTER_BEAN;
- }
-
- @Override
- public ConditionOutcome getMatchOutcome(ConditionContext context,
- AnnotatedTypeMetadata metadata) {
- Assert.isInstanceOf(DefaultListableBeanFactory.class,
- context.getBeanFactory());
- DefaultListableBeanFactory listableBeanFactory = (DefaultListableBeanFactory) context
- .getBeanFactory();
- int foundSenders = listableBeanFactory
- .getBeanNamesForType(Sender.class).length;
-
- // Previously we supported 1 Sender bean at a time
- // which could be overridden by another auto-configuration.
- // Now we support both the overridden bean and our default zipkinSender bean.
- // Since this config is adapting the old config we're searching for exactly 1
- // `Sender` bean before `ZipkinAutoConfiguration` kicks in.
- if (foundSenders != 1) {
- return ConditionOutcome.noMatch(
- "None or multiple Sender beans found - no reason to apply backwards compatibility");
- }
- int foundReporters = listableBeanFactory
- .getBeanNamesForType(Reporter.class).length;
- // Check if we need to provide a Reporter bean for the overridden Sender bean
- if (foundReporters > 0) {
- return ConditionOutcome.noMatch(
- "The old config setup already defines its own Reporter bean");
- }
- return ConditionOutcome.match();
- }
-
- }
-
-}
diff --git a/spring-cloud-sleuth-zipkin/src/main/resources/META-INF/spring.factories b/spring-cloud-sleuth-zipkin/src/main/resources/META-INF/spring.factories
index d36fcbaca..f2608fbe3 100644
--- a/spring-cloud-sleuth-zipkin/src/main/resources/META-INF/spring.factories
+++ b/spring-cloud-sleuth-zipkin/src/main/resources/META-INF/spring.factories
@@ -1,4 +1,3 @@
# Auto Configuration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
-org.springframework.cloud.sleuth.zipkin2.ZipkinAutoConfiguration,\
-org.springframework.cloud.sleuth.zipkin2.ZipkinBackwardsCompatibilityAutoConfiguration
+org.springframework.cloud.sleuth.zipkin2.ZipkinAutoConfiguration
diff --git a/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinBackwardsCompatibilityAutoConfigurationTests.java b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinBackwardsCompatibilityAutoConfigurationTests.java
deleted file mode 100644
index 934c9ff9b..000000000
--- a/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinBackwardsCompatibilityAutoConfigurationTests.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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.zipkin2;
-
-import org.junit.Test;
-import zipkin2.codec.BytesEncoder;
-import zipkin2.reporter.Reporter;
-import zipkin2.reporter.ReporterMetrics;
-
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * @author Tim Ysewyn
- */
-public class ZipkinBackwardsCompatibilityAutoConfigurationTests {
-
- private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withConfiguration(AutoConfigurations.of(
- ZipkinBackwardsCompatibilityAutoConfiguration.class,
- ZipkinAutoConfiguration.class, TraceAutoConfiguration.class));
-
- @Test
- public void shouldLoadBeans() {
- this.contextRunner.run(context -> {
- assertThat(context.getBean(ZipkinProperties.class)).isNotNull();
- assertThat(context.getBean(Reporter.class)).isNotNull();
- assertThat(context.getBean(BytesEncoder.class)).isNotNull();
- assertThat(context.getBean(ReporterMetrics.class)).isNotNull();
- });
- }
-
- @Test
- public void shouldNotLoadBackwardsCompatibilityConfigWhenZipkinDisabled() {
- this.contextRunner.withPropertyValues("spring.zipkin.enabled=false")
- .run(context -> {
- assertThat(context.getBeansOfType(ZipkinProperties.class)).isEmpty();
- assertThat(context.getBeansOfType(BytesEncoder.class)).isEmpty();
- assertThat(context.getBean(ReporterMetrics.class)).isNotNull(); // TraceAutoConfiguration
- assertThat(context.getBean(Reporter.class)).isNotNull(); // noOpSpanReporter
- });
- }
-
- @Test
- public void shouldNotLoadBackwardsCompatibilityConfigWhenSleuthDisabled() {
- this.contextRunner.withPropertyValues("spring.sleuth.enabled=false")
- .run(context -> {
- assertThat(context.getBeansOfType(ZipkinProperties.class)).isEmpty();
- assertThat(context.getBeansOfType(BytesEncoder.class)).isEmpty();
- assertThat(context.getBeansOfType(ReporterMetrics.class)).isEmpty();
- assertThat(context.getBeansOfType(Reporter.class)).isEmpty();
- });
- }
-
-}