6 min read

Understanding Spring-boot's health-check Indicators

July 26, 2020

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 as DOWN but the won’t know if the component that it needs is down or not unless management.endpoint.health.show-details is set to always (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 doesnt 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.

Raja Anbazhagan

About the author

Raja is a Software Engineer with over 7 years of experience in working with Enterprise Java applications. Lately, He is focused in cloud-based Java applications and serverless technologies. He spends his spare time in stackoverflow.

Browse Categories