diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java index 61583ff424c..b93ff7a0fcf 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java @@ -16,8 +16,12 @@ package org.springframework.boot.actuate.autoconfigure.metrics; +import java.io.File; import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -30,6 +34,7 @@ import org.springframework.boot.context.properties.NestedConfigurationProperty; * @author Jon Schneider * @author Alexander Abramov * @author Tadaya Tsuyukubo + * @author Chris Bono * @since 2.0.0 */ @ConfigurationProperties("management.metrics") @@ -57,6 +62,8 @@ public class MetricsProperties { private final Data data = new Data(); + private final System system = new System(); + private final Distribution distribution = new Distribution(); public boolean isUseGlobalRegistry() { @@ -83,6 +90,10 @@ public class MetricsProperties { return this.data; } + public System getSystem() { + return this.system; + } + public Distribution getDistribution() { return this.distribution; } @@ -257,6 +268,33 @@ public class MetricsProperties { } + public static class System { + + private final Diskspace diskspace = new Diskspace(); + + public Diskspace getDiskspace() { + return this.diskspace; + } + + public static class Diskspace { + + /** + * Comma-separated list of paths to report disk metrics for. + */ + private List paths = new ArrayList<>(Collections.singletonList(new File("."))); + + public List getPaths() { + return this.paths; + } + + public void setPaths(List paths) { + this.paths = paths; + } + + } + + } + public static class Distribution { /** diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/SystemMetricsAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/SystemMetricsAutoConfiguration.java index dd784698fc8..50b0ef9769f 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/SystemMetricsAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/SystemMetricsAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -17,18 +17,21 @@ package org.springframework.boot.actuate.autoconfigure.metrics; import java.io.File; +import java.util.List; import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.binder.system.DiskSpaceMetrics; +import io.micrometer.core.instrument.Tags; 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.boot.actuate.metrics.system.DiskSpaceMetricsBinder; import org.springframework.boot.autoconfigure.AutoConfigureAfter; 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.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -36,12 +39,14 @@ import org.springframework.context.annotation.Configuration; * {@link EnableAutoConfiguration Auto-configuration} for system metrics. * * @author Stephane Nicoll + * @author Chris Bono * @since 2.1.0 */ @Configuration(proxyBeanMethods = false) @AutoConfigureAfter({ MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class }) @ConditionalOnClass(MeterRegistry.class) @ConditionalOnBean(MeterRegistry.class) +@EnableConfigurationProperties(MetricsProperties.class) public class SystemMetricsAutoConfiguration { @Bean @@ -64,8 +69,9 @@ public class SystemMetricsAutoConfiguration { @Bean @ConditionalOnMissingBean - public DiskSpaceMetrics diskSpaceMetrics() { - return new DiskSpaceMetrics(new File(".")); + public DiskSpaceMetricsBinder diskSpaceMetrics(MetricsProperties properties) { + List paths = properties.getSystem().getDiskspace().getPaths(); + return new DiskSpaceMetricsBinder(paths, Tags.empty()); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index a226935654f..c101f7c12c8 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -326,6 +326,13 @@ "reason": "Instead, filter 'process.uptime' and 'process.start.time' metrics." } }, + { + "name": "management.metrics.system.diskspace.paths", + "type": "java.util.List", + "defaultValue": [ + "." + ] + }, { "name": "management.metrics.export.appoptics.num-threads", "type": "java.lang.Integer", diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/SystemMetricsAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/SystemMetricsAutoConfigurationTests.java index 7b34bd3c1ad..93f705df767 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/SystemMetricsAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/SystemMetricsAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2021 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. @@ -17,14 +17,17 @@ package org.springframework.boot.actuate.autoconfigure.metrics; import java.io.File; +import java.util.Arrays; +import java.util.Collections; -import io.micrometer.core.instrument.binder.system.DiskSpaceMetrics; +import io.micrometer.core.instrument.Tags; 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.junit.jupiter.api.Test; import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun; +import org.springframework.boot.actuate.metrics.system.DiskSpaceMetricsBinder; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; @@ -81,16 +84,37 @@ class SystemMetricsAutoConfigurationTests { @Test void autoConfiguresDiskSpaceMetrics() { - this.contextRunner.run((context) -> assertThat(context).hasSingleBean(DiskSpaceMetrics.class)); + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(DiskSpaceMetricsBinder.class)); } @Test void allowsCustomDiskSpaceMetricsToBeUsed() { this.contextRunner.withUserConfiguration(CustomDiskSpaceMetricsConfiguration.class) - .run((context) -> assertThat(context).hasSingleBean(DiskSpaceMetrics.class) + .run((context) -> assertThat(context).hasSingleBean(DiskSpaceMetricsBinder.class) .hasBean("customDiskSpaceMetrics")); } + @Test + void diskSpaceMetricsUsesDefaultPath() { + this.contextRunner + .run((context) -> assertThat(context).hasBean("diskSpaceMetrics").getBean(DiskSpaceMetricsBinder.class) + .hasFieldOrPropertyWithValue("paths", Collections.singletonList(new File(".")))); + } + + @Test + void allowsDiskSpaceMetricsPathToBeConfiguredWithSinglePath() { + this.contextRunner.withPropertyValues("management.metrics.system.diskspace.paths:..") + .run((context) -> assertThat(context).hasBean("diskSpaceMetrics").getBean(DiskSpaceMetricsBinder.class) + .hasFieldOrPropertyWithValue("paths", Collections.singletonList(new File("..")))); + } + + @Test + void allowsDiskSpaceMetricsPathToBeConfiguredWithMultiplePaths() { + this.contextRunner.withPropertyValues("management.metrics.system.diskspace.paths:.,..") + .run((context) -> assertThat(context).hasBean("diskSpaceMetrics").getBean(DiskSpaceMetricsBinder.class) + .hasFieldOrPropertyWithValue("paths", Arrays.asList(new File("."), new File("..")))); + } + @Configuration(proxyBeanMethods = false) static class CustomUptimeMetricsConfiguration { @@ -125,8 +149,9 @@ class SystemMetricsAutoConfigurationTests { static class CustomDiskSpaceMetricsConfiguration { @Bean - DiskSpaceMetrics customDiskSpaceMetrics() { - return new DiskSpaceMetrics(new File(System.getProperty("user.dir"))); + DiskSpaceMetricsBinder customDiskSpaceMetrics() { + return new DiskSpaceMetricsBinder(Collections.singletonList(new File(System.getProperty("user.dir"))), + Tags.empty()); } } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/system/DiskSpaceMetricsBinder.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/system/DiskSpaceMetricsBinder.java new file mode 100644 index 00000000000..d6518b2716e --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/system/DiskSpaceMetricsBinder.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012-2021 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.metrics.system; + +import java.io.File; +import java.util.List; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.binder.MeterBinder; +import io.micrometer.core.instrument.binder.system.DiskSpaceMetrics; + +import org.springframework.util.Assert; + +/** + * A {@link MeterBinder} that binds one or more {@link DiskSpaceMetrics}. + * + * @author Chris Bono + * @since 2.6.0 + */ +public class DiskSpaceMetricsBinder implements MeterBinder { + + private final List paths; + + private final Iterable tags; + + public DiskSpaceMetricsBinder(List paths, Iterable tags) { + Assert.notEmpty(paths, "Paths must not be empty"); + this.paths = paths; + this.tags = tags; + } + + @Override + public void bindTo(MeterRegistry registry) { + this.paths.forEach((path) -> new DiskSpaceMetrics(path, this.tags).bindTo(registry)); + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/system/package-info.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/system/package-info.java new file mode 100644 index 00000000000..c20815badf5 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/system/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2021 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. + */ + +/** + * Actuator support for system metrics. + */ +package org.springframework.boot.actuate.metrics.system; diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/system/DiskSpaceMetricsBinderTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/system/DiskSpaceMetricsBinderTests.java new file mode 100644 index 00000000000..e665c7a0b40 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/system/DiskSpaceMetricsBinderTests.java @@ -0,0 +1,79 @@ +/* + * Copyright 2012-2021 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.metrics.system; + +import java.io.File; +import java.util.Arrays; +import java.util.Collections; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link DiskSpaceMetricsBinder}. + * + * @author Chris Bono + */ +class DiskSpaceMetricsBinderTests { + + @Test + void diskSpaceMetricsWithSinglePath() { + MeterRegistry meterRegistry = new SimpleMeterRegistry(); + File path = new File("."); + DiskSpaceMetricsBinder metricsBinder = new DiskSpaceMetricsBinder(Collections.singletonList(path), + Tags.empty()); + metricsBinder.bindTo(meterRegistry); + + Tags tags = Tags.of("path", path.getAbsolutePath()); + assertThat(meterRegistry.get("disk.free").tags(tags).gauge()).isNotNull(); + assertThat(meterRegistry.get("disk.total").tags(tags).gauge()).isNotNull(); + } + + @Test + void diskSpaceMetricsWithMultiplePaths() { + MeterRegistry meterRegistry = new SimpleMeterRegistry(); + File path1 = new File("."); + File path2 = new File(".."); + DiskSpaceMetricsBinder metricsBinder = new DiskSpaceMetricsBinder(Arrays.asList(path1, path2), Tags.empty()); + metricsBinder.bindTo(meterRegistry); + + Tags tags = Tags.of("path", path1.getAbsolutePath()); + assertThat(meterRegistry.get("disk.free").tags(tags).gauge()).isNotNull(); + assertThat(meterRegistry.get("disk.total").tags(tags).gauge()).isNotNull(); + tags = Tags.of("path", path2.getAbsolutePath()); + assertThat(meterRegistry.get("disk.free").tags(tags).gauge()).isNotNull(); + assertThat(meterRegistry.get("disk.total").tags(tags).gauge()).isNotNull(); + } + + @Test + void diskSpaceMetricsWithCustomTags() { + MeterRegistry meterRegistry = new SimpleMeterRegistry(); + File path = new File("."); + Tags customTags = Tags.of("foo", "bar"); + DiskSpaceMetricsBinder metricsBinder = new DiskSpaceMetricsBinder(Collections.singletonList(path), customTags); + metricsBinder.bindTo(meterRegistry); + + Tags tags = Tags.of("path", path.getAbsolutePath(), "foo", "bar"); + assertThat(meterRegistry.get("disk.free").tags(tags).gauge()).isNotNull(); + assertThat(meterRegistry.get("disk.total").tags(tags).gauge()).isNotNull(); + } + +}