Merge branch 'master' into 2.0.x

This commit is contained in:
Marcin Grzejszczak
2017-08-17 14:05:41 +02:00
9 changed files with 109 additions and 7 deletions

View File

@@ -5,6 +5,7 @@
:toc: left
:toclevels: 8
:nofooter:
:sectlinks: true
Spring Cloud Sleuth
====================
@@ -768,7 +769,8 @@ You can disable this behaviour by setting the value of `spring.sleuth.async.enab
If you annotate your method with `@Async` then we'll automatically create a new Span with the following characteristics:
- the Span name will be the annotated method name
- if the method is annotated with `@SpanName` then the value of the annotation will be the Span's name
- if the method is *not* annotated with `@SpanName` the Span name will be the annotated method name
- the Span will be tagged with that method's class name and the method name too
==== @Scheduled annotated methods

View File

@@ -16,13 +16,16 @@
package org.springframework.cloud.sleuth;
import java.lang.reflect.Method;
import org.springframework.core.annotation.AnnotationUtils;
/**
* Default implementation of SpanNamer that tries to get the span name as follows:
*
* <li>
* <ul>from the @SpanName annotation if one is present</ul>
* <ul>from the @SpanName annotation on the class if one is present</ul>
* <ul>from the @SpanName annotation on the method if passed object is of a {@link Method} type</ul>
* <ul>from the toString() of the delegate if it's not the
* default {@link Object#toString()}</ul>
* <ul>the default provided value</ul>
@@ -37,8 +40,7 @@ public class DefaultSpanNamer implements SpanNamer {
@Override
public String name(Object object, String defaultValue) {
SpanName annotation = AnnotationUtils
.findAnnotation(object.getClass(), SpanName.class);
SpanName annotation = annotation(object);
String spanName = annotation != null ? annotation.value() : object.toString();
// If there is no overridden toString method we'll put a constant value
if (isDefaultToString(object, spanName)) {
@@ -47,7 +49,18 @@ public class DefaultSpanNamer implements SpanNamer {
return spanName;
}
private SpanName annotation(Object o) {
if (o instanceof Method) {
return AnnotationUtils.findAnnotation((Method) o, SpanName.class);
}
return AnnotationUtils
.findAnnotation(o.getClass(), SpanName.class);
}
private static boolean isDefaultToString(Object delegate, String spanName) {
if (delegate instanceof Method) {
return delegate.toString().equals(spanName);
}
return (delegate.getClass().getName() + "@" +
Integer.toHexString(delegate.hashCode())).equals(spanName);
}

View File

@@ -55,10 +55,13 @@ import java.lang.annotation.Target;
* };
* }</pre>
*
* Starting with version {@code 1.3.0} you can also put the annotation on an {@link org.springframework.scheduling.annotation.Async}
* annotated method and the value of that annotation will be used as the span name.
*
* @author Marcin Grzejszczak
* @since 1.0.0
*/
@Target(ElementType.TYPE)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SpanName {

View File

@@ -22,6 +22,7 @@ import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.SpanNamer;
import org.springframework.cloud.sleuth.TraceKeys;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.util.SpanNameUtil;
@@ -46,6 +47,7 @@ public class TraceAsyncAspect {
private final Tracer tracer;
private final TraceKeys traceKeys;
private final BeanFactory beanFactory;
private SpanNamer spanNamer;
public TraceAsyncAspect(Tracer tracer, TraceKeys traceKeys, BeanFactory beanFactory) {
this.tracer = tracer;
@@ -55,8 +57,9 @@ public class TraceAsyncAspect {
@Around("execution (@org.springframework.scheduling.annotation.Async * *.*(..))")
public Object traceBackgroundThread(final ProceedingJoinPoint pjp) throws Throwable {
Span span = this.tracer.createSpan(
String spanName = spanNamer().name(getMethod(pjp, pjp.getTarget()),
SpanNameUtil.toLowerHyphen(pjp.getSignature().getName()));
Span span = this.tracer.createSpan(spanName);
this.tracer.addTag(Span.SPAN_LOCAL_COMPONENT_TAG_NAME, ASYNC_COMPONENT);
this.tracer.addTag(this.traceKeys.getAsync().getPrefix() +
this.traceKeys.getAsync().getClassNameKey(), pjp.getTarget().getClass().getSimpleName());
@@ -76,4 +79,11 @@ public class TraceAsyncAspect {
.findMethod(object.getClass(), method.getName(), method.getParameterTypes());
}
SpanNamer spanNamer() {
if (this.spanNamer == null) {
this.spanNamer = this.beanFactory.getBean(SpanNamer.class);
}
return this.spanNamer;
}
}

View File

@@ -16,7 +16,10 @@
package org.springframework.cloud.sleuth;
import java.lang.reflect.Method;
import org.junit.Test;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.BDDAssertions.then;
@@ -42,6 +45,18 @@ public class DefaultSpanNamerTests {
then(this.defaultSpanNamer.name(new ClassWithoutToString(), "default")).isEqualTo("default");
}
@Test
public void should_return_value_of_span_name_from_annotation_on_method() throws Exception {
Method method = ReflectionUtils.findMethod(ClassWithAnnotatedMethod.class, "method");
then(this.defaultSpanNamer.name(method, "default")).isEqualTo("foo");
}
@Test
public void should_return_default_value_of_span_name_from_annotation_on_method() throws Exception {
Method method = ReflectionUtils.findMethod(ClassWithNonAnnotatedMethod.class, "method");
then(this.defaultSpanNamer.name(method, "default")).isEqualTo("default");
}
@SpanName("somevalue")
static class ClassWithAnnotation {}
@@ -60,4 +75,13 @@ public class DefaultSpanNamerTests {
}
static class ClassWithoutToString {}
static class ClassWithAnnotatedMethod {
@SpanName("foo")
void method() {}
}
static class ClassWithNonAnnotatedMethod {
void method() {}
}
}

View File

@@ -38,6 +38,7 @@ import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.hateoas.PagedResources;
import org.springframework.http.RequestEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;
@@ -58,6 +59,7 @@ import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ReservationServiceApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@DirtiesContext
@ActiveProfiles("data")
public class SpringDataInstrumentationTests {
@Autowired

View File

@@ -2,17 +2,21 @@
package org.springframework.cloud.sleuth.instrument.web;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.BDDAssertions.then;
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
import java.util.concurrent.atomic.AtomicReference;
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.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.Sampler;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.SpanName;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.instrument.DefaultTestAutoConfiguration;
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
@@ -35,6 +39,11 @@ public class TraceAsyncIntegrationTests {
@Autowired
Tracer tracer;
@Before
public void cleanup() {
this.classPerformingAsyncLogic.clear();
}
@Test
public void should_set_span_on_an_async_annotated_method() {
Span span = givenASpanInCurrentThread();
@@ -42,6 +51,17 @@ public class TraceAsyncIntegrationTests {
whenAsyncProcessingTakesPlace();
thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOne(span);
this.tracer.close(span);
}
@Test
public void should_set_span_with_custom_method_on_an_async_annotated_method() {
Span span = givenASpanInCurrentThread();
whenAsyncProcessingTakesPlaceWithCustomSpanName();
thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOneAndSpanHasCustomName(span);
this.tracer.close(span);
}
private Span givenASpanInCurrentThread() {
@@ -52,6 +72,10 @@ public class TraceAsyncIntegrationTests {
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())
@@ -62,6 +86,16 @@ public class TraceAsyncIntegrationTests {
.hasATag("method", "invokeAsynchronousLogic"));
}
private void thenTraceIdIsPassedFromTheCurrentThreadToTheAsyncOneAndSpanHasCustomName(final Span span) {
Awaitility.await().atMost(5, SECONDS).untilAsserted(
() -> then(TraceAsyncIntegrationTests.this.classPerformingAsyncLogic.getSpan())
.hasTraceIdEqualTo(span.getTraceId())
.hasNameEqualTo("foo")
.isALocalComponentSpan()
.hasATag("class", "ClassPerformingAsyncLogic")
.hasATag("method", "customNameInvokeAsynchronousLogic"));
}
@After
public void cleanTrace() {
TestSpanContextHolder.removeCurrentSpan();
@@ -93,8 +127,18 @@ public class TraceAsyncIntegrationTests {
this.span.set(TestSpanContextHolder.getCurrentSpan());
}
@Async
@SpanName("foo")
public void customNameInvokeAsynchronousLogic() {
this.span.set(TestSpanContextHolder.getCurrentSpan());
}
public Span getSpan() {
return this.span.get();
}
public void clear() {
this.span.set(null);
}
}
}

View File

@@ -0,0 +1 @@
spring.autoconfigure.exclude:

View File

@@ -15,4 +15,7 @@ ribbon.eureka.enabled: false
spring.sleuth.scheduled.skipPattern: "^org.*TestBeanWithScheduledMethodToBeIgnored$"
# comma separated list of matchers
spring.sleuth.rxjava.schedulers.ignoredthreads: HystixMetricPoller,^MyCustomThread.*$,^RxComputation.*$
security.ignored: /**
security.ignored: /**
#disable hibernate by default
spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration