From 0cbeb111d48e13a91fd1e83f983abec1f090f4ef Mon Sep 17 00:00:00 2001 From: Dmytro Nosan Date: Tue, 4 Nov 2025 11:29:21 +0100 Subject: [PATCH] Autoconfigure custom MeterConventions for JVM and system metrics Autoconfigure the following beans with their corresponding conventions, if available: - ProcessorMetrics with JvmCpuMeterConventions - JvmMemoryMetrics with JvmMemoryMeterConventions - JvmThreadMetrics with JvmThreadMeterConventions - ClassLoaderMetrics with JvmClassLoadingMeterConventions Signed-off-by: Dmytro Nosan --- .../jvm/JvmMetricsAutoConfiguration.java | 24 ++++++++++---- .../SystemMetricsAutoConfiguration.java | 9 ++++-- .../jvm/JvmMetricsAutoConfigurationTests.java | 32 +++++++++++++++++++ .../SystemMetricsAutoConfigurationTests.java | 11 +++++++ 4 files changed, 68 insertions(+), 8 deletions(-) diff --git a/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/jvm/JvmMetricsAutoConfiguration.java b/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/jvm/JvmMetricsAutoConfiguration.java index 2b3f5c3224be..10e4e8b6ce13 100644 --- a/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/jvm/JvmMetricsAutoConfiguration.java +++ b/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/jvm/JvmMetricsAutoConfiguration.java @@ -16,6 +16,8 @@ package org.springframework.boot.micrometer.metrics.autoconfigure.jvm; +import java.util.Collections; + import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.binder.MeterBinder; import io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics; @@ -25,12 +27,16 @@ import io.micrometer.core.instrument.binder.jvm.JvmInfoMetrics; import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics; import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics; +import io.micrometer.core.instrument.binder.jvm.convention.JvmClassLoadingMeterConventions; +import io.micrometer.core.instrument.binder.jvm.convention.JvmMemoryMeterConventions; +import io.micrometer.core.instrument.binder.jvm.convention.JvmThreadMeterConventions; import org.jspecify.annotations.Nullable; import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -71,20 +77,26 @@ JvmHeapPressureMetrics jvmHeapPressureMetrics() { @Bean @ConditionalOnMissingBean - JvmMemoryMetrics jvmMemoryMetrics() { - return new JvmMemoryMetrics(); + JvmMemoryMetrics jvmMemoryMetrics(ObjectProvider jvmMemoryMeterConventions) { + JvmMemoryMeterConventions conventions = jvmMemoryMeterConventions.getIfAvailable(); + return (conventions != null) ? new JvmMemoryMetrics(Collections.emptyList(), conventions) + : new JvmMemoryMetrics(); } @Bean @ConditionalOnMissingBean - JvmThreadMetrics jvmThreadMetrics() { - return new JvmThreadMetrics(); + JvmThreadMetrics jvmThreadMetrics(ObjectProvider jvmThreadMeterConventions) { + JvmThreadMeterConventions conventions = jvmThreadMeterConventions.getIfAvailable(); + return (conventions != null) ? new JvmThreadMetrics(Collections.emptyList(), conventions) + : new JvmThreadMetrics(); } @Bean @ConditionalOnMissingBean - ClassLoaderMetrics classLoaderMetrics() { - return new ClassLoaderMetrics(); + ClassLoaderMetrics classLoaderMetrics( + ObjectProvider jvmClassLoadingMeterConventions) { + JvmClassLoadingMeterConventions conventions = jvmClassLoadingMeterConventions.getIfAvailable(); + return (conventions != null) ? new ClassLoaderMetrics(conventions) : new ClassLoaderMetrics(); } @Bean diff --git a/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/system/SystemMetricsAutoConfiguration.java b/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/system/SystemMetricsAutoConfiguration.java index faf02f23c145..ef3e1dab5ca7 100644 --- a/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/system/SystemMetricsAutoConfiguration.java +++ b/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/system/SystemMetricsAutoConfiguration.java @@ -17,14 +17,17 @@ package org.springframework.boot.micrometer.metrics.autoconfigure.system; import java.io.File; +import java.util.Collections; import java.util.List; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.binder.jvm.convention.JvmCpuMeterConventions; import io.micrometer.core.instrument.binder.system.FileDescriptorMetrics; import io.micrometer.core.instrument.binder.system.ProcessorMetrics; import io.micrometer.core.instrument.binder.system.UptimeMetrics; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -58,8 +61,10 @@ UptimeMetrics uptimeMetrics() { @Bean @ConditionalOnMissingBean - ProcessorMetrics processorMetrics() { - return new ProcessorMetrics(); + ProcessorMetrics processorMetrics(ObjectProvider jvmCpuMeterConventions) { + JvmCpuMeterConventions conventions = jvmCpuMeterConventions.getIfAvailable(); + return (conventions != null) ? new ProcessorMetrics(Collections.emptyList(), conventions) + : new ProcessorMetrics(); } @Bean diff --git a/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/jvm/JvmMetricsAutoConfigurationTests.java b/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/jvm/JvmMetricsAutoConfigurationTests.java index a40865473902..a9ca0f518c65 100644 --- a/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/jvm/JvmMetricsAutoConfigurationTests.java +++ b/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/jvm/JvmMetricsAutoConfigurationTests.java @@ -25,10 +25,14 @@ import io.micrometer.core.instrument.binder.jvm.JvmInfoMetrics; import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics; import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics; +import io.micrometer.core.instrument.binder.jvm.convention.JvmClassLoadingMeterConventions; +import io.micrometer.core.instrument.binder.jvm.convention.JvmMemoryMeterConventions; +import io.micrometer.core.instrument.binder.jvm.convention.JvmThreadMeterConventions; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledForJreRange; import org.junit.jupiter.api.condition.JRE; +import org.mockito.Mockito; import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.RuntimeHints; @@ -82,18 +86,46 @@ void allowsCustomJvmMemoryMetricsToBeUsed() { .run(assertMetricsBeans().andThen((context) -> assertThat(context).hasBean("customJvmMemoryMetrics"))); } + @Test + void allowCustomJvmMemoryMeterConventionsToBeUsed() { + JvmMemoryMeterConventions jvmMemoryMeterConventions = Mockito.mock(JvmMemoryMeterConventions.class); + this.contextRunner.withBean(JvmMemoryMeterConventions.class, () -> jvmMemoryMeterConventions) + .run((context) -> assertThat(context).hasSingleBean(JvmMemoryMetrics.class) + .getBean(JvmMemoryMetrics.class) + .hasFieldOrPropertyWithValue("conventions", jvmMemoryMeterConventions)); + } + @Test void allowsCustomJvmThreadMetricsToBeUsed() { this.contextRunner.withUserConfiguration(CustomJvmThreadMetricsConfiguration.class) .run(assertMetricsBeans().andThen((context) -> assertThat(context).hasBean("customJvmThreadMetrics"))); } + @Test + void allowCustomJvmThreadMeterConventionsToBeUsed() { + JvmThreadMeterConventions jvmThreadMeterConventions = Mockito.mock(JvmThreadMeterConventions.class); + this.contextRunner.withBean(JvmThreadMeterConventions.class, () -> jvmThreadMeterConventions) + .run((context) -> assertThat(context).hasSingleBean(JvmThreadMetrics.class) + .getBean(JvmThreadMetrics.class) + .hasFieldOrPropertyWithValue("conventions", jvmThreadMeterConventions)); + } + @Test void allowsCustomClassLoaderMetricsToBeUsed() { this.contextRunner.withUserConfiguration(CustomClassLoaderMetricsConfiguration.class) .run(assertMetricsBeans().andThen((context) -> assertThat(context).hasBean("customClassLoaderMetrics"))); } + @Test + void allowCustomJvmClassLoadingMeterConventionsToBeUsed() { + JvmClassLoadingMeterConventions jvmClassLoadingMeterConventions = Mockito + .mock(JvmClassLoadingMeterConventions.class); + this.contextRunner.withBean(JvmClassLoadingMeterConventions.class, () -> jvmClassLoadingMeterConventions) + .run((context) -> assertThat(context).hasSingleBean(ClassLoaderMetrics.class) + .getBean(ClassLoaderMetrics.class) + .hasFieldOrPropertyWithValue("conventions", jvmClassLoadingMeterConventions)); + } + @Test void allowsCustomJvmInfoMetricsToBeUsed() { this.contextRunner.withUserConfiguration(CustomJvmInfoMetricsConfiguration.class) diff --git a/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/system/SystemMetricsAutoConfigurationTests.java b/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/system/SystemMetricsAutoConfigurationTests.java index eb077080f82d..39d1268c4f6c 100644 --- a/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/system/SystemMetricsAutoConfigurationTests.java +++ b/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/system/SystemMetricsAutoConfigurationTests.java @@ -21,11 +21,13 @@ import java.util.Collections; import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.binder.jvm.convention.JvmCpuMeterConventions; import io.micrometer.core.instrument.binder.system.FileDescriptorMetrics; import io.micrometer.core.instrument.binder.system.ProcessorMetrics; import io.micrometer.core.instrument.binder.system.UptimeMetrics; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.micrometer.metrics.system.DiskSpaceMetricsBinder; @@ -71,6 +73,15 @@ void allowsCustomProcessorMetricsToBeUsed() { .hasBean("customProcessorMetrics")); } + @Test + void allowsCustomJvmCpuMeterConventionsToBeUsed() { + JvmCpuMeterConventions jvmCpuMeterConventions = Mockito.mock(JvmCpuMeterConventions.class); + this.contextRunner.withBean(JvmCpuMeterConventions.class, () -> jvmCpuMeterConventions) + .run((context) -> assertThat(context).hasSingleBean(ProcessorMetrics.class) + .getBean(ProcessorMetrics.class) + .hasFieldOrPropertyWithValue("conventions", jvmCpuMeterConventions)); + } + @Test void autoConfiguresFileDescriptorMetrics() { this.contextRunner.run((context) -> assertThat(context).hasSingleBean(FileDescriptorMetrics.class));