Understanding Spring boot's health check Indicators
Writing a health-check for an application can be hard at some times. It would involve checking various aspects of an application like Database, Disk space or even connectivity to another web service. Spring boot makes it easier for us by providing opinionated /actuator/health
endpoint, at-least for the web based Spring-boot
applications.
The Health Indicators and their Auto-Configurations.
The HealthEndpoint
of spring-boot-starter-actuator
module collects Health
information for any beans that are defined as HealthIndicator
.
For a simple web application, there are two HealthIndicator
beans Auto-Configured by default. They are PingHealthIndicator
and DiskSpaceHealthIndicator
. But once we start adding starters for other features like database
,java-mail
etc, They come with their own HealthIndicator
beans that contribute to the HealthEndpoint
.
For example, I have a sample spring boot project with the following setup.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.springhow.examples</groupId>
<artifactId>health-check</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>health-check</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
And my sample main class looks like below.
package com.springhow.examples.healthcheck;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class HealthCheckApplication {
public static void main(String[] args) {
SpringApplication.run(HealthCheckApplication.class, args);
}
}
The Above example boots the application in port 8080. Lets hit the health-check URL on http://localhost:8080/actuator/health. And the result would look like below.
$ curl -s -i -X GET http://localhost:8080/actuator/health
HTTP/1.1 200
Content-Type: application/vnd.spring-boot.actuator.v3+json
Transfer-Encoding: chunked
Date: Sun, 26 Jul 2020 11:36:37 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{
"status": "UP"
}
By interpreting the results, Its a 200 OK
ok response. And the response content is a JSON with a key status
and value UP
which is a good thing to see.
But we want to see more information about these health checks. So in the application.properties
lets change management.endpoint.health.show-details
to always
. Now the response would look like below.
$ curl -s -i -X GET http://localhost:8080/actuator/health
HTTP/1.1 200
Content-Type: application/vnd.spring-boot.actuator.v3+json
Transfer-Encoding: chunked
Date: Sun, 26 Jul 2020 11:33:40 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{
"status": "UP",
"components": {
"diskSpace": {
"status": "UP",
"details": {
"total": 254971625472,
"free": 63426338816,
"threshold": 10485760,
"exists": true
}
},
"ping": {
"status": "UP"
}
}
}
The response contains two components objects which are diskSpace
and ping
These two health information come from DiskSpaceHealthIndicator
and PingHealthIndicator
respectively.
HealthIndicators from Application starters
Usually each application starters comes with their own Auto-Configurations for health indicator. for Example Adding spring-boot-starter-jdbc
enables DataSourceHealthIndicator
to be included in the HealthEndpoint
. Lets try this out by adding following dependencies to the above spring boot project and restart the application.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
This time hitting the health check endpoint return’s information about database as well.
$ curl -s -i -X GET http://localhost:8080/actuator/health
HTTP/1.1 200
Content-Type: application/vnd.spring-boot.actuator.v3+json
Transfer-Encoding: chunked
Date: Sun, 26 Jul 2020 12:41:29 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{
"status": "UP",
"components": {
"db": {
"status": "UP",
"details": {
"database": "H2",
"validationQuery": "isValid()"
}
},
"diskSpace": {
"status": "UP",
"details": {
"total": 254971625472,
"free": 63485485056,
"threshold": 10485760,
"exists": true
}
},
"ping": {
"status": "UP"
}
}
}
And the good thing about the health check endpoint is that individual component health-check is also possible. for Example, /actuator/health/db
would return just the health check for database component.
$ curl -s -i -X GET http://localhost:8080/actuator/health/db
HTTP/1.1 200
Content-Type: application/vnd.spring-boot.actuator.v3+json
Transfer-Encoding: chunked
Date: Sun, 26 Jul 2020 12:55:13 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{
"status": "UP",
"details": {
"database": "H2",
"validationQuery": "isValid()"
}
}
Default Health Indicator beans
The following HealthIndicator
beans are ready for auto configuration when appropriate starter is added to the spring-boot project. Each of these Health indicator is configured through a HealthContributorConfiguration
that resides in the actuator starter.
Health Indicator | Endpoint Path |
---|---|
CassandraHealthIndicator |
/actuator/health/cassandra |
CouchbaseHealthIndicator |
/actuator/health/couchbase |
DataSourceHealthIndicator |
/actuator/health/db |
DiskSpaceHealthIndicator |
/actuator/health/diskSpace |
ElasticsearchRestHealthIndicator |
/actuator/health/elasticsearch |
HazelcastHealthIndicator |
/actuator/health/hazelcast |
InfluxDbHealthIndicator |
/actuator/health/influx |
JmsHealthIndicator |
/actuator/health/jms |
LdapHealthIndicator |
/actuator/health/ldap |
LivenessStateHealthIndicator |
/actuator/health/liveliness |
MailHealthIndicator |
/actuator/health/mail |
MongoHealthIndicator |
/actuator/health/mongo |
Neo4jHealthIndicator |
/actuator/health/neo4j |
PingHealthIndicator |
/actuator/health/ping |
RabbitHealthIndicator |
/actuator/health/rabbit |
ReadinessStateHealthIndicator |
/actuator/health/readiness |
RedisHealthIndicator |
/actuator/health/readiness |
SolrHealthIndicator |
/actuator/health/solr |
Health check groups
Health check components can be grouped together for various purposes. For example, lets say our application use mail
, database
and RabbitMQ
for some of its business logic. But there might be cases that some client requests only use database and email functionality. Here the client faces the following inconveniences.
- With the default arrangement, health-check happens for all components and that may take more time as the components increase.
- If
RabbitMQ
connection is down, then the whole/actuator/health
returns status asDOWN
but the won’t know if the component that it needs is down or not unlessmanagement.endpoint.health.show-details
is set toalways
(which is not recommended for production). - The client will have to check each and every component’s health check that it requires by accessing
/actuator/health/<component-name>
. But for more than one component, the client would still spend more effort doing health-checks.
To avoid all these incontinences, we could arrange our health indicators to a named group. This is possible with a application configuration.
management.endpoint.health.group.my-health-check.include = db,ping
The above properties entry creates a group called my-health-check
and that can only contain health information of DataSourceHealthIndicator
and PingHealthIndicator
. This health check endpoint is mapped to the path /actuator/health/my-health-check
$ curl -s -i -X GET http://localhost:8080/actuator/health/my-health-check
HTTP/1.1 200
Content-Type: application/vnd.spring-boot.actuator.v3+json
Transfer-Encoding: chunked
Date: Sun, 26 Jul 2020 13:35:19 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{
"status": "UP",
"components": {
"db": {
"status": "UP",
"details": {
"database": "H2",
"validationQuery": "isValid()"
}
},
"ping": {
"status": "UP"
}
}
}
This is elegant as the client only has to hit one URL for application status check. The client doesn’t have to worry about any component failures other than db
and ping
. And you could event set management.endpoint.health.group.my-health-check.show-details=never
to just give the status without information for this custom health check.
$ curl -s -i -X GET http://localhost:8080/actuator/health/my-health-check
HTTP/1.1 200
Content-Type: application/vnd.spring-boot.actuator.v3+json
Transfer-Encoding: chunked
Date: Sun, 26 Jul 2020 13:43:17 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{
"status": "UP"
}
All the examples mentioned in this post are available in github for you to play. Check below for the link.