Skip to content

Commit 9717891

Browse files
committed
Polish disk space health indicator
- Supply auto-configuration for the new indicator - As suggested in the pull request, include the free disk space and configured threshold in the health details - Update the documentation to describe the indicator and its two configuration settings - Use @ConfigurationProperties to bind the indicator's configuration. This should make the changes sympathetic to the work being done to automate the configuration properties documentation Closes spring-projectsgh-1297
1 parent 78d7fe9 commit 9717891

File tree

6 files changed

+120
-62
lines changed

6 files changed

+120
-62
lines changed

spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/HealthIndicatorAutoConfiguration.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
3131
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
3232
import org.springframework.boot.actuate.health.DataSourceHealthIndicator;
33+
import org.springframework.boot.actuate.health.DiskSpaceHealthIndicator;
34+
import org.springframework.boot.actuate.health.DiskSpaceHealthIndicatorProperties;
3335
import org.springframework.boot.actuate.health.HealthAggregator;
3436
import org.springframework.boot.actuate.health.HealthIndicator;
3537
import org.springframework.boot.actuate.health.MongoHealthIndicator;
@@ -52,6 +54,7 @@
5254
import org.springframework.boot.autoconfigure.mongo.MongoDataAutoConfiguration;
5355
import org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration;
5456
import org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration;
57+
import org.springframework.boot.context.properties.ConfigurationProperties;
5558
import org.springframework.context.annotation.Bean;
5659
import org.springframework.context.annotation.Configuration;
5760
import org.springframework.data.mongodb.core.MongoTemplate;
@@ -256,4 +259,22 @@ public HealthIndicator rabbitHealthIndicator() {
256259
}
257260
}
258261

262+
@Configuration
263+
@ConditionalOnExpression("${health.diskspace.enabled:true}")
264+
public static class DiskSpaceHealthIndicatorConfiguration {
265+
266+
@Bean
267+
@ConditionalOnMissingBean(name = "diskSpaceHealthIndicator")
268+
public HealthIndicator diskSpaceHealthIndicator(
269+
DiskSpaceHealthIndicatorProperties properties) {
270+
return new DiskSpaceHealthIndicator(properties);
271+
}
272+
273+
@Bean
274+
@ConfigurationProperties("health.diskspace")
275+
public DiskSpaceHealthIndicatorProperties diskSpaceHealthIndicatorProperties() {
276+
return new DiskSpaceHealthIndicatorProperties();
277+
}
278+
}
279+
259280
}

spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/DiskSpaceHealthIndicator.java

Lines changed: 17 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -16,67 +16,45 @@
1616

1717
package org.springframework.boot.actuate.health;
1818

19-
import java.io.File;
2019
import org.apache.commons.logging.Log;
2120
import org.apache.commons.logging.LogFactory;
2221
import org.springframework.beans.factory.annotation.Autowired;
23-
import org.springframework.beans.factory.annotation.Value;
24-
import org.springframework.stereotype.Component;
25-
import org.springframework.util.Assert;
2622

2723
/**
28-
* A {@link HealthIndicator} that checks for available disk space.
24+
* A {@link HealthIndicator} that checks available disk space and reports a status of
25+
* {@link Status#DOWN} when it drops below a configurable threshold.
2926
*
3027
* @author Mattias Severson
28+
* @author Andy Wilkinson
3129
* @since 1.2.0
3230
*/
33-
@Component
3431
public class DiskSpaceHealthIndicator extends AbstractHealthIndicator {
32+
3533
private static Log logger = LogFactory.getLog(DiskSpaceHealthIndicator.class);
3634

37-
private final File path;
38-
private final long thresholdBytes;
35+
private final DiskSpaceHealthIndicatorProperties properties;
3936

4037
/**
41-
* Constructor.
42-
* @param path The path to a directory for checking available disk space. The
43-
* application must have read access to this path. Additionally, the
44-
* {@link java.lang.SecurityManager#checkRead(String)} will be called
45-
* if a security manager is used.
46-
* By default, it uses the system property {@code user.dir}, i.e. the
47-
* current directory when the JVM was started.
48-
* @param thresholdBytes The threshold (in Bytes) of remaining disk space that will
49-
* trigger this health indicator when exceeded.
50-
* Default value is 10485760 Bytes (10 MB).
38+
* Create a new {@code DiskSpaceHealthIndicator}
5139
*/
5240
@Autowired
53-
public DiskSpaceHealthIndicator(@Value("${health.path?:${user.dir}}") String path,
54-
@Value("${health.threshold.bytes:10485760}") long thresholdBytes) {
55-
this.path = new File(path);
56-
this.thresholdBytes = thresholdBytes;
57-
verifyPathIsAccessible(this.path);
58-
Assert.isTrue(thresholdBytes >= 0, "thresholdBytes must be greater than 0");
41+
public DiskSpaceHealthIndicator(DiskSpaceHealthIndicatorProperties properties) {
42+
this.properties = properties;
5943
}
6044

6145
@Override
6246
protected void doHealthCheck(Health.Builder builder) throws Exception {
63-
long diskFreeInBytes = this.path.getFreeSpace();
64-
if (diskFreeInBytes >= this.thresholdBytes) {
47+
long diskFreeInBytes = this.properties.getPath().getFreeSpace();
48+
if (diskFreeInBytes >= this.properties.getThreshold()) {
6549
builder.up();
66-
} else {
67-
logger.warn(String.format("Free disk space threshold exceeded. " +
68-
"Available: %d bytes (threshold: %d bytes).",
69-
diskFreeInBytes, this.thresholdBytes));
70-
builder.down();
71-
}
72-
}
73-
74-
private static void verifyPathIsAccessible(File path) {
75-
if (!path.exists()) {
76-
throw new IllegalArgumentException(String.format("Path does not exist: %s", path));
7750
}
78-
if (!path.canRead()) {
79-
throw new IllegalStateException(String.format("Path cannot be read: %s", path));
51+
else {
52+
logger.warn(String.format("Free disk space below threshold. "
53+
+ "Available: %d bytes (threshold: %d bytes)", diskFreeInBytes,
54+
this.properties.getThreshold()));
55+
builder.down();
8056
}
57+
builder.withDetail("free", diskFreeInBytes).withDetail("threshold",
58+
this.properties.getThreshold());
8159
}
8260
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2012-2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.health;
18+
19+
import java.io.File;
20+
21+
import org.springframework.util.Assert;
22+
23+
/**
24+
* External configuration properties for {@link DiskSpaceHealthIndicator}
25+
*
26+
* @author Andy Wilkinson
27+
* @since 1.2.0
28+
*/
29+
public class DiskSpaceHealthIndicatorProperties {
30+
31+
private File path = new File(".");
32+
33+
private long threshold = 10 * 1024 * 1024;
34+
35+
public File getPath() {
36+
return this.path;
37+
}
38+
39+
public void setPath(File path) {
40+
if (!path.exists()) {
41+
throw new IllegalArgumentException(String.format("Path '%s' does not exist",
42+
path));
43+
}
44+
if (!path.canRead()) {
45+
throw new IllegalStateException(String.format("Path '%s' cannot be read",
46+
path));
47+
}
48+
this.path = path;
49+
}
50+
51+
public long getThreshold() {
52+
53+
return this.threshold;
54+
}
55+
56+
public void setThreshold(long threshold) {
57+
Assert.isTrue(threshold >= 0, "threshold must be greater than 0");
58+
this.threshold = threshold;
59+
}
60+
}

spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/DiskSpaceHealthIndicatorTests.java

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@
1717
package org.springframework.boot.actuate.health;
1818

1919
import java.io.File;
20+
2021
import org.junit.Before;
2122
import org.junit.Rule;
2223
import org.junit.Test;
2324
import org.junit.rules.ExpectedException;
2425
import org.junit.runner.RunWith;
2526
import org.mockito.Mock;
2627
import org.mockito.runners.MockitoJUnitRunner;
27-
import org.springframework.test.util.ReflectionTestUtils;
2828

2929
import static org.junit.Assert.assertEquals;
3030
import static org.mockito.Mockito.when;
@@ -49,10 +49,10 @@ public class DiskSpaceHealthIndicatorTests {
4949

5050
@Before
5151
public void setUp() throws Exception {
52-
this.healthIndicator =
53-
new DiskSpaceHealthIndicator(DiskSpaceHealthIndicatorTests.class.getResource("").getPath(),
54-
THRESHOLD_BYTES);
55-
ReflectionTestUtils.setField(this.healthIndicator, "path", this.fileMock);
52+
when(this.fileMock.exists()).thenReturn(true);
53+
when(this.fileMock.canRead()).thenReturn(true);
54+
this.healthIndicator = new DiskSpaceHealthIndicator(createProperties(
55+
this.fileMock, THRESHOLD_BYTES));
5656
}
5757

5858
@Test
@@ -61,6 +61,8 @@ public void diskSpaceIsUp() throws Exception {
6161

6262
Health health = this.healthIndicator.health();
6363
assertEquals(Status.UP, health.getStatus());
64+
assertEquals(THRESHOLD_BYTES, health.getDetails().get("threshold"));
65+
assertEquals(THRESHOLD_BYTES + 10, health.getDetails().get("free"));
6466
}
6567

6668
@Test
@@ -69,21 +71,14 @@ public void diskSpaceIsDown() throws Exception {
6971

7072
Health health = this.healthIndicator.health();
7173
assertEquals(Status.DOWN, health.getStatus());
74+
assertEquals(THRESHOLD_BYTES, health.getDetails().get("threshold"));
75+
assertEquals(THRESHOLD_BYTES - 10, health.getDetails().get("free"));
7276
}
7377

74-
@Test
75-
public void throwsExceptionForUnknownPath() {
76-
exception.expect(IllegalArgumentException.class);
77-
exception.expectMessage("Path does not exist: an_path_that_does_not_exist");
78-
79-
new DiskSpaceHealthIndicator("an_path_that_does_not_exist", THRESHOLD_BYTES);
80-
}
81-
82-
@Test
83-
public void throwsExceptionForNegativeThreshold() {
84-
exception.expect(IllegalArgumentException.class);
85-
exception.expectMessage("thresholdBytes must be greater than 0");
86-
87-
new DiskSpaceHealthIndicator(DiskSpaceHealthIndicatorTests.class.getResource("").getPath(), -1);
78+
private DiskSpaceHealthIndicatorProperties createProperties(File path, long threshold) {
79+
DiskSpaceHealthIndicatorProperties properties = new DiskSpaceHealthIndicatorProperties();
80+
properties.setPath(path);
81+
properties.setThreshold(threshold);
82+
return properties;
8883
}
8984
}

spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,10 @@ content into your application; rather pick only the properties that you need.
387387
endpoints.trace.sensitive=true
388388
endpoints.trace.enabled=true
389389
390+
# HEALTH INDICATORS
391+
health.diskspace.path=.
392+
health.diskspace.threshold=10485760
393+
390394
# MVC ONLY ENDPOINTS
391395
endpoints.jolokia.path=jolokia
392396
endpoints.jolokia.sensitive=true

spring-boot-docs/src/main/asciidoc/production-ready-features.adoc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,10 @@ To provide custom health information you can register a Spring bean that impleme
165165
Spring Boot provides a
166166
{sc-spring-boot-actuator}/health/DataSourceHealthIndicator.{sc-ext}[`DataSourceHealthIndicator`]
167167
implementation that attempts a simple database test (reusing the validation query set on the data
168-
source, if any) as well as implementations for Redis, MongoDB and RabbitMQ.
169-
170-
Spring Boot adds the `HealthIndicator` instances automatically if beans of type `DataSource`,
171-
`MongoTemplate`, `RedisConnectionFactory`, `RabbitTemplate` are present in the `ApplicationContext`.
168+
source, if any) as well as implementations for Redis, MongoDB and RabbitMQ. Spring Boot adds the
169+
`HealthIndicator` instances automatically if beans of type `DataSource`, `MongoTemplate`,
170+
`RedisConnectionFactory`, and `RabbitTemplate` respectively are present in the
171+
`ApplicationContext`. A health indicator that checks free disk space is also provided.
172172

173173
Besides implementing custom a `HealthIndicator` type and using out-of-box {sc-spring-boot-actuator}/health/Status.{sc-ext}[`Status`]
174174
types, it is also possible to introduce custom `Status` types for different or more complex system

0 commit comments

Comments
 (0)