* reactor Mono/Flux enhancement : post review fixes (methods renamed to reactor) * new span started on @ContinueSpan should be closed on completion * #1027 tests for reactor Mono/Flux support enhancement fixes gh-1027
This commit is contained in:
committed by
Marcin Grzejszczak
parent
04440fa959
commit
85761191d8
@@ -35,7 +35,7 @@ class DefaultNewSpanParser implements NewSpanParser {
|
||||
|
||||
@Override
|
||||
public void parse(MethodInvocation pjp, NewSpan newSpan, SpanCustomizer span) {
|
||||
String name = StringUtils.isEmpty(newSpan.name()) ?
|
||||
String name = newSpan == null || StringUtils.isEmpty(newSpan.name()) ?
|
||||
pjp.getMethod().getName() : newSpan.name();
|
||||
String changedName = SpanNameUtil.toLowerHyphen(name);
|
||||
if (log.isDebugEnabled()) {
|
||||
|
||||
@@ -16,17 +16,13 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.aop.ClassFilter;
|
||||
import org.springframework.aop.IntroductionInterceptor;
|
||||
import org.springframework.aop.Pointcut;
|
||||
@@ -40,6 +36,15 @@ import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Custom pointcut advisor that picks all classes / interfaces that
|
||||
@@ -143,19 +148,14 @@ class SleuthAdvisorConfig extends AbstractPointcutAdvisor implements BeanFactory
|
||||
|
||||
public boolean hasAnnotatedMethods(Class<?> clazz) {
|
||||
final AtomicBoolean found = new AtomicBoolean(false);
|
||||
ReflectionUtils.doWithMethods(clazz,
|
||||
new ReflectionUtils.MethodCallback() {
|
||||
@Override
|
||||
public void doWith(Method method) throws IllegalArgumentException,
|
||||
IllegalAccessException {
|
||||
if (found.get()) {
|
||||
return;
|
||||
}
|
||||
Annotation annotation = AnnotationUtils.findAnnotation(method,
|
||||
SleuthAdvisorConfig.AnnotationMethodsResolver.this.annotationType);
|
||||
if (annotation != null) { found.set(true); }
|
||||
}
|
||||
});
|
||||
ReflectionUtils.doWithMethods(clazz, method -> {
|
||||
if (found.get()) {
|
||||
return;
|
||||
}
|
||||
Annotation annotation = AnnotationUtils.findAnnotation(method,
|
||||
AnnotationMethodsResolver.this.annotationType);
|
||||
if (annotation != null) { found.set(true); }
|
||||
});
|
||||
return found.get();
|
||||
}
|
||||
|
||||
@@ -183,6 +183,7 @@ class SleuthInterceptor implements IntroductionInterceptor, BeanFactoryAware {
|
||||
if (method == null) {
|
||||
return invocation.proceed();
|
||||
}
|
||||
|
||||
Method mostSpecificMethod = AopUtils
|
||||
.getMostSpecificMethod(method, invocation.getThis().getClass());
|
||||
NewSpan newSpan = SleuthAnnotationUtils.findAnnotation(mostSpecificMethod, NewSpan.class);
|
||||
@@ -190,39 +191,135 @@ class SleuthInterceptor implements IntroductionInterceptor, BeanFactoryAware {
|
||||
if (newSpan == null && continueSpan == null) {
|
||||
return invocation.proceed();
|
||||
}
|
||||
|
||||
if(isReactorReturnType(method.getReturnType())){
|
||||
return proceedUnderReactorSpan(invocation, newSpan, continueSpan);
|
||||
} else {
|
||||
return proceedUnderSynchronousSpan(invocation, newSpan, continueSpan);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isReactorReturnType(Class<?> returnType) {
|
||||
return Flux.class.equals(returnType) || Mono.class.equals(returnType);
|
||||
}
|
||||
|
||||
private Object proceedUnderSynchronousSpan(
|
||||
MethodInvocation invocation, NewSpan newSpan, ContinueSpan continueSpan) throws Throwable {
|
||||
Span span = tracer().currentSpan();
|
||||
if (newSpan != null || span == null) {
|
||||
span = tracer().nextSpan().start();
|
||||
//in case of @ContinueSpan and no span in tracer we start new span and should close it on completion
|
||||
boolean startNewSpan = newSpan != null || span == null;
|
||||
if (startNewSpan) {
|
||||
span = tracer().nextSpan();
|
||||
newSpanParser().parse(invocation, newSpan, span);
|
||||
span.start();
|
||||
}
|
||||
String log = log(continueSpan);
|
||||
boolean hasLog = StringUtils.hasText(log);
|
||||
try (Tracer.SpanInScope ws = tracer().withSpanInScope(span)) {
|
||||
if (hasLog) {
|
||||
logEvent(span, log + ".before");
|
||||
}
|
||||
spanTagAnnotationHandler().addAnnotatedParameters(invocation);
|
||||
addTags(invocation, span);
|
||||
before(invocation, span, log, hasLog);
|
||||
return invocation.proceed();
|
||||
} catch (Exception e) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Exception occurred while trying to continue the pointcut", e);
|
||||
}
|
||||
if (hasLog) {
|
||||
logEvent(span, log + ".afterFailure");
|
||||
}
|
||||
span.error(e);
|
||||
onFailure(span, log, hasLog, e);
|
||||
throw e;
|
||||
} finally {
|
||||
if (hasLog) {
|
||||
logEvent(span, log + ".after");
|
||||
after(span, startNewSpan, log, hasLog);
|
||||
}
|
||||
}
|
||||
|
||||
private Object proceedUnderReactorSpan(
|
||||
MethodInvocation invocation, NewSpan newSpan, ContinueSpan continueSpan) throws Throwable{
|
||||
Span spanPrevious = tracer().currentSpan();
|
||||
//in case of @ContinueSpan and no span in tracer we start new span and should close it on completion
|
||||
boolean startNewSpan = newSpan != null || spanPrevious == null;
|
||||
Span span;
|
||||
if (startNewSpan) {
|
||||
span = tracer().nextSpan();
|
||||
newSpanParser().parse(invocation, newSpan, span);
|
||||
} else {
|
||||
span = spanPrevious;
|
||||
}
|
||||
|
||||
String log = log(continueSpan);
|
||||
boolean hasLog = StringUtils.hasText(log);
|
||||
|
||||
try(Tracer.SpanInScope ws = tracer().withSpanInScope(span)) {
|
||||
|
||||
Publisher<?> publisher = (Publisher) invocation.proceed();
|
||||
|
||||
Mono<Span> startSpan = Mono.defer(() -> withSpanInScope(span, () -> {
|
||||
if (startNewSpan) {
|
||||
span.start();
|
||||
}
|
||||
|
||||
before(invocation, span, log, hasLog);
|
||||
return Mono.just(span);
|
||||
}));
|
||||
|
||||
if(publisher instanceof Mono){
|
||||
return startSpan.flatMap(spanStarted -> ((Mono<?>)publisher)
|
||||
.doOnError(onFailureReactor(log, hasLog, spanStarted))
|
||||
.doOnTerminate(afterReactor(startNewSpan, log, hasLog, spanStarted)));
|
||||
}
|
||||
if (newSpan != null) {
|
||||
span.finish();
|
||||
else if(publisher instanceof Flux){
|
||||
return startSpan.flatMapMany(spanStarted -> ((Flux<?>)publisher)
|
||||
.doOnError(onFailureReactor(log, hasLog, spanStarted))
|
||||
.doOnTerminate(afterReactor(startNewSpan, log, hasLog, spanStarted)));
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unexpected type of publisher: "+publisher.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T withSpanInScope(Span span, Supplier<T> supplier) {
|
||||
try(Tracer.SpanInScope ws1 = tracer().withSpanInScope(span)) {
|
||||
return supplier.get();
|
||||
}
|
||||
}
|
||||
|
||||
private Runnable afterReactor(boolean isNewSpan, String log, boolean hasLog, Span span) {
|
||||
return () -> {
|
||||
try(Tracer.SpanInScope ws = tracer().withSpanInScope(span)) {
|
||||
after(span, isNewSpan, log, hasLog);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Consumer<Throwable> onFailureReactor(String log, boolean hasLog, Span span) {
|
||||
return throwable -> {
|
||||
try(Tracer.SpanInScope ws = tracer().withSpanInScope(span)) {
|
||||
onFailure(span, log, hasLog, throwable);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void before(MethodInvocation invocation, Span span, String log, boolean hasLog) {
|
||||
if (hasLog) {
|
||||
logEvent(span, log + ".before");
|
||||
}
|
||||
spanTagAnnotationHandler().addAnnotatedParameters(invocation);
|
||||
addTags(invocation, span);
|
||||
}
|
||||
|
||||
private void after(Span span, boolean isNewSpan, String log, boolean hasLog) {
|
||||
if (hasLog) {
|
||||
logEvent(span, log + ".after");
|
||||
}
|
||||
if (isNewSpan) {
|
||||
span.finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void onFailure(Span span, String log, boolean hasLog, Throwable e) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Exception occurred while trying to continue the pointcut", e);
|
||||
}
|
||||
if (hasLog) {
|
||||
logEvent(span, log + ".afterFailure");
|
||||
}
|
||||
span.error(e);
|
||||
}
|
||||
|
||||
private void addTags(MethodInvocation invocation, Span span) {
|
||||
span.tag(CLASS_KEY, invocation.getThis().getClass().getSimpleName());
|
||||
span.tag(METHOD_KEY, invocation.getMethod().getName());
|
||||
|
||||
@@ -0,0 +1,505 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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
|
||||
*
|
||||
* http://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.annotation;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.sampler.Sampler;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
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.util.ArrayListSpanReporter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import zipkin2.Annotation;
|
||||
import zipkin2.reporter.Reporter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.springframework.cloud.sleuth.annotation.SleuthSpanCreatorAspectFluxTests.TestBean.TEST_STRING1;
|
||||
import static org.springframework.cloud.sleuth.annotation.SleuthSpanCreatorAspectFluxTests.TestBean.TEST_STRING2;
|
||||
|
||||
@SpringBootTest(classes = SleuthSpanCreatorAspectFluxTests.TestConfiguration.class)
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class SleuthSpanCreatorAspectFluxTests {
|
||||
|
||||
@Autowired TestBeanInterface testBean;
|
||||
@Autowired Tracer tracer;
|
||||
@Autowired ArrayListSpanReporter reporter;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.reporter.clear();
|
||||
testBean.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateSpanWhenAnnotationOnInterfaceMethod() {
|
||||
Flux<String> flux = this.testBean.testMethod();
|
||||
|
||||
verifyNoSpansUntilFluxComplete(flux);
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("test-method");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateSpanWhenAnnotationOnClassMethod() {
|
||||
Flux<String> flux = this.testBean.testMethod2();
|
||||
|
||||
verifyNoSpansUntilFluxComplete(flux);
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("test-method2");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateSpanWithCustomNameWhenAnnotationOnClassMethod() {
|
||||
Flux<String> flux = this.testBean.testMethod3();
|
||||
|
||||
verifyNoSpansUntilFluxComplete(flux);
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("custom-name-on-test-method3");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateSpanWithCustomNameWhenAnnotationOnInterfaceMethod() {
|
||||
Flux<String> flux = this.testBean.testMethod4();
|
||||
|
||||
verifyNoSpansUntilFluxComplete(flux);
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("custom-name-on-test-method4");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateSpanWithTagWhenAnnotationOnInterfaceMethod() {
|
||||
// tag::execution[]
|
||||
Flux<String> flux = this.testBean.testMethod5("test");
|
||||
|
||||
// end::execution[]
|
||||
verifyNoSpansUntilFluxComplete(flux);
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("custom-name-on-test-method5");
|
||||
then(spans.get(0).tags()).containsEntry("testTag", "test");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateSpanWithTagWhenAnnotationOnClassMethod() {
|
||||
Flux<String> flux = this.testBean.testMethod6("test");
|
||||
|
||||
verifyNoSpansUntilFluxComplete(flux);
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("custom-name-on-test-method6");
|
||||
then(spans.get(0).tags()).containsEntry("testTag6", "test");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateSpanWithLogWhenAnnotationOnInterfaceMethod() {
|
||||
Flux<String> flux = this.testBean.testMethod8("test");
|
||||
|
||||
verifyNoSpansUntilFluxComplete(flux);
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("custom-name-on-test-method8");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateSpanWithLogWhenAnnotationOnClassMethod() {
|
||||
Flux<String> flux = this.testBean.testMethod9("test");
|
||||
|
||||
verifyNoSpansUntilFluxComplete(flux);
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("custom-name-on-test-method9");
|
||||
then(spans.get(0).tags())
|
||||
.containsEntry("class", "TestBean")
|
||||
.containsEntry("method", "testMethod9");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldContinueSpanWithLogWhenAnnotationOnInterfaceMethod() {
|
||||
Span span = this.tracer.nextSpan().name("foo");
|
||||
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) {
|
||||
Flux<String> flux = this.testBean.testMethod10("test");
|
||||
|
||||
verifyNoSpansUntilFluxComplete(flux);
|
||||
} finally {
|
||||
span.finish();
|
||||
}
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("foo");
|
||||
then(spans.get(0).tags())
|
||||
.containsEntry("customTestTag10", "test");
|
||||
then(spans.get(0).annotations()
|
||||
.stream().map(Annotation::value).collect(Collectors.toList()))
|
||||
.contains("customTest.before", "customTest.after");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldStartAndCloseSpanOnContinueSpanIfSpanNotSet() {
|
||||
Flux<String> flux = this.testBean.testMethod10("test");
|
||||
verifyNoSpansUntilFluxComplete(flux);
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("test-method10");
|
||||
then(spans.get(0).tags())
|
||||
.containsEntry("customTestTag10", "test");
|
||||
then(spans.get(0).annotations()
|
||||
.stream().map(Annotation::value).collect(Collectors.toList()))
|
||||
.contains("customTest.before", "customTest.after");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldContinueSpanWhenKeyIsUsedOnSpanTagWhenAnnotationOnInterfaceMethod() {
|
||||
Span span = this.tracer.nextSpan().name("foo");
|
||||
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) {
|
||||
Flux<String> flux = this.testBean.testMethod10_v2("test");
|
||||
|
||||
verifyNoSpansUntilFluxComplete(flux);
|
||||
} finally {
|
||||
span.finish();
|
||||
}
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("foo");
|
||||
then(spans.get(0).tags())
|
||||
.containsEntry("customTestTag10", "test");
|
||||
then(spans.get(0).annotations()
|
||||
.stream().map(Annotation::value).collect(Collectors.toList()))
|
||||
.contains("customTest.before", "customTest.after");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldContinueSpanWithLogWhenAnnotationOnClassMethod() {
|
||||
Span span = this.tracer.nextSpan().name("foo");
|
||||
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) {
|
||||
// tag::continue_span_execution[]
|
||||
Flux<String> flux = this.testBean.testMethod11("test");
|
||||
// end::continue_span_execution[]
|
||||
verifyNoSpansUntilFluxComplete(flux);
|
||||
} finally {
|
||||
span.finish();
|
||||
}
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("foo");
|
||||
then(spans.get(0).tags())
|
||||
.containsEntry("class", "TestBean")
|
||||
.containsEntry("method", "testMethod11")
|
||||
.containsEntry("customTestTag11", "test");
|
||||
then(spans.get(0).annotations()
|
||||
.stream().map(Annotation::value).collect(Collectors.toList()))
|
||||
.contains("customTest.before", "customTest.after");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAddErrorTagWhenExceptionOccurredInNewSpan() {
|
||||
try {
|
||||
Flux<String> flux = this.testBean.testMethod12("test");
|
||||
|
||||
then(this.reporter.getSpans()).isEmpty();
|
||||
|
||||
flux.toIterable().iterator().next();
|
||||
} catch (RuntimeException ignored) {
|
||||
}
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("test-method12");
|
||||
then(spans.get(0).tags())
|
||||
.containsEntry("testTag12", "test")
|
||||
.containsEntry("error", "test exception 12");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAddErrorTagWhenExceptionOccurredInContinueSpan() {
|
||||
Span span = this.tracer.nextSpan().name("foo");
|
||||
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) {
|
||||
// tag::continue_span_execution[]
|
||||
Flux<String> flux = this.testBean.testMethod13();
|
||||
|
||||
then(this.reporter.getSpans()).isEmpty();
|
||||
|
||||
flux.toIterable().iterator().next();
|
||||
// end::continue_span_execution[]
|
||||
} catch (RuntimeException ignored) {
|
||||
} finally {
|
||||
span.finish();
|
||||
}
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("foo");
|
||||
then(spans.get(0).tags())
|
||||
.containsEntry("error", "test exception 13");
|
||||
then(spans.get(0).annotations()
|
||||
.stream().map(Annotation::value).collect(Collectors.toList()))
|
||||
.contains("testMethod13.before", "testMethod13.afterFailure",
|
||||
"testMethod13.after");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotCreateSpanWhenNotAnnotated() {
|
||||
Flux<String> flux = this.testBean.testMethod7();
|
||||
verifyNoSpansUntilFluxComplete(flux);
|
||||
|
||||
List<zipkin2.Span> spans = new ArrayList<>(this.reporter.getSpans());
|
||||
then(spans).isEmpty();
|
||||
}
|
||||
|
||||
private void verifyNoSpansUntilFluxComplete(Flux<String> flux) {
|
||||
Iterator<String> iterator = flux.toIterable().iterator();
|
||||
|
||||
then(this.reporter.getSpans()).isEmpty();
|
||||
testBean.proceed();
|
||||
|
||||
String result1 = iterator.next();
|
||||
then(result1).isEqualTo(TEST_STRING1);
|
||||
then(this.reporter.getSpans()).isEmpty();
|
||||
|
||||
testBean.proceed();
|
||||
String result2 = iterator.next();
|
||||
|
||||
then(result2).isEqualTo(TEST_STRING2);
|
||||
}
|
||||
|
||||
protected interface TestBeanInterface {
|
||||
|
||||
// tag::annotated_method[]
|
||||
@NewSpan
|
||||
Flux<String> testMethod();
|
||||
// end::annotated_method[]
|
||||
|
||||
Flux<String> testMethod2();
|
||||
|
||||
@NewSpan(name = "interfaceCustomNameOnTestMethod3")
|
||||
Flux<String> testMethod3();
|
||||
|
||||
// tag::custom_name_on_annotated_method[]
|
||||
@NewSpan("customNameOnTestMethod4")
|
||||
Flux<String> testMethod4();
|
||||
// end::custom_name_on_annotated_method[]
|
||||
|
||||
// tag::custom_name_and_tag_on_annotated_method[]
|
||||
@NewSpan(name = "customNameOnTestMethod5")
|
||||
Flux<String> testMethod5(@SpanTag("testTag") String param);
|
||||
// end::custom_name_and_tag_on_annotated_method[]
|
||||
|
||||
Flux<String> testMethod6(String test);
|
||||
|
||||
Flux<String> testMethod7();
|
||||
|
||||
@NewSpan(name = "customNameOnTestMethod8")
|
||||
Flux<String> testMethod8(String param);
|
||||
|
||||
@NewSpan(name = "testMethod9")
|
||||
Flux<String> testMethod9(String param);
|
||||
|
||||
@ContinueSpan(log = "customTest")
|
||||
Flux<String> testMethod10(@SpanTag(value = "testTag10") String param);
|
||||
|
||||
@ContinueSpan(log = "customTest")
|
||||
Flux<String> testMethod10_v2(@SpanTag(key = "testTag10") String param);
|
||||
|
||||
// tag::continue_span[]
|
||||
@ContinueSpan(log = "testMethod11")
|
||||
Flux<String> testMethod11(@SpanTag("testTag11") String param);
|
||||
// end::continue_span[]
|
||||
|
||||
@NewSpan
|
||||
Flux<String> testMethod12(@SpanTag("testTag12") String param);
|
||||
|
||||
@ContinueSpan(log = "testMethod13")
|
||||
Flux<String> testMethod13();
|
||||
|
||||
@ContinueSpan
|
||||
Flux<String> testMethod14(String param);
|
||||
|
||||
void proceed();
|
||||
|
||||
void reset();
|
||||
}
|
||||
|
||||
protected static class TestBean implements TestBeanInterface {
|
||||
|
||||
public static final String TEST_STRING1 = "Test String 1";
|
||||
public static final String TEST_STRING2 = "Test String 2";
|
||||
|
||||
private AtomicReference<CompletableFuture<Void>> proceed
|
||||
= new AtomicReference<>(new CompletableFuture<>());
|
||||
private Flux<String> testFlux = Flux.defer(() -> Flux.just(TEST_STRING1, TEST_STRING2))
|
||||
.delayUntil(s -> Mono.fromFuture(proceed.get()))
|
||||
.doOnNext(s -> proceed.set(new CompletableFuture<>()));
|
||||
|
||||
@Override
|
||||
public void reset(){
|
||||
proceed.set(new CompletableFuture<>());
|
||||
}
|
||||
|
||||
public void proceed(){
|
||||
proceed.get().complete(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<String> testMethod() {
|
||||
return testFlux;
|
||||
}
|
||||
|
||||
@NewSpan
|
||||
@Override
|
||||
public Flux<String> testMethod2() {
|
||||
return testFlux;
|
||||
}
|
||||
|
||||
// tag::name_on_implementation[]
|
||||
@NewSpan(name = "customNameOnTestMethod3")
|
||||
@Override
|
||||
public Flux<String> testMethod3() {
|
||||
return testFlux;
|
||||
}
|
||||
// end::name_on_implementation[]
|
||||
|
||||
@Override
|
||||
public Flux<String> testMethod4() {
|
||||
return testFlux;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<String> testMethod5(String test) {
|
||||
return testFlux;
|
||||
}
|
||||
|
||||
@NewSpan(name = "customNameOnTestMethod6")
|
||||
@Override
|
||||
public Flux<String> testMethod6(@SpanTag("testTag6") String test) {
|
||||
return testFlux;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<String> testMethod7() {
|
||||
return testFlux;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<String> testMethod8(String param) {
|
||||
return testFlux;
|
||||
}
|
||||
|
||||
@NewSpan(name = "customNameOnTestMethod9")
|
||||
@Override
|
||||
public Flux<String> testMethod9(String param) {
|
||||
return testFlux;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<String> testMethod10(@SpanTag(value = "customTestTag10") String param) {
|
||||
return testFlux;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<String> testMethod10_v2(@SpanTag(key = "customTestTag10") String param) {
|
||||
return testFlux;
|
||||
}
|
||||
|
||||
@ContinueSpan(log = "customTest")
|
||||
@Override
|
||||
public Flux<String> testMethod11(@SpanTag("customTestTag11") String param) {
|
||||
return testFlux;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<String> testMethod12(String param) {
|
||||
return Flux.defer(() -> Flux.error(new RuntimeException("test exception 12")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<String> testMethod13() {
|
||||
return Flux.defer(() -> Flux.error(new RuntimeException("test exception 13")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<String> testMethod14(String param) {
|
||||
return Flux.just(TEST_STRING1, TEST_STRING2);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
protected static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public TestBeanInterface testBean() {
|
||||
return new TestBean();
|
||||
}
|
||||
|
||||
@Bean Reporter<zipkin2.Span> spanReporter() {
|
||||
return new ArrayListSpanReporter();
|
||||
}
|
||||
|
||||
@Bean Sampler alwaysSampler() {
|
||||
return Sampler.ALWAYS_SAMPLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,480 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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
|
||||
*
|
||||
* http://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.annotation;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.sampler.Sampler;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
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.util.ArrayListSpanReporter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import reactor.core.publisher.Mono;
|
||||
import zipkin2.Annotation;
|
||||
import zipkin2.reporter.Reporter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.springframework.cloud.sleuth.annotation.SleuthSpanCreatorAspectMonoTests.TestBean.TEST_STRING;
|
||||
import static reactor.core.publisher.Mono.just;
|
||||
|
||||
@SpringBootTest(classes = SleuthSpanCreatorAspectMonoTests.TestConfiguration.class)
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class SleuthSpanCreatorAspectMonoTests {
|
||||
|
||||
@Autowired TestBeanInterface testBean;
|
||||
@Autowired Tracer tracer;
|
||||
@Autowired ArrayListSpanReporter reporter;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.reporter.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateSpanWhenAnnotationOnInterfaceMethod() {
|
||||
Mono<String> mono = this.testBean.testMethod();
|
||||
|
||||
then(this.reporter.getSpans()).isEmpty();
|
||||
|
||||
mono.block();
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("test-method");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateSpanWhenAnnotationOnClassMethod() {
|
||||
Mono<String> mono = this.testBean.testMethod2();
|
||||
|
||||
then(this.reporter.getSpans()).isEmpty();
|
||||
|
||||
mono.block();
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("test-method2");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateSpanWithCustomNameWhenAnnotationOnClassMethod() {
|
||||
Mono<String> mono = this.testBean.testMethod3();
|
||||
|
||||
then(this.reporter.getSpans()).isEmpty();
|
||||
|
||||
String result = mono.block();
|
||||
|
||||
then(result).isEqualTo(TEST_STRING);
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("custom-name-on-test-method3");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateSpanWithCustomNameWhenAnnotationOnInterfaceMethod() {
|
||||
Mono<String> mono = this.testBean.testMethod4();
|
||||
|
||||
then(this.reporter.getSpans()).isEmpty();
|
||||
|
||||
mono.block();
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("custom-name-on-test-method4");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateSpanWithTagWhenAnnotationOnInterfaceMethod() {
|
||||
// tag::execution[]
|
||||
Mono<String> mono = this.testBean.testMethod5("test");
|
||||
|
||||
// end::execution[]
|
||||
then(this.reporter.getSpans()).isEmpty();
|
||||
|
||||
mono.block();
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("custom-name-on-test-method5");
|
||||
then(spans.get(0).tags()).containsEntry("testTag", "test");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateSpanWithTagWhenAnnotationOnClassMethod() {
|
||||
Mono<String> mono = this.testBean.testMethod6("test");
|
||||
|
||||
then(this.reporter.getSpans()).isEmpty();
|
||||
|
||||
mono.block();
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("custom-name-on-test-method6");
|
||||
then(spans.get(0).tags()).containsEntry("testTag6", "test");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateSpanWithLogWhenAnnotationOnInterfaceMethod() {
|
||||
Mono<String> mono = this.testBean.testMethod8("test");
|
||||
|
||||
then(this.reporter.getSpans()).isEmpty();
|
||||
|
||||
mono.block();
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("custom-name-on-test-method8");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateSpanWithLogWhenAnnotationOnClassMethod() {
|
||||
Mono<String> mono = this.testBean.testMethod9("test");
|
||||
|
||||
then(this.reporter.getSpans()).isEmpty();
|
||||
|
||||
mono.block();
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("custom-name-on-test-method9");
|
||||
then(spans.get(0).tags())
|
||||
.containsEntry("class", "TestBean")
|
||||
.containsEntry("method", "testMethod9");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldContinueSpanWithLogWhenAnnotationOnInterfaceMethod() {
|
||||
Span span = this.tracer.nextSpan().name("foo");
|
||||
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) {
|
||||
Mono<String> mono = this.testBean.testMethod10("test");
|
||||
|
||||
then(this.reporter.getSpans()).isEmpty();
|
||||
|
||||
mono.block();
|
||||
} finally {
|
||||
span.finish();
|
||||
}
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("foo");
|
||||
then(spans.get(0).tags())
|
||||
.containsEntry("customTestTag10", "test");
|
||||
then(spans.get(0).annotations()
|
||||
.stream().map(Annotation::value).collect(Collectors.toList()))
|
||||
.contains("customTest.before", "customTest.after");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldStartAndCloseSpanOnContinueSpanIfSpanNotSet() {
|
||||
this.testBean.testMethod10("test").block();
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("test-method10");
|
||||
then(spans.get(0).tags())
|
||||
.containsEntry("customTestTag10", "test");
|
||||
then(spans.get(0).annotations()
|
||||
.stream().map(Annotation::value).collect(Collectors.toList()))
|
||||
.contains("customTest.before", "customTest.after");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldContinueSpanWhenKeyIsUsedOnSpanTagWhenAnnotationOnInterfaceMethod() {
|
||||
Span span = this.tracer.nextSpan().name("foo");
|
||||
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) {
|
||||
Mono<String> mono = this.testBean.testMethod10_v2("test");
|
||||
|
||||
then(this.reporter.getSpans()).isEmpty();
|
||||
|
||||
mono.block();
|
||||
} finally {
|
||||
span.finish();
|
||||
}
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("foo");
|
||||
then(spans.get(0).tags())
|
||||
.containsEntry("customTestTag10", "test");
|
||||
then(spans.get(0).annotations()
|
||||
.stream().map(Annotation::value).collect(Collectors.toList()))
|
||||
.contains("customTest.before", "customTest.after");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldContinueSpanWithLogWhenAnnotationOnClassMethod() {
|
||||
Span span = this.tracer.nextSpan().name("foo");
|
||||
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) {
|
||||
// tag::continue_span_execution[]
|
||||
Mono<String> mono = this.testBean.testMethod11("test");
|
||||
// end::continue_span_execution[]
|
||||
then(this.reporter.getSpans()).isEmpty();
|
||||
|
||||
mono.block();
|
||||
} finally {
|
||||
span.finish();
|
||||
}
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("foo");
|
||||
then(spans.get(0).tags())
|
||||
.containsEntry("class", "TestBean")
|
||||
.containsEntry("method", "testMethod11")
|
||||
.containsEntry("customTestTag11", "test");
|
||||
then(spans.get(0).annotations()
|
||||
.stream().map(Annotation::value).collect(Collectors.toList()))
|
||||
.contains("customTest.before", "customTest.after");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAddErrorTagWhenExceptionOccurredInNewSpan() {
|
||||
try {
|
||||
Mono<String> mono = this.testBean.testMethod12("test");
|
||||
|
||||
then(this.reporter.getSpans()).isEmpty();
|
||||
|
||||
mono.block();
|
||||
} catch (RuntimeException ignored) {
|
||||
}
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("test-method12");
|
||||
then(spans.get(0).tags())
|
||||
.containsEntry("testTag12", "test")
|
||||
.containsEntry("error", "test exception 12");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAddErrorTagWhenExceptionOccurredInContinueSpan() {
|
||||
Span span = this.tracer.nextSpan().name("foo");
|
||||
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) {
|
||||
// tag::continue_span_execution[]
|
||||
Mono<String> mono = this.testBean.testMethod13();
|
||||
|
||||
then(this.reporter.getSpans()).isEmpty();
|
||||
|
||||
mono.block();
|
||||
// end::continue_span_execution[]
|
||||
} catch (RuntimeException ignored) {
|
||||
} finally {
|
||||
span.finish();
|
||||
}
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("foo");
|
||||
then(spans.get(0).tags())
|
||||
.containsEntry("error", "test exception 13");
|
||||
then(spans.get(0).annotations()
|
||||
.stream().map(Annotation::value).collect(Collectors.toList()))
|
||||
.contains("testMethod13.before", "testMethod13.afterFailure",
|
||||
"testMethod13.after");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotCreateSpanWhenNotAnnotated() {
|
||||
Mono<String> mono = this.testBean.testMethod7();
|
||||
mono.block();
|
||||
|
||||
List<zipkin2.Span> spans = new ArrayList<>(this.reporter.getSpans());
|
||||
then(spans).isEmpty();
|
||||
}
|
||||
|
||||
protected interface TestBeanInterface {
|
||||
|
||||
// tag::annotated_method[]
|
||||
@NewSpan
|
||||
Mono<String> testMethod();
|
||||
// end::annotated_method[]
|
||||
|
||||
Mono<String> testMethod2();
|
||||
|
||||
@NewSpan(name = "interfaceCustomNameOnTestMethod3")
|
||||
Mono<String> testMethod3();
|
||||
|
||||
// tag::custom_name_on_annotated_method[]
|
||||
@NewSpan("customNameOnTestMethod4")
|
||||
Mono<String> testMethod4();
|
||||
// end::custom_name_on_annotated_method[]
|
||||
|
||||
// tag::custom_name_and_tag_on_annotated_method[]
|
||||
@NewSpan(name = "customNameOnTestMethod5")
|
||||
Mono<String> testMethod5(@SpanTag("testTag") String param);
|
||||
// end::custom_name_and_tag_on_annotated_method[]
|
||||
|
||||
Mono<String> testMethod6(String test);
|
||||
|
||||
Mono<String> testMethod7();
|
||||
|
||||
@NewSpan(name = "customNameOnTestMethod8")
|
||||
Mono<String> testMethod8(String param);
|
||||
|
||||
@NewSpan(name = "testMethod9")
|
||||
Mono<String> testMethod9(String param);
|
||||
|
||||
@ContinueSpan(log = "customTest")
|
||||
Mono<String> testMethod10(@SpanTag(value = "testTag10") String param);
|
||||
|
||||
@ContinueSpan(log = "customTest")
|
||||
Mono<String> testMethod10_v2(@SpanTag(key = "testTag10") String param);
|
||||
|
||||
// tag::continue_span[]
|
||||
@ContinueSpan(log = "testMethod11")
|
||||
Mono<String> testMethod11(@SpanTag("testTag11") String param);
|
||||
// end::continue_span[]
|
||||
|
||||
@NewSpan
|
||||
Mono<String> testMethod12(@SpanTag("testTag12") String param);
|
||||
|
||||
@ContinueSpan(log = "testMethod13")
|
||||
Mono<String> testMethod13();
|
||||
}
|
||||
|
||||
protected static class TestBean implements TestBeanInterface {
|
||||
|
||||
public static final String TEST_STRING = "Test String";
|
||||
public static final Mono<String> TEST_MONO = Mono.defer(() -> just(TEST_STRING));
|
||||
|
||||
@Override
|
||||
public Mono<String> testMethod() {
|
||||
return TEST_MONO;
|
||||
}
|
||||
|
||||
@NewSpan
|
||||
@Override
|
||||
public Mono<String> testMethod2() {
|
||||
return TEST_MONO;
|
||||
}
|
||||
|
||||
// tag::name_on_implementation[]
|
||||
@NewSpan(name = "customNameOnTestMethod3")
|
||||
@Override
|
||||
public Mono<String> testMethod3() {
|
||||
return TEST_MONO;
|
||||
}
|
||||
// end::name_on_implementation[]
|
||||
|
||||
@Override
|
||||
public Mono<String> testMethod4() {
|
||||
return TEST_MONO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<String> testMethod5(String test) {
|
||||
return TEST_MONO;
|
||||
}
|
||||
|
||||
@NewSpan(name = "customNameOnTestMethod6")
|
||||
@Override
|
||||
public Mono<String> testMethod6(@SpanTag("testTag6") String test) {
|
||||
return TEST_MONO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<String> testMethod7() {
|
||||
return TEST_MONO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<String> testMethod8(String param) {
|
||||
return TEST_MONO;
|
||||
}
|
||||
|
||||
@NewSpan(name = "customNameOnTestMethod9")
|
||||
@Override
|
||||
public Mono<String> testMethod9(String param) {
|
||||
return TEST_MONO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<String> testMethod10(@SpanTag(value = "customTestTag10") String param) {
|
||||
return TEST_MONO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<String> testMethod10_v2(@SpanTag(key = "customTestTag10") String param) {
|
||||
return TEST_MONO;
|
||||
}
|
||||
|
||||
@ContinueSpan(log = "customTest")
|
||||
@Override
|
||||
public Mono<String> testMethod11(@SpanTag("customTestTag11") String param) {
|
||||
return TEST_MONO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<String> testMethod12(String param) {
|
||||
return Mono.defer(() -> Mono.error(new RuntimeException("test exception 12")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<String> testMethod13() {
|
||||
return Mono.defer(() -> Mono.error(new RuntimeException("test exception 13")));
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
protected static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public TestBeanInterface testBean() {
|
||||
return new TestBean();
|
||||
}
|
||||
|
||||
@Bean Reporter<zipkin2.Span> spanReporter() {
|
||||
return new ArrayListSpanReporter();
|
||||
}
|
||||
|
||||
@Bean Sampler alwaysSampler() {
|
||||
return Sampler.ALWAYS_SAMPLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,7 @@ public class SleuthSpanCreatorAspectTests {
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("test-method");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -70,6 +71,7 @@ public class SleuthSpanCreatorAspectTests {
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("test-method2");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -80,6 +82,7 @@ public class SleuthSpanCreatorAspectTests {
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("custom-name-on-test-method3");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -90,6 +93,7 @@ public class SleuthSpanCreatorAspectTests {
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("custom-name-on-test-method4");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -103,6 +107,7 @@ public class SleuthSpanCreatorAspectTests {
|
||||
then(spans.get(0).name()).isEqualTo("custom-name-on-test-method5");
|
||||
then(spans.get(0).tags()).containsEntry("testTag", "test");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -114,6 +119,7 @@ public class SleuthSpanCreatorAspectTests {
|
||||
then(spans.get(0).name()).isEqualTo("custom-name-on-test-method6");
|
||||
then(spans.get(0).tags()).containsEntry("testTag6", "test");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -124,6 +130,7 @@ public class SleuthSpanCreatorAspectTests {
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("custom-name-on-test-method8");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -137,6 +144,7 @@ public class SleuthSpanCreatorAspectTests {
|
||||
.containsEntry("class", "TestBean")
|
||||
.containsEntry("method", "testMethod9");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -149,7 +157,7 @@ public class SleuthSpanCreatorAspectTests {
|
||||
span.finish();
|
||||
}
|
||||
|
||||
List<zipkin2.Span> spans = new ArrayList<>(this.reporter.getSpans());
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("foo");
|
||||
then(spans.get(0).tags())
|
||||
@@ -158,6 +166,23 @@ public class SleuthSpanCreatorAspectTests {
|
||||
.stream().map(Annotation::value).collect(Collectors.toList()))
|
||||
.contains("customTest.before", "customTest.after");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldStartAndCloseSpanOnContinueSpanIfSpanNotSet() {
|
||||
this.testBean.testMethod10("test");
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("test-method10");
|
||||
then(spans.get(0).tags())
|
||||
.containsEntry("customTestTag10", "test");
|
||||
then(spans.get(0).annotations()
|
||||
.stream().map(Annotation::value).collect(Collectors.toList()))
|
||||
.contains("customTest.before", "customTest.after");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -170,7 +195,7 @@ public class SleuthSpanCreatorAspectTests {
|
||||
span.finish();
|
||||
}
|
||||
|
||||
List<zipkin2.Span> spans = new ArrayList<>(this.reporter.getSpans());
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("foo");
|
||||
then(spans.get(0).tags())
|
||||
@@ -179,6 +204,7 @@ public class SleuthSpanCreatorAspectTests {
|
||||
.stream().map(Annotation::value).collect(Collectors.toList()))
|
||||
.contains("customTest.before", "customTest.after");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -193,7 +219,7 @@ public class SleuthSpanCreatorAspectTests {
|
||||
span.finish();
|
||||
}
|
||||
|
||||
List<zipkin2.Span> spans = new ArrayList<>(this.reporter.getSpans());
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("foo");
|
||||
then(spans.get(0).tags())
|
||||
@@ -204,6 +230,7 @@ public class SleuthSpanCreatorAspectTests {
|
||||
.stream().map(Annotation::value).collect(Collectors.toList()))
|
||||
.contains("customTest.before", "customTest.after");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -213,13 +240,14 @@ public class SleuthSpanCreatorAspectTests {
|
||||
} catch (RuntimeException ignored) {
|
||||
}
|
||||
|
||||
List<zipkin2.Span> spans = new ArrayList<>(this.reporter.getSpans());
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("test-method12");
|
||||
then(spans.get(0).tags())
|
||||
.containsEntry("testTag12", "test")
|
||||
.containsEntry("error", "test exception 12");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -235,7 +263,7 @@ public class SleuthSpanCreatorAspectTests {
|
||||
span.finish();
|
||||
}
|
||||
|
||||
List<zipkin2.Span> spans = new ArrayList<>(this.reporter.getSpans());
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
then(spans.get(0).name()).isEqualTo("foo");
|
||||
then(spans.get(0).tags())
|
||||
@@ -245,14 +273,16 @@ public class SleuthSpanCreatorAspectTests {
|
||||
.contains("testMethod13.before", "testMethod13.afterFailure",
|
||||
"testMethod13.after");
|
||||
then(spans.get(0).duration()).isNotZero();
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotCreateSpanWhenNotAnnotated() {
|
||||
this.testBean.testMethod7();
|
||||
|
||||
List<zipkin2.Span> spans = new ArrayList<>(this.reporter.getSpans());
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).isEmpty();
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
}
|
||||
|
||||
protected interface TestBeanInterface {
|
||||
|
||||
@@ -90,6 +90,16 @@
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.reactivestreams</groupId>
|
||||
<artifactId>reactive-streams</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -109,6 +109,14 @@
|
||||
<groupId>io.zipkin.zipkin2</groupId>
|
||||
<artifactId>zipkin</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.reactivestreams</groupId>
|
||||
<artifactId>reactive-streams</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -77,6 +77,16 @@
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.reactivestreams</groupId>
|
||||
<artifactId>reactive-streams</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -156,6 +156,16 @@
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.reactivestreams</groupId>
|
||||
<artifactId>reactive-streams</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
Reference in New Issue
Block a user