Migrates tests to separate modules (#1395)

fixes #1394
This commit is contained in:
Marcin Grzejszczak
2019-07-08 13:36:20 +02:00
committed by GitHub
parent e9ee0ba686
commit e6b78d267f
98 changed files with 1923 additions and 710 deletions

View File

@@ -18,10 +18,12 @@ package org.springframework.cloud.sleuth.instrument.web.client.feign;
import java.io.IOException;
import brave.http.HttpTracing;
import feign.Client;
import feign.Request;
import feign.Response;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
/**
@@ -33,18 +35,36 @@ class LazyClient implements Client {
private final BeanFactory beanFactory;
private final Client delegate;
private Client delegate;
private TraceFeignObjectWrapper wrapper;
LazyClient(BeanFactory beanFactory, Client delegate) {
LazyClient(BeanFactory beanFactory, Client client) {
this.beanFactory = beanFactory;
this.delegate = client;
}
LazyClient(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
this.delegate = delegate;
}
@Override
public Response execute(Request request, Request.Options options) throws IOException {
return ((Client) wrapper().wrap(this.delegate)).execute(request, options);
return ((Client) wrapper().wrap(delegate())).execute(request, options);
}
private Client delegate() {
if (this.delegate == null) {
try {
this.delegate = this.beanFactory.getBean(Client.class);
}
catch (BeansException ex) {
this.delegate = TracingFeignClient.create(
beanFactory.getBean(HttpTracing.class),
new Client.Default(null, null));
}
}
return this.delegate;
}
private TraceFeignObjectWrapper wrapper() {

View File

@@ -42,8 +42,7 @@ final class SleuthFeignBuilder {
private static Client client(BeanFactory beanFactory) {
try {
Client client = beanFactory.getBean(Client.class);
return new LazyClient(beanFactory, client);
return new LazyClient(beanFactory);
}
catch (BeansException ex) {
return TracingFeignClient.create(beanFactory.getBean(HttpTracing.class),

View File

@@ -18,6 +18,9 @@ package org.springframework.cloud.sleuth.annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import javax.annotation.concurrent.NotThreadSafe;
@@ -40,7 +43,6 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.util.Pair;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
@@ -712,3 +714,52 @@ public class SleuthSpanCreatorAspectMonoTests {
}
}
/**
* Copied from Spring Data
*/
final class Pair<S, T> {
private final S first;
private final T second;
Pair(S first, T second) {
this.first = first;
this.second = second;
}
public static <S, T> Pair<S, T> of(S first, T second) {
return new Pair<>(first, second);
}
public S getFirst() {
return first;
}
public T getSecond() {
return second;
}
public static <S, T> Collector<Pair<S, T>, ?, Map<S, T>> toMap() {
return Collectors.toMap(Pair::getFirst, Pair::getSecond);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Pair<?, ?> pair = (Pair<?, ?>) o;
return Objects.equals(first, pair.first) && Objects.equals(second, pair.second);
}
@Override
public int hashCode() {
return Objects.hash(first, second);
}
}

View File

@@ -1,252 +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.instrument.async;
import java.util.AbstractMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import brave.Span;
import brave.Tracer;
import brave.Tracing;
import brave.sampler.Sampler;
import org.awaitility.Awaitility;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.SpanName;
import org.springframework.cloud.sleuth.instrument.DefaultTestAutoConfiguration;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.test.context.junit4.SpringRunner;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.BDDAssertions.then;
@RunWith(SpringRunner.class)
@SpringBootTest(
classes = { TraceAsyncIntegrationTests.TraceAsyncITestConfiguration.class })
public class TraceAsyncIntegrationTests {
@Autowired
ClassPerformingAsyncLogic classPerformingAsyncLogic;
@Autowired
Tracing tracer;
@Autowired
ArrayListSpanReporter reporter;
@Before
public void cleanup() {
this.classPerformingAsyncLogic.clear();
this.reporter.clear();
}
@Test
public void should_set_span_on_an_async_annotated_method() {
whenAsyncProcessingTakesPlace();
thenANewAsyncSpanGetsCreated();
}
@Test
public void should_set_span_with_custom_method_on_an_async_annotated_method() {
whenAsyncProcessingTakesPlaceWithCustomSpanName();
thenAsyncSpanHasCustomName();
}
@Test
public void should_continue_a_span_on_an_async_annotated_method() {
Span span = givenASpanInCurrentThread();
try (Tracer.SpanInScope ws = this.tracer.tracer().withSpanInScope(span.start())) {
whenAsyncProcessingTakesPlace();
thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOne(span);
}
finally {
span.finish();
}
}
@Test
public void should_continue_a_span_with_custom_method_on_an_async_annotated_method() {
Span span = givenASpanInCurrentThread();
try (Tracer.SpanInScope ws = this.tracer.tracer().withSpanInScope(span.start())) {
whenAsyncProcessingTakesPlaceWithCustomSpanName();
thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOneAndSpanHasCustomName(
span);
}
finally {
span.finish();
}
}
private Span givenASpanInCurrentThread() {
return this.tracer.tracer().nextSpan().name("http:existing");
}
private void whenAsyncProcessingTakesPlace() {
this.classPerformingAsyncLogic.invokeAsynchronousLogic();
}
private void whenAsyncProcessingTakesPlaceWithCustomSpanName() {
this.classPerformingAsyncLogic.customNameInvokeAsynchronousLogic();
}
private void thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOne(final Span span) {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
Span asyncSpan = TraceAsyncIntegrationTests.this.classPerformingAsyncLogic
.getSpan();
then(asyncSpan.context().traceId()).isEqualTo(span.context().traceId());
List<zipkin2.Span> spans = TraceAsyncIntegrationTests.this.reporter
.getSpans();
zipkin2.Span reportedAsyncSpan = spans.stream()
.filter(span2 -> span2.name().equals("invoke-asynchronous-logic"))
.findFirst().orElseThrow(() -> new AssertionError(
"Should have a span with custom name"));
then(reportedAsyncSpan.traceId()).isEqualTo(span.context().traceIdString());
then(reportedAsyncSpan.name()).isEqualTo("invoke-asynchronous-logic");
then(reportedAsyncSpan.tags())
.contains(new AbstractMap.SimpleEntry<>("class",
"ClassPerformingAsyncLogic"))
.contains(new AbstractMap.SimpleEntry<>("method",
"invokeAsynchronousLogic"));
});
}
private void thenANewAsyncSpanGetsCreated() {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
List<zipkin2.Span> spans = TraceAsyncIntegrationTests.this.reporter
.getSpans();
then(spans).hasSize(2);
zipkin2.Span reportedAsyncSpan = spans.stream()
.filter(span -> span.name().equals("invoke-asynchronous-logic"))
.findFirst().orElseThrow(() -> new AssertionError(
"Should have a span with custom name"));
then(reportedAsyncSpan.tags())
.contains(new AbstractMap.SimpleEntry<>("class",
"ClassPerformingAsyncLogic"))
.contains(new AbstractMap.SimpleEntry<>("method",
"invokeAsynchronousLogic"));
});
}
private void thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOneAndSpanHasCustomName(
final Span span) {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
Span asyncSpan = TraceAsyncIntegrationTests.this.classPerformingAsyncLogic
.getSpan();
then(asyncSpan.context().traceId()).isEqualTo(span.context().traceId());
List<zipkin2.Span> spans = TraceAsyncIntegrationTests.this.reporter
.getSpans();
then(spans).hasSize(2);
zipkin2.Span reportedAsyncSpan = spans.stream()
.filter(span2 -> span2.name().equals("foo")).findFirst()
.orElseThrow(() -> new AssertionError(
"Should have a span with custom name"));
then(reportedAsyncSpan.traceId()).isEqualTo(span.context().traceIdString());
then(reportedAsyncSpan.name()).isEqualTo("foo");
then(reportedAsyncSpan.tags())
.contains(new AbstractMap.SimpleEntry<>("class",
"ClassPerformingAsyncLogic"))
.contains(new AbstractMap.SimpleEntry<>("method",
"customNameInvokeAsynchronousLogic"));
});
}
private void thenAsyncSpanHasCustomName() {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
List<zipkin2.Span> spans = TraceAsyncIntegrationTests.this.reporter
.getSpans();
zipkin2.Span reportedAsyncSpan = spans.stream()
.filter(span2 -> span2.name().equals("foo")).findFirst()
.orElseThrow(() -> new AssertionError(
"Should have a span with custom name"));
then(reportedAsyncSpan.name()).isEqualTo("foo");
then(reportedAsyncSpan.tags())
.contains(new AbstractMap.SimpleEntry<>("class",
"ClassPerformingAsyncLogic"))
.contains(new AbstractMap.SimpleEntry<>("method",
"customNameInvokeAsynchronousLogic"));
});
}
@DefaultTestAutoConfiguration
@EnableAsync
@Configuration
static class TraceAsyncITestConfiguration {
@Bean
ClassPerformingAsyncLogic asyncClass(Tracer tracer) {
return new ClassPerformingAsyncLogic(tracer);
}
@Bean
Sampler defaultSampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
ArrayListSpanReporter reporter() {
return new ArrayListSpanReporter();
}
}
static class ClassPerformingAsyncLogic {
private final Tracer tracer;
AtomicReference<Span> span = new AtomicReference<>();
ClassPerformingAsyncLogic(Tracer tracer) {
this.tracer = tracer;
}
@Async
public void invokeAsynchronousLogic() {
this.span.set(this.tracer.currentSpan());
}
@Async
@SpanName("foo")
public void customNameInvokeAsynchronousLogic() {
this.span.set(this.tracer.currentSpan());
}
public Span getSpan() {
return this.span.get();
}
public void clear() {
this.span.set(null);
}
}
}

View File

@@ -1,182 +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.instrument.async.issues.issue1212;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import org.junit.Test;
import org.slf4j.LoggerFactory;
import org.springframework.aop.interceptor.AsyncExecutionAspectSupport;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
import org.springframework.scheduling.annotation.EnableAsync;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Bertrand Renuart
*/
public class GH1212Tests {
@Test
public void defaultTaskExecutor() throws Exception {
try (ConfigurableApplicationContext ctx = new SpringApplicationBuilder(App.class,
DefaultTaskExecutorConfig.class).web(WebApplicationType.NONE).run()) {
String asyncThreadName = getAsyncThreadName(ctx);
assertThat(asyncThreadName).startsWith("defaultTaskExecutor");
}
}
@Test
public void singleTaskExecutor() throws Exception {
try (ConfigurableApplicationContext ctx = new SpringApplicationBuilder(App.class,
SingleTaskExecutorConfig.class).web(WebApplicationType.NONE).run()) {
String asyncThreadName = getAsyncThreadName(ctx);
assertThat(asyncThreadName).startsWith("singleTaskExecutor");
}
}
@Test
public void multipleTaskExecutors() throws Exception {
try (ConfigurableApplicationContext ctx = new SpringApplicationBuilder(App.class,
MultipleTaskExecutorConfig.class).web(WebApplicationType.NONE).run()) {
String asyncThreadName = getAsyncThreadName(ctx);
assertThat(asyncThreadName).doesNotStartWith("multipleTaskExecutor");
assertThat(asyncThreadName).startsWith("SimpleAsyncTaskExecutor"); // <--
// comes
// from
// Sleuth's
// own
// AsyncConfigurer
}
}
@Test
public void customAsyncConfigurer() throws Exception {
try (ConfigurableApplicationContext ctx = new SpringApplicationBuilder(App.class,
CustomAsyncConfigurerConfig.class).web(WebApplicationType.NONE).run()) {
String asyncThreadName = getAsyncThreadName(ctx);
assertThat(asyncThreadName).startsWith("customAsyncConfigurer");
}
}
private String getAsyncThreadName(ApplicationContext ctx) throws Exception {
return ctx.getBean(AsyncComponent.class).asyncMethod().get();
}
@SpringBootConfiguration
@EnableAutoConfiguration
@EnableAsync
static class App {
@Bean
AsyncComponent asyncComponent() {
return new AsyncComponent();
}
}
static class AsyncComponent {
@Async
public CompletableFuture<String> asyncMethod() {
LoggerFactory.getLogger("test").info("asyncMethod invoked");
return CompletableFuture.completedFuture(Thread.currentThread().getName());
}
}
/*
* Configuration with a single Executor named `taskExecutor`
*/
@Configuration
static class DefaultTaskExecutorConfig {
@Bean(name = AsyncExecutionAspectSupport.DEFAULT_TASK_EXECUTOR_BEAN_NAME)
public Executor taskExecutor() {
return new SimpleAsyncTaskExecutor("defaultTaskExecutor");
}
}
/*
* Configuration with a single TaskExecutor
*/
@Configuration
static class SingleTaskExecutorConfig {
@Bean
// there's the task
@Primary
public TaskExecutor singleTaskExecutor() {
return new SimpleAsyncTaskExecutor("singleTaskExecutor");
}
}
/*
* Configuration with a multiple TaskExecutors --> Spring won't pick any unless one
* is @Primary
*/
@Configuration
static class MultipleTaskExecutorConfig {
@Bean
public TaskExecutor multipleTaskExecutor1() {
return new SimpleAsyncTaskExecutor("multipleTaskExecutor1");
}
@Bean
public TaskExecutor multipleTaskExecutor2() {
return new SimpleAsyncTaskExecutor("multipleTaskExecutor2");
}
}
/*
* Configuration where a custom AsyncConfigurer is provided
*/
@Configuration
static class CustomAsyncConfigurerConfig {
@Bean
public AsyncConfigurer customAsyncConfigurer() {
return new AsyncConfigurerSupport() {
@Override
public Executor getAsyncExecutor() {
return new SimpleAsyncTaskExecutor("customAsyncConfigurer");
}
};
}
}
}

View File

@@ -1,462 +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.instrument.async.issues.issue410;
import java.lang.invoke.MethodHandles;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicReference;
import brave.Span;
import brave.Tracer;
import brave.sampler.Sampler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.awaitility.Awaitility;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.sleuth.instrument.async.LazyTraceExecutor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Component;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.BDDAssertions.then;
/**
* @author Marcin Grzejszczak
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, webEnvironment = WebEnvironment.RANDOM_PORT,
properties = { "ribbon.eureka.enabled=false", "feign.hystrix.enabled=false" })
public class Issue410Tests {
private static final Log log = LogFactory
.getLog(MethodHandles.lookup().lookupClass());
@Autowired
Environment environment;
@Autowired
Tracer tracer;
@Autowired
AsyncTask asyncTask;
@Autowired
RestTemplate restTemplate;
/**
* Related to issue #445.
*/
@Autowired
Application.MyService executorService;
@Test
public void should_pass_tracing_info_for_tasks_running_without_a_pool() {
Span span = this.tracer.nextSpan().name("foo");
log.info("Starting test");
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
String response = this.restTemplate.getForObject(
"http://localhost:" + port() + "/without_pool", String.class);
then(response).isEqualTo(span.context().traceIdString());
Awaitility.await().untilAsserted(() -> {
then(this.asyncTask.getSpan().get()).isNotNull();
then(this.asyncTask.getSpan().get().context().traceId())
.isEqualTo(span.context().traceId());
});
}
finally {
span.finish();
}
then(this.tracer.currentSpan()).isNull();
}
@Test
public void should_pass_tracing_info_for_tasks_running_with_a_pool() {
Span span = this.tracer.nextSpan().name("foo");
log.info("Starting test");
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
String response = this.restTemplate.getForObject(
"http://localhost:" + port() + "/with_pool", String.class);
then(response).isEqualTo(span.context().traceIdString());
Awaitility.await().untilAsserted(() -> {
then(this.asyncTask.getSpan().get()).isNotNull();
then(this.asyncTask.getSpan().get().context().traceId())
.isEqualTo(span.context().traceId());
});
}
finally {
span.finish();
}
then(this.tracer.currentSpan()).isNull();
}
/**
* Related to issue #423.
*/
@Test
public void should_pass_tracing_info_for_completable_futures_with_executor() {
Span span = this.tracer.nextSpan().name("foo");
log.info("Starting test");
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
String response = this.restTemplate.getForObject(
"http://localhost:" + port() + "/completable", String.class);
then(response).isEqualTo(span.context().traceIdString());
Awaitility.await().untilAsserted(() -> {
then(this.asyncTask.getSpan().get()).isNotNull();
then(this.asyncTask.getSpan().get().context().traceId())
.isEqualTo(span.context().traceId());
});
}
finally {
span.finish();
}
then(this.tracer.currentSpan()).isNull();
}
/**
* Related to issue #423.
*/
@Test
public void should_pass_tracing_info_for_completable_futures_with_task_scheduler() {
Span span = this.tracer.nextSpan().name("foo");
log.info("Starting test");
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
String response = this.restTemplate.getForObject(
"http://localhost:" + port() + "/taskScheduler", String.class);
then(response).isEqualTo(span.context().traceIdString());
Awaitility.await().untilAsserted(() -> {
then(this.asyncTask.getSpan().get()).isNotNull();
then(this.asyncTask.getSpan().get().context().traceId())
.isEqualTo(span.context().traceId());
});
}
finally {
span.finish();
}
then(this.tracer.currentSpan()).isNull();
}
/**
* Related to issue #1232
*/
@Test
public void should_pass_tracing_info_for_completable_futures_with_threadPoolTaskScheduler() {
Span span = this.tracer.nextSpan().name("foo");
log.info("Starting test");
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
String response = this.restTemplate.getForObject(
"http://localhost:" + port() + "/threadPoolTaskScheduler",
String.class);
then(response).isEqualTo(span.context().traceIdString());
Awaitility.await().untilAsserted(() -> {
then(this.asyncTask.getSpan().get()).isNotNull();
then(this.asyncTask.getSpan().get().context().traceId())
.isEqualTo(span.context().traceId());
});
}
finally {
span.finish();
}
then(this.tracer.currentSpan()).isNull();
}
/**
* Related to issue #1232
*/
@Test
public void should_pass_tracing_info_for_completable_futures_with_scheduledThreadPoolExecutor() {
Span span = this.tracer.nextSpan().name("foo");
log.info("Starting test");
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
String response = this.restTemplate.getForObject(
"http://localhost:" + port() + "/scheduledThreadPoolExecutor",
String.class);
then(response).isEqualTo(span.context().traceIdString());
Awaitility.await().untilAsserted(() -> {
then(this.asyncTask.getSpan().get()).isNotNull();
then(this.asyncTask.getSpan().get().context().traceId())
.isEqualTo(span.context().traceId());
});
}
finally {
span.finish();
}
then(this.tracer.currentSpan()).isNull();
}
private int port() {
return this.environment.getProperty("local.server.port", Integer.class);
}
}
@Configuration
@EnableAsync
class AppConfig {
@Bean
public Sampler testSampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public Executor poolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.initialize();
return executor;
}
@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
executor.initialize();
return executor;
}
@Bean
public ScheduledThreadPoolExecutor scheduledThreadPoolExecutor() {
return new ScheduledThreadPoolExecutor(10);
}
}
@Component
class AsyncTask {
private static final Log log = LogFactory.getLog(AsyncTask.class);
@Autowired
Tracer tracer;
@Autowired
@Qualifier("poolTaskExecutor")
Executor executor;
@Autowired
@Qualifier("taskScheduler")
Executor taskScheduler;
@Autowired
BeanFactory beanFactory;
@Autowired
ThreadPoolTaskScheduler threadPoolTaskScheduler;
@Autowired
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor;
private AtomicReference<Span> span = new AtomicReference<>();
@Async("poolTaskExecutor")
public void runWithPool() {
log.info("This task is running with a pool.");
this.span.set(this.tracer.currentSpan());
}
@Async
public void runWithoutPool() {
log.info("This task is running without a pool.");
this.span.set(this.tracer.currentSpan());
}
public Span completableFutures() throws ExecutionException, InterruptedException {
log.info("This task is running with completable future");
CompletableFuture<Span> span1 = CompletableFuture.supplyAsync(() -> {
AsyncTask.log.info("First completable future");
return AsyncTask.this.tracer.currentSpan();
}, AsyncTask.this.executor);
CompletableFuture<Span> span2 = CompletableFuture.supplyAsync(() -> {
AsyncTask.log.info("Second completable future");
return AsyncTask.this.tracer.currentSpan();
}, AsyncTask.this.executor);
CompletableFuture<Span> response = CompletableFuture.allOf(span1, span2)
.thenApply(ignoredVoid -> {
AsyncTask.log.info("Third completable future");
Span joinedSpan1 = span1.join();
Span joinedSpan2 = span2.join();
then(joinedSpan2).isNotNull();
then(joinedSpan1.context().traceId())
.isEqualTo(joinedSpan2.context().traceId());
AsyncTask.log.info("TraceIds are correct");
return joinedSpan2;
});
this.span.set(response.get());
return this.span.get();
}
public Span taskScheduler() throws ExecutionException, InterruptedException {
log.info("This task is running with completable future");
CompletableFuture<Span> span1 = CompletableFuture.supplyAsync(() -> {
AsyncTask.log.info("First completable future");
return AsyncTask.this.tracer.currentSpan();
}, new LazyTraceExecutor(AsyncTask.this.beanFactory,
AsyncTask.this.taskScheduler));
CompletableFuture<Span> span2 = CompletableFuture.supplyAsync(() -> {
AsyncTask.log.info("Second completable future");
return AsyncTask.this.tracer.currentSpan();
}, new LazyTraceExecutor(AsyncTask.this.beanFactory,
AsyncTask.this.taskScheduler));
CompletableFuture<Span> response = CompletableFuture.allOf(span1, span2)
.thenApply(ignoredVoid -> {
AsyncTask.log.info("Third completable future");
Span joinedSpan1 = span1.join();
Span joinedSpan2 = span2.join();
then(joinedSpan2).isNotNull();
then(joinedSpan1.context().traceId())
.isEqualTo(joinedSpan2.context().traceId());
AsyncTask.log.info("TraceIds are correct");
return joinedSpan2;
});
this.span.set(response.get());
return this.span.get();
}
public Span scheduledThreadPoolExecutor()
throws ExecutionException, InterruptedException {
log.info("This task is running with ScheduledThreadPoolExecutor");
this.scheduledThreadPoolExecutor.submit(() -> {
log.info("Hello from runnable");
AsyncTask.this.span.set(AsyncTask.this.tracer.currentSpan());
}).get();
return this.span.get();
}
public Span threadPoolTaskScheduler()
throws ExecutionException, InterruptedException {
log.info("This task is running with ThreadPoolTaskScheduler");
this.threadPoolTaskScheduler.submit(() -> {
log.info("Hello from runnable");
AsyncTask.this.span.set(AsyncTask.this.tracer.currentSpan());
}).get();
return this.span.get();
}
public AtomicReference<Span> getSpan() {
return this.span;
}
}
@SpringBootApplication(exclude = SpringDataWebAutoConfiguration.class)
@RestController
class Application {
private static final Log log = LogFactory.getLog(Application.class);
@Autowired
AsyncTask asyncTask;
@Autowired
Tracer tracer;
@RequestMapping("/with_pool")
public String withPool() {
log.info("Executing with pool.");
this.asyncTask.runWithPool();
return this.tracer.currentSpan().context().traceIdString();
}
@RequestMapping("/without_pool")
public String withoutPool() {
log.info("Executing without pool.");
this.asyncTask.runWithoutPool();
return this.tracer.currentSpan().context().traceIdString();
}
@RequestMapping("/completable")
public String completable() throws ExecutionException, InterruptedException {
log.info("Executing completable");
return this.asyncTask.completableFutures().context().traceIdString();
}
@RequestMapping("/taskScheduler")
public String taskScheduler() throws ExecutionException, InterruptedException {
log.info("Executing completable via task scheduler");
return this.asyncTask.taskScheduler().context().traceIdString();
}
@RequestMapping("/threadPoolTaskScheduler")
public String threadPoolTaskScheduler()
throws ExecutionException, InterruptedException {
log.info("Executing completable via ThreadPoolTaskScheduler");
return this.asyncTask.threadPoolTaskScheduler().context().traceIdString();
}
@RequestMapping("/scheduledThreadPoolExecutor")
public String scheduledThreadPoolExecutor()
throws ExecutionException, InterruptedException {
log.info("Executing completable via ScheduledThreadPoolExecutor");
return this.asyncTask.scheduledThreadPoolExecutor().context().traceIdString();
}
/**
* Related to issue #445.
* @return service bean
*/
@Bean
public MyService executorService() {
return new MyService() {
@Override
public void execute(Runnable command) {
}
};
}
interface MyService extends Executor {
}
}

View File

@@ -1,155 +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.instrument.async.issues.issue546;
import brave.Tracing;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.AsyncRestTemplate;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.BDDAssertions.then;
/**
* @author Marcin Grzejszczak
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Issue546TestsApp.class,
properties = { "ribbon.eureka.enabled=false", "feign.hystrix.enabled=false",
"server.port=0" },
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class Issue546Tests {
@Autowired
Environment environment;
@Test
public void should_pass_tracing_info_when_using_callbacks() {
new RestTemplate().getForObject(
"http://localhost:" + port() + "/trace-async-rest-template",
String.class);
}
private int port() {
return this.environment.getProperty("local.server.port", Integer.class);
}
}
@SpringBootApplication
class Issue546TestsApp {
@Bean
AsyncRestTemplate asyncRestTemplate() {
return new AsyncRestTemplate();
}
}
@RestController
class Controller {
private static final Log log = LogFactory.getLog(Controller.class);
private final AsyncRestTemplate traceAsyncRestTemplate;
private final Tracing tracer;
@Value("${server.port}")
private String port;
Controller(AsyncRestTemplate traceAsyncRestTemplate, Tracing tracer) {
this.traceAsyncRestTemplate = traceAsyncRestTemplate;
this.tracer = tracer;
}
@RequestMapping("/bean")
public HogeBean bean() {
log.info("(/bean) I got a request!");
return new HogeBean("test", 18);
}
@RequestMapping("/trace-async-rest-template")
public void asyncTest(@RequestParam(required = false) boolean isSleep)
throws InterruptedException {
log.info("(/trace-async-rest-template) I got a request!");
final long traceId = this.tracer.tracer().currentSpan().context().traceId();
ListenableFuture<ResponseEntity<HogeBean>> res = this.traceAsyncRestTemplate
.getForEntity("http://localhost:" + this.port + "/bean", HogeBean.class);
if (isSleep) {
Thread.sleep(1000);
}
res.addCallback(success -> {
then(Controller.this.tracer.tracer().currentSpan().context().traceId())
.isEqualTo(traceId);
log.info("(/trace-async-rest-template) success");
then(Controller.this.tracer.tracer().currentSpan().context().traceId())
.isEqualTo(traceId);
}, failure -> {
then(Controller.this.tracer.tracer().currentSpan().context().traceId())
.isEqualTo(traceId);
log.error("(/trace-async-rest-template) failure", failure);
then(Controller.this.tracer.tracer().currentSpan().context().traceId())
.isEqualTo(traceId);
});
}
}
class HogeBean {
private String name;
private int age;
HogeBean(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
}

View File

@@ -1,209 +0,0 @@
/*
* Copyright 2018-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.grpc;
import java.util.List;
import java.util.concurrent.TimeUnit;
import brave.sampler.Sampler;
import io.grpc.ClientInterceptor;
import io.grpc.ManagedChannel;
import io.grpc.ServerBuilder;
import io.grpc.stub.StreamObserver;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.lognet.springboot.grpc.GRpcServerBuilderConfigurer;
import org.lognet.springboot.grpc.GRpcService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.cloud.sleuth.instrument.grpc.stubs.HelloReply;
import org.springframework.cloud.sleuth.instrument.grpc.stubs.HelloRequest;
import org.springframework.cloud.sleuth.instrument.grpc.stubs.HelloServiceGrpc;
import org.springframework.cloud.sleuth.instrument.grpc.stubs.HelloServiceGrpc.HelloServiceBlockingStub;
import org.springframework.cloud.sleuth.instrument.grpc.stubs.HelloServiceGrpc.HelloServiceImplBase;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* This integration testing class starts an in-process gRPC server and calls that server
* via the client.
*
* This class uses stubs and skeletons that were generated originally by the gRPC maven
* plugin and copied into a "stubs" sub-package.
*
* @author Tyler Van Gorder
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = GrpcTracingIntegrationTests.TestConfiguration.class,
webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = { "grpc.enabled=false", "grpc.inProcessServerName=testServer" })
@DirtiesContext
public class GrpcTracingIntegrationTests {
@Autowired
SpringAwareManagedChannelBuilder clientManagedChannelBuilder;
@Autowired
ArrayListSpanReporter reporter;
@Before
public void beforeTest() {
this.reporter.clear();
}
@After
public void afterTest() {
this.reporter.clear();
}
@Test
public void integrationTest() throws Exception {
ManagedChannel inProcessManagedChannel = this.clientManagedChannelBuilder
.inProcessChannelBuilder("testServer").directExecutor().build();
HelloServiceGrpcClient client = new HelloServiceGrpcClient(
inProcessManagedChannel);
assertThat(client.sayHello("Testy McTest Face"))
.isEqualTo("Hello Testy McTest Face");
List<Span> spans = this.reporter.getSpans();
assertThat(spans).hasSize(2);
assertThat(spans.get(0).kind()).isEqualTo(Span.Kind.SERVER);
assertThat(spans.get(1).kind()).isEqualTo(Span.Kind.CLIENT);
// ManagedChannel does not implement Closeable...
inProcessManagedChannel.shutdownNow();
}
@Test
public void channelBuilderFromAddress() {
// Simple test to make sure the interceptor is added to the builder.
this.clientManagedChannelBuilder.forAddress("test", 1234);
@SuppressWarnings("unchecked")
List<ClientInterceptor> clientInterceptors = (List<ClientInterceptor>) ReflectionTestUtils
.getField(this.clientManagedChannelBuilder, "customizers");
assertThat(clientInterceptors).hasSize(1);
}
@Test
public void channelBuilderFromTarget() {
// Simple test to make sure the interceptor is added to the builder.
this.clientManagedChannelBuilder.forTarget("test");
@SuppressWarnings("unchecked")
List<ClientInterceptor> clientInterceptors = (List<ClientInterceptor>) ReflectionTestUtils
.getField(this.clientManagedChannelBuilder, "customizers");
assertThat(clientInterceptors).hasSize(1);
}
public interface HelloServiceClient {
String sayHello(String name) throws Exception;
}
@Configuration
@EnableAutoConfiguration
@Import(HelloGrpcService.class)
public static class TestConfiguration {
@Bean
Sampler alwaysSampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
Reporter<zipkin2.Span> reporter() {
return new ArrayListSpanReporter();
}
@Bean
GRpcServerBuilderConfigurer serverBuilderConfigurer() {
return new TestGrpcConfig();
}
}
public static class TestGrpcConfig extends GRpcServerBuilderConfigurer {
@Override
public void configure(ServerBuilder<?> serverBuilder) {
serverBuilder.directExecutor();
}
}
@GRpcService
public static class HelloGrpcService extends HelloServiceImplBase {
private Logger logger = LoggerFactory.getLogger(HelloGrpcService.class);
@Override
public void sayHello(HelloRequest request,
StreamObserver<HelloReply> responseObserver) {
String message = "Hello " + request.getName();
this.logger.debug("In the grpc server stub.");
HelloReply reply = HelloReply.newBuilder().setMessage(message).build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
}
}
public static class HelloServiceGrpcClient implements HelloServiceClient {
private ManagedChannel managedChannel;
public HelloServiceGrpcClient(ManagedChannel managedChannel) {
this.managedChannel = managedChannel;
}
/*
* (non-Javadoc)
*
* @see sample.HelloServiceClient#sayHello(java.lang.String)
*/
@Override
public String sayHello(String name) throws Exception {
HelloServiceBlockingStub stub = HelloServiceGrpc
.newBlockingStub(this.managedChannel)
.withDeadlineAfter(3, TimeUnit.SECONDS);
HelloReply reply = stub
.sayHello(HelloRequest.newBuilder().setName(name).build());
return reply.getMessage();
}
}
}

View File

@@ -1,588 +0,0 @@
/*
* Copyright 2018-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.grpc.stubs;
/**
* <pre>
* The response message containing the greetings.
* </pre>
*
* Protobuf type {@code HelloReply}
*
* @author Tyler Van Gorder
*/
public final class HelloReply extends com.google.protobuf.GeneratedMessageV3 implements
// @@protoc_insertion_point(message_implements:HelloReply)
HelloReplyOrBuilder {
public static final int MESSAGE_FIELD_NUMBER = 1;
private static final long serialVersionUID = 0L;
// @@protoc_insertion_point(class_scope:HelloReply)
private static final HelloReply DEFAULT_INSTANCE;
private static final com.google.protobuf.Parser<HelloReply> PARSER = new com.google.protobuf.AbstractParser<HelloReply>() {
@Override
public HelloReply parsePartialFrom(com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return new HelloReply(input, extensionRegistry);
}
};
static {
DEFAULT_INSTANCE = new HelloReply();
}
private volatile java.lang.Object message_;
private byte memoizedIsInitialized = -1;
// Use HelloReply.newBuilder() to construct.
private HelloReply(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
super(builder);
}
private HelloReply() {
this.message_ = "";
}
private HelloReply(com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
this();
if (extensionRegistry == null) {
throw new java.lang.NullPointerException();
}
int mutable_bitField0_ = 0;
com.google.protobuf.UnknownFieldSet.Builder unknownFields = com.google.protobuf.UnknownFieldSet
.newBuilder();
try {
boolean done = false;
while (!done) {
int tag = input.readTag();
switch (tag) {
case 0:
done = true;
break;
default:
if (!parseUnknownFieldProto3(input, unknownFields, extensionRegistry,
tag)) {
done = true;
}
break;
case 10:
String s = input.readStringRequireUtf8();
this.message_ = s;
break;
}
}
}
catch (com.google.protobuf.InvalidProtocolBufferException e) {
throw e.setUnfinishedMessage(this);
}
catch (java.io.IOException e) {
throw new com.google.protobuf.InvalidProtocolBufferException(e)
.setUnfinishedMessage(this);
}
finally {
this.unknownFields = unknownFields.build();
makeExtensionsImmutable();
}
}
public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() {
return HelloServiceOuterClass.internal_static_sample_grpc_HelloReply_descriptor;
}
public static HelloReply parseFrom(java.nio.ByteBuffer data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static HelloReply parseFrom(java.nio.ByteBuffer data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static HelloReply parseFrom(com.google.protobuf.ByteString data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static HelloReply parseFrom(com.google.protobuf.ByteString data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static HelloReply parseFrom(byte[] data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static HelloReply parseFrom(byte[] data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static HelloReply parseFrom(java.io.InputStream input)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input);
}
public static HelloReply parseFrom(java.io.InputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input,
extensionRegistry);
}
public static HelloReply parseDelimitedFrom(java.io.InputStream input)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3
.parseDelimitedWithIOException(PARSER, input);
}
public static HelloReply parseDelimitedFrom(java.io.InputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3
.parseDelimitedWithIOException(PARSER, input, extensionRegistry);
}
public static HelloReply parseFrom(com.google.protobuf.CodedInputStream input)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input);
}
public static HelloReply parseFrom(com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input,
extensionRegistry);
}
public static Builder newBuilder() {
return DEFAULT_INSTANCE.toBuilder();
}
public static Builder newBuilder(HelloReply prototype) {
return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
}
public static HelloReply getDefaultInstance() {
return DEFAULT_INSTANCE;
}
public static com.google.protobuf.Parser<HelloReply> parser() {
return PARSER;
}
@java.lang.Override
public final com.google.protobuf.UnknownFieldSet getUnknownFields() {
return this.unknownFields;
}
@Override
protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internalGetFieldAccessorTable() {
return HelloServiceOuterClass.internal_static_sample_grpc_HelloReply_fieldAccessorTable
.ensureFieldAccessorsInitialized(HelloReply.class,
HelloReply.Builder.class);
}
/**
* <code>string message = 1;</code>
*/
@Override
public java.lang.String getMessage() {
java.lang.Object ref = this.message_;
if (ref instanceof java.lang.String) {
return (java.lang.String) ref;
}
else {
com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref;
java.lang.String s = bs.toStringUtf8();
this.message_ = s;
return s;
}
}
/**
* <code>string message = 1;</code>
*/
@Override
public com.google.protobuf.ByteString getMessageBytes() {
java.lang.Object ref = this.message_;
if (ref instanceof java.lang.String) {
com.google.protobuf.ByteString b = com.google.protobuf.ByteString
.copyFromUtf8((java.lang.String) ref);
this.message_ = b;
return b;
}
else {
return (com.google.protobuf.ByteString) ref;
}
}
@Override
public final boolean isInitialized() {
byte isInitialized = this.memoizedIsInitialized;
if (isInitialized == 1) {
return true;
}
if (isInitialized == 0) {
return false;
}
this.memoizedIsInitialized = 1;
return true;
}
@Override
public void writeTo(com.google.protobuf.CodedOutputStream output)
throws java.io.IOException {
if (!getMessageBytes().isEmpty()) {
com.google.protobuf.GeneratedMessageV3.writeString(output, 1, this.message_);
}
this.unknownFields.writeTo(output);
}
@Override
public int getSerializedSize() {
int size = this.memoizedSize;
if (size != -1) {
return size;
}
size = 0;
if (!getMessageBytes().isEmpty()) {
size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1,
this.message_);
}
size += this.unknownFields.getSerializedSize();
this.memoizedSize = size;
return size;
}
@java.lang.Override
public boolean equals(final java.lang.Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof HelloReply)) {
return super.equals(obj);
}
HelloReply other = (HelloReply) obj;
boolean result = true;
result = result && getMessage().equals(other.getMessage());
result = result && this.unknownFields.equals(other.unknownFields);
return result;
}
@java.lang.Override
public int hashCode() {
if (this.memoizedHashCode != 0) {
return this.memoizedHashCode;
}
int hash = 41;
hash = (19 * hash) + getDescriptor().hashCode();
hash = (37 * hash) + MESSAGE_FIELD_NUMBER;
hash = (53 * hash) + getMessage().hashCode();
hash = (29 * hash) + this.unknownFields.hashCode();
this.memoizedHashCode = hash;
return hash;
}
@Override
public Builder newBuilderForType() {
return newBuilder();
}
@Override
public Builder toBuilder() {
return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this);
}
@java.lang.Override
protected Builder newBuilderForType(
com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
Builder builder = new Builder(parent);
return builder;
}
@java.lang.Override
public com.google.protobuf.Parser<HelloReply> getParserForType() {
return PARSER;
}
@Override
public HelloReply getDefaultInstanceForType() {
return DEFAULT_INSTANCE;
}
/**
* <pre>
* The response message containing the greetings
* </pre>
*
* Protobuf type {@code HelloReply}
*/
public static final class Builder
extends com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
// @@protoc_insertion_point(builder_implements:HelloReply)
HelloReplyOrBuilder {
private java.lang.Object message_ = "";
// Construct using HelloReply.newBuilder()
private Builder() {
maybeForceBuilderInitialization();
}
private Builder(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
super(parent);
maybeForceBuilderInitialization();
}
public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() {
return HelloServiceOuterClass.internal_static_sample_grpc_HelloReply_descriptor;
}
@Override
protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internalGetFieldAccessorTable() {
return HelloServiceOuterClass.internal_static_sample_grpc_HelloReply_fieldAccessorTable
.ensureFieldAccessorsInitialized(HelloReply.class,
HelloReply.Builder.class);
}
private void maybeForceBuilderInitialization() {
if (com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders) {
}
}
@Override
public Builder clear() {
super.clear();
this.message_ = "";
return this;
}
@Override
public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() {
return HelloServiceOuterClass.internal_static_sample_grpc_HelloReply_descriptor;
}
@Override
public HelloReply getDefaultInstanceForType() {
return HelloReply.getDefaultInstance();
}
@Override
public HelloReply build() {
HelloReply result = buildPartial();
if (!result.isInitialized()) {
throw newUninitializedMessageException(result);
}
return result;
}
@Override
public HelloReply buildPartial() {
HelloReply result = new HelloReply(this);
result.message_ = this.message_;
onBuilt();
return result;
}
@Override
public Builder clone() {
return super.clone();
}
@Override
public Builder setField(com.google.protobuf.Descriptors.FieldDescriptor field,
java.lang.Object value) {
return super.setField(field, value);
}
@Override
public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) {
return super.clearField(field);
}
@Override
public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) {
return super.clearOneof(oneof);
}
@Override
public Builder setRepeatedField(
com.google.protobuf.Descriptors.FieldDescriptor field, int index,
java.lang.Object value) {
return super.setRepeatedField(field, index, value);
}
@Override
public Builder addRepeatedField(
com.google.protobuf.Descriptors.FieldDescriptor field,
java.lang.Object value) {
return super.addRepeatedField(field, value);
}
@Override
public Builder mergeFrom(com.google.protobuf.Message other) {
if (other instanceof HelloReply) {
return mergeFrom((HelloReply) other);
}
else {
super.mergeFrom(other);
return this;
}
}
public Builder mergeFrom(HelloReply other) {
if (other == HelloReply.getDefaultInstance()) {
return this;
}
if (!other.getMessage().isEmpty()) {
this.message_ = other.message_;
onChanged();
}
this.mergeUnknownFields(other.unknownFields);
onChanged();
return this;
}
@Override
public final boolean isInitialized() {
return true;
}
@Override
public Builder mergeFrom(com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
HelloReply parsedMessage = null;
try {
parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
}
catch (com.google.protobuf.InvalidProtocolBufferException e) {
parsedMessage = (HelloReply) e.getUnfinishedMessage();
throw e.unwrapIOException();
}
finally {
if (parsedMessage != null) {
mergeFrom(parsedMessage);
}
}
return this;
}
/**
* <code>string message = 1;</code>
*/
@Override
public java.lang.String getMessage() {
java.lang.Object ref = this.message_;
if (!(ref instanceof java.lang.String)) {
com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref;
java.lang.String s = bs.toStringUtf8();
this.message_ = s;
return s;
}
else {
return (java.lang.String) ref;
}
}
/**
* <code>string message = 1;</code>
*/
public Builder setMessage(java.lang.String value) {
if (value == null) {
throw new NullPointerException();
}
this.message_ = value;
onChanged();
return this;
}
/**
* <code>string message = 1;</code>
*/
@Override
public com.google.protobuf.ByteString getMessageBytes() {
java.lang.Object ref = this.message_;
if (ref instanceof String) {
com.google.protobuf.ByteString b = com.google.protobuf.ByteString
.copyFromUtf8((java.lang.String) ref);
this.message_ = b;
return b;
}
else {
return (com.google.protobuf.ByteString) ref;
}
}
/**
* <code>string message = 1;</code>
*/
public Builder setMessageBytes(com.google.protobuf.ByteString value) {
if (value == null) {
throw new NullPointerException();
}
checkByteStringIsUtf8(value);
this.message_ = value;
onChanged();
return this;
}
/**
* <code>string message = 1;</code>
*/
public Builder clearMessage() {
this.message_ = getDefaultInstance().getMessage();
onChanged();
return this;
}
@Override
public final Builder setUnknownFields(
final com.google.protobuf.UnknownFieldSet unknownFields) {
return super.setUnknownFieldsProto3(unknownFields);
}
@Override
public final Builder mergeUnknownFields(
final com.google.protobuf.UnknownFieldSet unknownFields) {
return super.mergeUnknownFields(unknownFields);
}
// @@protoc_insertion_point(builder_scope:HelloReply)
}
}

View File

@@ -1,33 +0,0 @@
/*
* Copyright 2018-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.grpc.stubs;
public interface HelloReplyOrBuilder extends
// @@protoc_insertion_point(interface_extends:sample.grpc.HelloReply)
com.google.protobuf.MessageOrBuilder {
/**
* <code>string message = 1;</code>
*/
java.lang.String getMessage();
/**
* <code>string message = 1;</code>
*/
com.google.protobuf.ByteString getMessageBytes();
}

View File

@@ -1,587 +0,0 @@
/*
* Copyright 2018-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.grpc.stubs;
/**
* <pre>
* The request message containing the user's name.
* </pre>
*
* Protobuf type {@code HelloRequest}
*/
public final class HelloRequest extends com.google.protobuf.GeneratedMessageV3 implements
// @@protoc_insertion_point(message_implements:HelloRequest)
HelloRequestOrBuilder {
public static final int NAME_FIELD_NUMBER = 1;
private static final long serialVersionUID = 0L;
// @@protoc_insertion_point(class_scope:HelloRequest)
private static final HelloRequest DEFAULT_INSTANCE;
private static final com.google.protobuf.Parser<HelloRequest> PARSER = new com.google.protobuf.AbstractParser<HelloRequest>() {
@Override
public HelloRequest parsePartialFrom(com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return new HelloRequest(input, extensionRegistry);
}
};
static {
DEFAULT_INSTANCE = new HelloRequest();
}
private volatile java.lang.Object name_;
private byte memoizedIsInitialized = -1;
// Use HelloRequest.newBuilder() to construct.
private HelloRequest(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
super(builder);
}
private HelloRequest() {
this.name_ = "";
}
private HelloRequest(com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
this();
if (extensionRegistry == null) {
throw new java.lang.NullPointerException();
}
int mutable_bitField0_ = 0;
com.google.protobuf.UnknownFieldSet.Builder unknownFields = com.google.protobuf.UnknownFieldSet
.newBuilder();
try {
boolean done = false;
while (!done) {
int tag = input.readTag();
switch (tag) {
case 0:
done = true;
break;
default:
if (!parseUnknownFieldProto3(input, unknownFields, extensionRegistry,
tag)) {
done = true;
}
break;
case 10:
String s = input.readStringRequireUtf8();
this.name_ = s;
break;
}
}
}
catch (com.google.protobuf.InvalidProtocolBufferException e) {
throw e.setUnfinishedMessage(this);
}
catch (java.io.IOException e) {
throw new com.google.protobuf.InvalidProtocolBufferException(e)
.setUnfinishedMessage(this);
}
finally {
this.unknownFields = unknownFields.build();
makeExtensionsImmutable();
}
}
public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() {
return HelloServiceOuterClass.internal_static_sample_grpc_HelloRequest_descriptor;
}
public static HelloRequest parseFrom(java.nio.ByteBuffer data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static HelloRequest parseFrom(java.nio.ByteBuffer data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static HelloRequest parseFrom(com.google.protobuf.ByteString data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static HelloRequest parseFrom(com.google.protobuf.ByteString data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static HelloRequest parseFrom(byte[] data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static HelloRequest parseFrom(byte[] data,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static HelloRequest parseFrom(java.io.InputStream input)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input);
}
public static HelloRequest parseFrom(java.io.InputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input,
extensionRegistry);
}
public static HelloRequest parseDelimitedFrom(java.io.InputStream input)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3
.parseDelimitedWithIOException(PARSER, input);
}
public static HelloRequest parseDelimitedFrom(java.io.InputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3
.parseDelimitedWithIOException(PARSER, input, extensionRegistry);
}
public static HelloRequest parseFrom(com.google.protobuf.CodedInputStream input)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input);
}
public static HelloRequest parseFrom(com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input,
extensionRegistry);
}
public static Builder newBuilder() {
return DEFAULT_INSTANCE.toBuilder();
}
public static Builder newBuilder(HelloRequest prototype) {
return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
}
public static HelloRequest getDefaultInstance() {
return DEFAULT_INSTANCE;
}
public static com.google.protobuf.Parser<HelloRequest> parser() {
return PARSER;
}
@java.lang.Override
public final com.google.protobuf.UnknownFieldSet getUnknownFields() {
return this.unknownFields;
}
@Override
protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internalGetFieldAccessorTable() {
return HelloServiceOuterClass.internal_static_sample_grpc_HelloRequest_fieldAccessorTable
.ensureFieldAccessorsInitialized(HelloRequest.class,
HelloRequest.Builder.class);
}
/**
* <code>string name = 1;</code>
*/
@Override
public java.lang.String getName() {
java.lang.Object ref = this.name_;
if (ref instanceof java.lang.String) {
return (java.lang.String) ref;
}
else {
com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref;
java.lang.String s = bs.toStringUtf8();
this.name_ = s;
return s;
}
}
/**
* <code>string name = 1;</code>
*/
@Override
public com.google.protobuf.ByteString getNameBytes() {
java.lang.Object ref = this.name_;
if (ref instanceof java.lang.String) {
com.google.protobuf.ByteString b = com.google.protobuf.ByteString
.copyFromUtf8((java.lang.String) ref);
this.name_ = b;
return b;
}
else {
return (com.google.protobuf.ByteString) ref;
}
}
@Override
public final boolean isInitialized() {
byte isInitialized = this.memoizedIsInitialized;
if (isInitialized == 1) {
return true;
}
if (isInitialized == 0) {
return false;
}
this.memoizedIsInitialized = 1;
return true;
}
@Override
public void writeTo(com.google.protobuf.CodedOutputStream output)
throws java.io.IOException {
if (!getNameBytes().isEmpty()) {
com.google.protobuf.GeneratedMessageV3.writeString(output, 1, this.name_);
}
this.unknownFields.writeTo(output);
}
@Override
public int getSerializedSize() {
int size = this.memoizedSize;
if (size != -1) {
return size;
}
size = 0;
if (!getNameBytes().isEmpty()) {
size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1,
this.name_);
}
size += this.unknownFields.getSerializedSize();
this.memoizedSize = size;
return size;
}
@java.lang.Override
public boolean equals(final java.lang.Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof HelloRequest)) {
return super.equals(obj);
}
HelloRequest other = (HelloRequest) obj;
boolean result = true;
result = result && getName().equals(other.getName());
result = result && this.unknownFields.equals(other.unknownFields);
return result;
}
@java.lang.Override
public int hashCode() {
if (this.memoizedHashCode != 0) {
return this.memoizedHashCode;
}
int hash = 41;
hash = (19 * hash) + getDescriptor().hashCode();
hash = (37 * hash) + NAME_FIELD_NUMBER;
hash = (53 * hash) + getName().hashCode();
hash = (29 * hash) + this.unknownFields.hashCode();
this.memoizedHashCode = hash;
return hash;
}
@Override
public Builder newBuilderForType() {
return newBuilder();
}
@Override
public Builder toBuilder() {
return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this);
}
@java.lang.Override
protected Builder newBuilderForType(
com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
Builder builder = new Builder(parent);
return builder;
}
@java.lang.Override
public com.google.protobuf.Parser<HelloRequest> getParserForType() {
return PARSER;
}
@Override
public HelloRequest getDefaultInstanceForType() {
return DEFAULT_INSTANCE;
}
/**
* <pre>
* The request message containing the user's name.
* </pre>
*
* Protobuf type {@code HelloRequest}
*/
public static final class Builder
extends com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
// @@protoc_insertion_point(builder_implements:HelloRequest)
HelloRequestOrBuilder {
private java.lang.Object name_ = "";
// Construct using HelloRequest.newBuilder()
private Builder() {
maybeForceBuilderInitialization();
}
private Builder(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
super(parent);
maybeForceBuilderInitialization();
}
public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() {
return HelloServiceOuterClass.internal_static_sample_grpc_HelloRequest_descriptor;
}
@Override
protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internalGetFieldAccessorTable() {
return HelloServiceOuterClass.internal_static_sample_grpc_HelloRequest_fieldAccessorTable
.ensureFieldAccessorsInitialized(HelloRequest.class,
HelloRequest.Builder.class);
}
private void maybeForceBuilderInitialization() {
if (com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders) {
}
}
@Override
public Builder clear() {
super.clear();
this.name_ = "";
return this;
}
@Override
public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() {
return HelloServiceOuterClass.internal_static_sample_grpc_HelloRequest_descriptor;
}
@Override
public HelloRequest getDefaultInstanceForType() {
return HelloRequest.getDefaultInstance();
}
@Override
public HelloRequest build() {
HelloRequest result = buildPartial();
if (!result.isInitialized()) {
throw newUninitializedMessageException(result);
}
return result;
}
@Override
public HelloRequest buildPartial() {
HelloRequest result = new HelloRequest(this);
result.name_ = this.name_;
onBuilt();
return result;
}
@Override
public Builder clone() {
return super.clone();
}
@Override
public Builder setField(com.google.protobuf.Descriptors.FieldDescriptor field,
java.lang.Object value) {
return super.setField(field, value);
}
@Override
public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) {
return super.clearField(field);
}
@Override
public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) {
return super.clearOneof(oneof);
}
@Override
public Builder setRepeatedField(
com.google.protobuf.Descriptors.FieldDescriptor field, int index,
java.lang.Object value) {
return super.setRepeatedField(field, index, value);
}
@Override
public Builder addRepeatedField(
com.google.protobuf.Descriptors.FieldDescriptor field,
java.lang.Object value) {
return super.addRepeatedField(field, value);
}
@Override
public Builder mergeFrom(com.google.protobuf.Message other) {
if (other instanceof HelloRequest) {
return mergeFrom((HelloRequest) other);
}
else {
super.mergeFrom(other);
return this;
}
}
public Builder mergeFrom(HelloRequest other) {
if (other == HelloRequest.getDefaultInstance()) {
return this;
}
if (!other.getName().isEmpty()) {
this.name_ = other.name_;
onChanged();
}
this.mergeUnknownFields(other.unknownFields);
onChanged();
return this;
}
@Override
public final boolean isInitialized() {
return true;
}
@Override
public Builder mergeFrom(com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
HelloRequest parsedMessage = null;
try {
parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
}
catch (com.google.protobuf.InvalidProtocolBufferException e) {
parsedMessage = (HelloRequest) e.getUnfinishedMessage();
throw e.unwrapIOException();
}
finally {
if (parsedMessage != null) {
mergeFrom(parsedMessage);
}
}
return this;
}
/**
* <code>string name = 1;</code>
*/
@Override
public java.lang.String getName() {
java.lang.Object ref = this.name_;
if (!(ref instanceof java.lang.String)) {
com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref;
java.lang.String s = bs.toStringUtf8();
this.name_ = s;
return s;
}
else {
return (java.lang.String) ref;
}
}
/**
* <code>string name = 1;</code>
*/
public Builder setName(java.lang.String value) {
if (value == null) {
throw new NullPointerException();
}
this.name_ = value;
onChanged();
return this;
}
/**
* <code>string name = 1;</code>
*/
@Override
public com.google.protobuf.ByteString getNameBytes() {
java.lang.Object ref = this.name_;
if (ref instanceof String) {
com.google.protobuf.ByteString b = com.google.protobuf.ByteString
.copyFromUtf8((java.lang.String) ref);
this.name_ = b;
return b;
}
else {
return (com.google.protobuf.ByteString) ref;
}
}
/**
* <code>string name = 1;</code>
*/
public Builder setNameBytes(com.google.protobuf.ByteString value) {
if (value == null) {
throw new NullPointerException();
}
checkByteStringIsUtf8(value);
this.name_ = value;
onChanged();
return this;
}
/**
* <code>string name = 1;</code>
*/
public Builder clearName() {
this.name_ = getDefaultInstance().getName();
onChanged();
return this;
}
@Override
public final Builder setUnknownFields(
final com.google.protobuf.UnknownFieldSet unknownFields) {
return super.setUnknownFieldsProto3(unknownFields);
}
@Override
public final Builder mergeUnknownFields(
final com.google.protobuf.UnknownFieldSet unknownFields) {
return super.mergeUnknownFields(unknownFields);
}
// @@protoc_insertion_point(builder_scope:HelloRequest)
}
}

View File

@@ -1,33 +0,0 @@
/*
* Copyright 2018-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.grpc.stubs;
public interface HelloRequestOrBuilder extends
// @@protoc_insertion_point(interface_extends:sample.grpc.HelloRequest)
com.google.protobuf.MessageOrBuilder {
/**
* <code>string name = 1;</code>
*/
java.lang.String getName();
/**
* <code>string name = 1;</code>
*/
com.google.protobuf.ByteString getNameBytes();
}

View File

@@ -1,332 +0,0 @@
/*
* Copyright 2018-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.grpc.stubs;
import static io.grpc.MethodDescriptor.generateFullMethodName;
import static io.grpc.stub.ClientCalls.asyncUnaryCall;
import static io.grpc.stub.ClientCalls.blockingUnaryCall;
import static io.grpc.stub.ClientCalls.futureUnaryCall;
import static io.grpc.stub.ServerCalls.asyncUnaryCall;
import static io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall;
/**
* <pre>
* The Hello service definition.
* </pre>
*/
@javax.annotation.Generated(value = "by gRPC proto compiler (version 1.15.1)",
comments = "Source: HelloService.proto")
public final class HelloServiceGrpc {
public static final String SERVICE_NAME = "HelloService";
private static final int METHODID_SAY_HELLO = 0;
// Static method descriptors that strictly reflect the proto.
private static volatile io.grpc.MethodDescriptor<HelloRequest, HelloReply> getSayHelloMethod;
private static volatile io.grpc.ServiceDescriptor serviceDescriptor;
private HelloServiceGrpc() {
}
@io.grpc.stub.annotations.RpcMethod(fullMethodName = SERVICE_NAME + '/' + "SayHello",
requestType = HelloRequest.class, responseType = HelloReply.class,
methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
public static io.grpc.MethodDescriptor<HelloRequest, HelloReply> getSayHelloMethod() {
io.grpc.MethodDescriptor<HelloRequest, HelloReply> getSayHelloMethod;
if ((getSayHelloMethod = HelloServiceGrpc.getSayHelloMethod) == null) {
synchronized (HelloServiceGrpc.class) {
if ((getSayHelloMethod = HelloServiceGrpc.getSayHelloMethod) == null) {
HelloServiceGrpc.getSayHelloMethod = getSayHelloMethod = io.grpc.MethodDescriptor
.<HelloRequest, HelloReply>newBuilder()
.setType(io.grpc.MethodDescriptor.MethodType.UNARY)
.setFullMethodName(
generateFullMethodName("HelloService", "SayHello"))
.setSampledToLocalTracing(true)
.setRequestMarshaller(io.grpc.protobuf.ProtoUtils
.marshaller(HelloRequest.getDefaultInstance()))
.setResponseMarshaller(io.grpc.protobuf.ProtoUtils
.marshaller(HelloReply.getDefaultInstance()))
.setSchemaDescriptor(
new HelloServiceMethodDescriptorSupplier("SayHello"))
.build();
}
}
}
return getSayHelloMethod;
}
/**
* Creates a new async stub that supports all call types for the service
*/
public static HelloServiceStub newStub(io.grpc.Channel channel) {
return new HelloServiceStub(channel);
}
/**
* Creates a new blocking-style stub that supports unary and streaming output calls on
* the service
*/
public static HelloServiceBlockingStub newBlockingStub(io.grpc.Channel channel) {
return new HelloServiceBlockingStub(channel);
}
/**
* Creates a new ListenableFuture-style stub that supports unary calls on the service
*/
public static HelloServiceFutureStub newFutureStub(io.grpc.Channel channel) {
return new HelloServiceFutureStub(channel);
}
public static io.grpc.ServiceDescriptor getServiceDescriptor() {
io.grpc.ServiceDescriptor result = serviceDescriptor;
if (result == null) {
synchronized (HelloServiceGrpc.class) {
result = serviceDescriptor;
if (result == null) {
serviceDescriptor = result = io.grpc.ServiceDescriptor
.newBuilder(SERVICE_NAME)
.setSchemaDescriptor(new HelloServiceFileDescriptorSupplier())
.addMethod(getSayHelloMethod()).build();
}
}
}
return result;
}
/**
* <pre>
* The Hello service definition.
* </pre>
*/
public static abstract class HelloServiceImplBase implements io.grpc.BindableService {
/**
* <pre>
* Sends a greeting
* </pre>
*/
public void sayHello(HelloRequest request,
io.grpc.stub.StreamObserver<HelloReply> responseObserver) {
asyncUnimplementedUnaryCall(getSayHelloMethod(), responseObserver);
}
@java.lang.Override
public final io.grpc.ServerServiceDefinition bindService() {
return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
.addMethod(getSayHelloMethod(),
asyncUnaryCall(new MethodHandlers<HelloRequest, HelloReply>(
this, METHODID_SAY_HELLO)))
.build();
}
}
/**
* <pre>
* The Hello service definition.
* </pre>
*/
public static final class HelloServiceStub
extends io.grpc.stub.AbstractStub<HelloServiceStub> {
private HelloServiceStub(io.grpc.Channel channel) {
super(channel);
}
private HelloServiceStub(io.grpc.Channel channel,
io.grpc.CallOptions callOptions) {
super(channel, callOptions);
}
@java.lang.Override
protected HelloServiceStub build(io.grpc.Channel channel,
io.grpc.CallOptions callOptions) {
return new HelloServiceStub(channel, callOptions);
}
/**
* <pre>
* Sends a greeting
* </pre>
*/
public void sayHello(HelloRequest request,
io.grpc.stub.StreamObserver<HelloReply> responseObserver) {
asyncUnaryCall(getChannel().newCall(getSayHelloMethod(), getCallOptions()),
request, responseObserver);
}
}
/**
* <pre>
* The Hello service definition.
* </pre>
*/
public static final class HelloServiceBlockingStub
extends io.grpc.stub.AbstractStub<HelloServiceBlockingStub> {
private HelloServiceBlockingStub(io.grpc.Channel channel) {
super(channel);
}
private HelloServiceBlockingStub(io.grpc.Channel channel,
io.grpc.CallOptions callOptions) {
super(channel, callOptions);
}
@java.lang.Override
protected HelloServiceBlockingStub build(io.grpc.Channel channel,
io.grpc.CallOptions callOptions) {
return new HelloServiceBlockingStub(channel, callOptions);
}
/**
* <pre>
* Sends a greeting
* </pre>
*/
public HelloReply sayHello(HelloRequest request) {
return blockingUnaryCall(getChannel(), getSayHelloMethod(), getCallOptions(),
request);
}
}
/**
* <pre>
* The Hello service definition.
* </pre>
*/
public static final class HelloServiceFutureStub
extends io.grpc.stub.AbstractStub<HelloServiceFutureStub> {
private HelloServiceFutureStub(io.grpc.Channel channel) {
super(channel);
}
private HelloServiceFutureStub(io.grpc.Channel channel,
io.grpc.CallOptions callOptions) {
super(channel, callOptions);
}
@java.lang.Override
protected HelloServiceFutureStub build(io.grpc.Channel channel,
io.grpc.CallOptions callOptions) {
return new HelloServiceFutureStub(channel, callOptions);
}
/**
* <pre>
* Sends a greeting
* </pre>
*/
public com.google.common.util.concurrent.ListenableFuture<HelloReply> sayHello(
HelloRequest request) {
return futureUnaryCall(
getChannel().newCall(getSayHelloMethod(), getCallOptions()), request);
}
}
private static final class MethodHandlers<Req, Resp>
implements io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,
io.grpc.stub.ServerCalls.ServerStreamingMethod<Req, Resp>,
io.grpc.stub.ServerCalls.ClientStreamingMethod<Req, Resp>,
io.grpc.stub.ServerCalls.BidiStreamingMethod<Req, Resp> {
private final HelloServiceImplBase serviceImpl;
private final int methodId;
MethodHandlers(HelloServiceImplBase serviceImpl, int methodId) {
this.serviceImpl = serviceImpl;
this.methodId = methodId;
}
@java.lang.Override
@java.lang.SuppressWarnings("unchecked")
public void invoke(Req request,
io.grpc.stub.StreamObserver<Resp> responseObserver) {
switch (this.methodId) {
case METHODID_SAY_HELLO:
this.serviceImpl.sayHello((HelloRequest) request,
(io.grpc.stub.StreamObserver<HelloReply>) responseObserver);
break;
default:
throw new AssertionError();
}
}
@java.lang.Override
@java.lang.SuppressWarnings("unchecked")
public io.grpc.stub.StreamObserver<Req> invoke(
io.grpc.stub.StreamObserver<Resp> responseObserver) {
switch (this.methodId) {
default:
throw new AssertionError();
}
}
}
private static abstract class HelloServiceBaseDescriptorSupplier
implements io.grpc.protobuf.ProtoFileDescriptorSupplier,
io.grpc.protobuf.ProtoServiceDescriptorSupplier {
HelloServiceBaseDescriptorSupplier() {
}
@java.lang.Override
public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() {
return HelloServiceOuterClass.getDescriptor();
}
@java.lang.Override
public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() {
return getFileDescriptor().findServiceByName("HelloService");
}
}
private static final class HelloServiceFileDescriptorSupplier
extends HelloServiceBaseDescriptorSupplier {
HelloServiceFileDescriptorSupplier() {
}
}
private static final class HelloServiceMethodDescriptorSupplier
extends HelloServiceBaseDescriptorSupplier
implements io.grpc.protobuf.ProtoMethodDescriptorSupplier {
private final String methodName;
HelloServiceMethodDescriptorSupplier(String methodName) {
this.methodName = methodName;
}
@java.lang.Override
public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() {
return getServiceDescriptor().findMethodByName(this.methodName);
}
}
}

View File

@@ -1,76 +0,0 @@
/*
* Copyright 2018-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.grpc.stubs;
public final class HelloServiceOuterClass {
static final com.google.protobuf.Descriptors.Descriptor internal_static_sample_grpc_HelloRequest_descriptor;
static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_sample_grpc_HelloRequest_fieldAccessorTable;
static final com.google.protobuf.Descriptors.Descriptor internal_static_sample_grpc_HelloReply_descriptor;
static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_sample_grpc_HelloReply_fieldAccessorTable;
private static com.google.protobuf.Descriptors.FileDescriptor descriptor;
static {
java.lang.String[] descriptorData = {
"\n\022HelloService.proto\022\013sample.grpc\"\034\n\014Hel"
+ "loRequest\022\014\n\004name\030\001 \001(\t\"\035\n\nHelloReply\022\017\n"
+ "\007message\030\001 \001(\t2P\n\014HelloService\022@\n\010SayHel"
+ "lo\022\031.sample.grpc.HelloRequest\032\027.sample.g"
+ "rpc.HelloReply\"\000B\002P\001b\006proto3" };
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
@Override
public com.google.protobuf.ExtensionRegistry assignDescriptors(
com.google.protobuf.Descriptors.FileDescriptor root) {
descriptor = root;
return null;
}
};
com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom(
descriptorData, new com.google.protobuf.Descriptors.FileDescriptor[] {},
assigner);
internal_static_sample_grpc_HelloRequest_descriptor = getDescriptor()
.getMessageTypes().get(0);
internal_static_sample_grpc_HelloRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
internal_static_sample_grpc_HelloRequest_descriptor,
new java.lang.String[] { "Name", });
internal_static_sample_grpc_HelloReply_descriptor = getDescriptor()
.getMessageTypes().get(1);
internal_static_sample_grpc_HelloReply_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
internal_static_sample_grpc_HelloReply_descriptor,
new java.lang.String[] { "Message", });
}
private HelloServiceOuterClass() {
}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistryLite registry) {
}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistry registry) {
registerAllExtensions((com.google.protobuf.ExtensionRegistryLite) registry);
}
public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() {
return descriptor;
}
// @@protoc_insertion_point(outer_class_scope)
}

View File

@@ -1,125 +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.instrument.hystrix;
import java.util.concurrent.atomic.AtomicReference;
import brave.Span;
import brave.Tracing;
import brave.sampler.Sampler;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.strategy.HystrixPlugins;
import org.awaitility.Awaitility;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.sleuth.instrument.DefaultTestAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.BDDAssertions.then;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { HystrixAnnotationsIntegrationTests.TestConfig.class })
@DirtiesContext
public class HystrixAnnotationsIntegrationTests {
@Autowired
HystrixCommandInvocationSpanCatcher catcher;
@Autowired
Tracing tracer;
@BeforeClass
@AfterClass
public static void reset() {
HystrixPlugins.reset();
}
@Test
public void should_create_new_span_with_thread_name_when_executed_a_hystrix_command_annotated_method() {
whenHystrixCommandAnnotatedMethodGetsExecuted();
thenSpanInHystrixThreadIsCreated();
}
private void whenHystrixCommandAnnotatedMethodGetsExecuted() {
this.catcher.invokeLogicWrappedInHystrixCommand();
}
private void thenSpanInHystrixThreadIsCreated() {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
then(HystrixAnnotationsIntegrationTests.this.catcher.getSpan()).isNotNull();
});
}
@DefaultTestAutoConfiguration
@EnableHystrix
@Configuration
static class TestConfig {
@Bean
HystrixCommandInvocationSpanCatcher spanCatcher(Tracing tracing) {
return new HystrixCommandInvocationSpanCatcher(tracing);
}
@Bean
Sampler sampler() {
return Sampler.ALWAYS_SAMPLE;
}
}
public static class HystrixCommandInvocationSpanCatcher {
private final Tracing tracing;
AtomicReference<Span> spanCaughtFromHystrixThread;
public HystrixCommandInvocationSpanCatcher(Tracing tracing) {
this.tracing = tracing;
}
@HystrixCommand
public void invokeLogicWrappedInHystrixCommand() {
this.spanCaughtFromHystrixThread = new AtomicReference<>(
this.tracing.tracer().currentSpan());
}
public Long getTraceId() {
if (this.spanCaughtFromHystrixThread == null
|| this.spanCaughtFromHystrixThread.get() == null) {
return null;
}
return this.spanCaughtFromHystrixThread.get().context().traceId();
}
public Span getSpan() {
return this.spanCaughtFromHystrixThread.get();
}
}
}

View File

@@ -1,192 +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.instrument.messaging;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.annotation.PreDestroy;
import brave.Span;
import brave.Tracer;
import brave.Tracing;
import brave.propagation.StrictScopeDecorator;
import brave.propagation.ThreadLocalCurrentTraceContext;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.integration.channel.DirectChannel;
import org.springframework.integration.channel.ExecutorChannel;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
/**
* Ported from
* org.springframework.cloud.sleuth.instrument.messaging.TraceChannelInterceptorTest to
* allow sleuth to decommission its implementation.
*
* @author Marcin Grzejszczak
*/
@SpringBootTest(classes = ITTracingChannelInterceptorTests.App.class,
webEnvironment = WebEnvironment.NONE)
@RunWith(SpringRunner.class)
@DirtiesContext
public class ITTracingChannelInterceptorTests implements MessageHandler {
@Autowired
@Qualifier("directChannel")
DirectChannel directChannel;
@Autowired
@Qualifier("executorChannel")
ExecutorChannel executorChannel;
@Autowired
Tracer tracer;
@Autowired
List<zipkin2.Span> spans;
@Autowired
MessagingTemplate messagingTemplate;
Message<?> message;
Span currentSpan;
@Override
public void handleMessage(Message<?> msg) {
this.message = msg;
this.currentSpan = this.tracer.currentSpan();
if (this.message.getHeaders().containsKey("THROW_EXCEPTION")) {
throw new RuntimeException("A terrible exception has occurred");
}
}
@Before
public void init() {
this.directChannel.subscribe(this);
this.executorChannel.subscribe(this);
}
@After
public void close() {
this.directChannel.unsubscribe(this);
this.executorChannel.unsubscribe(this);
}
// formerly known as TraceChannelInterceptorTest.executableSpanCreation
@Test
public void propagatesNoopSpan() {
this.directChannel.send(
MessageBuilder.withPayload("hi").setHeader("X-B3-Sampled", "0").build());
assertThat(this.message.getHeaders()).containsEntry("X-B3-Sampled", "0");
assertThat(this.currentSpan.isNoop()).isTrue();
}
@Test
public void messageHeadersStillMutableForStomp() {
this.directChannel.send(MessageBuilder.withPayload("hi")
.setHeader("stompCommand", "DISCONNECT").build());
assertThat(MessageHeaderAccessor.getAccessor(this.message,
MessageHeaderAccessor.class)).isNotNull();
this.message = null;
this.directChannel.send(MessageBuilder.withPayload("hi")
.setHeader("simpMessageType", "sth").build());
assertThat(MessageHeaderAccessor.getAccessor(this.message,
MessageHeaderAccessor.class)).isNotNull();
}
@Test
public void messageHeadersImmutableForNonStomp() {
this.directChannel
.send(MessageBuilder.withPayload("hi").setHeader("foo", "bar").build());
assertThat(MessageHeaderAccessor.getAccessor(this.message,
MessageHeaderAccessor.class)).isNull();
}
@Configuration
@EnableAutoConfiguration
static class App {
ExecutorService service = Executors.newSingleThreadExecutor();
@Bean
List<zipkin2.Span> spans() {
return new ArrayList<>();
}
@Bean
Tracing tracing() {
return Tracing.newBuilder()
.currentTraceContext(ThreadLocalCurrentTraceContext.newBuilder()
.addScopeDecorator(StrictScopeDecorator.create()).build())
.spanReporter(spans()::add).build();
}
@Bean
Tracer tracer() {
return tracing().tracer();
}
@Bean
ExecutorChannel executorChannel() {
return new ExecutorChannel(this.service);
}
@PreDestroy
public void destroy() {
this.service.shutdown();
}
@Bean
DirectChannel directChannel() {
return new DirectChannel();
}
@Bean
public MessagingTemplate messagingTemplate() {
return new MessagingTemplate(directChannel());
}
}
}

View File

@@ -1,364 +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.instrument.messaging;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.MessageListener;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.XAConnection;
import javax.jms.XAConnectionFactory;
import javax.resource.spi.ResourceAdapter;
import brave.Tracing;
import brave.internal.HexCodec;
import brave.propagation.CurrentTraceContext;
import brave.propagation.TraceContext;
import org.apache.activemq.ra.ActiveMQActivationSpec;
import org.apache.activemq.ra.ActiveMQResourceAdapter;
import org.junit.Test;
import zipkin2.Annotation;
import zipkin2.Span;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration;
import org.springframework.boot.jms.XAConnectionFactoryWrapper;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.gateway.config.GatewayAutoConfiguration;
import org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration;
import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jca.support.ResourceAdapterFactoryBean;
import org.springframework.jca.work.SimpleTaskWorkManager;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.jms.annotation.JmsListenerConfigurer;
import org.springframework.jms.config.JmsListenerEndpointRegistrar;
import org.springframework.jms.config.SimpleJmsListenerEndpoint;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.listener.endpoint.JmsMessageEndpointManager;
import static org.assertj.core.api.Assertions.assertThat;
// inspired by org.springframework.boot.autoconfigure.jms.JmsAutoConfigurationTests
/**
* @author Adrian Cole
*/
public class JmsTracingConfigurationTest {
final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(JmsTestTracingConfiguration.class,
AnnotationJmsListenerConfiguration.class, XAConfiguration.class,
SimpleJmsListenerConfiguration.class,
JcaJmsListenerConfiguration.class));
static void clearSpans(AssertableApplicationContext ctx) throws JMSException {
ctx.getBean(JmsTestTracingConfiguration.class).clearSpan();
}
static void checkConnection(AssertableApplicationContext ctx) throws JMSException {
// Not using try-with-resources as that doesn't exist in JMS 1.1
Connection con = ctx.getBean(ConnectionFactory.class).createConnection();
try {
con.setExceptionListener(exception -> {
});
assertThat(con.getExceptionListener().getClass().getName())
.startsWith("brave.jms.TracingExceptionListener");
}
finally {
con.close();
}
}
static void checkXAConnection(AssertableApplicationContext ctx) throws JMSException {
// Not using try-with-resources as that doesn't exist in JMS 1.1
XAConnection con = ctx.getBean(XAConnectionFactory.class).createXAConnection();
try {
con.setExceptionListener(exception -> {
});
assertThat(con.getExceptionListener().getClass().getName())
.startsWith("brave.jms.TracingExceptionListener");
}
finally {
con.close();
}
}
static void checkTopicConnection(AssertableApplicationContext ctx)
throws JMSException {
// Not using try-with-resources as that doesn't exist in JMS 1.1
TopicConnection con = ctx.getBean(TopicConnectionFactory.class)
.createTopicConnection();
try {
con.setExceptionListener(exception -> {
});
assertThat(con.getExceptionListener().getClass().getName())
.startsWith("brave.jms.TracingExceptionListener");
}
finally {
con.close();
}
}
@Test
public void tracesConnectionFactory() {
this.contextRunner.run(JmsTracingConfigurationTest::checkConnection);
}
@Test
public void tracesXAConnectionFactories() {
this.contextRunner.withUserConfiguration(XAConfiguration.class).run(ctx -> {
clearSpans(ctx);
checkConnection(ctx);
checkXAConnection(ctx);
});
}
@Test
public void tracesTopicConnectionFactories() {
this.contextRunner.withUserConfiguration(XAConfiguration.class).run(ctx -> {
clearSpans(ctx);
checkConnection(ctx);
checkTopicConnection(ctx);
});
}
@Test
public void tracesListener_jmsMessageListener() {
this.contextRunner.withUserConfiguration(SimpleJmsListenerConfiguration.class)
.run(ctx -> {
clearSpans(ctx);
ctx.getBean(JmsTemplate.class).convertAndSend("myQueue", "foo");
Callable<Span> takeSpan = ctx.getBean("takeSpan", Callable.class);
List<Span> trace = Arrays.asList(takeSpan.call(), takeSpan.call(),
takeSpan.call());
assertThat(trace).allSatisfy(s -> assertThat(s.traceId())
.isEqualTo(trace.get(0).traceId()));
assertThat(trace).isNotNull().extracting(Span::name).contains("send",
"receive", "on-message");
});
}
@Test
public void tracesListener_annotationMessageListener() {
this.contextRunner.withUserConfiguration(AnnotationJmsListenerConfiguration.class)
.run(ctx -> {
clearSpans(ctx);
ctx.getBean(JmsTemplate.class).convertAndSend("myQueue", "foo");
Callable<Span> takeSpan = ctx.getBean("takeSpan", Callable.class);
List<Span> trace = Arrays.asList(takeSpan.call(), takeSpan.call(),
takeSpan.call());
assertThat(trace).allSatisfy(s -> assertThat(s.traceId())
.isEqualTo(trace.get(0).traceId()));
assertThat(trace).isNotNull().extracting(Span::name)
.containsExactlyInAnyOrder("send", "receive", "on-message");
});
}
@Test
public void tracesListener_jcaMessageListener() {
this.contextRunner.withUserConfiguration(JcaJmsListenerConfiguration.class)
.run(ctx -> {
clearSpans(ctx);
ctx.getBean(JmsTemplate.class).convertAndSend("myQueue", "foo");
Callable<Span> takeSpan = ctx.getBean("takeSpan", Callable.class);
List<Span> trace = Arrays.asList(takeSpan.call(), takeSpan.call(),
takeSpan.call());
assertThat(trace).allSatisfy(s -> assertThat(s.traceId())
.isEqualTo(trace.get(0).traceId()));
assertThat(trace).isNotNull().extracting(Span::name)
.containsExactlyInAnyOrder("send", "receive", "on-message");
});
}
@AutoConfigureBefore(ActiveMQAutoConfiguration.class)
static class XAConfiguration {
@Bean
XAConnectionFactoryWrapper xaConnectionFactoryWrapper() {
return connectionFactory -> (ConnectionFactory) connectionFactory;
}
}
@Configuration
@EnableJms
static class SimpleJmsListenerConfiguration implements JmsListenerConfigurer {
@Autowired
CurrentTraceContext current;
@Override
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
endpoint.setId("myCustomEndpointId");
endpoint.setDestination("myQueue");
endpoint.setMessageListener(simpleMessageListener(this.current));
registrar.registerEndpoint(endpoint);
}
@Bean
MessageListener simpleMessageListener(CurrentTraceContext current) {
return message -> {
// Didn't restart the trace
assertThat(current.get()).isNotNull()
.extracting(TraceContext::parentIdAsLong).isNotEqualTo(0L);
};
}
}
@Configuration
@EnableJms
static class AnnotationJmsListenerConfiguration {
@Autowired
CurrentTraceContext current;
@JmsListener(destination = "myQueue")
public void onMessage() {
assertThat(this.current.get()).isNotNull()
.extracting(TraceContext::parentIdAsLong).isNotEqualTo(0L);
}
}
@Configuration
static class JcaJmsListenerConfiguration {
@Autowired
CurrentTraceContext current;
@Bean
ResourceAdapterFactoryBean resourceAdapter() {
ResourceAdapterFactoryBean resourceAdapter = new ResourceAdapterFactoryBean();
ActiveMQResourceAdapter real = new ActiveMQResourceAdapter();
real.setServerUrl("vm://localhost?broker.persistent=false");
resourceAdapter.setResourceAdapter(real);
resourceAdapter.setWorkManager(new SimpleTaskWorkManager());
return resourceAdapter;
}
@Bean
MessageListener simpleMessageListener(CurrentTraceContext current) {
return message -> {
// Didn't restart the trace
assertThat(current.get()).isNotNull()
.extracting(TraceContext::parentIdAsLong).isNotEqualTo(0L);
};
}
@Bean
JmsMessageEndpointManager endpointManager(ResourceAdapter resourceAdapter,
MessageListener simpleMessageListener) {
JmsMessageEndpointManager endpointManager = new JmsMessageEndpointManager();
endpointManager.setResourceAdapter(resourceAdapter);
ActiveMQActivationSpec spec = new ActiveMQActivationSpec();
spec.setUseJndi(false);
spec.setDestinationType("javax.jms.Queue");
spec.setDestination("myQueue");
endpointManager.setActivationSpec(spec);
endpointManager.setMessageListener(simpleMessageListener);
return endpointManager;
}
}
}
@Configuration
@EnableAutoConfiguration(exclude = { GatewayAutoConfiguration.class,
GatewayClassPathWarningAutoConfiguration.class,
EurekaClientAutoConfiguration.class })
class JmsTestTracingConfiguration {
static final String CONTEXT_LEAK = "context.leak";
/**
* When testing servers or asynchronous clients, spans are reported on a worker
* thread. In order to read them on the main thread, we use a concurrent queue. As
* some implementations report after a response is sent, we use a blocking queue to
* prevent race conditions in tests.
*/
BlockingQueue<Span> spans = new LinkedBlockingQueue<>();
void clearSpan() {
this.spans.clear();
}
/**
* Call this to block until a span was reported.
* @return span from queue
*/
@Bean
Callable<Span> takeSpan() {
return () -> {
Span result = this.spans.poll(3, TimeUnit.SECONDS);
assertThat(result).withFailMessage("Span was not reported").isNotNull();
assertThat(result.annotations()).extracting(Annotation::value)
.doesNotContain(CONTEXT_LEAK);
return result;
};
}
@Bean
Tracing tracing(CurrentTraceContext currentTraceContext) {
return Tracing.newBuilder().spanReporter(s -> {
// make sure the context was cleared prior to finish.. no leaks!
TraceContext current = currentTraceContext.get();
boolean contextLeak = false;
if (current != null) {
// add annotation in addition to throwing, in case we are off the main
// thread
if (HexCodec.toLowerHex(current.spanId()).equals(s.id())) {
s = s.toBuilder().addAnnotation(s.timestampAsLong(), CONTEXT_LEAK)
.build();
contextLeak = true;
}
}
this.spans.add(s);
// throw so that we can see the path to the code that leaked the context
if (contextLeak) {
throw new AssertionError(
CONTEXT_LEAK + " on " + Thread.currentThread().getName());
}
}).currentTraceContext(currentTraceContext).build();
}
}

View File

@@ -1,81 +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.instrument.messaging;
import brave.Tracing;
import org.apache.kafka.streams.KafkaClientSupplier;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.config.StreamsBuilderFactoryBean;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.BDDAssertions.then;
/**
* @author Tim te Beek
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SleuthKafkaStreamsConfigurationTest.Config.class,
webEnvironment = WebEnvironment.NONE)
public class SleuthKafkaStreamsConfigurationTest {
@Autowired
TestTraceStreamsBuilderFactoryBean streamsBuilderFactoryBean;
@Test
public void clientSupplierInvokedOnStreamsBuilderFactoryBean() {
then(streamsBuilderFactoryBean.clientSupplierInvoked).isTrue();
}
@Configuration
@EnableAutoConfiguration
protected static class Config {
@Bean
Tracing tracing() {
return Tracing.newBuilder().build();
}
@Bean
StreamsBuilderFactoryBean streamsBuilderFactoryBean() {
TestTraceStreamsBuilderFactoryBean factoryBean = new TestTraceStreamsBuilderFactoryBean();
factoryBean.setAutoStartup(false);
return factoryBean;
}
}
}
class TestTraceStreamsBuilderFactoryBean extends StreamsBuilderFactoryBean {
boolean clientSupplierInvoked;
@Override
public void setClientSupplier(KafkaClientSupplier clientSupplier) {
this.clientSupplierInvoked = true;
super.setClientSupplier(clientSupplier);
}
}

View File

@@ -1,118 +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.instrument.messaging;
import brave.Span;
import brave.Tracer;
import brave.Tracing;
import brave.sampler.Sampler;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.cloud.sleuth.util.SpanUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.PollableChannel;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Spencer Gibb
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = TraceContextPropagationChannelInterceptorTests.App.class)
@DirtiesContext
public class TraceContextPropagationChannelInterceptorTests {
@Autowired
@Qualifier("channel")
private PollableChannel channel;
@Autowired
private Tracing tracing;
@Autowired
private ArrayListSpanReporter reporter;
@After
public void close() {
this.reporter.clear();
}
@Test
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());
}
finally {
span.finish();
}
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);
assertThat(parentId).as("parentId was not equal to parent's id")
.isEqualTo(this.reporter.getSpans().get(0).id());
}
@Configuration
@EnableAutoConfiguration
static class App {
@Bean
public QueueChannel channel() {
return new QueueChannel();
}
@Bean
Sampler testSampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
ArrayListSpanReporter reporter() {
return new ArrayListSpanReporter();
}
}
}

View File

@@ -1,204 +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.instrument.messaging;
import brave.Tracer;
import brave.kafka.clients.KafkaTracing;
import brave.sampler.Sampler;
import brave.spring.rabbit.SpringRabbitTracing;
import org.apache.kafka.clients.consumer.Consumer;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.producer.Producer;
import org.aspectj.lang.ProceedingJoinPoint;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.core.ConsumerFactory;
import org.springframework.kafka.core.ProducerFactory;
import org.springframework.kafka.listener.MessageListenerContainer;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.BDDAssertions.then;
/**
* @author Marcin Grzejszczak
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TraceMessagingAutoConfigurationTests.Config.class,
webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class TraceMessagingAutoConfigurationTests {
@Autowired
RabbitTemplate rabbitTemplate;
@Autowired
ArrayListSpanReporter reporter;
@Autowired
TestSleuthRabbitBeanPostProcessor postProcessor;
@Autowired
TestSleuthJmsBeanPostProcessor jmsBeanPostProcessor;
@Autowired
MySleuthKafkaAspect mySleuthKafkaAspect;
@Autowired
ProducerFactory producerFactory;
@Autowired
ConsumerFactory consumerFactory;
@Test
public void should_wrap_rabbit_template() {
then(this.rabbitTemplate).isNotNull();
then(this.postProcessor.rabbitTracingCalled).isTrue();
}
@Test
public void should_wrap_jms() {
then(this.jmsBeanPostProcessor).isNotNull();
then(this.jmsBeanPostProcessor.tracingCalled).isTrue();
}
@Test
public void should_wrap_kafka() {
this.producerFactory.createProducer();
then(this.mySleuthKafkaAspect.producerWrapped).isTrue();
this.consumerFactory.createConsumer();
then(this.mySleuthKafkaAspect.consumerWrapped).isTrue();
then(this.mySleuthKafkaAspect.adapterWrapped).isTrue();
}
@Configuration
@EnableAutoConfiguration
protected static class Config {
@Bean
Sampler sampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
ArrayListSpanReporter reporter() {
return new ArrayListSpanReporter();
}
@Bean
SleuthRabbitBeanPostProcessor sleuthRabbitBeanPostProcessor(
BeanFactory beanFactory) {
return new TestSleuthRabbitBeanPostProcessor(beanFactory);
}
@Bean
SleuthKafkaAspect sleuthKafkaAspect(KafkaTracing kafkaTracing, Tracer tracer) {
return new MySleuthKafkaAspect(kafkaTracing, tracer);
}
@Bean
TestSleuthJmsBeanPostProcessor sleuthJmsBeanPostProcessor(
BeanFactory beanFactory) {
return new TestSleuthJmsBeanPostProcessor(beanFactory);
}
@KafkaListener(topics = "backend", groupId = "foo")
public void onMessage(ConsumerRecord<?, ?> message) {
System.err.println(message);
}
}
}
class TestSleuthRabbitBeanPostProcessor extends SleuthRabbitBeanPostProcessor {
boolean rabbitTracingCalled = false;
TestSleuthRabbitBeanPostProcessor(BeanFactory beanFactory) {
super(beanFactory);
}
@Override
SpringRabbitTracing rabbitTracing() {
this.rabbitTracingCalled = true;
return super.rabbitTracing();
}
}
class MySleuthKafkaAspect extends SleuthKafkaAspect {
boolean producerWrapped;
boolean consumerWrapped;
boolean adapterWrapped;
MySleuthKafkaAspect(KafkaTracing kafkaTracing, Tracer tracer) {
super(kafkaTracing, tracer);
}
@Override
public Object wrapProducerFactory(ProceedingJoinPoint pjp) throws Throwable {
this.producerWrapped = true;
return Mockito.mock(Producer.class);
}
@Override
public Object wrapConsumerFactory(ProceedingJoinPoint pjp) throws Throwable {
this.consumerWrapped = true;
return Mockito.mock(Consumer.class);
}
@Override
public Object wrapListenerContainerCreation(ProceedingJoinPoint pjp)
throws Throwable {
this.adapterWrapped = true;
return Mockito.mock(MessageListenerContainer.class);
}
}
class TestSleuthJmsBeanPostProcessor extends TracingConnectionFactoryBeanPostProcessor {
boolean tracingCalled = false;
TestSleuthJmsBeanPostProcessor(BeanFactory beanFactory) {
super(beanFactory);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
this.tracingCalled = true;
return super.postProcessAfterInitialization(bean, beanName);
}
}

View File

@@ -1,55 +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.instrument.messaging;
import brave.Tracing;
import org.junit.After;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.support.ChannelInterceptor;
public class TracingChannelInterceptorAutowireTest {
@Test
public void autowiredWithBeanConfig() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(TracingConfiguration.class);
ctx.register(TracingChannelInterceptor.class);
ctx.refresh();
ctx.getBean(ChannelInterceptor.class);
}
@After
public void close() {
Tracing.current().close();
}
@Configuration
static class TracingConfiguration {
@Bean
Tracing tracing() {
return Tracing.newBuilder().build();
}
}
}

View File

@@ -1,49 +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.instrument.messaging.issues.issue_943;
import java.util.concurrent.Executor;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.sleuth.instrument.async.LazyTraceExecutor;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@EnableAsync
@Configuration
public class CustomExecutorConfig extends AsyncConfigurerSupport {
@Autowired
BeanFactory beanFactory;
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// CUSTOMIZE HERE
executor.setCorePoolSize(7);
executor.setMaxPoolSize(42);
executor.setQueueCapacity(11);
executor.setThreadNamePrefix("MyExecutor-");
// DON'T FORGET TO INITIALIZE
executor.initialize();
return new LazyTraceExecutor(this.beanFactory, executor);
}
}

View File

@@ -1,60 +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.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;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ImportResource;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class })
@ImportResource("classpath:beans/applicationContext.xml")
@EnableIntegration
@EnableAsync
public class HelloSpringIntegration {
public static void main(String[] args) {
SpringApplication.run(HelloSpringIntegration.class, args);
}
@Bean
Sampler sampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
ArrayListSpanReporter accumulator() {
return new ArrayListSpanReporter();
}
}

View File

@@ -1,51 +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.instrument.messaging.issues.issue_943;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorldImpl {
private static final Logger LOG = LoggerFactory.getLogger(HelloWorldImpl.class);
public String invokeProcessor(String message) throws InterruptedException {
LOG.info(" input message " + message);
Thread.currentThread().sleep(500);
LOG.info(" After the Sleep " + message);
String responseMessage = message + " Persist into DB ";
return responseMessage;
}
public List<String> aggregate(List<String> requestMessage) {
LOG.info(Thread.currentThread().getName());
LOG.info(" requestMessage aggregate " + requestMessage);
return requestMessage;
}
public List<String> splitMessage(String[] splitRequest) {
LOG.info(" Inside splitMessage " + splitRequest);
List<String> splitGBSResponse = new ArrayList<String>();
splitGBSResponse = Arrays.asList(splitRequest);
return splitGBSResponse;
}
}

View File

@@ -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.instrument.messaging.issues.issue_943;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloWorldRestController {
private static final Logger LOG = LoggerFactory
.getLogger(HelloWorldRestController.class);
@Autowired
private ApplicationContext applicationContext;
@RequestMapping(path = "getHelloWorldMessage", method = RequestMethod.GET,
produces = MediaType.TEXT_PLAIN_VALUE)
public ResponseEntity<String> getHelloWorld() throws Exception {
LOG.info("Inside getHelloWorldMessage");
String[] requestMessage = new String[3];
requestMessage[0] = "Hellow World Message 1";
requestMessage[1] = "Hellow World Message 2";
requestMessage[2] = "Hellow World Message 3";
PollableChannel outputChannel = (PollableChannel) this.applicationContext
.getBean("messagingOutputChannel");
MessagingGateway messagingGateway = (MessagingGateway) this.applicationContext
.getBean("messagingGateway");
messagingGateway.processMessage(requestMessage);
GenericMessage reply = (GenericMessage) outputChannel.receive();
List<String> body = (List<String>) reply.getPayload();
LOG.info(" Response Message " + body);
return new ResponseEntity<String>(body.toString(), HttpStatus.OK);
}
}

View File

@@ -1,79 +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.instrument.messaging.issues.issue_943;
import java.util.stream.Collectors;
import brave.Span;
import brave.Tracer;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.BDDAssertions.then;
/**
* Example taken from https://github.com/spring-cloud/spring-cloud-sleuth/issues/943 .
*
* @author Marcin Grzejszczak
*/
public class Issue943Tests {
@Test
public void should_pass_tracing_context_via_spring_integration() {
try (ConfigurableApplicationContext applicationContext = SpringApplication.run(
HelloSpringIntegration.class, "--spring.jmx.enabled=false",
"--server.port=0")) {
// given
Tracer tracer = applicationContext.getBean(Tracer.class);
Span newSpan = tracer.nextSpan().name("foo").start();
String object;
try (Tracer.SpanInScope ws = tracer.withSpanInScope(newSpan)) {
RestTemplate restTemplate = applicationContext
.getBean(RestTemplate.class);
// when
object = restTemplate
.getForObject(
"http://localhost:"
+ applicationContext.getEnvironment()
.getProperty("local.server.port")
+ "/getHelloWorldMessage",
String.class);
}
// then
ArrayListSpanReporter accumulator = applicationContext
.getBean(ArrayListSpanReporter.class);
then(object).contains("Hellow World Message 1 Persist into DB")
.contains("Hellow World Message 2 Persist into DB")
.contains("Hellow World Message 3 Persist into DB");
then(accumulator.getSpans().stream().filter(
span -> span.traceId().equals(newSpan.context().traceIdString()))
.map(span -> span.tags().getOrDefault("channel",
span.tags().get("http.path")))
.collect(Collectors.toList()))
.as("trace context was propagated successfully").isNotEmpty()
.contains("splitterOutChannel", "messagingChannel",
"messagingProcessedChannel", "messagingOutputChannel",
"/getHelloWorldMessage");
}
}
}

View File

@@ -1,23 +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.instrument.messaging.issues.issue_943;
public interface MessagingGateway {
void processMessage(String[] messageArray);
}

View File

@@ -1,84 +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.instrument.messaging.websocket;
import brave.sampler.Sampler;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.instrument.messaging.TracingChannelInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.DelegatingWebSocketMessageBrokerConfiguration;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import static org.assertj.core.api.BDDAssertions.then;
/**
* @author Marcin Grzejszczak
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TraceWebSocketAutoConfigurationTests.Config.class)
public class TraceWebSocketAutoConfigurationTests {
@Autowired
DelegatingWebSocketMessageBrokerConfiguration delegatingWebSocketMessageBrokerConfiguration;
@Test
public void should_register_interceptors_for_all_channels() {
then(this.delegatingWebSocketMessageBrokerConfiguration.clientInboundChannel()
.getInterceptors())
.hasAtLeastOneElementOfType(TracingChannelInterceptor.class);
then(this.delegatingWebSocketMessageBrokerConfiguration.clientOutboundChannel()
.getInterceptors())
.hasAtLeastOneElementOfType(TracingChannelInterceptor.class);
then(this.delegatingWebSocketMessageBrokerConfiguration.brokerChannel()
.getInterceptors())
.hasAtLeastOneElementOfType(TracingChannelInterceptor.class);
}
@EnableAutoConfiguration
@Configuration
@EnableWebSocketMessageBroker
public static class Config extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/hello").withSockJS();
}
@Bean
Sampler testSampler() {
return Sampler.ALWAYS_SAMPLE;
}
}
}

View File

@@ -1,71 +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.instrument.reactor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Marcin Grzejszczak
*/
@Configuration
public class Issue866Configuration {
private static final Log log = LogFactory.getLog(Issue866Configuration.class);
// we don't want to force direct dependencies between components
// because Spring might just properly setup the context
// we want to ensure that the HRBDRPP is always executed before
// any other object is started
public static TestHook hook;
@Bean
HookRegisteringBeanDefinitionRegistryPostProcessor overridingProcessorForTests(
ConfigurableApplicationContext context) {
log.info(
"Registering a HookRegisteringBeanDefinitionRegistryPostProcessor for context ["
+ context + "]");
TestHook hook = new TestHook(context);
Issue866Configuration.hook = hook;
return hook;
}
public static class TestHook
extends HookRegisteringBeanDefinitionRegistryPostProcessor {
public boolean executed = false;
public TestHook(ConfigurableApplicationContext context) {
super(context);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
super.postProcessBeanFactory(beanFactory);
this.executed = true;
}
}
}

View File

@@ -1,69 +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.instrument.reactor;
import brave.Span;
import brave.Tracer;
import brave.Tracing;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import reactor.core.CoreSubscriber;
import reactor.core.publisher.BaseSubscriber;
import reactor.util.context.Context;
import static org.assertj.core.api.BDDAssertions.then;
/**
* @author Marcin Grzejszczak
*/
@RunWith(MockitoJUnitRunner.class)
public class ScopePassingSpanSubscriberTests {
Tracing tracing = Tracing.newBuilder().build();
@Test
public void should_propagate_current_context() {
ScopePassingSpanSubscriber<?> subscriber = new ScopePassingSpanSubscriber<>(null,
Context.of("foo", "bar"), this.tracing, null);
then((String) subscriber.currentContext().get("foo")).isEqualTo("bar");
}
@Test
public void should_set_empty_context_when_context_is_null() {
ScopePassingSpanSubscriber<?> subscriber = new ScopePassingSpanSubscriber<>(null,
null, this.tracing, null);
then(subscriber.currentContext().isEmpty()).isTrue();
}
@Test
public void should_put_current_span_to_context() {
Span span = this.tracing.tracer().nextSpan();
try (Tracer.SpanInScope ws = this.tracing.tracer()
.withSpanInScope(span.start())) {
CoreSubscriber<?> subscriber = ReactorSleuth.scopePassingSpanSubscription(
this.tracing, new BaseSubscriber<Object>() {
});
then(subscriber.currentContext().get(Span.class)).isEqualTo(span);
}
}
}

View File

@@ -1,333 +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.instrument.reactor;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import brave.Span;
import brave.Tracer;
import brave.sampler.Sampler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.awaitility.Awaitility;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import reactor.core.CoreSubscriber;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.BDDAssertions.then;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpanSubscriberTests.Config.class,
webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class SpanSubscriberTests {
private static final Log log = LogFactory.getLog(SpanSubscriberTests.class);
@Autowired
Tracer tracer;
@Autowired
ConfigurableApplicationContext factory;
@Test
public void should_pass_tracing_info_when_using_reactor() {
Span span = this.tracer.nextSpan().name("foo").start();
final AtomicReference<Span> spanInOperation = new AtomicReference<>();
Publisher<Integer> traced = Flux.just(1, 2, 3);
log.info("Hello");
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
Flux.from(traced).map(d -> d + 1).map(d -> d + 1).map((d) -> {
spanInOperation.set(this.tracer.currentSpan());
return d + 1;
}).map(d -> d + 1).subscribe(System.out::println);
}
finally {
span.finish();
}
then(this.tracer.currentSpan()).isNull();
then(spanInOperation.get().context().spanId()).isEqualTo(span.context().spanId());
}
@Test
public void should_support_reactor_fusion_optimization() {
Span span = this.tracer.nextSpan().name("foo").start();
final AtomicReference<Span> spanInOperation = new AtomicReference<>();
log.info("Hello");
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
Mono.just(1).flatMap(d -> Flux.just(d + 1).collectList().map(p -> p.get(0)))
.map(d -> d + 1).map((d) -> {
spanInOperation.set(this.tracer.currentSpan());
return d + 1;
}).map(d -> d + 1).subscribe(System.out::println);
}
finally {
span.finish();
}
then(this.tracer.currentSpan()).isNull();
then(spanInOperation.get().context().spanId()).isEqualTo(span.context().spanId());
}
@Test
public void should_not_trace_scalar_flows() {
Span span = this.tracer.nextSpan().name("foo").start();
log.info("Hello");
// Disable global hooks for local hook testing
TraceReactorAutoConfigurationAccessorConfiguration.close();
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
Function<? super Publisher<Integer>, ? extends Publisher<Integer>> transformer = ReactorSleuth
.scopePassingSpanOperator(this.factory);
Subscriber<Object> assertNoSpanSubscriber = new CoreSubscriber<Object>() {
@Override
public void onSubscribe(Subscription s) {
s.request(Long.MAX_VALUE);
assertThat(s).isNotInstanceOf(ScopePassingSpanSubscriber.class);
}
@Override
public void onNext(Object o) {
}
@Override
public void onError(Throwable t) {
}
@Override
public void onComplete() {
}
};
Subscriber<Object> assertSpanSubscriber = new CoreSubscriber<Object>() {
@Override
public void onSubscribe(Subscription s) {
s.request(Long.MAX_VALUE);
assertThat(s).isInstanceOf(ScopePassingSpanSubscriber.class);
}
@Override
public void onNext(Object o) {
}
@Override
public void onError(Throwable t) {
}
@Override
public void onComplete() {
}
};
transformer.apply(Mono.just(1).hide()).subscribe(assertSpanSubscriber);
transformer.apply(Mono.just(1)).subscribe(assertNoSpanSubscriber);
transformer.apply(Mono.<Integer>error(new Exception()).hide())
.subscribe(assertSpanSubscriber);
transformer.apply(Mono.error(new Exception()))
.subscribe(assertNoSpanSubscriber);
transformer.apply(Mono.<Integer>empty().hide())
.subscribe(assertSpanSubscriber);
transformer.apply(Mono.empty()).subscribe(assertNoSpanSubscriber);
}
finally {
span.finish();
}
Awaitility.await().untilAsserted(() -> {
then(this.tracer.currentSpan()).isNull();
});
TraceReactorAutoConfigurationAccessorConfiguration.setup(this.factory);
}
@Test
public void should_pass_tracing_info_when_using_reactor_async() {
Span span = this.tracer.nextSpan().name("foo").start();
final AtomicReference<Span> spanInOperation = new AtomicReference<>();
log.info("Hello");
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
Flux.just(1, 2, 3).publishOn(Schedulers.single()).log("reactor.1")
.map(d -> d + 1).map(d -> d + 1)
.publishOn(Schedulers.newSingle("secondThread")).log("reactor.2")
.map((d) -> {
spanInOperation.set(this.tracer.currentSpan());
return d + 1;
}).map(d -> d + 1).blockLast();
Awaitility.await().untilAsserted(() -> {
then(spanInOperation.get().context().traceId())
.isEqualTo(span.context().traceId());
});
then(this.tracer.currentSpan()).isEqualTo(span);
}
finally {
span.finish();
}
then(this.tracer.currentSpan()).isNull();
Span foo2 = this.tracer.nextSpan().name("foo").start();
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(foo2)) {
Flux.just(1, 2, 3).publishOn(Schedulers.single()).log("reactor.")
.map(d -> d + 1).map(d -> d + 1).map((d) -> {
spanInOperation.set(this.tracer.currentSpan());
return d + 1;
}).map(d -> d + 1).blockLast();
then(this.tracer.currentSpan()).isEqualTo(foo2);
// parent cause there's an async span in the meantime
then(spanInOperation.get().context().traceId())
.isEqualTo(foo2.context().traceId());
}
finally {
foo2.finish();
}
then(this.tracer.currentSpan()).isNull();
}
@Test
public void checkSequenceOfOperations() {
Span parentSpan = this.tracer.nextSpan().name("foo").start();
log.info("Hello");
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(parentSpan)) {
final Long spanId = Mono.fromCallable(this.tracer::currentSpan)
.map(span -> span.context().spanId()).block();
then(spanId).isNotNull();
final Long secondSpanId = Mono.fromCallable(this.tracer::currentSpan)
.map(span -> span.context().spanId()).block();
then(secondSpanId).isEqualTo(spanId); // different trace ids here
}
}
@Test
public void checkTraceIdDuringZipOperation() {
Span initSpan = this.tracer.nextSpan().name("foo").start();
final AtomicReference<Long> spanInOperation = new AtomicReference<>();
final AtomicReference<Long> spanInZipOperation = new AtomicReference<>();
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(initSpan)) {
Mono.fromCallable(this.tracer::currentSpan)
.map(span -> span.context().spanId()).doOnNext(spanInOperation::set)
.zipWith(Mono.fromCallable(this.tracer::currentSpan)
.map(span -> span.context().spanId())
.doOnNext(spanInZipOperation::set))
.block();
}
then(spanInZipOperation).hasValue(initSpan.context().spanId()); // ok here
then(spanInOperation).hasValue(initSpan.context().spanId()); // Expecting
// <AtomicReference[null]>
// to have value:
// <1L> but did
// not.
}
// #646
@Test
public void should_work_for_mono_just_with_flat_map() {
Span initSpan = this.tracer.nextSpan().name("foo").start();
log.info("Hello");
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(initSpan)) {
Mono.just("value1")
.flatMap(request -> Mono.just("value2").then(Mono.just("foo")))
.map(a -> "qwe").block();
}
}
// #1030
@Test
public void checkTraceIdFromSubscriberContext() {
Span initSpan = this.tracer.nextSpan().name("foo").start();
final AtomicReference<Long> spanInSubscriberContext = new AtomicReference<>();
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(initSpan)) {
Mono.subscriberContext()
.map(context -> this.tracer.currentSpan().context().spanId())
.doOnNext(spanInSubscriberContext::set).block();
}
then(spanInSubscriberContext).hasValue(initSpan.context().spanId()); // ok here
}
@Test
public void should_pass_tracing_info_into_inner_publishers() {
Span span = this.tracer.nextSpan().name("foo").start();
final AtomicReference<Span> spanInOperation = new AtomicReference<>();
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
Flux.range(0, 5)
.flatMap(it -> Mono.delay(Duration.ofMillis(1))
.map(context -> this.tracer.currentSpan())
.doOnNext(spanInOperation::set))
.blockFirst();
}
finally {
span.finish();
}
then(spanInOperation.get().context().spanId()).isEqualTo(span.context().spanId());
}
@EnableAutoConfiguration
@Configuration
static class Config {
@Bean
Sampler sampler() {
return Sampler.ALWAYS_SAMPLE;
}
}
}

View File

@@ -1,231 +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.instrument.reactor.sample;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import brave.Tracer;
import brave.sampler.Sampler;
import org.awaitility.Awaitility;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import zipkin2.Span;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.rule.OutputCapture;
import org.springframework.cloud.sleuth.DisableWebFluxSecurity;
import org.springframework.cloud.sleuth.instrument.reactor.Issue866Configuration;
import org.springframework.cloud.sleuth.instrument.reactor.TraceReactorAutoConfigurationAccessorConfiguration;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import static org.assertj.core.api.BDDAssertions.then;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
// https://github.com/spring-cloud/spring-cloud-sleuth/issues/850
public class FlatMapTests {
private static final Logger LOGGER = LoggerFactory.getLogger(FlatMapTests.class);
@Rule
public OutputCapture capture = new OutputCapture();
@BeforeClass
public static void setup() {
TraceReactorAutoConfigurationAccessorConfiguration.close();
Issue866Configuration.hook = null;
}
@AfterClass
public static void cleanup() {
Issue866Configuration.hook = null;
}
@Test
public void should_work_with_flat_maps() {
// given
ConfigurableApplicationContext context = new SpringApplicationBuilder(
FlatMapTests.TestConfiguration.class, Issue866Configuration.class)
.web(WebApplicationType.REACTIVE)
.properties("server.port=0", "spring.jmx.enabled=false",
"spring.application.name=TraceWebFluxTests",
"security.basic.enabled=false",
"management.security.enabled=false")
.run();
ArrayListSpanReporter accumulator = context.getBean(ArrayListSpanReporter.class);
int port = context.getBean(Environment.class).getProperty("local.server.port",
Integer.class);
RequestSender sender = context.getBean(RequestSender.class);
TestConfiguration config = context.getBean(TestConfiguration.class);
FactoryUser factoryUser = context.getBean(FactoryUser.class);
sender.port = port;
accumulator.clear();
Awaitility.await().untilAsserted(() -> {
// when
LOGGER.info("Start");
accumulator.clear();
String firstTraceId = flatMapTraceId(accumulator, callFlatMap(port).block());
// then
LOGGER.info("Checking first trace id");
thenAllWebClientCallsHaveSameTraceId(firstTraceId, sender);
thenSpanInFooHasSameTraceId(firstTraceId, config);
accumulator.clear();
LOGGER.info("All web client calls have same trace id");
// when
LOGGER.info("Second trace start");
String secondTraceId = flatMapTraceId(accumulator, callFlatMap(port).block());
// then
then(firstTraceId).as("Id will not be reused between calls")
.isNotEqualTo(secondTraceId);
LOGGER.info("Id was not reused between calls");
thenSpanInFooHasSameTraceId(secondTraceId, config);
LOGGER.info("Span in Foo has same trace id");
// and
List<String> requestUri = Arrays.stream(this.capture.toString().split("\n"))
.filter(s -> s.contains("Received a request to uri"))
.map(s -> s.split(",")[1]).collect(Collectors.toList());
LOGGER.info(
"TracingFilter should not have any trace when receiving a request "
+ requestUri);
then(requestUri).as(
"TracingFilter should not have any trace when receiving a request")
.containsOnly("");
// and #866
then(factoryUser.wasSchedulerWrapped).isTrue();
LOGGER.info("Factory was wrapped");
});
}
private void thenAllWebClientCallsHaveSameTraceId(String traceId,
RequestSender sender) {
then(sender.span.context().traceIdString()).isEqualTo(traceId);
}
private void thenSpanInFooHasSameTraceId(String traceId, TestConfiguration config) {
then(config.spanInFoo.context().traceIdString()).isEqualTo(traceId);
}
private Mono<ClientResponse> callFlatMap(int port) {
return WebClient.create().get().uri("http://localhost:" + port + "/withFlatMap")
.exchange();
}
private String flatMapTraceId(ArrayListSpanReporter accumulator,
ClientResponse response) {
then(response.statusCode().value()).isEqualTo(200);
then(accumulator.getSpans()).isNotEmpty();
LOGGER.info("Accumulated spans: " + accumulator.getSpans());
List<String> traceIdOfFlatMap = accumulator.getSpans().stream()
.filter(span -> span.tags().containsKey("http.path")
&& span.tags().get("http.path").equals("/withFlatMap"))
.map(Span::traceId).collect(Collectors.toList());
then(traceIdOfFlatMap).hasSize(1);
return traceIdOfFlatMap.get(0);
}
@Configuration
@EnableAutoConfiguration
@DisableWebFluxSecurity
static class TestConfiguration {
brave.Span spanInFoo;
@Bean
RouterFunction<ServerResponse> handlers(Tracer tracer,
RequestSender requestSender) {
return route(GET("/noFlatMap"), request -> {
LOGGER.info("noFlatMap");
Flux<Integer> one = requestSender.getAll().map(String::length);
return ServerResponse.ok().body(one, Integer.class);
}).andRoute(GET("/withFlatMap"), request -> {
LOGGER.info("withFlatMap");
Flux<Integer> one = requestSender.getAll().map(String::length);
Flux<Integer> response = one
.flatMap(size -> requestSender.getAll().doOnEach(
sig -> LOGGER.info(sig.getContext().toString())))
.map(string -> {
LOGGER.info("WHATEVER YEAH");
return string.length();
});
return ServerResponse.ok().body(response, Integer.class);
}).andRoute(GET("/foo"), request -> {
LOGGER.info("foo");
this.spanInFoo = tracer.currentSpan();
return ServerResponse.ok().body(Flux.just(1), Integer.class);
});
}
@Bean
WebClient webClient() {
return WebClient.create();
}
@Bean
ArrayListSpanReporter reporter() {
return new ArrayListSpanReporter();
}
@Bean
Sampler sampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
RequestSender sender(WebClient client, Tracer tracer) {
return new RequestSender(client, tracer);
}
// https://github.com/spring-cloud/spring-cloud-sleuth/issues/866
@Bean
FactoryUser factoryUser() {
return new FactoryUser();
}
}
}
class FactoryUser {
boolean wasSchedulerWrapped = false;
FactoryUser() {
Issue866Configuration.TestHook hook = Issue866Configuration.hook;
this.wasSchedulerWrapped = hook != null && hook.executed;
}
}

View File

@@ -1,61 +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.instrument.reactor.sample;
import brave.Span;
import brave.Tracer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.http.HttpMethod;
import org.springframework.web.reactive.function.client.WebClient;
class RequestSender {
private static final Logger LOGGER = LoggerFactory.getLogger(RequestSender.class);
private final WebClient webClient;
private final Tracer tracer;
int port;
Span span;
RequestSender(WebClient webClient, Tracer tracer) {
this.webClient = webClient;
this.tracer = tracer;
}
public Mono<String> get(Integer someParameterNotUsedNow) {
LOGGER.info("getting for parameter {}", someParameterNotUsedNow);
this.span = this.tracer.currentSpan();
return this.webClient.method(HttpMethod.GET)
.uri("http://localhost:" + this.port + "/foo").retrieve()
.bodyToMono(String.class);
}
public Flux<String> getAll() {
LOGGER.info("Before merge");
Flux<String> merge = Flux.merge(get(1), get(2), get(3));
LOGGER.info("after merge");
return merge;
}
}

View File

@@ -1,91 +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.instrument.redis;
import brave.Tracing;
import io.lettuce.core.resource.ClientResources;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.BeansException;
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 static org.assertj.core.api.BDDAssertions.then;
/**
* @author Chao Chang
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TraceRedisAutoConfigurationTests.Config.class,
webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class TraceRedisAutoConfigurationTests {
@Autowired
ClientResources clientResources;
@Autowired
TestTraceLettuceClientResourcesBeanPostProcessor traceLettuceClientResourcesBeanPostProcessor;
@Test
public void tracing_should_be_set() {
then(this.traceLettuceClientResourcesBeanPostProcessor.tracingCalled).isTrue();
then(this.clientResources.tracing().isEnabled()).isTrue();
}
@Configuration
@EnableAutoConfiguration
protected static class Config {
@Bean
ClientResources clientResources() {
ClientResources clientResources = ClientResources.create();
then(clientResources.tracing().isEnabled()).isFalse();
return clientResources;
}
@Bean
TestTraceLettuceClientResourcesBeanPostProcessor testTraceLettuceClientResourcesBeanPostProcessor(
Tracing tracing) {
return new TestTraceLettuceClientResourcesBeanPostProcessor(tracing);
}
}
}
class TestTraceLettuceClientResourcesBeanPostProcessor
extends TraceLettuceClientResourcesBeanPostProcessor {
boolean tracingCalled = false;
TestTraceLettuceClientResourcesBeanPostProcessor(Tracing tracing) {
super(tracing);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
this.tracingCalled = true;
return super.postProcessAfterInitialization(bean, beanName);
}
}

View File

@@ -1,157 +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.instrument.rxjava;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import brave.Tracer;
import brave.Tracing;
import brave.propagation.StrictScopeDecorator;
import brave.propagation.ThreadLocalCurrentTraceContext;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import rx.functions.Action0;
import rx.plugins.RxJavaErrorHandler;
import rx.plugins.RxJavaObservableExecutionHook;
import rx.plugins.RxJavaPlugins;
import rx.plugins.RxJavaSchedulersHook;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import static org.assertj.core.api.BDDAssertions.then;
/**
* @author Shivang Shah
*/
public class SleuthRxJavaSchedulersHookTests {
private static StringBuilder caller;
List<String> threadsToIgnore = new ArrayList<>();
ArrayListSpanReporter reporter = new ArrayListSpanReporter();
Tracing tracing = Tracing.newBuilder()
.currentTraceContext(ThreadLocalCurrentTraceContext.newBuilder()
.addScopeDecorator(StrictScopeDecorator.create()).build())
.spanReporter(this.reporter).build();
Tracer tracer = this.tracing.tracer();
@After
public void clean() {
this.tracing.close();
this.reporter.clear();
}
@Before
@After
public void setup() {
RxJavaPlugins.getInstance().reset();
caller = new StringBuilder();
}
@Test
public void should_not_override_existing_custom_hooks() {
RxJavaPlugins.getInstance().registerErrorHandler(new MyRxJavaErrorHandler());
RxJavaPlugins.getInstance()
.registerObservableExecutionHook(new MyRxJavaObservableExecutionHook());
new SleuthRxJavaSchedulersHook(this.tracer, this.threadsToIgnore);
then(RxJavaPlugins.getInstance().getErrorHandler())
.isExactlyInstanceOf(MyRxJavaErrorHandler.class);
then(RxJavaPlugins.getInstance().getObservableExecutionHook())
.isExactlyInstanceOf(MyRxJavaObservableExecutionHook.class);
}
@Test
public void should_wrap_delegates_action_in_wrapped_action_when_delegate_is_present_on_schedule() {
RxJavaPlugins.getInstance().registerSchedulersHook(new MyRxJavaSchedulersHook());
SleuthRxJavaSchedulersHook schedulersHook = new SleuthRxJavaSchedulersHook(
this.tracer, this.threadsToIgnore);
Action0 action = schedulersHook.onSchedule(() -> {
caller = new StringBuilder("hello");
});
action.call();
then(action).isInstanceOf(SleuthRxJavaSchedulersHook.TraceAction.class);
then(caller.toString()).isEqualTo("called_from_schedulers_hook");
then(this.reporter.getSpans()).isNotEmpty();
then(this.tracer.currentSpan()).isNull();
}
@Test
public void should_not_create_a_span_when_current_thread_should_be_ignored()
throws ExecutionException, InterruptedException {
String threadNameToIgnore = "^MyCustomThread.*$";
RxJavaPlugins.getInstance().registerSchedulersHook(new MyRxJavaSchedulersHook());
SleuthRxJavaSchedulersHook schedulersHook = new SleuthRxJavaSchedulersHook(
this.tracer, Collections.singletonList(threadNameToIgnore));
Future<Void> hello = executorService().submit((Callable<Void>) () -> {
Action0 action = schedulersHook.onSchedule(() -> {
caller = new StringBuilder("hello");
});
action.call();
return null;
});
hello.get();
then(this.reporter.getSpans()).isEmpty();
then(this.tracer.currentSpan()).isNull();
}
private ExecutorService executorService() {
ThreadFactory threadFactory = r -> {
Thread thread = new Thread(r);
thread.setName("MyCustomThread10");
return thread;
};
return Executors.newSingleThreadExecutor(threadFactory);
}
static class MyRxJavaObservableExecutionHook extends RxJavaObservableExecutionHook {
}
static class MyRxJavaSchedulersHook extends RxJavaSchedulersHook {
@Override
public Action0 onSchedule(Action0 action) {
return () -> {
caller = new StringBuilder("called_from_schedulers_hook");
};
}
}
static class MyRxJavaErrorHandler extends RxJavaErrorHandler {
}
}

View File

@@ -1,124 +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.instrument.rxjava;
import brave.Span;
import brave.Tracer;
import brave.sampler.Sampler;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import rx.Observable;
import rx.functions.Action0;
import rx.plugins.RxJavaPlugins;
import rx.schedulers.Schedulers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.BDDAssertions.then;
import static org.awaitility.Awaitility.await;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { SleuthRxJavaTests.TestConfig.class })
@DirtiesContext
public class SleuthRxJavaTests {
@Autowired
ArrayListSpanReporter reporter;
@Autowired
Tracer tracer;
StringBuffer caller = new StringBuffer();
@BeforeClass
@AfterClass
public static void cleanUp() {
RxJavaPlugins.getInstance().reset();
}
@Before
public void clean() {
this.reporter.clear();
}
@Test
public void should_create_new_span_when_rx_java_action_is_executed_and_there_was_no_span() {
Observable
.defer(() -> Observable.just(
(Action0) () -> this.caller = new StringBuffer("actual_action")))
.subscribeOn(Schedulers.newThread()).toBlocking()
.subscribe(Action0::call);
then(this.caller.toString()).isEqualTo("actual_action");
then(this.tracer.currentSpan()).isNull();
await().atMost(5, SECONDS)
.untilAsserted(() -> then(this.reporter.getSpans()).hasSize(1));
then(this.reporter.getSpans()).hasSize(1);
zipkin2.Span span = this.reporter.getSpans().get(0);
then(span.name()).isEqualTo("rxjava");
}
@Test
public void should_continue_current_span_when_rx_java_action_is_executed() {
Span spanInCurrentThread = this.tracer.nextSpan().name("current_span");
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(spanInCurrentThread)) {
Observable.defer(() -> Observable.just(
(Action0) () -> this.caller = new StringBuffer("actual_action")))
.subscribeOn(Schedulers.newThread()).toBlocking()
.subscribe(Action0::call);
}
finally {
spanInCurrentThread.finish();
}
then(this.caller.toString()).isEqualTo("actual_action");
then(this.tracer.currentSpan()).isNull();
// making sure here that no new spans were created or reported as closed
then(this.reporter.getSpans()).hasSize(1);
zipkin2.Span span = this.reporter.getSpans().get(0);
then(span.name()).isEqualTo("current_span");
}
@Configuration
@EnableAutoConfiguration
public static class TestConfig {
@Bean
Sampler alwaysSampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
ArrayListSpanReporter spanReporter() {
return new ArrayListSpanReporter();
}
}
}

View File

@@ -1,281 +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.instrument.scheduling;
import java.util.AbstractMap;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.concurrent.NotThreadSafe;
import brave.Span;
import brave.Tracing;
import brave.sampler.Sampler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import zipkin2.reporter.Reporter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.instrument.DefaultTestAutoConfiguration;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.BDDAssertions.then;
import static org.awaitility.Awaitility.await;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { ScheduledTestConfiguration.class })
@DirtiesContext
@NotThreadSafe
public class TracingOnScheduledTests {
@Autowired
TestBeanWithScheduledMethod beanWithScheduledMethod;
@Autowired
TestBeanWithScheduledMethodToBeIgnored beanWithScheduledMethodToBeIgnored;
@Autowired
TestBeanWithScheduledMethodThatThrowsAnException throwsAnException;
@Autowired
ArrayListSpanReporter reporter;
@Before
public void setup() {
this.beanWithScheduledMethod.clear();
this.beanWithScheduledMethodToBeIgnored.clear();
this.reporter.clear();
}
@Test
@DirtiesContext
public void should_have_span_set_after_scheduled_method_has_been_executed() {
await().atMost(10, SECONDS).untilAsserted(() -> {
then(this.beanWithScheduledMethod.isExecuted()).isTrue();
spanIsSetOnAScheduledMethod();
});
}
@Test
public void should_have_span_set_with_error_tag() {
await().atMost(10, SECONDS).untilAsserted(() -> {
then(this.throwsAnException.isExecuted()).isTrue();
spanIsSetOnAScheduledMethodWithErrorTag();
});
}
@Test
public void should_have_a_new_span_set_each_time_a_scheduled_method_has_been_executed() {
final Span firstSpan = this.beanWithScheduledMethod.getSpan();
await().atMost(5, SECONDS).untilAsserted(() -> {
then(this.beanWithScheduledMethod.isExecuted()).isTrue();
differentSpanHasBeenSetThan(firstSpan);
});
}
@Test
public void should_not_create_span_in_the_scheduled_class_that_matches_skip_pattern()
throws Exception {
await().atMost(5, SECONDS).untilAsserted(() -> {
then(this.beanWithScheduledMethodToBeIgnored.isExecuted()).isTrue();
then(this.beanWithScheduledMethodToBeIgnored.getSpan()).isNull();
});
}
private void spanIsSetOnAScheduledMethod() {
Span storedSpan = TracingOnScheduledTests.this.beanWithScheduledMethod.getSpan();
then(storedSpan).isNotNull();
then(storedSpan.context().traceId()).isNotNull();
zipkin2.Span foundSpan = this.reporter.getSpans().stream()
.filter(span -> !span.tags().containsKey("error")
&& span.tags().containsValue("TestBeanWithScheduledMethod"))
.findFirst().orElseThrow(() -> new AssertionError("Span is missing"));
then(foundSpan.tags()).contains(
new AbstractMap.SimpleEntry<>("class", "TestBeanWithScheduledMethod"),
new AbstractMap.SimpleEntry<>("method", "scheduledMethod"));
then(foundSpan.durationAsLong()).isGreaterThan(0L);
}
private void spanIsSetOnAScheduledMethodWithErrorTag() {
Span storedSpan = TracingOnScheduledTests.this.beanWithScheduledMethod.getSpan();
then(storedSpan).isNotNull();
then(storedSpan.context().traceId()).isNotNull();
zipkin2.Span foundSpan = this.reporter.getSpans().stream()
.filter(span -> span.tags().containsKey("error")).findFirst()
.orElseThrow(() -> new AssertionError("Span is missing"));
then(foundSpan.tags()).contains(
new AbstractMap.SimpleEntry<>("class",
"TestBeanWithScheduledMethodThatThrowsAnException"),
new AbstractMap.SimpleEntry<>("method", "scheduledMethod"));
then(foundSpan.durationAsLong()).isGreaterThan(0L);
then(foundSpan.tags().get("error")).isNotEmpty();
}
private void differentSpanHasBeenSetThan(final Span spanToCompare) {
then(TracingOnScheduledTests.this.beanWithScheduledMethod.getSpan())
.isNotEqualTo(spanToCompare);
}
}
@Configuration
@DefaultTestAutoConfiguration
@EnableScheduling
class ScheduledTestConfiguration {
@Bean
Reporter<zipkin2.Span> testRepoter() {
return new ArrayListSpanReporter();
}
@Bean
TestBeanWithScheduledMethod testBeanWithScheduledMethod(Tracing tracing) {
return new TestBeanWithScheduledMethod(tracing);
}
@Bean
TestBeanWithScheduledMethodToBeIgnored testBeanWithScheduledMethodToBeIgnored(
Tracing tracing) {
return new TestBeanWithScheduledMethodToBeIgnored(tracing);
}
@Bean
TestBeanWithScheduledMethodThatThrowsAnException throwsAnException(Tracing tracing) {
return new TestBeanWithScheduledMethodThatThrowsAnException(tracing);
}
@Bean
Sampler alwaysSampler() {
return Sampler.ALWAYS_SAMPLE;
}
}
class TestBeanWithScheduledMethod {
private static final Log log = LogFactory.getLog(TestBeanWithScheduledMethod.class);
private final Tracing tracing;
Span span;
AtomicBoolean executed = new AtomicBoolean(false);
TestBeanWithScheduledMethod(Tracing tracing) {
this.tracing = tracing;
}
@Scheduled(fixedDelay = 1000L)
public void scheduledMethod() {
this.span = this.tracing.tracer().currentSpan();
this.executed.set(true);
}
public Span getSpan() {
return this.span;
}
public AtomicBoolean isExecuted() {
return this.executed;
}
public void clear() {
this.span = null;
this.executed.set(false);
}
}
class TestBeanWithScheduledMethodThatThrowsAnException {
private static final Log log = LogFactory.getLog(TestBeanWithScheduledMethod.class);
private final Tracing tracing;
Span span;
AtomicBoolean executed = new AtomicBoolean(false);
TestBeanWithScheduledMethodThatThrowsAnException(Tracing tracing) {
this.tracing = tracing;
}
@Scheduled(fixedDelay = 1L)
public void scheduledMethod() {
log.info("Running the scheduled method");
this.span = this.tracing.tracer().currentSpan();
log.info("Stored the span " + this.span + " as current span");
this.executed.set(true);
throw new RuntimeException("HELLO");
}
public Span getSpan() {
return this.span;
}
public AtomicBoolean isExecuted() {
return this.executed;
}
public void clear() {
this.span = null;
this.executed.set(false);
}
}
class TestBeanWithScheduledMethodToBeIgnored {
private final Tracing tracing;
Span span;
AtomicBoolean executed = new AtomicBoolean(false);
TestBeanWithScheduledMethodToBeIgnored(Tracing tracing) {
this.tracing = tracing;
}
@Scheduled(fixedDelay = 1000L)
public void scheduledMethodToIgnore() {
this.span = this.tracing.tracer().currentSpan();
this.executed.set(true);
}
public Span getSpan() {
return this.span;
}
public AtomicBoolean isExecuted() {
return this.executed;
}
public void clear() {
this.executed.set(false);
}
}

View File

@@ -1,71 +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.instrument.web;
import brave.Tracing;
import org.junit.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.sleuth.autoconfig.SleuthProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
/**
* Base for specifications that use Spring's {@link MockMvc}. Provides also
* {@link WebApplicationContext}, {@link ApplicationContext}. The latter you can use to
* specify what kind of address should be returned for a given dependency name.
*
* @see WebApplicationContext
* @see ApplicationContext
* @author 4finance IT
*/
@WebAppConfiguration
public abstract class AbstractMvcIntegrationTest {
@Autowired
protected WebApplicationContext webApplicationContext;
protected MockMvc mockMvc;
@Autowired
protected SleuthProperties properties;
@Autowired
protected Tracing tracing;
@Before
public void setup() {
DefaultMockMvcBuilder mockMvcBuilder = MockMvcBuilders
.webAppContextSetup(this.webApplicationContext);
configureMockMvcBuilder(mockMvcBuilder);
this.mockMvc = mockMvcBuilder.build();
}
/**
* Override in a subclass to modify mockMvcBuilder configuration (e.g. add filter).
* <p>
* The method from super class should be called.
* @param mockMvcBuilder builder to configure
*/
protected void configureMockMvcBuilder(DefaultMockMvcBuilder mockMvcBuilder) {
}
}

View File

@@ -1,238 +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.instrument.web;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
import brave.Span;
import brave.Tracer;
import brave.sampler.Sampler;
import org.awaitility.Awaitility;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.SpanName;
import org.springframework.cloud.sleuth.instrument.DefaultTestAutoConfiguration;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.test.context.junit4.SpringRunner;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.BDDAssertions.then;
@RunWith(SpringRunner.class)
@SpringBootTest(
classes = { TraceAsyncIntegrationTests.TraceAsyncITestConfiguration.class },
properties = "spring.sleuth.http.legacy.enabled=true")
public class TraceAsyncIntegrationTests {
@Autowired
ClassPerformingAsyncLogic classPerformingAsyncLogic;
@Autowired
Tracer tracer;
@Autowired
ArrayListSpanReporter reporter;
@Before
public void cleanup() {
this.reporter.clear();
this.classPerformingAsyncLogic.clear();
}
@Test
public void should_set_span_on_an_async_annotated_method() {
whenAsyncProcessingTakesPlace();
thenANewAsyncSpanGetsCreated();
}
@Test
public void should_set_span_with_custom_method_on_an_async_annotated_method() {
whenAsyncProcessingTakesPlaceWithCustomSpanName();
thenAsyncSpanHasCustomName();
}
@Test
public void should_continue_a_span_on_an_async_annotated_method() {
Span span = givenASpanInCurrentThread();
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
whenAsyncProcessingTakesPlace();
}
finally {
span.finish();
}
thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOne(span);
}
@Test
public void should_continue_a_span_with_custom_method_on_an_async_annotated_method() {
Span span = givenASpanInCurrentThread();
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
whenAsyncProcessingTakesPlaceWithCustomSpanName();
}
finally {
span.finish();
}
thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOneAndSpanHasCustomName(span);
}
private Span givenASpanInCurrentThread() {
return this.tracer.nextSpan().name("http:existing");
}
private void whenAsyncProcessingTakesPlace() {
this.classPerformingAsyncLogic.invokeAsynchronousLogic();
}
private void whenAsyncProcessingTakesPlaceWithCustomSpanName() {
this.classPerformingAsyncLogic.customNameInvokeAsynchronousLogic();
}
private void thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOne(final Span span) {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
then(TraceAsyncIntegrationTests.this.classPerformingAsyncLogic.getSpan()
.context().traceId()).isEqualTo(span.context().traceId());
then(this.reporter.getSpans()).hasSize(2);
// HTTP
then(this.reporter.getSpans().get(0).name()).isEqualTo("http:existing");
// ASYNC
then(this.reporter.getSpans().get(1).tags())
.containsEntry("class", "ClassPerformingAsyncLogic")
.containsEntry("method", "invokeAsynchronousLogic");
});
}
private void thenANewAsyncSpanGetsCreated() {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
then(this.reporter.getSpans()).hasSize(1);
zipkin2.Span storedSpan = this.reporter.getSpans().get(0);
then(storedSpan.name()).isEqualTo("invoke-asynchronous-logic");
then(storedSpan.tags()).containsEntry("class", "ClassPerformingAsyncLogic")
.containsEntry("method", "invokeAsynchronousLogic");
});
}
private void thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOneAndSpanHasCustomName(
final Span span) {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
then(TraceAsyncIntegrationTests.this.classPerformingAsyncLogic.getSpan()
.context().traceId()).isEqualTo(span.context().traceId());
then(this.reporter.getSpans()).hasSize(2);
// HTTP
then(this.reporter.getSpans().get(0).name()).isEqualTo("http:existing");
// ASYNC
then(this.reporter.getSpans().get(1).tags())
.containsEntry("class", "ClassPerformingAsyncLogic")
.containsEntry("method", "customNameInvokeAsynchronousLogic");
});
}
private void thenAsyncSpanHasCustomName() {
Awaitility.await().atMost(5, SECONDS).untilAsserted(() -> {
then(this.reporter.getSpans()).hasSize(1);
zipkin2.Span storedSpan = this.reporter.getSpans().get(0);
then(storedSpan.name()).isEqualTo("foo");
then(storedSpan.tags()).containsEntry("class", "ClassPerformingAsyncLogic")
.containsEntry("method", "customNameInvokeAsynchronousLogic");
});
}
@After
public void cleanTrace() {
this.reporter.clear();
}
@DefaultTestAutoConfiguration
@EnableAsync
@Configuration
static class TraceAsyncITestConfiguration {
@Bean
ClassPerformingAsyncLogic asyncClass(Tracer tracer) {
return new ClassPerformingAsyncLogic(tracer);
}
@Bean
Sampler defaultSampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
ArrayListSpanReporter reporter() {
return new ArrayListSpanReporter();
}
@Bean
Executor fooExecutor() {
return new SimpleAsyncTaskExecutor();
}
@Bean
Executor barExecutor() {
return new SimpleAsyncTaskExecutor();
}
}
static class ClassPerformingAsyncLogic {
private final Tracer tracer;
AtomicReference<Span> span = new AtomicReference<>();
ClassPerformingAsyncLogic(Tracer tracer) {
this.tracer = tracer;
}
@Async("fooExecutor")
public void invokeAsynchronousLogic() {
this.span.set(this.tracer.currentSpan());
}
@Async
@SpanName("foo")
public void customNameInvokeAsynchronousLogic() {
this.span.set(this.tracer.currentSpan());
}
public Span getSpan() {
return this.span.get();
}
public void clear() {
this.span.set(null);
}
}
}

View File

@@ -1,158 +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.instrument.web;
import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import brave.Span;
import brave.http.HttpTracing;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent;
import org.springframework.cloud.sleuth.util.SpanUtil;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.filter.GenericFilterBean;
import static org.assertj.core.api.BDDAssertions.then;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = TraceCustomFilterResponseInjectorTests.Config.class,
webEnvironment = RANDOM_PORT)
@DirtiesContext
public class TraceCustomFilterResponseInjectorTests {
static final String TRACE_ID_NAME = "X-B3-TraceId";
static final String SPAN_ID_NAME = "X-B3-SpanId";
@Autowired
RestTemplate restTemplate;
@Autowired
Config config;
@Autowired
CustomRestController customRestController;
@Test
@SuppressWarnings("unchecked")
public void should_inject_trace_and_span_ids_in_response_headers() {
RequestEntity<?> requestEntity = RequestEntity
.get(URI.create("http://localhost:" + this.config.port + "/headers"))
.build();
@SuppressWarnings("rawtypes")
ResponseEntity<Map> responseEntity = this.restTemplate.exchange(requestEntity,
Map.class);
then(responseEntity.getHeaders()).containsKeys(TRACE_ID_NAME, SPAN_ID_NAME)
.as("Trace headers must be present in response headers");
}
@Configuration
@EnableAutoConfiguration
static class Config implements ApplicationListener<ServletWebServerInitializedEvent> {
int port;
// tag::configuration[]
@Bean
HttpResponseInjectingTraceFilter responseInjectingTraceFilter(
HttpTracing httpTracing) {
return new HttpResponseInjectingTraceFilter(httpTracing);
}
// end::configuration[]
@Override
public void onApplicationEvent(ServletWebServerInitializedEvent event) {
this.port = event.getSource().getPort();
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
CustomRestController customRestController() {
return new CustomRestController();
}
}
// tag::injector[]
static class HttpResponseInjectingTraceFilter extends GenericFilterBean {
private final HttpTracing httpTracing;
HttpResponseInjectingTraceFilter(HttpTracing httpTracing) {
this.httpTracing = httpTracing;
}
@Override
public void doFilter(ServletRequest request, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
Span currentSpan = this.httpTracing.tracing().tracer().currentSpan();
response.addHeader("X-B3-TraceId", currentSpan.context().traceIdString());
response.addHeader("X-B3-SpanId",
SpanUtil.idToHex(currentSpan.context().spanId()));
filterChain.doFilter(request, response);
}
}
// end::injector[]
@RestController
static class CustomRestController {
@RequestMapping("/headers")
public Map<String, String> headers(@RequestHeader HttpHeaders headers) {
Map<String, String> map = new HashMap<>();
for (String key : headers.keySet()) {
map.put(key, headers.getFirst(key));
}
return map;
}
}
}

View File

@@ -1,423 +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.instrument.web;
import java.io.IOException;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import brave.Span;
import brave.Tracer;
import brave.sampler.Sampler;
import brave.servlet.TracingFilter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.instrument.DefaultTestAutoConfiguration;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.cloud.sleuth.util.SpanUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.filter.GenericFilterBean;
import org.springframework.web.util.NestedServletException;
import static org.assertj.core.api.Assertions.fail;
import static org.assertj.core.api.BDDAssertions.then;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TraceFilterIntegrationTests.Config.class,
properties = "spring.sleuth.http.legacy.enabled=true")
public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest {
static final String TRACE_ID_NAME = "X-B3-TraceId";
static final String SPAN_ID_NAME = "X-B3-SpanId";
static final String SAMPLED_NAME = "X-B3-Sampled";
private static Log logger = LogFactory.getLog(TraceFilterIntegrationTests.class);
private static Span span;
@Autowired
TracingFilter traceFilter;
@Autowired
MyFilter myFilter;
@Autowired
ArrayListSpanReporter reporter;
@Autowired
Tracer tracer;
@Before
@After
public void clearSpans() {
this.reporter.clear();
}
@Test
public void should_create_a_trace() throws Exception {
whenSentPingWithoutTracingData();
then(this.reporter.getSpans()).hasSize(1);
zipkin2.Span span = this.reporter.getSpans().get(0);
then(span.tags()).containsKey(TraceWebFilter.MVC_CONTROLLER_CLASS_KEY)
.containsKey(TraceWebFilter.MVC_CONTROLLER_METHOD_KEY);
then(this.tracer.currentSpan()).isNull();
}
@Test
public void should_ignore_sampling_the_span_if_uri_matches_management_properties_context_path()
throws Exception {
MvcResult mvcResult = whenSentInfoWithTraceId(new Random().nextLong());
// https://github.com/spring-cloud/spring-cloud-sleuth/issues/327
// we don't want to respond with any tracing data
then(notSampledHeaderIsPresent(mvcResult)).isEqualTo(false);
then(this.reporter.getSpans()).isEmpty();
then(this.tracer.currentSpan()).isNull();
}
@Test
public void when_traceId_is_sent_should_not_create_a_new_one_but_return_the_existing_one_instead()
throws Exception {
Long expectedTraceId = new Random().nextLong();
whenSentPingWithTraceId(expectedTraceId);
then(this.reporter.getSpans()).hasSize(1);
then(this.tracer.currentSpan()).isNull();
}
@Test
public void when_message_is_sent_should_eventually_clear_mdc() throws Exception {
Long expectedTraceId = new Random().nextLong();
whenSentPingWithTraceId(expectedTraceId);
then(MDC.getCopyOfContextMap()).isEmpty();
then(this.reporter.getSpans()).hasSize(1);
then(this.tracer.currentSpan()).isNull();
}
@Test
public void when_traceId_is_sent_to_async_endpoint_span_is_joined() throws Exception {
Long expectedTraceId = new Random().nextLong();
MvcResult mvcResult = whenSentFutureWithTraceId(expectedTraceId);
this.mockMvc.perform(asyncDispatch(mvcResult)).andExpect(status().isOk())
.andReturn();
then(this.tracer.currentSpan()).isNull();
}
@Test
public void should_add_a_custom_tag_to_the_span_created_in_controller()
throws Exception {
Long expectedTraceId = new Random().nextLong();
MvcResult mvcResult = whenSentDeferredWithTraceId(expectedTraceId);
this.mockMvc.perform(asyncDispatch(mvcResult)).andExpect(status().isOk())
.andReturn();
Optional<zipkin2.Span> taggedSpan = this.reporter.getSpans().stream()
.filter(span -> span.tags().containsKey("tag")).findFirst();
then(taggedSpan.isPresent()).isTrue();
then(taggedSpan.get().tags()).containsEntry("tag", "value")
.containsEntry("mvc.controller.method", "deferredMethod")
.containsEntry("mvc.controller.class", "TestController");
then(this.tracer.currentSpan()).isNull();
}
@Test
public void should_log_tracing_information_when_404_exception_was_thrown()
throws Exception {
Long expectedTraceId = new Random().nextLong();
whenSentToNonExistentEndpointWithTraceId(expectedTraceId);
// it's a span with the same ids
then(this.reporter.getSpans()).hasSize(1);
zipkin2.Span serverSpan = this.reporter.getSpans().get(0);
then(serverSpan.tags()).containsEntry("custom", "tag")
.containsEntry("http.status_code", "404");
then(this.tracer.currentSpan()).isNull();
}
@Test
public void should_log_tracing_information_when_500_exception_was_thrown()
throws Exception {
Long expectedTraceId = new Random().nextLong();
try {
whenSentToExceptionThrowingEndpoint(expectedTraceId);
fail("Should fail");
}
catch (NestedServletException e) {
then(e).hasRootCauseInstanceOf(RuntimeException.class);
}
// we need to dump the span cause it's not in TracingFilter since TF
// has also error dispatch and the ErrorController would report the span
then(this.reporter.getSpans()).hasSize(1);
then(this.reporter.getSpans().get(0).tags()).containsEntry("error",
"Request processing failed; nested exception is java.lang.RuntimeException");
}
@Test
public void should_assume_that_a_request_without_span_and_with_trace_is_a_root_span()
throws Exception {
Long expectedTraceId = new Random().nextLong();
whenSentRequestWithTraceIdAndNoSpanId(expectedTraceId);
whenSentRequestWithTraceIdAndNoSpanId(expectedTraceId);
then(this.reporter.getSpans().stream()
.filter(span -> span.id().equals(span.traceId())).findAny().isPresent())
.as("a root span exists").isTrue();
then(this.tracer.currentSpan()).isNull();
}
@Test
public void should_return_custom_response_headers_when_custom_trace_filter_gets_registered()
throws Exception {
Long expectedTraceId = new Random().nextLong();
MvcResult mvcResult = whenSentPingWithTraceId(expectedTraceId);
then(mvcResult.getResponse().getHeader("ZIPKIN-TRACE-ID"))
.isEqualTo(SpanUtil.idToHex(expectedTraceId));
then(this.reporter.getSpans()).hasSize(1);
then(this.reporter.getSpans().get(0).tags()).containsEntry("custom", "tag");
}
@Override
protected void configureMockMvcBuilder(DefaultMockMvcBuilder mockMvcBuilder) {
mockMvcBuilder.addFilters(this.traceFilter, this.myFilter);
}
private MvcResult whenSentPingWithoutTracingData() throws Exception {
return this.mockMvc
.perform(MockMvcRequestBuilders.get("/ping").accept(MediaType.TEXT_PLAIN))
.andReturn();
}
private MvcResult whenSentPingWithTraceId(Long passedTraceId) throws Exception {
return sendPingWithTraceId(TRACE_ID_NAME, passedTraceId);
}
private MvcResult whenSentInfoWithTraceId(Long passedTraceId) throws Exception {
return sendRequestWithTraceId("/additionalContextPath/info", TRACE_ID_NAME,
passedTraceId);
}
private MvcResult whenSentFutureWithTraceId(Long passedTraceId) throws Exception {
return sendRequestWithTraceId("/future", TRACE_ID_NAME, passedTraceId);
}
private MvcResult whenSentDeferredWithTraceId(Long passedTraceId) throws Exception {
return sendDeferredWithTraceId(TRACE_ID_NAME, passedTraceId);
}
private MvcResult whenSentToNonExistentEndpointWithTraceId(Long passedTraceId)
throws Exception {
return sendRequestWithTraceId("/exception/nonExistent", TRACE_ID_NAME,
passedTraceId, HttpStatus.NOT_FOUND);
}
private MvcResult whenSentToExceptionThrowingEndpoint(Long passedTraceId)
throws Exception {
return sendRequestWithTraceId("/throwsException", TRACE_ID_NAME, passedTraceId,
HttpStatus.INTERNAL_SERVER_ERROR);
}
private MvcResult sendPingWithTraceId(String headerName, Long traceId)
throws Exception {
return sendRequestWithTraceId("/ping", headerName, traceId);
}
private MvcResult sendDeferredWithTraceId(String headerName, Long traceId)
throws Exception {
return sendRequestWithTraceId("/deferred", headerName, traceId);
}
private MvcResult sendRequestWithTraceId(String path, String headerName, Long traceId)
throws Exception {
return this.mockMvc
.perform(MockMvcRequestBuilders.get(path).accept(MediaType.TEXT_PLAIN)
.header(headerName, SpanUtil.idToHex(traceId))
.header(SPAN_ID_NAME, SpanUtil.idToHex(new Random().nextLong())))
.andReturn();
}
private MvcResult whenSentRequestWithTraceIdAndNoSpanId(Long traceId)
throws Exception {
return this.mockMvc
.perform(MockMvcRequestBuilders.get("/ping").accept(MediaType.TEXT_PLAIN)
.header(TRACE_ID_NAME, SpanUtil.idToHex(traceId)))
.andReturn();
}
private MvcResult sendRequestWithTraceId(String path, String headerName, Long traceId,
HttpStatus status) throws Exception {
return this.mockMvc
.perform(MockMvcRequestBuilders.get(path).accept(MediaType.TEXT_PLAIN)
.header(headerName, SpanUtil.idToHex(traceId))
.header(SPAN_ID_NAME, SpanUtil.idToHex(new Random().nextLong())))
.andExpect(status().is(status.value())).andReturn();
}
private boolean notSampledHeaderIsPresent(MvcResult mvcResult) {
return "0".equals(mvcResult.getResponse().getHeader(SAMPLED_NAME));
}
@DefaultTestAutoConfiguration
@Configuration
protected static class Config {
private static final Log log = LogFactory.getLog(Config.class);
@Bean
public ArrayListSpanReporter testSpanReporter() {
return new ArrayListSpanReporter();
}
@Bean
Sampler alwaysSampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
@Order(TraceWebServletAutoConfiguration.TRACING_FILTER_ORDER + 1)
Filter myFilter(Tracer tracer) {
return new MyFilter(tracer);
}
@RestController
public static class TestController {
@Autowired
private Tracer tracer;
@RequestMapping("/ping")
public String ping() {
logger.info("ping");
span = this.tracer.currentSpan();
return "ping";
}
@RequestMapping("/throwsException")
public void throwsException() {
throw new RuntimeException();
}
@RequestMapping("/deferred")
public DeferredResult<String> deferredMethod() {
logger.info("deferred");
span = this.tracer.currentSpan();
span.tag("tag", "value");
DeferredResult<String> result = new DeferredResult<>();
result.setResult("deferred");
return result;
}
@RequestMapping("/future")
public CompletableFuture<String> future() {
logger.info("future");
return CompletableFuture.completedFuture("ping");
}
}
@Configuration
static class ManagementServer {
@Bean
@Primary
ManagementServerProperties managementServerProperties() {
ManagementServerProperties managementServerProperties = new ManagementServerProperties();
managementServerProperties.getServlet()
.setContextPath("/additionalContextPath");
return managementServerProperties;
}
}
}
}
// tag::response_headers[]
@Component
@Order(TraceWebServletAutoConfiguration.TRACING_FILTER_ORDER + 1)
class MyFilter extends GenericFilterBean {
private final Tracer tracer;
MyFilter(Tracer tracer) {
this.tracer = tracer;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
Span currentSpan = this.tracer.currentSpan();
if (currentSpan == null) {
chain.doFilter(request, response);
return;
}
// for readability we're returning trace id in a hex form
((HttpServletResponse) response).addHeader("ZIPKIN-TRACE-ID",
currentSpan.context().traceIdString());
// we can also add some custom tags
currentSpan.tag("custom", "tag");
chain.doFilter(request, response);
}
}
// end::response_headers[]

View File

@@ -1,236 +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.instrument.web;
import java.io.IOException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.PreDestroy;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import brave.Span;
import brave.Tracing;
import brave.sampler.Sampler;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.filter.GenericFilterBean;
import static org.assertj.core.api.BDDAssertions.then;
/**
* @author Marcin Grzejszczak
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { TraceFilterWebIntegrationMultipleFiltersTests.Config.class },
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = "spring.sleuth.http.legacy.enabled=true")
public class TraceFilterWebIntegrationMultipleFiltersTests {
@Autowired
Tracing tracer;
@Autowired
RestTemplate restTemplate;
@Autowired
Environment environment;
@Autowired
MyFilter myFilter;
@Autowired
ArrayListSpanReporter reporter;
// issue #550
@Autowired
@Qualifier("myExecutor")
Executor myExecutor;
@Autowired
@Qualifier("finalExecutor")
Executor finalExecutor;
@Autowired
MyExecutor cglibExecutor;
@Test
public void should_register_trace_filter_before_the_custom_filter() {
this.myExecutor.execute(() -> System.out.println("foo"));
this.cglibExecutor.execute(() -> System.out.println("foo"));
this.finalExecutor.execute(() -> System.out.println("foo"));
this.restTemplate.getForObject("http://localhost:" + port() + "/", String.class);
then(this.tracer.tracer().currentSpan()).isNull();
then(this.myFilter.getSpan().get()).isNotNull();
then(this.reporter.getSpans()).isNotEmpty();
}
private int port() {
return this.environment.getProperty("local.server.port", Integer.class);
}
@EnableAutoConfiguration
@Configuration
public static class Config {
// issue #550
@Bean
Executor myExecutor() {
return new MyExecutorWithFinalMethod();
}
// issue #550
@Bean
MyExecutor cglibExecutor() {
return new MyExecutor();
}
// issue #550
@Bean
MyFinalExecutor finalExecutor() {
return new MyFinalExecutor();
}
@Bean
Sampler alwaysSampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
@Override
public void handleError(ClientHttpResponse response) throws IOException {
}
});
return restTemplate;
}
@Bean
MyFilter myFilter(Tracing tracer) {
return new MyFilter(tracer);
}
@Bean
FilterRegistrationBean registrationBean(MyFilter myFilter) {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(myFilter);
bean.setOrder(0);
return bean;
}
@Bean
ArrayListSpanReporter reporter() {
return new ArrayListSpanReporter();
}
}
static class MyFilter extends GenericFilterBean {
private final Tracing tracer;
AtomicReference<Span> span = new AtomicReference<>();
MyFilter(Tracing tracer) {
this.tracer = tracer;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
Span currentSpan = this.tracer.tracer().currentSpan();
this.span.set(currentSpan);
}
public AtomicReference<Span> getSpan() {
return this.span;
}
}
static class MyExecutor implements Executor {
private final Executor delegate = Executors.newSingleThreadExecutor();
@Override
public void execute(Runnable command) {
this.delegate.execute(command);
}
@PreDestroy
public void destroy() {
((ExecutorService) this.delegate).shutdown();
}
}
static class MyExecutorWithFinalMethod implements Executor {
private final Executor delegate = Executors.newSingleThreadExecutor();
@Override
public final void execute(Runnable command) {
this.delegate.execute(command);
}
@PreDestroy
public void destroy() {
((ExecutorService) this.delegate).shutdown();
}
}
static final class MyFinalExecutor implements Executor {
private final Executor delegate = Executors.newSingleThreadExecutor();
@Override
public void execute(Runnable command) {
this.delegate.execute(command);
}
@PreDestroy
public void destroy() {
((ExecutorService) this.delegate).shutdown();
}
}
}

View File

@@ -1,212 +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.instrument.web;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import brave.Tracing;
import brave.http.HttpAdapter;
import brave.http.HttpSampler;
import brave.sampler.Sampler;
import org.assertj.core.api.BDDAssertions;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import zipkin2.Span;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.rule.OutputCapture;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.fail;
import static org.assertj.core.api.BDDAssertions.then;
/**
* @author Marcin Grzejszczak
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TraceFilterWebIntegrationTests.Config.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = "spring.sleuth.http.legacy.enabled=true")
public class TraceFilterWebIntegrationTests {
@Rule
public OutputCapture capture = new OutputCapture();
@Autowired
Tracing tracer;
@Autowired
ArrayListSpanReporter accumulator;
@Autowired
@ServerSampler
HttpSampler sampler;
@Autowired
Environment environment;
@Before
@After
public void cleanup() {
this.accumulator.clear();
}
@Test
public void should_not_create_a_span_for_error_controller() {
try {
new RestTemplate().getForObject("http://localhost:" + port() + "/",
String.class);
BDDAssertions.fail("should fail due to runtime exception");
}
catch (Exception e) {
}
then(Tracing.current().tracer().currentSpan()).isNull();
then(this.accumulator.getSpans()).hasSize(1);
Span fromFirstTraceFilterFlow = this.accumulator.getSpans().get(0);
then(fromFirstTraceFilterFlow.tags()).containsEntry("http.status_code", "500")
.containsEntry("http.method", "GET")
.containsEntry("mvc.controller.class", "ExceptionThrowingController")
.containsEntry("error",
"Request processing failed; nested exception is java.lang.RuntimeException: Throwing exception");
// issue#714
String hex = fromFirstTraceFilterFlow.traceId();
String[] split = this.capture.toString().split("\n");
List<String> list = Arrays.stream(split)
.filter(s -> s.contains("Uncaught exception thrown"))
.filter(s -> s.contains(hex + "," + hex + ",true]"))
.collect(Collectors.toList());
then(list).isNotEmpty();
}
@Test
public void should_create_spans_for_endpoint_returning_unsuccessful_result() {
try {
new RestTemplate().getForObject(
"http://localhost:" + port() + "/test_bad_request", String.class);
fail("should throw exception");
}
catch (HttpClientErrorException e) {
}
then(Tracing.current().tracer().currentSpan()).isNull();
then(this.accumulator.getSpans()).hasSize(1);
then(this.accumulator.getSpans().get(0).kind().ordinal())
.isEqualTo(Span.Kind.SERVER.ordinal());
then(this.accumulator.getSpans().get(0).tags()).containsEntry("http.status_code",
"400");
then(this.accumulator.getSpans().get(0).tags()).containsEntry("http.path",
"/test_bad_request");
}
@Test
public void should_inject_http_sampler() {
then(this.sampler).isNotNull();
}
private int port() {
return this.environment.getProperty("local.server.port", Integer.class);
}
@EnableAutoConfiguration
@Configuration
public static class Config {
@Bean
ExceptionThrowingController controller() {
return new ExceptionThrowingController();
}
@Bean
ArrayListSpanReporter reporter() {
return new ArrayListSpanReporter();
}
@Bean
Sampler alwaysSampler() {
return Sampler.ALWAYS_SAMPLE;
}
// tag::custom_server_sampler[]
@Bean(name = ServerSampler.NAME)
HttpSampler myHttpSampler(SkipPatternProvider provider) {
Pattern pattern = provider.skipPattern();
return new HttpSampler() {
@Override
public <Req> Boolean trySample(HttpAdapter<Req, ?> adapter, Req request) {
String url = adapter.path(request);
boolean shouldSkip = pattern.matcher(url).matches();
if (shouldSkip) {
return false;
}
return null;
}
};
}
// end::custom_server_sampler[]
@Bean
RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
@Override
public void handleError(ClientHttpResponse response) throws IOException {
}
});
return restTemplate;
}
}
@RestController
public static class ExceptionThrowingController {
@RequestMapping("/")
public void throwException() {
throw new RuntimeException("Throwing exception");
}
@RequestMapping(path = "/test_bad_request", method = RequestMethod.GET)
public ResponseEntity<?> processFail() {
return ResponseEntity.badRequest().build();
}
}
}

View File

@@ -1,46 +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.instrument.web;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author Marcin Grzejszczak
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { TraceWebDisabledTests.Config.class }, properties = {
"spring.sleuth.web.enabled=true", "spring.sleuth.web.client.enabled=false" })
public class TraceWebDisabledTests {
@Test
public void should_load_context() {
}
@Configuration
@EnableAutoConfiguration
public static class Config {
}
}

View File

@@ -1,571 +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.instrument.web;
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.concurrent.NotThreadSafe;
import brave.Span;
import brave.Tracer;
import brave.sampler.Sampler;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.assertj.core.api.BDDAssertions;
import org.awaitility.Awaitility;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.actuate.trace.http.HttpTrace;
import org.springframework.boot.actuate.trace.http.HttpTraceRepository;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.sleuth.DisableWebFluxSecurity;
import org.springframework.cloud.sleuth.annotation.ContinueSpan;
import org.springframework.cloud.sleuth.annotation.NewSpan;
import org.springframework.cloud.sleuth.instrument.reactor.TraceReactorAutoConfigurationAccessorConfiguration;
import org.springframework.cloud.sleuth.instrument.web.client.TraceWebClientAutoConfiguration;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import static org.assertj.core.api.BDDAssertions.then;
@NotThreadSafe
public class TraceWebFluxTests {
public static final String EXPECTED_TRACE_ID = "b919095138aa4c6e";
@Test
public void should_instrument_web_filter() throws Exception {
// setup
TraceReactorAutoConfigurationAccessorConfiguration.close();
ConfigurableApplicationContext context = new SpringApplicationBuilder(
TraceWebFluxTests.Config.class)
.web(WebApplicationType.REACTIVE)
.properties("server.port=0", "spring.jmx.enabled=false",
"spring.sleuth.web.skipPattern=/skipped",
"spring.application.name=TraceWebFluxTests",
"security.basic.enabled=false",
"management.security.enabled=false")
.run();
ArrayListSpanReporter accumulator = context.getBean(ArrayListSpanReporter.class);
int port = context.getBean(Environment.class).getProperty("local.server.port",
Integer.class);
Controller2 controller2 = context.getBean(Controller2.class);
clean(accumulator, controller2);
// when
ClientResponse response = whenRequestIsSent(port);
// then
thenSpanWasReportedWithTags(accumulator, response);
clean(accumulator, controller2);
// when
ClientResponse functionResponse = whenRequestIsSentToFunction(port);
// then
thenSpanWasReportedForFunction(accumulator, functionResponse);
accumulator.clear();
// when
ClientResponse nonSampledResponse = whenNonSampledRequestIsSent(port);
// then
thenNoSpanWasReported(accumulator, nonSampledResponse, controller2);
accumulator.clear();
// when
ClientResponse skippedPatternResponse = whenRequestIsSentToSkippedPattern(port);
// then
thenNoSpanWasReported(accumulator, skippedPatternResponse, controller2);
// some other tests
SleuthSpanCreatorAspectWebFlux bean = context
.getBean(SleuthSpanCreatorAspectWebFlux.class);
bean.setPort(port);
// then
bean.shouldContinueSpanInWebFlux();
bean.shouldCreateNewSpanInWebFlux();
bean.shouldCreateNewSpanInWebFluxInSubscriberContext();
bean.shouldReturnSpanFromWebFluxSubscriptionContext();
bean.shouldReturnSpanFromWebFluxTraceContext();
bean.shouldSetupCorrectSpanInHttpTrace();
// cleanup
context.close();
TraceReactorAutoConfigurationAccessorConfiguration.close();
}
private void clean(ArrayListSpanReporter accumulator, Controller2 controller2) {
accumulator.clear();
controller2.span = null;
}
private void thenSpanWasReportedWithTags(ArrayListSpanReporter accumulator,
ClientResponse response) {
Awaitility.await()
.untilAsserted(() -> then(response.statusCode().value()).isEqualTo(200));
List<zipkin2.Span> spans = accumulator.getSpans().stream()
.filter(span -> "get /api/c2/{id}".equals(span.name()))
.collect(Collectors.toList());
then(spans).hasSize(1);
then(spans.get(0).name()).isEqualTo("get /api/c2/{id}");
then(spans.get(0).tags()).containsEntry("mvc.controller.method", "successful")
.containsEntry("mvc.controller.class", "Controller2");
}
private void thenSpanWasReportedForFunction(ArrayListSpanReporter accumulator,
ClientResponse response) {
Awaitility.await()
.untilAsserted(() -> then(response.statusCode().value()).isEqualTo(200));
List<zipkin2.Span> spans = accumulator.getSpans().stream()
.filter(span -> "get".equals(span.name())).collect(Collectors.toList());
then(spans).hasSize(1);
}
private void thenNoSpanWasReported(ArrayListSpanReporter accumulator,
ClientResponse response, Controller2 controller2) {
Awaitility.await().untilAsserted(() -> {
then(response.statusCode().value()).isEqualTo(200);
then(accumulator.getSpans()).isEmpty();
});
then(controller2.span).isNotNull();
then(controller2.span.context().traceIdString()).isEqualTo(EXPECTED_TRACE_ID);
}
private ClientResponse whenRequestIsSent(int port) {
Mono<ClientResponse> exchange = WebClient.create().get()
.uri("http://localhost:" + port + "/api/c2/10").exchange();
return exchange.block();
}
private ClientResponse whenRequestIsSentToFunction(int port) {
Mono<ClientResponse> exchange = WebClient.create().get()
.uri("http://localhost:" + port + "/function").exchange();
return exchange.block();
}
private ClientResponse whenRequestIsSentToSkippedPattern(int port) {
Mono<ClientResponse> exchange = WebClient.create().get()
.uri("http://localhost:" + port + "/skipped").exchange();
return exchange.block();
}
private ClientResponse whenNonSampledRequestIsSent(int port) {
Mono<ClientResponse> exchange = WebClient.create().get()
.uri("http://localhost:" + port + "/api/c2/10")
.header("X-B3-SpanId", EXPECTED_TRACE_ID)
.header("X-B3-TraceId", EXPECTED_TRACE_ID).header("X-B3-Sampled", "0")
.exchange();
return exchange.block();
}
@Configuration
@EnableAutoConfiguration(exclude = { TraceWebClientAutoConfiguration.class })
@DisableWebFluxSecurity
static class Config {
@Bean
WebClient webClient() {
return WebClient.create();
}
@Bean
Sampler sampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
ArrayListSpanReporter spanReporter() {
return new ArrayListSpanReporter();
}
@Bean
Controller2 controller2(Tracer tracer) {
return new Controller2(tracer);
}
@Bean
TestEndpoint testEndpoint() {
return new TestEndpoint();
}
@Bean
RouterFunction<ServerResponse> function() {
return RouterFunctions.route(RequestPredicates.GET("/function"), r -> {
then(MDC.get("X-B3-TraceId")).isNotEmpty();
return ServerResponse.ok().syncBody("functionOk");
});
}
@Bean
TestBean testBean(Tracer tracer) {
return new TestBean(tracer);
}
@Bean
SleuthSpanCreatorAspectWebFlux.AccessLoggingHttpTraceRepository accessLoggingHttpTraceRepository() {
return new SleuthSpanCreatorAspectWebFlux.AccessLoggingHttpTraceRepository();
}
@Bean
SleuthSpanCreatorAspectWebFlux sleuthSpanCreatorAspectWebFlux(Tracer tracer,
SleuthSpanCreatorAspectWebFlux.AccessLoggingHttpTraceRepository repository,
ArrayListSpanReporter reporter) {
return new SleuthSpanCreatorAspectWebFlux(tracer, repository, reporter);
}
}
@RestController
static class Controller2 {
private final Tracer tracer;
Span span;
Controller2(Tracer tracer) {
this.tracer = tracer;
}
@GetMapping("/api/c2/{id}")
public Flux<String> successful(@PathVariable Long id) {
// #786
then(MDC.get("X-B3-TraceId")).isNotEmpty();
this.span = this.tracer.currentSpan();
return Flux.just(id.toString());
}
@GetMapping("/skipped")
public Flux<String> skipped() {
Boolean sampled = this.tracer.currentSpan().context().sampled();
then(sampled).isFalse();
return Flux.just(sampled.toString());
}
}
@RestController
@RequestMapping("/test")
static class TestEndpoint {
private static final Logger log = LoggerFactory.getLogger(TestEndpoint.class);
@Autowired
Tracer tracer;
@Autowired
TestBean testBean;
@GetMapping("/ping")
Mono<Long> ping() {
log.info("ping");
return Mono.just(this.tracer.currentSpan().context().spanId());
}
@GetMapping("/pingFromContext")
Mono<Long> pingFromContext() {
log.info("pingFromContext");
return Mono.subscriberContext()
.doOnSuccess(context -> log.info("Ping from context"))
.flatMap(context -> Mono
.just(this.tracer.currentSpan().context().spanId()));
}
@GetMapping("/continueSpan")
Mono<Long> continueSpan() {
log.info("continueSpan");
return this.testBean.continueSpanInTraceContext();
}
@GetMapping("/newSpan1")
Mono<Long> newSpan1() {
log.info("newSpan1");
return this.testBean.newSpanInTraceContext();
}
@GetMapping("/newSpan2")
Mono<Long> newSpan2() {
log.info("newSpan2");
return this.testBean.newSpanInSubscriberContext();
}
}
}
class SleuthSpanCreatorAspectWebFlux {
private static final Log log = LogFactory
.getLog(SleuthSpanCreatorAspectWebFlux.class);
private final Tracer tracer;
private final SleuthSpanCreatorAspectWebFlux.AccessLoggingHttpTraceRepository repository;
private final ArrayListSpanReporter reporter;
int port;
private WebTestClient webClient;
SleuthSpanCreatorAspectWebFlux(Tracer tracer,
AccessLoggingHttpTraceRepository repository, ArrayListSpanReporter reporter) {
this.tracer = tracer;
this.repository = repository;
this.reporter = reporter;
}
private static String toHexString(Long value) {
BDDAssertions.then(value).isNotNull();
return StringUtils.leftPad(Long.toHexString(value), 16, '0');
}
void setPort(int port) {
this.port = port;
}
public void setup() {
this.reporter.clear();
this.repository.clear();
log.info("Running app on port [" + this.port + "]");
this.webClient = WebTestClient.bindToServer()
.baseUrl("http://localhost:" + this.port).build();
}
public void shouldReturnSpanFromWebFluxTraceContext() {
setup();
Mono<Object> mono = this.webClient.get().uri("/test/ping").exchange()
.returnResult(Object.class).getResponseBody().single();
Object object = mono.block();
log.info("Received [" + object + "]");
Long newSpanId = (Long) object;
Awaitility.await().untilAsserted(() -> {
List<zipkin2.Span> spans = getSpans();
zipkin2.Span spanToFind = spans.stream()
.filter(span -> "get /test/ping".equals(span.name())).findFirst()
.orElseThrow(() -> new AssertionError(
"No span with name [get /test/ping] found"));
then(spanToFind.kind()).isEqualTo(zipkin2.Span.Kind.SERVER);
then(spanToFind.name()).isEqualTo("get /test/ping");
then(spanToFind.id()).isEqualTo(toHexString(newSpanId));
then(this.tracer.currentSpan()).isNull();
});
}
private List<zipkin2.Span> getSpans() {
List<zipkin2.Span> spans = this.reporter.getSpans();
log.info("Reported the following spans: \n\n" + spans);
return spans;
}
public void shouldReturnSpanFromWebFluxSubscriptionContext() {
setup();
Mono<Object> mono = this.webClient.get().uri("/test/pingFromContext").exchange()
.returnResult(Object.class).getResponseBody().single();
Object object = mono.block();
log.info("Received [" + object + "]");
Long newSpanId = (Long) object;
Awaitility.await().untilAsserted(() -> {
List<zipkin2.Span> spans = getSpans();
zipkin2.Span pingFromContext = spans.stream()
.filter(span -> "get /test/pingfromcontext".equals(span.name()))
.findFirst().orElseThrow(() -> new AssertionError(
"No span with name [get /test/pingfromcontext] found"));
then(pingFromContext.name()).isEqualTo("get /test/pingfromcontext");
then(pingFromContext.kind()).isEqualTo(zipkin2.Span.Kind.SERVER);
then(pingFromContext.id()).isEqualTo(toHexString(newSpanId));
then(this.tracer.currentSpan()).isNull();
});
}
public void shouldContinueSpanInWebFlux() {
setup();
Mono<Object> mono = this.webClient.get().uri("/test/continueSpan").exchange()
.returnResult(Object.class).getResponseBody().single();
Object object = mono.block();
log.info("Received [" + object + "]");
Long newSpanId = (Long) object;
Awaitility.await().untilAsserted(() -> {
List<zipkin2.Span> spans = getSpans();
zipkin2.Span spanToFind = spans.stream()
.filter(span -> "get /test/continuespan".equals(span.name()))
.findFirst().orElseThrow(() -> new AssertionError(
"No span with name [get /test/continuespan] found"));
then(spanToFind.kind()).isEqualTo(zipkin2.Span.Kind.SERVER);
then(spanToFind.name()).isEqualTo("get /test/continuespan");
then(spanToFind.id()).isEqualTo(toHexString(newSpanId));
then(this.tracer.currentSpan()).isNull();
});
}
public void shouldCreateNewSpanInWebFlux() {
setup();
Mono<Object> mono = this.webClient.get().uri("/test/newSpan1").exchange()
.returnResult(Object.class).getResponseBody().single();
Object object = mono.block();
log.info("Received [" + object + "]");
Long newSpanId = (Long) object;
Awaitility.await().untilAsserted(() -> {
then(spanWithName("new-span-in-trace-context").id())
.isEqualTo(toHexString(newSpanId));
then(spanWithName("get /test/newspan1").kind())
.isEqualTo(zipkin2.Span.Kind.SERVER);
then(this.tracer.currentSpan()).isNull();
});
}
private zipkin2.Span spanWithName(String name) {
return getSpans().stream().filter(span -> name.equals(span.name())).findFirst()
.orElseThrow(() -> new AssertionError(
"Span with name [" + name + "] not found"));
}
public void shouldCreateNewSpanInWebFluxInSubscriberContext() {
setup();
Mono<Object> mono = this.webClient.get().uri("/test/newSpan2").exchange()
.returnResult(Object.class).getResponseBody().single();
Object object = mono.block();
log.info("Received [" + object + "]");
Long newSpanId = (Long) object;
Awaitility.await().untilAsserted(() -> {
then(spanWithName("new-span-in-subscriber-context").id())
.isEqualTo(toHexString(newSpanId));
then(spanWithName("get /test/newspan2").kind())
.isEqualTo(zipkin2.Span.Kind.SERVER);
then(this.tracer.currentSpan()).isNull();
});
}
public void shouldSetupCorrectSpanInHttpTrace() {
setup();
Mono<Object> mono = this.webClient.get().uri("/test/ping").exchange()
.returnResult(Object.class).getResponseBody().single();
Object object = mono.block();
log.info("Received [" + object + "]");
Long newSpanId = (Long) object;
Awaitility.await().untilAsserted(() -> {
then(spanWithName("get /test/ping").kind())
.isEqualTo(zipkin2.Span.Kind.SERVER);
then(this.repository.getSpan()).isNotNull();
then(spanWithName("get /test/ping").id()).isEqualTo(toHexString(newSpanId))
.isEqualTo(this.repository.getSpan().context().traceIdString());
then(this.tracer.currentSpan()).isNull();
});
}
static class AccessLoggingHttpTraceRepository implements HttpTraceRepository {
private static final Log log = LogFactory.getLog(
SleuthSpanCreatorAspectWebFlux.AccessLoggingHttpTraceRepository.class);
@Autowired
Tracer tracer;
brave.Span span;
@Override
public List<HttpTrace> findAll() {
log.info("Find all executed");
return null;
}
@Override
public void add(HttpTrace trace) {
this.span = this.tracer.currentSpan();
log.info("Setting span [" + this.span + "]");
}
public brave.Span getSpan() {
return this.span;
}
public void clear() {
this.span = null;
}
}
}
class TestBean {
private static final Logger log = LoggerFactory.getLogger(TestBean.class);
private final Tracer tracer;
TestBean(Tracer tracer) {
this.tracer = tracer;
}
@ContinueSpan
public Mono<Long> continueSpanInTraceContext() {
log.info("Continue");
Long span = this.tracer.currentSpan().context().spanId();
return Mono.defer(() -> Mono.just(span));
}
@NewSpan(name = "newSpanInTraceContext")
public Mono<Long> newSpanInTraceContext() {
log.info("New Span in Trace Context");
return Mono.defer(() -> Mono.just(this.tracer.currentSpan().context().spanId()));
}
@NewSpan(name = "newSpanInSubscriberContext")
public Mono<Long> newSpanInSubscriberContext() {
log.info("New Span in Subscriber Context");
return Mono.subscriberContext()
.doOnSuccess(context -> log.info("New Span in deferred Trace Context"))
.flatMap(context -> Mono.defer(
() -> Mono.just(this.tracer.currentSpan().context().spanId())));
}
}

View File

@@ -1,78 +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.instrument.web;
import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Michał Ziemba
*/
public class TraceWebServletAutoConfigurationTests {
private static final String EXCEPTION_LOGGING_FILTER_BEAN_NAME = "exceptionThrowingFilter";
private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(TraceAutoConfiguration.class,
TraceHttpAutoConfiguration.class, TraceWebAutoConfiguration.class,
TraceWebServletAutoConfiguration.class));
@Test
public void shouldCreateExceptionLoggingFilterBeanByDefault() {
this.contextRunner.run((context) -> {
assertThat(context).hasBean(EXCEPTION_LOGGING_FILTER_BEAN_NAME);
});
}
@Test
public void shouldCreateExceptionLoggingFilterBeanIfExplicitlyEnabled() {
this.contextRunner
.withPropertyValues(
"spring.sleuth.web.exception-logging-filter-enabled=true")
.run((context) -> {
assertThat(context).hasBean(EXCEPTION_LOGGING_FILTER_BEAN_NAME);
});
}
@Test
public void shouldNotCreateExceptionLoggingFilterBeanIfDisabledInProperties() {
this.contextRunner
.withPropertyValues(
"spring.sleuth.web.exception-logging-filter-enabled=false")
.run((context) -> {
assertThat(context)
.doesNotHaveBean(EXCEPTION_LOGGING_FILTER_BEAN_NAME);
});
}
@Test
public void shouldNotCreateExceptionLoggingFilterBeanIfDisabledInPropertiesUsingCamelCase() {
this.contextRunner
.withPropertyValues(
"spring.sleuth.web.exceptionLoggingFilterEnabled=false")
.run((context) -> {
assertThat(context)
.doesNotHaveBean(EXCEPTION_LOGGING_FILTER_BEAN_NAME);
});
}
}

View File

@@ -1,129 +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.instrument.web.client;
import brave.ScopedSpan;
import brave.Tracer;
import brave.sampler.Sampler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.assertj.core.api.BDDAssertions;
import org.junit.Test;
import org.junit.runner.RunWith;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientResponseException;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@RunWith(SpringRunner.class)
public class GH1102Tests {
@Autowired
Tracer tracer;
@Autowired
WebClient webClient;
@Autowired
TestRetry testRetry;
@Autowired
ArrayListSpanReporter reporter;
@LocalServerPort
int port;
@Test
public void should_store_retries_as_separate_spans() throws Exception {
ScopedSpan foo = this.tracer.startScopedSpan("foo");
try {
this.webClient.get().uri("http://localhost:" + this.port + "/test").retrieve()
.bodyToMono(String.class).retry(1).block();
BDDAssertions.fail("should throw exception");
}
catch (WebClientResponseException ex) {
}
finally {
foo.finish();
}
BDDAssertions.then(this.testRetry.getHttpHeaders().get("x-b3-traceid"))
.hasSize(1);
}
@EnableAutoConfiguration
@Configuration
static class WebConfig {
@Bean
Sampler sampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
ArrayListSpanReporter reporter() {
return new ArrayListSpanReporter();
}
@Bean
WebClient webClient() {
return WebClient.builder().build();
}
@Bean
TestRetry testRetry() {
return new TestRetry();
}
}
@RestController
static class TestRetry {
private static final Log log = LogFactory.getLog(TestRetry.class);
private MultiValueMap<String, String> httpHeaders;
@GetMapping("test")
Mono<String> test(@RequestHeader MultiValueMap<String, String> map) {
this.httpHeaders = map;
log.info("Processing test. Headers [" + this.httpHeaders + "]");
return Mono.error(new RuntimeException("BOOM!"));
}
MultiValueMap<String, String> getHttpHeaders() {
return this.httpHeaders;
}
}
}

View File

@@ -1,177 +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.instrument.web.client;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import brave.Tracer;
import brave.Tracing;
import brave.sampler.Sampler;
import org.assertj.core.api.BDDAssertions;
import org.awaitility.Awaitility;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import zipkin2.Span;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.AsyncRestTemplate;
import static org.assertj.core.api.BDDAssertions.then;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
/**
* @author Marcin Grzejszczak
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(
classes = { TraceWebAsyncClientAutoConfigurationTests.TestConfiguration.class },
webEnvironment = RANDOM_PORT)
public class TraceWebAsyncClientAutoConfigurationTests {
@Autowired
AsyncRestTemplate asyncRestTemplate;
@Autowired
Environment environment;
@Autowired
ArrayListSpanReporter accumulator;
@Autowired
Tracing tracer;
@Before
public void setup() {
this.accumulator.clear();
}
@Test
public void should_close_span_upon_success_callback()
throws ExecutionException, InterruptedException {
brave.Span initialSpan = this.tracer.tracer().nextSpan().name("foo");
try (Tracer.SpanInScope ws = this.tracer.tracer()
.withSpanInScope(initialSpan.start())) {
ListenableFuture<ResponseEntity<String>> future = this.asyncRestTemplate
.getForEntity("http://localhost:" + port() + "/foo", String.class);
String result = future.get().getBody();
then(result).isEqualTo("foo");
}
finally {
initialSpan.finish();
}
Awaitility.await().untilAsserted(() -> {
then(this.accumulator.getSpans().stream()
.filter(span -> Span.Kind.CLIENT == span.kind()).findFirst().get())
.matches(span -> span.duration() >= TimeUnit.MILLISECONDS
.toMicros(100));
then(this.tracer.tracer().currentSpan()).isNull();
});
}
@Test
public void should_close_span_upon_failure_callback()
throws ExecutionException, InterruptedException {
ListenableFuture<ResponseEntity<String>> future;
try {
future = this.asyncRestTemplate.getForEntity(
"http://localhost:" + port() + "/blowsup", String.class);
future.get();
BDDAssertions.fail("should throw an exception from the controller");
}
catch (Exception e) {
}
Awaitility.await().untilAsserted(() -> {
Span reportedRpcSpan = new ArrayList<>(this.accumulator.getSpans()).stream()
.filter(span -> Span.Kind.CLIENT == span.kind()).findFirst().get();
then(reportedRpcSpan).matches(
span -> span.duration() >= TimeUnit.MILLISECONDS.toMicros(100));
then(reportedRpcSpan.tags()).containsKey("error");
then(this.tracer.tracer().currentSpan()).isNull();
});
}
int port() {
return this.environment.getProperty("local.server.port", Integer.class);
}
@EnableAutoConfiguration(
// spring boot test will otherwise instrument the client and server with the
// same bean factory
// which isn't expected
exclude = TraceWebServletAutoConfiguration.class)
@Configuration
public static class TestConfiguration {
@Bean
ArrayListSpanReporter reporter() {
return new ArrayListSpanReporter();
}
@Bean
MyController myController() {
return new MyController();
}
@Bean
Sampler sampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
AsyncRestTemplate restTemplate() {
return new AsyncRestTemplate();
}
}
@RestController
public static class MyController {
@RequestMapping("/foo")
String foo() throws Exception {
Thread.sleep(100);
return "foo";
}
@RequestMapping("/blowsup")
String blowsup() throws Exception {
Thread.sleep(100);
throw new RuntimeException("boom");
}
}
}

View File

@@ -1,201 +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.instrument.web.client.exceptionresolver;
import java.time.Instant;
import javax.servlet.http.HttpServletRequest;
import brave.Span;
import brave.Tracing;
import brave.sampler.Sampler;
import com.fasterxml.jackson.annotation.JsonInclude;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import static org.assertj.core.api.BDDAssertions.then;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestConfig.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class Issue585Tests {
TestRestTemplate testRestTemplate = new TestRestTemplate();
@Autowired
ArrayListSpanReporter reporter;
@LocalServerPort
int port;
@Test
public void should_report_span_when_using_custom_exception_resolver() {
ResponseEntity<String> entity = this.testRestTemplate.getForEntity(
"http://localhost:" + this.port + "/sleuthtest?greeting=foo",
String.class);
then(Tracing.current().tracer().currentSpan()).isNull();
then(entity.getStatusCode().value()).isEqualTo(500);
then(this.reporter.getSpans().get(0).tags()).containsEntry("custom", "tag")
.containsKeys("error");
}
}
@SpringBootApplication
class TestConfig {
@Bean
ArrayListSpanReporter testSpanReporter() {
return new ArrayListSpanReporter();
}
@Bean
Sampler testSampler() {
return Sampler.ALWAYS_SAMPLE;
}
}
@RestController
class TestController {
private final static Logger logger = LoggerFactory.getLogger(TestController.class);
@RequestMapping(value = "sleuthtest", method = RequestMethod.GET)
public ResponseEntity<String> testSleuth(@RequestParam String greeting) {
if (greeting.equalsIgnoreCase("hello")) {
return new ResponseEntity<>("Hello World", HttpStatus.OK);
}
else {
throw new RuntimeException("This is a test error");
}
}
}
@ControllerAdvice
class CustomExceptionHandler extends ResponseEntityExceptionHandler {
private final static Logger logger = LoggerFactory
.getLogger(CustomExceptionHandler.class);
@Autowired
private Tracing tracer;
@ExceptionHandler(Exception.class)
protected ResponseEntity<ExceptionResponse> handleDefaultError(Exception ex,
HttpServletRequest request) {
ExceptionResponse exceptionResponse = new ExceptionResponse("ERR-01",
ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR,
request.getRequestURI(), Instant.now().toEpochMilli());
reportErrorSpan(ex.getMessage());
return new ResponseEntity<>(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
private void reportErrorSpan(String message) {
Span span = this.tracer.tracer().currentSpan();
span.annotate("ERROR: " + message);
span.tag("custom", "tag");
logger.info("Foo");
}
}
@JsonInclude(JsonInclude.Include.NON_NULL)
class ExceptionResponse {
private String errorCode;
private String errorMessage;
private HttpStatus httpStatus;
private String path;
private Long epochTime;
ExceptionResponse(String errorCode, String errorMessage, HttpStatus httpStatus,
String path, Long epochTime) {
this.errorCode = errorCode;
this.errorMessage = errorMessage;
this.httpStatus = httpStatus;
this.path = path;
this.epochTime = epochTime;
}
public String getErrorCode() {
return this.errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public String getErrorMessage() {
return this.errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public HttpStatus getHttpStatus() {
return this.httpStatus;
}
public void setHttpStatus(HttpStatus httpStatus) {
this.httpStatus = httpStatus;
}
public String getPath() {
return this.path;
}
public void setPath(String path) {
this.path = path;
}
public Long getEpochTime() {
return this.epochTime;
}
public void setEpochTime(Long epochTime) {
this.epochTime = epochTime;
}
}

View File

@@ -1,128 +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.instrument.web.client.feign.issues.issue307;
import java.util.ArrayList;
import java.util.List;
import brave.sampler.Sampler;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@FeignClient("participants")
interface ParticipantsClient {
@RequestMapping(method = RequestMethod.GET, value = "/races/{raceId}")
List<Object> getParticipants(@PathVariable("raceId") String raceId);
}
public class Issue307Tests {
@Test
public void should_start_context() {
try (ConfigurableApplicationContext applicationContext = SpringApplication.run(
SleuthSampleApplication.class, "--spring.jmx.enabled=false",
"--server.port=0")) {
// code
}
}
}
@EnableAutoConfiguration
@Import({ ParticipantsBean.class })
@RestController
@EnableFeignClients
@EnableCircuitBreaker
class SleuthSampleApplication {
private static final Logger LOG = LoggerFactory
.getLogger(SleuthSampleApplication.class.getName());
@Autowired
private RestTemplate restTemplate;
@Autowired
private Environment environment;
@Autowired
private ParticipantsBean participantsBean;
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
@Bean
public Sampler defaultSampler() {
return Sampler.ALWAYS_SAMPLE;
}
@RequestMapping("/")
public String home() {
LOG.info("you called home");
return "Hello World";
}
@RequestMapping("/callhome")
public String callHome() {
LOG.info("calling home");
return this.restTemplate.getForObject("http://localhost:" + port(), String.class);
}
private int port() {
return this.environment.getProperty("local.server.port", Integer.class);
}
}
@Component
class ParticipantsBean {
@Autowired
private ParticipantsClient participantsClient;
@HystrixCommand(fallbackMethod = "defaultParticipants")
public List<Object> getParticipants(String raceId) {
return this.participantsClient.getParticipants(raceId);
}
public List<Object> defaultParticipants(String raceId) {
return new ArrayList<>();
}
}

View File

@@ -1,162 +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.instrument.web.client.feign.issues.issue350;
import java.util.List;
import java.util.concurrent.ExecutionException;
import brave.Tracing;
import brave.sampler.Sampler;
import feign.Logger;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
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.boot.test.web.client.TestRestTemplate;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import static org.assertj.core.api.BDDAssertions.then;
@FeignClient(name = "myFeignClient", url = "localhost:9988")
interface MyFeignClient {
@RequestMapping("/service/ok")
String ok();
@RequestMapping("/service/not-ok")
String exp();
}
/**
* @author Marcin Grzejszczak
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class,
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@TestPropertySource(properties = { "ribbon.eureka.enabled=false",
"feign.hystrix.enabled=false", "server.port=9988" })
public class Issue350Tests {
TestRestTemplate template = new TestRestTemplate();
@Autowired
Tracing tracer;
@Autowired
ArrayListSpanReporter reporter;
@Before
public void setup() {
this.reporter.clear();
}
@Test
public void should_successfully_work_without_hystrix() {
this.template.getForEntity("http://localhost:9988/sleuth/test-not-ok",
String.class);
List<Span> spans = this.reporter.getSpans();
then(spans).hasSize(1);
then(spans.get(0).tags()).containsEntry("http.status_code", "406");
}
}
@Configuration
@EnableAutoConfiguration(exclude = TraceWebServletAutoConfiguration.class)
@EnableFeignClients(basePackageClasses = { SleuthTestController.class })
class Application {
@Bean
public ServiceTestController serviceTestController() {
return new ServiceTestController();
}
@Bean
public SleuthTestController sleuthTestController() {
return new SleuthTestController();
}
@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
@Bean
public Sampler defaultSampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
public Reporter<Span> spanReporter() {
return new ArrayListSpanReporter();
}
}
@RestController
@RequestMapping(path = "/service")
class ServiceTestController {
@RequestMapping("/ok")
public String ok() throws InterruptedException, ExecutionException {
return "I'm OK";
}
@RequestMapping("/not-ok")
@ResponseStatus(HttpStatus.NOT_ACCEPTABLE)
public String notOk() throws InterruptedException, ExecutionException {
return "Not OK";
}
}
@RestController
@RequestMapping(path = "/sleuth")
class SleuthTestController {
@Autowired
private MyFeignClient myFeignClient;
@RequestMapping("/test-ok")
public String ok() throws InterruptedException, ExecutionException {
return this.myFeignClient.ok();
}
@RequestMapping("/test-not-ok")
public String notOk() throws InterruptedException, ExecutionException {
return this.myFeignClient.exp();
}
}

View File

@@ -1,277 +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.instrument.web.client.feign.issues.issue362;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import brave.Tracing;
import brave.sampler.Sampler;
import feign.Client;
import feign.Logger;
import feign.Request;
import feign.Response;
import feign.RetryableException;
import feign.Retryer;
import feign.codec.ErrorDecoder;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
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.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.fail;
import static org.assertj.core.api.BDDAssertions.then;
@FeignClient(value = "myFeignClient", url = "http://localhost:9998",
configuration = CustomConfig.class)
interface MyFeignClient {
@RequestMapping("/service/ok")
String ok();
@RequestMapping("/service/not-ok")
String exp();
}
/**
* @author Marcin Grzejszczak
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class,
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@TestPropertySource(properties = { "ribbon.eureka.enabled=false",
"feign.hystrix.enabled=false", "server.port=9998" })
public class Issue362Tests {
RestTemplate template = new RestTemplate();
@Autowired
FeignComponentAsserter feignComponentAsserter;
@Autowired
Tracing tracer;
@Autowired
ArrayListSpanReporter reporter;
@Before
public void setup() {
this.feignComponentAsserter.executedComponents.clear();
this.reporter.clear();
}
@Test
public void should_successfully_work_with_custom_error_decoder_when_sending_successful_request() {
String securedURl = "http://localhost:9998/sleuth/test-ok";
ResponseEntity<String> response = this.template.getForEntity(securedURl,
String.class);
then(response.getBody()).isEqualTo("I'm OK");
then(this.feignComponentAsserter.executedComponents).containsEntry(Client.class,
true);
List<Span> spans = this.reporter.getSpans();
then(spans).hasSize(1);
then(spans.get(0).tags()).containsEntry("http.path", "/service/ok");
}
@Test
public void should_successfully_work_with_custom_error_decoder_when_sending_failing_request() {
String securedURl = "http://localhost:9998/sleuth/test-not-ok";
try {
this.template.getForEntity(securedURl, String.class);
fail("should propagate an exception");
}
catch (Exception e) {
}
then(this.feignComponentAsserter.executedComponents)
.containsEntry(ErrorDecoder.class, true)
.containsEntry(Client.class, true);
List<Span> spans = this.reporter.getSpans();
// retries
then(spans).hasSize(5);
then(spans.stream().map(span -> span.tags().get("http.status_code"))
.collect(Collectors.toList())).containsOnly("409");
}
}
@Configuration
@EnableAutoConfiguration(exclude = TraceWebServletAutoConfiguration.class)
@EnableFeignClients(basePackageClasses = { SleuthTestController.class })
class Application {
@Bean
public ServiceTestController serviceTestController() {
return new ServiceTestController();
}
@Bean
public SleuthTestController sleuthTestController() {
return new SleuthTestController();
}
@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
@Bean
public Sampler defaultSampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
public FeignComponentAsserter testHolder() {
return new FeignComponentAsserter();
}
@Bean
public Reporter<Span> spanReporter() {
return new ArrayListSpanReporter();
}
}
class FeignComponentAsserter {
Map<Class, Boolean> executedComponents = new ConcurrentHashMap<>();
}
@Configuration
class CustomConfig {
@Bean
public ErrorDecoder errorDecoder(FeignComponentAsserter feignComponentAsserter) {
return new CustomErrorDecoder(feignComponentAsserter);
}
@Bean
public Retryer retryer() {
return new Retryer.Default();
}
@Bean
public Client client(FeignComponentAsserter feignComponentAsserter) {
return new CustomClient(feignComponentAsserter);
}
public static class CustomErrorDecoder extends ErrorDecoder.Default {
private final FeignComponentAsserter feignComponentAsserter;
CustomErrorDecoder(FeignComponentAsserter feignComponentAsserter) {
this.feignComponentAsserter = feignComponentAsserter;
}
@Override
public Exception decode(String methodKey, Response response) {
this.feignComponentAsserter.executedComponents.put(ErrorDecoder.class, true);
if (response.status() == 409) {
return new RetryableException(409, "Article not Ready",
Request.HttpMethod.GET, new Date());
}
else {
return super.decode(methodKey, response);
}
}
}
public static class CustomClient extends Client.Default {
private final FeignComponentAsserter feignComponentAsserter;
CustomClient(FeignComponentAsserter feignComponentAsserter) {
super(null, null);
this.feignComponentAsserter = feignComponentAsserter;
}
@Override
public Response execute(Request request, Request.Options options)
throws IOException {
this.feignComponentAsserter.executedComponents.put(Client.class, true);
return super.execute(request, options);
}
}
}
@RestController
@RequestMapping(path = "/service")
class ServiceTestController {
@RequestMapping("/ok")
public String ok() throws InterruptedException, ExecutionException {
return "I'm OK";
}
@RequestMapping("/not-ok")
@ResponseStatus(HttpStatus.CONFLICT)
public String notOk() throws InterruptedException, ExecutionException {
return "Not OK";
}
}
@RestController
@RequestMapping(path = "/sleuth")
class SleuthTestController {
@Autowired
private MyFeignClient myFeignClient;
@RequestMapping("/test-ok")
public String ok() throws InterruptedException, ExecutionException {
return this.myFeignClient.ok();
}
@RequestMapping("/test-not-ok")
public String notOk() throws InterruptedException, ExecutionException {
return this.myFeignClient.exp();
}
}

View File

@@ -1,152 +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.instrument.web.client.feign.issues.issue393;
import java.util.List;
import java.util.stream.Collectors;
import brave.Tracing;
import brave.sampler.Sampler;
import feign.okhttp.OkHttpClient;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
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.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.BDDAssertions.then;
@FeignClient(name = "no-name", url = "http://localhost:9978")
interface MyNameRemote {
@RequestMapping(value = "/name/{id}", method = RequestMethod.GET)
String getName(@PathVariable("id") String id);
}
/**
* @author Marcin Grzejszczak
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class,
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@TestPropertySource(properties = { "spring.application.name=demo-feign-uri",
"server.port=9978", "eureka.client.enabled=true", "ribbon.eureka.enabled=true" })
public class Issue393Tests {
RestTemplate template = new RestTemplate();
@Autowired
ArrayListSpanReporter reporter;
@Autowired
Tracing tracer;
@Before
public void open() {
this.reporter.clear();
}
@Test
public void should_successfully_work_when_service_discovery_is_on_classpath_and_feign_uses_url() {
String url = "http://localhost:9978/hello/mikesarver";
ResponseEntity<String> response = this.template.getForEntity(url, String.class);
then(response.getBody()).isEqualTo("mikesarver foo");
List<Span> spans = this.reporter.getSpans();
// retries
then(spans).hasSize(2);
then(spans.stream().map(span -> span.tags().get("http.path"))
.collect(Collectors.toList())).containsOnly("/name/mikesarver");
}
}
@Configuration
@EnableAutoConfiguration(exclude = TraceWebServletAutoConfiguration.class)
@EnableFeignClients
@EnableDiscoveryClient
class Application {
@Bean
public DemoController demoController(MyNameRemote myNameRemote) {
return new DemoController(myNameRemote);
}
// issue #513
@Bean
public OkHttpClient myOkHttpClient() {
return new OkHttpClient();
}
@Bean
public feign.Logger.Level feignLoggerLevel() {
return feign.Logger.Level.BASIC;
}
@Bean
public Sampler defaultSampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
public Reporter<Span> spanReporter() {
return new ArrayListSpanReporter();
}
}
@RestController
class DemoController {
private final MyNameRemote myNameRemote;
DemoController(MyNameRemote myNameRemote) {
this.myNameRemote = myNameRemote;
}
@RequestMapping("/hello/{name}")
public String getHello(@PathVariable("name") String name) {
return this.myNameRemote.getName(name) + " foo";
}
@RequestMapping("/name/{name}")
public String getName(@PathVariable("name") String name) {
return name;
}
}

View File

@@ -1,136 +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.instrument.web.client.feign.issues.issue502;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.List;
import brave.Tracing;
import brave.sampler.Sampler;
import feign.Client;
import feign.Request;
import feign.Response;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
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.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import static org.assertj.core.api.BDDAssertions.then;
@FeignClient(name = "foo", url = "https://non.existing.url")
interface MyNameRemote {
@RequestMapping(value = "/", method = RequestMethod.GET)
String get();
}
/**
* @author Marcin Grzejszczak
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class,
webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = { "feign.hystrix.enabled=false" })
public class Issue502Tests {
@Autowired
MyClient myClient;
@Autowired
MyNameRemote myNameRemote;
@Autowired
ArrayListSpanReporter reporter;
@Autowired
Tracing tracer;
@Before
public void open() {
this.reporter.clear();
}
@Test
public void should_reuse_custom_feign_client() {
String response = this.myNameRemote.get();
then(this.myClient.wasCalled()).isTrue();
then(response).isEqualTo("foo");
List<Span> spans = this.reporter.getSpans();
// retries
then(spans).hasSize(1);
then(spans.get(0).tags().get("http.path")).isEqualTo("/");
}
}
@Configuration
@EnableAutoConfiguration
@EnableFeignClients
class Application {
@Bean
public Client client() {
return new MyClient();
}
@Bean
public Sampler defaultSampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
public Reporter<Span> spanReporter() {
return new ArrayListSpanReporter();
}
}
class MyClient implements Client {
boolean wasCalled;
@Override
public Response execute(Request request, Request.Options options) throws IOException {
this.wasCalled = true;
return Response.builder().body("foo", Charset.forName("UTF-8"))
.request(Request.create(Request.HttpMethod.POST, "/foo", new HashMap<>(),
Request.Body.empty()))
.headers(new HashMap<>()).status(200).build();
}
boolean wasCalled() {
return this.wasCalled;
}
}

View File

@@ -1,47 +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.instrument.web.view;
import brave.sampler.Sampler;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@EnableAutoConfiguration
@Configuration
public class Issue469 extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/welcome").setViewName("welcome");
}
@Bean
ArrayListSpanReporter reporter() {
return new ArrayListSpanReporter();
}
@Bean
Sampler sampler() {
return Sampler.ALWAYS_SAMPLE;
}
}

View File

@@ -1,66 +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.instrument.web.view;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.core.env.Environment;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.BDDAssertions.then;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Issue469.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(properties = { "spring.mvc.view.prefix=/WEB-INF/jsp/",
"spring.mvc.view.suffix=.jsp" })
public class Issue469Tests {
@Autowired
ArrayListSpanReporter reporter;
@Autowired
Environment environment;
RestTemplate restTemplate = new RestTemplate();
@Test
public void should_not_result_in_tracing_exceptions_when_using_view_controllers()
throws Exception {
try {
this.restTemplate.getForObject("http://localhost:" + port() + "/welcome",
String.class);
}
catch (Exception e) {
// JSPs are not rendered
then(e).hasMessageContaining("404");
}
then(this.reporter.getSpans()).isNotEmpty();
}
private int port() {
return this.environment.getProperty("local.server.port", Integer.class);
}
}

View File

@@ -1,241 +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.instrument.zuul;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import brave.Span;
import brave.Tracer;
import brave.Tracing;
import brave.sampler.Sampler;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.assertj.core.api.BDDAssertions;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.StaticServerList;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.discovery.DiscoveryClientRouteLocator;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.BDDAssertions.then;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SampleZuulProxyApplication.class,
properties = { "zuul.routes.simple: /simple/**" },
webEnvironment = WebEnvironment.RANDOM_PORT)
@DirtiesContext
public class TraceZuulIntegrationTests {
private static final Log log = LogFactory.getLog(TraceZuulIntegrationTests.class);
@Autowired
Tracing tracing;
@Autowired
ArrayListSpanReporter spanAccumulator;
@Autowired
RestTemplate restTemplate;
@Value("${local.server.port}")
private int port;
@Before
@After
public void cleanup() {
this.spanAccumulator.clear();
}
@Test
public void should_close_span_when_routing_to_service_via_discovery() {
Span span = this.tracing.tracer().nextSpan().name("foo").start();
try (Tracer.SpanInScope ws = this.tracing.tracer().withSpanInScope(span)) {
ResponseEntity<String> result = this.restTemplate.exchange(
"http://localhost:" + this.port + "/simple/foo", HttpMethod.GET,
new HttpEntity<>((Void) null), String.class);
then(result.getStatusCode()).isEqualTo(HttpStatus.OK);
then(result.getBody()).isEqualTo("Hello world");
}
catch (Exception e) {
log.error(e);
throw e;
}
finally {
span.finish();
}
then(this.tracing.tracer().currentSpan()).isNull();
List<zipkin2.Span> spans = this.spanAccumulator.getSpans();
then(spans).isNotEmpty();
everySpanHasTheSameTraceId(spans);
everyParentIdHasItsCorrespondingSpan(spans);
}
@Test
public void should_close_span_when_routing_to_service_via_discovery_to_a_non_existent_url() {
Span span = this.tracing.tracer().nextSpan().name("foo").start();
try (Tracer.SpanInScope ws = this.tracing.tracer().withSpanInScope(span)) {
ResponseEntity<String> result = this.restTemplate.exchange(
"http://localhost:" + this.port + "/simple/nonExistentUrl",
HttpMethod.GET, new HttpEntity<>((Void) null), String.class);
then(result.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
}
finally {
span.finish();
}
then(this.tracing.tracer().currentSpan()).isNull();
List<zipkin2.Span> spans = this.spanAccumulator.getSpans();
then(spans).isNotEmpty();
everySpanHasTheSameTraceId(spans);
everyParentIdHasItsCorrespondingSpan(spans);
}
void everySpanHasTheSameTraceId(List<zipkin2.Span> actual) {
BDDAssertions.assertThat(actual).isNotNull();
List<String> traceIds = actual.stream().map(zipkin2.Span::traceId).distinct()
.collect(toList());
log.info("Stored traceids " + traceIds);
assertThat(traceIds).hasSize(1);
}
void everyParentIdHasItsCorrespondingSpan(List<zipkin2.Span> actual) {
BDDAssertions.assertThat(actual).isNotNull();
List<String> parentSpanIds = actual.stream().map(zipkin2.Span::parentId)
.filter(Objects::nonNull).collect(toList());
List<String> spanIds = actual.stream().map(zipkin2.Span::id).distinct()
.collect(toList());
List<String> difference = new ArrayList<>(parentSpanIds);
difference.removeAll(spanIds);
log.info("Difference between parent ids and span ids " + difference.stream()
.map(span -> "id as hex [" + span + "]").collect(joining("\n")));
assertThat(spanIds).containsAll(parentSpanIds);
}
}
// Don't use @SpringBootApplication because we don't want to component scan
@Configuration
@EnableAutoConfiguration
@RestController
@EnableZuulProxy
@RibbonClient(name = "simple", configuration = SimpleRibbonClientConfiguration.class)
class SampleZuulProxyApplication {
@RequestMapping("/foo")
public String home() {
return "Hello world";
}
@RequestMapping("/exception")
public String exception() {
throw new RuntimeException();
}
@Bean
RouteLocator routeLocator(DiscoveryClient discoveryClient,
ZuulProperties zuulProperties) {
return new MyRouteLocator("/", discoveryClient, zuulProperties);
}
@Bean
ArrayListSpanReporter testSpanReporter() {
return new ArrayListSpanReporter();
}
@Bean
RestTemplate restTemplate() {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setReadTimeout(5000);
RestTemplate restTemplate = new RestTemplate(factory);
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
@Override
public void handleError(ClientHttpResponse response) throws IOException {
}
});
return restTemplate;
}
@Bean
Sampler alwaysSampler() {
return Sampler.ALWAYS_SAMPLE;
}
}
class MyRouteLocator extends DiscoveryClientRouteLocator {
MyRouteLocator(String servletPath, DiscoveryClient discovery,
ZuulProperties properties) {
super(servletPath, discovery, properties);
}
}
// Load balancer with fixed server list for "simple" pointing to localhost
@Configuration
class SimpleRibbonClientConfiguration {
@Value("${local.server.port}")
private int port;
@Bean
public ServerList<Server> ribbonServerList() {
return new StaticServerList<>(new Server("localhost", this.port));
}
}

View File

@@ -1,135 +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.instrument.zuul.issues.issue634;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import brave.Tracing;
import brave.http.HttpTracing;
import brave.sampler.Sampler;
import com.netflix.zuul.ZuulFilter;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.BDDAssertions.then;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestZuulApplication.class, webEnvironment = RANDOM_PORT,
properties = { "feign.hystrix.enabled=false", "zuul.routes.dp.path:/display/**",
"zuul.routes.dp.path.url: http://localhost:9987/unknown" })
@DirtiesContext
public class Issue634Tests {
@LocalServerPort
int port;
@Autowired
HttpTracing tracer;
@Autowired
TraceCheckingSpanFilter filter;
@Autowired
ArrayListSpanReporter reporter;
@Test
public void should_reuse_custom_feign_client() {
for (int i = 0; i < 15; i++) {
new TestRestTemplate().getForEntity(
"http://localhost:" + this.port + "/display/ddd", String.class);
then(this.tracer.tracing().tracer().currentSpan()).isNull();
}
then(new HashSet<>(this.filter.counter.values()))
.describedAs("trace id should not be reused from thread").hasSize(1);
then(this.reporter.getSpans()).isNotEmpty();
}
}
@EnableZuulProxy
@EnableAutoConfiguration
@Configuration
class TestZuulApplication {
@Bean
TraceCheckingSpanFilter traceCheckingSpanFilter(Tracing tracer) {
return new TraceCheckingSpanFilter(tracer);
}
@Bean
Sampler sampler() {
return Sampler.ALWAYS_SAMPLE;
}
@Bean
ArrayListSpanReporter reporter() {
return new ArrayListSpanReporter();
}
}
class TraceCheckingSpanFilter extends ZuulFilter {
final Map<Long, Integer> counter = new ConcurrentHashMap<>();
private final Tracing tracer;
TraceCheckingSpanFilter(Tracing tracer) {
this.tracer = tracer;
}
@Override
public String filterType() {
return "post";
}
@Override
public int filterOrder() {
return -1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
long trace = this.tracer.tracer().currentSpan().context().traceId();
Integer integer = this.counter.getOrDefault(trace, 0);
this.counter.put(trace, integer + 1);
return null;
}
}

View File

@@ -1,3 +0,0 @@
spring.autoconfigure.exclude:
spring.data.jdbc.repositories.enabled: true
spring.data.jpa.repositories.enabled: true

View File

@@ -13,14 +13,9 @@ eureka.client.enabled: false
ribbon.eureka.enabled: false
spring.sleuth.scheduled.skipPattern: "^org.*TestBeanWithScheduledMethodToBeIgnored$"
# comma separated list of matchers
spring.sleuth.rxjava.schedulers.ignoredthreads: HystixMetricPoller,^MyCustomThread.*$,^RxComputation.*$
logging.level.org.springframework.cloud: DEBUG
logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR
logging.level.org.springframework.cloud.sleuth.instrument.web.client.feign: TRACE
#disable hibernate by default
spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration, org.springframework.cloud.gateway.config.GatewayAutoConfiguration, org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration
spring.data.jdbc.repositories.enabled: false
spring.data.jpa.repositories.enabled: false

View File

@@ -19,6 +19,7 @@
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<logger name="feign" level="DEBUG"/>
<logger name="com.netflix.discovery.InstanceInfoReplicator" level="ERROR"/>
<logger name="org.springframework" level="INFO"/>
<logger name="org.springframework.cloud.sleuth" level="TRACE"/>
<logger name="org.springframework.boot.autoconfigure.logging" level="INFO"/>
<logger name="org.springframework.cloud.sleuth.log" level="DEBUG"/>