Skip to main content

Spring Boot Actuator

Introduction

Spring Boot Actuator is a sub-project of Spring Boot that adds several production-ready features to your application. These features help you monitor and manage your application when deployed in production environments. Actuator provides HTTP endpoints or JMX beans that allow you to interact with your application programmatically to get valuable insights about its internal workings and health status.

With minimal setup, Actuator gives you:

  • Health checks
  • Metrics collection
  • Environment information
  • Thread dump information
  • And much more!

In this tutorial, you'll learn how to integrate Spring Boot Actuator into your applications and leverage its powerful features to enhance application monitoring and management.

Getting Started with Spring Boot Actuator

Adding Actuator to Your Project

To use Spring Boot Actuator, you need to add the actuator dependency to your project:

For Maven:

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

For Gradle:

groovy
implementation 'org.springframework.boot:spring-boot-starter-actuator'

Once added, restart your application, and Spring Boot will automatically configure several built-in endpoints.

Actuator Endpoints

Actuator provides numerous endpoints that expose operational information about your application. By default, most endpoints except for /health are disabled for security reasons.

Common Actuator Endpoints

Here are some of the most useful endpoints:

Endpoint IDDescription
healthShows application health information
infoDisplays arbitrary application info
metricsShows metrics information
envExposes environment properties
loggersShows and modifies the configured loggers
httptraceDisplays HTTP trace information
threaddumpPerforms a thread dump
mappingsDisplays all @RequestMapping paths

Enabling Endpoints

To enable all web endpoints, add the following to your application.properties or application.yml:

properties
# application.properties
management.endpoints.web.exposure.include=*

Or in YAML:

yaml
# application.yml
management:
endpoints:
web:
exposure:
include: "*"

For production environments, it's better to explicitly specify which endpoints to expose:

properties
management.endpoints.web.exposure.include=health,info,metrics

Accessing Endpoints

Once configured, you can access the endpoints via HTTP. By default, all endpoints are accessible under the /actuator base path:

Health Endpoint: Monitoring Application Status

The /health endpoint is one of the most useful Actuator features. It provides basic information about your application's health.

Basic Health Check

By default, accessing http://localhost:8080/actuator/health returns:

json
{
"status": "UP"
}

Detailed Health Information

To show more details, modify your properties:

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

Now the health endpoint provides more information:

json
{
"status": "UP",
"components": {
"db": {
"status": "UP",
"details": {
"database": "H2",
"validationQuery": "isValid()"
}
},
"diskSpace": {
"status": "UP",
"details": {
"total": 500107862016,
"free": 201500540928,
"threshold": 10485760
}
}
}
}

Custom Health Indicators

You can create custom health indicators by implementing the HealthIndicator interface:

java
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;

@Component
public class CustomHealthIndicator implements HealthIndicator {

@Override
public Health health() {
boolean healthy = checkIfSystemIsHealthy(); // Your custom health check logic

if (healthy) {
return Health.up()
.withDetail("description", "The system is running smoothly!")
.build();
} else {
return Health.down()
.withDetail("description", "The system is experiencing issues!")
.withDetail("error", "Connection timeout")
.build();
}
}

private boolean checkIfSystemIsHealthy() {
// Your custom health check implementation
return true; // For demonstration purposes
}
}

Your custom health indicator will appear as a new component in the health endpoint output.

Metrics Endpoint: Measuring Application Performance

The /metrics endpoint provides valuable performance metrics about your application.

Viewing Available Metrics

Access http://localhost:8080/actuator/metrics to see all available metrics:

json
{
"names": [
"jvm.memory.used",
"jvm.memory.max",
"http.server.requests",
"system.cpu.usage",
// ... more metrics
]
}

Viewing Specific Metrics

To view a specific metric, append its name to the URL. For example, http://localhost:8080/actuator/metrics/system.cpu.usage:

json
{
"name": "system.cpu.usage",
"description": "The \"recent cpu usage\" for the whole system",
"baseUnit": null,
"measurements": [
{
"statistic": "VALUE",
"value": 0.123456
}
],
"availableTags": []
}

Custom Metrics

You can create custom metrics using the MeterRegistry:

java
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

private final Counter orderCounter;

public OrderService(MeterRegistry meterRegistry) {
this.orderCounter = meterRegistry.counter("orders.created", "type", "created");
}

public void createOrder() {
// Business logic to create an order

// Increment the metric
orderCounter.increment();
}
}

Info Endpoint: Providing Application Information

The /info endpoint displays information about your application, but by default, it returns an empty JSON object. You can customize it in several ways:

Using Properties

In your application.properties:

properties
info.app.name=My Spring Boot Application
info.app.description=A sample Spring Boot application with Actuator
info.app.version=1.0.0
[email protected]
info.company.name=My Company

Using Build Information

For Maven, add to your pom.xml:

xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

For Gradle, add to your build.gradle:

groovy
springBoot {
buildInfo()
}

Using Git Information

You can add Git details to the info endpoint using:

For Maven:

xml
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
</plugin>

For Gradle:

groovy
plugins {
id "com.gorylenko.gradle-git-properties" version "2.2.4"
}

Custom Info Contributors

For more dynamic information, implement InfoContributor:

java
import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Component
public class CustomInfoContributor implements InfoContributor {

@Override
public void contribute(Info.Builder builder) {
Map<String, Object> systemInfo = new HashMap<>();
systemInfo.put("cores", Runtime.getRuntime().availableProcessors());
systemInfo.put("memory", Runtime.getRuntime().maxMemory());

builder.withDetail("system", systemInfo);
}
}

Securing Actuator Endpoints

Since Actuator endpoints contain sensitive information, it's important to secure them in production environments.

Basic Security Configuration

Add Spring Security to your project:

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

Then configure security for Actuator endpoints:

java
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class ActuatorSecurityConfig {

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.requestMatcher(EndpointRequest.toAnyEndpoint())
.authorizeRequests()
.requestMatchers(EndpointRequest.to("health", "info")).permitAll()
.anyRequest().hasRole("ACTUATOR")
.and()
.httpBasic();

return http.build();
}
}

This configuration:

  • Allows public access to /health and /info
  • Requires authentication with the "ACTUATOR" role for all other endpoints
  • Uses HTTP Basic Authentication

Real-world Example: Monitoring a Spring Boot Application

Let's create a complete example of a Spring Boot application with Actuator and custom metrics:

Project Setup

Start with a basic Spring Boot web application with Actuator:

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

Application Configuration

Configure Actuator in application.properties:

properties
# Expose all actuator endpoints
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always

# Info endpoint data
info.app.name=Product Service
info.app.description=A service that manages products
info.app.version=1.0.0

# Custom management port (optional)
management.server.port=8081

Creating the Product Service

java
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Service
public class ProductService {

private final List<Product> productList = new ArrayList<>();
private final Counter productCreatedCounter;
private final Counter productRequestCounter;

public ProductService(MeterRegistry meterRegistry) {
this.productCreatedCounter = meterRegistry.counter("products.created");
this.productRequestCounter = meterRegistry.counter("products.requested");

// Add some sample products
productList.add(new Product(1L, "Laptop", 999.99));
productList.add(new Product(2L, "Phone", 599.99));
}

public Product createProduct(Product product) {
product.setId((long) (productList.size() + 1));
productList.add(product);
productCreatedCounter.increment();
return product;
}

public List<Product> getAllProducts() {
productRequestCounter.increment();
return productList;
}

public Optional<Product> getProductById(Long id) {
productRequestCounter.increment();
return productList.stream()
.filter(product -> product.getId().equals(id))
.findFirst();
}
}

Product Model Class

java
public class Product {
private Long id;
private String name;
private double price;

// Default constructor
public Product() {}

public Product(Long id, String name, double price) {
this.id = id;
this.name = name;
this.price = price;
}

// Getters and setters
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public double getPrice() {
return price;
}

public void setPrice(double price) {
this.price = price;
}
}

Controller Class

java
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/products")
public class ProductController {

private final ProductService productService;

public ProductController(ProductService productService) {
this.productService = productService;
}

@GetMapping
public List<Product> getAllProducts() {
return productService.getAllProducts();
}

@GetMapping("/{id}")
public ResponseEntity<Product> getProductById(@PathVariable Long id) {
return productService.getProductById(id)
.map(ResponseEntity::ok)
.orElseGet(() -> ResponseEntity.notFound().build());
}

@PostMapping
public Product createProduct(@RequestBody Product product) {
return productService.createProduct(product);
}
}

Custom 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 ProductServiceHealthIndicator implements HealthIndicator {

private final ProductService productService;

public ProductServiceHealthIndicator(ProductService productService) {
this.productService = productService;
}

@Override
public Health health() {
try {
int productCount = productService.getAllProducts().size();

return Health.up()
.withDetail("productCount", productCount)
.withDetail("status", "Product service is working correctly")
.build();
} catch (Exception e) {
return Health.down()
.withDetail("error", e.getMessage())
.build();
}
}
}

Testing the Application

  1. Start the application

  2. Access the REST API:

  3. Access Actuator endpoints:

You'll see that the metrics are updated as you interact with the API, and the health endpoint shows your custom health indicator's status.

Summary

Spring Boot Actuator is a powerful tool for monitoring and managing your Spring Boot applications in production environments. In this tutorial, we've covered:

  • How to add and configure Actuator to your Spring Boot application
  • Key Actuator endpoints like health, metrics, and info
  • Creating custom health indicators and metrics
  • Securing Actuator endpoints
  • A real-world example integrating Actuator with a product service

By leveraging Actuator, you can gain valuable insights into your application's health, performance, and behavior. This allows for better troubleshooting, monitoring, and management of your applications in production.

Additional Resources

Exercises

  1. Add Spring Boot Actuator to an existing Spring Boot application and enable all endpoints.
  2. Create a custom health indicator that checks if an external service is available.
  3. Implement custom metrics to track the number of successful and failed requests to a specific endpoint.
  4. Configure Actuator to use a separate port from your application.
  5. Secure your Actuator endpoints using Spring Security, allowing only authenticated users with a specific role to access sensitive endpoints.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)