Introduction to Spring Boot Actuator

Spring Boot offers an impressive suite of built-in health checks through its Actuator module. This makes it easy to monitor and manage various aspects of an application’s health. However, sometimes the default health indicators might not be sufficient to cover all the specific requirements of an application. This is where custom health checks come into play. In this article, we will explore how to create custom health checks in Spring Boot, with detailed coding examples.

Spring Boot Actuator provides production-ready features that help you monitor and manage your application. It includes a set of built-in endpoints that can be used to gather various metrics, check health, and trace requests. Some of the commonly used endpoints include:

  • /actuator/health: Provides basic health information of the application.
  • /actuator/metrics: Displays various application metrics.
  • /actuator/info: Displays arbitrary application info.

Enabling Spring Boot Actuator

Before diving into custom health checks, let’s first enable Spring Boot Actuator in a Spring Boot application. To do this, add the following dependency to your pom.xml:

xml

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

You also need to configure the endpoints in your application.properties or application.yml file to make them accessible:

properties

management.endpoints.web.exposure.include=*

Creating a Basic Custom Health Check

Spring Boot allows you to create custom health indicators by implementing the HealthIndicator interface. Here’s a simple example that checks if a particular feature is enabled in the application.

Create the Health Indicator

java

import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
@Component
public class FeatureToggleHealthIndicator implements HealthIndicator {@Override
public Health health() {
boolean featureEnabled = checkIfFeatureIsEnabled();if (featureEnabled) {
return Health.up().withDetail(“featureToggle”, “enabled”).build();
} else {
return Health.down().withDetail(“featureToggle”, “disabled”).build();
}
}private boolean checkIfFeatureIsEnabled() {
// Logic to check if the feature is enabled
return true; // or false based on your check
}
}

In this example, the FeatureToggleHealthIndicator class implements the HealthIndicator interface and overrides the health() method to return the health status of a specific feature.

Accessing the Custom Health Check

After defining the custom health indicator, it will be automatically included in the /actuator/health endpoint output:

json

{
"status": "UP",
"components": {
"featureToggle": {
"status": "UP",
"details": {
"featureToggle": "enabled"
}
}
}
}

Creating a Custom Database Health Check

Let’s take a more complex example where we create a custom health check for a database connection. While Spring Boot Actuator provides built-in database health checks, there may be scenarios where custom logic is needed.

Define the Health Indicator

java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
@Component
public class CustomDatabaseHealthIndicator implements HealthIndicator {@Autowired
private JdbcTemplate jdbcTemplate;@Override
public Health health() {
try {
// Execute a simple query to check the health of the database
jdbcTemplate.execute(“SELECT 1”);
return Health.up().withDetail(“database”, “reachable”).build();
} catch (Exception e) {
return Health.down().withDetail(“database”, “unreachable”).withException(e).build();
}
}
}

In this example, the CustomDatabaseHealthIndicator class checks the connectivity of the database by executing a simple query.

Accessing the Custom Database Health Check

The custom database health check will be included in the /actuator/health endpoint output:

json

{
"status": "UP",
"components": {
"db": {
"status": "UP",
"details": {
"database": "reachable"
}
}
}
}

Combining Multiple Custom Health Checks

In a real-world application, you may have multiple custom health checks. Spring Boot Actuator can combine these health checks into a single health status. Here’s how you can combine multiple health checks:

Create Multiple Health Indicators

java

@Component
public class ServiceAHealthIndicator implements HealthIndicator {
@Override
public Health health() {
// Custom logic for Service A
return Health.up().withDetail(“serviceA”, “running”).build();
}
}@Component
public class ServiceBHealthIndicator implements HealthIndicator {@Override
public Health health() {
// Custom logic for Service B
return Health.up().withDetail(“serviceB”, “running”).build();
}
}

Access the Combined Health Check

The combined health status of all custom health checks will be available in the /actuator/health endpoint output:

json

{
"status": "UP",
"components": {
"serviceA": {
"status": "UP",
"details": {
"serviceA": "running"
}
},
"serviceB": {
"status": "UP",
"details": {
"serviceB": "running"
}
}
}
}

Advanced Custom Health Checks

For more advanced use cases, you can leverage additional Actuator features, such as HealthAggregator and HealthEndpointExtension.

Implementing a Health Aggregator

A HealthAggregator can be used to define custom rules for aggregating health statuses. Here’s an example:

java

import org.springframework.boot.actuate.health.CompositeHealth;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.stereotype.Component;
import java.util.Map;@Component
public class CustomHealthAggregator implements HealthAggregator {@Override
public Health aggregate(Map<String, Health> healths) {
boolean allUp = healths.values().stream().allMatch(health -> health.getStatus().equals(Status.UP));if (allUp) {
return Health.up().withDetails(healths).build();
} else {
return Health.down().withDetails(healths).build();
}
}
}

Extending the Health Endpoint

You can also extend the /actuator/health endpoint to include custom logic:

java

import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.health.Health;
import org.springframework.stereotype.Component;
@Component
@Endpoint(id = “customHealth”)
public class CustomHealthEndpoint {@ReadOperation
public Health health() {
// Custom health check logic
return Health.up().withDetail(“customHealthCheck”, “OK”).build();
}
}

This defines a new endpoint /actuator/customHealth that includes custom health check logic.

Conclusion

Spring Boot Actuator provides a robust framework for monitoring and managing application health. While the built-in health indicators cover a broad range of common use cases, custom health checks offer the flexibility to address specific needs that are unique to your application. By implementing custom HealthIndicator classes, you can monitor various aspects of your system, such as feature toggles, external service dependencies, or specific business logic requirements.

In addition, combining multiple custom health checks and leveraging advanced features like HealthAggregator and HealthEndpointExtension allows for fine-grained control and comprehensive health reporting. This ensures that you can detect and respond to potential issues proactively, maintaining the reliability and availability of your application.

By understanding and utilizing these capabilities, you can create a robust health monitoring strategy tailored to the specific needs of your application, ensuring that it remains resilient and performant in production environments.

Spring Boot’s approach to health checks underscores the importance of observability in modern software development. As systems grow increasingly complex, the ability to monitor and manage application health in a detailed and customizable manner becomes indispensable. Custom health checks are a powerful tool in this regard, providing the insight and control necessary to maintain healthy, reliable systems.