From 0fc97e9315304e99b68c54ca5da8a98e1e2397e7 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Fri, 15 Sep 2023 16:28:06 +0200 Subject: [PATCH] Auto-configure ObservationRegistry on JmsTemplate Spring Boot auto-configures both a `JmsTemplate` and a `JmsMessagingTemplate`. As of Spring Framework 6.2, JMS has observability support when publishing messages. This commit creates a bean post-processor that configures an `ObservationRegistry` on the template, if the registry is present. Closes gh-37388 --- ...sTemplateObservationAutoConfiguration.java | 72 +++++++++++++++++++ .../observation/jms/package-info.java | 20 ++++++ ...ot.autoconfigure.AutoConfiguration.imports | 1 + ...lateObservationAutoConfigurationTests.java | 70 ++++++++++++++++++ .../src/docs/asciidoc/actuator/metrics.adoc | 7 ++ 5 files changed, 170 insertions(+) create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/jms/JmsTemplateObservationAutoConfiguration.java create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/jms/package-info.java create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/jms/JmsTemplateObservationAutoConfigurationTests.java diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/jms/JmsTemplateObservationAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/jms/JmsTemplateObservationAutoConfiguration.java new file mode 100644 index 0000000000..faa7db23e6 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/jms/JmsTemplateObservationAutoConfiguration.java @@ -0,0 +1,72 @@ +/* + * Copyright 2012-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. + * 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.boot.actuate.autoconfigure.observation.jms; + +import io.micrometer.core.instrument.binder.jms.JmsPublishObservationContext; +import io.micrometer.observation.Observation; +import io.micrometer.observation.ObservationRegistry; +import jakarta.jms.Message; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.jms.core.JmsTemplate; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for instrumenting + * {@link JmsTemplate} beans for Observability. + * + * @author Brian Clozel + * @since 3.2.0 + */ +@AutoConfiguration(after = { JmsAutoConfiguration.class, ObservationAutoConfiguration.class }) +@ConditionalOnBean({ ObservationRegistry.class, JmsTemplate.class }) +@ConditionalOnClass({ Observation.class, Message.class, JmsTemplate.class, JmsPublishObservationContext.class }) +public class JmsTemplateObservationAutoConfiguration { + + @Bean + static JmsTemplateObservationPostProcessor jmsTemplateObservationPostProcessor( + ObjectProvider observationRegistry) { + return new JmsTemplateObservationPostProcessor(observationRegistry); + } + + static class JmsTemplateObservationPostProcessor implements BeanPostProcessor { + + private final ObjectProvider observationRegistry; + + JmsTemplateObservationPostProcessor(ObjectProvider observationRegistry) { + this.observationRegistry = observationRegistry; + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof JmsTemplate jmsTemplate) { + this.observationRegistry.ifAvailable(jmsTemplate::setObservationRegistry); + } + return bean; + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/jms/package-info.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/jms/package-info.java new file mode 100644 index 0000000000..417a73aed6 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/observation/jms/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-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. + * 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. + */ + +/** + * Auto-configuration for JMS observations. + */ +package org.springframework.boot.actuate.autoconfigure.observation.jms; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 79c1000625..9ce3a9092f 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -70,6 +70,7 @@ org.springframework.boot.actuate.autoconfigure.metrics.export.statsd.StatsdMetri org.springframework.boot.actuate.autoconfigure.metrics.export.wavefront.WavefrontMetricsExportAutoConfiguration org.springframework.boot.actuate.autoconfigure.observation.batch.BatchObservationAutoConfiguration org.springframework.boot.actuate.autoconfigure.observation.graphql.GraphQlObservationAutoConfiguration +org.springframework.boot.actuate.autoconfigure.observation.jms.JmsTemplateObservationAutoConfiguration org.springframework.boot.actuate.autoconfigure.metrics.integration.IntegrationMetricsAutoConfiguration org.springframework.boot.actuate.autoconfigure.metrics.jdbc.DataSourcePoolMetricsAutoConfiguration org.springframework.boot.actuate.autoconfigure.metrics.jersey.JerseyServerMetricsAutoConfiguration diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/jms/JmsTemplateObservationAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/jms/JmsTemplateObservationAutoConfigurationTests.java new file mode 100644 index 0000000000..3abd6c1d64 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/observation/jms/JmsTemplateObservationAutoConfigurationTests.java @@ -0,0 +1,70 @@ +/* + * Copyright 2012-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. + * 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.boot.actuate.autoconfigure.observation.jms; + +import jakarta.jms.ConnectionFactory; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration; +import org.springframework.boot.test.context.FilteredClassLoader; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.jms.core.JmsTemplate; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link JmsTemplateObservationAutoConfiguration}. + * + * @author Brian Clozel + */ +class JmsTemplateObservationAutoConfigurationTests { + + ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(JmsAutoConfiguration.class, ObservationAutoConfiguration.class, + JmsTemplateObservationAutoConfiguration.class)) + .withUserConfiguration(JmsConnectionConfiguration.class); + + @Test + void shouldConfigureObservationRegistryOnTemplate() { + this.contextRunner.run((context) -> { + JmsTemplate jmsTemplate = context.getBean(JmsTemplate.class); + assertThat(jmsTemplate).extracting("observationRegistry").isNotNull(); + }); + } + + @Test + void shouldBackOffWhenMircrometerCoreIsNotPresent() { + this.contextRunner.withClassLoader(new FilteredClassLoader("io.micrometer.core")).run((context) -> { + JmsTemplate jmsTemplate = context.getBean(JmsTemplate.class); + assertThat(jmsTemplate).extracting("observationRegistry").isNull(); + }); + } + + static class JmsConnectionConfiguration { + + @Bean + ConnectionFactory connectionFactory() { + return mock(ConnectionFactory.class); + } + + } + +} diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/metrics.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/metrics.adoc index 853ca9dc4b..14d9c035ac 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/metrics.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/metrics.adoc @@ -739,6 +739,13 @@ Auto-configuration enables the instrumentation of all available `ThreadPoolTaskE Metrics are tagged by the name of the executor, which is derived from the bean name. +[[actuator.metrics.supported.jms]] +==== JMS Metrics +Auto-configuration enables the instrumentation of all available `JmsTemplate` beans. +`JmsMessagingTemplate` instances built with instrumented `JmsTemplate` beans will also record observations. +See the {spring-framework-docs}/integration/observability.html#observability.jms.publish[Spring Framework reference documentation for more information on produced observations]. + + [[actuator.metrics.supported.spring-mvc]] ==== Spring MVC Metrics