Custom Health Aggregator in Spring Boot 2
Tags: java, spring, springboot, health
Published: 2021-06-26 23:35:37

In this article, we are going to explain how to set up and customise a health endpoint in a Spring Boot application via Spring Actuator.

## Table Of Contents
 * [Getting started with Spring Boot Actuator](#chapter-1)
 * [Health Indicator vs Health Aggregator](#chapter-2)
 * [Setting up Spring Actuator](#chapter-3)
 * [Advanced Configuration](#chapter-4)
 * [Additional Resources](#chapter-5)

The code for this demo could be found here:
{% github psideris89/spring-health-aggregator-demo %}

<br/>
## Getting started with Spring Boot Actuator <a name="chapter-1"></a>

*Spring Boot Actuator* is part of the Spring ecosystem and provides a plethora of functionalities, including the ability to scan the health of your application. This is often called a health endpoint.


## Health Indicator vs Health Aggregator <a name="chapter-2"></a>

__*Health Indicator*__ is a component that returns a health status (UP, DOWN, etc). This component could perform an action, like calling an API or querying a database or service.

__*Health Aggregator*__, as the name implies, processes the various Health Status (of the Health Indicators) and produces a general Health Status for the application.

## Setting up Spring Actuator <a name="chapter-3"></a>
### Create a Spring Boot project
For the demo let's create a Spring Boot app with the following specs:

- Project: **Gradle**
- Language: **Java 16**
- Spring Boot: **2.5.2** (or latest)
- Packaging: **jar**
- Dependencies: **Spring Web**, **Spring Boot Actuator**

Press **Generate** which will download a zip file and unzip the file in the directory of your preference.

![Spring boot configuration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lnhkzk7iqb6be533e4lb.png)

### Run the application
To run the application you can either use your *IDE* (if you use Intellij and Java 16 make sure to update to the latest version) or you can use your terminal and the *Gradle Wrapper* (gradlew).

Navigate to the directory you extracted the zip and run from the terminal

```bash
$ ./gradlew bootRun
```

Just by adding the Actuator in the project dependencies you enable the health auditing of your app which is accessible from __*/actuator/health*__.

As we are running the application locally we need to access __http://localhost:8080/actuator/health__ and the status should be __UP__.

![health status](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ec9ag77g982h2d8fpzem.png)

### Create a Health Indicator
You have the option to either __extent__ the __*AbstractHealthIndicator*__ or __implement__ the __*HealthIndicator*__. In our case we choose the former. 

Make sure you annotate your Health Indicator with __*@Component*__ (you could use other Stereotype annotations or __*@Configuration*__ if you prefer) in order for Spring to create a bean.

The following Indicator is creating a random number and conditionally returns status UP or DOWN with the error details.

```java
package com.psideris.springhealthaggregatordemo;

import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.stereotype.Component;

import java.util.Random;

@Component
public class NumberAppHealthIndicator extends AbstractHealthIndicator {

    @Override
    protected void doHealthCheck(Health.Builder builder) {
        int number = getRandomNumber();

        if (number > 5) {
            builder.up().build();
        } else {
            builder.down().withDetail("error", "Number was less than 5").build();
        }
    }

    private int getRandomNumber() {
        return new Random().ints(1, 10).findFirst().orElseThrow(() -> new RuntimeException("Failed to generate random number"));
    }
}

```

Bear in mind that you can create as many Health Indicators as you want but Spring checks the health of natively supported services (e.g. SQL DBs, mongoDB, elasticsearch, etc) without requiring any customisation, just adding the Actuator dependency.

### Create a Health Aggregator
For the heath aggregator we are going to __implement__ the 
__*StatusAggregator*__.

As we mentioned before the aggregator accumulates the health status from multiple Indicators, which means you can define the logic to indicate when the app is up or down.

```java
package com.psideris.springhealthaggregatordemo;

import org.springframework.boot.actuate.health.Status;
import org.springframework.boot.actuate.health.StatusAggregator;
import org.springframework.stereotype.Component;

import java.util.Set;

@Component
public class AppHealthAggregator implements StatusAggregator {

    @Override
    public Status getAggregateStatus(Set<Status> statuses) {
        boolean statusUp = statuses.stream().allMatch(s -> s.equals(Status.UP));

        return statusUp ? Status.UP : Status.DOWN;
    }
}

```

## Advanced configuration <a name="chapter-4"></a>
### Show details in the health status
To display additional details you can add in the __application.properties__ or __application.yml__ file.

```yml
// application.yml

management:
  endpoint:
    health:
      show-details: always
```

```
// application.properties

management.endpoint.health.show-details=always
```

The health endpoint should now include the following details.

![health status with details](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ci47s3dfxu1ccurrjw11.png)

### Configure the health URL
To configure the url which exposes the health status you can add the following properties in the __application.properties__ or __application.yml__ file.

```yml
// application.yml

management:
  endpoints:
    web:
      base-path: /api
      path-mapping:
        health: health-status
```

```
// application.properties

management.endpoints.web.base-path=/api
management.endpoints.web.path-mapping.health=health-status

```
Now the url that we need to access is __*/api/health-status*__.

### Accessing the Status of individual Health Indicator
By default when creating a Health Indicator it is being exposed in the URL that matches its name (Spring removes the HealthIndicator suffix from the url).

If you provide a value inside the annotation then the URL changes to reflect that. In our case the new one is http://localhost:8080/actuator/health/numberService.

```java
@Component("numberService")
public class NumberAppHealthIndicator extends AbstractHealthIndicator {

    ...

}
``` 

## Additional Resources <a name="chapter-5"></a>
You can find more about Spring Boot Actuator in the official documentation

https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html