committed by
GitHub
parent
e9ee0ba686
commit
e6b78d267f
@@ -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() {
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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[]
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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())));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<>();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
spring.autoconfigure.exclude:
|
||||
spring.data.jdbc.repositories.enabled: true
|
||||
spring.data.jpa.repositories.enabled: true
|
||||
@@ -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
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
Reference in New Issue
Block a user