Fixed a lot of checkstyle warnings'
This commit is contained in:
16
.editorconfig
Normal file
16
.editorconfig
Normal 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
0
.springformat
Normal file
4
pom.xml
4
pom.xml
@@ -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>
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
* @SpanName("custom-operation")
|
||||
* class CustomRunnable implements Runnable {
|
||||
* @Override
|
||||
* @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
|
||||
* @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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 "";
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 "";
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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 there’s 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 there’s 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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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:-}]");
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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{}";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
@@ -25,5 +25,7 @@ import java.util.regex.Pattern;
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public interface SkipPatternProvider {
|
||||
|
||||
Pattern skipPattern();
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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())));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user