From f62c1188a19100727254b1c8c4708f1760e43005 Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Wed, 15 Nov 2023 10:12:11 +0100 Subject: [PATCH] Close meter registries early in the shutdown process Closes gh-38240 Co-authored-by: Phillip Webb --- .../metrics/MetricsAutoConfiguration.java | 47 +++++++++++++++++++ .../MetricsAutoConfigurationTests.java | 20 ++++++++ 2 files changed, 67 insertions(+) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfiguration.java index 16eaab791d..fc695471f7 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfiguration.java @@ -16,8 +16,11 @@ package org.springframework.boot.actuate.autoconfigure.metrics; +import java.util.List; + import io.micrometer.core.annotation.Timed; import io.micrometer.core.instrument.Clock; +import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.binder.MeterBinder; import io.micrometer.core.instrument.config.MeterFilter; @@ -28,6 +31,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.ApplicationContext; +import org.springframework.context.SmartLifecycle; import org.springframework.context.annotation.Bean; import org.springframework.core.annotation.Order; @@ -36,6 +40,8 @@ import org.springframework.core.annotation.Order; * * @author Jon Schneider * @author Stephane Nicoll + * @author Phil Webb + * @author Moritz Halbritter * @since 2.0.0 */ @AutoConfiguration(before = CompositeMeterRegistryAutoConfiguration.class) @@ -64,4 +70,45 @@ public class MetricsAutoConfiguration { return new PropertiesMeterFilter(properties); } + @Bean + MeterRegistryLifecycle meterRegistryLifecycle(ObjectProvider meterRegistries) { + return new MeterRegistryLifecycle(meterRegistries.orderedStream().toList()); + } + + /** + * Ensures that {@link MeterRegistry meter registries} are closed early in the + * shutdown process. + */ + static class MeterRegistryLifecycle implements SmartLifecycle { + + private volatile boolean running; + + private final List meterRegistries; + + MeterRegistryLifecycle(List meterRegistries) { + this.meterRegistries = meterRegistries; + } + + @Override + public void start() { + this.running = true; + } + + @Override + public void stop() { + this.running = false; + this.meterRegistries.forEach((registry) -> { + if (!registry.isClosed()) { + registry.close(); + } + }); + } + + @Override + public boolean isRunning() { + return this.running; + } + + } + } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfigurationTests.java index c04c31435a..cd7a84d455 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfigurationTests.java @@ -16,15 +16,19 @@ package org.springframework.boot.actuate.autoconfigure.metrics; +import java.util.List; + import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.binder.MeterBinder; +import io.micrometer.core.instrument.composite.CompositeMeterRegistry; import io.micrometer.core.instrument.config.MeterFilter; import io.micrometer.core.instrument.config.MeterFilterReply; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.junit.jupiter.api.Test; +import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration.MeterRegistryLifecycle; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; @@ -40,6 +44,7 @@ import static org.mockito.Mockito.mock; * Tests for {@link MetricsAutoConfiguration}. * * @author Andy Wilkinson + * @author Moritz Halbritter */ class MetricsAutoConfigurationTests { @@ -72,6 +77,21 @@ class MetricsAutoConfigurationTests { }); } + @Test + void shouldSupplyMeterRegistryLifecycle() { + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(MeterRegistryLifecycle.class)); + } + + @Test + void meterRegistryLifecycleShouldCloseRegistryOnShutdown() { + MeterRegistry meterRegistry = new CompositeMeterRegistry(); + assertThat(meterRegistry.isClosed()).isFalse(); + MeterRegistryLifecycle lifecycle = new MeterRegistryLifecycle(List.of(meterRegistry)); + lifecycle.start(); + lifecycle.stop(); + assertThat(meterRegistry.isClosed()).isTrue(); + } + @Configuration(proxyBeanMethods = false) static class CustomClockConfiguration {