Fix JMS Inbound Endpoints for observation

The `JmsMessageDrivenEndpoint` delegates all the hard work to the
`ChannelPublishingJmsMessageListener`, but missed to propagate an `ObservationRegistry`
and other related options.
The `JmsInboundGateway` is worse: it delegated to the `JmsMessageDrivenEndpoint`

* Add `IntegrationObservation.HANDLER` observation to the `MessagingGatewaySupport.send()`
operation: used by the delegate in the `ChannelPublishingJmsMessageListener`
* Expose and propagate observation-related options from `JmsInboundGateway`
and `JmsMessageDrivenEndpoint`
* Expose `observationConvention()` option on the `MessagingGatewaySpec`
and `MessageProducerSpec`
* Remove unused imports
* Do not start a new `RECEIVER` observation if there is already `SERVER` one
* Fix `MessagingGatewaySupport` for `Observation.NOOP` check.
The parent process may still use `ObservationRegistry.NOOP` which sets
`Observation.NOOP` instance into the current context and thread local.

**Cherry-pick to `6.1.x` & `6.0.x`**
This commit is contained in:
Artem Bilan
2023-09-19 12:02:49 -04:00
committed by GitHub
parent 459de03ea3
commit 9ad2b8a8e2
8 changed files with 208 additions and 35 deletions

View File

@@ -18,6 +18,7 @@ package org.springframework.integration.dsl;
import org.springframework.integration.endpoint.MessageProducerSupport;
import org.springframework.integration.support.ErrorMessageStrategy;
import org.springframework.integration.support.management.observation.MessageReceiverObservationConvention;
import org.springframework.lang.Nullable;
import org.springframework.messaging.MessageChannel;
@@ -152,4 +153,16 @@ public abstract class MessageProducerSpec<S extends MessageProducerSpec<S, P>, P
return _this();
}
/**
* Provide a custom {@link MessageReceiverObservationConvention}.
* @param observationConvention the observation convention to use.
* @return the spec.
* @since 6.0.8
* @see MessageProducerSupport#setObservationConvention(MessageReceiverObservationConvention)
*/
public S observationConvention(MessageReceiverObservationConvention observationConvention) {
this.target.setObservationConvention(observationConvention);
return _this();
}
}

View File

@@ -19,6 +19,7 @@ package org.springframework.integration.dsl;
import org.springframework.integration.gateway.MessagingGatewaySupport;
import org.springframework.integration.mapping.InboundMessageMapper;
import org.springframework.integration.mapping.OutboundMessageMapper;
import org.springframework.integration.support.management.observation.MessageRequestReplyReceiverObservationConvention;
import org.springframework.lang.Nullable;
import org.springframework.messaging.MessageChannel;
@@ -205,4 +206,16 @@ public abstract class MessagingGatewaySpec<S extends MessagingGatewaySpec<S, G>,
return _this();
}
/**
* Provide a custom {@link MessageRequestReplyReceiverObservationConvention}.
* @param observationConvention the observation convention to use.
* @return the spec.
* @since 6.0.8
* @see MessagingGatewaySupport#setObservationConvention(MessageRequestReplyReceiverObservationConvention)
*/
public S observationConvention(MessageRequestReplyReceiverObservationConvention observationConvention) {
this.target.setObservationConvention(observationConvention);
return _this();
}
}

View File

@@ -22,6 +22,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
@@ -56,12 +57,16 @@ import org.springframework.integration.support.MutableMessageBuilder;
import org.springframework.integration.support.converter.SimpleMessageConverter;
import org.springframework.integration.support.management.IntegrationInboundManagement;
import org.springframework.integration.support.management.IntegrationManagedResource;
import org.springframework.integration.support.management.TrackableComponent;
import org.springframework.integration.support.management.metrics.MeterFacade;
import org.springframework.integration.support.management.metrics.MetricsCaptor;
import org.springframework.integration.support.management.metrics.SampleFacade;
import org.springframework.integration.support.management.metrics.TimerFacade;
import org.springframework.integration.support.management.observation.DefaultMessageReceiverObservationConvention;
import org.springframework.integration.support.management.observation.DefaultMessageRequestReplyReceiverObservationConvention;
import org.springframework.integration.support.management.observation.IntegrationObservation;
import org.springframework.integration.support.management.observation.MessageReceiverContext;
import org.springframework.integration.support.management.observation.MessageReceiverObservationConvention;
import org.springframework.integration.support.management.observation.MessageRequestReplyReceiverContext;
import org.springframework.integration.support.management.observation.MessageRequestReplyReceiverObservationConvention;
import org.springframework.lang.Nullable;
@@ -91,7 +96,7 @@ import org.springframework.util.Assert;
*/
@IntegrationManagedResource
public abstract class MessagingGatewaySupport extends AbstractEndpoint
implements org.springframework.integration.support.management.TrackableComponent,
implements TrackableComponent,
IntegrationInboundManagement, IntegrationPattern {
protected final ConvertingMessagingTemplate messagingTemplate; // NOSONAR
@@ -144,6 +149,8 @@ public abstract class MessagingGatewaySupport extends AbstractEndpoint
@Nullable
private MessageRequestReplyReceiverObservationConvention observationConvention;
private MessageReceiverObservationConvention receiverObservationConvention;
private volatile AbstractEndpoint replyMessageCorrelator;
private volatile boolean initialized;
@@ -384,6 +391,10 @@ public abstract class MessagingGatewaySupport extends AbstractEndpoint
this.observationConvention = observationConvention;
}
public void setReceiverObservationConvention(MessageReceiverObservationConvention receiverObservationConvention) {
this.receiverObservationConvention = receiverObservationConvention;
}
@Override
protected void onInit() {
Assert.state(!(this.requestChannelName != null && this.requestChannel != null),
@@ -468,27 +479,65 @@ public abstract class MessagingGatewaySupport extends AbstractEndpoint
MessageChannel channel = getRequestChannel();
Assert.state(channel != null,
"send is not supported, because no request channel has been configured");
SampleFacade sample = null;
if (this.metricsCaptor != null) {
sample = this.metricsCaptor.start();
Message<?> requestMessage = this.messagingTemplate.doConvert(object, null, this.historyWritingPostProcessor);
if (!ObservationRegistry.NOOP.equals(this.observationRegistry)
&& (this.observationRegistry.getCurrentObservation() == null
|| Observation.NOOP.equals(this.observationRegistry.getCurrentObservation()))) {
sendWithObservation(channel, requestMessage);
}
else if (this.metricsCaptor != null) {
sendWithMetrics(channel, requestMessage);
}
else {
doSend(channel, requestMessage);
}
}
private void sendWithObservation(MessageChannel channel, Message<?> message) {
try {
this.messagingTemplate.convertAndSend(channel, object, this.historyWritingPostProcessor);
if (sample != null) {
sample.stop(sendTimer());
}
IntegrationObservation.HANDLER.observation(
this.receiverObservationConvention,
DefaultMessageReceiverObservationConvention.INSTANCE,
() -> new MessageReceiverContext(message, getComponentName()),
this.observationRegistry)
.observe(() -> this.messagingTemplate.send(channel, message));
}
catch (Exception e) {
if (sample != null) {
sample.stop(buildSendTimer(false, e.getClass().getSimpleName()));
}
MessageChannel errorChan = getErrorChannel();
if (errorChan != null) {
this.messagingTemplate.send(errorChan, new ErrorMessage(e));
}
else {
rethrow(e, "failed to send message");
}
catch (Exception ex) {
sendErrorMessage(ex, message);
}
}
private void sendWithMetrics(MessageChannel channel, Message<?> message) {
SampleFacade sample = this.metricsCaptor.start();
try {
this.messagingTemplate.send(channel, message);
sample.stop(sendTimer());
}
catch (Exception ex) {
sample.stop(buildSendTimer(false, ex.getClass().getSimpleName()));
sendErrorMessage(ex, message);
}
}
private void doSend(MessageChannel channel, Message<?> message) {
try {
this.messagingTemplate.send(channel, message);
}
catch (Exception ex) {
sendErrorMessage(ex, message);
}
}
private void sendErrorMessage(Exception exception, Message<?> failedMessage) {
MessageChannel errorChan = getErrorChannel();
if (errorChan != null) {
this.messagingTemplate.send(errorChan, buildErrorMessage(failedMessage, exception));
}
else {
rethrow(exception, "failed to send message");
}
}