diff --git a/core/spring-boot-test/src/main/java/org/springframework/boot/test/metrics/DisableGlobalMeterRegistryContextCustomizerFactory.java b/core/spring-boot-test/src/main/java/org/springframework/boot/test/metrics/DisableGlobalMeterRegistryContextCustomizerFactory.java deleted file mode 100644 index d69c5da9860..00000000000 --- a/core/spring-boot-test/src/main/java/org/springframework/boot/test/metrics/DisableGlobalMeterRegistryContextCustomizerFactory.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2012-present 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.test.metrics; - -import java.util.List; - -import org.jspecify.annotations.Nullable; - -import org.springframework.boot.test.util.TestPropertyValues; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.test.context.ContextConfigurationAttributes; -import org.springframework.test.context.ContextCustomizer; -import org.springframework.test.context.ContextCustomizerFactory; -import org.springframework.test.context.MergedContextConfiguration; -import org.springframework.util.ClassUtils; - -/** - * {@link ContextCustomizerFactory} to disable the use of Micrometer's - * {@link io.micrometer.core.instrument.Metrics#globalRegistry global registry} in tests, - * preventing {@link io.micrometer.core.instrument.MeterRegistry meter registries} from - * pinning application contexts when many test contexts are cached. - */ -class DisableGlobalMeterRegistryContextCustomizerFactory implements ContextCustomizerFactory { - - private static final String METRICS_CLASS = "io.micrometer.core.instrument.Metrics"; - - private static final String USE_GLOBAL_REGISTRY_PROPERTY = "management.metrics.use-global-registry"; - - @Override - public @Nullable ContextCustomizer createContextCustomizer(Class testClass, - List configAttributes) { - if (ClassUtils.isPresent(METRICS_CLASS, testClass.getClassLoader())) { - return new DisableGlobalMeterRegistryContextCustomizer(); - } - return null; - } - - static final class DisableGlobalMeterRegistryContextCustomizer implements ContextCustomizer { - - @Override - public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) { - ConfigurableEnvironment environment = context.getEnvironment(); - if (environment.getProperty(USE_GLOBAL_REGISTRY_PROPERTY) == null) { - TestPropertyValues.of(USE_GLOBAL_REGISTRY_PROPERTY + "=false").applyTo(environment); - } - } - - @Override - public boolean equals(@Nullable Object obj) { - return obj instanceof DisableGlobalMeterRegistryContextCustomizer; - } - - @Override - public int hashCode() { - return getClass().hashCode(); - } - - } - -} diff --git a/core/spring-boot-test/src/main/java/org/springframework/boot/test/metrics/package-info.java b/core/spring-boot-test/src/main/java/org/springframework/boot/test/metrics/package-info.java deleted file mode 100644 index 4d2792dd73c..00000000000 --- a/core/spring-boot-test/src/main/java/org/springframework/boot/test/metrics/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2012-present 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. - */ - -/** - * Spring Boot support for metrics testing. - */ -@NullMarked -package org.springframework.boot.test.metrics; - -import org.jspecify.annotations.NullMarked; diff --git a/core/spring-boot-test/src/main/resources/META-INF/spring.factories b/core/spring-boot-test/src/main/resources/META-INF/spring.factories index 69d1c39b321..8a9b2c7d141 100644 --- a/core/spring-boot-test/src/main/resources/META-INF/spring.factories +++ b/core/spring-boot-test/src/main/resources/META-INF/spring.factories @@ -5,8 +5,7 @@ org.springframework.boot.test.context.PropertyMappingContextCustomizerFactory,\ org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizerFactory,\ org.springframework.boot.test.context.filter.annotation.TypeExcludeFiltersContextCustomizerFactory,\ org.springframework.boot.test.http.client.DisableReactorResourceFactoryGlobalResourcesContextCustomizerFactory,\ -org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory,\ -org.springframework.boot.test.metrics.DisableGlobalMeterRegistryContextCustomizerFactory +org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory # Application Context Initializers org.springframework.context.ApplicationContextInitializer=\ diff --git a/core/spring-boot-test/src/test/java/org/springframework/boot/test/metrics/DisableGlobalMeterRegistryContextCustomizerFactoryTests.java b/core/spring-boot-test/src/test/java/org/springframework/boot/test/metrics/DisableGlobalMeterRegistryContextCustomizerFactoryTests.java deleted file mode 100644 index fd11852f4bc..00000000000 --- a/core/spring-boot-test/src/test/java/org/springframework/boot/test/metrics/DisableGlobalMeterRegistryContextCustomizerFactoryTests.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2012-present 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.test.metrics; - -import org.junit.jupiter.api.Test; -import org.springframework.test.context.MergedContextConfiguration; - -import org.springframework.boot.test.util.TestPropertyValues; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.core.env.StandardEnvironment; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link DisableGlobalMeterRegistryContextCustomizerFactory}. - */ -class DisableGlobalMeterRegistryContextCustomizerFactoryTests { - - @Test - void disablesGlobalMeterRegistryByDefault() { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - context.setEnvironment(new StandardEnvironment()); - new DisableGlobalMeterRegistryContextCustomizerFactory.DisableGlobalMeterRegistryContextCustomizer() - .customizeContext(context, mock(MergedContextConfiguration.class)); - assertThat(context.getEnvironment().getProperty("management.metrics.use-global-registry")) - .isEqualTo("false"); - } - - @Test - void doesNotOverrideExplicitProperty() { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - StandardEnvironment environment = new StandardEnvironment(); - TestPropertyValues.of("management.metrics.use-global-registry=true").applyTo(environment); - context.setEnvironment(environment); - new DisableGlobalMeterRegistryContextCustomizerFactory.DisableGlobalMeterRegistryContextCustomizer() - .customizeContext(context, mock(MergedContextConfiguration.class)); - assertThat(context.getEnvironment().getProperty("management.metrics.use-global-registry")) - .isEqualTo("true"); - } - -} diff --git a/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/MeterRegistryCloser.java b/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/MeterRegistryCloser.java new file mode 100644 index 00000000000..0e0dd488c13 --- /dev/null +++ b/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/MeterRegistryCloser.java @@ -0,0 +1,75 @@ +/* + * Copyright 2012-present 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.micrometer.metrics.autoconfigure; + +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Metrics; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextClosedEvent; + +/** + * Ensures that {@link MeterRegistry meter registries} are closed early in the shutdown + * process. Also unregisters the registries from the global registry if needed. + * + * @author Jon Schneider + * @author Stephane Nicoll + * @author Moritz Halbritter + * @author Michael Berry + * @author Phillip Webb + * @author Lordwill Kandiro + */ +class MeterRegistryCloser implements ApplicationListener { + + private final ApplicationContext context; + + private final Set registriesToClose = new CopyOnWriteArraySet<>(); + + private final Set registriesToRemoveFromGlobalRegistry = new CopyOnWriteArraySet<>(); + + MeterRegistryCloser(ApplicationContext context) { + this.context = context; + } + + void track(MeterRegistry meterRegistry) { + this.registriesToClose.add(meterRegistry); + } + + void trackAddedToGlobalRegistry(MeterRegistry meterRegistry) { + this.registriesToRemoveFromGlobalRegistry.add(meterRegistry); + } + + @Override + public void onApplicationEvent(ContextClosedEvent event) { + if (!this.context.equals(event.getApplicationContext())) { + return; + } + for (MeterRegistry meterRegistry : this.registriesToRemoveFromGlobalRegistry) { + Metrics.removeRegistry(meterRegistry); + } + for (MeterRegistry meterRegistry : this.registriesToClose) { + if (!meterRegistry.isClosed()) { + meterRegistry.close(); + } + } + } + +} diff --git a/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/MeterRegistryPostProcessor.java b/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/MeterRegistryPostProcessor.java index e90937278a1..549b0d3bac8 100644 --- a/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/MeterRegistryPostProcessor.java +++ b/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/MeterRegistryPostProcessor.java @@ -54,17 +54,16 @@ class MeterRegistryPostProcessor implements BeanPostProcessor, SmartInitializing private final ObjectProvider binders; - private final ObjectProvider meterRegistryCloser; + private final ObjectProvider meterRegistryCloser; private volatile boolean deferBinding = true; private final Set deferredBindings = new LinkedHashSet<>(); MeterRegistryPostProcessor(ApplicationContext applicationContext, - ObjectProvider metricsProperties, - ObjectProvider> customizers, ObjectProvider filters, - ObjectProvider binders, - ObjectProvider meterRegistryCloser) { + ObjectProvider metricsProperties, ObjectProvider> customizers, + ObjectProvider filters, ObjectProvider binders, + ObjectProvider meterRegistryCloser) { this(CompositeMeterRegistries.of(applicationContext), metricsProperties, customizers, filters, binders, meterRegistryCloser); } @@ -72,7 +71,7 @@ class MeterRegistryPostProcessor implements BeanPostProcessor, SmartInitializing MeterRegistryPostProcessor(CompositeMeterRegistries compositeMeterRegistries, ObjectProvider properties, ObjectProvider> customizers, ObjectProvider filters, ObjectProvider binders, - ObjectProvider meterRegistryCloser) { + ObjectProvider meterRegistryCloser) { this.compositeMeterRegistries = compositeMeterRegistries; this.properties = properties; this.customizers = customizers; @@ -98,6 +97,7 @@ class MeterRegistryPostProcessor implements BeanPostProcessor, SmartInitializing } private void postProcessMeterRegistry(MeterRegistry meterRegistry) { + this.meterRegistryCloser.getObject().track(meterRegistry); // Customizers must be applied before binders, as they may add custom tags or // alter timer or summary configuration. applyCustomizers(meterRegistry); @@ -129,7 +129,7 @@ class MeterRegistryPostProcessor implements BeanPostProcessor, SmartInitializing private void addToGlobalRegistryIfNecessary(MeterRegistry meterRegistry) { if (this.properties.getObject().isUseGlobalRegistry() && !isGlobalRegistry(meterRegistry)) { Metrics.addRegistry(meterRegistry); - this.meterRegistryCloser.getObject().track(meterRegistry); + this.meterRegistryCloser.getObject().trackAddedToGlobalRegistry(meterRegistry); } } diff --git a/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/MetricsAutoConfiguration.java b/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/MetricsAutoConfiguration.java index 7143faf724a..9f8ee142a05 100644 --- a/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/MetricsAutoConfiguration.java +++ b/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/MetricsAutoConfiguration.java @@ -16,13 +16,9 @@ package org.springframework.boot.micrometer.metrics.autoconfigure; -import java.util.LinkedHashSet; -import java.util.Set; - import io.micrometer.core.annotation.Timed; import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.binder.MeterBinder; import io.micrometer.core.instrument.composite.CompositeMeterRegistry; import io.micrometer.core.instrument.config.MeterFilter; @@ -38,9 +34,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.micrometer.observation.autoconfigure.ObservationHandlerGroup; import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Bean; -import org.springframework.context.event.ContextClosedEvent; import org.springframework.core.annotation.Order; /** @@ -81,8 +75,8 @@ public final class MetricsAutoConfiguration { } @Bean - MeterRegistryCloser meterRegistryCloser(ApplicationContext context, MetricsProperties properties) { - return new MeterRegistryCloser(context, properties.isUseGlobalRegistry()); + MeterRegistryCloser meterRegistryCloser(ApplicationContext context) { + return new MeterRegistryCloser(context); } @Bean @@ -98,43 +92,4 @@ public final class MetricsAutoConfiguration { properties.getObservations().getIgnoredMeters().toArray(IgnoredMeters[]::new)); } - /** - * Ensures that {@link MeterRegistry meter registries} are closed early in the - * shutdown process. - */ - static class MeterRegistryCloser implements ApplicationListener { - - private final ApplicationContext context; - - private final boolean useGlobalRegistry; - - private final Set trackedRegistries = new LinkedHashSet<>(); - - MeterRegistryCloser(ApplicationContext context, boolean useGlobalRegistry) { - this.context = context; - this.useGlobalRegistry = useGlobalRegistry; - } - - void track(MeterRegistry meterRegistry) { - this.trackedRegistries.add(meterRegistry); - } - - @Override - public void onApplicationEvent(ContextClosedEvent event) { - if (this.context.equals(event.getApplicationContext())) { - Set meterRegistries = new LinkedHashSet<>(this.trackedRegistries); - meterRegistries.addAll(this.context.getBeansOfType(MeterRegistry.class).values()); - for (MeterRegistry meterRegistry : meterRegistries) { - if (this.useGlobalRegistry) { - Metrics.globalRegistry.remove(meterRegistry); - } - if (!meterRegistry.isClosed()) { - meterRegistry.close(); - } - } - } - } - - } - } diff --git a/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/MeterRegistryCloserTests.java b/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/MeterRegistryCloserTests.java new file mode 100644 index 00000000000..53c92be6e4a --- /dev/null +++ b/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/MeterRegistryCloserTests.java @@ -0,0 +1,113 @@ +/* + * Copyright 2012-present 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.micrometer.metrics.autoconfigure; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Metrics; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.junit.jupiter.api.Test; +import org.mockito.InOrder; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.event.ContextClosedEvent; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; + +/** + * Tests for {@link MeterRegistryCloser}. + * + * @author Lordwill Kandiro + * @author Moritz Halbritter + */ +class MeterRegistryCloserTests { + + private final ApplicationContext context = mock(ApplicationContext.class); + + @Test + void trackedRegistryIsRemovedFromGlobalRegistryOnContextClosedEvent() { + MeterRegistry meterRegistry = new SimpleMeterRegistry(); + MeterRegistryCloser closer = new MeterRegistryCloser(this.context); + try { + Metrics.addRegistry(meterRegistry); + closer.trackAddedToGlobalRegistry(meterRegistry); + closer.onApplicationEvent(new ContextClosedEvent(this.context)); + assertThat(Metrics.globalRegistry.getRegistries()).doesNotContain(meterRegistry); + } + finally { + Metrics.removeRegistry(meterRegistry); + } + } + + @Test + void trackedRegistryIsClosedOnContextClosedEvent() { + MeterRegistry meterRegistry = new SimpleMeterRegistry(); + MeterRegistryCloser closer = new MeterRegistryCloser(this.context); + closer.track(meterRegistry); + closer.onApplicationEvent(new ContextClosedEvent(this.context)); + assertThat(meterRegistry.isClosed()).isTrue(); + } + + @Test + void trackedRegistriesAreClosedInTheOrderThatTheyWereTracked() { + MeterRegistry first = mock(MeterRegistry.class); + MeterRegistry second = mock(MeterRegistry.class); + MeterRegistry third = mock(MeterRegistry.class); + MeterRegistryCloser closer = new MeterRegistryCloser(this.context); + closer.track(first); + closer.track(second); + closer.track(third); + closer.onApplicationEvent(new ContextClosedEvent(this.context)); + InOrder ordered = inOrder(first, second, third); + then(first).should(ordered).close(); + then(second).should(ordered).close(); + then(third).should(ordered).close(); + } + + @Test + void alreadyClosedRegistryIsNotClosedAgain() { + MeterRegistry meterRegistry = mock(MeterRegistry.class); + given(meterRegistry.isClosed()).willReturn(true); + MeterRegistryCloser closer = new MeterRegistryCloser(this.context); + closer.track(meterRegistry); + closer.onApplicationEvent(new ContextClosedEvent(this.context)); + then(meterRegistry).should(never()).close(); + } + + @Test + void onApplicationEventIgnoresEventsFromOtherContexts() { + MeterRegistry meterRegistry = new SimpleMeterRegistry(); + MeterRegistryCloser closer = new MeterRegistryCloser(this.context); + try { + Metrics.addRegistry(meterRegistry); + closer.track(meterRegistry); + closer.trackAddedToGlobalRegistry(meterRegistry); + ApplicationContext otherContext = mock(ApplicationContext.class); + closer.onApplicationEvent(new ContextClosedEvent(otherContext)); + assertThat(Metrics.globalRegistry.getRegistries()).contains(meterRegistry); + assertThat(meterRegistry.isClosed()).isFalse(); + } + finally { + Metrics.removeRegistry(meterRegistry); + } + } + +} diff --git a/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/MeterRegistryPostProcessorTests.java b/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/MeterRegistryPostProcessorTests.java index 18bc9402cb3..d0a5f1178f8 100644 --- a/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/MeterRegistryPostProcessorTests.java +++ b/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/MeterRegistryPostProcessorTests.java @@ -39,9 +39,9 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.micrometer.metrics.MaximumAllowableTagsMeterFilter; -import org.springframework.boot.micrometer.metrics.autoconfigure.MetricsAutoConfiguration.MeterRegistryCloser; import org.springframework.boot.micrometer.metrics.autoconfigure.MeterRegistryPostProcessor.CompositeMeterRegistries; import org.springframework.context.ApplicationContext; +import org.springframework.context.event.ContextClosedEvent; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; @@ -86,12 +86,13 @@ class MeterRegistryPostProcessorTests { @SuppressWarnings("NullAway.Init") private Config mockConfig; - private final MetricsAutoConfiguration.MeterRegistryCloser meterRegistryCloser; + private final ApplicationContext meterRegistryCloserContext = mock(ApplicationContext.class); + + private final MeterRegistryCloser meterRegistryCloser; MeterRegistryPostProcessorTests() { this.properties.setUseGlobalRegistry(false); - this.meterRegistryCloser = new MetricsAutoConfiguration.MeterRegistryCloser( - mock(ApplicationContext.class), true); + this.meterRegistryCloser = new MeterRegistryCloser(this.meterRegistryCloserContext); } @Test @@ -111,7 +112,8 @@ class MeterRegistryPostProcessorTests { this.customizers.add(this.mockCustomizer); MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.AUTO_CONFIGURED, createObjectProvider(this.properties), createObjectProvider(this.customizers), - createEmptyObjectProvider(), createObjectProvider(this.binders), createMeterRegistryCloserProvider(this.meterRegistryCloser)); + createEmptyObjectProvider(), createObjectProvider(this.binders), + createMeterRegistryCloserProvider(this.meterRegistryCloser)); AutoConfiguredCompositeMeterRegistry composite = new AutoConfiguredCompositeMeterRegistry(Clock.SYSTEM, Collections.emptyList()); postProcessAndInitialize(processor, composite); @@ -124,7 +126,8 @@ class MeterRegistryPostProcessorTests { this.customizers.add(this.mockCustomizer); MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.NONE, createObjectProvider(this.properties), createObjectProvider(this.customizers), - createObjectProvider(this.filters), createObjectProvider(this.binders), createMeterRegistryCloserProvider(this.meterRegistryCloser)); + createObjectProvider(this.filters), createObjectProvider(this.binders), + createMeterRegistryCloserProvider(this.meterRegistryCloser)); postProcessAndInitialize(processor, this.mockRegistry); then(this.mockCustomizer).should().customize(this.mockRegistry); } @@ -135,7 +138,8 @@ class MeterRegistryPostProcessorTests { this.filters.add(this.mockFilter); MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.NONE, createObjectProvider(this.properties), createObjectProvider(this.customizers), - createObjectProvider(this.filters), createObjectProvider(this.binders), createMeterRegistryCloserProvider(this.meterRegistryCloser)); + createObjectProvider(this.filters), createObjectProvider(this.binders), + createMeterRegistryCloserProvider(this.meterRegistryCloser)); postProcessAndInitialize(processor, this.mockRegistry); then(this.mockConfig).should().meterFilter(this.mockFilter); } @@ -147,7 +151,8 @@ class MeterRegistryPostProcessorTests { this.filters.add(onlyOnceFilter); MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.AUTO_CONFIGURED, createObjectProvider(this.properties), createObjectProvider(this.customizers), - createObjectProvider(this.filters), createObjectProvider(this.binders), createMeterRegistryCloserProvider(this.meterRegistryCloser)); + createObjectProvider(this.filters), createObjectProvider(this.binders), + createMeterRegistryCloserProvider(this.meterRegistryCloser)); AutoConfiguredCompositeMeterRegistry composite = new AutoConfiguredCompositeMeterRegistry(Clock.SYSTEM, Collections.emptyList()); postProcessAndInitialize(processor, composite); @@ -162,7 +167,8 @@ class MeterRegistryPostProcessorTests { this.binders.add(this.mockBinder); MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.NONE, createObjectProvider(this.properties), createObjectProvider(this.customizers), - createObjectProvider(this.filters), createObjectProvider(this.binders), createMeterRegistryCloserProvider(this.meterRegistryCloser)); + createObjectProvider(this.filters), createObjectProvider(this.binders), + createMeterRegistryCloserProvider(this.meterRegistryCloser)); postProcessAndInitialize(processor, this.mockRegistry); then(this.mockBinder).should().bindTo(this.mockRegistry); } @@ -184,8 +190,8 @@ class MeterRegistryPostProcessorTests { given(this.mockRegistry.config()).willReturn(this.mockConfig); MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor( CompositeMeterRegistries.ONLY_USER_DEFINED, createObjectProvider(this.properties), - createObjectProvider(this.customizers), createObjectProvider(this.filters), - createEmptyObjectProvider(), createMeterRegistryCloserProvider(this.meterRegistryCloser)); + createObjectProvider(this.customizers), createObjectProvider(this.filters), createEmptyObjectProvider(), + createMeterRegistryCloserProvider(this.meterRegistryCloser)); postProcessAndInitialize(processor, this.mockRegistry); then(this.mockBinder).shouldHaveNoInteractions(); } @@ -195,7 +201,8 @@ class MeterRegistryPostProcessorTests { this.binders.add(this.mockBinder); MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.AUTO_CONFIGURED, createObjectProvider(this.properties), createObjectProvider(this.customizers), - createEmptyObjectProvider(), createObjectProvider(this.binders), createMeterRegistryCloserProvider(this.meterRegistryCloser)); + createEmptyObjectProvider(), createObjectProvider(this.binders), + createMeterRegistryCloserProvider(this.meterRegistryCloser)); AutoConfiguredCompositeMeterRegistry composite = new AutoConfiguredCompositeMeterRegistry(Clock.SYSTEM, Collections.emptyList()); postProcessAndInitialize(processor, composite); @@ -207,7 +214,8 @@ class MeterRegistryPostProcessorTests { this.binders.add(this.mockBinder); MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.AUTO_CONFIGURED, createObjectProvider(this.properties), createObjectProvider(this.customizers), - createObjectProvider(this.filters), createEmptyObjectProvider(), createMeterRegistryCloserProvider(this.meterRegistryCloser)); + createObjectProvider(this.filters), createEmptyObjectProvider(), + createMeterRegistryCloserProvider(this.meterRegistryCloser)); CompositeMeterRegistry composite = new CompositeMeterRegistry(); postProcessAndInitialize(processor, composite); then(this.mockBinder).shouldHaveNoInteractions(); @@ -218,7 +226,8 @@ class MeterRegistryPostProcessorTests { given(this.mockRegistry.config()).willReturn(this.mockConfig); MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.AUTO_CONFIGURED, createObjectProvider(this.properties), createObjectProvider(this.customizers), - createObjectProvider(this.filters), createEmptyObjectProvider(), createMeterRegistryCloserProvider(this.meterRegistryCloser)); + createObjectProvider(this.filters), createEmptyObjectProvider(), + createMeterRegistryCloserProvider(this.meterRegistryCloser)); postProcessAndInitialize(processor, this.mockRegistry); then(this.mockBinder).shouldHaveNoInteractions(); } @@ -231,7 +240,8 @@ class MeterRegistryPostProcessorTests { this.binders.add(this.mockBinder); MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.NONE, createObjectProvider(this.properties), createObjectProvider(this.customizers), - createObjectProvider(this.filters), createObjectProvider(this.binders), createMeterRegistryCloserProvider(this.meterRegistryCloser)); + createObjectProvider(this.filters), createObjectProvider(this.binders), + createMeterRegistryCloserProvider(this.meterRegistryCloser)); postProcessAndInitialize(processor, this.mockRegistry); InOrder ordered = inOrder(this.mockBinder, this.mockConfig, this.mockCustomizer); then(this.mockCustomizer).should(ordered).customize(this.mockRegistry); @@ -240,15 +250,16 @@ class MeterRegistryPostProcessorTests { } @Test - void trackedRegistryIsRemovedFromGlobalRegistryOnContextClosedEvent() { - ApplicationContext applicationContext = mock(ApplicationContext.class); - MetricsAutoConfiguration.MeterRegistryCloser closer = new MetricsAutoConfiguration.MeterRegistryCloser( - applicationContext, true); + void postProcessAndInitializeWhenUseGlobalRegistryTrueAddsToGlobalRegistry() { + given(this.mockRegistry.config()).willReturn(this.mockConfig); + this.properties.setUseGlobalRegistry(true); + MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.NONE, + createObjectProvider(this.properties), createObjectProvider(this.customizers), + createObjectProvider(this.filters), createObjectProvider(this.binders), + createMeterRegistryCloserProvider(this.meterRegistryCloser)); try { - Metrics.addRegistry(this.mockRegistry); - closer.track(this.mockRegistry); - closer.onApplicationEvent(new org.springframework.context.event.ContextClosedEvent(applicationContext)); - assertThat(Metrics.globalRegistry.getRegistries()).doesNotContain(this.mockRegistry); + postProcessAndInitialize(processor, this.mockRegistry); + assertThat(Metrics.globalRegistry.getRegistries()).contains(this.mockRegistry); } finally { Metrics.removeRegistry(this.mockRegistry); @@ -256,15 +267,17 @@ class MeterRegistryPostProcessorTests { } @Test - void postProcessAndInitializeWhenUseGlobalRegistryTrueAddsToGlobalRegistry() { + void addToGlobalRegistryIfNecessaryTracksRegistryWithMeterRegistryCloser() { given(this.mockRegistry.config()).willReturn(this.mockConfig); this.properties.setUseGlobalRegistry(true); MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.NONE, createObjectProvider(this.properties), createObjectProvider(this.customizers), - createObjectProvider(this.filters), createObjectProvider(this.binders), createMeterRegistryCloserProvider(this.meterRegistryCloser)); + createObjectProvider(this.filters), createObjectProvider(this.binders), + createMeterRegistryCloserProvider(this.meterRegistryCloser)); try { postProcessAndInitialize(processor, this.mockRegistry); - assertThat(Metrics.globalRegistry.getRegistries()).contains(this.mockRegistry); + this.meterRegistryCloser.onApplicationEvent(new ContextClosedEvent(this.meterRegistryCloserContext)); + assertThat(Metrics.globalRegistry.getRegistries()).doesNotContain(this.mockRegistry); } finally { Metrics.removeRegistry(this.mockRegistry); @@ -276,18 +289,32 @@ class MeterRegistryPostProcessorTests { given(this.mockRegistry.config()).willReturn(this.mockConfig); MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.NONE, createObjectProvider(this.properties), createObjectProvider(this.customizers), - createObjectProvider(this.filters), createObjectProvider(this.binders), createMeterRegistryCloserProvider(this.meterRegistryCloser)); + createObjectProvider(this.filters), createObjectProvider(this.binders), + createMeterRegistryCloserProvider(this.meterRegistryCloser)); postProcessAndInitialize(processor, this.mockRegistry); assertThat(Metrics.globalRegistry.getRegistries()).doesNotContain(this.mockRegistry); } + @Test + void postProcessMeterRegistryTracksRegistryForClosingWithMeterRegistryCloser() { + given(this.mockRegistry.config()).willReturn(this.mockConfig); + MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.NONE, + createObjectProvider(this.properties), createObjectProvider(this.customizers), + createObjectProvider(this.filters), createObjectProvider(this.binders), + createMeterRegistryCloserProvider(this.meterRegistryCloser)); + postProcessAndInitialize(processor, this.mockRegistry); + this.meterRegistryCloser.onApplicationEvent(new ContextClosedEvent(this.meterRegistryCloserContext)); + then(this.mockRegistry).should().close(); + } + @Test void postProcessDoesNotBindToUntilSingletonsInitialized() { given(this.mockRegistry.config()).willReturn(this.mockConfig); this.binders.add(this.mockBinder); MeterRegistryPostProcessor processor = new MeterRegistryPostProcessor(CompositeMeterRegistries.NONE, createObjectProvider(this.properties), createObjectProvider(this.customizers), - createObjectProvider(this.filters), createObjectProvider(this.binders), createMeterRegistryCloserProvider(this.meterRegistryCloser)); + createObjectProvider(this.filters), createObjectProvider(this.binders), + createMeterRegistryCloserProvider(this.meterRegistryCloser)); processor.postProcessAfterInitialization(this.mockRegistry, "meterRegistry"); then(this.mockBinder).shouldHaveNoInteractions(); processor.afterSingletonsInstantiated(); diff --git a/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/MetricsAutoConfigurationTests.java b/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/MetricsAutoConfigurationTests.java index 5146b80fb4a..03d42a8657f 100644 --- a/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/MetricsAutoConfigurationTests.java +++ b/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/MetricsAutoConfigurationTests.java @@ -30,7 +30,6 @@ import io.micrometer.observation.ObservationHandler; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.micrometer.metrics.autoconfigure.MetricsAutoConfiguration.MeterRegistryCloser; import org.springframework.boot.micrometer.observation.autoconfigure.ObservationHandlerGroup; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean;