Merge branch '2.2.x'
This commit is contained in:
@@ -57,9 +57,9 @@ public class SleuthMessagingProperties {
|
|||||||
/**
|
/**
|
||||||
* An array of patterns against which channel names will be matched.
|
* An array of patterns against which channel names will be matched.
|
||||||
* @see org.springframework.integration.config.GlobalChannelInterceptor#patterns()
|
* @see org.springframework.integration.config.GlobalChannelInterceptor#patterns()
|
||||||
* Defaults to any channel name not matching the Hystrix Stream channel name.
|
* Defaults to any channel name not matching the Hystrix Stream and functional Stream channel names.
|
||||||
*/
|
*/
|
||||||
private String[] patterns = new String[] { "!hystrixStreamOutput*", "*" };
|
private String[] patterns = new String[] { "!hystrixStreamOutput*", "*", "!channel*"};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable Spring Integration sleuth instrumentation.
|
* Enable Spring Integration sleuth instrumentation.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2019 the original author or authors.
|
* Copyright 2013-2020 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -59,8 +59,7 @@ import org.springframework.util.ClassUtils;
|
|||||||
*
|
*
|
||||||
* @author Marcin Grzejszczak
|
* @author Marcin Grzejszczak
|
||||||
*/
|
*/
|
||||||
public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
|
public final class TracingChannelInterceptor extends ChannelInterceptorAdapter implements ExecutorChannelInterceptor {
|
||||||
implements ExecutorChannelInterceptor {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of the class in Spring Cloud Stream that is a direct channel.
|
* Name of the class in Spring Cloud Stream that is a direct channel.
|
||||||
@@ -109,25 +108,22 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
|
|||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
TracingChannelInterceptor(Tracing tracing) {
|
TracingChannelInterceptor(Tracing tracing) {
|
||||||
this(tracing, MessageHeaderPropagation.INSTANCE,
|
this(tracing, MessageHeaderPropagation.INSTANCE, MessageHeaderPropagation.INSTANCE);
|
||||||
MessageHeaderPropagation.INSTANCE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TracingChannelInterceptor(Tracing tracing,
|
TracingChannelInterceptor(Tracing tracing, Propagation.Setter<MessageHeaderAccessor, String> setter,
|
||||||
Propagation.Setter<MessageHeaderAccessor, String> setter,
|
|
||||||
Propagation.Getter<MessageHeaderAccessor, String> getter) {
|
Propagation.Getter<MessageHeaderAccessor, String> getter) {
|
||||||
this.tracing = tracing;
|
this.tracing = tracing;
|
||||||
this.tracer = tracing.tracer();
|
this.tracer = tracing.tracer();
|
||||||
this.threadLocalSpan = ThreadLocalSpan.create(this.tracer);
|
this.threadLocalSpan = ThreadLocalSpan.create(this.tracer);
|
||||||
this.injector = tracing.propagation().injector(setter);
|
this.injector = tracing.propagation().injector(setter);
|
||||||
this.extractor = tracing.propagation().extractor(getter);
|
this.extractor = tracing.propagation().extractor(getter);
|
||||||
this.integrationObjectSupportPresent = ClassUtils.isPresent(
|
this.integrationObjectSupportPresent = ClassUtils
|
||||||
"org.springframework.integration.context.IntegrationObjectSupport", null);
|
.isPresent("org.springframework.integration.context.IntegrationObjectSupport", null);
|
||||||
this.hasDirectChannelClass = ClassUtils
|
this.hasDirectChannelClass = ClassUtils.isPresent("org.springframework.integration.channel.DirectChannel",
|
||||||
.isPresent("org.springframework.integration.channel.DirectChannel", null);
|
null);
|
||||||
this.directWithAttributesChannelClass = ClassUtils
|
this.directWithAttributesChannelClass = ClassUtils.isPresent(STREAM_DIRECT_CHANNEL, null)
|
||||||
.isPresent(STREAM_DIRECT_CHANNEL, null)
|
? ClassUtils.resolveClassName(STREAM_DIRECT_CHANNEL, null) : null;
|
||||||
? ClassUtils.resolveClassName(STREAM_DIRECT_CHANNEL, null) : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TracingChannelInterceptor create(Tracing tracing) {
|
public static TracingChannelInterceptor create(Tracing tracing) {
|
||||||
@@ -170,12 +166,11 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
|
|||||||
MessageHeaderAccessor headers = mutableHeaderAccessor(retrievedMessage);
|
MessageHeaderAccessor headers = mutableHeaderAccessor(retrievedMessage);
|
||||||
TraceContextOrSamplingFlags extracted = this.extractor.extract(headers);
|
TraceContextOrSamplingFlags extracted = this.extractor.extract(headers);
|
||||||
Span span = this.threadLocalSpan.next(extracted);
|
Span span = this.threadLocalSpan.next(extracted);
|
||||||
MessageHeaderPropagation.removeAnyTraceHeaders(headers,
|
MessageHeaderPropagation.removeAnyTraceHeaders(headers, this.tracing.propagation().keys());
|
||||||
this.tracing.propagation().keys());
|
|
||||||
this.injector.inject(span.context(), headers);
|
this.injector.inject(span.context(), headers);
|
||||||
if (!span.isNoop()) {
|
if (!span.isNoop()) {
|
||||||
span.kind(Span.Kind.PRODUCER).name("send").start();
|
span.kind(Span.Kind.PRODUCER).name("send").start();
|
||||||
span.remoteServiceName(REMOTE_SERVICE_NAME);
|
span.remoteServiceName(toRemoteServiceName(headers));
|
||||||
addTags(message, span, channel);
|
addTags(message, span, channel);
|
||||||
}
|
}
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
@@ -188,24 +183,31 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
|
|||||||
return outputMessage;
|
return outputMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Message<?> outputMessage(Message<?> originalMessage,
|
private String toRemoteServiceName(MessageHeaderAccessor headers) {
|
||||||
Message<?> retrievedMessage, MessageHeaderAccessor additionalHeaders) {
|
for (String key : headers.getMessageHeaders().keySet()) {
|
||||||
MessageHeaderAccessor headers = MessageHeaderAccessor
|
if (key.startsWith("kafka_")) {
|
||||||
.getMutableAccessor(originalMessage);
|
return "kafka";
|
||||||
|
}
|
||||||
|
else if (key.startsWith("amqp_")) {
|
||||||
|
return "rabbitmq";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return REMOTE_SERVICE_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Message<?> outputMessage(Message<?> originalMessage, Message<?> retrievedMessage,
|
||||||
|
MessageHeaderAccessor additionalHeaders) {
|
||||||
|
MessageHeaderAccessor headers = MessageHeaderAccessor.getMutableAccessor(originalMessage);
|
||||||
if (originalMessage instanceof ErrorMessage) {
|
if (originalMessage instanceof ErrorMessage) {
|
||||||
ErrorMessage errorMessage = (ErrorMessage) originalMessage;
|
ErrorMessage errorMessage = (ErrorMessage) originalMessage;
|
||||||
headers.copyHeaders(MessageHeaderPropagation.propagationHeaders(
|
headers.copyHeaders(MessageHeaderPropagation.propagationHeaders(additionalHeaders.getMessageHeaders(),
|
||||||
additionalHeaders.getMessageHeaders(),
|
|
||||||
this.tracing.propagation().keys()));
|
this.tracing.propagation().keys()));
|
||||||
return new ErrorMessage(errorMessage.getPayload(),
|
return new ErrorMessage(errorMessage.getPayload(), isWebSockets(headers) ? headers.getMessageHeaders()
|
||||||
isWebSockets(headers) ? headers.getMessageHeaders()
|
: new MessageHeaders(headers.getMessageHeaders()), errorMessage.getOriginalMessage());
|
||||||
: new MessageHeaders(headers.getMessageHeaders()),
|
|
||||||
errorMessage.getOriginalMessage());
|
|
||||||
}
|
}
|
||||||
headers.copyHeaders(additionalHeaders.getMessageHeaders());
|
headers.copyHeaders(additionalHeaders.getMessageHeaders());
|
||||||
return new GenericMessage<>(retrievedMessage.getPayload(),
|
return new GenericMessage<>(retrievedMessage.getPayload(),
|
||||||
isWebSockets(headers) ? headers.getMessageHeaders()
|
isWebSockets(headers) ? headers.getMessageHeaders() : new MessageHeaders(headers.getMessageHeaders()));
|
||||||
: new MessageHeaders(headers.getMessageHeaders()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isWebSockets(MessageHeaderAccessor headerAccessor) {
|
private boolean isWebSockets(MessageHeaderAccessor headerAccessor) {
|
||||||
@@ -215,8 +217,7 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
|
|||||||
|
|
||||||
private boolean isDirectChannel(MessageChannel channel) {
|
private boolean isDirectChannel(MessageChannel channel) {
|
||||||
Class<?> targetClass = AopUtils.getTargetClass(channel);
|
Class<?> targetClass = AopUtils.getTargetClass(channel);
|
||||||
boolean directChannel = this.hasDirectChannelClass
|
boolean directChannel = this.hasDirectChannelClass && DirectChannel.class.isAssignableFrom(targetClass);
|
||||||
&& DirectChannel.class.isAssignableFrom(targetClass);
|
|
||||||
if (!directChannel) {
|
if (!directChannel) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -231,8 +232,7 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterSendCompletion(Message<?> message, MessageChannel channel,
|
public void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, Exception ex) {
|
||||||
boolean sent, Exception ex) {
|
|
||||||
if (emptyMessage(message)) {
|
if (emptyMessage(message)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -240,8 +240,7 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
|
|||||||
afterMessageHandled(message, channel, null, ex);
|
afterMessageHandled(message, channel, null, ex);
|
||||||
}
|
}
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
log.debug("Will finish the current span after completion "
|
log.debug("Will finish the current span after completion " + this.tracer.currentSpan());
|
||||||
+ this.tracer.currentSpan());
|
|
||||||
}
|
}
|
||||||
finishSpan(ex);
|
finishSpan(ex);
|
||||||
}
|
}
|
||||||
@@ -258,12 +257,11 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
|
|||||||
MessageHeaderAccessor headers = mutableHeaderAccessor(message);
|
MessageHeaderAccessor headers = mutableHeaderAccessor(message);
|
||||||
TraceContextOrSamplingFlags extracted = this.extractor.extract(headers);
|
TraceContextOrSamplingFlags extracted = this.extractor.extract(headers);
|
||||||
Span span = this.threadLocalSpan.next(extracted);
|
Span span = this.threadLocalSpan.next(extracted);
|
||||||
MessageHeaderPropagation.removeAnyTraceHeaders(headers,
|
MessageHeaderPropagation.removeAnyTraceHeaders(headers, this.tracing.propagation().keys());
|
||||||
this.tracing.propagation().keys());
|
|
||||||
this.injector.inject(span.context(), headers);
|
this.injector.inject(span.context(), headers);
|
||||||
if (!span.isNoop()) {
|
if (!span.isNoop()) {
|
||||||
span.kind(Span.Kind.CONSUMER).name("receive").start();
|
span.kind(Span.Kind.CONSUMER).name("receive").start();
|
||||||
span.remoteServiceName(REMOTE_SERVICE_NAME);
|
span.remoteServiceName(toRemoteServiceName(headers));
|
||||||
addTags(message, span, channel);
|
addTags(message, span, channel);
|
||||||
}
|
}
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
@@ -272,21 +270,19 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
|
|||||||
headers.setImmutable();
|
headers.setImmutable();
|
||||||
if (message instanceof ErrorMessage) {
|
if (message instanceof ErrorMessage) {
|
||||||
ErrorMessage errorMessage = (ErrorMessage) message;
|
ErrorMessage errorMessage = (ErrorMessage) message;
|
||||||
return new ErrorMessage(errorMessage.getPayload(),
|
return new ErrorMessage(errorMessage.getPayload(), headers.getMessageHeaders(),
|
||||||
headers.getMessageHeaders(), errorMessage.getOriginalMessage());
|
errorMessage.getOriginalMessage());
|
||||||
}
|
}
|
||||||
return new GenericMessage<>(message.getPayload(), headers.getMessageHeaders());
|
return new GenericMessage<>(message.getPayload(), headers.getMessageHeaders());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterReceiveCompletion(Message<?> message, MessageChannel channel,
|
public void afterReceiveCompletion(Message<?> message, MessageChannel channel, Exception ex) {
|
||||||
Exception ex) {
|
|
||||||
if (emptyMessage(message)) {
|
if (emptyMessage(message)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
log.debug("Will finish the current span after receive completion "
|
log.debug("Will finish the current span after receive completion " + this.tracer.currentSpan());
|
||||||
+ this.tracer.currentSpan());
|
|
||||||
}
|
}
|
||||||
finishSpan(ex);
|
finishSpan(ex);
|
||||||
}
|
}
|
||||||
@@ -296,8 +292,7 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
|
|||||||
* context. It then creates a span for the handler, placing it in scope.
|
* context. It then creates a span for the handler, placing it in scope.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Message<?> beforeHandle(Message<?> message, MessageChannel channel,
|
public Message<?> beforeHandle(Message<?> message, MessageChannel channel, MessageHandler handler) {
|
||||||
MessageHandler handler) {
|
|
||||||
if (emptyMessage(message)) {
|
if (emptyMessage(message)) {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
@@ -312,34 +307,28 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
|
|||||||
consumerSpan.finish();
|
consumerSpan.finish();
|
||||||
}
|
}
|
||||||
// create and scope a span for the message processor
|
// create and scope a span for the message processor
|
||||||
this.threadLocalSpan
|
this.threadLocalSpan.next(TraceContextOrSamplingFlags.create(consumerSpan.context())).name("handle").start();
|
||||||
.next(TraceContextOrSamplingFlags.create(consumerSpan.context()))
|
|
||||||
.name("handle").start();
|
|
||||||
// remove any trace headers, but don't re-inject as we are synchronously
|
// remove any trace headers, but don't re-inject as we are synchronously
|
||||||
// processing the
|
// processing the
|
||||||
// message and can rely on scoping to access this span later.
|
// message and can rely on scoping to access this span later.
|
||||||
MessageHeaderPropagation.removeAnyTraceHeaders(headers,
|
MessageHeaderPropagation.removeAnyTraceHeaders(headers, this.tracing.propagation().keys());
|
||||||
this.tracing.propagation().keys());
|
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
log.debug("Created a new span in before handle" + consumerSpan);
|
log.debug("Created a new span in before handle" + consumerSpan);
|
||||||
}
|
}
|
||||||
if (message instanceof ErrorMessage) {
|
if (message instanceof ErrorMessage) {
|
||||||
return new ErrorMessage((Throwable) message.getPayload(),
|
return new ErrorMessage((Throwable) message.getPayload(), headers.getMessageHeaders());
|
||||||
headers.getMessageHeaders());
|
|
||||||
}
|
}
|
||||||
headers.setImmutable();
|
headers.setImmutable();
|
||||||
return new GenericMessage<>(message.getPayload(), headers.getMessageHeaders());
|
return new GenericMessage<>(message.getPayload(), headers.getMessageHeaders());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterMessageHandled(Message<?> message, MessageChannel channel,
|
public void afterMessageHandled(Message<?> message, MessageChannel channel, MessageHandler handler, Exception ex) {
|
||||||
MessageHandler handler, Exception ex) {
|
|
||||||
if (emptyMessage(message)) {
|
if (emptyMessage(message)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
log.debug("Will finish the current span after message handled "
|
log.debug("Will finish the current span after message handled " + this.tracer.currentSpan());
|
||||||
+ this.tracer.currentSpan());
|
|
||||||
}
|
}
|
||||||
finishSpan(ex);
|
finishSpan(ex);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2019 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.cloud.sleuth.instrument.reactor;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Avoids calling the expensive {@link ConfigurableApplicationContext#getBean(Class)} many
|
||||||
|
* times or throwing an exception.
|
||||||
|
*/
|
||||||
|
final class LazyBean<T> {
|
||||||
|
|
||||||
|
// spring-jcl uses commons-logging, so do we.
|
||||||
|
private static final Log log = LogFactory.getLog(LazyBean.class);
|
||||||
|
|
||||||
|
final ConfigurableApplicationContext springContext;
|
||||||
|
|
||||||
|
final Class<T> requiredType;
|
||||||
|
|
||||||
|
T value;
|
||||||
|
|
||||||
|
LazyBean(ConfigurableApplicationContext springContext, Class<T> requiredType) {
|
||||||
|
this.springContext = springContext;
|
||||||
|
this.requiredType = requiredType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to provision from the underlying bean factory, if not already provisioned.
|
||||||
|
* @return the bean value or null if there was an exception getting it.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
T get() {
|
||||||
|
if (this.value != null) {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.value = springContext.getBean(requiredType);
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Spring context [" + springContext + "] error getting ["
|
||||||
|
+ requiredType + "].", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013-2019 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.cloud.sleuth.instrument.reactor;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import org.reactivestreams.Subscription;
|
|
||||||
import reactor.util.context.Context;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A lazy representation of the {@link SpanSubscription}.
|
|
||||||
*
|
|
||||||
* @param <T> of what subscription returns
|
|
||||||
* @author Marcin Grzejszczak
|
|
||||||
* @since 2.0.0
|
|
||||||
*/
|
|
||||||
final class LazySpanSubscriber<T> extends AtomicBoolean implements SpanSubscription<T> {
|
|
||||||
|
|
||||||
private final Supplier<SpanSubscription<T>> supplier;
|
|
||||||
|
|
||||||
LazySpanSubscriber(Supplier<SpanSubscription<T>> supplier) {
|
|
||||||
this.supplier = supplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSubscribe(Subscription subscription) {
|
|
||||||
this.supplier.get().onSubscribe(subscription);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void request(long n) {
|
|
||||||
this.supplier.get().request(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void cancel() {
|
|
||||||
this.supplier.get().cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNext(T o) {
|
|
||||||
this.supplier.get().onNext(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(Throwable throwable) {
|
|
||||||
this.supplier.get().onError(throwable);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onComplete() {
|
|
||||||
this.supplier.get().onComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Context currentContext() {
|
|
||||||
return this.supplier.get().currentContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -16,9 +16,6 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.sleuth.instrument.reactor;
|
package org.springframework.cloud.sleuth.instrument.reactor;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.function.BooleanSupplier;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import brave.Tracing;
|
import brave.Tracing;
|
||||||
@@ -33,7 +30,6 @@ import reactor.core.Scannable;
|
|||||||
import reactor.core.publisher.Operators;
|
import reactor.core.publisher.Operators;
|
||||||
import reactor.util.context.Context;
|
import reactor.util.context.Context;
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,75 +52,89 @@ public abstract class ReactorSleuth {
|
|||||||
* {@link reactor.core.publisher.Hooks#onLastOperator(Function)} or
|
* {@link reactor.core.publisher.Hooks#onLastOperator(Function)} or
|
||||||
* {@link reactor.core.publisher.Hooks#onLastOperator(Function)}. The Span operator
|
* {@link reactor.core.publisher.Hooks#onLastOperator(Function)}. The Span operator
|
||||||
* pointcut will pass the Scope of the Span without ever creating any new spans.
|
* pointcut will pass the Scope of the Span without ever creating any new spans.
|
||||||
* @param beanFactory - {@link BeanFactory}
|
* @param springContext the Spring context.
|
||||||
* @param <T> an arbitrary type that is left unchanged by the span operator
|
* @param <T> an arbitrary type that is left unchanged by the span operator
|
||||||
* @return a new lazy span operator pointcut
|
* @return a new lazy span operator pointcut
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
// Much of Boot assumes that the Spring context will be a
|
||||||
|
// ConfigurableApplicationContext, rooted in SpringApplication's
|
||||||
|
// requirement for it to be so. Previous versions of Reactor
|
||||||
|
// instrumentation injected both BeanFactory and also
|
||||||
|
// ConfigurableApplicationContext. This chooses the more narrow
|
||||||
|
// signature as it is simpler than explaining instanceof checks.
|
||||||
public static <T> Function<? super Publisher<T>, ? extends Publisher<T>> scopePassingSpanOperator(
|
public static <T> Function<? super Publisher<T>, ? extends Publisher<T>> scopePassingSpanOperator(
|
||||||
BeanFactory beanFactory) {
|
ConfigurableApplicationContext springContext) {
|
||||||
if (log.isTraceEnabled()) {
|
if (log.isTraceEnabled()) {
|
||||||
log.trace("Scope passing operator [" + beanFactory + "]");
|
log.trace("Scope passing operator [" + springContext + "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adapt if lazy bean factory
|
// keep a reference outside the lambda so that any caching will be visible to
|
||||||
BooleanSupplier isActive = beanFactory instanceof ConfigurableApplicationContext
|
// all publishers
|
||||||
? ((ConfigurableApplicationContext) beanFactory)::isActive : () -> true;
|
LazyBean<CurrentTraceContext> lazyCurrentTraceContext = new LazyBean<>(
|
||||||
|
springContext, CurrentTraceContext.class);
|
||||||
|
|
||||||
return Operators.liftPublisher((p, sub) -> {
|
return Operators.liftPublisher((p, sub) -> {
|
||||||
// if Flux/Mono #just, #empty, #error
|
// We don't scope scalar results as they happen in an instant. This prevents
|
||||||
|
// excessive overhead when using Flux/Mono #just, #empty, #error, etc.
|
||||||
if (p instanceof Fuseable.ScalarCallable) {
|
if (p instanceof Fuseable.ScalarCallable) {
|
||||||
return sub;
|
return sub;
|
||||||
}
|
}
|
||||||
Scannable scannable = Scannable.from(p);
|
|
||||||
// rest of the logic unchanged...
|
|
||||||
if (isActive.getAsBoolean()) {
|
|
||||||
if (log.isTraceEnabled()) {
|
|
||||||
log.trace("Spring Context [" + beanFactory
|
|
||||||
+ "] already refreshed. Creating a scope "
|
|
||||||
+ "passing span subscriber with Reactor Context " + "["
|
|
||||||
+ sub.currentContext() + "] and name [" + scannable.name()
|
|
||||||
+ "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
return scopePassingSpanSubscription(beanFactory, sub);
|
if (!springContext.isActive()) {
|
||||||
|
if (log.isTraceEnabled()) {
|
||||||
|
log.trace("Spring Context [" + springContext
|
||||||
|
+ "] is not yet refreshed. This is unexpected. Reactor Context is ["
|
||||||
|
+ sub.currentContext() + "] and name is [" + name(sub) + "]");
|
||||||
|
}
|
||||||
|
assert false; // should never happen, but don't break.
|
||||||
|
return sub;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Context context = sub.currentContext();
|
||||||
|
|
||||||
if (log.isTraceEnabled()) {
|
if (log.isTraceEnabled()) {
|
||||||
log.trace("Spring Context [" + beanFactory
|
log.trace("Spring context [" + springContext + "], Reactor context ["
|
||||||
+ "] is not yet refreshed, falling back to lazy span subscriber. Reactor Context is ["
|
+ context + "], name [" + name(sub) + "]");
|
||||||
+ sub.currentContext() + "] and name is [" + scannable.name()
|
|
||||||
+ "]");
|
|
||||||
}
|
}
|
||||||
return new LazySpanSubscriber<>(
|
|
||||||
lazyScopePassingSpanSubscription(beanFactory, scannable, sub));
|
// Try to get the current trace context bean, lenient when there are problems
|
||||||
|
CurrentTraceContext currentTraceContext = lazyCurrentTraceContext.get();
|
||||||
|
if (currentTraceContext == null) {
|
||||||
|
if (log.isTraceEnabled()) {
|
||||||
|
log.trace("Spring Context [" + springContext
|
||||||
|
+ "] did not return a CurrentTraceContext. Reactor Context is ["
|
||||||
|
+ sub.currentContext() + "] and name is [" + name(sub) + "]");
|
||||||
|
}
|
||||||
|
assert false; // should never happen, but don't break.
|
||||||
|
return sub;
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceContext parent = traceContext(context, currentTraceContext);
|
||||||
|
if (parent == null) {
|
||||||
|
return sub; // no need to scope a null parent
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log.isTraceEnabled()) {
|
||||||
|
log.trace("Creating a scope passing span subscriber with Reactor Context "
|
||||||
|
+ "[" + context + "] and name [" + name(sub) + "]");
|
||||||
|
}
|
||||||
|
return new ScopePassingSpanSubscriber<>(sub, context, currentTraceContext,
|
||||||
|
parent);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static <T> SpanSubscriptionProvider<T> lazyScopePassingSpanSubscription(
|
static String name(CoreSubscriber<?> sub) {
|
||||||
BeanFactory beanFactory, Scannable scannable, CoreSubscriber<? super T> sub) {
|
return Scannable.from(sub).name();
|
||||||
return new SpanSubscriptionProvider<>(beanFactory, sub, sub.currentContext(),
|
|
||||||
scannable.name());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Map<BeanFactory, CurrentTraceContext> CACHE = new ConcurrentHashMap<>();
|
/**
|
||||||
|
* Like {@link CurrentTraceContext#get()}, except it first checks the reactor context.
|
||||||
static <T> CoreSubscriber<? super T> scopePassingSpanSubscription(
|
*/
|
||||||
BeanFactory beanFactory, CoreSubscriber<? super T> sub) {
|
static TraceContext traceContext(Context context, CurrentTraceContext fallback) {
|
||||||
CurrentTraceContext currentTraceContext = CACHE.computeIfAbsent(beanFactory,
|
if (context.hasKey(TraceContext.class)) {
|
||||||
beanFactory1 -> beanFactory1.getBean(CurrentTraceContext.class));
|
return context.get(TraceContext.class);
|
||||||
Context context = sub.currentContext();
|
|
||||||
|
|
||||||
TraceContext parent = context.getOrDefault(TraceContext.class, null);
|
|
||||||
if (parent == null) {
|
|
||||||
parent = currentTraceContext.get();
|
|
||||||
}
|
|
||||||
if (parent != null) {
|
|
||||||
return new ScopePassingSpanSubscriber<>(sub, context, currentTraceContext,
|
|
||||||
parent);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return sub; // no need to trace
|
|
||||||
}
|
}
|
||||||
|
return fallback.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,8 +54,7 @@ final class ScopePassingSpanSubscriber<T> implements SpanSubscription<T>, Scanna
|
|||||||
this.subscriber = subscriber;
|
this.subscriber = subscriber;
|
||||||
this.currentTraceContext = currentTraceContext;
|
this.currentTraceContext = currentTraceContext;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.context = ctx != null && parent != null ? ctx.put(TraceContext.class, parent)
|
this.context = parent != null ? ctx.put(TraceContext.class, parent) : ctx;
|
||||||
: ctx != null ? ctx : Context.empty();
|
|
||||||
if (log.isTraceEnabled()) {
|
if (log.isTraceEnabled()) {
|
||||||
log.trace("Parent span [" + parent + "], context [" + this.context + "]");
|
log.trace("Parent span [" + parent + "], context [" + this.context + "]");
|
||||||
}
|
}
|
||||||
@@ -81,7 +80,6 @@ final class ScopePassingSpanSubscriber<T> implements SpanSubscription<T>, Scanna
|
|||||||
try (Scope scope = this.currentTraceContext.maybeScope(this.parent)) {
|
try (Scope scope = this.currentTraceContext.maybeScope(this.parent)) {
|
||||||
this.s.cancel();
|
this.s.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -120,4 +118,10 @@ final class ScopePassingSpanSubscriber<T> implements SpanSubscription<T>, Scanna
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ScopePassingSpanSubscriber{" + "subscriber=" + this.subscriber
|
||||||
|
+ ", parent=" + this.parent + "}";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ final class SpanSubscriber<T> extends AtomicBoolean implements SpanSubscription<
|
|||||||
// no additional cleaning is required cause we operate on scopes
|
// no additional cleaning is required cause we operate on scopes
|
||||||
if (log.isTraceEnabled()) {
|
if (log.isTraceEnabled()) {
|
||||||
log.trace("Request after cleaning. Current span [{}]",
|
log.trace("Request after cleaning. Current span [{}]",
|
||||||
this.currentTraceContext.get());
|
this.span.context());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,92 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013-2019 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.cloud.sleuth.instrument.reactor;
|
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import brave.propagation.CurrentTraceContext;
|
|
||||||
import brave.propagation.TraceContext;
|
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
import org.reactivestreams.Subscriber;
|
|
||||||
import reactor.util.context.Context;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Supplier to lazily start a {@link SpanSubscription}.
|
|
||||||
*
|
|
||||||
* @param <T> type of returned subscription
|
|
||||||
* @author Marcin Grzejszczak
|
|
||||||
*/
|
|
||||||
final 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 CurrentTraceContext currentTraceContext;
|
|
||||||
|
|
||||||
SpanSubscriptionProvider(BeanFactory beanFactory, Subscriber<? super T> subscriber,
|
|
||||||
Context context, String name) {
|
|
||||||
this.beanFactory = beanFactory;
|
|
||||||
this.subscriber = subscriber;
|
|
||||||
this.context = context;
|
|
||||||
this.name = name;
|
|
||||||
if (log.isTraceEnabled()) {
|
|
||||||
log.trace("Spring context [" + beanFactory + "], Reactor context [" + context
|
|
||||||
+ "], name [" + name + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SpanSubscription<T> get() {
|
|
||||||
return newCoreSubscriber(currentTraceContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
SpanSubscription<T> newCoreSubscriber(CurrentTraceContext currentTraceContext) {
|
|
||||||
TraceContext root = this.context.hasKey(TraceContext.class)
|
|
||||||
? this.context.get(TraceContext.class) : currentTraceContext.get();
|
|
||||||
return new ScopePassingSpanSubscriber<>(this.subscriber, this.context,
|
|
||||||
currentTraceContext, root);
|
|
||||||
}
|
|
||||||
|
|
||||||
private CurrentTraceContext currentTraceContext() {
|
|
||||||
if (this.currentTraceContext == null) {
|
|
||||||
try {
|
|
||||||
this.currentTraceContext = this.beanFactory
|
|
||||||
.getBean(CurrentTraceContext.class);
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
if (log.isDebugEnabled()) {
|
|
||||||
log.debug(
|
|
||||||
"Exception occurred while trying to get the currentTraceContext bean. Will return a default instance",
|
|
||||||
ex);
|
|
||||||
}
|
|
||||||
return CurrentTraceContext.Default.create();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.currentTraceContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -25,8 +25,6 @@ import reactor.core.publisher.Hooks;
|
|||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.core.scheduler.Schedulers;
|
import reactor.core.scheduler.Schedulers;
|
||||||
|
|
||||||
import org.springframework.beans.BeansException;
|
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||||
@@ -47,6 +45,7 @@ import org.springframework.context.annotation.Bean;
|
|||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.env.ConfigurableEnvironment;
|
import org.springframework.core.env.ConfigurableEnvironment;
|
||||||
|
|
||||||
|
import static org.springframework.cloud.sleuth.instrument.reactor.ReactorSleuth.scopePassingSpanOperator;
|
||||||
import static org.springframework.cloud.sleuth.instrument.reactor.TraceReactorAutoConfiguration.TraceReactorConfiguration.SLEUTH_TRACE_REACTOR_KEY;
|
import static org.springframework.cloud.sleuth.instrument.reactor.TraceReactorAutoConfiguration.TraceReactorConfiguration.SLEUTH_TRACE_REACTOR_KEY;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,14 +75,14 @@ public class TraceReactorAutoConfiguration {
|
|||||||
private static final Log log = LogFactory.getLog(TraceReactorConfiguration.class);
|
private static final Log log = LogFactory.getLog(TraceReactorConfiguration.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
BeanFactory beanFactory;
|
ConfigurableApplicationContext springContext;
|
||||||
|
|
||||||
@PreDestroy
|
@PreDestroy
|
||||||
public void cleanupHooks() {
|
public void cleanupHooks() {
|
||||||
if (log.isTraceEnabled()) {
|
if (log.isTraceEnabled()) {
|
||||||
log.trace("Cleaning up hooks");
|
log.trace("Cleaning up hooks");
|
||||||
}
|
}
|
||||||
SleuthReactorProperties reactorProperties = this.beanFactory
|
SleuthReactorProperties reactorProperties = this.springContext
|
||||||
.getBean(SleuthReactorProperties.class);
|
.getBean(SleuthReactorProperties.class);
|
||||||
if (reactorProperties.isDecorateOnEach()) {
|
if (reactorProperties.isDecorateOnEach()) {
|
||||||
if (log.isTraceEnabled()) {
|
if (log.isTraceEnabled()) {
|
||||||
@@ -102,9 +101,8 @@ public class TraceReactorAutoConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
// for tests
|
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
static HookRegisteringBeanDefinitionRegistryPostProcessor traceHookRegisteringBeanDefinitionRegistryPostProcessor(
|
HookRegisteringBeanDefinitionRegistryPostProcessor traceHookRegisteringBeanDefinitionRegistryPostProcessor(
|
||||||
ConfigurableApplicationContext context) {
|
ConfigurableApplicationContext context) {
|
||||||
if (log.isTraceEnabled()) {
|
if (log.isTraceEnabled()) {
|
||||||
log.trace(
|
log.trace(
|
||||||
@@ -156,14 +154,14 @@ class HooksRefresher implements ApplicationListener<RefreshScopeRefreshedEvent>
|
|||||||
log.trace("Decorating onEach operator instrumentation");
|
log.trace("Decorating onEach operator instrumentation");
|
||||||
}
|
}
|
||||||
Hooks.onEachOperator(SLEUTH_TRACE_REACTOR_KEY,
|
Hooks.onEachOperator(SLEUTH_TRACE_REACTOR_KEY,
|
||||||
ReactorSleuth.scopePassingSpanOperator(this.context));
|
scopePassingSpanOperator(this.context));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (log.isTraceEnabled()) {
|
if (log.isTraceEnabled()) {
|
||||||
log.trace("Decorating onLast operator instrumentation");
|
log.trace("Decorating onLast operator instrumentation");
|
||||||
}
|
}
|
||||||
Hooks.onLastOperator(SLEUTH_TRACE_REACTOR_KEY,
|
Hooks.onLastOperator(SLEUTH_TRACE_REACTOR_KEY,
|
||||||
ReactorSleuth.scopePassingSpanOperator(this.context));
|
scopePassingSpanOperator(this.context));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,26 +173,24 @@ class HookRegisteringBeanDefinitionRegistryPostProcessor
|
|||||||
private static final Log log = LogFactory
|
private static final Log log = LogFactory
|
||||||
.getLog(HookRegisteringBeanDefinitionRegistryPostProcessor.class);
|
.getLog(HookRegisteringBeanDefinitionRegistryPostProcessor.class);
|
||||||
|
|
||||||
private final ConfigurableApplicationContext context;
|
final ConfigurableApplicationContext springContext;
|
||||||
|
|
||||||
HookRegisteringBeanDefinitionRegistryPostProcessor(
|
HookRegisteringBeanDefinitionRegistryPostProcessor(
|
||||||
ConfigurableApplicationContext context) {
|
ConfigurableApplicationContext springContext) {
|
||||||
this.context = context;
|
this.springContext = springContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
|
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
|
||||||
throws BeansException {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
|
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
|
||||||
throws BeansException {
|
setupHooks(this.springContext);
|
||||||
setupHooks(beanFactory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupHooks(BeanFactory beanFactory) {
|
static void setupHooks(ConfigurableApplicationContext springContext) {
|
||||||
ConfigurableEnvironment environment = this.context.getEnvironment();
|
ConfigurableEnvironment environment = springContext.getEnvironment();
|
||||||
boolean decorateOnEach = environment.getProperty(
|
boolean decorateOnEach = environment.getProperty(
|
||||||
"spring.sleuth.reactor.decorate-on-each", Boolean.class, true);
|
"spring.sleuth.reactor.decorate-on-each", Boolean.class, true);
|
||||||
if (decorateOnEach) {
|
if (decorateOnEach) {
|
||||||
@@ -202,20 +198,20 @@ class HookRegisteringBeanDefinitionRegistryPostProcessor
|
|||||||
log.trace("Decorating onEach operator instrumentation");
|
log.trace("Decorating onEach operator instrumentation");
|
||||||
}
|
}
|
||||||
Hooks.onEachOperator(SLEUTH_TRACE_REACTOR_KEY,
|
Hooks.onEachOperator(SLEUTH_TRACE_REACTOR_KEY,
|
||||||
ReactorSleuth.scopePassingSpanOperator(this.context));
|
scopePassingSpanOperator(springContext));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (log.isTraceEnabled()) {
|
if (log.isTraceEnabled()) {
|
||||||
log.trace("Decorating onLast operator instrumentation");
|
log.trace("Decorating onLast operator instrumentation");
|
||||||
}
|
}
|
||||||
Hooks.onLastOperator(SLEUTH_TRACE_REACTOR_KEY,
|
Hooks.onLastOperator(SLEUTH_TRACE_REACTOR_KEY,
|
||||||
ReactorSleuth.scopePassingSpanOperator(this.context));
|
scopePassingSpanOperator(springContext));
|
||||||
}
|
}
|
||||||
Schedulers.setExecutorServiceDecorator(
|
Schedulers.setExecutorServiceDecorator(
|
||||||
TraceReactorAutoConfiguration.SLEUTH_REACTOR_EXECUTOR_SERVICE_KEY,
|
TraceReactorAutoConfiguration.SLEUTH_REACTOR_EXECUTOR_SERVICE_KEY,
|
||||||
(scheduler,
|
(scheduler,
|
||||||
scheduledExecutorService) -> new TraceableScheduledExecutorService(
|
scheduledExecutorService) -> new TraceableScheduledExecutorService(
|
||||||
beanFactory, scheduledExecutorService));
|
springContext, scheduledExecutorService));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import org.springframework.boot.web.client.RestTemplateCustomizer;
|
|||||||
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
|
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
|
||||||
import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter;
|
import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter;
|
||||||
import org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration;
|
import org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
@@ -133,8 +134,8 @@ public class TraceWebClientAutoConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
static TraceWebClientBeanPostProcessor traceWebClientBeanPostProcessor(
|
static TraceWebClientBeanPostProcessor traceWebClientBeanPostProcessor(
|
||||||
BeanFactory beanFactory) {
|
ConfigurableApplicationContext springContext) {
|
||||||
return new TraceWebClientBeanPostProcessor(beanFactory);
|
return new TraceWebClientBeanPostProcessor(springContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,9 +38,8 @@ import reactor.util.annotation.Nullable;
|
|||||||
import reactor.util.context.Context;
|
import reactor.util.context.Context;
|
||||||
|
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
|
||||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||||
import org.springframework.cloud.sleuth.instrument.reactor.ReactorSleuth;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.core.io.buffer.DataBuffer;
|
import org.springframework.core.io.buffer.DataBuffer;
|
||||||
import org.springframework.web.client.RestClientException;
|
import org.springframework.web.client.RestClientException;
|
||||||
import org.springframework.web.reactive.function.client.ClientRequest;
|
import org.springframework.web.reactive.function.client.ClientRequest;
|
||||||
@@ -49,6 +48,8 @@ import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
|
|||||||
import org.springframework.web.reactive.function.client.ExchangeFunction;
|
import org.springframework.web.reactive.function.client.ExchangeFunction;
|
||||||
import org.springframework.web.reactive.function.client.WebClient;
|
import org.springframework.web.reactive.function.client.WebClient;
|
||||||
|
|
||||||
|
import static org.springframework.cloud.sleuth.instrument.reactor.ReactorSleuth.scopePassingSpanOperator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link BeanPostProcessor} to wrap a {@link WebClient} instance into its trace
|
* {@link BeanPostProcessor} to wrap a {@link WebClient} instance into its trace
|
||||||
* representation.
|
* representation.
|
||||||
@@ -58,10 +59,10 @@ import org.springframework.web.reactive.function.client.WebClient;
|
|||||||
*/
|
*/
|
||||||
final class TraceWebClientBeanPostProcessor implements BeanPostProcessor {
|
final class TraceWebClientBeanPostProcessor implements BeanPostProcessor {
|
||||||
|
|
||||||
private final BeanFactory beanFactory;
|
private final ConfigurableApplicationContext springContext;
|
||||||
|
|
||||||
TraceWebClientBeanPostProcessor(BeanFactory beanFactory) {
|
TraceWebClientBeanPostProcessor(ConfigurableApplicationContext springContext) {
|
||||||
this.beanFactory = beanFactory;
|
this.springContext = springContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -92,7 +93,7 @@ final class TraceWebClientBeanPostProcessor implements BeanPostProcessor {
|
|||||||
return functions -> {
|
return functions -> {
|
||||||
boolean noneMatch = noneMatchTraceExchangeFunction(functions);
|
boolean noneMatch = noneMatchTraceExchangeFunction(functions);
|
||||||
if (noneMatch) {
|
if (noneMatch) {
|
||||||
functions.add(new TraceExchangeFilterFunction(this.beanFactory));
|
functions.add(new TraceExchangeFilterFunction(this.springContext));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -134,7 +135,7 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction {
|
|||||||
|
|
||||||
private static final String CANCELLED_SUBSCRIPTION_ERROR = "CANCELLED";
|
private static final String CANCELLED_SUBSCRIPTION_ERROR = "CANCELLED";
|
||||||
|
|
||||||
final BeanFactory beanFactory;
|
final ConfigurableApplicationContext springContext;
|
||||||
|
|
||||||
final Function<? super Publisher<DataBuffer>, ? extends Publisher<DataBuffer>> scopePassingTransformer;
|
final Function<? super Publisher<DataBuffer>, ? extends Publisher<DataBuffer>> scopePassingTransformer;
|
||||||
|
|
||||||
@@ -146,14 +147,14 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction {
|
|||||||
|
|
||||||
TraceContext.Injector<ClientRequest.Builder> injector;
|
TraceContext.Injector<ClientRequest.Builder> injector;
|
||||||
|
|
||||||
TraceExchangeFilterFunction(BeanFactory beanFactory) {
|
TraceExchangeFilterFunction(ConfigurableApplicationContext springContext) {
|
||||||
this.beanFactory = beanFactory;
|
this.springContext = springContext;
|
||||||
this.scopePassingTransformer = ReactorSleuth
|
this.scopePassingTransformer = scopePassingSpanOperator(springContext);
|
||||||
.scopePassingSpanOperator(beanFactory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ExchangeFilterFunction create(BeanFactory beanFactory) {
|
public static ExchangeFilterFunction create(
|
||||||
return new TraceExchangeFilterFunction(beanFactory);
|
ConfigurableApplicationContext springContext) {
|
||||||
|
return new TraceExchangeFilterFunction(springContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -177,7 +178,7 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction {
|
|||||||
HttpClientHandler<brave.http.HttpClientRequest, brave.http.HttpClientResponse> handler() {
|
HttpClientHandler<brave.http.HttpClientRequest, brave.http.HttpClientResponse> handler() {
|
||||||
if (this.handler == null) {
|
if (this.handler == null) {
|
||||||
this.handler = HttpClientHandler
|
this.handler = HttpClientHandler
|
||||||
.create(this.beanFactory.getBean(HttpTracing.class));
|
.create(this.springContext.getBean(HttpTracing.class));
|
||||||
}
|
}
|
||||||
return this.handler;
|
return this.handler;
|
||||||
}
|
}
|
||||||
@@ -191,14 +192,14 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction {
|
|||||||
|
|
||||||
HttpTracing httpTracing() {
|
HttpTracing httpTracing() {
|
||||||
if (this.httpTracing == null) {
|
if (this.httpTracing == null) {
|
||||||
this.httpTracing = this.beanFactory.getBean(HttpTracing.class);
|
this.httpTracing = this.springContext.getBean(HttpTracing.class);
|
||||||
}
|
}
|
||||||
return this.httpTracing;
|
return this.httpTracing;
|
||||||
}
|
}
|
||||||
|
|
||||||
TraceContext.Injector<ClientRequest.Builder> injector() {
|
TraceContext.Injector<ClientRequest.Builder> injector() {
|
||||||
if (this.injector == null) {
|
if (this.injector == null) {
|
||||||
this.injector = this.beanFactory.getBean(HttpTracing.class).tracing()
|
this.injector = this.springContext.getBean(HttpTracing.class).tracing()
|
||||||
.propagation().injector(SETTER);
|
.propagation().injector(SETTER);
|
||||||
}
|
}
|
||||||
return this.injector;
|
return this.injector;
|
||||||
|
|||||||
@@ -29,8 +29,10 @@ import org.junit.After;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import zipkin2.Span;
|
import zipkin2.Span;
|
||||||
|
|
||||||
|
import org.springframework.amqp.support.AmqpHeaders;
|
||||||
import org.springframework.integration.channel.DirectChannel;
|
import org.springframework.integration.channel.DirectChannel;
|
||||||
import org.springframework.integration.channel.QueueChannel;
|
import org.springframework.integration.channel.QueueChannel;
|
||||||
|
import org.springframework.kafka.support.KafkaHeaders;
|
||||||
import org.springframework.messaging.Message;
|
import org.springframework.messaging.Message;
|
||||||
import org.springframework.messaging.MessageChannel;
|
import org.springframework.messaging.MessageChannel;
|
||||||
import org.springframework.messaging.MessageHandler;
|
import org.springframework.messaging.MessageHandler;
|
||||||
@@ -51,10 +53,11 @@ public class TracingChannelInterceptorTest {
|
|||||||
|
|
||||||
List<Span> spans = new ArrayList<>();
|
List<Span> spans = new ArrayList<>();
|
||||||
|
|
||||||
ChannelInterceptor interceptor = TracingChannelInterceptor.create(Tracing.newBuilder()
|
ChannelInterceptor interceptor = TracingChannelInterceptor
|
||||||
.currentTraceContext(ThreadLocalCurrentTraceContext.newBuilder()
|
.create(Tracing.newBuilder()
|
||||||
.addScopeDecorator(StrictScopeDecorator.create()).build())
|
.currentTraceContext(ThreadLocalCurrentTraceContext.newBuilder()
|
||||||
.spanReporter(this.spans::add).build());
|
.addScopeDecorator(StrictScopeDecorator.create()).build())
|
||||||
|
.spanReporter(this.spans::add).build());
|
||||||
|
|
||||||
QueueChannel channel = new QueueChannel();
|
QueueChannel channel = new QueueChannel();
|
||||||
|
|
||||||
@@ -83,10 +86,9 @@ public class TracingChannelInterceptorTest {
|
|||||||
|
|
||||||
this.channel.send(MessageBuilder.withPayload("foo").build());
|
this.channel.send(MessageBuilder.withPayload("foo").build());
|
||||||
|
|
||||||
assertThat(this.channel.receive().getHeaders()).containsKeys("X-B3-TraceId",
|
assertThat(this.channel.receive().getHeaders()).containsKeys("X-B3-TraceId", "X-B3-SpanId", "X-B3-Sampled",
|
||||||
"X-B3-SpanId", "X-B3-Sampled", "nativeHeaders");
|
"nativeHeaders");
|
||||||
assertThat(this.spans).hasSize(1).flatExtracting(Span::kind)
|
assertThat(this.spans).hasSize(1).flatExtracting(Span::kind).containsExactly(Span.Kind.PRODUCER);
|
||||||
.containsExactly(Span.Kind.PRODUCER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -96,10 +98,9 @@ public class TracingChannelInterceptorTest {
|
|||||||
this.directChannel.send(MessageBuilder.withPayload("foo").build());
|
this.directChannel.send(MessageBuilder.withPayload("foo").build());
|
||||||
|
|
||||||
assertThat(this.message).isNotNull();
|
assertThat(this.message).isNotNull();
|
||||||
assertThat(this.message.getHeaders()).containsKeys("X-B3-TraceId", "X-B3-SpanId",
|
assertThat(this.message.getHeaders()).containsKeys("X-B3-TraceId", "X-B3-SpanId", "X-B3-Sampled",
|
||||||
"X-B3-Sampled", "nativeHeaders");
|
"nativeHeaders");
|
||||||
assertThat(this.spans).flatExtracting(Span::kind).contains(Span.Kind.CONSUMER,
|
assertThat(this.spans).flatExtracting(Span::kind).contains(Span.Kind.CONSUMER, Span.Kind.PRODUCER);
|
||||||
Span.Kind.PRODUCER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -108,9 +109,8 @@ public class TracingChannelInterceptorTest {
|
|||||||
|
|
||||||
this.channel.send(MessageBuilder.withPayload("foo").build());
|
this.channel.send(MessageBuilder.withPayload("foo").build());
|
||||||
|
|
||||||
assertThat((Map) this.channel.receive().getHeaders().get(NATIVE_HEADERS))
|
assertThat((Map) this.channel.receive().getHeaders().get(NATIVE_HEADERS)).containsOnlyKeys("X-B3-TraceId",
|
||||||
.containsOnlyKeys("X-B3-TraceId", "X-B3-SpanId", "X-B3-Sampled",
|
"X-B3-SpanId", "X-B3-Sampled", "spanTraceId", "spanId", "spanSampled");
|
||||||
"spanTraceId", "spanId", "spanSampled");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -122,13 +122,11 @@ public class TracingChannelInterceptorTest {
|
|||||||
public void producerConsidersOldSpanIds() {
|
public void producerConsidersOldSpanIds() {
|
||||||
this.channel.addInterceptor(producerSideOnly(this.interceptor));
|
this.channel.addInterceptor(producerSideOnly(this.interceptor));
|
||||||
|
|
||||||
this.channel.send(MessageBuilder.withPayload("foo")
|
this.channel.send(MessageBuilder.withPayload("foo").setHeader("X-B3-TraceId", "000000000000000a")
|
||||||
.setHeader("X-B3-TraceId", "000000000000000a")
|
.setHeader("X-B3-ParentSpanId", "000000000000000a").setHeader("X-B3-SpanId", "000000000000000b")
|
||||||
.setHeader("X-B3-ParentSpanId", "000000000000000a")
|
.build());
|
||||||
.setHeader("X-B3-SpanId", "000000000000000b").build());
|
|
||||||
|
|
||||||
assertThat(this.channel.receive().getHeaders()).containsEntry("X-B3-ParentSpanId",
|
assertThat(this.channel.receive().getHeaders()).containsEntry("X-B3-ParentSpanId", "000000000000000b");
|
||||||
"000000000000000b");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -142,12 +140,10 @@ public class TracingChannelInterceptorTest {
|
|||||||
accessor.setNativeHeader("X-B3-ParentSpanId", "000000000000000a");
|
accessor.setNativeHeader("X-B3-ParentSpanId", "000000000000000a");
|
||||||
accessor.setNativeHeader("X-B3-SpanId", "000000000000000b");
|
accessor.setNativeHeader("X-B3-SpanId", "000000000000000b");
|
||||||
|
|
||||||
this.channel.send(MessageBuilder.withPayload("foo")
|
this.channel.send(MessageBuilder.withPayload("foo").copyHeaders(accessor.toMessageHeaders()).build());
|
||||||
.copyHeaders(accessor.toMessageHeaders()).build());
|
|
||||||
|
|
||||||
assertThat((Map) this.channel.receive().getHeaders().get(NATIVE_HEADERS))
|
assertThat((Map) this.channel.receive().getHeaders().get(NATIVE_HEADERS)).containsEntry("X-B3-ParentSpanId",
|
||||||
.containsEntry("X-B3-ParentSpanId",
|
Collections.singletonList("000000000000000b"));
|
||||||
Collections.singletonList("000000000000000b"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -160,10 +156,9 @@ public class TracingChannelInterceptorTest {
|
|||||||
|
|
||||||
this.channel.send(MessageBuilder.withPayload("foo").build());
|
this.channel.send(MessageBuilder.withPayload("foo").build());
|
||||||
|
|
||||||
assertThat(this.channel.receive().getHeaders()).containsKeys("X-B3-TraceId",
|
assertThat(this.channel.receive().getHeaders()).containsKeys("X-B3-TraceId", "X-B3-SpanId", "X-B3-Sampled",
|
||||||
"X-B3-SpanId", "X-B3-Sampled", "nativeHeaders");
|
"nativeHeaders");
|
||||||
assertThat(this.spans).hasSize(1).flatExtracting(Span::kind)
|
assertThat(this.spans).hasSize(1).flatExtracting(Span::kind).containsExactly(Span.Kind.CONSUMER);
|
||||||
.containsExactly(Span.Kind.CONSUMER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -172,9 +167,8 @@ public class TracingChannelInterceptorTest {
|
|||||||
|
|
||||||
this.channel.send(MessageBuilder.withPayload("foo").build());
|
this.channel.send(MessageBuilder.withPayload("foo").build());
|
||||||
|
|
||||||
assertThat((Map) this.channel.receive().getHeaders().get(NATIVE_HEADERS))
|
assertThat((Map) this.channel.receive().getHeaders().get(NATIVE_HEADERS)).containsOnlyKeys("X-B3-TraceId",
|
||||||
.containsOnlyKeys("X-B3-TraceId", "X-B3-SpanId", "X-B3-Sampled",
|
"X-B3-SpanId", "X-B3-Sampled", "spanTraceId", "spanId", "spanSampled");
|
||||||
"spanTraceId", "spanId", "spanSampled");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -186,10 +180,9 @@ public class TracingChannelInterceptorTest {
|
|||||||
|
|
||||||
channel.send(MessageBuilder.withPayload("foo").build());
|
channel.send(MessageBuilder.withPayload("foo").build());
|
||||||
|
|
||||||
assertThat(messages.get(0).getHeaders()).doesNotContainKeys("X-B3-TraceId",
|
assertThat(messages.get(0).getHeaders()).doesNotContainKeys("X-B3-TraceId", "X-B3-SpanId", "X-B3-Sampled",
|
||||||
"X-B3-SpanId", "X-B3-Sampled", "nativeHeaders");
|
"nativeHeaders");
|
||||||
assertThat(this.spans).flatExtracting(Span::kind)
|
assertThat(this.spans).flatExtracting(Span::kind).containsExactly(Span.Kind.CONSUMER, null);
|
||||||
.containsExactly(Span.Kind.CONSUMER, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -206,8 +199,7 @@ public class TracingChannelInterceptorTest {
|
|||||||
|
|
||||||
channel.send(MessageBuilder.withPayload("foo").build());
|
channel.send(MessageBuilder.withPayload("foo").build());
|
||||||
|
|
||||||
assertThat(messages.get(0).getHeaders()).doesNotContainKeys("X-B3-TraceId",
|
assertThat(messages.get(0).getHeaders()).doesNotContainKeys("X-B3-TraceId", "X-B3-SpanId", "X-B3-Sampled");
|
||||||
"X-B3-SpanId", "X-B3-Sampled");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -219,8 +211,8 @@ public class TracingChannelInterceptorTest {
|
|||||||
|
|
||||||
channel.send(MessageBuilder.withPayload("foo").build());
|
channel.send(MessageBuilder.withPayload("foo").build());
|
||||||
|
|
||||||
assertThat((Map) messages.get(0).getHeaders().get(NATIVE_HEADERS))
|
assertThat((Map) messages.get(0).getHeaders().get(NATIVE_HEADERS)).doesNotContainKeys("X-B3-TraceId",
|
||||||
.doesNotContainKeys("X-B3-TraceId", "X-B3-SpanId", "X-B3-Sampled");
|
"X-B3-SpanId", "X-B3-Sampled");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -230,8 +222,8 @@ public class TracingChannelInterceptorTest {
|
|||||||
this.channel.send(MessageBuilder.withPayload("foo").build());
|
this.channel.send(MessageBuilder.withPayload("foo").build());
|
||||||
this.channel.receive();
|
this.channel.receive();
|
||||||
|
|
||||||
assertThat(this.spans).flatExtracting(Span::kind)
|
assertThat(this.spans).flatExtracting(Span::kind).containsExactlyInAnyOrder(Span.Kind.CONSUMER,
|
||||||
.containsExactlyInAnyOrder(Span.Kind.CONSUMER, Span.Kind.PRODUCER);
|
Span.Kind.PRODUCER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -243,8 +235,7 @@ public class TracingChannelInterceptorTest {
|
|||||||
|
|
||||||
channel.send(MessageBuilder.withPayload("foo").build());
|
channel.send(MessageBuilder.withPayload("foo").build());
|
||||||
|
|
||||||
assertThat(this.spans).flatExtracting(Span::kind)
|
assertThat(this.spans).flatExtracting(Span::kind).containsExactly(Span.Kind.CONSUMER, null, Span.Kind.PRODUCER);
|
||||||
.containsExactly(Span.Kind.CONSUMER, null, Span.Kind.PRODUCER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -255,54 +246,41 @@ public class TracingChannelInterceptorTest {
|
|||||||
Map<String, Object> errorChannelHeaders = new HashMap<>();
|
Map<String, Object> errorChannelHeaders = new HashMap<>();
|
||||||
errorChannelHeaders.put(MessageHeaders.REPLY_CHANNEL, errorsReplyChannel);
|
errorChannelHeaders.put(MessageHeaders.REPLY_CHANNEL, errorsReplyChannel);
|
||||||
errorChannelHeaders.put(MessageHeaders.ERROR_CHANNEL, errorsReplyChannel);
|
errorChannelHeaders.put(MessageHeaders.ERROR_CHANNEL, errorsReplyChannel);
|
||||||
this.channel
|
this.channel.send(new ErrorMessage(
|
||||||
.send(new ErrorMessage(
|
new MessagingException(MessageBuilder.withPayload("hi")
|
||||||
new MessagingException(MessageBuilder.withPayload("hi")
|
.setHeader(TraceMessageHeaders.TRACE_ID_NAME, "000000000000000a")
|
||||||
.setHeader(TraceMessageHeaders.TRACE_ID_NAME,
|
.setHeader(TraceMessageHeaders.SPAN_ID_NAME, "000000000000000a")
|
||||||
"000000000000000a")
|
.setReplyChannel(deadReplyChannel).setErrorChannel(deadReplyChannel).build()),
|
||||||
.setHeader(TraceMessageHeaders.SPAN_ID_NAME,
|
errorChannelHeaders));
|
||||||
"000000000000000a")
|
|
||||||
.setReplyChannel(deadReplyChannel)
|
|
||||||
.setErrorChannel(deadReplyChannel).build()),
|
|
||||||
errorChannelHeaders));
|
|
||||||
|
|
||||||
this.message = this.channel.receive();
|
this.message = this.channel.receive();
|
||||||
|
|
||||||
assertThat(this.message).isNotNull();
|
assertThat(this.message).isNotNull();
|
||||||
String spanId = this.message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME,
|
String spanId = this.message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME, String.class);
|
||||||
String.class);
|
|
||||||
assertThat(spanId).isNotNull();
|
assertThat(spanId).isNotNull();
|
||||||
String traceId = this.message.getHeaders().get(TraceMessageHeaders.TRACE_ID_NAME,
|
String traceId = this.message.getHeaders().get(TraceMessageHeaders.TRACE_ID_NAME, String.class);
|
||||||
String.class);
|
|
||||||
assertThat(traceId).isEqualTo("000000000000000a");
|
assertThat(traceId).isEqualTo("000000000000000a");
|
||||||
assertThat(spanId).isNotEqualTo("000000000000000a");
|
assertThat(spanId).isNotEqualTo("000000000000000a");
|
||||||
assertThat(this.spans).hasSize(2);
|
assertThat(this.spans).hasSize(2);
|
||||||
assertThat(this.message.getHeaders().getReplyChannel())
|
assertThat(this.message.getHeaders().getReplyChannel()).isSameAs(errorsReplyChannel);
|
||||||
.isSameAs(errorsReplyChannel);
|
assertThat(this.message.getHeaders().getErrorChannel()).isSameAs(errorsReplyChannel);
|
||||||
assertThat(this.message.getHeaders().getErrorChannel())
|
|
||||||
.isSameAs(errorsReplyChannel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void errorMessageOriginalMessageRetained() {
|
public void errorMessageOriginalMessageRetained() {
|
||||||
this.channel.addInterceptor(this.interceptor);
|
this.channel.addInterceptor(this.interceptor);
|
||||||
Message<?> originalMessage = MessageBuilder.withPayload("Hello")
|
Message<?> originalMessage = MessageBuilder.withPayload("Hello").setHeader("header", "value").build();
|
||||||
.setHeader("header", "value").build();
|
Message<?> failedMessage = MessageBuilder.fromMessage(originalMessage).removeHeader("header").build();
|
||||||
Message<?> failedMessage = MessageBuilder.fromMessage(originalMessage)
|
this.channel.send(
|
||||||
.removeHeader("header").build();
|
new ErrorMessage(new MessagingException(failedMessage), originalMessage.getHeaders(), originalMessage));
|
||||||
this.channel.send(new ErrorMessage(new MessagingException(failedMessage),
|
|
||||||
originalMessage.getHeaders(), originalMessage));
|
|
||||||
|
|
||||||
this.message = this.channel.receive();
|
this.message = this.channel.receive();
|
||||||
|
|
||||||
assertThat(this.message).isNotNull();
|
assertThat(this.message).isNotNull();
|
||||||
assertThat(this.message).isInstanceOfSatisfying(ErrorMessage.class,
|
assertThat(this.message).isInstanceOfSatisfying(ErrorMessage.class, errorMessage -> {
|
||||||
errorMessage -> {
|
assertThat(errorMessage.getOriginalMessage()).isSameAs(originalMessage);
|
||||||
assertThat(errorMessage.getOriginalMessage())
|
assertThat(errorMessage.getHeaders().get("header")).isEqualTo("value");
|
||||||
.isSameAs(originalMessage);
|
});
|
||||||
assertThat(errorMessage.getHeaders().get("header"))
|
|
||||||
.isEqualTo("value");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -311,22 +289,60 @@ public class TracingChannelInterceptorTest {
|
|||||||
Map<String, Object> errorChannelHeaders = new HashMap<>();
|
Map<String, Object> errorChannelHeaders = new HashMap<>();
|
||||||
errorChannelHeaders.put(TraceMessageHeaders.TRACE_ID_NAME, "000000000000000a");
|
errorChannelHeaders.put(TraceMessageHeaders.TRACE_ID_NAME, "000000000000000a");
|
||||||
errorChannelHeaders.put(TraceMessageHeaders.SPAN_ID_NAME, "000000000000000a");
|
errorChannelHeaders.put(TraceMessageHeaders.SPAN_ID_NAME, "000000000000000a");
|
||||||
this.channel.send(new ErrorMessage(new MessagingException("exception"),
|
this.channel.send(new ErrorMessage(new MessagingException("exception"), errorChannelHeaders));
|
||||||
errorChannelHeaders));
|
|
||||||
|
|
||||||
this.message = this.channel.receive();
|
this.message = this.channel.receive();
|
||||||
|
|
||||||
assertThat(this.message).isNotNull();
|
assertThat(this.message).isNotNull();
|
||||||
String spanId = this.message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME,
|
String spanId = this.message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME, String.class);
|
||||||
String.class);
|
|
||||||
assertThat(spanId).isNotNull();
|
assertThat(spanId).isNotNull();
|
||||||
String traceId = this.message.getHeaders().get(TraceMessageHeaders.TRACE_ID_NAME,
|
String traceId = this.message.getHeaders().get(TraceMessageHeaders.TRACE_ID_NAME, String.class);
|
||||||
String.class);
|
|
||||||
assertThat(traceId).isEqualTo("000000000000000a");
|
assertThat(traceId).isEqualTo("000000000000000a");
|
||||||
assertThat(spanId).isNotEqualTo("000000000000000a");
|
assertThat(spanId).isNotEqualTo("000000000000000a");
|
||||||
assertThat(this.spans).hasSize(2);
|
assertThat(this.spans).hasSize(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void should_store_kafka_as_remote_service_name_when_kafka_header_is_present() {
|
||||||
|
ExecutorSubscribableChannel channel = new ExecutorSubscribableChannel();
|
||||||
|
channel.addInterceptor(this.interceptor);
|
||||||
|
List<Message<?>> messages = new ArrayList<>();
|
||||||
|
channel.subscribe(messages::add);
|
||||||
|
|
||||||
|
Map<String, Object> headers = new HashMap<>();
|
||||||
|
headers.put(KafkaHeaders.MESSAGE_KEY, "hello");
|
||||||
|
channel.send(MessageBuilder.createMessage("foo", new MessageHeaders(headers)));
|
||||||
|
|
||||||
|
assertThat(this.spans).flatExtracting(Span::remoteServiceName).contains("kafka");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void should_store_rabbitmq_as_remote_service_name_when_rabbit_header_is_present() {
|
||||||
|
ExecutorSubscribableChannel channel = new ExecutorSubscribableChannel();
|
||||||
|
channel.addInterceptor(this.interceptor);
|
||||||
|
List<Message<?>> messages = new ArrayList<>();
|
||||||
|
channel.subscribe(messages::add);
|
||||||
|
|
||||||
|
Map<String, Object> headers = new HashMap<>();
|
||||||
|
headers.put(AmqpHeaders.RECEIVED_ROUTING_KEY, "hello");
|
||||||
|
channel.send(MessageBuilder.createMessage("foo", new MessageHeaders(headers)));
|
||||||
|
|
||||||
|
assertThat(this.spans).flatExtracting(Span::remoteServiceName).contains("rabbitmq");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void should_store_broker_as_remote_service_name_when_no_special_headers_were_found() {
|
||||||
|
ExecutorSubscribableChannel channel = new ExecutorSubscribableChannel();
|
||||||
|
channel.addInterceptor(this.interceptor);
|
||||||
|
List<Message<?>> messages = new ArrayList<>();
|
||||||
|
channel.subscribe(messages::add);
|
||||||
|
|
||||||
|
Map<String, Object> headers = new HashMap<>();
|
||||||
|
channel.send(MessageBuilder.createMessage("foo", new MessageHeaders(headers)));
|
||||||
|
|
||||||
|
assertThat(this.spans).flatExtracting(Span::remoteServiceName).containsOnly("broker", null);
|
||||||
|
}
|
||||||
|
|
||||||
ChannelInterceptor producerSideOnly(ChannelInterceptor delegate) {
|
ChannelInterceptor producerSideOnly(ChannelInterceptor delegate) {
|
||||||
return new ChannelInterceptorAdapter() {
|
return new ChannelInterceptorAdapter() {
|
||||||
@Override
|
@Override
|
||||||
@@ -335,8 +351,7 @@ public class TracingChannelInterceptorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterSendCompletion(Message<?> message, MessageChannel channel,
|
public void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, Exception ex) {
|
||||||
boolean sent, Exception ex) {
|
|
||||||
delegate.afterSendCompletion(message, channel, sent, ex);
|
delegate.afterSendCompletion(message, channel, sent, ex);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -350,29 +365,24 @@ public class TracingChannelInterceptorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterReceiveCompletion(Message<?> message, MessageChannel channel,
|
public void afterReceiveCompletion(Message<?> message, MessageChannel channel, Exception ex) {
|
||||||
Exception ex) {
|
|
||||||
delegate.afterReceiveCompletion(message, channel, ex);
|
delegate.afterReceiveCompletion(message, channel, ex);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
ExecutorChannelInterceptor executorSideOnly(ChannelInterceptor delegate) {
|
ExecutorChannelInterceptor executorSideOnly(ChannelInterceptor delegate) {
|
||||||
class ExecutorSideOnly extends ChannelInterceptorAdapter
|
class ExecutorSideOnly extends ChannelInterceptorAdapter implements ExecutorChannelInterceptor {
|
||||||
implements ExecutorChannelInterceptor {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Message<?> beforeHandle(Message<?> message, MessageChannel channel,
|
public Message<?> beforeHandle(Message<?> message, MessageChannel channel, MessageHandler handler) {
|
||||||
MessageHandler handler) {
|
return ((ExecutorChannelInterceptor) delegate).beforeHandle(message, channel, handler);
|
||||||
return ((ExecutorChannelInterceptor) delegate).beforeHandle(message,
|
|
||||||
channel, handler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterMessageHandled(Message<?> message, MessageChannel channel,
|
public void afterMessageHandled(Message<?> message, MessageChannel channel, MessageHandler handler,
|
||||||
MessageHandler handler, Exception ex) {
|
Exception ex) {
|
||||||
((ExecutorChannelInterceptor) delegate).afterMessageHandled(message,
|
((ExecutorChannelInterceptor) delegate).afterMessageHandled(message, channel, handler, ex);
|
||||||
channel, handler, ex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,26 +16,29 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.sleuth.instrument.reactor;
|
package org.springframework.cloud.sleuth.instrument.reactor;
|
||||||
|
|
||||||
import org.assertj.core.api.BDDAssertions;
|
import brave.propagation.CurrentTraceContext;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.BDDMockito;
|
|
||||||
import reactor.util.context.Context;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
|
||||||
public class SpanSubscriptionProviderTests {
|
import static org.assertj.core.api.BDDAssertions.then;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
public class LazyBeanTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void should_return_default_tracing_instance_when_exception_thrown_upon_bean_retrieval() {
|
public void should_return_null_when_exception_thrown_upon_bean_retrieval() {
|
||||||
BeanFactory beanFactory = BDDMockito.mock(BeanFactory.class);
|
ConfigurableApplicationContext springContext = mock(
|
||||||
BDDMockito.when(beanFactory.getBean(BDDMockito.any(Class.class)))
|
ConfigurableApplicationContext.class);
|
||||||
|
|
||||||
|
when(springContext.getBean(CurrentTraceContext.class))
|
||||||
.thenThrow(new IllegalStateException());
|
.thenThrow(new IllegalStateException());
|
||||||
SpanSubscriptionProvider provider = new SpanSubscriptionProvider(beanFactory,
|
|
||||||
null, Context.empty(), "example");
|
|
||||||
|
|
||||||
SpanSubscription spanSubscription = provider.get();
|
LazyBean<CurrentTraceContext> provider = new LazyBean<>(springContext,
|
||||||
|
CurrentTraceContext.class);
|
||||||
|
|
||||||
BDDAssertions.then(spanSubscription).isNotNull();
|
then(provider.get()).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -51,9 +51,7 @@ public final class TraceReactorAutoConfigurationAccessorConfiguration {
|
|||||||
if (log.isTraceEnabled()) {
|
if (log.isTraceEnabled()) {
|
||||||
log.trace("Setting up hooks");
|
log.trace("Setting up hooks");
|
||||||
}
|
}
|
||||||
TraceReactorAutoConfiguration.TraceReactorConfiguration
|
HookRegisteringBeanDefinitionRegistryPostProcessor.setupHooks(context);
|
||||||
.traceHookRegisteringBeanDefinitionRegistryPostProcessor(context)
|
|
||||||
.setupHooks(context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import org.junit.runner.RunWith;
|
|||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.web.reactive.function.client.WebClient;
|
import org.springframework.web.reactive.function.client.WebClient;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,12 +32,12 @@ import org.springframework.web.reactive.function.client.WebClient;
|
|||||||
public class TraceWebClientBeanPostProcessorTest {
|
public class TraceWebClientBeanPostProcessorTest {
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
BeanFactory beanFactory;
|
ConfigurableApplicationContext springContext;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void should_add_filter_only_once_to_web_client() {
|
public void should_add_filter_only_once_to_web_client() {
|
||||||
TraceWebClientBeanPostProcessor processor = new TraceWebClientBeanPostProcessor(
|
TraceWebClientBeanPostProcessor processor = new TraceWebClientBeanPostProcessor(
|
||||||
this.beanFactory);
|
this.springContext);
|
||||||
WebClient client = WebClient.create();
|
WebClient client = WebClient.create();
|
||||||
|
|
||||||
client = (WebClient) processor.postProcessAfterInitialization(client, "foo");
|
client = (WebClient) processor.postProcessAfterInitialization(client, "foo");
|
||||||
@@ -53,7 +53,7 @@ public class TraceWebClientBeanPostProcessorTest {
|
|||||||
@Test
|
@Test
|
||||||
public void should_add_filter_only_once_to_web_client_via_builder() {
|
public void should_add_filter_only_once_to_web_client_via_builder() {
|
||||||
TraceWebClientBeanPostProcessor processor = new TraceWebClientBeanPostProcessor(
|
TraceWebClientBeanPostProcessor processor = new TraceWebClientBeanPostProcessor(
|
||||||
this.beanFactory);
|
this.springContext);
|
||||||
WebClient.Builder builder = WebClient.builder();
|
WebClient.Builder builder = WebClient.builder();
|
||||||
|
|
||||||
builder = (WebClient.Builder) processor.postProcessAfterInitialization(builder,
|
builder = (WebClient.Builder) processor.postProcessAfterInitialization(builder,
|
||||||
|
|||||||
@@ -0,0 +1,204 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2019 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.cloud.sleuth.instrument.reactor;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import brave.propagation.CurrentTraceContext;
|
||||||
|
import brave.propagation.CurrentTraceContext.Scope;
|
||||||
|
import brave.propagation.TraceContext;
|
||||||
|
import brave.sampler.Sampler;
|
||||||
|
import org.awaitility.Awaitility;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.reactivestreams.Publisher;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
import reactor.core.scheduler.Schedulers;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.BDDAssertions.then;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like {@link ScopePassingSpanSubscriberTests}, except this tests wiring with spring boot
|
||||||
|
* config.
|
||||||
|
*/
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
@SpringBootTest(classes = ScopePassingSpanSubscriberSpringBootTests.Config.class,
|
||||||
|
webEnvironment = SpringBootTest.WebEnvironment.NONE)
|
||||||
|
public class ScopePassingSpanSubscriberSpringBootTests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
CurrentTraceContext currentTraceContext;
|
||||||
|
|
||||||
|
TraceContext context = TraceContext.newBuilder().traceId(1).spanId(1).sampled(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
TraceContext context2 = TraceContext.newBuilder().traceId(1).spanId(2).sampled(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void should_pass_tracing_info_when_using_reactor() {
|
||||||
|
final AtomicReference<TraceContext> spanInOperation = new AtomicReference<>();
|
||||||
|
Publisher<Integer> traced = Flux.just(1, 2, 3);
|
||||||
|
|
||||||
|
try (Scope ws = this.currentTraceContext.newScope(context)) {
|
||||||
|
Flux.from(traced).map(d -> d + 1).map(d -> d + 1).map((d) -> {
|
||||||
|
spanInOperation.set(this.currentTraceContext.get());
|
||||||
|
return d + 1;
|
||||||
|
}).map(d -> d + 1).subscribe(d -> {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
then(this.currentTraceContext.get()).isNull();
|
||||||
|
then(spanInOperation.get()).isEqualTo(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void should_support_reactor_fusion_optimization() {
|
||||||
|
final AtomicReference<TraceContext> spanInOperation = new AtomicReference<>();
|
||||||
|
|
||||||
|
try (Scope ws = this.currentTraceContext.newScope(context)) {
|
||||||
|
Mono.just(1).flatMap(d -> Flux.just(d + 1).collectList().map(p -> p.get(0)))
|
||||||
|
.map(d -> d + 1).map((d) -> {
|
||||||
|
spanInOperation.set(this.currentTraceContext.get());
|
||||||
|
return d + 1;
|
||||||
|
}).map(d -> d + 1).subscribe(d -> {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
then(this.currentTraceContext.get()).isNull();
|
||||||
|
then(spanInOperation.get()).isEqualTo(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void should_pass_tracing_info_when_using_reactor_async() {
|
||||||
|
final AtomicReference<TraceContext> spanInOperation = new AtomicReference<>();
|
||||||
|
|
||||||
|
try (Scope ws = this.currentTraceContext.newScope(context)) {
|
||||||
|
Flux.just(1, 2, 3).publishOn(Schedulers.single()).log("reactor.1")
|
||||||
|
.map(d -> d + 1).map(d -> d + 1)
|
||||||
|
.publishOn(Schedulers.newSingle("secondThread")).log("reactor.2")
|
||||||
|
.map((d) -> {
|
||||||
|
spanInOperation.set(this.currentTraceContext.get());
|
||||||
|
return d + 1;
|
||||||
|
}).map(d -> d + 1).blockLast();
|
||||||
|
|
||||||
|
Awaitility.await()
|
||||||
|
.untilAsserted(() -> then(spanInOperation.get()).isEqualTo(context));
|
||||||
|
then(this.currentTraceContext.get()).isEqualTo(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
then(this.currentTraceContext.get()).isNull();
|
||||||
|
|
||||||
|
try (Scope ws = this.currentTraceContext.newScope(context2)) {
|
||||||
|
Flux.just(1, 2, 3).publishOn(Schedulers.single()).log("reactor.")
|
||||||
|
.map(d -> d + 1).map(d -> d + 1).map((d) -> {
|
||||||
|
spanInOperation.set(this.currentTraceContext.get());
|
||||||
|
return d + 1;
|
||||||
|
}).map(d -> d + 1).blockLast();
|
||||||
|
|
||||||
|
then(this.currentTraceContext.get()).isEqualTo(context2);
|
||||||
|
then(spanInOperation.get()).isEqualTo(context2);
|
||||||
|
}
|
||||||
|
|
||||||
|
then(this.currentTraceContext.get()).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onlyConsidersContextDuringSubscribe() {
|
||||||
|
Mono<TraceContext> fromMono = Mono.fromCallable(this.currentTraceContext::get);
|
||||||
|
|
||||||
|
try (Scope ws = this.currentTraceContext.newScope(context)) {
|
||||||
|
then(fromMono.map(context -> context).block()).isNotNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkTraceIdDuringZipOperation() {
|
||||||
|
final AtomicReference<TraceContext> spanInOperation = new AtomicReference<>();
|
||||||
|
final AtomicReference<TraceContext> spanInZipOperation = new AtomicReference<>();
|
||||||
|
|
||||||
|
try (Scope ws = this.currentTraceContext.newScope(context)) {
|
||||||
|
Mono.fromCallable(this.currentTraceContext::get).map(span -> span)
|
||||||
|
.doOnNext(spanInOperation::set)
|
||||||
|
.zipWith(Mono.fromCallable(this.currentTraceContext::get)
|
||||||
|
.map(span -> span).doOnNext(spanInZipOperation::set))
|
||||||
|
.block();
|
||||||
|
}
|
||||||
|
|
||||||
|
then(spanInZipOperation).hasValue(context);
|
||||||
|
then(spanInOperation).hasValue(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
// #646
|
||||||
|
@Test
|
||||||
|
public void should_work_for_mono_just_with_flat_map() {
|
||||||
|
try (Scope ws = this.currentTraceContext.newScope(context)) {
|
||||||
|
Mono.just("value1")
|
||||||
|
.flatMap(request -> Mono.just("value2").then(Mono.just("foo")))
|
||||||
|
.map(a -> "qwe").block();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// #1030
|
||||||
|
@Test
|
||||||
|
public void checkTraceIdFromSubscriberContext() {
|
||||||
|
final AtomicReference<TraceContext> spanInSubscriberContext = new AtomicReference<>();
|
||||||
|
|
||||||
|
try (Scope ws = this.currentTraceContext.newScope(context)) {
|
||||||
|
Mono.subscriberContext().map(context -> this.currentTraceContext.get())
|
||||||
|
.doOnNext(spanInSubscriberContext::set).block();
|
||||||
|
}
|
||||||
|
|
||||||
|
then(spanInSubscriberContext).hasValue(context); // ok here
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void should_pass_tracing_info_into_inner_publishers() {
|
||||||
|
final AtomicReference<TraceContext> spanInOperation = new AtomicReference<>();
|
||||||
|
|
||||||
|
try (Scope ws = this.currentTraceContext.newScope(context)) {
|
||||||
|
Flux.range(0, 5)
|
||||||
|
.flatMap(it -> Mono.delay(Duration.ofMillis(1))
|
||||||
|
.map(context -> this.currentTraceContext.get())
|
||||||
|
.doOnNext(spanInOperation::set))
|
||||||
|
.blockFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
then(spanInOperation.get()).isEqualTo(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
@Configuration
|
||||||
|
static class Config {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
Sampler sampler() {
|
||||||
|
return Sampler.ALWAYS_SAMPLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -16,32 +16,59 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.sleuth.instrument.reactor;
|
package org.springframework.cloud.sleuth.instrument.reactor;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
import brave.propagation.CurrentTraceContext;
|
import brave.propagation.CurrentTraceContext;
|
||||||
import brave.propagation.CurrentTraceContext.Scope;
|
import brave.propagation.CurrentTraceContext.Scope;
|
||||||
import brave.propagation.TraceContext;
|
import brave.propagation.TraceContext;
|
||||||
|
import org.assertj.core.presentation.StandardRepresentation;
|
||||||
|
import org.awaitility.Awaitility;
|
||||||
|
import org.junit.After;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.reactivestreams.Publisher;
|
||||||
import org.mockito.BDDMockito;
|
import org.reactivestreams.Subscriber;
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
import org.reactivestreams.Subscription;
|
||||||
import reactor.core.CoreSubscriber;
|
import reactor.core.CoreSubscriber;
|
||||||
import reactor.core.publisher.BaseSubscriber;
|
import reactor.core.publisher.BaseSubscriber;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.util.context.Context;
|
import reactor.util.context.Context;
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.BDDAssertions.then;
|
import static org.assertj.core.api.BDDAssertions.then;
|
||||||
|
import static org.springframework.cloud.sleuth.instrument.reactor.ReactorSleuth.scopePassingSpanOperator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Marcin Grzejszczak
|
* @author Marcin Grzejszczak
|
||||||
*/
|
*/
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
|
||||||
public class ScopePassingSpanSubscriberTests {
|
public class ScopePassingSpanSubscriberTests {
|
||||||
|
|
||||||
CurrentTraceContext currentTraceContext = CurrentTraceContext.Default.create();
|
static {
|
||||||
|
// AssertJ will recognise QueueSubscription implements queue and try to invoke
|
||||||
|
// iterator. That's not allowed, and will cause an exception
|
||||||
|
// Fuseable$QueueSubscription.NOT_SUPPORTED_MESSAGE.
|
||||||
|
// This ensures AssertJ uses normal toString.
|
||||||
|
StandardRepresentation.registerFormatterForType(ScopePassingSpanSubscriber.class,
|
||||||
|
Objects::toString);
|
||||||
|
}
|
||||||
|
|
||||||
|
final CurrentTraceContext currentTraceContext = CurrentTraceContext.Default.create();
|
||||||
|
|
||||||
TraceContext context = TraceContext.newBuilder().traceId(1).spanId(1).sampled(true)
|
TraceContext context = TraceContext.newBuilder().traceId(1).spanId(1).sampled(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
TraceContext context2 = TraceContext.newBuilder().traceId(1).spanId(2).sampled(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
AnnotationConfigApplicationContext springContext = new AnnotationConfigApplicationContext();
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void close() {
|
||||||
|
springContext.close();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void should_propagate_current_context() {
|
public void should_propagate_current_context() {
|
||||||
ScopePassingSpanSubscriber<?> subscriber = new ScopePassingSpanSubscriber<>(null,
|
ScopePassingSpanSubscriber<?> subscriber = new ScopePassingSpanSubscriber<>(null,
|
||||||
@@ -53,28 +80,96 @@ public class ScopePassingSpanSubscriberTests {
|
|||||||
@Test
|
@Test
|
||||||
public void should_set_empty_context_when_context_is_null() {
|
public void should_set_empty_context_when_context_is_null() {
|
||||||
ScopePassingSpanSubscriber<?> subscriber = new ScopePassingSpanSubscriber<>(null,
|
ScopePassingSpanSubscriber<?> subscriber = new ScopePassingSpanSubscriber<>(null,
|
||||||
null, this.currentTraceContext, null);
|
Context.empty(), this.currentTraceContext, null);
|
||||||
|
|
||||||
then(subscriber.currentContext().isEmpty()).isTrue();
|
then(subscriber.currentContext().isEmpty()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void should_put_current_span_to_context() {
|
public void should_put_current_span_to_context() {
|
||||||
try (Scope ws = this.currentTraceContext.newScope(context)) {
|
try (Scope ws = this.currentTraceContext.newScope(context2)) {
|
||||||
CoreSubscriber<?> subscriber = ReactorSleuth.scopePassingSpanSubscription(
|
CoreSubscriber<?> subscriber = new ScopePassingSpanSubscriber<>(
|
||||||
beanFactory(), new BaseSubscriber<Object>() {
|
new BaseSubscriber<Object>() {
|
||||||
});
|
}, Context.empty(), currentTraceContext, context);
|
||||||
|
|
||||||
then(subscriber.currentContext().get(TraceContext.class)).isEqualTo(context);
|
then(subscriber.currentContext().get(TraceContext.class)).isEqualTo(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private BeanFactory beanFactory() {
|
@Test
|
||||||
BeanFactory beanFactory = BDDMockito.mock(BeanFactory.class);
|
public void should_not_trace_scalar_flows() {
|
||||||
BDDMockito.given(beanFactory.getBean(CurrentTraceContext.class))
|
springContext.registerBean(CurrentTraceContext.class, () -> currentTraceContext);
|
||||||
.willReturn(this.currentTraceContext);
|
springContext.refresh();
|
||||||
return beanFactory;
|
|
||||||
|
Function<? super Publisher<Integer>, ? extends Publisher<Integer>> transformer = scopePassingSpanOperator(
|
||||||
|
this.springContext);
|
||||||
|
|
||||||
|
try (Scope ws = this.currentTraceContext.newScope(context)) {
|
||||||
|
Subscriber<Object> assertNoSpanSubscriber = new CoreSubscriber<Object>() {
|
||||||
|
@Override
|
||||||
|
public void onSubscribe(Subscription s) {
|
||||||
|
s.request(Long.MAX_VALUE);
|
||||||
|
assertThat(s).isNotInstanceOf(ScopePassingSpanSubscriber.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNext(Object o) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Throwable t) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete() {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Subscriber<Object> assertSpanSubscriber = new CoreSubscriber<Object>() {
|
||||||
|
@Override
|
||||||
|
public void onSubscribe(Subscription s) {
|
||||||
|
s.request(Long.MAX_VALUE);
|
||||||
|
assertThat(s).isInstanceOf(ScopePassingSpanSubscriber.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNext(Object o) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Throwable t) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete() {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
transformer.apply(Mono.just(1).hide()).subscribe(assertSpanSubscriber);
|
||||||
|
|
||||||
|
transformer.apply(Mono.just(1)).subscribe(assertNoSpanSubscriber);
|
||||||
|
|
||||||
|
transformer.apply(Mono.<Integer>error(new Exception()).hide())
|
||||||
|
.subscribe(assertSpanSubscriber);
|
||||||
|
|
||||||
|
transformer.apply(Mono.error(new Exception()))
|
||||||
|
.subscribe(assertNoSpanSubscriber);
|
||||||
|
|
||||||
|
transformer.apply(Mono.<Integer>empty().hide())
|
||||||
|
.subscribe(assertSpanSubscriber);
|
||||||
|
|
||||||
|
transformer.apply(Mono.empty()).subscribe(assertNoSpanSubscriber);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Awaitility.await().untilAsserted(() -> {
|
||||||
|
then(this.currentTraceContext.get()).isNull();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,333 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013-2019 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.cloud.sleuth.instrument.reactor;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import brave.Span;
|
|
||||||
import brave.Tracer;
|
|
||||||
import brave.sampler.Sampler;
|
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
import org.awaitility.Awaitility;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.reactivestreams.Publisher;
|
|
||||||
import org.reactivestreams.Subscriber;
|
|
||||||
import org.reactivestreams.Subscription;
|
|
||||||
import reactor.core.CoreSubscriber;
|
|
||||||
import reactor.core.publisher.Flux;
|
|
||||||
import reactor.core.publisher.Mono;
|
|
||||||
import reactor.core.scheduler.Schedulers;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.test.context.junit4.SpringRunner;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.assertj.core.api.BDDAssertions.then;
|
|
||||||
|
|
||||||
@RunWith(SpringRunner.class)
|
|
||||||
@SpringBootTest(classes = SpanSubscriberTests.Config.class,
|
|
||||||
webEnvironment = SpringBootTest.WebEnvironment.NONE)
|
|
||||||
public class SpanSubscriberTests {
|
|
||||||
|
|
||||||
private static final Log log = LogFactory.getLog(SpanSubscriberTests.class);
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
Tracer tracer;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
ConfigurableApplicationContext factory;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void should_pass_tracing_info_when_using_reactor() {
|
|
||||||
Span span = this.tracer.nextSpan().name("foo").start();
|
|
||||||
final AtomicReference<Span> spanInOperation = new AtomicReference<>();
|
|
||||||
Publisher<Integer> traced = Flux.just(1, 2, 3);
|
|
||||||
log.info("Hello");
|
|
||||||
|
|
||||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
|
|
||||||
Flux.from(traced).map(d -> d + 1).map(d -> d + 1).map((d) -> {
|
|
||||||
spanInOperation.set(this.tracer.currentSpan());
|
|
||||||
return d + 1;
|
|
||||||
}).map(d -> d + 1).subscribe(System.out::println);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
span.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
then(this.tracer.currentSpan()).isNull();
|
|
||||||
then(spanInOperation.get().context().spanId()).isEqualTo(span.context().spanId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void should_support_reactor_fusion_optimization() {
|
|
||||||
Span span = this.tracer.nextSpan().name("foo").start();
|
|
||||||
final AtomicReference<Span> spanInOperation = new AtomicReference<>();
|
|
||||||
log.info("Hello");
|
|
||||||
|
|
||||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
|
|
||||||
Mono.just(1).flatMap(d -> Flux.just(d + 1).collectList().map(p -> p.get(0)))
|
|
||||||
.map(d -> d + 1).map((d) -> {
|
|
||||||
spanInOperation.set(this.tracer.currentSpan());
|
|
||||||
return d + 1;
|
|
||||||
}).map(d -> d + 1).subscribe(System.out::println);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
span.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
then(this.tracer.currentSpan()).isNull();
|
|
||||||
then(spanInOperation.get().context().spanId()).isEqualTo(span.context().spanId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void should_not_trace_scalar_flows() {
|
|
||||||
Span span = this.tracer.nextSpan().name("foo").start();
|
|
||||||
log.info("Hello");
|
|
||||||
|
|
||||||
// Disable global hooks for local hook testing
|
|
||||||
TraceReactorAutoConfigurationAccessorConfiguration.close();
|
|
||||||
|
|
||||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
|
|
||||||
|
|
||||||
Function<? super Publisher<Integer>, ? extends Publisher<Integer>> transformer = ReactorSleuth
|
|
||||||
.scopePassingSpanOperator(this.factory);
|
|
||||||
|
|
||||||
Subscriber<Object> assertNoSpanSubscriber = new CoreSubscriber<Object>() {
|
|
||||||
@Override
|
|
||||||
public void onSubscribe(Subscription s) {
|
|
||||||
s.request(Long.MAX_VALUE);
|
|
||||||
assertThat(s).isNotInstanceOf(ScopePassingSpanSubscriber.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNext(Object o) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(Throwable t) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onComplete() {
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Subscriber<Object> assertSpanSubscriber = new CoreSubscriber<Object>() {
|
|
||||||
@Override
|
|
||||||
public void onSubscribe(Subscription s) {
|
|
||||||
s.request(Long.MAX_VALUE);
|
|
||||||
assertThat(s).isInstanceOf(ScopePassingSpanSubscriber.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNext(Object o) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(Throwable t) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onComplete() {
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
transformer.apply(Mono.just(1).hide()).subscribe(assertSpanSubscriber);
|
|
||||||
|
|
||||||
transformer.apply(Mono.just(1)).subscribe(assertNoSpanSubscriber);
|
|
||||||
|
|
||||||
transformer.apply(Mono.<Integer>error(new Exception()).hide())
|
|
||||||
.subscribe(assertSpanSubscriber);
|
|
||||||
|
|
||||||
transformer.apply(Mono.error(new Exception()))
|
|
||||||
.subscribe(assertNoSpanSubscriber);
|
|
||||||
|
|
||||||
transformer.apply(Mono.<Integer>empty().hide())
|
|
||||||
.subscribe(assertSpanSubscriber);
|
|
||||||
|
|
||||||
transformer.apply(Mono.empty()).subscribe(assertNoSpanSubscriber);
|
|
||||||
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
span.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
Awaitility.await().untilAsserted(() -> {
|
|
||||||
then(this.tracer.currentSpan()).isNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
TraceReactorAutoConfigurationAccessorConfiguration.setup(this.factory);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void should_pass_tracing_info_when_using_reactor_async() {
|
|
||||||
Span span = this.tracer.nextSpan().name("foo").start();
|
|
||||||
final AtomicReference<Span> spanInOperation = new AtomicReference<>();
|
|
||||||
log.info("Hello");
|
|
||||||
|
|
||||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
|
|
||||||
Flux.just(1, 2, 3).publishOn(Schedulers.single()).log("reactor.1")
|
|
||||||
.map(d -> d + 1).map(d -> d + 1)
|
|
||||||
.publishOn(Schedulers.newSingle("secondThread")).log("reactor.2")
|
|
||||||
.map((d) -> {
|
|
||||||
spanInOperation.set(this.tracer.currentSpan());
|
|
||||||
return d + 1;
|
|
||||||
}).map(d -> d + 1).blockLast();
|
|
||||||
|
|
||||||
Awaitility.await().untilAsserted(() -> {
|
|
||||||
then(spanInOperation.get().context().traceId())
|
|
||||||
.isEqualTo(span.context().traceId());
|
|
||||||
});
|
|
||||||
then(this.tracer.currentSpan()).isEqualTo(span);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
span.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
then(this.tracer.currentSpan()).isNull();
|
|
||||||
Span foo2 = this.tracer.nextSpan().name("foo").start();
|
|
||||||
|
|
||||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(foo2)) {
|
|
||||||
Flux.just(1, 2, 3).publishOn(Schedulers.single()).log("reactor.")
|
|
||||||
.map(d -> d + 1).map(d -> d + 1).map((d) -> {
|
|
||||||
spanInOperation.set(this.tracer.currentSpan());
|
|
||||||
return d + 1;
|
|
||||||
}).map(d -> d + 1).blockLast();
|
|
||||||
|
|
||||||
then(this.tracer.currentSpan()).isEqualTo(foo2);
|
|
||||||
// parent cause there's an async span in the meantime
|
|
||||||
then(spanInOperation.get().context().traceId())
|
|
||||||
.isEqualTo(foo2.context().traceId());
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
foo2.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
then(this.tracer.currentSpan()).isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void checkSequenceOfOperations() {
|
|
||||||
Span parentSpan = this.tracer.nextSpan().name("foo").start();
|
|
||||||
log.info("Hello");
|
|
||||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(parentSpan)) {
|
|
||||||
final Long spanId = Mono.fromCallable(this.tracer::currentSpan)
|
|
||||||
.map(span -> span.context().spanId()).block();
|
|
||||||
then(spanId).isNotNull();
|
|
||||||
|
|
||||||
final Long secondSpanId = Mono.fromCallable(this.tracer::currentSpan)
|
|
||||||
.map(span -> span.context().spanId()).block();
|
|
||||||
then(secondSpanId).isEqualTo(spanId); // different trace ids here
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void checkTraceIdDuringZipOperation() {
|
|
||||||
Span initSpan = this.tracer.nextSpan().name("foo").start();
|
|
||||||
final AtomicReference<Long> spanInOperation = new AtomicReference<>();
|
|
||||||
final AtomicReference<Long> spanInZipOperation = new AtomicReference<>();
|
|
||||||
|
|
||||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(initSpan)) {
|
|
||||||
Mono.fromCallable(this.tracer::currentSpan)
|
|
||||||
.map(span -> span.context().spanId()).doOnNext(spanInOperation::set)
|
|
||||||
.zipWith(Mono.fromCallable(this.tracer::currentSpan)
|
|
||||||
.map(span -> span.context().spanId())
|
|
||||||
.doOnNext(spanInZipOperation::set))
|
|
||||||
.block();
|
|
||||||
}
|
|
||||||
|
|
||||||
then(spanInZipOperation).hasValue(initSpan.context().spanId()); // ok here
|
|
||||||
then(spanInOperation).hasValue(initSpan.context().spanId()); // Expecting
|
|
||||||
// <AtomicReference[null]>
|
|
||||||
// to have value:
|
|
||||||
// <1L> but did
|
|
||||||
// not.
|
|
||||||
}
|
|
||||||
|
|
||||||
// #646
|
|
||||||
@Test
|
|
||||||
public void should_work_for_mono_just_with_flat_map() {
|
|
||||||
Span initSpan = this.tracer.nextSpan().name("foo").start();
|
|
||||||
log.info("Hello");
|
|
||||||
|
|
||||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(initSpan)) {
|
|
||||||
Mono.just("value1")
|
|
||||||
.flatMap(request -> Mono.just("value2").then(Mono.just("foo")))
|
|
||||||
.map(a -> "qwe").block();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// #1030
|
|
||||||
@Test
|
|
||||||
public void checkTraceIdFromSubscriberContext() {
|
|
||||||
Span initSpan = this.tracer.nextSpan().name("foo").start();
|
|
||||||
final AtomicReference<Long> spanInSubscriberContext = new AtomicReference<>();
|
|
||||||
|
|
||||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(initSpan)) {
|
|
||||||
Mono.subscriberContext()
|
|
||||||
.map(context -> this.tracer.currentSpan().context().spanId())
|
|
||||||
.doOnNext(spanInSubscriberContext::set).block();
|
|
||||||
}
|
|
||||||
|
|
||||||
then(spanInSubscriberContext).hasValue(initSpan.context().spanId()); // ok here
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void should_pass_tracing_info_into_inner_publishers() {
|
|
||||||
Span span = this.tracer.nextSpan().name("foo").start();
|
|
||||||
final AtomicReference<Span> spanInOperation = new AtomicReference<>();
|
|
||||||
|
|
||||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
|
|
||||||
Flux.range(0, 5)
|
|
||||||
.flatMap(it -> Mono.delay(Duration.ofMillis(1))
|
|
||||||
.map(context -> this.tracer.currentSpan())
|
|
||||||
.doOnNext(spanInOperation::set))
|
|
||||||
.blockFirst();
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
span.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
then(spanInOperation.get().context().spanId()).isEqualTo(span.context().spanId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@EnableAutoConfiguration
|
|
||||||
@Configuration
|
|
||||||
static class Config {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
Sampler sampler() {
|
|
||||||
return Sampler.ALWAYS_SAMPLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -51,9 +51,7 @@ public final class TraceReactorAutoConfigurationAccessorConfiguration {
|
|||||||
if (log.isTraceEnabled()) {
|
if (log.isTraceEnabled()) {
|
||||||
log.trace("Setting up hooks");
|
log.trace("Setting up hooks");
|
||||||
}
|
}
|
||||||
TraceReactorAutoConfiguration.TraceReactorConfiguration
|
HookRegisteringBeanDefinitionRegistryPostProcessor.setupHooks(context);
|
||||||
.traceHookRegisteringBeanDefinitionRegistryPostProcessor(context)
|
|
||||||
.setupHooks(context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user