Skip to content

Commit 0bb309f

Browse files
committed
Instrument @JmsListener session for response messages
Prior to this commit, the observation instrumentation for `@JmsListener` annotated methods (implemented in `AbstractMessageListenerContainer` would not instrument the JMS session using the Micrometer JMS support. This means that response messages returned from the listener method would be sent but no observation would be recorded. As a result, tracing message properties would be also missing. This commit ensures that the session provided to the listener method is instrumented beforehand, if Micrometer is on the classpath and an observation registry has been configured. Fixes gh-33221
1 parent e8630f3 commit 0bb309f

File tree

3 files changed

+43
-0
lines changed

3 files changed

+43
-0
lines changed

framework-docs/modules/ROOT/pages/integration/observability.adoc

+2
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ include-code::./JmsTemplatePublish[]
162162

163163
It uses the `io.micrometer.jakarta9.instrument.jms.DefaultJmsPublishObservationConvention` by default, backed by the `io.micrometer.jakarta9.instrument.jms.JmsPublishObservationContext`.
164164

165+
Similar observations are recorded with `@JmsListener` annotated methods when response messages are returned from the listener method.
166+
165167
[[observability.jms.process]]
166168
=== JMS message Processing instrumentation
167169

spring-jms/src/main/java/org/springframework/jms/listener/AbstractMessageListenerContainer.java

+12
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.jms.listener;
1818

1919
import io.micrometer.jakarta9.instrument.jms.DefaultJmsProcessObservationConvention;
20+
import io.micrometer.jakarta9.instrument.jms.JmsInstrumentation;
2021
import io.micrometer.jakarta9.instrument.jms.JmsObservationDocumentation;
2122
import io.micrometer.jakarta9.instrument.jms.JmsProcessObservationContext;
2223
import io.micrometer.jakarta9.instrument.jms.JmsProcessObservationConvention;
@@ -772,6 +773,9 @@ protected void doInvokeListener(SessionAwareMessageListener listener, Session se
772773
Observation observation = createObservation(message);
773774
try {
774775
Session sessionToUse = session;
776+
if (micrometerJakartaPresent && this.observationRegistry != null) {
777+
sessionToUse = MicrometerInstrumentation.instrumentSession(sessionToUse, this.observationRegistry);
778+
}
775779
if (!isExposeListenerSession()) {
776780
// We need to expose a separate Session.
777781
conToClose = createConnection();
@@ -992,6 +996,14 @@ protected void invokeErrorHandler(Throwable ex) {
992996
private static class MessageRejectedWhileStoppingException extends RuntimeException {
993997
}
994998

999+
private abstract static class MicrometerInstrumentation {
1000+
1001+
static Session instrumentSession(Session session, ObservationRegistry registry) {
1002+
return JmsInstrumentation.instrumentSession(session, registry);
1003+
}
1004+
1005+
}
1006+
9951007
private abstract static class ObservationFactory {
9961008

9971009
private static final JmsProcessObservationConvention DEFAULT_CONVENTION = new DefaultJmsProcessObservationConvention();

spring-jms/src/test/java/org/springframework/jms/listener/MessageListenerContainerObservationTests.java

+29
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323

2424
import io.micrometer.observation.Observation;
2525
import io.micrometer.observation.tck.TestObservationRegistry;
26+
import jakarta.jms.Message;
2627
import jakarta.jms.MessageListener;
28+
import jakarta.jms.TextMessage;
2729
import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
2830
import org.apache.activemq.artemis.junit.EmbeddedActiveMQExtension;
2931
import org.assertj.core.api.Assertions;
@@ -81,6 +83,33 @@ void shouldRecordJmsProcessObservations(AbstractMessageListenerContainer listene
8183
listenerContainer.shutdown();
8284
}
8385

86+
@ParameterizedTest(name = "[{index}] {0}")
87+
@MethodSource("listenerContainers")
88+
void shouldRecordJmsPublishObservations(AbstractMessageListenerContainer listenerContainer) throws Exception {
89+
CountDownLatch latch = new CountDownLatch(1);
90+
listenerContainer.setConnectionFactory(connectionFactory);
91+
listenerContainer.setObservationRegistry(registry);
92+
listenerContainer.setDestinationName("spring.test.observation");
93+
listenerContainer.setMessageListener((SessionAwareMessageListener) (message, session) -> {
94+
Message response = session.createTextMessage("test response");
95+
session.createProducer(message.getJMSReplyTo()).send(response);
96+
});
97+
listenerContainer.afterPropertiesSet();
98+
listenerContainer.start();
99+
JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory);
100+
TextMessage response = (TextMessage) jmsTemplate.sendAndReceive("spring.test.observation",
101+
session -> session.createTextMessage("test request"));
102+
103+
// request received by listener and response received by template
104+
assertThat(registry).hasNumberOfObservationsWithNameEqualTo("jms.message.process", 2);
105+
// response sent to the template
106+
assertThat(registry).hasNumberOfObservationsWithNameEqualTo("jms.message.publish", 1);
107+
108+
Assertions.assertThat(response.getText()).isEqualTo("test response");
109+
listenerContainer.stop();
110+
listenerContainer.shutdown();
111+
}
112+
84113
@ParameterizedTest(name = "[{index}] {0}")
85114
@MethodSource("listenerContainers")
86115
void shouldHaveObservationScopeInErrorHandler(AbstractMessageListenerContainer listenerContainer) throws Exception {

0 commit comments

Comments
 (0)