committed by
GitHub
parent
e9ee0ba686
commit
e6b78d267f
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
logging.level.org.springframework.cloud: DEBUG
|
||||
logging.level.com.netflix.discovery.InstanceInfoReplicator: ERROR
|
||||
logging.level.org.springframework.cloud.sleuth.instrument.web.client.feign: TRACE
|
||||
|
||||
# comma separated list of matchers
|
||||
spring.sleuth.rxjava.schedulers.ignoredthreads: HystixMetricPoller,^MyCustomThread.*$,^RxComputation.*$
|
||||
Reference in New Issue
Block a user