Fixed a lot of checkstyle warnings'

This commit is contained in:
Marcin Grzejszczak
2018-10-01 12:29:33 +02:00
parent 6f5ef23674
commit 99d38afc69
268 changed files with 6488 additions and 4456 deletions

16
.editorconfig Normal file
View File

@@ -0,0 +1,16 @@
root=true
[*.java]
indent_style = tab
indent_size = 4
continuation_indent_size = 8
[*.groovy]
indent_style = tab
indent_size = 4
continuation_indent_size = 8
[*.xml]
indent_style = tab
indent_size = 4
continuation_indent_size = 8

0
.springformat Normal file
View File

View File

@@ -115,6 +115,10 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
</plugin>
<plugin>
<groupId>io.spring.javaformat</groupId>
<artifactId>spring-javaformat-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

View File

@@ -23,17 +23,24 @@ 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 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>
* <li>
* <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>
* </li>
*
* @author Marcin Grzejszczak
* @since 1.0.0
*
* @see SpanName
*/
public class DefaultSpanNamer implements SpanNamer {
@@ -53,15 +60,15 @@ public class DefaultSpanNamer implements SpanNamer {
if (o instanceof Method) {
return AnnotationUtils.findAnnotation((Method) o, SpanName.class);
}
return AnnotationUtils
.findAnnotation(o.getClass(), 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);
return (delegate.getClass().getName() + "@"
+ Integer.toHexString(delegate.hashCode())).equals(spanName);
}
}

View File

@@ -19,19 +19,27 @@ package org.springframework.cloud.sleuth;
import zipkin2.Span;
/**
* Deprecated Span Adjuster.
*
* @deprecated use {@link brave.handler.FinishedSpanHandler}
* @author Marcin Grzejszczak
*/
@Deprecated
public interface SpanAdjuster {
/**
* You can adjust the {@link zipkin2.Span} by creating a new one using the {@link Span#toBuilder()}
* before reporting it.
* You can adjust the {@link zipkin2.Span} by creating a new one using the
* {@link Span#toBuilder()} before reporting it.
*
* With the legacy Sleuth approach we're generating spans with a fixed name. Some users want to modify the name
* depending on some values of tags. Implementation of this interface can be used to alter
* then name. Example:
* With the legacy Sleuth approach we're generating spans with a fixed name. Some
* users want to modify the name depending on some values of tags. Implementation of
* this interface can be used to alter then name. Example:
*
* {@code span -> span.toBuilder().name(scrub(span.getName())).build();}
*
* @param - span to adjust
* @return - adjusted span
*/
Span adjust(Span span);
}
}

View File

@@ -23,16 +23,15 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to provide the name for the span. You should annotate all your
* custom {@link Runnable Runnable} or {@link java.util.concurrent.Callable Callable} classes
* for the instrumentation logic to pick up how to name the span.
* Annotation to provide the name for the span. You should annotate all your custom
* {@link Runnable Runnable} or {@link java.util.concurrent.Callable Callable} classes for
* the instrumentation logic to pick up how to name the span.
* <p>
*
* Having for example the following code
* <pre>{@code
* @SpanName("custom-operation")
* Having for example the following code <pre>{@code
* &#64;SpanName("custom-operation")
* class CustomRunnable implements Runnable {
* @Override
* &#64;Override
* public void run() {
* // latency of this method will be recorded in a span named "custom-operation"
* }
@@ -42,21 +41,21 @@ import java.lang.annotation.Target;
* Will result in creating a span with name {@code custom-operation}.
* <p>
*
* When there's no @SpanName annotation, {@code toString} is used. Here's an
* example of the above, but via an anonymous instance.
* <pre>{@code
* When there's no @SpanName annotation, {@code toString} is used. Here's an example of
* the above, but via an anonymous instance. <pre>{@code
* return new Runnable() {
* -- snip --
*
* @Override
* &#64;Override
* public String toString() {
* return "custom-operation";
* }
* };
* }</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.
* 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
@@ -65,8 +64,11 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SpanName {
/**
* Name of the span to be resolved at runtime
* Name of the span to be resolved at runtime.
* @return - value of the span name.
*/
String value();
}

View File

@@ -17,10 +17,9 @@
package org.springframework.cloud.sleuth;
/**
* Describes how for a given object a span should be named. In the vast majority
* of cases a name should be provided explicitly. In case of instrumentation
* where the name has to be resolved at runtime this interface will provide
* the name of the span.
* Describes how for a given object a span should be named. In the vast majority of cases
* a name should be provided explicitly. In case of instrumentation where the name has to
* be resolved at runtime this interface will provide the name of the span.
*
* @author Marcin Grzejszczak
* @since 1.0.0
@@ -29,10 +28,11 @@ public interface SpanNamer {
/**
* Retrieves the span name for the given object.
*
* @param object - object for which span name should be picked
* @param defaultValue - the default valued to be returned if span name can't be calculated
* @param defaultValue - the default valued to be returned if span name can't be
* calculated
* @return span name
*/
String name(Object object, String defaultValue);
}

View File

@@ -26,18 +26,26 @@ import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
/**
* Sleuth annotation processor
*
* @author Marcin Grzejszczak
*/
abstract class AbstractSleuthMethodInvocationProcessor
implements SleuthMethodInvocationProcessor, BeanFactoryAware {
private static final Log logger = LogFactory
.getLog(AbstractSleuthMethodInvocationProcessor.class);
private static final String CLASS_KEY = "class";
private static final String METHOD_KEY = "method";
BeanFactory beanFactory;
private NewSpanParser newSpanParser;
private Tracer tracer;
private SpanTagAnnotationHandler spanTagAnnotationHandler;
void before(MethodInvocation invocation, Span span, String log, boolean hasLog) {
@@ -111,7 +119,9 @@ abstract class AbstractSleuthMethodInvocationProcessor
return this.spanTagAnnotationHandler;
}
@Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}

View File

@@ -23,20 +23,25 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Tells Sleuth that all Sleuth related annotations should be applied
* to an existing span instead of creating a new one.
* Tells Sleuth that all Sleuth related annotations should be applied to an existing span
* instead of creating a new one.
*
* @author Marcin Grzejszczak
* @since 1.2.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Target(value = { ElementType.METHOD })
@Target(value = {
ElementType.METHOD
})
public @interface ContinueSpan {
/**
* The value passed to the annotation will be used and the framework
* will create two events with the {@code .start} and {@code .end} suffixes
* Log statement to be appended to the span.
*
* @return - the value passed to the annotation will be used and the framework will create two
* events with the {@code .start} and {@code .end} suffixes.
*/
String log() default "";
}

View File

@@ -23,24 +23,24 @@ import org.springframework.cloud.sleuth.util.SpanNameUtil;
import org.springframework.util.StringUtils;
/**
* Default implementation of the {@link NewSpanParser} that parses only the
* span name.
* Default implementation of the {@link NewSpanParser} that parses only the span name.
*
* @author Christian Schwerdtfeger
* @since 1.2.0
*/
class DefaultNewSpanParser implements NewSpanParser {
class DefaultSpanCreator implements NewSpanParser {
private static final Log log = LogFactory.getLog(DefaultNewSpanParser.class);
private static final Log log = LogFactory.getLog(DefaultSpanCreator.class);
@Override
public void parse(MethodInvocation pjp, NewSpan newSpan, SpanCustomizer span) {
String name = newSpan == null || StringUtils.isEmpty(newSpan.name()) ?
pjp.getMethod().getName() : newSpan.name();
String name = newSpan == null || StringUtils.isEmpty(newSpan.name())
? pjp.getMethod().getName() : newSpan.name();
String changedName = SpanNameUtil.toLowerHyphen(name);
if (log.isDebugEnabled()) {
log.debug("For the class [" + pjp.getThis().getClass() + "] method "
+ "[" + pjp.getMethod().getName() + "] will name the span [" + changedName + "]");
log.debug("For the class [" + pjp.getThis().getClass() + "] method " + "["
+ pjp.getMethod().getName() + "] will name the span [" + changedName
+ "]");
}
span.name(changedName);
}

View File

@@ -24,13 +24,13 @@ import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
/**
* Allows to create a new span around a public method. The new span
* will be either a child of an existing span if a trace is already in progress
* or a new span will be created if there was no previous trace.
* Allows to create a new span around a public method. The new span will be either a child
* of an existing span if a trace is already in progress or a new span will be created if
* there was no previous trace.
* <p>
* Method parameters can be annotated with {@link SpanTag}, which will end
* in adding the parameter value as a tag value to the span. The tag key will be
* the value of the {@code key} annotation from {@link SpanTag}.
* Method parameters can be annotated with {@link SpanTag}, which will end in adding the
* parameter value as a tag value to the span. The tag key will be the value of the
* {@code key} annotation from {@link SpanTag}.
*
*
* @author Christian Schwerdtfeger
@@ -38,17 +38,21 @@ import org.springframework.core.annotation.AliasFor;
*/
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Target(value = { ElementType.METHOD })
@Target(value = {
ElementType.METHOD
})
public @interface NewSpan {
/**
* The name of the span which will be created. Default is the annotated method's name separated by hyphens.
* @return - The name of the span which will be created. Default is the annotated method's name
* separated by hyphens.
*/
@AliasFor("value")
String name() default "";
/**
* The name of the span which will be created. Default is the annotated method's name separated by hyphens.
* @return - The name of the span which will be created. Default is the annotated method's name
* separated by hyphens.
*/
@AliasFor("name")
String value() default "";

View File

@@ -27,6 +27,13 @@ import org.aopalliance.intercept.MethodInvocation;
*/
public interface NewSpanParser {
/** Override to control the name and tags on an annotation-based span */
/**
* Override to control the name and tags on an annotation-based span.
*
* @param methodInvocation
* @param newSpan
* @param span
*/
void parse(MethodInvocation methodInvocation, NewSpan newSpan, SpanCustomizer span);
}

View File

@@ -17,13 +17,16 @@
package org.springframework.cloud.sleuth.annotation;
/**
* Does nothing
* Does nothing.
*
* @author Marcin Grzejszczak
* @since 1.2.0
*/
class NoOpTagValueResolver implements TagValueResolver {
@Override public String resolve(Object parameter) {
@Override
public String resolve(Object parameter) {
return null;
}
}

View File

@@ -22,20 +22,25 @@ import org.aopalliance.intercept.MethodInvocation;
import org.springframework.util.StringUtils;
/**
* Method Invocation processor for non reactor apps.
*
* @author Marcin Grzejszczak
* @since 2.1.0
*/
class NonReactorSleuthMethodInvocationProcessor extends AbstractSleuthMethodInvocationProcessor {
class NonReactorSleuthMethodInvocationProcessor
extends AbstractSleuthMethodInvocationProcessor {
@Override public Object process(MethodInvocation invocation, NewSpan newSpan,
ContinueSpan continueSpan) throws Throwable {
@Override
public Object process(MethodInvocation invocation, NewSpan newSpan,
ContinueSpan continueSpan) throws Throwable {
return proceedUnderSynchronousSpan(invocation, newSpan, continueSpan);
}
private Object proceedUnderSynchronousSpan(
MethodInvocation invocation, NewSpan newSpan, ContinueSpan continueSpan) throws Throwable {
private Object proceedUnderSynchronousSpan(MethodInvocation invocation,
NewSpan newSpan, ContinueSpan continueSpan) throws Throwable {
Span span = tracer().currentSpan();
//in case of @ContinueSpan and no span in tracer we start new span and should close it on completion
// 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();
@@ -47,11 +52,14 @@ class NonReactorSleuthMethodInvocationProcessor extends AbstractSleuthMethodInvo
try (Tracer.SpanInScope ws = tracer().withSpanInScope(span)) {
before(invocation, span, log, hasLog);
return invocation.proceed();
} catch (Exception e) {
onFailure(span, log, hasLog, e);
throw e;
} finally {
}
catch (Exception ex) {
onFailure(span, log, hasLog, ex);
throw ex;
}
finally {
after(span, startNewSpan, log, hasLog);
}
}
}

View File

@@ -30,39 +30,46 @@ import reactor.core.publisher.Mono;
import reactor.core.publisher.SignalType;
/**
* Method Invocation Processor for Reactor.
*
* @author Marcin Grzejszczak
* @since 2.1.0
*/
class ReactorSleuthMethodInvocationProcessor extends AbstractSleuthMethodInvocationProcessor {
class ReactorSleuthMethodInvocationProcessor
extends AbstractSleuthMethodInvocationProcessor {
private NonReactorSleuthMethodInvocationProcessor nonReactorSleuthMethodInvocationProcessor;
@Override public Object process(MethodInvocation invocation, NewSpan newSpan,
@Override
public Object process(MethodInvocation invocation, NewSpan newSpan,
ContinueSpan continueSpan) throws Throwable {
Method method = invocation.getMethod();
if(isReactorReturnType(method.getReturnType())){
if (isReactorReturnType(method.getReturnType())) {
return proceedUnderReactorSpan(invocation, newSpan, continueSpan);
} else {
return nonReactorSleuthMethodInvocationProcessor()
.process(invocation, newSpan, continueSpan);
}
else {
return nonReactorSleuthMethodInvocationProcessor().process(invocation,
newSpan, continueSpan);
}
}
private Object proceedUnderReactorSpan(
MethodInvocation invocation, NewSpan newSpan, ContinueSpan continueSpan) throws Throwable{
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
// 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 {
}
else {
span = spanPrevious;
}
String log = log(continueSpan);
boolean hasLog = StringUtils.hasText(log);
try(Tracer.SpanInScope ws = tracer().withSpanInScope(span)) {
try (Tracer.SpanInScope ws = tracer().withSpanInScope(span)) {
Publisher<?> publisher = (Publisher) invocation.proceed();
Mono<Span> startSpan = Mono.defer(() -> withSpanInScope(span, () -> {
if (startNewSpan) {
@@ -71,35 +78,43 @@ class ReactorSleuthMethodInvocationProcessor extends AbstractSleuthMethodInvocat
before(invocation, span, log, hasLog);
return Mono.just(span);
}));
if(publisher instanceof Mono){
return startSpan.flatMap(spanStarted -> ((Mono<?>)publisher)
.doOnError(onFailureReactor(log, hasLog, spanStarted))
.doFinally(afterReactor(startNewSpan, log, hasLog, spanStarted)))
//put span in context so it can be used by ScopePassingSpanSubscriber
if (publisher instanceof Mono) {
return startSpan
.flatMap(spanStarted -> ((Mono<?>) publisher)
.doOnError(onFailureReactor(log, hasLog, spanStarted))
.doFinally(afterReactor(startNewSpan, log, hasLog,
spanStarted)))
// put span in context so it can be used by
// ScopePassingSpanSubscriber
.subscriberContext(context -> context.put(Span.class, span));
}
else if(publisher instanceof Flux){
return startSpan.flatMapMany(spanStarted -> ((Flux<?>)publisher)
.doOnError(onFailureReactor(log, hasLog, spanStarted))
.doFinally(afterReactor(startNewSpan, log, hasLog, spanStarted)))
//put span in context so it can be used by ScopePassingSpanSubscriber
else if (publisher instanceof Flux) {
return startSpan
.flatMapMany(spanStarted -> ((Flux<?>) publisher)
.doOnError(onFailureReactor(log, hasLog, spanStarted))
.doFinally(afterReactor(startNewSpan, log, hasLog,
spanStarted)))
// put span in context so it can be used by
// ScopePassingSpanSubscriber
.subscriberContext(context -> context.put(Span.class, span));
}
else {
throw new IllegalArgumentException("Unexpected type of publisher: "+publisher.getClass());
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)) {
try (Tracer.SpanInScope ws1 = tracer().withSpanInScope(span)) {
return supplier.get();
}
}
private Consumer<SignalType> afterReactor(boolean isNewSpan, String log, boolean hasLog, Span span) {
private Consumer<SignalType> afterReactor(boolean isNewSpan, String log,
boolean hasLog, Span span) {
return signalType -> {
try(Tracer.SpanInScope ws = tracer().withSpanInScope(span)) {
try (Tracer.SpanInScope ws = tracer().withSpanInScope(span)) {
after(span, isNewSpan, log, hasLog);
}
};
@@ -107,7 +122,7 @@ class ReactorSleuthMethodInvocationProcessor extends AbstractSleuthMethodInvocat
private Consumer<Throwable> onFailureReactor(String log, boolean hasLog, Span span) {
return throwable -> {
try(Tracer.SpanInScope ws = tracer().withSpanInScope(span)) {
try (Tracer.SpanInScope ws = tracer().withSpanInScope(span)) {
onFailure(span, log, hasLog, throwable);
}
};
@@ -120,8 +135,10 @@ class ReactorSleuthMethodInvocationProcessor extends AbstractSleuthMethodInvocat
private NonReactorSleuthMethodInvocationProcessor nonReactorSleuthMethodInvocationProcessor() {
if (this.nonReactorSleuthMethodInvocationProcessor == null) {
this.nonReactorSleuthMethodInvocationProcessor = new NonReactorSleuthMethodInvocationProcessor();
this.nonReactorSleuthMethodInvocationProcessor.setBeanFactory(this.beanFactory);
this.nonReactorSleuthMethodInvocationProcessor
.setBeanFactory(this.beanFactory);
}
return this.nonReactorSleuthMethodInvocationProcessor;
}
}

View File

@@ -37,8 +37,8 @@ import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ReflectionUtils;
/**
* Custom pointcut advisor that picks all classes / interfaces that
* have the Sleuth related annotations.
* Custom pointcut advisor that picks all classes / interfaces that have the Sleuth
* related annotations.
*
* @author Marcin Grzejszczak
* @since 1.2.0
@@ -88,22 +88,26 @@ class SleuthAdvisorConfig extends AbstractPointcutAdvisor implements BeanFactory
}
/**
* Checks if a class or a method is is annotated with Sleuth related annotations
* Checks if a class or a method is is annotated with Sleuth related annotations.
*/
private final class AnnotationClassOrMethodOrArgsPointcut extends
DynamicMethodMatcherPointcut {
private final class AnnotationClassOrMethodOrArgsPointcut
extends DynamicMethodMatcherPointcut {
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
//Skip check here as actual check takes place in SleuthInterceptor.invoke(MethodInvocation)
// Skip check here as actual check takes place in
// SleuthInterceptor.invoke(MethodInvocation)
return true;
}
@Override public ClassFilter getClassFilter() {
@Override
public ClassFilter getClassFilter() {
return new ClassFilter() {
@Override public boolean matches(Class<?> clazz) {
return new AnnotationClassOrMethodFilter(NewSpan.class).matches(clazz) ||
new AnnotationClassOrMethodFilter(ContinueSpan.class).matches(clazz);
@Override
public boolean matches(Class<?> clazz) {
return new AnnotationClassOrMethodFilter(NewSpan.class).matches(clazz)
|| new AnnotationClassOrMethodFilter(ContinueSpan.class)
.matches(clazz);
}
};
}
@@ -127,39 +131,45 @@ class SleuthAdvisorConfig extends AbstractPointcutAdvisor implements BeanFactory
}
/**
* Checks if a method is properly annotated with a given Sleuth annotation
* Checks if a method is properly annotated with a given Sleuth annotation.
*/
private static class AnnotationMethodsResolver {
private final Class<? extends Annotation> annotationType;
public AnnotationMethodsResolver(Class<? extends Annotation> annotationType) {
AnnotationMethodsResolver(Class<? extends Annotation> annotationType) {
this.annotationType = annotationType;
}
public boolean hasAnnotatedMethods(Class<?> clazz) {
boolean hasAnnotatedMethods(Class<?> clazz) {
final AtomicBoolean found = new AtomicBoolean(false);
ReflectionUtils.doWithMethods(clazz, method -> {
ReflectionUtils.doWithMethods(clazz, (method -> {
if (found.get()) {
return;
}
Annotation annotation = AnnotationUtils.findAnnotation(method,
AnnotationMethodsResolver.this.annotationType);
if (annotation != null) { found.set(true); }
});
if (annotation != null) {
found.set(true);
}
}));
return found.get();
}
}
}
/**
* Interceptor that creates or continues a span depending on the provided
* annotation. Also it adds logs and tags if necessary.
* Interceptor that creates or continues a span depending on the provided annotation. Also
* it adds logs and tags if necessary.
*
* @author Marcin Grzejszczak
*/
class SleuthInterceptor implements IntroductionInterceptor, BeanFactoryAware {
class SleuthInterceptor implements IntroductionInterceptor, BeanFactoryAware {
private BeanFactory beanFactory;
private SleuthMethodInvocationProcessor methodInvocationProcessor;
@Override
@@ -168,10 +178,12 @@ 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);
ContinueSpan continueSpan = SleuthAnnotationUtils.findAnnotation(mostSpecificMethod, ContinueSpan.class);
Method mostSpecificMethod = AopUtils.getMostSpecificMethod(method,
invocation.getThis().getClass());
NewSpan newSpan = SleuthAnnotationUtils.findAnnotation(mostSpecificMethod,
NewSpan.class);
ContinueSpan continueSpan = SleuthAnnotationUtils
.findAnnotation(mostSpecificMethod, ContinueSpan.class);
if (newSpan == null && continueSpan == null) {
return invocation.proceed();
}
@@ -180,16 +192,20 @@ class SleuthInterceptor implements IntroductionInterceptor, BeanFactoryAware {
private SleuthMethodInvocationProcessor methodInvocationProcessor() {
if (this.methodInvocationProcessor == null) {
this.methodInvocationProcessor = this.beanFactory.getBean(SleuthMethodInvocationProcessor.class);
this.methodInvocationProcessor = this.beanFactory
.getBean(SleuthMethodInvocationProcessor.class);
}
return this.methodInvocationProcessor;
}
@Override public boolean implementsInterface(Class<?> intf) {
@Override
public boolean implementsInterface(Class<?> intf) {
return true;
}
@Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}

View File

@@ -16,8 +16,8 @@
package org.springframework.cloud.sleuth.annotation;
/**
* A container class that holds information about the parameter
* of the annotated method argument.
* A container class that holds information about the parameter of the annotated method
* argument.
*
* @author Christian Schwerdtfeger
* @since 1.2.0
@@ -25,11 +25,12 @@ package org.springframework.cloud.sleuth.annotation;
class SleuthAnnotatedParameter {
final int parameterIndex;
final SpanTag annotation;
final Object argument;
SleuthAnnotatedParameter(int parameterIndex, SpanTag annotation,
Object argument) {
SleuthAnnotatedParameter(int parameterIndex, SpanTag annotation, Object argument) {
this.parameterIndex = parameterIndex;
this.annotation = annotation;
this.argument = argument;

View File

@@ -30,9 +30,9 @@ import org.springframework.context.annotation.Role;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that allows creating spans by means of a
* {@link NewSpan} annotation. You can annotate classes or just methods.
* You can also apply this annotation to an interface.
* Auto-configuration} that allows creating spans by means of a {@link NewSpan}
* annotation. You can annotate classes or just methods. You can also apply this
* annotation to an interface.
*
* @author Christian Schwerdtfeger
* @author Marcin Grzejszczak
@@ -44,24 +44,28 @@ import org.springframework.context.annotation.Role;
@ConditionalOnProperty(name = "spring.sleuth.annotation.enabled", matchIfMissing = true)
@AutoConfigureAfter(TraceAutoConfiguration.class)
public class SleuthAnnotationAutoConfiguration {
@Bean
@ConditionalOnMissingBean NewSpanParser newSpanParser() {
return new DefaultNewSpanParser();
@ConditionalOnMissingBean
NewSpanParser newSpanParser() {
return new DefaultSpanCreator();
}
@Bean
@ConditionalOnMissingBean TagValueExpressionResolver spelTagValueExpressionResolver() {
@ConditionalOnMissingBean
TagValueExpressionResolver spelTagValueExpressionResolver() {
return new SpelTagValueExpressionResolver();
}
@Bean
@ConditionalOnMissingBean TagValueResolver noOpTagValueResolver() {
@ConditionalOnMissingBean
TagValueResolver noOpTagValueResolver() {
return new NoOpTagValueResolver();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE) SleuthAdvisorConfig sleuthAdvisorConfig() {
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
SleuthAdvisorConfig sleuthAdvisorConfig() {
return new SleuthAdvisorConfig();
}

View File

@@ -19,7 +19,7 @@ package org.springframework.cloud.sleuth.annotation;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Sleuth annotation settings
* Sleuth annotation settings.
*
* @author Marcin Grzejszczak
* @since 1.2.0
@@ -36,4 +36,5 @@ public class SleuthAnnotationProperties {
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}

View File

@@ -26,33 +26,39 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.core.annotation.AnnotationUtils;
/**
* Utility class that can verify whether the method is annotated with
* the Sleuth annotations.
* Utility class that can verify whether the method is annotated with the Sleuth
* annotations.
*
* @author Christian Schwerdtfeger
* @since 1.2.0
*/
class SleuthAnnotationUtils {
private SleuthAnnotationUtils() {
}
private static final Log log = LogFactory.getLog(SleuthAnnotationUtils.class);
static boolean isMethodAnnotated(Method method) {
return findAnnotation(method, NewSpan.class) != null ||
findAnnotation(method, ContinueSpan.class) != null;
return findAnnotation(method, NewSpan.class) != null
|| findAnnotation(method, ContinueSpan.class) != null;
}
static boolean hasAnnotatedParams(Method method, Object[] args) {
return !findAnnotatedParameters(method, args).isEmpty();
}
static List<SleuthAnnotatedParameter> findAnnotatedParameters(Method method, Object[] args) {
static List<SleuthAnnotatedParameter> findAnnotatedParameters(Method method,
Object[] args) {
Annotation[][] parameters = method.getParameterAnnotations();
List<SleuthAnnotatedParameter> result = new ArrayList<>();
int i = 0;
for (Annotation[] parameter : parameters) {
for (Annotation parameter2 : parameter) {
if (parameter2 instanceof SpanTag) {
result.add(new SleuthAnnotatedParameter(i, (SpanTag) parameter2, args[i]));
result.add(new SleuthAnnotatedParameter(i, (SpanTag) parameter2,
args[i]));
}
}
i++;
@@ -61,21 +67,28 @@ class SleuthAnnotationUtils {
}
/**
* Searches for an annotation either on a method or inside the method parameters
* Searches for an annotation either on a method or inside the method parameters.
*
* @param <T> - annotation
* @param clazz - class with annotation
* @param method - annotated method
* @return annotation
*/
static <T extends Annotation> T findAnnotation(Method method, Class<T> clazz) {
T annotation = AnnotationUtils.findAnnotation(method, clazz);
if (annotation == null) {
try {
annotation = AnnotationUtils.findAnnotation(
method.getDeclaringClass().getMethod(method.getName(),
method.getParameterTypes()), clazz);
} catch (NoSuchMethodException | SecurityException e) {
annotation = AnnotationUtils.findAnnotation(method.getDeclaringClass()
.getMethod(method.getName(), method.getParameterTypes()), clazz);
}
catch (NoSuchMethodException | SecurityException ex) {
if (log.isDebugEnabled()) {
log.debug("Exception occurred while tyring to find the annotation", e);
log.debug("Exception occurred while tyring to find the annotation",
ex);
}
}
}
return annotation;
}
}

View File

@@ -19,10 +19,14 @@ package org.springframework.cloud.sleuth.annotation;
import org.aopalliance.intercept.MethodInvocation;
/**
* Contract for processing Sleuth annotations.
*
* @author Marcin Grzejszczak
* @since 2.1.0
*/
interface SleuthMethodInvocationProcessor {
Object process(MethodInvocation invocation, NewSpan newSpan, ContinueSpan continueSpan) throws Throwable;
Object process(MethodInvocation invocation, NewSpan newSpan,
ContinueSpan continueSpan) throws Throwable;
}

View File

@@ -25,43 +25,46 @@ import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
/**
* There are 3 different ways to add tags to a span. All of them are controlled by the annotation values.
* Precedence is:
* There are 3 different ways to add tags to a span. All of them are controlled by the
* annotation values. Precedence is:
*
* <ul>
* <li>try with the {@link TagValueResolver} bean</li>
* <li>if the value of the bean wasn't set, try to evaluate a SPEL expression</li>
* <li>if theres no SPEL expression just return a {@code toString()} value of the parameter</li>
* </ul>
* <ul>
* <li>try with the {@link TagValueResolver} bean</li>
* <li>if the value of the bean wasn't set, try to evaluate a SPEL expression</li>
* <li>if theres no SPEL expression just return a {@code toString()} value of the
* parameter</li>
* </ul>
*
* @author Christian Schwerdtfeger
* @since 1.2.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Target(value = { ElementType.PARAMETER })
@Target(value = {
ElementType.PARAMETER
})
public @interface SpanTag {
/**
* The name of the key of the tag which should be created.
* @return - The name of the key of the tag which should be created.
*/
@AliasFor("key")
String value() default "";
/**
* The name of the key of the tag which should be created.
* @return - The name of the key of the tag which should be created.
*/
@AliasFor("value")
String key() default "";
/**
* Execute this SPEL expression to calculate the tag value. Will be analyzed if no value of the
* {@link SpanTag#resolver()} was set.
* @return - Execute this SPEL expression to calculate the tag value. Will be analyzed if no
* value of the {@link SpanTag#resolver()} was set.
*/
String expression() default "";
/**
* Use this bean to resolve the tag value. Has the highest precedence.
* @return - Use this bean to resolve the tag value. Has the highest precedence.
*/
Class<? extends TagValueResolver> resolver() default NoOpTagValueResolver.class;

View File

@@ -29,13 +29,13 @@ import org.springframework.beans.factory.BeanFactory;
import org.springframework.util.StringUtils;
/**
* This class is able to find all methods annotated with the
* Sleuth annotations. All methods mean that if you have both an interface
* and an implementation annotated with Sleuth annotations then this class is capable
* of finding both of them and merging into one set of tracing information.
* This class is able to find all methods annotated with the Sleuth annotations. All
* methods mean that if you have both an interface and an implementation annotated with
* Sleuth annotations then this class is capable of finding both of them and merging into
* one set of tracing information.
*
* This information is then used to add proper tags to the span from the
* method arguments that are annotated with {@link SpanTag}.
* This information is then used to add proper tags to the span from the method arguments
* that are annotated with {@link SpanTag}.
*
* @author Christian Schwerdtfeger
* @since 1.2.0
@@ -45,8 +45,9 @@ class SpanTagAnnotationHandler {
private static final Log log = LogFactory.getLog(SpanTagAnnotationHandler.class);
private final BeanFactory beanFactory;
private SpanCustomizer spanCustomizer;
SpanTagAnnotationHandler(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@@ -56,14 +57,15 @@ class SpanTagAnnotationHandler {
Method method = pjp.getMethod();
Method mostSpecificMethod = AopUtils.getMostSpecificMethod(method,
pjp.getThis().getClass());
List<SleuthAnnotatedParameter> annotatedParameters =
SleuthAnnotationUtils.findAnnotatedParameters(mostSpecificMethod, pjp.getArguments());
List<SleuthAnnotatedParameter> annotatedParameters = SleuthAnnotationUtils
.findAnnotatedParameters(mostSpecificMethod, pjp.getArguments());
getAnnotationsFromInterfaces(pjp, mostSpecificMethod, annotatedParameters);
mergeAnnotatedMethodsIfNecessary(pjp, method, mostSpecificMethod,
annotatedParameters);
addAnnotatedArguments(annotatedParameters);
} catch (SecurityException e) {
log.error("Exception occurred while trying to add annotated parameters", e);
}
catch (SecurityException ex) {
log.error("Exception occurred while trying to add annotated parameters", ex);
}
}
@@ -75,9 +77,11 @@ class SpanTagAnnotationHandler {
for (Class<?> implementedInterface : implementedInterfaces) {
for (Method methodFromInterface : implementedInterface.getMethods()) {
if (methodsAreTheSame(mostSpecificMethod, methodFromInterface)) {
List<SleuthAnnotatedParameter> annotatedParametersForActualMethod =
SleuthAnnotationUtils.findAnnotatedParameters(methodFromInterface, pjp.getArguments());
mergeAnnotatedParameters(annotatedParameters, annotatedParametersForActualMethod);
List<SleuthAnnotatedParameter> annotatedParametersForActualMethod = SleuthAnnotationUtils
.findAnnotatedParameters(methodFromInterface,
pjp.getArguments());
mergeAnnotatedParameters(annotatedParameters,
annotatedParametersForActualMethod);
}
}
}
@@ -85,22 +89,25 @@ class SpanTagAnnotationHandler {
}
private boolean methodsAreTheSame(Method mostSpecificMethod, Method method1) {
return method1.getName().equals(mostSpecificMethod.getName()) &&
Arrays.equals(method1.getParameterTypes(), mostSpecificMethod.getParameterTypes());
return method1.getName().equals(mostSpecificMethod.getName()) && Arrays.equals(
method1.getParameterTypes(), mostSpecificMethod.getParameterTypes());
}
private void mergeAnnotatedMethodsIfNecessary(MethodInvocation pjp, Method method,
Method mostSpecificMethod, List<SleuthAnnotatedParameter> annotatedParameters) {
Method mostSpecificMethod,
List<SleuthAnnotatedParameter> annotatedParameters) {
// that can happen if we have an abstraction and a concrete class that is
// annotated with @NewSpan annotation
if (!method.equals(mostSpecificMethod)) {
List<SleuthAnnotatedParameter> annotatedParametersForActualMethod = SleuthAnnotationUtils.findAnnotatedParameters(
method, pjp.getArguments());
mergeAnnotatedParameters(annotatedParameters, annotatedParametersForActualMethod);
List<SleuthAnnotatedParameter> annotatedParametersForActualMethod = SleuthAnnotationUtils
.findAnnotatedParameters(method, pjp.getArguments());
mergeAnnotatedParameters(annotatedParameters,
annotatedParametersForActualMethod);
}
}
private void mergeAnnotatedParameters(List<SleuthAnnotatedParameter> annotatedParametersIndices,
private void mergeAnnotatedParameters(
List<SleuthAnnotatedParameter> annotatedParametersIndices,
List<SleuthAnnotatedParameter> annotatedParametersIndicesForActualMethod) {
for (SleuthAnnotatedParameter container : annotatedParametersIndicesForActualMethod) {
final int index = container.parameterIndex;
@@ -132,11 +139,9 @@ class SpanTagAnnotationHandler {
return this.spanCustomizer;
}
private String resolveTagKey(
SleuthAnnotatedParameter container) {
return StringUtils.hasText(container.annotation.value()) ?
container.annotation.value() : container.annotation.key();
private String resolveTagKey(SleuthAnnotatedParameter container) {
return StringUtils.hasText(container.annotation.value())
? container.annotation.value() : container.annotation.key();
}
String resolveTagValue(SpanTag annotation, Object argument) {
@@ -144,12 +149,15 @@ class SpanTagAnnotationHandler {
return "";
}
if (annotation.resolver() != NoOpTagValueResolver.class) {
TagValueResolver tagValueResolver = this.beanFactory.getBean(annotation.resolver());
TagValueResolver tagValueResolver = this.beanFactory
.getBean(annotation.resolver());
return tagValueResolver.resolve(argument);
} else if (StringUtils.hasText(annotation.expression())) {
}
else if (StringUtils.hasText(annotation.expression())) {
return this.beanFactory.getBean(TagValueExpressionResolver.class)
.resolve(annotation.expression(), argument);
}
return argument.toString();
}
}

View File

@@ -24,27 +24,32 @@ import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.SimpleEvaluationContext;
/**
* Uses SPEL to evaluate the expression. If an exception is thrown will return
* the {@code toString()} of the parameter.
* Uses SPEL to evaluate the expression. If an exception is thrown will return the
* {@code toString()} of the parameter.
*
* @author Marcin Grzejszczak
* @since 1.2.0
*/
class SpelTagValueExpressionResolver implements TagValueExpressionResolver {
private static final Log log = LogFactory.getLog(SpelTagValueExpressionResolver.class);
private static final Log log = LogFactory
.getLog(SpelTagValueExpressionResolver.class);
@Override
public String resolve(String expression, Object parameter) {
try {
SimpleEvaluationContext context = SimpleEvaluationContext
.forReadOnlyDataBinding()
.build();
.forReadOnlyDataBinding().build();
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expressionToEvaluate = expressionParser.parseExpression(expression);
Expression expressionToEvaluate = expressionParser
.parseExpression(expression);
return expressionToEvaluate.getValue(context, parameter, String.class);
} catch (Exception e) {
log.error("Exception occurred while tying to evaluate the SPEL expression [" + expression + "]", e);
}
catch (Exception ex) {
log.error("Exception occurred while tying to evaluate the SPEL expression ["
+ expression + "]", ex);
}
return parameter.toString();
}
}

View File

@@ -25,12 +25,11 @@ package org.springframework.cloud.sleuth.annotation;
public interface TagValueExpressionResolver {
/**
* Returns the tag value for the given parameter and the provided expression
*
* Returns the tag value for the given parameter and the provided expression.
* @param expression - the expression coming from {@link SpanTag#expression()}
* @param parameter - parameter annotated with {@link SpanTag}
* @return the value of the tag
*/
String resolve(String expression, Object parameter);
}

View File

@@ -25,11 +25,10 @@ package org.springframework.cloud.sleuth.annotation;
public interface TagValueResolver {
/**
* Returns the tag value for the given parameter
*
* Returns the tag value for the given parameter.
* @param parameter - parameter annotated with {@link SpanTag}
* @return the value of the tag
*/
String resolve(Object parameter);
}

View File

@@ -22,8 +22,9 @@ import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Sleuth settings
* Sleuth settings.
*
* @author Marcin Grzejszczak
* @since 1.0.11
*/
@ConfigurationProperties("spring.sleuth")
@@ -34,24 +35,28 @@ public class SleuthProperties {
/** When true, generate 128-bit trace IDs instead of 64-bit ones. */
private boolean traceId128 = false;
/** True means the tracing system supports sharing a span ID between a client and server. */
/**
* True means the tracing system supports sharing a span ID between a client and
* server.
*/
private boolean supportsJoin = true;
/**
* List of baggage key names that should be propagated out of process.
* These keys will be prefixed with `baggage` before the actual key.
* This property is set in order to be backward compatible with previous
* Sleuth versions.
* List of baggage key names that should be propagated out of process. These keys will
* be prefixed with `baggage` before the actual key. This property is set in order to
* be backward compatible with previous Sleuth versions.
*
* @see brave.propagation.ExtraFieldPropagation.FactoryBuilder#addPrefixedFields(String, java.util.Collection)
* @see brave.propagation.ExtraFieldPropagation.FactoryBuilder#addPrefixedFields(String,
* java.util.Collection)
*/
private List<String> baggageKeys = new ArrayList<>();
/**
* List of fields that are referenced the same in-process as it is on the wire. For example, the
* name "x-vcap-request-id" would be set as-is including the prefix.
* List of fields that are referenced the same in-process as it is on the wire. For
* example, the name "x-vcap-request-id" would be set as-is including the prefix.
*
* <p>Note: {@code fieldName} will be implicitly lower-cased.
* <p>
* Note: {@code fieldName} will be implicitly lower-cased.
*
* @see brave.propagation.ExtraFieldPropagation.FactoryBuilder#addField(String)
*/
@@ -96,4 +101,5 @@ public class SleuthProperties {
public void setPropagationKeys(List<String> propagationKeys) {
this.propagationKeys = propagationKeys;
}
}

View File

@@ -44,41 +44,43 @@ import zipkin2.Span;
import zipkin2.reporter.Reporter;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration}
* to enable tracing via Spring Cloud Sleuth.
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} to enable tracing via Spring Cloud Sleuth.
*
* @author Spencer Gibb
* @author Marcin Grzejszczak
* @since 2.0.0
*/
@Configuration
@ConditionalOnProperty(value="spring.sleuth.enabled", matchIfMissing=true)
@ConditionalOnProperty(value = "spring.sleuth.enabled", matchIfMissing = true)
@EnableConfigurationProperties(SleuthProperties.class)
public class TraceAutoConfiguration {
/**
* Tracer bean name. Name of the bean matters for some instrumentations.
*/
public static final String TRACER_BEAN_NAME = "tracer";
@Autowired(required = false) List<SpanAdjuster> spanAdjusters = new ArrayList<>();
@Autowired(required = false) List<FinishedSpanHandler> finishedSpanHandlers = new ArrayList<>();
@Autowired(required = false) List<CurrentTraceContext.ScopeDecorator> scopeDecorators = new ArrayList<>();
@Autowired(required = false)
List<SpanAdjuster> spanAdjusters = new ArrayList<>();
@Autowired(required = false)
List<FinishedSpanHandler> finishedSpanHandlers = new ArrayList<>();
@Autowired(required = false)
List<CurrentTraceContext.ScopeDecorator> scopeDecorators = new ArrayList<>();
@Bean
@ConditionalOnMissingBean
// NOTE: stable bean name as might be used outside sleuth
Tracing tracing(@Value("${spring.zipkin.service.name:${spring.application.name:default}}") String serviceName,
Propagation.Factory factory,
CurrentTraceContext currentTraceContext,
Reporter<zipkin2.Span> reporter,
Sampler sampler,
ErrorParser errorParser,
SleuthProperties sleuthProperties
) {
Tracing.Builder builder = Tracing.newBuilder()
.sampler(sampler)
.errorParser(errorParser)
.localServiceName(serviceName)
.propagationFactory(factory)
.currentTraceContext(currentTraceContext)
Tracing tracing(
@Value("${spring.zipkin.service.name:${spring.application.name:default}}") String serviceName,
Propagation.Factory factory, CurrentTraceContext currentTraceContext,
Reporter<zipkin2.Span> reporter, Sampler sampler, ErrorParser errorParser,
SleuthProperties sleuthProperties) {
Tracing.Builder builder = Tracing.newBuilder().sampler(sampler)
.errorParser(errorParser).localServiceName(serviceName)
.propagationFactory(factory).currentTraceContext(currentTraceContext)
.spanReporter(adjustedReporter(reporter))
.traceId128Bit(sleuthProperties.isTraceId128())
.supportsJoin(sleuthProperties.isSupportsJoin());
@@ -89,7 +91,7 @@ public class TraceAutoConfiguration {
}
private Reporter<zipkin2.Span> adjustedReporter(Reporter<zipkin2.Span> delegate) {
return span -> {
return (span) -> {
Span spanToAdjust = span;
for (SpanAdjuster spanAdjuster : this.spanAdjusters) {
spanToAdjust = spanAdjuster.adjust(spanToAdjust);
@@ -111,14 +113,16 @@ public class TraceAutoConfiguration {
}
@Bean
@ConditionalOnMissingBean SpanNamer sleuthSpanNamer() {
@ConditionalOnMissingBean
SpanNamer sleuthSpanNamer() {
return new DefaultSpanNamer();
}
@Bean
@ConditionalOnMissingBean
Propagation.Factory sleuthPropagation(SleuthProperties sleuthProperties) {
if (sleuthProperties.getBaggageKeys().isEmpty() && sleuthProperties.getPropagationKeys().isEmpty()) {
if (sleuthProperties.getBaggageKeys().isEmpty()
&& sleuthProperties.getPropagationKeys().isEmpty()) {
return B3Propagation.FACTORY;
}
ExtraFieldPropagation.FactoryBuilder factoryBuilder = ExtraFieldPropagation
@@ -170,4 +174,5 @@ public class TraceAutoConfiguration {
CurrentSpanCustomizer spanCustomizer(Tracing tracing) {
return CurrentSpanCustomizer.create(tracing);
}
}

View File

@@ -29,7 +29,7 @@ import org.springframework.core.env.PropertySource;
/**
* Adds default properties for the application:
* <ul>
* <li>logging pattern level that prints trace information (e.g. trace ids)</li>
* <li>logging pattern level that prints trace information (e.g. trace ids)</li>
* </ul>
*
* @author Dave Syer
@@ -46,7 +46,8 @@ public class TraceEnvironmentPostProcessor implements EnvironmentPostProcessor {
Map<String, Object> map = new HashMap<String, Object>();
// This doesn't work with all logging systems but it's a useful default so you see
// traces in logs without having to configure it.
if (Boolean.parseBoolean(environment.getProperty("spring.sleuth.enabled", "true"))) {
if (Boolean
.parseBoolean(environment.getProperty("spring.sleuth.enabled", "true"))) {
map.put("logging.pattern.level",
"%5p [${spring.zipkin.service.name:${spring.application.name:-}},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}]");
}

View File

@@ -21,8 +21,9 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration}
* that wraps an existing custom {@link AsyncConfigurer} in a {@link LazyTraceAsyncCustomizer}
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that wraps an existing custom {@link AsyncConfigurer} in a
* {@link LazyTraceAsyncCustomizer}.
*
* @author Jesus Alonso
* @since 2.1.0
@@ -30,4 +31,5 @@ import org.springframework.scheduling.annotation.AsyncConfigurer;
@Configuration
@EnableConfigurationProperties(SleuthAsyncProperties.class)
public class AsyncAutoConfiguration {
}

View File

@@ -29,8 +29,9 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration}
* that wraps an existing custom {@link AsyncConfigurer} in a {@link LazyTraceAsyncCustomizer}
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that wraps an existing custom {@link AsyncConfigurer} in a
* {@link LazyTraceAsyncCustomizer}.
*
* @author Dave Syer
* @since 1.0.0
@@ -54,11 +55,12 @@ public class AsyncCustomAutoConfiguration implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof AsyncConfigurer && !(bean instanceof LazyTraceAsyncCustomizer)) {
if (bean instanceof AsyncConfigurer
&& !(bean instanceof LazyTraceAsyncCustomizer)) {
AsyncConfigurer configurer = (AsyncConfigurer) bean;
return new LazyTraceAsyncCustomizer(this.beanFactory, configurer);
}
return bean;
}
}
}

View File

@@ -35,13 +35,12 @@ import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration}
* enabling async related processing.
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} enabling async related processing.
*
* @author Dave Syer
* @author Marcin Grzejszczak
* @since 1.0.0
*
* @see LazyTraceExecutor
* @see TraceAsyncAspect
*/
@@ -50,18 +49,10 @@ import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
@ConditionalOnBean(Tracing.class)
public class AsyncDefaultAutoConfiguration {
@Configuration
@ConditionalOnMissingBean(AsyncConfigurer.class)
@ConditionalOnProperty(value = "spring.sleuth.async.configurer.enabled", matchIfMissing = true)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static class DefaultAsyncConfigurerSupport extends AsyncConfigurerSupport {
@Autowired private BeanFactory beanFactory;
@Override
public Executor getAsyncExecutor() {
return new LazyTraceExecutor(this.beanFactory, new SimpleAsyncTaskExecutor());
}
@Bean
public static ExecutorBeanPostProcessor executorBeanPostProcessor(
BeanFactory beanFactory) {
return new ExecutorBeanPostProcessor(beanFactory);
}
@Bean
@@ -69,9 +60,23 @@ public class AsyncDefaultAutoConfiguration {
return new TraceAsyncAspect(tracer, spanNamer);
}
@Bean
public static ExecutorBeanPostProcessor executorBeanPostProcessor(BeanFactory beanFactory) {
return new ExecutorBeanPostProcessor(beanFactory);
/**
* Wrapper for the async executor.
*/
@Configuration
@ConditionalOnMissingBean(AsyncConfigurer.class)
@ConditionalOnProperty(value = "spring.sleuth.async.configurer.enabled", matchIfMissing = true)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static class DefaultAsyncConfigurerSupport extends AsyncConfigurerSupport {
@Autowired
private BeanFactory beanFactory;
@Override
public Executor getAsyncExecutor() {
return new LazyTraceExecutor(this.beanFactory, new SimpleAsyncTaskExecutor());
}
}
}
}

View File

@@ -34,9 +34,8 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.ReflectionUtils;
/**
* Bean post processor that wraps a call to an {@link Executor} either in a
* JDK or CGLIB proxy. Depending on whether the implementation has a final
* method or is final.
* Bean post processor that wraps a call to an {@link Executor} either in a JDK or CGLIB
* proxy. Depending on whether the implementation has a final method or is final.
*
* @author Marcin Grzejszczak
* @author Jesus Alonso
@@ -45,10 +44,10 @@ import org.springframework.util.ReflectionUtils;
*/
class ExecutorBeanPostProcessor implements BeanPostProcessor {
private static final Log log = LogFactory.getLog(
ExecutorBeanPostProcessor.class);
private static final Log log = LogFactory.getLog(ExecutorBeanPostProcessor.class);
private final BeanFactory beanFactory;
private SleuthAsyncProperties sleuthAsyncProperties;
ExecutorBeanPostProcessor(BeanFactory beanFactory) {
@@ -65,29 +64,35 @@ class ExecutorBeanPostProcessor implements BeanPostProcessor {
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof Executor && !(bean instanceof ThreadPoolTaskExecutor)) {
Method execute = ReflectionUtils.findMethod(bean.getClass(), "execute", Runnable.class);
Method execute = ReflectionUtils.findMethod(bean.getClass(), "execute",
Runnable.class);
boolean methodFinal = Modifier.isFinal(execute.getModifiers());
boolean classFinal = Modifier.isFinal(bean.getClass().getModifiers());
boolean cglibProxy = !methodFinal && !classFinal;
Executor executor = (Executor) bean;
try {
return createProxy(bean, cglibProxy, executor);
} catch (AopConfigException e) {
}
catch (AopConfigException ex) {
if (cglibProxy) {
if (log.isDebugEnabled()) {
log.debug("Exception occurred while trying to create a proxy, falling back to JDK proxy", e);
log.debug(
"Exception occurred while trying to create a proxy, falling back to JDK proxy",
ex);
}
return createProxy(bean, false, executor);
}
throw e;
throw ex;
}
} else if (bean instanceof ThreadPoolTaskExecutor) {
}
else if (bean instanceof ThreadPoolTaskExecutor) {
if (isProxyNeeded(beanName)) {
boolean classFinal = Modifier.isFinal(bean.getClass().getModifiers());
boolean cglibProxy = !classFinal;
ThreadPoolTaskExecutor executor = (ThreadPoolTaskExecutor) bean;
return createThreadPoolTaskExecutorProxy(bean, cglibProxy, executor);
} else {
}
else {
log.info("Not instrumenting bean " + beanName);
}
}
@@ -103,8 +108,10 @@ class ExecutorBeanPostProcessor implements BeanPostProcessor {
ThreadPoolTaskExecutor executor) {
ProxyFactoryBean factory = new ProxyFactoryBean();
factory.setProxyTargetClass(cglibProxy);
factory.addAdvice(new ExecutorMethodInterceptor<ThreadPoolTaskExecutor>(executor, this.beanFactory) {
@Override Executor executor(BeanFactory beanFactory, ThreadPoolTaskExecutor executor) {
factory.addAdvice(new ExecutorMethodInterceptor<ThreadPoolTaskExecutor>(executor,
this.beanFactory) {
@Override
Executor executor(BeanFactory beanFactory, ThreadPoolTaskExecutor executor) {
return new LazyTraceThreadPoolTaskExecutor(beanFactory, executor);
}
});
@@ -120,18 +127,27 @@ class ExecutorBeanPostProcessor implements BeanPostProcessor {
factory.setTarget(bean);
return factory.getObject();
}
private SleuthAsyncProperties asyncConfigurationProperties() {
if (this.sleuthAsyncProperties == null) {
this.sleuthAsyncProperties = this.beanFactory.getBean(SleuthAsyncProperties.class);
this.sleuthAsyncProperties = this.beanFactory
.getBean(SleuthAsyncProperties.class);
}
return this.sleuthAsyncProperties;
}
}
/**
* Interceptor for executor methods.
*
* @param <T> - executor type
* @author Marcin Grzejszczak
*/
class ExecutorMethodInterceptor<T extends Executor> implements MethodInterceptor {
private final T delegate;
private final BeanFactory beanFactory;
ExecutorMethodInterceptor(T delegate, BeanFactory beanFactory) {
@@ -139,14 +155,15 @@ class ExecutorMethodInterceptor<T extends Executor> implements MethodInterceptor
this.beanFactory = beanFactory;
}
@Override public Object invoke(MethodInvocation invocation)
throws Throwable {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Executor executor = executor(this.beanFactory, this.delegate);
Method methodOnTracedBean = getMethod(invocation, executor);
if (methodOnTracedBean != null) {
try {
return methodOnTracedBean.invoke(executor, invocation.getArguments());
} catch (InvocationTargetException ex) {
}
catch (InvocationTargetException ex) {
// gh-1092: throw the target exception (if present)
Throwable cause = ex.getCause();
throw (cause != null) ? cause : ex;
@@ -157,11 +174,12 @@ class ExecutorMethodInterceptor<T extends Executor> implements MethodInterceptor
private Method getMethod(MethodInvocation invocation, Object object) {
Method method = invocation.getMethod();
return ReflectionUtils
.findMethod(object.getClass(), method.getName(), method.getParameterTypes());
return ReflectionUtils.findMethod(object.getClass(), method.getName(),
method.getParameterTypes());
}
Executor executor(BeanFactory beanFactory, T executor) {
return new LazyTraceExecutor(beanFactory, executor);
}
}

View File

@@ -24,8 +24,8 @@ import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
/**
* {@link AsyncConfigurerSupport} that creates a tracing data passing version
* of the {@link Executor}
* {@link AsyncConfigurerSupport} that creates a tracing data passing version of the
* {@link Executor}.
*
* @author Dave Syer
* @since 1.0.0
@@ -33,6 +33,7 @@ import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
public class LazyTraceAsyncCustomizer extends AsyncConfigurerSupport {
private final BeanFactory beanFactory;
private final AsyncConfigurer delegate;
public LazyTraceAsyncCustomizer(BeanFactory beanFactory, AsyncConfigurer delegate) {

View File

@@ -27,7 +27,7 @@ import org.springframework.cloud.sleuth.DefaultSpanNamer;
import org.springframework.cloud.sleuth.SpanNamer;
/**
* {@link Executor} that wraps {@link Runnable} in a trace representation
* {@link Executor} that wraps {@link Runnable} in a trace representation.
*
* @author Dave Syer
* @since 1.0.0
@@ -35,10 +35,9 @@ import org.springframework.cloud.sleuth.SpanNamer;
public class LazyTraceExecutor implements Executor {
private static final Log log = LogFactory.getLog(LazyTraceExecutor.class);
private Tracing tracing;
private final BeanFactory beanFactory;
private final Executor delegate;
private Tracing tracing;
private SpanNamer spanNamer;
public LazyTraceExecutor(BeanFactory beanFactory, Executor delegate) {
@@ -52,7 +51,7 @@ public class LazyTraceExecutor implements Executor {
try {
this.tracing = this.beanFactory.getBean(Tracing.class);
}
catch (NoSuchBeanDefinitionException e) {
catch (NoSuchBeanDefinitionException ex) {
this.delegate.execute(command);
return;
}
@@ -66,11 +65,13 @@ public class LazyTraceExecutor implements Executor {
try {
this.spanNamer = this.beanFactory.getBean(SpanNamer.class);
}
catch (NoSuchBeanDefinitionException e) {
log.warn("SpanNamer bean not found - will provide a manually created instance");
catch (NoSuchBeanDefinitionException ex) {
log.warn(
"SpanNamer bean not found - will provide a manually created instance");
return new DefaultSpanNamer();
}
}
return this.spanNamer;
}
}

View File

@@ -34,7 +34,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.concurrent.ListenableFuture;
/**
* Trace representation of {@link ThreadPoolTaskExecutor}
* Trace representation of {@link ThreadPoolTaskExecutor}.
*
* @author Marcin Grzejszczak
* @since 1.0.10
@@ -42,11 +42,15 @@ import org.springframework.util.concurrent.ListenableFuture;
@SuppressWarnings("serial")
public class LazyTraceThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
private static final Log log = LogFactory.getLog(LazyTraceThreadPoolTaskExecutor.class);
private static final Log log = LogFactory
.getLog(LazyTraceThreadPoolTaskExecutor.class);
private final BeanFactory beanFactory;
private final ThreadPoolTaskExecutor delegate;
private Tracing tracing;
private SpanNamer spanNamer;
public LazyTraceThreadPoolTaskExecutor(BeanFactory beanFactory,
@@ -62,7 +66,8 @@ public class LazyTraceThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
@Override
public void execute(Runnable task, long startTimeout) {
this.delegate.execute(new TraceRunnable(tracing(), spanNamer(), task), startTimeout);
this.delegate.execute(new TraceRunnable(tracing(), spanNamer(), task),
startTimeout);
}
@Override
@@ -77,41 +82,46 @@ public class LazyTraceThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
@Override
public ListenableFuture<?> submitListenable(Runnable task) {
return this.delegate.submitListenable(new TraceRunnable(tracing(), spanNamer(), task));
return this.delegate
.submitListenable(new TraceRunnable(tracing(), spanNamer(), task));
}
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
return this.delegate.submitListenable(new TraceCallable<>(tracing(), spanNamer(), task));
return this.delegate
.submitListenable(new TraceCallable<>(tracing(), spanNamer(), task));
}
@Override public boolean prefersShortLivedTasks() {
@Override
public boolean prefersShortLivedTasks() {
return this.delegate.prefersShortLivedTasks();
}
@Override public void setThreadFactory(ThreadFactory threadFactory) {
@Override
public void setThreadFactory(ThreadFactory threadFactory) {
this.delegate.setThreadFactory(threadFactory);
}
@Override public void setThreadNamePrefix(String threadNamePrefix) {
this.delegate.setThreadNamePrefix(threadNamePrefix);
}
@Override public void setRejectedExecutionHandler(
@Override
public void setRejectedExecutionHandler(
RejectedExecutionHandler rejectedExecutionHandler) {
this.delegate.setRejectedExecutionHandler(rejectedExecutionHandler);
}
@Override public void setWaitForTasksToCompleteOnShutdown(
@Override
public void setWaitForTasksToCompleteOnShutdown(
boolean waitForJobsToCompleteOnShutdown) {
this.delegate.setWaitForTasksToCompleteOnShutdown(waitForJobsToCompleteOnShutdown);
this.delegate
.setWaitForTasksToCompleteOnShutdown(waitForJobsToCompleteOnShutdown);
}
@Override public void setAwaitTerminationSeconds(int awaitTerminationSeconds) {
@Override
public void setAwaitTerminationSeconds(int awaitTerminationSeconds) {
this.delegate.setAwaitTerminationSeconds(awaitTerminationSeconds);
}
@Override public void setBeanName(String name) {
@Override
public void setBeanName(String name) {
this.delegate.setBeanName(name);
}
@@ -120,11 +130,13 @@ public class LazyTraceThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
return this.delegate.getThreadPoolExecutor();
}
@Override public int getPoolSize() {
@Override
public int getPoolSize() {
return this.delegate.getPoolSize();
}
@Override public int getActiveCount() {
@Override
public int getActiveCount() {
return this.delegate.getActiveCount();
}
@@ -140,7 +152,8 @@ public class LazyTraceThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
super.afterPropertiesSet();
}
@Override public void initialize() {
@Override
public void initialize() {
this.delegate.initialize();
}
@@ -150,79 +163,103 @@ public class LazyTraceThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
super.shutdown();
}
@Override public Thread newThread(Runnable runnable) {
@Override
public Thread newThread(Runnable runnable) {
return this.delegate.newThread(runnable);
}
@Override public String getThreadNamePrefix() {
@Override
public String getThreadNamePrefix() {
return this.delegate.getThreadNamePrefix();
}
@Override public void setThreadPriority(int threadPriority) {
this.delegate.setThreadPriority(threadPriority);
@Override
public void setThreadNamePrefix(String threadNamePrefix) {
this.delegate.setThreadNamePrefix(threadNamePrefix);
}
@Override public int getThreadPriority() {
@Override
public int getThreadPriority() {
return this.delegate.getThreadPriority();
}
@Override public void setDaemon(boolean daemon) {
this.delegate.setDaemon(daemon);
@Override
public void setThreadPriority(int threadPriority) {
this.delegate.setThreadPriority(threadPriority);
}
@Override public boolean isDaemon() {
@Override
public boolean isDaemon() {
return this.delegate.isDaemon();
}
@Override public void setThreadGroupName(String name) {
@Override
public void setDaemon(boolean daemon) {
this.delegate.setDaemon(daemon);
}
@Override
public void setThreadGroupName(String name) {
this.delegate.setThreadGroupName(name);
}
@Override public void setThreadGroup(ThreadGroup threadGroup) {
this.delegate.setThreadGroup(threadGroup);
}
@Override public ThreadGroup getThreadGroup() {
@Override
public ThreadGroup getThreadGroup() {
return this.delegate.getThreadGroup();
}
@Override public Thread createThread(Runnable runnable) {
@Override
public void setThreadGroup(ThreadGroup threadGroup) {
this.delegate.setThreadGroup(threadGroup);
}
@Override
public Thread createThread(Runnable runnable) {
return this.delegate.createThread(runnable);
}
@Override public void setCorePoolSize(int corePoolSize) {
this.delegate.setCorePoolSize(corePoolSize);
}
@Override public int getCorePoolSize() {
@Override
public int getCorePoolSize() {
return this.delegate.getCorePoolSize();
}
@Override public void setMaxPoolSize(int maxPoolSize) {
this.delegate.setMaxPoolSize(maxPoolSize);
@Override
public void setCorePoolSize(int corePoolSize) {
this.delegate.setCorePoolSize(corePoolSize);
}
@Override public int getMaxPoolSize() {
@Override
public int getMaxPoolSize() {
return this.delegate.getMaxPoolSize();
}
@Override public void setKeepAliveSeconds(int keepAliveSeconds) {
this.delegate.setKeepAliveSeconds(keepAliveSeconds);
@Override
public void setMaxPoolSize(int maxPoolSize) {
this.delegate.setMaxPoolSize(maxPoolSize);
}
@Override public int getKeepAliveSeconds() {
@Override
public int getKeepAliveSeconds() {
return this.delegate.getKeepAliveSeconds();
}
@Override public void setQueueCapacity(int queueCapacity) {
@Override
public void setKeepAliveSeconds(int keepAliveSeconds) {
this.delegate.setKeepAliveSeconds(keepAliveSeconds);
}
@Override
public void setQueueCapacity(int queueCapacity) {
this.delegate.setQueueCapacity(queueCapacity);
}
@Override public void setAllowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) {
@Override
public void setAllowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) {
this.delegate.setAllowCoreThreadTimeOut(allowCoreThreadTimeOut);
}
@Override public void setTaskDecorator(TaskDecorator taskDecorator) {
@Override
public void setTaskDecorator(TaskDecorator taskDecorator) {
this.delegate.setTaskDecorator(taskDecorator);
}
@@ -238,11 +275,13 @@ public class LazyTraceThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
try {
this.spanNamer = this.beanFactory.getBean(SpanNamer.class);
}
catch (NoSuchBeanDefinitionException e) {
log.warn("SpanNamer bean not found - will provide a manually created instance");
catch (NoSuchBeanDefinitionException ex) {
log.warn(
"SpanNamer bean not found - will provide a manually created instance");
return new DefaultSpanNamer();
}
}
return this.spanNamer;
}
}

View File

@@ -22,7 +22,7 @@ import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Settings for disable instrumentation of ThreadPoolTaskExecutors
* Settings for disable instrumentation of ThreadPoolTaskExecutors.
*
* @author Jesus Alonso
* @since 2.1.0
@@ -32,8 +32,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
public class SleuthAsyncProperties {
/**
* List of {@link java.util.concurrent.Executor} bean names that should
* be ignored and not wrapped in a trace representation
* List of {@link java.util.concurrent.Executor} bean names that should be ignored and
* not wrapped in a trace representation.
*/
private List<String> ignoredBeans = Collections.emptyList();
@@ -44,4 +44,5 @@ public class SleuthAsyncProperties {
public void setIgnoredBeans(List<String> ignoredBeans) {
this.ignoredBeans = ignoredBeans;
}
}

View File

@@ -34,16 +34,17 @@ import org.springframework.util.ReflectionUtils;
*
* @author Marcin Grzejszczak
* @since 1.0.0
*
* @see Tracer
*/
@Aspect
public class TraceAsyncAspect {
private static final String CLASS_KEY = "class";
private static final String METHOD_KEY = "method";
private final Tracer tracer;
private final SpanNamer spanNamer;
public TraceAsyncAspect(Tracer tracer, SpanNamer spanNamer) {
@@ -59,11 +60,12 @@ public class TraceAsyncAspect {
span = this.tracer.nextSpan();
}
span = span.name(spanName);
try(Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) {
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) {
span.tag(CLASS_KEY, pjp.getTarget().getClass().getSimpleName());
span.tag(METHOD_KEY, pjp.getSignature().getName());
return pjp.proceed();
} finally {
}
finally {
span.finish();
}
}
@@ -76,8 +78,8 @@ public class TraceAsyncAspect {
private Method getMethod(ProceedingJoinPoint pjp, Object object) {
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
return ReflectionUtils
.findMethod(object.getClass(), method.getName(), method.getParameterTypes());
return ReflectionUtils.findMethod(object.getClass(), method.getName(),
method.getParameterTypes());
}
}

View File

@@ -24,17 +24,18 @@ import org.springframework.core.task.AsyncListenableTaskExecutor;
import org.springframework.util.concurrent.ListenableFuture;
/**
* AsyncListenableTaskExecutor that wraps all Runnable / Callable tasks into
* their trace related representation
* AsyncListenableTaskExecutor that wraps all Runnable / Callable tasks into their trace
* related representation.
*
* @since 1.0.0
*
* @author Marcin Grzejszczak
* @see brave.propagation.CurrentTraceContext#wrap(Runnable)
* @see brave.propagation.CurrentTraceContext#wrap(Callable)
*/
public class TraceAsyncListenableTaskExecutor implements AsyncListenableTaskExecutor {
private final AsyncListenableTaskExecutor delegate;
private final Tracing tracing;
TraceAsyncListenableTaskExecutor(AsyncListenableTaskExecutor delegate,
@@ -45,17 +46,20 @@ public class TraceAsyncListenableTaskExecutor implements AsyncListenableTaskExec
@Override
public ListenableFuture<?> submitListenable(Runnable task) {
return this.delegate.submitListenable(this.tracing.currentTraceContext().wrap(task));
return this.delegate
.submitListenable(this.tracing.currentTraceContext().wrap(task));
}
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
return this.delegate.submitListenable(this.tracing.currentTraceContext().wrap(task));
return this.delegate
.submitListenable(this.tracing.currentTraceContext().wrap(task));
}
@Override
public void execute(Runnable task, long startTimeout) {
this.delegate.execute(this.tracing.currentTraceContext().wrap(task), startTimeout);
this.delegate.execute(this.tracing.currentTraceContext().wrap(task),
startTimeout);
}
@Override
@@ -73,4 +77,4 @@ public class TraceAsyncListenableTaskExecutor implements AsyncListenableTaskExec
this.delegate.execute(this.tracing.currentTraceContext().wrap(task));
}
}
}

View File

@@ -16,56 +16,64 @@
package org.springframework.cloud.sleuth.instrument.async;
import brave.ScopedSpan;
import brave.Tracing;
import brave.propagation.TraceContext;
import java.util.concurrent.Callable;
import brave.ScopedSpan;
import brave.Tracer;
import brave.Tracing;
import brave.propagation.TraceContext;
import org.springframework.cloud.sleuth.SpanNamer;
/**
* Callable that passes Span between threads. The Span name is
* taken either from the passed value or from the {@link SpanNamer}
* interface.
* Callable that passes Span between threads. The Span name is taken either from the
* passed value or from the {@link SpanNamer} interface.
*
* @author Spencer Gibb
* @author Marcin Grzejszczak
* @param <V> - return type from callable
* @since 1.0.0
*/
public class TraceCallable<V> implements Callable<V> {
/**
* Since we don't know the exact operation name we provide a default
* name for the Span
* Since we don't know the exact operation name we provide a default name for the Span.
*/
private static final String DEFAULT_SPAN_NAME = "async";
private final Tracer tracer;
private final Callable<V> delegate;
private final TraceContext parent;
private final String spanName;
public TraceCallable(Tracing tracing, SpanNamer spanNamer, Callable<V> delegate) {
this(tracing, spanNamer, delegate, null);
}
public TraceCallable(Tracing tracing, SpanNamer spanNamer, Callable<V> delegate, String name) {
public TraceCallable(Tracing tracing, SpanNamer spanNamer, Callable<V> delegate,
String name) {
this.tracer = tracing.tracer();
this.delegate = delegate;
this.parent = tracing.currentTraceContext().get();
this.spanName = name != null ? name : spanNamer.name(delegate, DEFAULT_SPAN_NAME);
}
@Override public V call() throws Exception {
ScopedSpan span = this.tracer.startScopedSpanWithParent(this.spanName, this.parent);
@Override
public V call() throws Exception {
ScopedSpan span = this.tracer.startScopedSpanWithParent(this.spanName,
this.parent);
try {
return this.delegate.call();
} catch (Exception | Error e) {
span.error(e);
throw e;
} finally {
}
catch (Exception | Error ex) {
span.error(ex);
throw ex;
}
finally {
span.finish();
}
}
}

View File

@@ -23,9 +23,8 @@ import brave.propagation.TraceContext;
import org.springframework.cloud.sleuth.SpanNamer;
/**
* Runnable that passes Span between threads. The Span name is
* taken either from the passed value or from the {@link SpanNamer}
* interface.
* Runnable that passes Span between threads. The Span name is taken either from the
* passed value or from the {@link SpanNamer} interface.
*
* @author Spencer Gibb
* @author Marcin Grzejszczak
@@ -34,21 +33,24 @@ import org.springframework.cloud.sleuth.SpanNamer;
public class TraceRunnable implements Runnable {
/**
* Since we don't know the exact operation name we provide a default
* name for the Span
* Since we don't know the exact operation name we provide a default name for the Span
*/
private static final String DEFAULT_SPAN_NAME = "async";
private final Tracer tracer;
private final Runnable delegate;
private final TraceContext parent;
private final String spanName;
public TraceRunnable(Tracing tracing, SpanNamer spanNamer, Runnable delegate) {
this(tracing, spanNamer, delegate, null);
}
public TraceRunnable(Tracing tracing, SpanNamer spanNamer, Runnable delegate, String name) {
public TraceRunnable(Tracing tracing, SpanNamer spanNamer, Runnable delegate,
String name) {
this.tracer = tracing.tracer();
this.delegate = delegate;
this.parent = tracing.currentTraceContext().get();
@@ -57,14 +59,18 @@ public class TraceRunnable implements Runnable {
@Override
public void run() {
ScopedSpan span = this.tracer.startScopedSpanWithParent(this.spanName, this.parent);
ScopedSpan span = this.tracer.startScopedSpanWithParent(this.spanName,
this.parent);
try {
this.delegate.run();
} catch (Exception | Error e) {
}
catch (Exception | Error e) {
span.error(e);
throw e;
} finally {
}
finally {
span.finish();
}
}
}

View File

@@ -36,17 +36,24 @@ import org.springframework.cloud.sleuth.SpanNamer;
* @since 1.0.0
*/
public class TraceableExecutorService implements ExecutorService {
final ExecutorService delegate;
private final String spanName;
Tracing tracing;
SpanNamer spanNamer;
BeanFactory beanFactory;
public TraceableExecutorService(BeanFactory beanFactory, final ExecutorService delegate) {
public TraceableExecutorService(BeanFactory beanFactory,
final ExecutorService delegate) {
this(beanFactory, delegate, null);
}
public TraceableExecutorService(BeanFactory beanFactory, final ExecutorService delegate, String spanName) {
public TraceableExecutorService(BeanFactory beanFactory,
final ExecutorService delegate, String spanName) {
this.delegate = delegate;
this.beanFactory = beanFactory;
this.spanName = spanName;
@@ -54,7 +61,8 @@ public class TraceableExecutorService implements ExecutorService {
@Override
public void execute(Runnable command) {
final Runnable r = new TraceRunnable(tracing(), spanNamer(), command, this.spanName);
final Runnable r = new TraceRunnable(tracing(), spanNamer(), command,
this.spanName);
this.delegate.execute(r);
}
@@ -79,7 +87,8 @@ public class TraceableExecutorService implements ExecutorService {
}
@Override
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
return this.delegate.awaitTermination(timeout, unit);
}
@@ -102,28 +111,32 @@ public class TraceableExecutorService implements ExecutorService {
}
@Override
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException {
return this.delegate.invokeAll(wrapCallableCollection(tasks));
}
@Override
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException {
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit) throws InterruptedException {
return this.delegate.invokeAll(wrapCallableCollection(tasks), timeout, unit);
}
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException {
return this.delegate.invokeAny(wrapCallableCollection(tasks));
}
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout,
TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return this.delegate.invokeAny(wrapCallableCollection(tasks), timeout, unit);
}
private <T> Collection<? extends Callable<T>> wrapCallableCollection(Collection<? extends Callable<T>> tasks) {
private <T> Collection<? extends Callable<T>> wrapCallableCollection(
Collection<? extends Callable<T>> tasks) {
List<Callable<T>> ts = new ArrayList<>();
for (Callable<T> task : tasks) {
if (!(task instanceof TraceCallable)) {
@@ -146,4 +159,5 @@ public class TraceableExecutorService implements ExecutorService {
}
return this.spanNamer;
}
}

View File

@@ -30,9 +30,11 @@ import org.springframework.beans.factory.BeanFactory;
* @author Gaurav Rai Mazra
* @since 1.0.0
*/
public class TraceableScheduledExecutorService extends TraceableExecutorService implements ScheduledExecutorService {
public class TraceableScheduledExecutorService extends TraceableExecutorService
implements ScheduledExecutorService {
public TraceableScheduledExecutorService(BeanFactory beanFactory, final ExecutorService delegate) {
public TraceableScheduledExecutorService(BeanFactory beanFactory,
final ExecutorService delegate) {
super(beanFactory, delegate);
}
@@ -47,21 +49,26 @@ public class TraceableScheduledExecutorService extends TraceableExecutorService
}
@Override
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay,
TimeUnit unit) {
Callable<V> c = new TraceCallable<>(tracing(), spanNamer(), callable);
return getScheduledExecutorService().schedule(c, delay, unit);
}
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay,
long period, TimeUnit unit) {
Runnable r = new TraceRunnable(tracing(), spanNamer(), command);
return getScheduledExecutorService().scheduleAtFixedRate(r, initialDelay, period, unit);
return getScheduledExecutorService().scheduleAtFixedRate(r, initialDelay, period,
unit);
}
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay,
long delay, TimeUnit unit) {
Runnable r = new TraceRunnable(tracing(), spanNamer(), command);
return getScheduledExecutorService().scheduleWithFixedDelay(r, initialDelay, delay, unit);
return getScheduledExecutorService().scheduleWithFixedDelay(r, initialDelay,
delay, unit);
}
}

View File

@@ -17,6 +17,7 @@
package org.springframework.cloud.sleuth.instrument.hystrix;
import brave.Tracing;
import com.netflix.hystrix.HystrixCommand;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -26,15 +27,13 @@ import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.netflix.hystrix.HystrixCommand;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration}
* that registers a custom Sleuth {@link com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy}.
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that registers a custom Sleuth
* {@link com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy}.
*
* @author Marcin Grzejszczak
* @since 1.0.0
*
* @see SleuthHystrixConcurrencyStrategy
*/
@Configuration
@@ -44,7 +43,8 @@ import com.netflix.hystrix.HystrixCommand;
@ConditionalOnProperty(value = "spring.sleuth.hystrix.strategy.enabled", matchIfMissing = true)
public class SleuthHystrixAutoConfiguration {
@Bean SleuthHystrixConcurrencyStrategy sleuthHystrixConcurrencyStrategy(Tracing tracing,
@Bean
SleuthHystrixConcurrencyStrategy sleuthHystrixConcurrencyStrategy(Tracing tracing,
SpanNamer spanNamer) {
return new SleuthHystrixConcurrencyStrategy(tracing, spanNamer);
}

View File

@@ -49,11 +49,14 @@ import org.springframework.cloud.sleuth.instrument.async.TraceCallable;
public class SleuthHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
private static final String HYSTRIX_COMPONENT = "hystrix";
private static final Log log = LogFactory
.getLog(SleuthHystrixConcurrencyStrategy.class);
private final Tracing tracing;
private final SpanNamer spanNamer;
private HystrixConcurrencyStrategy delegate;
public SleuthHystrixConcurrencyStrategy(Tracing tracing, SpanNamer spanNamer) {
@@ -83,8 +86,8 @@ public class SleuthHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy
HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);
}
catch (Exception e) {
log.error("Failed to register Sleuth Hystrix Concurrency Strategy", e);
catch (Exception ex) {
log.error("Failed to register Sleuth Hystrix Concurrency Strategy", ex);
}
}
@@ -92,10 +95,10 @@ public class SleuthHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy
HystrixMetricsPublisher metricsPublisher,
HystrixPropertiesStrategy propertiesStrategy) {
if (log.isDebugEnabled()) {
log.debug("Current Hystrix plugins configuration is [" + "concurrencyStrategy ["
+ this.delegate + "]," + "eventNotifier [" + eventNotifier + "],"
+ "metricPublisher [" + metricsPublisher + "]," + "propertiesStrategy ["
+ propertiesStrategy + "]," + "]");
log.debug("Current Hystrix plugins configuration is ["
+ "concurrencyStrategy [" + this.delegate + "]," + "eventNotifier ["
+ eventNotifier + "]," + "metricPublisher [" + metricsPublisher + "],"
+ "propertiesStrategy [" + propertiesStrategy + "]," + "]");
log.debug("Registering Sleuth Hystrix Concurrency Strategy.");
}
}
@@ -110,8 +113,8 @@ public class SleuthHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy
if (wrappedCallable instanceof TraceCallable) {
return wrappedCallable;
}
return new TraceCallable<>(this.tracing, this.spanNamer,
wrappedCallable, HYSTRIX_COMPONENT);
return new TraceCallable<>(this.tracing, this.spanNamer, wrappedCallable,
HYSTRIX_COMPONENT);
}
@Override
@@ -140,4 +143,5 @@ public class SleuthHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy
HystrixRequestVariableLifecycle<T> rv) {
return this.delegate.getRequestVariable(rv);
}
}

View File

@@ -23,24 +23,28 @@ import brave.Tracer;
import com.netflix.hystrix.HystrixCommand;
/**
* Abstraction over {@code HystrixCommand} that wraps command execution with Trace setting
*
* @see HystrixCommand
* @see Tracer
* Abstraction over {@code HystrixCommand} that wraps command execution with Trace setting.
*
* @param <R> - return type of Hystrix Command
* @author Tomasz Nurkiewicz, 4financeIT
* @author Marcin Grzejszczak
* @author Spencer Gibb
* @since 1.0.0
* @see HystrixCommand
* @see Tracer
*/
public abstract class TraceCommand<R> extends HystrixCommand<R> {
private static final String COMMAND_KEY = "commandKey";
private static final String COMMAND_GROUP_KEY = "commandGroup";
private static final String THREAD_POOL_KEY = "threadPoolKey";
private static final String FALLBACK_METHOD_NAME_KEY = "fallbackMethodName";
private final Tracer tracer;
private final AtomicReference<Span> span;
protected TraceCommand(Tracer tracer, Setter setter) {
@@ -59,10 +63,12 @@ public abstract class TraceCommand<R> extends HystrixCommand<R> {
Throwable throwable = null;
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) {
return doRun();
} catch (Throwable t) {
}
catch (Throwable t) {
throwable = t;
throw t;
} finally {
}
finally {
if (throwable == null) {
span.finish();
this.span.set(null);
@@ -73,12 +79,14 @@ public abstract class TraceCommand<R> extends HystrixCommand<R> {
public abstract R doRun() throws Exception;
@Override protected R getFallback() {
@Override
protected R getFallback() {
Span span = this.span.get();
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
span.tag(FALLBACK_METHOD_NAME_KEY, getFallbackMethodName());
return doGetFallback();
} finally {
}
finally {
span.finish();
this.span.set(null);
}
@@ -87,4 +95,5 @@ public abstract class TraceCommand<R> extends HystrixCommand<R> {
public R doGetFallback() {
return super.getFallback();
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.cloud.sleuth.instrument.messaging;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -29,15 +30,16 @@ import org.springframework.messaging.support.NativeMessageHeaderAccessor;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.StringUtils;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.springframework.messaging.support.NativeMessageHeaderAccessor.NATIVE_HEADERS;
/**
* This always sets native headers in defence of STOMP issues discussed <a href="https://github.com/spring-cloud/spring-cloud-sleuth/issues/716#issuecomment-337523705">here</a>
* This always sets native headers in defence of STOMP issues discussed <a href=
* "https://github.com/spring-cloud/spring-cloud-sleuth/issues/716#issuecomment-337523705">here</a>.
*
* @author Marcin Grzejszczak
*/
enum MessageHeaderPropagation
implements Propagation.Setter<MessageHeaderAccessor, String>,
Propagation.Getter<MessageHeaderAccessor, String> {
INSTANCE;
private static final Log log = LogFactory.getLog(MessageHeaderPropagation.class);
@@ -45,99 +47,24 @@ enum MessageHeaderPropagation
private static final Map<String, String> LEGACY_HEADER_MAPPING = new HashMap<>();
private static final String TRACE_ID_NAME = "X-B3-TraceId";
private static final String SPAN_ID_NAME = "X-B3-SpanId";
private static final String PARENT_SPAN_ID_NAME = "X-B3-ParentSpanId";
private static final String SAMPLED_NAME = "X-B3-Sampled";
private static final String FLAGS_NAME = "X-B3-Flags";
static {
LEGACY_HEADER_MAPPING.put(TRACE_ID_NAME, TraceMessageHeaders.TRACE_ID_NAME);
LEGACY_HEADER_MAPPING.put(SPAN_ID_NAME, TraceMessageHeaders.SPAN_ID_NAME);
LEGACY_HEADER_MAPPING.put(PARENT_SPAN_ID_NAME, TraceMessageHeaders.PARENT_ID_NAME);
LEGACY_HEADER_MAPPING.put(PARENT_SPAN_ID_NAME,
TraceMessageHeaders.PARENT_ID_NAME);
LEGACY_HEADER_MAPPING.put(SAMPLED_NAME, TraceMessageHeaders.SAMPLED_NAME);
LEGACY_HEADER_MAPPING.put(FLAGS_NAME, TraceMessageHeaders.SPAN_FLAGS_NAME);
}
@Override public void put(MessageHeaderAccessor accessor, String key, String value) {
try {
doPut(accessor, key, value);
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug("An exception happened when we tried to retrieve the [" + key + "] from message", e);
}
}
String legacyKey = LEGACY_HEADER_MAPPING.get(key);
if (legacyKey != null) {
doPut(accessor, legacyKey, value);
}
}
private void doPut(MessageHeaderAccessor accessor, String key, String value) {
accessor.setHeader(key, value);
if (accessor instanceof NativeMessageHeaderAccessor) {
NativeMessageHeaderAccessor nativeAccessor = (NativeMessageHeaderAccessor) accessor;
nativeAccessor.setNativeHeader(key, value);
}
else {
Object nativeHeaders = accessor.getHeader(NATIVE_HEADERS);
if (nativeHeaders == null) {
accessor.setHeader(NATIVE_HEADERS,
nativeHeaders = new LinkedMultiValueMap<>());
}
if (nativeHeaders instanceof Map<?, ?>) {
((Map) nativeHeaders).put(key, Collections.singletonList(value));
}
}
}
@Override public String get(MessageHeaderAccessor accessor, String key) {
try {
String value = doGet(accessor, key);
if (StringUtils.hasText(value)) {
return value;
}
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug("An exception happened when we tried to retrieve the [" + key + "] from message", e);
}
}
return legacyValue(accessor, key);
}
private String legacyValue(MessageHeaderAccessor accessor, String key) {
String legacyKey = LEGACY_HEADER_MAPPING.get(key);
if (legacyKey != null) {
return doGet(accessor, legacyKey);
}
return null;
}
private String doGet(MessageHeaderAccessor accessor, String key) {
if (accessor instanceof NativeMessageHeaderAccessor) {
NativeMessageHeaderAccessor nativeAccessor = (NativeMessageHeaderAccessor) accessor;
String result = nativeAccessor.getFirstNativeHeader(key);
if (result != null) {
return result;
}
} else {
Object nativeHeaders = accessor.getHeader(NATIVE_HEADERS);
if (nativeHeaders instanceof Map) {
Object result = ((Map) nativeHeaders).get(key);
if (result instanceof List && !((List) result).isEmpty()) {
return String.valueOf(((List) result).get(0));
}
}
}
Object result = accessor.getHeader(key);
if (result != null) {
if (result instanceof byte[]) {
return new String((byte[]) result, UTF_8);
}
return result.toString();
}
return null;
}
static Map<String, ?> propagationHeaders(Map<String, ?> headers,
List<String> propagationHeaders) {
Map<String, Object> headersToCopy = new HashMap<>();
@@ -156,8 +83,9 @@ enum MessageHeaderPropagation
if (accessor instanceof NativeMessageHeaderAccessor) {
NativeMessageHeaderAccessor nativeAccessor = (NativeMessageHeaderAccessor) accessor;
nativeAccessor.removeNativeHeader(keyToRemove);
} else {
Object nativeHeaders = accessor.getHeader(NATIVE_HEADERS);
}
else {
Object nativeHeaders = accessor.getHeader(NativeMessageHeaderAccessor.NATIVE_HEADERS);
if (nativeHeaders instanceof Map) {
((Map) nativeHeaders).remove(keyToRemove);
}
@@ -165,7 +93,96 @@ enum MessageHeaderPropagation
}
}
@Override public String toString() {
@Override
public void put(MessageHeaderAccessor accessor, String key, String value) {
try {
doPut(accessor, key, value);
}
catch (Exception ex) {
if (log.isDebugEnabled()) {
log.debug("An exception happened when we tried to retrieve the [" + key
+ "] from message", ex);
}
}
String legacyKey = LEGACY_HEADER_MAPPING.get(key);
if (legacyKey != null) {
doPut(accessor, legacyKey, value);
}
}
private void doPut(MessageHeaderAccessor accessor, String key, String value) {
accessor.setHeader(key, value);
if (accessor instanceof NativeMessageHeaderAccessor) {
NativeMessageHeaderAccessor nativeAccessor = (NativeMessageHeaderAccessor) accessor;
nativeAccessor.setNativeHeader(key, value);
}
else {
Object nativeHeaders = accessor.getHeader(NativeMessageHeaderAccessor.NATIVE_HEADERS);
if (nativeHeaders == null) {
accessor.setHeader(NativeMessageHeaderAccessor.NATIVE_HEADERS,
nativeHeaders = new LinkedMultiValueMap<>());
}
if (nativeHeaders instanceof Map<?, ?>) {
((Map) nativeHeaders).put(key, Collections.singletonList(value));
}
}
}
@Override
public String get(MessageHeaderAccessor accessor, String key) {
try {
String value = doGet(accessor, key);
if (StringUtils.hasText(value)) {
return value;
}
}
catch (Exception ex) {
if (log.isDebugEnabled()) {
log.debug("An exception happened when we tried to retrieve the [" + key
+ "] from message", ex);
}
}
return legacyValue(accessor, key);
}
private String legacyValue(MessageHeaderAccessor accessor, String key) {
String legacyKey = LEGACY_HEADER_MAPPING.get(key);
if (legacyKey != null) {
return doGet(accessor, legacyKey);
}
return null;
}
private String doGet(MessageHeaderAccessor accessor, String key) {
if (accessor instanceof NativeMessageHeaderAccessor) {
NativeMessageHeaderAccessor nativeAccessor = (NativeMessageHeaderAccessor) accessor;
String result = nativeAccessor.getFirstNativeHeader(key);
if (result != null) {
return result;
}
}
else {
Object nativeHeaders = accessor.getHeader(NativeMessageHeaderAccessor.NATIVE_HEADERS);
if (nativeHeaders instanceof Map) {
Object result = ((Map) nativeHeaders).get(key);
if (result instanceof List && !((List) result).isEmpty()) {
return String.valueOf(((List) result).get(0));
}
}
}
Object result = accessor.getHeader(key);
if (result != null) {
if (result instanceof byte[]) {
return new String((byte[]) result, StandardCharsets.UTF_8);
}
return result.toString();
}
return null;
}
@Override
public String toString() {
return "MessageHeaderPropagation{}";
}
}

View File

@@ -25,6 +25,8 @@ import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
/**
* Verifies if messaging property was enabled.
*
* @author Marcin Grzejszczak
* @since 2.0.0
*/
@@ -33,4 +35,5 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@Documented
@ConditionalOnProperty(value = "spring.sleuth.messaging.enabled", matchIfMissing = true)
@interface OnMessagingEnabled {
}

View File

@@ -19,6 +19,8 @@ package org.springframework.cloud.sleuth.instrument.messaging;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Properties for messaging
*
* @author Marcin Grzejszczak
* @since 2.0.0
*/
@@ -45,7 +47,13 @@ public class SleuthMessagingProperties {
this.messaging = messaging;
}
/**
* Properties for Spring Integration
*
* @author Marcin Grzejszczak
*/
public static class Integration {
/**
* An array of patterns against which channel names will be matched.
* @see org.springframework.integration.config.GlobalChannelInterceptor#patterns().
@@ -54,7 +62,7 @@ public class SleuthMessagingProperties {
private String[] patterns = new String[] { "!hystrixStreamOutput*", "*" };
/**
* Enable Spring Integration sleuth instrumentation
* Enable Spring Integration sleuth instrumentation.
*/
private boolean enabled;
@@ -73,15 +81,34 @@ public class SleuthMessagingProperties {
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
/**
* Generic messaging properties.
*
* @author Marcin Grzejszczak
*/
public static class Messaging {
/**
* Should messaging be turned on.
*/
private boolean enabled;
/**
* Rabbit related properties.
*/
private Rabbit rabbit = new Rabbit();
/**
* Kafka related properties.
*/
private Kafka kafka = new Kafka();
/**
* JMS related properties.
*/
private Jms jms = new Jms();
public boolean isEnabled() {
@@ -115,9 +142,11 @@ public class SleuthMessagingProperties {
public void setJms(Jms jms) {
this.jms = jms;
}
}
public static class Rabbit {
private boolean enabled;
private String remoteServiceName = "rabbitmq";
@@ -137,9 +166,11 @@ public class SleuthMessagingProperties {
public void setRemoteServiceName(String remoteServiceName) {
this.remoteServiceName = remoteServiceName;
}
}
public static class Kafka {
private boolean enabled;
private String remoteServiceName = "kafka";
@@ -159,9 +190,11 @@ public class SleuthMessagingProperties {
public void setRemoteServiceName(String remoteServiceName) {
this.remoteServiceName = remoteServiceName;
}
}
public static class Jms {
private boolean enabled;
private String remoteServiceName = "jms";
@@ -181,5 +214,7 @@ public class SleuthMessagingProperties {
public void setRemoteServiceName(String remoteServiceName) {
this.remoteServiceName = remoteServiceName;
}
}
}

View File

@@ -27,11 +27,18 @@ package org.springframework.cloud.sleuth.instrument.messaging;
public class TraceMessageHeaders {
public static final String SPAN_ID_NAME = "spanId";
public static final String SAMPLED_NAME = "spanSampled";
public static final String PARENT_ID_NAME = "spanParentSpanId";
public static final String TRACE_ID_NAME = "spanTraceId";
public static final String SPAN_NAME_NAME = "spanName";
public static final String SPAN_FLAGS_NAME = "spanFlags";
private TraceMessageHeaders() {}
private TraceMessageHeaders() {
}
}

View File

@@ -63,8 +63,7 @@ import org.springframework.util.ReflectionUtils;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that registers a tracing instrumentation of
* messaging components.
* Auto-configuration} that registers a tracing instrumentation of messaging components.
*
* @author Marcin Grzejszczak
* @since 2.0.0
@@ -80,21 +79,25 @@ public class TraceMessagingAutoConfiguration {
@ConditionalOnProperty(value = "spring.sleuth.messaging.rabbit.enabled", matchIfMissing = true)
@ConditionalOnClass(RabbitTemplate.class)
protected static class SleuthRabbitConfiguration {
@Bean
// for tests
@ConditionalOnMissingBean
static SleuthRabbitBeanPostProcessor sleuthRabbitBeanPostProcessor(
BeanFactory beanFactory) {
return new SleuthRabbitBeanPostProcessor(beanFactory);
}
@Bean
@ConditionalOnMissingBean
SpringRabbitTracing springRabbitTracing(Tracing tracing,
SleuthMessagingProperties properties) {
return SpringRabbitTracing.newBuilder(tracing)
.remoteServiceName(properties.getMessaging().getRabbit().getRemoteServiceName())
.remoteServiceName(
properties.getMessaging().getRabbit().getRemoteServiceName())
.build();
}
@Bean
// for tests
@ConditionalOnMissingBean
static SleuthRabbitBeanPostProcessor sleuthRabbitBeanPostProcessor(BeanFactory beanFactory) {
return new SleuthRabbitBeanPostProcessor(beanFactory);
}
}
@Configuration
@@ -105,9 +108,9 @@ public class TraceMessagingAutoConfiguration {
@Bean
@ConditionalOnMissingBean
KafkaTracing kafkaTracing(Tracing tracing, SleuthMessagingProperties properties) {
return KafkaTracing
.newBuilder(tracing)
.remoteServiceName(properties.getMessaging().getKafka().getRemoteServiceName())
return KafkaTracing.newBuilder(tracing)
.remoteServiceName(
properties.getMessaging().getKafka().getRemoteServiceName())
.build();
}
@@ -117,6 +120,7 @@ public class TraceMessagingAutoConfiguration {
SleuthKafkaAspect sleuthKafkaAspect(KafkaTracing kafkaTracing, Tracer tracer) {
return new SleuthKafkaAspect(kafkaTracing, tracer);
}
}
@Configuration
@@ -128,48 +132,56 @@ public class TraceMessagingAutoConfiguration {
@ConditionalOnMissingBean
JmsTracing jmsTracing(Tracing tracing, SleuthMessagingProperties properties) {
return JmsTracing.newBuilder(tracing)
.remoteServiceName(properties.getMessaging().getJms().getRemoteServiceName())
.remoteServiceName(
properties.getMessaging().getJms().getRemoteServiceName())
.build();
}
@Bean
// for tests
@ConditionalOnMissingBean
TracingConnectionFactoryBeanPostProcessor tracingConnectionFactoryBeanPostProcessor(BeanFactory beanFactory) {
TracingConnectionFactoryBeanPostProcessor tracingConnectionFactoryBeanPostProcessor(
BeanFactory beanFactory) {
return new TracingConnectionFactoryBeanPostProcessor(beanFactory);
}
/** Choose the tracing endpoint registry */
@Bean
TracingJmsListenerEndpointRegistry tracingJmsListenerEndpointRegistry(JmsTracing jmsTracing, CurrentTraceContext current) {
TracingJmsListenerEndpointRegistry tracingJmsListenerEndpointRegistry(
JmsTracing jmsTracing, CurrentTraceContext current) {
return new TracingJmsListenerEndpointRegistry(jmsTracing, current);
}
/** Setup the tracing endpoint registry */
@Bean
JmsListenerConfigurer configureTracing(TracingJmsListenerEndpointRegistry registry) {
JmsListenerConfigurer configureTracing(
TracingJmsListenerEndpointRegistry registry) {
return registrar -> registrar.setEndpointRegistry(registry);
}
}
}
class SleuthRabbitBeanPostProcessor implements BeanPostProcessor {
private final BeanFactory beanFactory;
private SpringRabbitTracing tracing;
SleuthRabbitBeanPostProcessor(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override public Object postProcessBeforeInitialization(Object bean, String beanName)
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof RabbitTemplate) {
return rabbitTracing()
.decorateRabbitTemplate((RabbitTemplate) bean);
} else if (bean instanceof SimpleRabbitListenerContainerFactory) {
return rabbitTracing()
.decorateSimpleRabbitListenerContainerFactory((SimpleRabbitListenerContainerFactory) bean);
return rabbitTracing().decorateRabbitTemplate((RabbitTemplate) bean);
}
else if (bean instanceof SimpleRabbitListenerContainerFactory) {
return rabbitTracing().decorateSimpleRabbitListenerContainerFactory(
(SimpleRabbitListenerContainerFactory) bean);
}
return bean;
}
@@ -180,31 +192,35 @@ class SleuthRabbitBeanPostProcessor implements BeanPostProcessor {
}
return this.tracing;
}
}
@Aspect
class SleuthKafkaAspect {
private static final Log log = LogFactory.getLog(SleuthKafkaAspect.class);
final Field recordMessageConverter;
private final KafkaTracing kafkaTracing;
private final Tracer tracer;
final Field recordMessageConverter;
SleuthKafkaAspect(KafkaTracing kafkaTracing, Tracer tracer) {
this.kafkaTracing = kafkaTracing;
this.tracer = tracer;
this.recordMessageConverter = ReflectionUtils.findField(MessagingMessageListenerAdapter.class, "recordMessageConverter");
this.recordMessageConverter = ReflectionUtils.findField(
MessagingMessageListenerAdapter.class, "recordMessageConverter");
}
@Pointcut("execution(public * org.springframework.kafka.core.ProducerFactory.createProducer(..))")
private void anyProducerFactory() { } // NOSONAR
private void anyProducerFactory() {
} // NOSONAR
@Pointcut("execution(public * org.springframework.kafka.core.ConsumerFactory.createConsumer(..))")
private void anyConsumerFactory() { } // NOSONAR
private void anyConsumerFactory() {
} // NOSONAR
@Pointcut("execution(public * org.springframework.kafka.config.KafkaListenerContainerFactory.createListenerContainer(..))")
private void anyCreateListenerContainer() { } // NOSONAR
private void anyCreateListenerContainer() {
} // NOSONAR
@Around("anyProducerFactory()")
public Object wrapProducerFactory(ProceedingJoinPoint pjp) throws Throwable {
@@ -219,23 +235,28 @@ class SleuthKafkaAspect {
}
@Around("anyCreateListenerContainer()")
public Object wrapListenerContainerCreation(ProceedingJoinPoint pjp) throws Throwable {
public Object wrapListenerContainerCreation(ProceedingJoinPoint pjp)
throws Throwable {
MessageListenerContainer listener = (MessageListenerContainer) pjp.proceed();
if (listener instanceof AbstractMessageListenerContainer) {
AbstractMessageListenerContainer container = (AbstractMessageListenerContainer) listener;
Object someMessageListener = container.getContainerProperties().getMessageListener();
Object someMessageListener = container.getContainerProperties()
.getMessageListener();
if (someMessageListener == null) {
if (log.isDebugEnabled()) {
log.debug("No message listener to wrap. Proceeding");
}
} else if (someMessageListener instanceof MessageListener) {
}
else if (someMessageListener instanceof MessageListener) {
container.setupMessageListener(createProxy(someMessageListener));
} else {
}
else {
if (log.isDebugEnabled()) {
log.debug("ATM we don't support Batch message listeners");
}
}
} else {
}
else {
if (log.isDebugEnabled()) {
log.debug("Can't wrap this listener. Proceeding");
}
@@ -247,17 +268,22 @@ class SleuthKafkaAspect {
Object createProxy(Object bean) {
ProxyFactoryBean factory = new ProxyFactoryBean();
factory.setProxyTargetClass(true);
factory.addAdvice(new MessageListenerMethodInterceptor(this.kafkaTracing, this.tracer));
factory.addAdvice(
new MessageListenerMethodInterceptor(this.kafkaTracing, this.tracer));
factory.setTarget(bean);
return factory.getObject();
}
}
class MessageListenerMethodInterceptor<T extends MessageListener> implements MethodInterceptor {
class MessageListenerMethodInterceptor<T extends MessageListener>
implements MethodInterceptor {
private static final Log log = LogFactory.getLog(MessageListenerMethodInterceptor.class);
private static final Log log = LogFactory
.getLog(MessageListenerMethodInterceptor.class);
private final KafkaTracing kafkaTracing;
private final Tracer tracer;
MessageListenerMethodInterceptor(KafkaTracing kafkaTracing, Tracer tracer) {
@@ -265,29 +291,35 @@ class MessageListenerMethodInterceptor<T extends MessageListener> implements Met
this.tracer = tracer;
}
@Override public Object invoke(MethodInvocation invocation)
throws Throwable {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
if (!"onMessage".equals(invocation.getMethod().getName())) {
return invocation.proceed();
}
Object[] arguments = invocation.getArguments();
Optional<Object> record = Arrays.stream(arguments).filter(o -> o instanceof ConsumerRecord).findFirst();
Optional<Object> record = Arrays.stream(arguments)
.filter(o -> o instanceof ConsumerRecord).findFirst();
if (!record.isPresent()) {
return invocation.proceed();
}
if (log.isDebugEnabled()) {
log.debug("Wrapping onMessage call");
}
Span span = this.kafkaTracing.nextSpan((ConsumerRecord<?, ?>) record.get()).name("on-message").start();
Span span = this.kafkaTracing.nextSpan((ConsumerRecord<?, ?>) record.get())
.name("on-message").start();
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
return invocation.proceed();
} catch (RuntimeException | Error e) {
}
catch (RuntimeException | Error e) {
String message = e.getMessage();
if (message == null) message = e.getClass().getSimpleName();
if (message == null)
message = e.getClass().getSimpleName();
span.tag("error", message);
throw e;
} finally {
}
finally {
span.finish();
}
}
}

View File

@@ -38,7 +38,6 @@ import org.springframework.messaging.support.MessageHeaderAccessor;
*
* @author Spencer Gibb
* @since 1.0.0
*
* @see TracingChannelInterceptor
*/
@Configuration
@@ -52,9 +51,9 @@ public class TraceSpringIntegrationAutoConfiguration {
@Bean
public GlobalChannelInterceptorWrapper tracingGlobalChannelInterceptorWrapper(
TracingChannelInterceptor interceptor,
SleuthMessagingProperties properties) {
GlobalChannelInterceptorWrapper wrapper = new GlobalChannelInterceptorWrapper(interceptor);
TracingChannelInterceptor interceptor, SleuthMessagingProperties properties) {
GlobalChannelInterceptorWrapper wrapper = new GlobalChannelInterceptorWrapper(
interceptor);
wrapper.setPatterns(properties.getIntegration().getPatterns());
return wrapper;
}
@@ -63,7 +62,8 @@ public class TraceSpringIntegrationAutoConfiguration {
TracingChannelInterceptor traceChannelInterceptor(Tracing tracing,
Propagation.Setter<MessageHeaderAccessor, String> traceMessagePropagationSetter,
Propagation.Getter<MessageHeaderAccessor, String> traceMessagePropagationGetter) {
return new TracingChannelInterceptor(tracing, traceMessagePropagationSetter, traceMessagePropagationGetter);
return new TracingChannelInterceptor(tracing, traceMessagePropagationSetter,
traceMessagePropagationGetter);
}
@Bean

View File

@@ -45,39 +45,41 @@ import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.util.ClassUtils;
/**
* This starts and propagates {@link Span.Kind#PRODUCER} span for each message sent (via native
* headers. It also extracts or creates a {@link Span.Kind#CONSUMER} span for each message
* received. This span is injected onto each message so it becomes the parent when a handler later
* calls {@link MessageHandler#handleMessage(Message)}, or a another processing library calls {@link #nextSpan(Message)}.
* This starts and propagates {@link Span.Kind#PRODUCER} span for each message sent (via
* native headers. It also extracts or creates a {@link Span.Kind#CONSUMER} span for each
* message received. This span is injected onto each message so it becomes the parent when
* a handler later calls {@link MessageHandler#handleMessage(Message)}, or a another
* processing library calls {@link #nextSpan(Message)}.
* <p>
* <p>This implementation uses {@link ThreadLocalSpan} to propagate context between callbacks. This
* is an alternative to {@code ThreadStatePropagationChannelInterceptor} which is less sensitive
* to message manipulation by other interceptors.
* <p>
* This implementation uses {@link ThreadLocalSpan} to propagate context between
* callbacks. This is an alternative to {@code ThreadStatePropagationChannelInterceptor}
* which is less sensitive to message manipulation by other interceptors.
*/
public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
implements ExecutorChannelInterceptor {
private static final Log log = LogFactory.getLog(TracingChannelInterceptor.class);
/**
* Using the literal "broker" until we come up with a better solution.
*
* <p>If the message originated from a binder (consumer binding), there will be different
* headers present (e.g. "KafkaHeaders.RECEIVED_TOPIC" Vs. "AmqpHeaders.CONSUMER_QUEUE"
* (unless the application removes them before sending). These don't represent the broker,
* rather a queue, and in any case the heuristics are not great. At least we might be able
* to tell if this is rabbit or not (ex how spring-rabbit works). We need to think this
* through before making an api, possibly experimenting.
* <p>
* If the message originated from a binder (consumer binding), there will be different
* headers present (e.g. "KafkaHeaders.RECEIVED_TOPIC" Vs.
* "AmqpHeaders.CONSUMER_QUEUE" (unless the application removes them before sending).
* These don't represent the broker, rather a queue, and in any case the heuristics
* are not great. At least we might be able to tell if this is rabbit or not (ex how
* spring-rabbit works). We need to think this through before making an api, possibly
* experimenting.
*
* <p>If the app is outbound only (producer), there's no indication of what type the
* <p>
* If the app is outbound only (producer), there's no indication of what type the
* destination broker is. This may hint at a non-manual solution being overwriting the
* remoteServiceName later, similar to how servlet instrumentation lazy set "http.route".
* remoteServiceName later, similar to how servlet instrumentation lazy set
* "http.route".
*/
private static final String REMOTE_SERVICE_NAME = "broker";
public static TracingChannelInterceptor create(Tracing tracing) {
return new TracingChannelInterceptor(tracing);
}
final Tracing tracing;
final Tracer tracer;
final ThreadLocalSpan threadLocalSpan;
@@ -88,31 +90,35 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
@Autowired
TracingChannelInterceptor(Tracing tracing) {
this(tracing, MessageHeaderPropagation.INSTANCE, MessageHeaderPropagation.INSTANCE);
this(tracing, MessageHeaderPropagation.INSTANCE,
MessageHeaderPropagation.INSTANCE);
}
TracingChannelInterceptor(Tracing tracing, Propagation.Setter<MessageHeaderAccessor, String> setter,
TracingChannelInterceptor(Tracing tracing,
Propagation.Setter<MessageHeaderAccessor, String> setter,
Propagation.Getter<MessageHeaderAccessor, String> getter) {
this.tracing = tracing;
this.tracer = tracing.tracer();
this.threadLocalSpan = ThreadLocalSpan.create(this.tracer);
this.injector = tracing.propagation()
.injector(setter);
this.extractor = tracing.propagation()
.extractor(getter);
this.injector = tracing.propagation().injector(setter);
this.extractor = tracing.propagation().extractor(getter);
this.integrationObjectSupportPresent = ClassUtils.isPresent(
"org.springframework.integration.context.IntegrationObjectSupport",
null);
"org.springframework.integration.context.IntegrationObjectSupport", null);
this.hasDirectChannelClass = ClassUtils
.isPresent("org.springframework.integration.channel.DirectChannel", null);
}
public static TracingChannelInterceptor create(Tracing tracing) {
return new TracingChannelInterceptor(tracing);
}
/**
* Use this to create a span for processing the given message. Note: the result has no name and is
* not started.
* Use this to create a span for processing the given message. Note: the result has no
* name and is not started.
* <p>
* <p>This creates a child from identifiers extracted from the message headers, or a new span if
* one couldn't be extracted.
* <p>
* This creates a child from identifiers extracted from the message headers, or a new
* span if one couldn't be extracted.
*/
public Span nextSpan(Message<?> message) {
MessageHeaderAccessor headers = mutableHeaderAccessor(message);
@@ -131,7 +137,8 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
/**
* Starts and propagates {@link Span.Kind#PRODUCER} span for each message sent.
*/
@Override public Message<?> preSend(Message<?> message, MessageChannel channel) {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
if (emptyMessage(message)) {
return message;
}
@@ -139,8 +146,8 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
MessageHeaderAccessor headers = mutableHeaderAccessor(retrievedMessage);
TraceContextOrSamplingFlags extracted = this.extractor.extract(headers);
Span span = this.threadLocalSpan.next(extracted);
MessageHeaderPropagation
.removeAnyTraceHeaders(headers, this.tracing.propagation().keys());
MessageHeaderPropagation.removeAnyTraceHeaders(headers,
this.tracing.propagation().keys());
this.injector.inject(span.context(), headers);
if (!span.isNoop()) {
span.kind(Span.Kind.PRODUCER).name("send").start();
@@ -157,30 +164,36 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
return outputMessage;
}
private Message<?> outputMessage(Message<?> originalMessage, Message<?> retrievedMessage, MessageHeaderAccessor additionalHeaders) {
MessageHeaderAccessor headers = MessageHeaderAccessor.getMutableAccessor(originalMessage);
private Message<?> outputMessage(Message<?> originalMessage,
Message<?> retrievedMessage, MessageHeaderAccessor additionalHeaders) {
MessageHeaderAccessor headers = MessageHeaderAccessor
.getMutableAccessor(originalMessage);
if (originalMessage.getPayload() instanceof MessagingException) {
headers.copyHeaders(MessageHeaderPropagation.propagationHeaders(additionalHeaders.getMessageHeaders(),
headers.copyHeaders(MessageHeaderPropagation.propagationHeaders(
additionalHeaders.getMessageHeaders(),
this.tracing.propagation().keys()));
return new ErrorMessage((MessagingException) originalMessage.getPayload(),
isWebSockets(headers) ? headers.getMessageHeaders() : new MessageHeaders(headers.getMessageHeaders()));
isWebSockets(headers) ? headers.getMessageHeaders()
: new MessageHeaders(headers.getMessageHeaders()));
}
headers.copyHeaders(additionalHeaders.getMessageHeaders());
return new GenericMessage<>(retrievedMessage.getPayload(),
isWebSockets(headers) ? headers.getMessageHeaders() : new MessageHeaders(headers.getMessageHeaders()));
isWebSockets(headers) ? headers.getMessageHeaders()
: new MessageHeaders(headers.getMessageHeaders()));
}
private boolean isWebSockets(MessageHeaderAccessor headerAccessor) {
return headerAccessor.getMessageHeaders().containsKey("stompCommand") ||
headerAccessor.getMessageHeaders().containsKey("simpMessageType");
return headerAccessor.getMessageHeaders().containsKey("stompCommand")
|| headerAccessor.getMessageHeaders().containsKey("simpMessageType");
}
private boolean isDirectChannel(MessageChannel channel) {
return this.hasDirectChannelClass &&
DirectChannel.class.isAssignableFrom(AopUtils.getTargetClass(channel));
return this.hasDirectChannelClass
&& DirectChannel.class.isAssignableFrom(AopUtils.getTargetClass(channel));
}
@Override public void afterSendCompletion(Message<?> message, MessageChannel channel,
@Override
public void afterSendCompletion(Message<?> message, MessageChannel channel,
boolean sent, Exception ex) {
if (emptyMessage(message)) {
return;
@@ -189,24 +202,26 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
afterMessageHandled(message, channel, null, ex);
}
if (log.isDebugEnabled()) {
log.debug("Will finish the current span after completion " + this.tracer.currentSpan());
log.debug("Will finish the current span after completion "
+ this.tracer.currentSpan());
}
finishSpan(ex);
}
/**
* This starts a consumer span as a child of the incoming message or the current trace context,
* placing it in scope until the receive completes.
* This starts a consumer span as a child of the incoming message or the current trace
* context, placing it in scope until the receive completes.
*/
@Override public Message<?> postReceive(Message<?> message, MessageChannel channel) {
@Override
public Message<?> postReceive(Message<?> message, MessageChannel channel) {
if (emptyMessage(message)) {
return message;
}
MessageHeaderAccessor headers = mutableHeaderAccessor(message);
TraceContextOrSamplingFlags extracted = this.extractor.extract(headers);
Span span = this.threadLocalSpan.next(extracted);
MessageHeaderPropagation
.removeAnyTraceHeaders(headers, this.tracing.propagation().keys());
MessageHeaderPropagation.removeAnyTraceHeaders(headers,
this.tracing.propagation().keys());
this.injector.inject(span.context(), headers);
if (!span.isNoop()) {
span.kind(Span.Kind.CONSUMER).name("receive").start();
@@ -227,16 +242,18 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
return;
}
if (log.isDebugEnabled()) {
log.debug("Will finish the current span after receive completion " + this.tracer.currentSpan());
log.debug("Will finish the current span after receive completion "
+ this.tracer.currentSpan());
}
finishSpan(ex);
}
/**
* This starts a consumer span as a child of the incoming message or the current trace context.
* It then creates a span for the handler, placing it in scope.
* This starts a consumer span as a child of the incoming message or the current trace
* context. It then creates a span for the handler, placing it in scope.
*/
@Override public Message<?> beforeHandle(Message<?> message, MessageChannel channel,
@Override
public Message<?> beforeHandle(Message<?> message, MessageChannel channel,
MessageHandler handler) {
if (emptyMessage(message)) {
return message;
@@ -252,29 +269,34 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
consumerSpan.finish();
}
// create and scope a span for the message processor
this.threadLocalSpan.next(TraceContextOrSamplingFlags.create(consumerSpan.context()))
this.threadLocalSpan
.next(TraceContextOrSamplingFlags.create(consumerSpan.context()))
.name("handle").start();
// remove any trace headers, but don't re-inject as we are synchronously processing the
// remove any trace headers, but don't re-inject as we are synchronously
// processing the
// message and can rely on scoping to access this span later.
MessageHeaderPropagation
.removeAnyTraceHeaders(headers, this.tracing.propagation().keys());
MessageHeaderPropagation.removeAnyTraceHeaders(headers,
this.tracing.propagation().keys());
if (log.isDebugEnabled()) {
log.debug("Created a new span in before handle" + consumerSpan);
}
if (message instanceof ErrorMessage) {
return new ErrorMessage((Throwable) message.getPayload(), headers.getMessageHeaders());
return new ErrorMessage((Throwable) message.getPayload(),
headers.getMessageHeaders());
}
headers.setImmutable();
return new GenericMessage<>(message.getPayload(), headers.getMessageHeaders());
}
@Override public void afterMessageHandled(Message<?> message, MessageChannel channel,
@Override
public void afterMessageHandled(Message<?> message, MessageChannel channel,
MessageHandler handler, Exception ex) {
if (emptyMessage(message)) {
return;
}
if (log.isDebugEnabled()) {
log.debug("Will finish the current span after message handled " + this.tracer.currentSpan());
log.debug("Will finish the current span after message handled "
+ this.tracer.currentSpan());
}
finishSpan(ex);
}
@@ -340,4 +362,5 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
private boolean emptyMessage(Message<?> message) {
return message == null;
}
}

View File

@@ -17,6 +17,7 @@
package org.springframework.cloud.sleuth.instrument.messaging;
import java.lang.reflect.Field;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSContext;
@@ -48,6 +49,7 @@ import org.springframework.lang.Nullable;
/**
* {@link BeanPostProcessor} wrapping around JMS {@link ConnectionFactory}
*
* @author Adrian Cole
* @since 2.1.0
*/
@@ -59,10 +61,13 @@ class TracingConnectionFactoryBeanPostProcessor implements BeanPostProcessor {
this.beanFactory = beanFactory;
}
@Override public Object postProcessAfterInitialization(Object bean, String beanName)
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
// Wrap the caching connection factories instead of its target, because it catches callbacks
// such as ExceptionListener. If we don't wrap, cached callbacks like this won't be traced.
// Wrap the caching connection factories instead of its target, because it catches
// callbacks
// such as ExceptionListener. If we don't wrap, cached callbacks like this won't
// be traced.
if (bean instanceof CachingConnectionFactory) {
return new LazyConnectionFactory(this.beanFactory,
(CachingConnectionFactory) bean);
@@ -76,7 +81,8 @@ class TracingConnectionFactoryBeanPostProcessor implements BeanPostProcessor {
}
return bean;
}
// We check XA first in case the ConnectionFactory also implements XAConnectionFactory
// We check XA first in case the ConnectionFactory also implements
// XAConnectionFactory
if (bean instanceof XAConnectionFactory) {
return new LazyXAConnectionFactory(this.beanFactory,
(XAConnectionFactory) bean);
@@ -86,13 +92,17 @@ class TracingConnectionFactoryBeanPostProcessor implements BeanPostProcessor {
}
return bean;
}
}
class LazyXAConnectionFactory implements XAConnectionFactory {
private final BeanFactory beanFactory;
private final XAConnectionFactory delegate;
private JmsTracing jmsTracing;
private XAConnectionFactory wrappedDelegate;
LazyXAConnectionFactory(BeanFactory beanFactory, XAConnectionFactory delegate) {
@@ -100,20 +110,23 @@ class LazyXAConnectionFactory implements XAConnectionFactory {
this.delegate = delegate;
}
@Override public XAConnection createXAConnection() throws JMSException {
@Override
public XAConnection createXAConnection() throws JMSException {
return wrappedDelegate().createXAConnection();
}
@Override public XAConnection createXAConnection(String s, String s1)
throws JMSException {
@Override
public XAConnection createXAConnection(String s, String s1) throws JMSException {
return wrappedDelegate().createXAConnection(s, s1);
}
@Override public XAJMSContext createXAContext() {
@Override
public XAJMSContext createXAContext() {
return wrappedDelegate().createXAContext();
}
@Override public XAJMSContext createXAContext(String s, String s1) {
@Override
public XAJMSContext createXAContext(String s, String s1) {
return wrappedDelegate().createXAContext(s, s1);
}
@@ -130,13 +143,17 @@ class LazyXAConnectionFactory implements XAConnectionFactory {
}
return this.wrappedDelegate = jmsTracing().xaConnectionFactory(this.delegate);
}
}
class LazyConnectionFactory implements ConnectionFactory {
private final BeanFactory beanFactory;
private final ConnectionFactory delegate;
private JmsTracing jmsTracing;
private ConnectionFactory wrappedDelegate;
LazyConnectionFactory(BeanFactory beanFactory, ConnectionFactory delegate) {
@@ -144,28 +161,33 @@ class LazyConnectionFactory implements ConnectionFactory {
this.delegate = delegate;
}
@Override public Connection createConnection() throws JMSException {
@Override
public Connection createConnection() throws JMSException {
return wrappedDelegate().createConnection();
}
@Override public Connection createConnection(String s, String s1)
throws JMSException {
@Override
public Connection createConnection(String s, String s1) throws JMSException {
return wrappedDelegate().createConnection(s, s1);
}
@Override public JMSContext createContext() {
@Override
public JMSContext createContext() {
return wrappedDelegate().createContext();
}
@Override public JMSContext createContext(String s, String s1) {
@Override
public JMSContext createContext(String s, String s1) {
return wrappedDelegate().createContext(s, s1);
}
@Override public JMSContext createContext(String s, String s1, int i) {
@Override
public JMSContext createContext(String s, String s1, int i) {
return wrappedDelegate().createContext(s, s1, i);
}
@Override public JMSContext createContext(int i) {
@Override
public JMSContext createContext(int i) {
return wrappedDelegate().createContext(i);
}
@@ -182,12 +204,15 @@ class LazyConnectionFactory implements ConnectionFactory {
}
return this.wrappedDelegate = jmsTracing().connectionFactory(this.delegate);
}
}
class LazyMessageListener implements MessageListener {
private final BeanFactory beanFactory;
private final MessageListener delegate;
private JmsTracing jmsTracing;
LazyMessageListener(BeanFactory beanFactory, MessageListener delegate) {
@@ -195,7 +220,8 @@ class LazyMessageListener implements MessageListener {
this.delegate = delegate;
}
@Override public void onMessage(Message message) {
@Override
public void onMessage(Message message) {
wrappedDelegate().onMessage(message);
}
@@ -207,19 +233,26 @@ class LazyMessageListener implements MessageListener {
}
private MessageListener wrappedDelegate() {
// Adds a consumer span as we have no visibility into JCA's implementation of messaging
// Adds a consumer span as we have no visibility into JCA's implementation of
// messaging
return jmsTracing().messageListener(this.delegate, true);
}
}
/**
* This ensures listeners end up continuing the trace from {@link MessageConsumer#receive()}
* This ensures listeners end up continuing the trace from
* {@link MessageConsumer#receive()}
*/
class TracingJmsListenerEndpointRegistry extends JmsListenerEndpointRegistry {
final JmsTracing jmsTracing;
final CurrentTraceContext current;
// Not all state can be copied without using reflection
final Field messageHandlerMethodFactoryField;
final Field embeddedValueResolverField;
TracingJmsListenerEndpointRegistry(JmsTracing jmsTracing,
@@ -230,7 +263,25 @@ class TracingJmsListenerEndpointRegistry extends JmsListenerEndpointRegistry {
this.embeddedValueResolverField = tryField("embeddedValueResolver");
}
@Override public void registerListenerContainer(JmsListenerEndpoint endpoint,
@Nullable
static Field tryField(String name) {
try {
Field field = MethodJmsListenerEndpoint.class.getDeclaredField(name);
field.setAccessible(true);
return field;
}
catch (NoSuchFieldException e) {
return null;
}
}
@Nullable
static <T> T get(Object object, Field field) throws IllegalAccessException {
return (T) field.get(object);
}
@Override
public void registerListenerContainer(JmsListenerEndpoint endpoint,
JmsListenerContainerFactory<?> factory, boolean startImmediately) {
if (endpoint instanceof MethodJmsListenerEndpoint) {
endpoint = trace((MethodJmsListenerEndpoint) endpoint);
@@ -242,7 +293,8 @@ class TracingJmsListenerEndpointRegistry extends JmsListenerEndpointRegistry {
}
/**
* This wraps the {@link SimpleJmsListenerEndpoint#getMessageListener()} delegate in a new span.
* This wraps the {@link SimpleJmsListenerEndpoint#getMessageListener()} delegate in a
* new span.
*/
SimpleJmsListenerEndpoint trace(SimpleJmsListenerEndpoint source) {
MessageListener delegate = source.getMessageListener();
@@ -253,14 +305,16 @@ class TracingJmsListenerEndpointRegistry extends JmsListenerEndpointRegistry {
}
/**
* It would be better to trace by wrapping, but {@link MethodJmsListenerEndpoint#createMessageListener(MessageListenerContainer)},
* is protected so we can't call it from outside code. In other words, a forwarding pattern can't
* be used. Instead, we copy state from the input.
* It would be better to trace by wrapping, but
* {@link MethodJmsListenerEndpoint#createMessageListener(MessageListenerContainer)},
* is protected so we can't call it from outside code. In other words, a forwarding
* pattern can't be used. Instead, we copy state from the input.
* <p>
* NOTE: As {@linkplain MethodJmsListenerEndpoint} is neither final, nor effectively final. For
* this reason we can't ensure copying will get all state. For example, a subtype could hold state
* we aren't aware of, or change behavior. We can consider checking that input is not a subtype,
* and most conservatively leaving unknown subtypes untraced.
* NOTE: As {@linkplain MethodJmsListenerEndpoint} is neither final, nor effectively
* final. For this reason we can't ensure copying will get all state. For example, a
* subtype could hold state we aren't aware of, or change behavior. We can consider
* checking that input is not a subtype, and most conservatively leaving unknown
* subtypes untraced.
*/
MethodJmsListenerEndpoint trace(MethodJmsListenerEndpoint source) {
// Skip out rather than incompletely copying the source
@@ -269,9 +323,11 @@ class TracingJmsListenerEndpointRegistry extends JmsListenerEndpointRegistry {
return source;
}
// We want the stock implementation, except we want to wrap the message listener in a new span
// We want the stock implementation, except we want to wrap the message listener
// in a new span
MethodJmsListenerEndpoint dest = new MethodJmsListenerEndpoint() {
@Override protected MessagingMessageListenerAdapter createMessageListenerInstance() {
@Override
protected MessagingMessageListenerAdapter createMessageListenerInstance() {
return new TracingMessagingMessageListenerAdapter(
TracingJmsListenerEndpointRegistry.this.jmsTracing,
TracingJmsListenerEndpointRegistry.this.current);
@@ -301,20 +357,6 @@ class TracingJmsListenerEndpointRegistry extends JmsListenerEndpointRegistry {
return dest;
}
@Nullable static Field tryField(String name) {
try {
Field field = MethodJmsListenerEndpoint.class.getDeclaredField(name);
field.setAccessible(true);
return field;
}
catch (NoSuchFieldException e) {
return null;
}
}
@Nullable static <T> T get(Object object, Field field) throws IllegalAccessException {
return (T) field.get(object);
}
}
/**
@@ -324,6 +366,7 @@ final class TracingMessagingMessageListenerAdapter
extends MessagingMessageListenerAdapter {
final JmsTracing jmsTracing;
final CurrentTraceContext current;
TracingMessagingMessageListenerAdapter(JmsTracing jmsTracing,
@@ -332,8 +375,8 @@ final class TracingMessagingMessageListenerAdapter
this.current = current;
}
@Override public void onMessage(Message message, Session session)
throws JMSException {
@Override
public void onMessage(Message message, Session session) throws JMSException {
Span span = this.jmsTracing.nextSpan(message).name("on-message").start();
try (CurrentTraceContext.Scope ws = this.current.newScope(span.context())) {
super.onMessage(message, session);
@@ -346,4 +389,5 @@ final class TracingMessagingMessageListenerAdapter
span.finish();
}
}
}

View File

@@ -35,7 +35,6 @@ import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
*
* @author Dave Syer
* @since 1.0.0
*
* @see AbstractWebSocketMessageBrokerConfigurer
*/
@Configuration
@@ -55,7 +54,8 @@ public class TraceWebSocketAutoConfiguration
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.configureBrokerChannel().setInterceptors(TracingChannelInterceptor.create(this.tracing));
registry.configureBrokerChannel()
.setInterceptors(TracingChannelInterceptor.create(this.tracing));
}
@Override
@@ -67,4 +67,5 @@ public class TraceWebSocketAutoConfiguration
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.setInterceptors(TracingChannelInterceptor.create(this.tracing));
}
}

View File

@@ -28,15 +28,15 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration}
* to enable tracing via Opentracing.
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} to enable tracing via Opentracing.
*
* @author Spencer Gibb
* @author Marcin Grzejszczak
* @since 2.0.0
*/
@Configuration
@ConditionalOnProperty(value="spring.sleuth.opentracing.enabled", matchIfMissing=true)
@ConditionalOnProperty(value = "spring.sleuth.opentracing.enabled", matchIfMissing = true)
@ConditionalOnBean(Tracing.class)
@ConditionalOnClass(Tracer.class)
@EnableConfigurationProperties(SleuthOpentracingProperties.class)
@@ -48,4 +48,5 @@ public class OpentracingAutoConfiguration {
Tracer sleuthOpenTracing(brave.Tracing braveTracing) {
return BraveTracer.create(braveTracing);
}
}

View File

@@ -19,8 +19,9 @@ package org.springframework.cloud.sleuth.instrument.opentracing;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Sleuth Opentracing settings
* Sleuth Opentracing settings.
*
* @author Marcin Grzejszczak
* @since 2.0.0
*/
@ConfigurationProperties("spring.sleuth.opentracing")

View File

@@ -23,8 +23,9 @@ import org.reactivestreams.Subscription;
import reactor.util.context.Context;
/**
* A lazy representation of the {@link SpanSubscription}
* A lazy representation of the {@link SpanSubscription}.
*
* @param - type of what subscription returns
* @author Marcin Grzejszczak
* @since 2.0.0
*/
@@ -36,32 +37,39 @@ final class LazySpanSubscriber<T> extends AtomicBoolean implements SpanSubscript
this.supplier = supplier;
}
@Override public void onSubscribe(Subscription subscription) {
@Override
public void onSubscribe(Subscription subscription) {
this.supplier.get().onSubscribe(subscription);
}
@Override public void request(long n) {
@Override
public void request(long n) {
this.supplier.get().request(n);
}
@Override public void cancel() {
@Override
public void cancel() {
this.supplier.get().cancel();
}
@Override public void onNext(T o) {
@Override
public void onNext(T o) {
this.supplier.get().onNext(o);
}
@Override public void onError(Throwable throwable) {
@Override
public void onError(Throwable throwable) {
this.supplier.get().onError(throwable);
}
@Override public void onComplete() {
@Override
public void onComplete() {
this.supplier.get().onComplete();
}
@Override public Context currentContext() {
@Override
public Context currentContext() {
return this.supplier.get().currentContext();
}
}
}

View File

@@ -16,6 +16,8 @@
package org.springframework.cloud.sleuth.instrument.reactor;
import java.util.function.Function;
import brave.Tracing;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -29,10 +31,8 @@ import reactor.core.publisher.GroupedFlux;
import reactor.core.publisher.Operators;
import reactor.util.context.Context;
import java.util.function.Function;
/**
* Reactive Span pointcuts factories
* Reactive Span pointcuts factories.
*
* @author Stephane Maldini
* @since 2.0.0
@@ -41,17 +41,18 @@ public abstract class ReactorSleuth {
private static final Log log = LogFactory.getLog(ReactorSleuth.class);
private ReactorSleuth() {
}
/**
* Return a span operator pointcut given a {@link BeanFactory}. This can be used in reactor
* via {@link reactor.core.publisher.Flux#transform(Function)}, {@link
* reactor.core.publisher.Mono#transform(Function)}, {@link
* reactor.core.publisher.Hooks#onEachOperator(Function)} or {@link
* reactor.core.publisher.Hooks#onLastOperator(Function)}.
*
* Return a span operator pointcut given a {@link BeanFactory}. This can be used in
* reactor via {@link reactor.core.publisher.Flux#transform(Function)},
* {@link reactor.core.publisher.Mono#transform(Function)},
* {@link reactor.core.publisher.Hooks#onEachOperator(Function)} or
* {@link reactor.core.publisher.Hooks#onLastOperator(Function)}.
* @deprecated use {@link ReactorSleuth#scopePassingSpanOperator} instead
* @param beanFactory
* @param beanFactory - {@link BeanFactory}
* @param <T> an arbitrary type that is left unchanged by the span operator
*
* @return a new lazy span operator pointcut
*/
@SuppressWarnings("unchecked")
@@ -59,119 +60,127 @@ public abstract class ReactorSleuth {
public static <T> Function<? super Publisher<T>, ? extends Publisher<T>> spanOperator(
BeanFactory beanFactory) {
if(log.isWarnEnabled()){
log.warn("spanOperator method will be deleted in the next major release. " +
"Use scopePassingSpanOperator() method instead");
if (log.isWarnEnabled()) {
log.warn("spanOperator method will be deleted in the next major release. "
+ "Use scopePassingSpanOperator() method instead");
}
return sourcePub -> {
return (sourcePub -> {
// TODO: Remove this once Reactor 3.1.8 is released
//do the checks directly on actual original Publisher
if (sourcePub instanceof ConnectableFlux //Operators.lift can't handle that
|| sourcePub instanceof GroupedFlux //Operators.lift can't handle that
) {
// do the checks directly on actual original Publisher
if (sourcePub instanceof ConnectableFlux // Operators.lift can't handle that
|| sourcePub instanceof GroupedFlux // Operators.lift can't handle
// that
) {
return sourcePub;
}
//no more POINTCUT_FILTER since mechanism is broken
Function<? super Publisher<T>, ? extends Publisher<T>> lift = Operators.lift((scannable, sub) -> {
if (contextRefreshed(beanFactory)) {
if (log.isTraceEnabled()) {
log.trace("Spring Context already refreshed. Creating a Sleuth span subscriber with Reactor Context " + "[" + sub.currentContext() + "] and name [" + scannable.name() + "]");
}
return spanSubscriptionProvider(beanFactory, scannable, sub).get();
}
if (log.isTraceEnabled()) {
log.trace(
"Spring Context is not yet refreshed, falling back to lazy span subscriber. Reactor Context is [" + sub.currentContext() + "] and name is [" + scannable.name() + "]");
}
//rest of the logic unchanged...
return new LazySpanSubscriber<T>(
spanSubscriptionProvider(beanFactory, scannable, sub)
);
});
// no more POINTCUT_FILTER since mechanism is broken
Function<? super Publisher<T>, ? extends Publisher<T>> lift = Operators
.lift((scannable, sub) -> {
if (contextRefreshed(beanFactory)) {
if (log.isTraceEnabled()) {
log.trace(
"Spring Context already refreshed. Creating a Sleuth span subscriber with Reactor Context "
+ "[" + sub.currentContext()
+ "] and name [" + scannable.name()
+ "]");
}
return spanSubscriptionProvider(beanFactory, scannable, sub)
.get();
}
if (log.isTraceEnabled()) {
log.trace(
"Spring Context is not yet refreshed, falling back to lazy span subscriber. Reactor Context is ["
+ sub.currentContext() + "] and name is ["
+ scannable.name() + "]");
}
// rest of the logic unchanged...
return new LazySpanSubscriber<T>(
spanSubscriptionProvider(beanFactory, scannable, sub));
});
return lift.apply(sourcePub);
};
});
}
private static <T> SpanSubscriptionProvider spanSubscriptionProvider(
BeanFactory beanFactory, Scannable scannable, CoreSubscriber<? super T> sub) {
return new SpanSubscriptionProvider(
beanFactory,
sub,
sub.currentContext(),
return new SpanSubscriptionProvider(beanFactory, sub, sub.currentContext(),
scannable.name());
}
/**
* Return a span operator pointcut given a {@link Tracing}. This can be used in reactor
* via {@link reactor.core.publisher.Flux#transform(Function)}, {@link
* reactor.core.publisher.Mono#transform(Function)}, {@link
* reactor.core.publisher.Hooks#onEachOperator(Function)} or {@link
* reactor.core.publisher.Hooks#onLastOperator(Function)}. The Span operator
* Return a span operator pointcut given a {@link Tracing}. This can be used in
* reactor via {@link reactor.core.publisher.Flux#transform(Function)},
* {@link reactor.core.publisher.Mono#transform(Function)},
* {@link reactor.core.publisher.Hooks#onEachOperator(Function)} or
* {@link reactor.core.publisher.Hooks#onLastOperator(Function)}. The Span operator
* pointcut will pass the Scope of the Span without ever creating any new spans.
*
* @param beanFactory
* @param beanFactory - {@link BeanFactory}
* @param <T> an arbitrary type that is left unchanged by the span operator
*
* @return a new lazy span operator pointcut
*/
@SuppressWarnings("unchecked")
public static <T> Function<? super Publisher<T>, ? extends Publisher<T>> scopePassingSpanOperator(
BeanFactory beanFactory) {
return sourcePub -> {
return (sourcePub -> {
// TODO: Remove this once Reactor 3.1.8 is released
//do the checks directly on actual original Publisher
if (sourcePub instanceof ConnectableFlux //Operators.lift can't handle that
|| sourcePub instanceof GroupedFlux //Operators.lift can't handle that
) {
// do the checks directly on actual original Publisher
if (sourcePub instanceof ConnectableFlux // Operators.lift can't handle that
|| sourcePub instanceof GroupedFlux // Operators.lift can't handle
// that
) {
return sourcePub;
}
//no more POINTCUT_FILTER since mechanism is broken
Function<? super Publisher<T>, ? extends Publisher<T>> lift = Operators.lift((scannable, sub) -> {
//rest of the logic unchanged...
if (contextRefreshed(beanFactory)) {
if (log.isTraceEnabled()) {
log.trace("Spring Context already refreshed. Creating a scope " + "passing span subscriber with Reactor Context " + "[" + sub.currentContext() + "] and name [" + scannable.name() + "]");
}
return scopePassingSpanSubscription(beanFactory, scannable, sub).get();
}
if (log.isTraceEnabled()) {
log.trace(
"Spring Context is not yet refreshed, falling back to lazy span subscriber. Reactor Context is [" + sub.currentContext() + "] and name is [" + scannable.name() + "]");
}
return new LazySpanSubscriber<T>(
scopePassingSpanSubscription(beanFactory, scannable, sub)
);
});
// no more POINTCUT_FILTER since mechanism is broken
Function<? super Publisher<T>, ? extends Publisher<T>> lift = Operators
.lift((scannable, sub) -> {
// rest of the logic unchanged...
if (contextRefreshed(beanFactory)) {
if (log.isTraceEnabled()) {
log.trace(
"Spring Context already refreshed. Creating a scope "
+ "passing span subscriber with Reactor Context "
+ "[" + sub.currentContext()
+ "] and name [" + scannable.name()
+ "]");
}
return scopePassingSpanSubscription(beanFactory, scannable,
sub).get();
}
if (log.isTraceEnabled()) {
log.trace(
"Spring Context is not yet refreshed, falling back to lazy span subscriber. Reactor Context is ["
+ sub.currentContext() + "] and name is ["
+ scannable.name() + "]");
}
return new LazySpanSubscriber<T>(scopePassingSpanSubscription(
beanFactory, scannable, sub));
});
return lift.apply(sourcePub);
};
});
}
private static boolean contextRefreshed(BeanFactory beanFactory) {
try {
return beanFactory.getBean(ApplicationContextRefreshedListener.class).isRefreshed();
} catch (NoSuchBeanDefinitionException e) {
return beanFactory.getBean(ApplicationContextRefreshedListener.class)
.isRefreshed();
}
catch (NoSuchBeanDefinitionException ex) {
return false;
}
}
private static <T> SpanSubscriptionProvider<T> scopePassingSpanSubscription(
BeanFactory beanFactory, Scannable scannable, CoreSubscriber<? super T> sub) {
return new SpanSubscriptionProvider<T>(
beanFactory,
sub,
sub.currentContext(),
return new SpanSubscriptionProvider<T>(beanFactory, sub, sub.currentContext(),
scannable.name()) {
@Override SpanSubscription newCoreSubscriber(Tracing tracing) {
return new ScopePassingSpanSubscriber<T>(
sub,
sub != null ? sub.currentContext() : Context.empty(),
tracing);
@Override
SpanSubscription newCoreSubscriber(Tracing tracing) {
return new ScopePassingSpanSubscriber<T>(sub,
sub != null ? sub.currentContext() : Context.empty(), tracing);
}
};
}
private ReactorSleuth() {
}
}
}

View File

@@ -21,81 +21,94 @@ import java.util.concurrent.atomic.AtomicBoolean;
import brave.Span;
import brave.Tracer;
import brave.Tracing;
import reactor.util.context.Context;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import reactor.util.context.Context;
/**
* A trace representation of the {@link Subscriber} that always
* continues a span
* A trace representation of the {@link Subscriber} that always continues a span.
*
* @param - span subscription type
* @author Marcin Grzejszczak
* @since 2.0.0
*/
final class ScopePassingSpanSubscriber<T> extends AtomicBoolean implements SpanSubscription<T> {
final class ScopePassingSpanSubscriber<T> extends AtomicBoolean
implements SpanSubscription<T> {
private static final Log log = LogFactory.getLog(ScopePassingSpanSubscriber.class);
private final Span span;
private final Subscriber<? super T> subscriber;
private final Context context;
private final Tracer tracer;
private Subscription s;
ScopePassingSpanSubscriber(Subscriber<? super T> subscriber, Context ctx, Tracing tracing) {
ScopePassingSpanSubscriber(Subscriber<? super T> subscriber, Context ctx,
Tracing tracing) {
this.subscriber = subscriber;
this.tracer = tracing.tracer();
Span root = ctx != null ?
ctx.getOrDefault(Span.class, this.tracer.currentSpan()) : null;
Span root = ctx != null ? ctx.getOrDefault(Span.class, this.tracer.currentSpan())
: null;
this.span = root;
this.context = ctx != null && root != null ? ctx.put(Span.class, root) :
ctx != null ? ctx : Context.empty();
this.context = ctx != null && root != null ? ctx.put(Span.class, root)
: ctx != null ? ctx : Context.empty();
if (log.isTraceEnabled()) {
log.trace("Root span [" + root + "], context [" + this.context + "]");
}
}
@Override public void onSubscribe(Subscription subscription) {
@Override
public void onSubscribe(Subscription subscription) {
this.s = subscription;
try (Tracer.SpanInScope inScope = this.tracer.withSpanInScope(this.span)) {
this.subscriber.onSubscribe(this);
}
}
@Override public void request(long n) {
@Override
public void request(long n) {
try (Tracer.SpanInScope inScope = this.tracer.withSpanInScope(this.span)) {
this.s.request(n);
}
}
@Override public void cancel() {
@Override
public void cancel() {
try (Tracer.SpanInScope inScope = this.tracer.withSpanInScope(this.span)) {
this.s.cancel();
}
}
@Override public void onNext(T o) {
@Override
public void onNext(T o) {
try (Tracer.SpanInScope inScope = this.tracer.withSpanInScope(this.span)) {
this.subscriber.onNext(o);
}
}
@Override public void onError(Throwable throwable) {
@Override
public void onError(Throwable throwable) {
try (Tracer.SpanInScope inScope = this.tracer.withSpanInScope(this.span)) {
this.subscriber.onError(throwable);
}
}
@Override public void onComplete() {
@Override
public void onComplete() {
try (Tracer.SpanInScope inScope = this.tracer.withSpanInScope(this.span)) {
this.subscriber.onComplete();
}
}
@Override public Context currentContext() {
@Override
public Context currentContext() {
return this.context;
}
}
}

View File

@@ -19,15 +19,16 @@ package org.springframework.cloud.sleuth.instrument.reactor;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Sleuth Reactor settings
* Sleuth Reactor settings.
*
* @author Marcin Grzejszczak
* @since 2.0.2
*/
@ConfigurationProperties("spring.sleuth.reactor.enabled")
public class SleuthReactorProperties {
/**
* When true enables instrumentation for reactor
* When true enables instrumentation for reactor.
*/
private boolean enabled = true;
@@ -38,4 +39,5 @@ public class SleuthReactorProperties {
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}

View File

@@ -16,6 +16,8 @@
package org.springframework.cloud.sleuth.instrument.reactor;
import java.util.concurrent.atomic.AtomicBoolean;
import brave.Span;
import brave.Tracer;
import brave.Tracing;
@@ -26,12 +28,11 @@ import reactor.util.Logger;
import reactor.util.Loggers;
import reactor.util.context.Context;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* A trace representation of the {@link Subscriber}
* A trace representation of the {@link Subscriber}.
*
* @deprecated use {@link ScopePassingSpanSubscriber} instead
* @param <T> - return type of the subscriber
* @author Stephane Maldini
* @author Marcin Grzejszczak
* @since 2.0.0
@@ -39,14 +40,18 @@ import java.util.concurrent.atomic.AtomicBoolean;
@Deprecated
final class SpanSubscriber<T> extends AtomicBoolean implements SpanSubscription<T> {
private static final Logger log = Loggers.getLogger(
SpanSubscriber.class);
private static final Logger log = Loggers.getLogger(SpanSubscriber.class);
private final Span span;
private final Span rootSpan;
private final Subscriber<? super T> subscriber;
private final Context context;
private final Tracer tracer;
private Subscription s;
SpanSubscriber(Subscriber<? super T> subscriber, Context ctx, Tracing tracing,
@@ -61,16 +66,17 @@ final class SpanSubscriber<T> extends AtomicBoolean implements SpanSubscription<
if (log.isTraceEnabled()) {
log.trace("Stored context root span [{}]", this.rootSpan);
}
this.span = root != null ?
this.tracer.nextSpan(TraceContextOrSamplingFlags.create(root.context()))
.name(name) : this.tracer.nextSpan().name(name);
this.span = root != null ? this.tracer
.nextSpan(TraceContextOrSamplingFlags.create(root.context())).name(name)
: this.tracer.nextSpan().name(name);
if (log.isTraceEnabled()) {
log.trace("Created span [{}], with name [{}]", this.span, name);
}
this.context = ctx.put(Span.class, this.span);
}
@Override public void onSubscribe(Subscription subscription) {
@Override
public void onSubscribe(Subscription subscription) {
if (log.isTraceEnabled()) {
log.trace("On subscribe");
}
@@ -83,7 +89,8 @@ final class SpanSubscriber<T> extends AtomicBoolean implements SpanSubscription<
}
}
@Override public void request(long n) {
@Override
public void request(long n) {
if (log.isTraceEnabled()) {
log.trace("Request");
}
@@ -100,7 +107,8 @@ final class SpanSubscriber<T> extends AtomicBoolean implements SpanSubscription<
}
}
@Override public void cancel() {
@Override
public void cancel() {
try {
if (log.isTraceEnabled()) {
log.trace("Cancel");
@@ -112,11 +120,13 @@ final class SpanSubscriber<T> extends AtomicBoolean implements SpanSubscription<
}
}
@Override public void onNext(T o) {
@Override
public void onNext(T o) {
this.subscriber.onNext(o);
}
@Override public void onError(Throwable throwable) {
@Override
public void onError(Throwable throwable) {
try {
this.subscriber.onError(throwable);
}
@@ -125,7 +135,8 @@ final class SpanSubscriber<T> extends AtomicBoolean implements SpanSubscription<
}
}
@Override public void onComplete() {
@Override
public void onComplete() {
try {
this.subscriber.onComplete();
}
@@ -152,7 +163,9 @@ final class SpanSubscriber<T> extends AtomicBoolean implements SpanSubscription<
}
}
@Override public Context currentContext() {
@Override
public Context currentContext() {
return this.context;
}
}
}

View File

@@ -22,12 +22,14 @@ import reactor.core.Fuseable;
/**
* A {@link SpanSubscription} is a {@link Subscription} that fakes being {@link Fuseable}
* (implementing {@link reactor.core.Fuseable.QueueSubscription} with default no-op methods
* and always negotiating fusion to be {@link Fuseable#NONE}).
* (implementing {@link reactor.core.Fuseable.QueueSubscription} with default no-op
* methods and always negotiating fusion to be {@link Fuseable#NONE}).
*
* @author Marcin Grzejszczak
* @param <T> - type of the subsciption
*/
interface SpanSubscription<T> extends Subscription, CoreSubscriber<T>, Fuseable.QueueSubscription<T> {
interface SpanSubscription<T>
extends Subscription, CoreSubscriber<T>, Fuseable.QueueSubscription<T> {
@Override
default T poll() {
@@ -36,7 +38,7 @@ interface SpanSubscription<T> extends Subscription, CoreSubscriber<T>, Fuseable.
@Override
default int requestFusion(int i) {
return Fuseable.NONE; //always negotiate to no fusion
return Fuseable.NONE; // always negotiate to no fusion
}
@Override
@@ -51,7 +53,7 @@ interface SpanSubscription<T> extends Subscription, CoreSubscriber<T>, Fuseable.
@Override
default void clear() {
//NO-OP
// NO-OP
}
}

View File

@@ -19,15 +19,16 @@ package org.springframework.cloud.sleuth.instrument.reactor;
import java.util.function.Supplier;
import brave.Tracing;
import reactor.util.context.Context;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.reactivestreams.Subscriber;
import org.springframework.beans.factory.BeanFactory;
import reactor.util.context.Context;
/**
* Supplier to lazily start a {@link SpanSubscription}
* Supplier to lazily start a {@link SpanSubscription}.
*
* @param <T> type of returned subscription
* @author Marcin Grzejszczak
*/
class SpanSubscriptionProvider<T> implements Supplier<SpanSubscription<T>> {
@@ -35,13 +36,16 @@ class SpanSubscriptionProvider<T> implements Supplier<SpanSubscription<T>> {
private static final Log log = LogFactory.getLog(SpanSubscriptionProvider.class);
final BeanFactory beanFactory;
final Subscriber<? super T> subscriber;
final Context context;
final String name;
private volatile Tracing tracing;
SpanSubscriptionProvider(BeanFactory beanFactory,
Subscriber<? super T> subscriber,
SpanSubscriptionProvider(BeanFactory beanFactory, Subscriber<? super T> subscriber,
Context context, String name) {
this.beanFactory = beanFactory;
this.subscriber = subscriber;
@@ -52,7 +56,8 @@ class SpanSubscriptionProvider<T> implements Supplier<SpanSubscription<T>> {
}
}
@Override public SpanSubscription<T> get() {
@Override
public SpanSubscription<T> get() {
return newCoreSubscriber(tracing());
}
@@ -66,4 +71,5 @@ class SpanSubscriptionProvider<T> implements Supplier<SpanSubscription<T>> {
}
return this.tracing;
}
}

View File

@@ -19,6 +19,7 @@ package org.springframework.cloud.sleuth.instrument.reactor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
import javax.annotation.PreDestroy;
import brave.Tracing;
@@ -43,15 +44,15 @@ import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration}
* to enable tracing of Reactor components via Spring Cloud Sleuth.
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} to enable tracing of Reactor components via Spring Cloud Sleuth.
*
* @author Stephane Maldini
* @author Marcin Grzejszczak
* @since 2.0.0
*/
@Configuration
@ConditionalOnProperty(value="spring.sleuth.reactor.enabled", matchIfMissing=true)
@ConditionalOnProperty(value = "spring.sleuth.reactor.enabled", matchIfMissing = true)
@ConditionalOnClass(Mono.class)
@AutoConfigureAfter(TraceWebFluxAutoConfiguration.class)
public class TraceReactorAutoConfiguration {
@@ -60,13 +61,8 @@ public class TraceReactorAutoConfiguration {
@ConditionalOnBean(Tracing.class)
static class TraceReactorConfiguration {
static final String SLEUTH_TRACE_REACTOR_KEY = TraceReactorConfiguration.class.getName();
@PreDestroy
public void cleanupHooks() {
Hooks.resetOnEachOperator(SLEUTH_TRACE_REACTOR_KEY);
Schedulers.resetFactory();
}
static final String SLEUTH_TRACE_REACTOR_KEY = TraceReactorConfiguration.class
.getName();
@Bean
// for tests
@@ -75,21 +71,32 @@ public class TraceReactorAutoConfiguration {
return new HookRegisteringBeanDefinitionRegistryPostProcessor();
}
@Bean ApplicationContextRefreshedListener traceApplicationContextRefreshedListener() {
@PreDestroy
public void cleanupHooks() {
Hooks.resetOnEachOperator(SLEUTH_TRACE_REACTOR_KEY);
Schedulers.resetFactory();
}
@Bean
ApplicationContextRefreshedListener traceApplicationContextRefreshedListener() {
return new ApplicationContextRefreshedListener();
}
}
}
class HookRegisteringBeanDefinitionRegistryPostProcessor implements
BeanDefinitionRegistryPostProcessor {
class HookRegisteringBeanDefinitionRegistryPostProcessor
implements BeanDefinitionRegistryPostProcessor {
@Override public void postProcessBeanDefinitionRegistry(
BeanDefinitionRegistry registry) throws BeansException {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
throws BeansException {
}
@Override public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) throws BeansException {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
setupHooks(beanFactory);
}
@@ -102,17 +109,18 @@ class HookRegisteringBeanDefinitionRegistryPostProcessor implements
private Schedulers.Factory factoryInstance(final BeanFactory beanFactory) {
return new Schedulers.Factory() {
@Override public ScheduledExecutorService decorateExecutorService(String schedulerType,
@Override
public ScheduledExecutorService decorateExecutorService(String schedulerType,
Supplier<? extends ScheduledExecutorService> actual) {
return new TraceableScheduledExecutorService(beanFactory,
actual.get());
return new TraceableScheduledExecutorService(beanFactory, actual.get());
}
};
}
}
class ApplicationContextRefreshedListener implements
ApplicationListener<ContextRefreshedEvent> {
class ApplicationContextRefreshedListener
implements ApplicationListener<ContextRefreshedEvent> {
AtomicBoolean refreshed = new AtomicBoolean();
@@ -124,4 +132,5 @@ class ApplicationContextRefreshedListener implements
boolean isRefreshed() {
return this.refreshed.get();
}
}

View File

@@ -31,8 +31,8 @@ import org.springframework.context.annotation.Configuration;
import rx.plugins.RxJavaSchedulersHook;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration} that
* enables support for RxJava via {@link RxJavaSchedulersHook}.
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that enables support for RxJava via {@link RxJavaSchedulersHook}.
*
* @author Shivang Shah
* @since 1.0.0
@@ -51,4 +51,5 @@ public class RxJavaAutoConfiguration {
return new SleuthRxJavaSchedulersHook(tracer,
Arrays.asList(sleuthRxJavaSchedulersProperties.getIgnoredthreads()));
}
}

View File

@@ -37,12 +37,14 @@ import rx.plugins.RxJavaSchedulersHook;
*/
class SleuthRxJavaSchedulersHook extends RxJavaSchedulersHook {
private static final Log log = LogFactory.getLog(
SleuthRxJavaSchedulersHook.class);
private static final Log log = LogFactory.getLog(SleuthRxJavaSchedulersHook.class);
private static final String RXJAVA_COMPONENT = "rxjava";
private final Tracer tracer;
private final List<String> threadsToSample;
private RxJavaSchedulersHook delegate;
SleuthRxJavaSchedulersHook(Tracer tracer, List<String> threadsToSample) {
@@ -53,27 +55,28 @@ class SleuthRxJavaSchedulersHook extends RxJavaSchedulersHook {
if (this.delegate instanceof SleuthRxJavaSchedulersHook) {
return;
}
RxJavaErrorHandler errorHandler = RxJavaPlugins.getInstance().getErrorHandler();
RxJavaObservableExecutionHook observableExecutionHook
= RxJavaPlugins.getInstance().getObservableExecutionHook();
RxJavaErrorHandler errorHandler = RxJavaPlugins.getInstance()
.getErrorHandler();
RxJavaObservableExecutionHook observableExecutionHook = RxJavaPlugins
.getInstance().getObservableExecutionHook();
logCurrentStateOfRxJavaPlugins(errorHandler, observableExecutionHook);
RxJavaPlugins.getInstance().reset();
RxJavaPlugins.getInstance().registerSchedulersHook(this);
RxJavaPlugins.getInstance().registerErrorHandler(errorHandler);
RxJavaPlugins.getInstance().registerObservableExecutionHook(observableExecutionHook);
} catch (Exception e) {
log.error("Failed to register Sleuth RxJava SchedulersHook", e);
RxJavaPlugins.getInstance()
.registerObservableExecutionHook(observableExecutionHook);
}
catch (Exception ex) {
log.error("Failed to register Sleuth RxJava SchedulersHook", ex);
}
}
private void logCurrentStateOfRxJavaPlugins(RxJavaErrorHandler errorHandler,
RxJavaObservableExecutionHook observableExecutionHook) {
RxJavaObservableExecutionHook observableExecutionHook) {
if (log.isDebugEnabled()) {
log.debug("Current RxJava plugins configuration is ["
+ "schedulersHook [" + this.delegate + "],"
+ "errorHandler [" + errorHandler + "],"
+ "observableExecutionHook [" + observableExecutionHook + "],"
+ "]");
log.debug("Current RxJava plugins configuration is [" + "schedulersHook ["
+ this.delegate + "]," + "errorHandler [" + errorHandler + "],"
+ "observableExecutionHook [" + observableExecutionHook + "]," + "]");
log.debug("Registering Sleuth RxJava Schedulers Hook.");
}
}
@@ -83,26 +86,32 @@ class SleuthRxJavaSchedulersHook extends RxJavaSchedulersHook {
if (action instanceof TraceAction) {
return action;
}
Action0 wrappedAction = this.delegate != null
? this.delegate.onSchedule(action) : action;
Action0 wrappedAction = this.delegate != null ? this.delegate.onSchedule(action)
: action;
if (wrappedAction instanceof TraceAction) {
return action;
}
return super.onSchedule(new TraceAction(this.tracer, wrappedAction,
this.threadsToSample));
return super.onSchedule(
new TraceAction(this.tracer, wrappedAction, this.threadsToSample));
}
/**
* Wrapped Action element.
* @author Marcin Grzejszczak
*/
static class TraceAction implements Action0 {
private static final String THREAD_NAME_KEY = "thread";
private final Action0 actual;
private final Tracer tracer;
private final Span parent;
private final List<String> threadsToIgnore;
public TraceAction(Tracer tracer, Action0 actual,
List<String> threadsToIgnore) {
TraceAction(Tracer tracer, Action0 actual, List<String> threadsToIgnore) {
this.tracer = tracer;
this.threadsToIgnore = threadsToIgnore;
this.parent = this.tracer.currentSpan();
@@ -129,18 +138,22 @@ class SleuthRxJavaSchedulersHook extends RxJavaSchedulersHook {
boolean created = false;
if (span != null) {
span = this.tracer.toSpan(this.parent.context());
} else {
}
else {
span = this.tracer.nextSpan().name(RXJAVA_COMPONENT).start();
span.tag(THREAD_NAME_KEY, Thread.currentThread().getName());
created = true;
}
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
this.actual.call();
} finally {
}
finally {
if (created) {
span.finish();
}
}
}
}
}

View File

@@ -19,7 +19,7 @@ package org.springframework.cloud.sleuth.instrument.rxjava;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Configuration properties for RxJava tracing
* Configuration properties for RxJava tracing.
*
* @author Arthur Gavlyukovskiy
* @since 1.0.12
@@ -31,6 +31,7 @@ public class SleuthRxJavaSchedulersProperties {
* Thread names for which spans will not be sampled.
*/
private String[] ignoredthreads = { "HystrixMetricPoller", "^RxComputation.*$" };
private Hook hook = new Hook();
public String[] getIgnoredthreads() {
@@ -63,5 +64,7 @@ public class SleuthRxJavaSchedulersProperties {
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
}

View File

@@ -19,7 +19,8 @@ package org.springframework.cloud.sleuth.instrument.scheduling;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Configuration properties for {@link org.springframework.scheduling.annotation.Scheduled} tracing
* Configuration properties for
* {@link org.springframework.scheduling.annotation.Scheduled} tracing.
*
* @author Arthur Gavlyukovskiy
* @since 1.0.12
@@ -52,4 +53,5 @@ public class SleuthSchedulingProperties {
public void setSkipPattern(String skipPattern) {
this.skipPattern = skipPattern;
}
}

View File

@@ -28,9 +28,9 @@ import org.springframework.cloud.sleuth.util.SpanNameUtil;
/**
* Aspect that creates a new Span for running threads executing methods annotated with
* {@link org.springframework.scheduling.annotation.Scheduled} annotation.
* For every execution of scheduled method a new trace will be started. The name of the
* span will be the simple name of the class annotated with
* {@link org.springframework.scheduling.annotation.Scheduled} annotation. For every
* execution of scheduled method a new trace will be started. The name of the span will be
* the simple name of the class annotated with
* {@link org.springframework.scheduling.annotation.Scheduled}
*
* @author Tomasz Nurkewicz, 4financeIT
@@ -38,16 +38,17 @@ import org.springframework.cloud.sleuth.util.SpanNameUtil;
* @author Marcin Grzejszczak
* @author Spencer Gibb
* @since 1.0.0
*
* @see Tracing
*/
@Aspect
public class TraceSchedulingAspect {
private static final String CLASS_KEY = "class";
private static final String METHOD_KEY = "method";
private final Tracer tracer;
private final Pattern skipPattern;
public TraceSchedulingAspect(Tracer tracer, Pattern skipPattern) {
@@ -62,11 +63,12 @@ public class TraceSchedulingAspect {
}
String spanName = SpanNameUtil.toLowerHyphen(pjp.getSignature().getName());
Span span = startOrContinueRenamedSpan(spanName);
try(Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) {
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.start())) {
span.tag(CLASS_KEY, pjp.getTarget().getClass().getSimpleName());
span.tag(METHOD_KEY, pjp.getSignature().getName());
return pjp.proceed();
} finally {
}
finally {
span.finish();
}
}

View File

@@ -36,7 +36,6 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy;
* @author Michal Chmielarz, 4financeIT
* @author Spencer Gibb
* @since 1.0.0
*
* @see TraceSchedulingAspect
*/
@Configuration
@@ -54,4 +53,5 @@ public class TraceSchedulingAutoConfiguration {
return new TraceSchedulingAspect(tracer,
Pattern.compile(sleuthSchedulingProperties.getSkipPattern()));
}
}

View File

@@ -26,20 +26,24 @@ import java.lang.annotation.Target;
import org.springframework.beans.factory.annotation.Qualifier;
/**
* Annotate a client {@link brave.http.HttpSampler} that hsould be
* injected to {@link brave.http.HttpTracing}
* Annotate a client {@link brave.http.HttpSampler} that hsould be injected to
* {@link brave.http.HttpTracing}.
*
* @author Marcin Grzejszczak
* @since 2.0.0
* @see Qualifier
*/
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Qualifier(ClientSampler.NAME)
public @interface ClientSampler {
/**
* Default name for Sleuth client sampler.
*/
String NAME = "sleuthClientSampler";
}

View File

@@ -17,6 +17,7 @@
package org.springframework.cloud.sleuth.instrument.web;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
@@ -28,8 +29,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Filter running after {@link brave.servlet.TracingFilter}
* that logs uncaught exceptions
* Filter running after {@link brave.servlet.TracingFilter} that logs uncaught exceptions.
*
* @author Marcin Grzejszczak
* @since 2.0.0
@@ -38,23 +38,28 @@ class ExceptionLoggingFilter implements Filter {
private static final Log log = LogFactory.getLog(ExceptionLoggingFilter.class);
@Override public void init(FilterConfig filterConfig) throws ServletException {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override public void doFilter(ServletRequest request, ServletResponse response,
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(request, response);
} catch (Exception e) {
}
catch (Exception ex) {
if (log.isErrorEnabled()) {
log.error("Uncaught exception thrown", e);
log.error("Uncaught exception thrown", ex);
}
throw e;
throw ex;
}
}
@Override public void destroy() {
@Override
public void destroy() {
}
}

View File

@@ -26,18 +26,24 @@ import java.lang.annotation.Target;
import org.springframework.beans.factory.annotation.Qualifier;
/**
* Annotate a server {@link brave.http.HttpSampler} that hsould be
* injected to {@link brave.http.HttpTracing}
* Annotate a server {@link brave.http.HttpSampler} that hsould be injected to
* {@link brave.http.HttpTracing}.
*
* @author Marcin Grzejszczak
* @since 2.0.0
* @see Qualifier
*/
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Qualifier(ServerSampler.NAME)
public @interface ServerSampler {
/**
* Default name for the Sleuth server sampler.
*/
String NAME = "sleuthServerSampler";
}

View File

@@ -20,14 +20,16 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Utility class to retrieve data from Servlet
* HTTP request and response
* Utility class to retrieve data from Servlet HTTP request and response.
*
* @author Marcin Grzejszczak
*
* @since 1.0.0
*/
class ServletUtils {
final class ServletUtils {
private ServletUtils() {
}
static String getHeader(HttpServletRequest request, HttpServletResponse response,
String name) {

View File

@@ -20,13 +20,15 @@ import java.util.Optional;
import java.util.regex.Pattern;
/**
* Provides a URL {@link Pattern} for spans that should be not sampled.
* The default implementation of {@link SkipPatternProvider} will harvest all
* Provides a URL {@link Pattern} for spans that should be not sampled. The default
* implementation of {@link SkipPatternProvider} will harvest all
* {@link SingleSkipPattern}s and combine them in a single pattern
*
* @author Marcin Grzejszczak
* @since 2.1.0
*/
interface SingleSkipPattern {
Optional<Pattern> skipPattern();
}

View File

@@ -25,5 +25,7 @@ import java.util.regex.Pattern;
* @since 2.0.0
*/
public interface SkipPatternProvider {
Pattern skipPattern();
}

View File

@@ -24,7 +24,7 @@ import brave.http.HttpClientParser;
import org.springframework.cloud.sleuth.util.SpanNameUtil;
/**
* An {@link HttpClientParser} that behaves like Sleuth in versions 1.x
* An {@link HttpClientParser} that behaves like Sleuth in versions 1.x.
*
* @author Marcin Grzejszczak
* @since 2.0.0
@@ -32,8 +32,11 @@ import org.springframework.cloud.sleuth.util.SpanNameUtil;
class SleuthHttpClientParser extends HttpClientParser {
private static final String HOST_KEY = "http.host";
private static final String METHOD_KEY = "http.method";
private static final String PATH_KEY = "http.path";
private static final String URL_KEY = "http.url";
private final TraceKeys traceKeys;
@@ -42,24 +45,25 @@ class SleuthHttpClientParser extends HttpClientParser {
this.traceKeys = traceKeys;
}
@Override protected <Req> String spanName(HttpAdapter<Req, ?> adapter,
Req req) {
@Override
protected <Req> String spanName(HttpAdapter<Req, ?> adapter, Req req) {
return getName(URI.create(adapter.url(req)));
}
@Override public <Req> void request(HttpAdapter<Req, ?> adapter, Req req,
@Override
public <Req> void request(HttpAdapter<Req, ?> adapter, Req req,
SpanCustomizer customizer) {
super.request(adapter, req, customizer);
String url = adapter.url(req);
URI uri = URI.create(url);
addRequestTags(customizer, url, uri.getHost(), uri.getPath(), adapter.method(req));
this.traceKeys.getHttp().getHeaders()
.forEach(s -> {
String headerValue = adapter.requestHeader(req, s);
if (headerValue != null) {
customizer.tag(key(s), headerValue);
}
});
addRequestTags(customizer, url, uri.getHost(), uri.getPath(),
adapter.method(req));
this.traceKeys.getHttp().getHeaders().forEach(((s) -> {
String headerValue = adapter.requestHeader(req, s);
if (headerValue != null) {
customizer.tag(key(s), headerValue);
}
}));
}
private String key(String key) {
@@ -85,4 +89,5 @@ class SleuthHttpClientParser extends HttpClientParser {
customizer.tag(PATH_KEY, path);
customizer.tag(METHOD_KEY, method);
}
}
}

View File

@@ -19,11 +19,17 @@ package org.springframework.cloud.sleuth.instrument.web;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Legacy HTTP Sleuth properties.
*
* @author Marcin Grzejszczak
* @since 2.0.0
*/
@ConfigurationProperties("spring.sleuth.http.legacy")
public class SleuthHttpLegacyProperties {
/**
* Enables the legacy Sleuth setup.
*/
private boolean enabled;
public boolean isEnabled() {
@@ -33,4 +39,5 @@ public class SleuthHttpLegacyProperties {
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}

View File

@@ -19,8 +19,9 @@ package org.springframework.cloud.sleuth.instrument.web;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Sleuth HTTP settings
* Sleuth HTTP settings.
*
* @author Marcin Grzejszczak
* @since 2.0.0
*/
@ConfigurationProperties("spring.sleuth.http")
@@ -60,5 +61,7 @@ public class SleuthHttpProperties {
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
}

View File

@@ -22,7 +22,7 @@ import brave.http.HttpAdapter;
import brave.http.HttpSampler;
/**
* Doesn't sample a span if skip pattern is matched
* Doesn't sample a span if skip pattern is matched.
*
* @author Marcin Grzejszczak
* @since 2.0.0
@@ -35,7 +35,8 @@ class SleuthHttpSampler extends HttpSampler {
this.pattern = provider.skipPattern();
}
@Override public <Req> Boolean trySample(HttpAdapter<Req, ?> adapter, Req request) {
@Override
public <Req> Boolean trySample(HttpAdapter<Req, ?> adapter, Req request) {
String url = adapter.path(request);
boolean shouldSkip = this.pattern.matcher(url).matches();
if (shouldSkip) {
@@ -43,4 +44,5 @@ class SleuthHttpSampler extends HttpSampler {
}
return null;
}
}

View File

@@ -25,7 +25,7 @@ import brave.http.HttpClientParser;
import brave.http.HttpServerParser;
/**
* An {@link HttpClientParser} that behaves like Sleuth in versions 1.x
* An {@link HttpClientParser} that behaves like Sleuth in versions 1.x.
*
* @author Marcin Grzejszczak
* @since 2.0.0
@@ -35,6 +35,7 @@ class SleuthHttpServerParser extends HttpServerParser {
private static final String STATUS_CODE_KEY = "http.status_code";
private final SleuthHttpClientParser clientParser;
private final ErrorParser errorParser;
SleuthHttpServerParser(TraceKeys traceKeys, ErrorParser errorParser) {
@@ -42,16 +43,18 @@ class SleuthHttpServerParser extends HttpServerParser {
this.errorParser = errorParser;
}
@Override protected ErrorParser errorParser() {
@Override
protected ErrorParser errorParser() {
return this.errorParser;
}
@Override protected <Req> String spanName(HttpAdapter<Req, ?> adapter,
Req req) {
@Override
protected <Req> String spanName(HttpAdapter<Req, ?> adapter, Req req) {
return this.clientParser.spanName(adapter, req);
}
@Override public <Req> void request(HttpAdapter<Req, ?> adapter, Req req,
@Override
public <Req> void request(HttpAdapter<Req, ?> adapter, Req req,
SpanCustomizer customizer) {
this.clientParser.request(adapter, req, customizer);
}
@@ -71,7 +74,8 @@ class SleuthHttpServerParser extends HttpServerParser {
if (httpStatus == HttpServletResponse.SC_OK && error != null) {
// Filter chain threw exception but the response status may not have been set
// yet, so we have to guess.
customizer.tag(STATUS_CODE_KEY, String.valueOf(HttpServletResponse.SC_INTERNAL_SERVER_ERROR));
customizer.tag(STATUS_CODE_KEY,
String.valueOf(HttpServletResponse.SC_INTERNAL_SERVER_ERROR));
}
// only tag valid http statuses
else if (httpStatus >= 100 && (httpStatus < 200) || (httpStatus > 399)) {
@@ -79,4 +83,5 @@ class SleuthHttpServerParser extends HttpServerParser {
}
error(httpStatus, error, customizer);
}
}

View File

@@ -20,7 +20,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
/**
* Configuration properties for web tracing
* Configuration properties for web tracing.
*
* @author Arthur Gavlyukovskiy
* @since 1.0.12
@@ -28,39 +28,47 @@ import org.springframework.boot.context.properties.NestedConfigurationProperty;
@ConfigurationProperties("spring.sleuth.web")
public class SleuthWebProperties {
public static final String DEFAULT_SKIP_PATTERN =
"/api-docs.*|/autoconfig|/configprops|/dump|/health|/info|/metrics.*|/mappings|/trace|/swagger.*|.*\\.png|.*\\.css|.*\\.js|.*\\.html|/favicon.ico|/hystrix.stream|/application/.*|/actuator.*|/cloudfoundryapplication";
/**
* Default set of skip patterns.
*/
public static final String DEFAULT_SKIP_PATTERN = "/api-docs.*|/autoconfig|/configprops|/dump|/health|/info|/metrics.*|/mappings|/trace|/swagger.*|.*\\.png|.*\\.css|.*\\.js|.*\\.html|/favicon.ico|/hystrix.stream|/application/.*|/actuator.*|/cloudfoundryapplication";
/**
* When true enables instrumentation for web applications
* When true enables instrumentation for web applications.
*/
private boolean enabled = true;
/**
* Pattern for URLs that should be skipped in tracing
* Pattern for URLs that should be skipped in tracing.
*/
private String skipPattern = DEFAULT_SKIP_PATTERN;
/**
* Additional pattern for URLs that should be skipped in tracing.
* This will be appended to the {@link SleuthWebProperties#skipPattern}
* Additional pattern for URLs that should be skipped in tracing. This will be
* appended to the {@link SleuthWebProperties#skipPattern}.
*/
private String additionalSkipPattern;
/**
* Order in which the tracing filters should be registered.
* Defaults to {@link TraceHttpAutoConfiguration#TRACING_FILTER_ORDER}
* Order in which the tracing filters should be registered. Defaults to
* {@link TraceHttpAutoConfiguration#TRACING_FILTER_ORDER}.
*/
private int filterOrder = TraceHttpAutoConfiguration.TRACING_FILTER_ORDER;
/**
* Flag to toggle the presence of a filter that logs thrown exceptions
* Flag to toggle the presence of a filter that logs thrown exceptions.
*/
private boolean exceptionThrowingFilterEnabled = true;
/**
* Properties related to HTTP clients.
*/
private Client client = new Client();
public static String getDefaultSkipPattern() {
return DEFAULT_SKIP_PATTERN;
}
public boolean isEnabled() {
return this.enabled;
}
@@ -85,10 +93,6 @@ public class SleuthWebProperties {
this.additionalSkipPattern = additionalSkipPattern;
}
public static String getDefaultSkipPattern() {
return DEFAULT_SKIP_PATTERN;
}
public int getFilterOrder() {
return this.filterOrder;
}
@@ -114,14 +118,20 @@ public class SleuthWebProperties {
this.client = client;
}
/**
* Web client properties.
* @author Marcin Grzejszczak
*/
public static class Client {
/**
* Pattern for URLs that should be skipped in client side tracing
* Pattern for URLs that should be skipped in client side tracing.
*/
private String skipPattern = "";
/**
* Enable interceptor injecting into {@link org.springframework.web.client.RestTemplate}
* Enable interceptor injecting into
* {@link org.springframework.web.client.RestTemplate}.
*/
private boolean enabled = true;
@@ -140,8 +150,13 @@ public class SleuthWebProperties {
public void setSkipPattern(String skipPattern) {
this.skipPattern = skipPattern;
}
}
/**
* Async computing properties.
* @author Marcin Grzejszczak
*/
public static class Async {
@NestedConfigurationProperty
@@ -154,12 +169,18 @@ public class SleuthWebProperties {
public void setClient(AsyncClient client) {
this.client = client;
}
}
/**
* Async client properties.
* @author Marcin Grzejszczak
*/
public static class AsyncClient {
/**
* Enable span information propagation for {@link org.springframework.http.client.AsyncClientHttpRequestFactory}.
* Enable span information propagation for
* {@link org.springframework.http.client.AsyncClientHttpRequestFactory}.
*/
private boolean enabled;
@@ -181,12 +202,18 @@ public class SleuthWebProperties {
public void setTemplate(Template template) {
this.template = template;
}
}
/**
* Async Rest Template properties.
* @author Marcin Grzejszczak
*/
public static class Template {
/**
* Enable span information propagation for {@link org.springframework.web.client.AsyncRestTemplate}.
* Enable span information propagation for
* {@link org.springframework.web.client.AsyncRestTemplate}.
*/
private boolean enabled;
@@ -197,5 +224,7 @@ public class SleuthWebProperties {
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
}

View File

@@ -34,8 +34,8 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration}
* related to HTTP based communication.
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} related to HTTP based communication.
*
* @author Marcin Grzejszczak
* @since 2.0.0
@@ -44,30 +44,34 @@ import org.springframework.core.Ordered;
@ConditionalOnBean(Tracing.class)
@ConditionalOnProperty(name = "spring.sleuth.http.enabled", havingValue = "true", matchIfMissing = true)
@AutoConfigureAfter(TraceWebAutoConfiguration.class)
@EnableConfigurationProperties({TraceKeys.class, SleuthHttpLegacyProperties.class})
@EnableConfigurationProperties({ TraceKeys.class, SleuthHttpLegacyProperties.class })
public class TraceHttpAutoConfiguration {
static final int TRACING_FILTER_ORDER = Ordered.HIGHEST_PRECEDENCE + 5;
@Autowired HttpClientParser clientParser;
@Autowired HttpServerParser serverParser;
@Autowired @ClientSampler HttpSampler clientSampler;
@Autowired(required = false) @ServerSampler HttpSampler serverSampler;
@Autowired
HttpClientParser clientParser;
@Autowired
HttpServerParser serverParser;
@Autowired
@ClientSampler
HttpSampler clientSampler;
@Autowired(required = false)
@ServerSampler
HttpSampler serverSampler;
@Bean
@ConditionalOnMissingBean
// NOTE: stable bean name as might be used outside sleuth
HttpTracing httpTracing(
Tracing tracing,
SkipPatternProvider provider) {
HttpTracing httpTracing(Tracing tracing, SkipPatternProvider provider) {
HttpSampler serverSampler = combineUserProvidedSamplerWithSkipPatternSampler(
provider);
return HttpTracing.newBuilder(tracing)
.clientParser(this.clientParser)
.serverParser(this.serverParser)
.clientSampler(this.clientSampler)
.serverSampler(serverSampler)
.build();
return HttpTracing.newBuilder(tracing).clientParser(this.clientParser)
.serverParser(this.serverParser).clientSampler(this.clientSampler)
.serverSampler(serverSampler).build();
}
private HttpSampler combineUserProvidedSamplerWithSkipPatternSampler(
@@ -87,12 +91,12 @@ public class TraceHttpAutoConfiguration {
}
@Bean
@ConditionalOnProperty(name = "spring.sleuth.http.legacy.enabled",
havingValue = "false", matchIfMissing = true)
@ConditionalOnProperty(name = "spring.sleuth.http.legacy.enabled", havingValue = "false", matchIfMissing = true)
@ConditionalOnMissingBean
HttpClientParser httpClientParser(ErrorParser errorParser) {
return new HttpClientParser() {
@Override protected ErrorParser errorParser() {
@Override
protected ErrorParser errorParser() {
return errorParser;
}
};
@@ -100,13 +104,13 @@ public class TraceHttpAutoConfiguration {
@Bean
@ConditionalOnProperty(name = "spring.sleuth.http.legacy.enabled", havingValue = "true")
HttpServerParser sleuthHttpServerParser(TraceKeys traceKeys, ErrorParser errorParser) {
HttpServerParser sleuthHttpServerParser(TraceKeys traceKeys,
ErrorParser errorParser) {
return new SleuthHttpServerParser(traceKeys, errorParser);
}
@Bean
@ConditionalOnProperty(name = "spring.sleuth.http.legacy.enabled",
havingValue = "false", matchIfMissing = true)
@ConditionalOnProperty(name = "spring.sleuth.http.legacy.enabled", havingValue = "false", matchIfMissing = true)
@ConditionalOnMissingBean
HttpServerParser defaultHttpServerParser() {
return new HttpServerParser();
@@ -117,43 +121,69 @@ public class TraceHttpAutoConfiguration {
HttpSampler sleuthClientSampler(SleuthWebProperties sleuthWebProperties) {
return new PathMatchingHttpSampler(sleuthWebProperties);
}
}
/**
* Composite Http Sampler.
*
* @author Adrian Cole
*/
class CompositeHttpSampler extends HttpSampler {
private final HttpSampler left, right;
private final HttpSampler left;
private final HttpSampler right;
CompositeHttpSampler(HttpSampler left, HttpSampler right) {
this.left = left;
this.right = right;
}
@Override public <Req> Boolean trySample(HttpAdapter<Req, ?> adapter, Req request) {
@Override
public <Req> Boolean trySample(HttpAdapter<Req, ?> adapter, Req request) {
// If either decision is false, return false
Boolean leftDecision = this.left.trySample(adapter, request);
if (Boolean.FALSE.equals(leftDecision)) return false;
if (Boolean.FALSE.equals(leftDecision)) {
return false;
}
Boolean rightDecision = this.right.trySample(adapter, request);
if (Boolean.FALSE.equals(rightDecision)) return false;
if (Boolean.FALSE.equals(rightDecision)) {
return false;
}
// If either decision is null, return the other
if (leftDecision == null) return rightDecision;
if (rightDecision == null) return leftDecision;
if (leftDecision == null) {
return rightDecision;
}
if (rightDecision == null) {
return leftDecision;
}
// Neither are null and at least one is true
return leftDecision && rightDecision;
}
}
/**
* Http Sampler that looks at paths.
*
* @author Marcin Grzejszczak
*/
class PathMatchingHttpSampler extends HttpSampler {
private final SleuthWebProperties properties;
PathMatchingHttpSampler(SleuthWebProperties properties) {
this.properties = properties;
}
@Override public <Req> Boolean trySample(HttpAdapter<Req, ?> adapter, Req request) {
@Override
public <Req> Boolean trySample(HttpAdapter<Req, ?> adapter, Req request) {
String path = adapter.path(request);
if (path == null) {
return null;
}
return path.matches(this.properties.getClient().getSkipPattern()) ? false : null;
}
}
}

View File

@@ -22,12 +22,10 @@ import java.util.LinkedHashSet;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Well-known {@link brave.Span#tag(String, String) span tag} keys.
* With the deprecation we only left the option to pass a list of
* HTTP request headers that will be set as tags
* Well-known {@link brave.Span#tag(String, String) span tag} keys. With the deprecation
* we only left the option to pass a list of HTTP request headers that will be set as tags
*
* @since 1.0.0
*
* @deprecated the Brave's defaults are suggested to be used
*/
@ConfigurationProperties("spring.sleuth.keys")
@@ -62,16 +60,18 @@ class TraceKeys {
return this.prefix;
}
public Collection<String> getHeaders() {
return this.headers;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public Collection<String> getHeaders() {
return this.headers;
}
public void setHeaders(Collection<String> headers) {
this.headers = headers;
}
}
}

View File

@@ -17,6 +17,7 @@
package org.springframework.cloud.sleuth.instrument.web;
import java.util.Collections;
import javax.servlet.http.HttpServletRequest;
import brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor;
@@ -30,28 +31,31 @@ import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerMapping;
/**
* Bean post processor that wraps Spring Data REST Controllers in named Spans
* Bean post processor that wraps Spring Data REST Controllers in named Spans.
*
* @author Marcin Grzejszczak
* @since 1.0.3
*/
class TraceSpringDataBeanPostProcessor implements BeanPostProcessor {
private static final Log log = LogFactory.getLog(TraceSpringDataBeanPostProcessor.class);
private static final Log log = LogFactory
.getLog(TraceSpringDataBeanPostProcessor.class);
private final ApplicationContext applicationContext;
public TraceSpringDataBeanPostProcessor(ApplicationContext applicationContext) {
TraceSpringDataBeanPostProcessor(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof DelegatingHandlerMapping && !(bean instanceof TraceDelegatingHandlerMapping)) {
if (bean instanceof DelegatingHandlerMapping
&& !(bean instanceof TraceDelegatingHandlerMapping)) {
if (log.isDebugEnabled()) {
log.debug("Wrapping bean [" + beanName + "] of type [" + bean.getClass().getSimpleName() +
"] in its trace representation");
log.debug("Wrapping bean [" + beanName + "] of type ["
+ bean.getClass().getSimpleName()
+ "] in its trace representation");
}
return new TraceDelegatingHandlerMapping((DelegatingHandlerMapping) bean,
this.applicationContext);
@@ -68,9 +72,10 @@ class TraceSpringDataBeanPostProcessor implements BeanPostProcessor {
private static class TraceDelegatingHandlerMapping extends DelegatingHandlerMapping {
private final DelegatingHandlerMapping delegate;
private final ApplicationContext applicationContext;
public TraceDelegatingHandlerMapping(DelegatingHandlerMapping delegate,
TraceDelegatingHandlerMapping(DelegatingHandlerMapping delegate,
ApplicationContext beanFactory) {
super(Collections.<HandlerMapping>emptyList());
this.delegate = delegate;
@@ -85,12 +90,16 @@ class TraceSpringDataBeanPostProcessor implements BeanPostProcessor {
@Override
public HandlerExecutionChain getHandler(HttpServletRequest request)
throws Exception {
HandlerExecutionChain handlerExecutionChain = this.delegate.getHandler(request);
HandlerExecutionChain handlerExecutionChain = this.delegate
.getHandler(request);
if (handlerExecutionChain == null) {
return null;
}
handlerExecutionChain.addInterceptor(this.applicationContext.getBean(SpanCustomizingAsyncHandlerInterceptor.class));
handlerExecutionChain.addInterceptor(this.applicationContext
.getBean(SpanCustomizingAsyncHandlerInterceptor.class));
return handlerExecutionChain;
}
}
}

View File

@@ -34,29 +34,25 @@ import org.springframework.web.context.request.async.WebAsyncTask;
* Aspect that adds tracing to
* <p/>
* <ul>
* <li>{@code RestController} annotated classes
* with public {@link Callable} methods</li>
* <li>{@code RestController} annotated classes with public {@link Callable} methods</li>
* <li>{@link org.springframework.stereotype.Controller} annotated classes with public
* {@link Callable} methods</li>
* <li>{@link org.springframework.stereotype.Controller} or
* {@code RestController} annotated classes with
* public {@link WebAsyncTask} methods</li>
* <li>{@link org.springframework.stereotype.Controller} or {@code RestController}
* annotated classes with public {@link WebAsyncTask} methods</li>
* </ul>
* <p/>
* For controllers an around aspect is created that wraps the {@link Callable#call()}
* method execution in {@link TraceCallable}
* <p/>
*
* This aspect will continue a span created by the TracingFilter. It will not create
* a new span - since the one in TracingFilter will wait until processing has been
* finished
* This aspect will continue a span created by the TracingFilter. It will not create a new
* span - since the one in TracingFilter will wait until processing has been finished
*
* @author Tomasz Nurkewicz, 4financeIT
* @author Michal Chmielarz, 4financeIT
* @author Marcin Grzejszczak
* @author Spencer Gibb
* @since 1.0.0
*
* @see org.springframework.stereotype.Controller
* @see org.springframework.web.client.RestOperations
*/
@@ -68,6 +64,7 @@ public class TraceWebAspect {
.getLog(TraceWebAspect.class);
private final Tracing tracing;
private final SpanNamer spanNamer;
public TraceWebAspect(Tracing tracing, SpanNamer spanNamer) {
@@ -76,22 +73,28 @@ public class TraceWebAspect {
}
@Pointcut("@within(org.springframework.web.bind.annotation.RestController)")
private void anyRestControllerAnnotated() { }// NOSONAR
private void anyRestControllerAnnotated() {
}// NOSONAR
@Pointcut("@within(org.springframework.stereotype.Controller)")
private void anyControllerAnnotated() { } // NOSONAR
private void anyControllerAnnotated() {
} // NOSONAR
@Pointcut("execution(public java.util.concurrent.Callable *(..))")
private void anyPublicMethodReturningCallable() { } // NOSONAR
private void anyPublicMethodReturningCallable() {
} // NOSONAR
@Pointcut("(anyRestControllerAnnotated() || anyControllerAnnotated()) && anyPublicMethodReturningCallable()")
private void anyControllerOrRestControllerWithPublicAsyncMethod() { } // NOSONAR
private void anyControllerOrRestControllerWithPublicAsyncMethod() {
} // NOSONAR
@Pointcut("execution(public org.springframework.web.context.request.async.WebAsyncTask *(..))")
private void anyPublicMethodReturningWebAsyncTask() { } // NOSONAR
private void anyPublicMethodReturningWebAsyncTask() {
} // NOSONAR
@Pointcut("(anyRestControllerAnnotated() || anyControllerAnnotated()) && anyPublicMethodReturningWebAsyncTask()")
private void anyControllerOrRestControllerWithPublicWebAsyncTaskMethod() { } // NOSONAR
private void anyControllerOrRestControllerWithPublicWebAsyncTaskMethod() {
} // NOSONAR
@Around("anyControllerOrRestControllerWithPublicAsyncMethod()")
@SuppressWarnings("unchecked")
@@ -108,7 +111,8 @@ public class TraceWebAspect {
}
@Around("anyControllerOrRestControllerWithPublicWebAsyncTaskMethod()")
public Object wrapWebAsyncTaskWithCorrelationId(ProceedingJoinPoint pjp) throws Throwable {
public Object wrapWebAsyncTaskWithCorrelationId(ProceedingJoinPoint pjp)
throws Throwable {
final WebAsyncTask<?> webAsyncTask = (WebAsyncTask<?>) pjp.proceed();
TraceContext currentSpan = this.tracing.currentTraceContext().get();
if (currentSpan == null) {
@@ -120,9 +124,10 @@ public class TraceWebAspect {
}
Field callableField = WebAsyncTask.class.getDeclaredField("callable");
callableField.setAccessible(true);
callableField.set(webAsyncTask, new TraceCallable<>(this.tracing, this.spanNamer,
webAsyncTask.getCallable()));
} catch (NoSuchFieldException ex) {
callableField.set(webAsyncTask, new TraceCallable<>(this.tracing,
this.spanNamer, webAsyncTask.getCallable()));
}
catch (NoSuchFieldException ex) {
log.warn("Cannot wrap webAsyncTask's callable with TraceCallable", ex);
}
return webAsyncTask;

View File

@@ -38,8 +38,8 @@ import org.springframework.util.StringUtils;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} that sets up common building blocks for both reactive
* and servlet based web application.
* Auto-configuration} that sets up common building blocks for both reactive and servlet
* based web application.
*
* @author Marcin Grzejszczak
* @since 1.0.0
@@ -51,33 +51,25 @@ import org.springframework.util.StringUtils;
@EnableConfigurationProperties(SleuthWebProperties.class)
public class TraceWebAutoConfiguration {
@Autowired(required = false) List<SingleSkipPattern> patterns = new ArrayList<>();
@Autowired(required = false)
List<SingleSkipPattern> patterns = new ArrayList<>();
@Bean
@ConditionalOnMissingBean
SkipPatternProvider sleuthSkipPatternProvider() {
return () -> Pattern.compile(this.patterns
.stream()
.map(SingleSkipPattern::skipPattern)
.filter(Optional::isPresent)
.map(Optional::get)
.map(Pattern::pattern)
.collect(Collectors.joining("|")));
return () -> Pattern
.compile(this.patterns.stream().map(SingleSkipPattern::skipPattern)
.filter(Optional::isPresent).map(Optional::get)
.map(Pattern::pattern).collect(Collectors.joining("|")));
}
@Configuration
@ConditionalOnClass(ManagementServerProperties.class)
protected static class ManagementSkipPatternProviderConfig {
@Bean
@ConditionalOnBean(ManagementServerProperties.class)
public SingleSkipPattern skipPatternForManagementServerProperties(
final ManagementServerProperties managementServerProperties) {
return () -> getPatternForManagementServerProperties(managementServerProperties);
}
/**
* Sets or appends {@link ManagementServerProperties#getServlet()#getContextPath()} to the skip
* Sets or appends
* {@link ManagementServerProperties#getServlet()#getContextPath()} to the skip
* pattern. If neither is available then sets the default one
*/
static Optional<Pattern> getPatternForManagementServerProperties(
@@ -88,22 +80,24 @@ public class TraceWebAutoConfiguration {
}
return Optional.empty();
}
@Bean
@ConditionalOnBean(ManagementServerProperties.class)
public SingleSkipPattern skipPatternForManagementServerProperties(
final ManagementServerProperties managementServerProperties) {
return () -> getPatternForManagementServerProperties(
managementServerProperties);
}
}
@Configuration
@ConditionalOnClass(ServerProperties.class)
protected static class ServerSkipPatternProviderConfig {
@Bean
@ConditionalOnBean(ServerProperties.class)
public SingleSkipPattern skipPatternForServerProperties(
final ServerProperties serverProperties) {
return () -> getPatternForServerProperties(serverProperties);
}
/**
* Sets or appends {@link ServerProperties#getServlet()#getContextPath()} to the skip
* pattern. If neither is available then sets the default one
* Sets or appends {@link ServerProperties#getServlet()#getContextPath()} to the
* skip pattern. If neither is available then sets the default one
*/
static Optional<Pattern> getPatternForServerProperties(
ServerProperties serverProperties) {
@@ -113,22 +107,21 @@ public class TraceWebAutoConfiguration {
}
return Optional.empty();
}
@Bean
@ConditionalOnBean(ServerProperties.class)
public SingleSkipPattern skipPatternForServerProperties(
final ServerProperties serverProperties) {
return () -> getPatternForServerProperties(serverProperties);
}
}
@Configuration
static class DefaultSkipPatternConfig {
@Bean
SingleSkipPattern defaultSkipPatternBean(SleuthWebProperties sleuthWebProperties) {
return () -> Optional.of(
Pattern.compile(
combinedPattern(sleuthWebProperties.getSkipPattern(),
sleuthWebProperties.getAdditionalSkipPattern())
)
);
}
private static String combinedPattern(String skipPattern, String additionalSkipPattern) {
private static String combinedPattern(String skipPattern,
String additionalSkipPattern) {
String pattern = skipPattern;
if (!StringUtils.hasText(skipPattern)) {
pattern = SleuthWebProperties.DEFAULT_SKIP_PATTERN;
@@ -138,7 +131,15 @@ public class TraceWebAutoConfiguration {
}
return pattern;
}
@Bean
SingleSkipPattern defaultSkipPatternBean(
SleuthWebProperties sleuthWebProperties) {
return () -> Optional.of(
Pattern.compile(combinedPattern(sleuthWebProperties.getSkipPattern(),
sleuthWebProperties.getAdditionalSkipPattern())));
}
}
}

View File

@@ -40,63 +40,63 @@ import reactor.core.publisher.Mono;
import reactor.util.context.Context;
/**
* A {@link WebFilter} that creates / continues / closes and detaches spans
* for a reactive web application.
* A {@link WebFilter} that creates / continues / closes and detaches spans for a reactive
* web application.
*
* @author Marcin Grzejszczak
* @since 2.0.0
*/
public final class TraceWebFilter implements WebFilter, Ordered {
private static final Log log = LogFactory.getLog(TraceWebFilter.class);
private static final String STATUS_CODE_KEY = "http.status_code";
static final String MVC_CONTROLLER_CLASS_KEY = "mvc.controller.class";
static final String MVC_CONTROLLER_METHOD_KEY = "mvc.controller.method";
protected static final String TRACE_REQUEST_ATTR = TraceWebFilter.class.getName()
+ ".TRACE";
private static final String TRACE_SPAN_WITHOUT_PARENT = TraceWebFilter.class.getName()
+ ".SPAN_WITH_NO_PARENT";
/**
* If you register your filter before the {@link TraceWebFilter} then you will not
* have the tracing context passed for you out of the box. That means that e.g. your
* logs will not get correlated.
*/
public static final int ORDER = TraceHttpAutoConfiguration.TRACING_FILTER_ORDER;
protected static final String TRACE_REQUEST_ATTR = TraceWebFilter.class.getName()
+ ".TRACE";
static final String MVC_CONTROLLER_CLASS_KEY = "mvc.controller.class";
static final String MVC_CONTROLLER_METHOD_KEY = "mvc.controller.method";
static final Propagation.Getter<HttpHeaders, String> GETTER = new Propagation.Getter<HttpHeaders, String>() {
static final Propagation.Getter<HttpHeaders, String> GETTER =
new Propagation.Getter<HttpHeaders, String>() {
@Override
public String get(HttpHeaders carrier, String key) {
return carrier.getFirst(key);
}
@Override public String get(HttpHeaders carrier, String key) {
return carrier.getFirst(key);
}
@Override public String toString() {
return "HttpHeaders::getFirst";
}
};
public static WebFilter create(BeanFactory beanFactory) {
return new TraceWebFilter(beanFactory);
}
Tracer tracer;
HttpServerHandler<ServerHttpRequest, ServerHttpResponse> handler;
TraceContext.Extractor<HttpHeaders> extractor;
SleuthWebProperties webProperties;
@Override
public String toString() {
return "HttpHeaders::getFirst";
}
};
private static final Log log = LogFactory.getLog(TraceWebFilter.class);
private static final String STATUS_CODE_KEY = "http.status_code";
private static final String TRACE_SPAN_WITHOUT_PARENT = TraceWebFilter.class.getName()
+ ".SPAN_WITH_NO_PARENT";
private final BeanFactory beanFactory;
Tracer tracer;
HttpServerHandler<ServerHttpRequest, ServerHttpResponse> handler;
TraceContext.Extractor<HttpHeaders> extractor;
SleuthWebProperties webProperties;
TraceWebFilter(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public static WebFilter create(BeanFactory beanFactory) {
return new TraceWebFilter(beanFactory);
}
@SuppressWarnings("unchecked")
HttpServerHandler<ServerHttpRequest, ServerHttpResponse> handler() {
if (this.handler == null) {
this.handler = HttpServerHandler
.create(this.beanFactory.getBean(HttpTracing.class),
new TraceWebFilter.HttpAdapter());
this.handler = HttpServerHandler.create(
this.beanFactory.getBean(HttpTracing.class),
new TraceWebFilter.HttpAdapter());
}
return this.handler;
}
@@ -110,8 +110,8 @@ public final class TraceWebFilter implements WebFilter, Ordered {
TraceContext.Extractor<HttpHeaders> extractor() {
if (this.extractor == null) {
this.extractor = this.beanFactory.getBean(HttpTracing.class)
.tracing().propagation().extractor(GETTER);
this.extractor = this.beanFactory.getBean(HttpTracing.class).tracing()
.propagation().extractor(GETTER);
}
return this.extractor;
}
@@ -123,7 +123,8 @@ public final class TraceWebFilter implements WebFilter, Ordered {
return this.webProperties;
}
@Override public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
if (tracer().currentSpan() != null) {
// clear any previous trace
tracer().withSpanInScope(null);
@@ -134,62 +135,63 @@ public final class TraceWebFilter implements WebFilter, Ordered {
}
Span spanFromAttribute = getSpanFromAttribute(exchange);
final String CONTEXT_ERROR = "sleuth.webfilter.context.error";
return chain
.filter(exchange)
.compose(f -> f.then(Mono.subscriberContext())
.onErrorResume(t -> Mono.subscriberContext()
.map(c -> c.put(CONTEXT_ERROR, t)))
return chain.filter(exchange)
.compose(f -> f.then(Mono.subscriberContext()).onErrorResume(
t -> Mono.subscriberContext().map(c -> c.put(CONTEXT_ERROR, t)))
.flatMap(c -> {
//reactivate span from context
// reactivate span from context
Span span = spanFromContext(c);
Mono<Void> continuation;
Throwable t = null;
if (c.hasKey(CONTEXT_ERROR)) {
t = c.get(CONTEXT_ERROR);
continuation = Mono.error(t);
} else {
}
else {
continuation = Mono.empty();
}
String httpRoute = null;
Object attribute = exchange
.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE);
Object attribute = exchange.getAttribute(
HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE);
if (attribute instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) attribute;
addClassMethodTag(handlerMethod, span);
addClassNameTag(handlerMethod, span);
Object pattern = exchange
.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
Object pattern = exchange.getAttribute(
HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
httpRoute = pattern != null ? pattern.toString() : "";
}
addResponseTagsForSpanWithoutParent(exchange, exchange.getResponse(), span);
addResponseTagsForSpanWithoutParent(exchange,
exchange.getResponse(), span);
DecoratedServerHttpResponse delegate = new DecoratedServerHttpResponse(
exchange.getResponse(), exchange.getRequest().getMethodValue(),
httpRoute);
exchange.getResponse(),
exchange.getRequest().getMethodValue(), httpRoute);
handler().handleSend(delegate, t, span);
if (log.isDebugEnabled()) {
log.debug("Handled send of " + span);
}
return continuation;
})
.subscriberContext(c -> {
}).subscriberContext(c -> {
Span span;
if (c.hasKey(Span.class)) {
Span parent = c.get(Span.class);
span = tracer()
.nextSpan(TraceContextOrSamplingFlags.create(parent.context()))
.start();
span = tracer().nextSpan(TraceContextOrSamplingFlags
.create(parent.context())).start();
if (log.isDebugEnabled()) {
log.debug("Found span in reactor context" + span);
}
} else {
}
else {
if (spanFromAttribute != null) {
span = spanFromAttribute;
if (log.isDebugEnabled()) {
log.debug("Found span in attribute " + span);
}
} else {
}
else {
span = handler().handleReceive(extractor(),
exchange.getRequest().getHeaders(), exchange.getRequest());
exchange.getRequest().getHeaders(),
exchange.getRequest());
if (log.isDebugEnabled()) {
log.debug("Handled receive of span " + span);
}
@@ -240,7 +242,8 @@ public final class TraceWebFilter implements WebFilter, Ordered {
String methodName = ((HandlerMethod) handler).getMethod().getName();
span.tag(MVC_CONTROLLER_METHOD_KEY, methodName);
if (log.isDebugEnabled()) {
log.debug("Adding a method tag with value [" + methodName + "] to a span " + span);
log.debug("Adding a method tag with value [" + methodName + "] to a span "
+ span);
}
}
}
@@ -249,16 +252,19 @@ public final class TraceWebFilter implements WebFilter, Ordered {
String className;
if (handler instanceof HandlerMethod) {
className = ((HandlerMethod) handler).getBeanType().getSimpleName();
} else {
}
else {
className = handler.getClass().getSimpleName();
}
if (log.isDebugEnabled()) {
log.debug("Adding a class tag with value [" + className + "] to a span " + span);
log.debug("Adding a class tag with value [" + className + "] to a span "
+ span);
}
span.tag(MVC_CONTROLLER_CLASS_KEY, className);
}
@Override public int getOrder() {
@Override
public int getOrder() {
return sleuthWebProperties().getFilterOrder();
}
@@ -266,47 +272,56 @@ public final class TraceWebFilter implements WebFilter, Ordered {
final String method, httpRoute;
DecoratedServerHttpResponse(ServerHttpResponse delegate, String method, String httpRoute) {
DecoratedServerHttpResponse(ServerHttpResponse delegate, String method,
String httpRoute) {
super(delegate);
this.method = method;
this.httpRoute = httpRoute;
}
}
static final class HttpAdapter
extends brave.http.HttpServerAdapter<ServerHttpRequest, ServerHttpResponse> {
@Override public String method(ServerHttpRequest request) {
@Override
public String method(ServerHttpRequest request) {
return request.getMethodValue();
}
@Override public String url(ServerHttpRequest request) {
@Override
public String url(ServerHttpRequest request) {
return request.getURI().toString();
}
@Override public String requestHeader(ServerHttpRequest request, String name) {
@Override
public String requestHeader(ServerHttpRequest request, String name) {
Object result = request.getHeaders().getFirst(name);
return result != null ? result.toString() : null;
}
@Override public Integer statusCode(ServerHttpResponse response) {
return response.getStatusCode() != null ?
response.getStatusCode().value() : null;
@Override
public Integer statusCode(ServerHttpResponse response) {
return response.getStatusCode() != null ? response.getStatusCode().value()
: null;
}
@Override public String methodFromResponse(ServerHttpResponse response) {
@Override
public String methodFromResponse(ServerHttpResponse response) {
if (response instanceof DecoratedServerHttpResponse) {
return ((DecoratedServerHttpResponse) response).method;
}
return null;
}
@Override public String route(ServerHttpResponse response) {
@Override
public String route(ServerHttpResponse response) {
if (response instanceof DecoratedServerHttpResponse) {
return ((DecoratedServerHttpResponse) response).httpRoute;
}
return null;
}
}
}
}
}

View File

@@ -28,16 +28,19 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
* MVC Adapter that adds the {@link SpanCustomizingAsyncHandlerInterceptor}
*
* @author Marcin Grzejszczak
*
* @since 1.0.3
*/
@Configuration
@Import(SpanCustomizingAsyncHandlerInterceptor.class)
class TraceWebMvcConfigurer implements WebMvcConfigurer {
@Autowired ApplicationContext applicationContext;
@Autowired
ApplicationContext applicationContext;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(this.applicationContext.getBean(SpanCustomizingAsyncHandlerInterceptor.class));
registry.addInterceptor(this.applicationContext
.getBean(SpanCustomizingAsyncHandlerInterceptor.class));
}
}

View File

@@ -16,6 +16,8 @@
package org.springframework.cloud.sleuth.instrument.web;
import javax.servlet.DispatcherType;
import brave.Tracing;
import brave.http.HttpTracing;
import brave.servlet.TracingFilter;
@@ -34,12 +36,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import static javax.servlet.DispatcherType.ASYNC;
import static javax.servlet.DispatcherType.ERROR;
import static javax.servlet.DispatcherType.FORWARD;
import static javax.servlet.DispatcherType.INCLUDE;
import static javax.servlet.DispatcherType.REQUEST;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} enables tracing to HTTP requests.
@@ -56,16 +52,16 @@ import static javax.servlet.DispatcherType.REQUEST;
@Import(SpanCustomizingAsyncHandlerInterceptor.class)
public class TraceWebServletAutoConfiguration {
/**
* Default filter order for the Http tracing filter.
*/
public static final int TRACING_FILTER_ORDER = TraceHttpAutoConfiguration.TRACING_FILTER_ORDER;
/**
* Nested config that configures Web MVC if it's present (without adding a runtime
* dependency to it)
*/
@Configuration
@ConditionalOnClass(WebMvcConfigurer.class)
@Import(TraceWebMvcConfigurer.class)
protected static class TraceWebMvcAutoConfiguration {
@Bean
@ConditionalOnClass(name = "org.springframework.data.rest.webmvc.support.DelegatingHandlerMapping")
public static TraceSpringDataBeanPostProcessor traceSpringDataBeanPostProcessor(
ApplicationContext applicationContext) {
return new TraceSpringDataBeanPostProcessor(applicationContext);
}
@Bean
@@ -74,26 +70,26 @@ public class TraceWebServletAutoConfiguration {
}
@Bean
@ConditionalOnClass(name = "org.springframework.data.rest.webmvc.support.DelegatingHandlerMapping")
public static TraceSpringDataBeanPostProcessor traceSpringDataBeanPostProcessor(
ApplicationContext applicationContext) {
return new TraceSpringDataBeanPostProcessor(applicationContext);
}
@Bean
public FilterRegistrationBean traceWebFilter(
TracingFilter tracingFilter, SleuthWebProperties webProperties) {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(tracingFilter);
filterRegistrationBean.setDispatcherTypes(ASYNC, ERROR, FORWARD, INCLUDE, REQUEST);
public FilterRegistrationBean traceWebFilter(TracingFilter tracingFilter,
SleuthWebProperties webProperties) {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(
tracingFilter);
filterRegistrationBean.setDispatcherTypes(DispatcherType.ASYNC,
DispatcherType.ERROR, DispatcherType.FORWARD, DispatcherType.INCLUDE,
DispatcherType.REQUEST);
filterRegistrationBean.setOrder(webProperties.getFilterOrder());
return filterRegistrationBean;
}
@Bean
@ConditionalOnProperty(value = "spring.sleuth.web.exceptionThrowingFilterEnabled", matchIfMissing = true)
public FilterRegistrationBean exceptionThrowingFilter(SleuthWebProperties webProperties) {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new ExceptionLoggingFilter());
filterRegistrationBean.setDispatcherTypes(ASYNC, ERROR, FORWARD, INCLUDE, REQUEST);
public FilterRegistrationBean exceptionThrowingFilter(
SleuthWebProperties webProperties) {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(
new ExceptionLoggingFilter());
filterRegistrationBean.setDispatcherTypes(DispatcherType.ASYNC,
DispatcherType.ERROR, DispatcherType.FORWARD, DispatcherType.INCLUDE,
DispatcherType.REQUEST);
filterRegistrationBean.setOrder(webProperties.getFilterOrder());
return filterRegistrationBean;
}
@@ -103,4 +99,16 @@ public class TraceWebServletAutoConfiguration {
public TracingFilter tracingFilter(HttpTracing tracing) {
return (TracingFilter) TracingFilter.create(tracing);
}
/**
* Nested config that configures Web MVC if it's present (without adding a runtime
* dependency to it).
*/
@Configuration
@ConditionalOnClass(WebMvcConfigurer.class)
@Import(TraceWebMvcConfigurer.class)
protected static class TraceWebMvcAutoConfiguration {
}
}

View File

@@ -25,14 +25,15 @@ import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
/**
* Helper annotation to enable Sleuth web client
* Helper annotation to enable Sleuth web client.
*
* @author Marcin Grzejszczak
* @since 1.0.11
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD})
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@ConditionalOnProperty(value = "spring.sleuth.web.client.enabled", matchIfMissing = true)
@interface SleuthWebClientEnabled {
}

View File

@@ -16,11 +16,12 @@
package org.springframework.cloud.sleuth.instrument.web.client;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.annotation.PostConstruct;
import brave.http.HttpTracing;
import brave.spring.web.TracingAsyncClientHttpRequestInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
@@ -36,9 +37,9 @@ import org.springframework.http.client.AsyncClientHttpRequestInterceptor;
import org.springframework.web.client.AsyncRestTemplate;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration}
* enables span information propagation for {@link AsyncClientHttpRequestFactory} and
* {@link AsyncRestTemplate}
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} enables span information propagation for
* {@link AsyncClientHttpRequestFactory} and {@link AsyncRestTemplate}
*
* @author Marcin Grzejszczak
* @since 1.0.0
@@ -55,8 +56,10 @@ public class TraceWebAsyncClientAutoConfiguration {
static class AsyncRestTemplateConfig {
@Bean
public TracingAsyncClientHttpRequestInterceptor asyncTracingClientHttpRequestInterceptor(HttpTracing httpTracing) {
return (TracingAsyncClientHttpRequestInterceptor) TracingAsyncClientHttpRequestInterceptor.create(httpTracing);
public TracingAsyncClientHttpRequestInterceptor asyncTracingClientHttpRequestInterceptor(
HttpTracing httpTracing) {
return (TracingAsyncClientHttpRequestInterceptor) TracingAsyncClientHttpRequestInterceptor
.create(httpTracing);
}
@Configuration
@@ -79,6 +82,9 @@ public class TraceWebAsyncClientAutoConfiguration {
}
}
}
}
}
}

View File

@@ -96,23 +96,32 @@ public class TraceWebClientAutoConfiguration {
static class RestTemplateConfig {
@Bean
public TracingClientHttpRequestInterceptor tracingClientHttpRequestInterceptor(HttpTracing httpTracing) {
return (TracingClientHttpRequestInterceptor) TracingClientHttpRequestInterceptor.create(httpTracing);
public TracingClientHttpRequestInterceptor tracingClientHttpRequestInterceptor(
HttpTracing httpTracing) {
return (TracingClientHttpRequestInterceptor) TracingClientHttpRequestInterceptor
.create(httpTracing);
}
@Configuration
protected static class TraceInterceptorConfiguration {
@Autowired private TracingClientHttpRequestInterceptor clientInterceptor;
@Autowired
private TracingClientHttpRequestInterceptor clientInterceptor;
@Bean @Order RestTemplateCustomizer traceRestTemplateCustomizer() {
@Bean
static TraceRestTemplateBeanPostProcessor traceRestTemplateBPP(
ListableBeanFactory beanFactory) {
return new TraceRestTemplateBeanPostProcessor(beanFactory);
}
@Bean
@Order
RestTemplateCustomizer traceRestTemplateCustomizer() {
return new TraceRestTemplateCustomizer(this.clientInterceptor);
}
@Bean static TraceRestTemplateBeanPostProcessor traceRestTemplateBPP(ListableBeanFactory beanFactory) {
return new TraceRestTemplateBeanPostProcessor(beanFactory);
}
}
}
@Configuration
@@ -124,6 +133,7 @@ public class TraceWebClientAutoConfiguration {
HttpClientBuilder traceHttpClientBuilder(HttpTracing httpTracing) {
return TracingHttpClientBuilder.create(httpTracing);
}
}
@Configuration
@@ -135,41 +145,51 @@ public class TraceWebClientAutoConfiguration {
HttpAsyncClientBuilder traceHttpAsyncClientBuilder(HttpTracing httpTracing) {
return TracingHttpAsyncClientBuilder.create(httpTracing);
}
}
@ConditionalOnClass(WebClient.class)
static class WebClientConfig {
@Bean static TraceWebClientBeanPostProcessor traceWebClientBeanPostProcessor(BeanFactory beanFactory) {
@Bean
static TraceWebClientBeanPostProcessor traceWebClientBeanPostProcessor(
BeanFactory beanFactory) {
return new TraceWebClientBeanPostProcessor(beanFactory);
}
}
@Configuration
@ConditionalOnClass(HttpClient.class)
static class NettyConfiguration {
@Bean
public NettyAspect traceNetyAspect(HttpTracing httpTracing) {
return new NettyAspect(httpTracing);
}
}
@Configuration
@ConditionalOnClass({ UserInfoRestTemplateCustomizer.class, OAuth2RestTemplate.class })
@ConditionalOnClass({ UserInfoRestTemplateCustomizer.class,
OAuth2RestTemplate.class })
protected static class TraceOAuthConfiguration {
@Bean
UserInfoRestTemplateCustomizerBPP userInfoRestTemplateCustomizerBeanPostProcessor(BeanFactory beanFactory) {
UserInfoRestTemplateCustomizerBPP userInfoRestTemplateCustomizerBeanPostProcessor(
BeanFactory beanFactory) {
return new UserInfoRestTemplateCustomizerBPP(beanFactory);
}
@Bean
@ConditionalOnMissingBean
UserInfoRestTemplateCustomizer traceUserInfoRestTemplateCustomizer(BeanFactory beanFactory) {
UserInfoRestTemplateCustomizer traceUserInfoRestTemplateCustomizer(
BeanFactory beanFactory) {
return new TraceUserInfoRestTemplateCustomizer(beanFactory);
}
private static class UserInfoRestTemplateCustomizerBPP implements BeanPostProcessor {
private static class UserInfoRestTemplateCustomizerBPP
implements BeanPostProcessor {
private final BeanFactory beanFactory;
@@ -178,8 +198,8 @@ public class TraceWebClientAutoConfiguration {
}
@Override
public Object postProcessBeforeInitialization(Object bean,
String beanName) throws BeansException {
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
@@ -187,17 +207,21 @@ public class TraceWebClientAutoConfiguration {
public Object postProcessAfterInitialization(final Object bean,
String beanName) throws BeansException {
final BeanFactory beanFactory = this.beanFactory;
if (bean instanceof UserInfoRestTemplateCustomizer &&
!(bean instanceof TraceUserInfoRestTemplateCustomizer)) {
if (bean instanceof UserInfoRestTemplateCustomizer
&& !(bean instanceof TraceUserInfoRestTemplateCustomizer)) {
return new TraceUserInfoRestTemplateCustomizer(beanFactory, bean);
}
return bean;
}
}
}
}
class RestTemplateInterceptorInjector {
private final ClientHttpRequestInterceptor interceptor;
RestTemplateInterceptorInjector(ClientHttpRequestInterceptor interceptor) {
@@ -215,14 +239,14 @@ class RestTemplateInterceptorInjector {
}
private boolean hasTraceInterceptor(RestTemplate restTemplate) {
for (ClientHttpRequestInterceptor interceptor : restTemplate
.getInterceptors()) {
for (ClientHttpRequestInterceptor interceptor : restTemplate.getInterceptors()) {
if (interceptor instanceof TracingClientHttpRequestInterceptor) {
return true;
}
}
return false;
}
}
class TraceRestTemplateCustomizer implements RestTemplateCustomizer {
@@ -233,10 +257,11 @@ class TraceRestTemplateCustomizer implements RestTemplateCustomizer {
this.interceptor = interceptor;
}
@Override public void customize(RestTemplate restTemplate) {
new RestTemplateInterceptorInjector(this.interceptor)
.inject(restTemplate);
@Override
public void customize(RestTemplate restTemplate) {
new RestTemplateInterceptorInjector(this.interceptor).inject(restTemplate);
}
}
class TraceRestTemplateBeanPostProcessor implements BeanPostProcessor {
@@ -247,14 +272,16 @@ class TraceRestTemplateBeanPostProcessor implements BeanPostProcessor {
this.beanFactory = beanFactory;
}
@Override public Object postProcessBeforeInitialization(Object bean, String beanName)
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
@Override public Object postProcessAfterInitialization(Object bean, String beanName)
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof RestTemplate) {
if (bean instanceof RestTemplate) {
RestTemplate rt = (RestTemplate) bean;
new RestTemplateInterceptorInjector(interceptor()).inject(rt);
}
@@ -270,23 +297,27 @@ class TraceRestTemplateBeanPostProcessor implements BeanPostProcessor {
class LazyTracingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
private final BeanFactory beanFactory;
private TracingClientHttpRequestInterceptor interceptor;
public LazyTracingClientHttpRequestInterceptor(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override public ClientHttpResponse intercept(HttpRequest request, byte[] body,
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException {
return interceptor().intercept(request, body, execution);
}
private TracingClientHttpRequestInterceptor interceptor() {
if (this.interceptor == null) {
this.interceptor = this.beanFactory.getBean(TracingClientHttpRequestInterceptor.class);
this.interceptor = this.beanFactory
.getBean(TracingClientHttpRequestInterceptor.class);
}
return this.interceptor;
}
}
@Aspect
@@ -300,11 +331,13 @@ class NettyAspect {
@Pointcut("execution(public * reactor.netty.http.client.HttpClient.RequestSender.send(..)) && args(function)")
private void anyHttpClientRequestSending(
BiFunction<? super HttpClientRequest,? super NettyOutbound,? extends Publisher<Void>> function) { } // NOSONAR
BiFunction<? super HttpClientRequest, ? super NettyOutbound, ? extends Publisher<Void>> function) {
} // NOSONAR
@Around("anyHttpClientRequestSending(function)")
public Object wrapHttpClientRequestSending(ProceedingJoinPoint pjp,
BiFunction<? super HttpClientRequest,? super NettyOutbound,? extends Publisher<Void>> function) throws Throwable {
BiFunction<? super HttpClientRequest, ? super NettyOutbound, ? extends Publisher<Void>> function)
throws Throwable {
return Mono.defer(() -> {
try {
return this.instrumentation.wrapHttpClientRequestSending(pjp, function);
@@ -314,37 +347,37 @@ class NettyAspect {
}
});
}
}
class TracingHttpClientInstrumentation {
private static final Log log = LogFactory.getLog(TracingHttpClientInstrumentation.class);
static final Propagation.Setter<HttpHeaders, String> SETTER = new Propagation.Setter<HttpHeaders, String>() {
@Override public void put(HttpHeaders carrier, String key, String value) {
@Override
public void put(HttpHeaders carrier, String key, String value) {
if (!carrier.contains(key)) {
carrier.add(key, value);
}
}
@Override public String toString() {
@Override
public String toString() {
return "HttpHeaders::add";
}
};
static final Propagation.Getter<HttpHeaders, String> GETTER = new Propagation.Getter<HttpHeaders, String>() {
@Override public String get(HttpHeaders carrier, String key) {
@Override
public String get(HttpHeaders carrier, String key) {
return carrier.get(key);
}
@Override public String toString() {
@Override
public String toString() {
return "HttpHeaders::get";
}
};
static TracingHttpClientInstrumentation create(HttpTracing httpTracing) {
return new TracingHttpClientInstrumentation(httpTracing);
}
private static final Log log = LogFactory
.getLog(TracingHttpClientInstrumentation.class);
final Tracer tracer;
final HttpClientHandler<HttpClientRequest, HttpClientResponse> handler;
final TraceContext.Injector<HttpHeaders> injector;
@@ -357,35 +390,45 @@ class TracingHttpClientInstrumentation {
this.httpTracing = httpTracing;
}
static TracingHttpClientInstrumentation create(HttpTracing httpTracing) {
return new TracingHttpClientInstrumentation(httpTracing);
}
Mono<HttpClientResponse> wrapHttpClientRequestSending(ProceedingJoinPoint pjp,
BiFunction<? super HttpClientRequest,? super NettyOutbound,? extends Publisher<Void>> function) throws Throwable {
BiFunction<? super HttpClientRequest, ? super NettyOutbound, ? extends Publisher<Void>> function)
throws Throwable {
// add headers and set CS
final Span currentSpan = this.tracer.currentSpan();
final AtomicReference<Span> span = new AtomicReference<>();
BiFunction<HttpClientRequest, NettyOutbound, Publisher<Void>> combinedFunction =
(req, nettyOutbound) -> {
try (Tracer.SpanInScope spanInScope = this.tracer.withSpanInScope(currentSpan)) {
io.netty.handler.codec.http.HttpHeaders originalHeaders = req
.requestHeaders().copy();
io.netty.handler.codec.http.HttpHeaders tracedHeaders = req
.requestHeaders();
span.set(this.handler.handleSend(this.injector, tracedHeaders, req));
if (log.isDebugEnabled()) {
log.debug("Handled send of " + span.get());
}
io.netty.handler.codec.http.HttpHeaders addedHeaders = tracedHeaders.copy();
originalHeaders.forEach(header -> addedHeaders.remove(header.getKey()));
try (Tracer.SpanInScope clientInScope = this.tracer.withSpanInScope(span.get())) {
if (log.isDebugEnabled()) {
log.debug("Created a new client span for Netty client");
}
return handle(function, new TracedHttpClientRequest(req, addedHeaders), nettyOutbound);
}
BiFunction<HttpClientRequest, NettyOutbound, Publisher<Void>> combinedFunction = (
req, nettyOutbound) -> {
try (Tracer.SpanInScope spanInScope = this.tracer
.withSpanInScope(currentSpan)) {
io.netty.handler.codec.http.HttpHeaders originalHeaders = req
.requestHeaders().copy();
io.netty.handler.codec.http.HttpHeaders tracedHeaders = req
.requestHeaders();
span.set(this.handler.handleSend(this.injector, tracedHeaders, req));
if (log.isDebugEnabled()) {
log.debug("Handled send of " + span.get());
}
io.netty.handler.codec.http.HttpHeaders addedHeaders = tracedHeaders
.copy();
originalHeaders.forEach(header -> addedHeaders.remove(header.getKey()));
try (Tracer.SpanInScope clientInScope = this.tracer
.withSpanInScope(span.get())) {
if (log.isDebugEnabled()) {
log.debug("Created a new client span for Netty client");
}
};
return handle(function,
new TracedHttpClientRequest(req, addedHeaders),
nettyOutbound);
}
}
};
// run
Mono<HttpClientResponse> responseMono =
(Mono<HttpClientResponse>) pjp.proceed(new Object[] { combinedFunction });
Mono<HttpClientResponse> responseMono = (Mono<HttpClientResponse>) pjp
.proceed(new Object[] { combinedFunction });
// get response
return responseMono.doOnSuccessOrError((httpClientResponse, throwable) -> {
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span.get())) {
@@ -400,96 +443,6 @@ class TracingHttpClientInstrumentation {
});
}
/**
* The `org.springframework.cloud.gateway.filter.NettyRoutingFilter` in SC Gateway
* is adding only these headers that were set when the request came in. That means
* that adding any additional headers (via instrumentation) is completely ignored.
* That's why we're wrapping the `HttpClientRequest` in such a wrapper that
* when `setHeaders` is called (that clears any current headers), will also add
* the tracing headers
*/
static class TracedHttpClientRequest implements HttpClientRequest {
private HttpClientRequest delegate;
private final io.netty.handler.codec.http.HttpHeaders addedHeaders;
TracedHttpClientRequest(HttpClientRequest delegate, HttpHeaders addedHeaders) {
this.delegate = delegate;
this.addedHeaders = addedHeaders;
}
@Override public HttpClientRequest addCookie(Cookie cookie) {
this.delegate = this.delegate.addCookie(cookie);
return this;
}
@Override public HttpClientRequest addHeader(CharSequence name,
CharSequence value) {
this.delegate = this.delegate.addHeader(name, value);
return this;
}
@Override public boolean hasSentHeaders() {
return this.delegate.hasSentHeaders();
}
@Override public HttpClientRequest header(CharSequence name, CharSequence value) {
this.delegate = this.delegate.header(name, value);
return this;
}
@Override public HttpClientRequest headers(HttpHeaders headers) {
HttpHeaders copy = headers.copy();
copy.add(this.addedHeaders);
this.delegate = this.delegate.headers(copy);
return this;
}
@Override public boolean isFollowRedirect() {
return this.delegate.isFollowRedirect();
}
@Override public HttpClientRequest keepAlive(boolean keepAlive) {
this.delegate = this.delegate.keepAlive(keepAlive);
return this;
}
@Override public String[] redirectedFrom() {
return this.delegate.redirectedFrom();
}
@Override public HttpHeaders requestHeaders() {
return this.delegate.requestHeaders();
}
@Override public Map<CharSequence, Set<Cookie>> cookies() {
return this.delegate.cookies();
}
@Override public boolean isKeepAlive() {
return this.delegate.isKeepAlive();
}
@Override public boolean isWebsocket() {
return this.delegate.isWebsocket();
}
@Override public HttpMethod method() {
return this.delegate.method();
}
@Override public String path() {
return this.delegate.path();
}
@Override public String uri() {
return this.delegate.uri();
}
@Override public HttpVersion version() {
return this.delegate.version();
}
}
private Publisher<Void> handle(
BiFunction<? super HttpClientRequest, ? super NettyOutbound, ? extends Publisher<Void>> handler,
HttpClientRequest req, NettyOutbound nettyOutbound) {
@@ -499,31 +452,144 @@ class TracingHttpClientInstrumentation {
return nettyOutbound;
}
/**
* The `org.springframework.cloud.gateway.filter.NettyRoutingFilter` in SC Gateway is
* adding only these headers that were set when the request came in. That means that
* adding any additional headers (via instrumentation) is completely ignored. That's
* why we're wrapping the `HttpClientRequest` in such a wrapper that when `setHeaders`
* is called (that clears any current headers), will also add the tracing headers
*/
static class TracedHttpClientRequest implements HttpClientRequest {
private final io.netty.handler.codec.http.HttpHeaders addedHeaders;
private HttpClientRequest delegate;
TracedHttpClientRequest(HttpClientRequest delegate, HttpHeaders addedHeaders) {
this.delegate = delegate;
this.addedHeaders = addedHeaders;
}
@Override
public HttpClientRequest addCookie(Cookie cookie) {
this.delegate = this.delegate.addCookie(cookie);
return this;
}
@Override
public HttpClientRequest addHeader(CharSequence name, CharSequence value) {
this.delegate = this.delegate.addHeader(name, value);
return this;
}
@Override
public boolean hasSentHeaders() {
return this.delegate.hasSentHeaders();
}
@Override
public HttpClientRequest header(CharSequence name, CharSequence value) {
this.delegate = this.delegate.header(name, value);
return this;
}
@Override
public HttpClientRequest headers(HttpHeaders headers) {
HttpHeaders copy = headers.copy();
copy.add(this.addedHeaders);
this.delegate = this.delegate.headers(copy);
return this;
}
@Override
public boolean isFollowRedirect() {
return this.delegate.isFollowRedirect();
}
@Override
public HttpClientRequest keepAlive(boolean keepAlive) {
this.delegate = this.delegate.keepAlive(keepAlive);
return this;
}
@Override
public String[] redirectedFrom() {
return this.delegate.redirectedFrom();
}
@Override
public HttpHeaders requestHeaders() {
return this.delegate.requestHeaders();
}
@Override
public Map<CharSequence, Set<Cookie>> cookies() {
return this.delegate.cookies();
}
@Override
public boolean isKeepAlive() {
return this.delegate.isKeepAlive();
}
@Override
public boolean isWebsocket() {
return this.delegate.isWebsocket();
}
@Override
public HttpMethod method() {
return this.delegate.method();
}
@Override
public String path() {
return this.delegate.path();
}
@Override
public String uri() {
return this.delegate.uri();
}
@Override
public HttpVersion version() {
return this.delegate.version();
}
}
static final class HttpAdapter
extends brave.http.HttpClientAdapter<HttpClientRequest, HttpClientResponse> {
@Override public String method(HttpClientRequest request) {
@Override
public String method(HttpClientRequest request) {
return request.method().name();
}
@Override public String url(HttpClientRequest request) {
@Override
public String url(HttpClientRequest request) {
return request.uri();
}
@Override public String requestHeader(HttpClientRequest request, String name) {
@Override
public String requestHeader(HttpClientRequest request, String name) {
Object result = request.requestHeaders().get(name);
return result != null ? result.toString() : "";
}
@Override public Integer statusCode(HttpClientResponse response) {
@Override
public Integer statusCode(HttpClientResponse response) {
return response.status().code();
}
}
}
class TraceUserInfoRestTemplateCustomizer implements UserInfoRestTemplateCustomizer {
private final BeanFactory beanFactory;
private final Object delegate;
TraceUserInfoRestTemplateCustomizer(BeanFactory beanFactory) {
@@ -536,12 +602,14 @@ class TraceUserInfoRestTemplateCustomizer implements UserInfoRestTemplateCustomi
this.delegate = bean;
}
@Override public void customize(OAuth2RestTemplate template) {
final TracingClientHttpRequestInterceptor interceptor =
this.beanFactory.getBean(TracingClientHttpRequestInterceptor.class);
@Override
public void customize(OAuth2RestTemplate template) {
final TracingClientHttpRequestInterceptor interceptor = this.beanFactory
.getBean(TracingClientHttpRequestInterceptor.class);
new RestTemplateInterceptorInjector(interceptor).inject(template);
if (this.delegate != null) {
((UserInfoRestTemplateCustomizer) this.delegate).customize(template);
}
}
}

View File

@@ -39,8 +39,8 @@ import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
/**
* {@link BeanPostProcessor} to wrap a {@link WebClient} instance into
* its trace representation
* {@link BeanPostProcessor} to wrap a {@link WebClient} instance into its trace
* representation
*
* @author Marcin Grzejszczak
* @since 2.0.0
@@ -53,20 +53,21 @@ class TraceWebClientBeanPostProcessor implements BeanPostProcessor {
this.beanFactory = beanFactory;
}
@Override public Object postProcessBeforeInitialization(Object bean, String beanName)
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
@Override public Object postProcessAfterInitialization(Object bean, String beanName)
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof WebClient) {
WebClient webClient = (WebClient) bean;
return webClient
.mutate()
.filters(addTraceExchangeFilterFunctionIfNotPresent())
.build();
} else if (bean instanceof WebClient.Builder) {
return webClient.mutate()
.filters(addTraceExchangeFilterFunctionIfNotPresent()).build();
}
else if (bean instanceof WebClient.Builder) {
WebClient.Builder webClientBuilder = (WebClient.Builder) bean;
return webClientBuilder.filters(addTraceExchangeFilterFunctionIfNotPresent());
}
@@ -75,46 +76,41 @@ class TraceWebClientBeanPostProcessor implements BeanPostProcessor {
private Consumer<List<ExchangeFilterFunction>> addTraceExchangeFilterFunctionIfNotPresent() {
return functions -> {
if (functions
.stream()
if (functions.stream()
.noneMatch(f -> f instanceof TraceExchangeFilterFunction)) {
functions.add(new TraceExchangeFilterFunction(this.beanFactory));
}
};
}
}
class TraceExchangeFilterFunction implements ExchangeFilterFunction {
private static final Log log = LogFactory.getLog(
TraceExchangeFilterFunction.class);
private static final String CLIENT_SPAN_KEY = "sleuth.webclient.clientSpan";
static final Propagation.Setter<ClientRequest.Builder, String> SETTER =
new Propagation.Setter<ClientRequest.Builder, String>() {
@Override public void put(ClientRequest.Builder carrier, String key, String value) {
carrier.header(key, value);
}
@Override public String toString() {
return "ClientRequest.Builder::header";
}
};
static final Propagation.Setter<ClientRequest.Builder, String> SETTER = new Propagation.Setter<ClientRequest.Builder, String>() {
@Override
public void put(ClientRequest.Builder carrier, String key, String value) {
carrier.header(key, value);
}
@Override
public String toString() {
return "ClientRequest.Builder::header";
}
};
static final Propagation.Getter<ClientRequest, String> GETTER = new Propagation.Getter<ClientRequest, String>() {
@Override public String get(ClientRequest carrier, String key) {
@Override
public String get(ClientRequest carrier, String key) {
return carrier.headers().getFirst(key);
}
@Override public String toString() {
@Override
public String toString() {
return "HttpHeaders::getFirst";
}
};
public static ExchangeFilterFunction create(BeanFactory beanFactory) {
return new TraceExchangeFilterFunction(beanFactory);
}
private static final Log log = LogFactory.getLog(TraceExchangeFilterFunction.class);
private static final String CLIENT_SPAN_KEY = "sleuth.webclient.clientSpan";
final BeanFactory beanFactory;
Tracer tracer;
HttpTracing httpTracing;
@@ -125,38 +121,43 @@ class TraceExchangeFilterFunction implements ExchangeFilterFunction {
this.beanFactory = beanFactory;
}
@Override public Mono<ClientResponse> filter(ClientRequest request,
ExchangeFunction next) {
public static ExchangeFilterFunction create(BeanFactory beanFactory) {
return new TraceExchangeFilterFunction(beanFactory);
}
@Override
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
final ClientRequest.Builder builder = ClientRequest.from(request);
Mono<ClientResponse> exchange = Mono
.defer(() -> next.exchange(builder.build()))
.cast(Object.class)
.onErrorResume(Mono::just)
.zipWith(Mono.subscriberContext())
.flatMap(anyAndContext -> {
Mono<ClientResponse> exchange = Mono.defer(() -> next.exchange(builder.build()))
.cast(Object.class).onErrorResume(Mono::just)
.zipWith(Mono.subscriberContext()).flatMap(anyAndContext -> {
Object any = anyAndContext.getT1();
Span clientSpan = anyAndContext.getT2().get(CLIENT_SPAN_KEY);
Mono<ClientResponse> continuation;
final Tracer.SpanInScope ws = tracer().withSpanInScope(clientSpan);
if (any instanceof Throwable) {
continuation = Mono.error((Throwable) any);
} else {
continuation = Mono.just((ClientResponse) any);
}
return continuation.doAfterSuccessOrError(
(clientResponse, throwable1) -> {
if (any instanceof Throwable) {
continuation = Mono.error((Throwable) any);
}
else {
continuation = Mono.just((ClientResponse) any);
}
return continuation
.doAfterSuccessOrError((clientResponse, throwable1) -> {
Throwable throwable = throwable1;
if (clientResponse == null || clientResponse.statusCode() == null) {
if (clientResponse == null
|| clientResponse.statusCode() == null) {
if (log.isDebugEnabled()) {
log.debug(
"No response was returned. Will close the span ["
+ clientSpan + "]");
}
handleReceive(clientSpan, ws, clientResponse, throwable);
handleReceive(clientSpan, ws, clientResponse,
throwable);
return;
}
boolean error = clientResponse.statusCode().is4xxClientError() ||
clientResponse.statusCode().is5xxServerError();
boolean error = clientResponse.statusCode()
.is4xxClientError()
|| clientResponse.statusCode().is5xxServerError();
if (error) {
if (log.isDebugEnabled()) {
log.debug(
@@ -164,27 +165,30 @@ class TraceExchangeFilterFunction implements ExchangeFilterFunction {
+ clientSpan + "]");
}
throwable = new RestClientException(
"Status code of the response is [" + clientResponse.statusCode()
.value() + "] and the reason is [" + clientResponse
.statusCode().getReasonPhrase() + "]");
"Status code of the response is ["
+ clientResponse.statusCode().value()
+ "] and the reason is ["
+ clientResponse.statusCode()
.getReasonPhrase()
+ "]");
}
handleReceive(clientSpan, ws, clientResponse, throwable);
});
})
.subscriberContext(c -> {
}).subscriberContext(c -> {
if (log.isDebugEnabled()) {
log.debug("Instrumenting WebClient call");
}
Span parent = c.getOrDefault(Span.class, null);
Span clientSpan = handler().handleSend(injector(), builder,
request, tracer().nextSpan());
Span clientSpan = handler().handleSend(injector(), builder, request,
tracer().nextSpan());
if (log.isDebugEnabled()) {
log.debug("Handled send of " + clientSpan);
}
if (parent == null) {
c = c.put(Span.class, clientSpan);
if (log.isDebugEnabled()) {
log.debug("Reactor Context got injected with the client span " + clientSpan);
log.debug("Reactor Context got injected with the client span "
+ clientSpan);
}
}
return c.put(CLIENT_SPAN_KEY, clientSpan);
@@ -201,8 +205,9 @@ class TraceExchangeFilterFunction implements ExchangeFilterFunction {
@SuppressWarnings("unchecked")
HttpClientHandler<ClientRequest, ClientResponse> handler() {
if (this.handler == null) {
this.handler = HttpClientHandler
.create(this.beanFactory.getBean(HttpTracing.class), new TraceExchangeFilterFunction.HttpAdapter());
this.handler = HttpClientHandler.create(
this.beanFactory.getBean(HttpTracing.class),
new TraceExchangeFilterFunction.HttpAdapter());
}
return this.handler;
}
@@ -223,31 +228,36 @@ class TraceExchangeFilterFunction implements ExchangeFilterFunction {
TraceContext.Injector<ClientRequest.Builder> injector() {
if (this.injector == null) {
this.injector = this.beanFactory.getBean(HttpTracing.class)
.tracing().propagation().injector(SETTER);
this.injector = this.beanFactory.getBean(HttpTracing.class).tracing()
.propagation().injector(SETTER);
}
return this.injector;
}
static final class HttpAdapter
extends brave.http.HttpClientAdapter<ClientRequest, ClientResponse> {
@Override public String method(ClientRequest request) {
@Override
public String method(ClientRequest request) {
return request.method().name();
}
@Override public String url(ClientRequest request) {
@Override
public String url(ClientRequest request) {
return request.url().toString();
}
@Override public String requestHeader(ClientRequest request, String name) {
@Override
public String requestHeader(ClientRequest request, String name) {
Object result = request.headers().getFirst(name);
return result != null ? result.toString() : null;
}
@Override public Integer statusCode(ClientResponse response) {
@Override
public Integer statusCode(ClientResponse response) {
return response.statusCode().value();
}
}
}

View File

@@ -25,7 +25,6 @@ import org.springframework.cloud.openfeign.FeignContext;
* Post processor that wraps Feign Context in its tracing representations.
*
* @author Marcin Grzejszczak
*
* @since 1.0.2
*/
final class FeignContextBeanPostProcessor implements BeanPostProcessor {
@@ -54,4 +53,5 @@ final class FeignContextBeanPostProcessor implements BeanPostProcessor {
private TraceFeignObjectWrapper traceFeignObjectWrapper() {
return new TraceFeignObjectWrapper(this.beanFactory);
}
}

View File

@@ -23,9 +23,15 @@ import feign.Request;
import feign.Response;
import org.springframework.beans.factory.BeanFactory;
class LazyClient implements Client {
/**
* Lazy implementation of the Feign Client.
*
* @author Marcin Grzejszczak
*/
class LazyClient implements Client {
private final BeanFactory beanFactory;
private final Client delegate;
private TraceFeignObjectWrapper wrapper;
@@ -35,8 +41,8 @@ class LazyClient implements Client {
this.delegate = delegate;
}
@Override public Response execute(Request request, Request.Options options)
throws IOException {
@Override
public Response execute(Request request, Request.Options options) throws IOException {
return ((Client) wrapper().wrap(this.delegate)).execute(request, options);
}
@@ -46,4 +52,5 @@ class LazyClient implements Client {
}
return this.wrapper;
}
}

View File

@@ -27,7 +27,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
/**
* Lazilly resolves the Trace Feign Client
* Lazilly resolves the Trace Feign Client.
*
* @author Marcin Grzejszczak
* @since 2.0.0
@@ -35,29 +35,30 @@ import org.springframework.beans.factory.BeanFactory;
class LazyTracingFeignClient implements Client {
private static final Log log = LogFactory.getLog(LazyTracingFeignClient.class);
private Client tracingFeignClient;
private HttpTracing httpTracing;
private final BeanFactory beanFactory;
private final Client delegate;
private Client tracingFeignClient;
private HttpTracing httpTracing;
LazyTracingFeignClient(BeanFactory beanFactory, Client delegate) {
this.beanFactory = beanFactory;
this.delegate = delegate;
}
@Override public Response execute(Request request, Request.Options options)
throws IOException {
@Override
public Response execute(Request request, Request.Options options) throws IOException {
if (log.isDebugEnabled()) {
log.debug("Sending a request via tracing feign client [" + tracingFeignClient() + "] "
+ "and the delegate [" + this.delegate + "]");
log.debug(
"Sending a request via tracing feign client [" + tracingFeignClient()
+ "] " + "and the delegate [" + this.delegate + "]");
}
return tracingFeignClient().execute(request, options);
}
private Client tracingFeignClient() {
if (this.tracingFeignClient == null) {
this.tracingFeignClient = TracingFeignClient.create(httpTracing(), this.delegate);
this.tracingFeignClient = TracingFeignClient.create(httpTracing(),
this.delegate);
}
return this.tracingFeignClient;
}
@@ -68,4 +69,5 @@ class LazyTracingFeignClient implements Client {
}
return this.httpTracing;
}
}

View File

@@ -22,9 +22,16 @@ import feign.Retryer;
/**
* This is essentially the same implementation of a Retryer that is in newer versions of
* Feign. For the 1.0.x stream we add it here.
*
* @author Ryan Baxter
*/
public class NeverRetry implements Retryer {
/**
* Default retry entry.
*/
public static final NeverRetry INSTANCE = new NeverRetry();
@Override
public void continueOrPropagate(RetryableException e) {
throw e;
@@ -35,5 +42,4 @@ public class NeverRetry implements Retryer {
return this;
}
public static final NeverRetry INSTANCE = new NeverRetry();
}

View File

@@ -23,10 +23,9 @@ import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* Post processor that wraps takes care of the OkHttp Feign Client instrumentation
* Post processor that wraps takes care of the OkHttp Feign Client instrumentation.
*
* @author Marcin Grzejszczak
*
* @since 1.1.3
*/
final class OkHttpFeignClientBeanPostProcessor implements BeanPostProcessor {
@@ -51,4 +50,5 @@ final class OkHttpFeignClientBeanPostProcessor implements BeanPostProcessor {
throws BeansException {
return bean;
}
}
}

Some files were not shown because too many files have changed in this diff Show More