Propagate and Observation from Reactive context (#3999)
To propagate an `Observation` from reactive stream (e.g. WebFlux) we have to capture its context and set it into the current thread scope. * Add a `io.micrometer:context-propagation` dependency to support reactive context propagation * Populate a `parentObservation` in the `IntegrationObservation.PRODUCER.observation()` since it is not available for tracing on `Observation.onStart()`. Might be tentative until upcoming fix in Micrometer Observation * Populate from reactive context in the `WebFluxInboundEndpoint` where we use just `send()` operation downstream * Populate from reactive context in the `MessagingGatewaySupport` where we use `send()` operation downstream or `FluxMessageChannel.subscribeTo()` * Use `contextCapture()` in the `FluxMessageChannel` to gather a `ThreadLocal` info into a Reactor context and then set back to `ThreadLocal` in the `transformDeferredContextual()` which really happens on a different thread * Verify a trace propagation from WebFlux to an integration flow via Brave instrumentation in the `WebFluxObservationPropagationTests`
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -335,6 +335,7 @@ public abstract class AbstractMessageChannel extends IntegrationObjectSupport
|
||||
DefaultMessageSenderObservationConvention.INSTANCE,
|
||||
() -> new MessageSenderContext(messageToSend, getComponentName()),
|
||||
this.observationRegistry)
|
||||
.parentObservation(this.observationRegistry.getCurrentObservation()) // TODO until the fix in micrometer-observation
|
||||
.observe(() -> sendInternal(messageToSend, timeout));
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.time.Duration;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
import io.micrometer.context.ContextSnapshot;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.reactivestreams.Subscriber;
|
||||
import reactor.core.Disposable;
|
||||
@@ -111,17 +112,20 @@ public class FluxMessageChannel extends AbstractMessageChannel
|
||||
Flux.from(publisher)
|
||||
.delaySubscription(this.subscribedSignal.asFlux().filter(Boolean::booleanValue).next())
|
||||
.publishOn(this.scheduler)
|
||||
.doOnNext((message) -> {
|
||||
try {
|
||||
if (!send(message)) {
|
||||
throw new MessageDeliveryException(message,
|
||||
"Failed to send message to channel '" + this);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.warn(ex, () -> "Error during processing event: " + message);
|
||||
}
|
||||
})
|
||||
.transformDeferredContextual((flux, contextView) ->
|
||||
flux.doOnNext((message) -> {
|
||||
var scope = ContextSnapshot.setAllThreadLocalsFrom(contextView);
|
||||
try (scope) {
|
||||
if (!send(message)) {
|
||||
throw new MessageDeliveryException(message,
|
||||
"Failed to send message to channel '" + this);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.warn(ex, () -> "Error during processing event: " + message);
|
||||
}
|
||||
}))
|
||||
.contextCapture()
|
||||
.subscribe());
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import io.micrometer.context.ContextSnapshot;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.reactivestreams.Subscriber;
|
||||
@@ -720,7 +721,7 @@ public abstract class MessagingGatewaySupport extends AbstractEndpoint
|
||||
throw new MessageMappingException("Cannot map to message: " + object, e);
|
||||
}
|
||||
|
||||
return Mono.defer(() -> {
|
||||
return Mono.deferContextual(contextView -> {
|
||||
Object originalReplyChannelHeader = requestMessage.getHeaders().getReplyChannel();
|
||||
Object originalErrorChannelHeader = requestMessage.getHeaders().getErrorChannel();
|
||||
|
||||
@@ -738,7 +739,10 @@ public abstract class MessagingGatewaySupport extends AbstractEndpoint
|
||||
.setErrorChannel(replyChan)
|
||||
.build();
|
||||
|
||||
sendMessageForReactiveFlow(requestChannel, messageToSend);
|
||||
var scope = ContextSnapshot.setAllThreadLocalsFrom(contextView);
|
||||
try (scope) {
|
||||
sendMessageForReactiveFlow(requestChannel, messageToSend);
|
||||
}
|
||||
|
||||
return buildReplyMono(requestMessage, replyChan.replyMono.asMono(), error,
|
||||
originalReplyChannelHeader, originalErrorChannelHeader);
|
||||
|
||||
Reference in New Issue
Block a user