Spring Boot custom HealthIndicator

A big part of the DevOps responsibilities is to monitor and maintain the health of running servers. If a production server goes down, appropriate actions must be undertaken to bring the service back to life. However, before any resurrection, one must know that the server is malfunctioning in the first place. In an automated cloud environment, this can be handled by a load balancer calling a known endpoint. For this reason, Spring Boot has a /health endpoint that is part of its Actuator features. In this blog post, we will see how a custom health indicator can be implemented that adds to the existing /health endpoint.

Not enough space?

A common problem is that you run out of disk space. Spring Boot provides default log rotation, however there might be an access log, a database log, environment changes, and what not. Over time, even small log statements will eat up the disk space. More often than not, the problem manifests itself in production. The same problem is probably present in test and staging environments as well, but typically these environments are restarted more frequently (opportunity for clearing logs) and they are subject to less load (fewer actions logged).

The idea with a custom DiskSpaceHealthIndicator is to provide a heads up warning before the server runs out of space completely.

Implementation

A custom HealthIndicator that checks the available disk space can be implemented as follows:

Note that this example is based on Spring Boot version 1.1+, because the HealthIndicator interface was changed as part of the Spring Boot 1.1 release.

When tested, the application responds appropriately:

or if the specified threshold has been exceeded:

Explanation

  • Any HealthIndicator Spring bean will contribute to the overall health status presented at the /health endpoint. The AbstractHealthIndicator implements the interface and provides a convenient Health.Builder that can be used.
  • Two optional properties have been specified. They have default values, but they can easily be overridden, e.g. by providing appropriate values in the application.properties file.

    • The ${health.filestore.path:${user.dir}}" specifies the path to the underlying FileStore. Here, it defaults to ${user.dir}, i.e. a system property that defines the current directory when the JVM was started. You may need to set it to something else, such as the root folder, the folder in which the application is deployed, etc, see considerations below.
    • The ${health.filestore.threshold.bytes:10485760} is the amount of free space left in bytes before the health indicator will change its state. In this case 10485760 (10MB) has been set to the default, which gives some space for any services to flush data to their log files despite that the application has become unavailable.
  • The health check itself is trivial, set the status to Status.UP if the available disk is greater than the threshold. If not, set the status to Status.DOWN

HTTP Status Codes

It is possible to change the HTTP status codes of the /health response. By default, if the status is Status.DOWN or Status.OUT_OF_SERVICE HTTP status 503 - Service Unavailable will be returned. (Please note that in Spring Boot versions <= 1.1.4, there is a bug that will cause 200 - OK to be returned by default.)

If you do not like the default HTTP status codes, or if you instantiate your own Status, you can add a line in your application.properties to configure the desired HTTP status code. For example, if you would like the Status.DOWN to return 500 - Internal Server Error, specify the following:

The key starts with endpoints.health.mapping followed by the code (case sensitive) that is passed to the Status constructor, and the value a HttpStatus (case insensitive).

Considerations

  • The application must have read access to the path specified by the ${health.filestore.path:${user.dir}}" property, otherwise the
    Files.getFileStore(Path path) will throw an exception. Likewise, if a security manager has been installed, the checkRead(String path) method will be called, potentially resulting in a SecurityException being thrown.
  • The FileStore.getUnallocatedSpace() was used to find out how much disk space is available. As an alternative, one could call the FileStore.getUsableSpace() method instead that checks only for the available disk for this JVM.
  • The methods of the FileStore return hints to the available disk size, but it does not provide any guarantees “that it is possible to use most or any of these bytes”, see the Javadoc.
  • The actual data can be presented as part of the /health response message, simply add the following lines to the end of the doHealthCheck() method above:

    As a result, any call to the /health endpoint will present the current figures:
  • Alternatively, if you are more cautious, you can implement the health as it is, and add the actual figures to the /metrics endpoint instead, please see the reference docs for details.

Update

July 27th, 2014. Changed default path to ${user.dir}.

September 9th, 2014. After publishing this blog post I was asked to submit a pull request with this feature. Christian Dupuis reviewed the code, and it was merged into Spring Boot as of 1.2.0.M1. More information can be found in the Spring Boot 1.2 Release Notes.

References

Mattias Severson

Mattias is a senior software engineer specialized in backend architecture and development with experience of cloud based applications and scalable solutions. He is a clean code proponent who appreciates Agile methodologies and pragmatic Test Driven Development. Mattias has experience from many different environments, including everything between big international projects that last for years and solo, single day jobs. He is open-minded and curious about new technologies. Mattias believes in continuous improvement on a personal level as well as in the projects that he is working on. Additionally, Mattias is a frequent speaker at user groups, companies and conferences.

This Post Has 2 Comments

  1. Mattias, thank you for posting this.

    I wonder if the bug you refer to whereby 200 OK is returned instead of 503 should refer to Spring Boot version 1.1.4 rather than 1.14? I don’t see a version 1.14 in Maven Central.

Leave a Reply

Close Menu