Health Monitoring

  1. Health monitoring means checking, through a URL, whether an application and the systems it depends on are working.
  2. It is used so that monitoring tools, load balancers and Kubernetes can detect a failing application automatically and react, for example by restarting it or stopping traffic to it.
  3. Spring Boot Actuator provides /actuator/health, which combines the status of the database, disk space and any custom checks into one result.
  4. If any critical component is DOWN, the endpoint returns HTTP 503, so tools don’t need to read the JSON.

Features

  • Reports an overall status plus the status of each component.
  • Checks the MySQL connection automatically through the db indicator.
  • Checks free disk space through the diskSpace indicator.
  • Supports custom health checks, such as reading the students table.
  • Provides separate liveness and readiness probes for Kubernetes.
  • Supports health groups that check only selected components.

Health Indicators

Indicator Checks Where it comes from
db Whether the application can connect to MySQL. Built in.
diskSpace Whether enough disk space is free. Built in.
ping A simple “application responds” check. Built in.
student Whether the students table can be read, and how many rows it has. Custom (StudentHealthIndicator).
livenessState Whether the application itself is alive. Built in, when probes are enabled.
readinessState Whether the application is ready to accept traffic. Built in, when probes are enabled.

Health status values

  • UP means the component is working (HTTP 200).
  • DOWN means the component is failing (HTTP 503).
  • OUT_OF_SERVICE means the component was taken out of service on purpose (HTTP 503).
  • UNKNOWN means the state cannot be determined (HTTP 200).

Health endpoints

  • /actuator/health is the overall status with all components.
  • /actuator/health/{name} is one component, such as db.
  • /actuator/health/liveness and /actuator/health/readiness are the Kubernetes probes.
  • /actuator/health/database is a custom group made of db and student.

Hands-on Experiment: Creating a spring boot application to understand how application and dependency health can be checked through monitoring endpoints.

The following steps are involved in developing this hands-on experiment.

Step 1: Generate the Project

Step 2: Project Structure

Step 3: pom.xml

Step 4: application.properties

Step 5: Create Entity Class

Step 6: Create Repository Interface

Step 7: Create Service Class

Step 8: Create Controller Class

Step 9: Main Application Class

Step 10: Run the Main Application Class

Step 1: Configure the Project on Spring Initializr

Go to start.spring.io and set:

  • Project: Maven
  • Language: Java
  • Spring Boot version: 3.2.x
  • Group: com.example
  • Artifact: healthdemo
  • Name: healthdemo
  • Package name: com.example.healthdemo
  • Packaging: Jar
  • Java: 17
  • Dependencies: Spring Web, Spring Data JPA, MySQL Driver, Actuator.

Click Generate to download the ZIP, then extract and import it into your IDE as a Maven project.

Step 2: Generated Project Structure

Step 3: Explore pom.xml

<?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>3.2.5</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>student-health-demo</artifactId>
    <version>1.0.0</version>
    <name>student-health-demo</name>
    <description>Health monitoring of the Student application and its MySQL connection</description>

    <properties>
        <java.version>17</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <!-- Provides /actuator/health and the health indicators -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Step 4: application.properties

spring.application.name=student-health-demo
server.port=8082

# MySQL (database is created automatically if it does not exist) - change username/password as needed
spring.datasource.url=jdbc:mysql://localhost:3306/studentdb?createDatabaseIfNotExist=true&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
spring.datasource.username=root
spring.datasource.password=password

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

# ---------- Health monitoring settings ----------
# Expose only the health and info endpoints
management.endpoints.web.exposure.include=health,info

# Show every component (db, diskSpace, ping, student) with its details
management.endpoint.health.show-components=always
management.endpoint.health.show-details=always

# Enable the liveness and readiness probes (/actuator/health/liveness and /actuator/health/readiness)
management.endpoint.health.probes.enabled=true

# Built-in indicators (enabled by default, shown here for clarity)
management.health.db.enabled=true
management.health.diskspace.enabled=true

# A custom health group: /actuator/health/database checks only the db and student indicators
management.endpoint.health.group.database.include=db,student
management.endpoint.health.group.database.show-details=alwaysCode language: PHP (php)

Step 5: Create Entity Class

package com.example.healthdemo.entity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

@Entity
@Table(name = "students")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @Column(unique = true)
    private String email;

    private String department;

    public Student() {
    }

    public Student(String name, String email, String department) {
        this.name = name;
        this.email = email;
        this.department = department;
    }

    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 String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }

    public String getDepartment() { return department; }
    public void setDepartment(String department) { this.department = department; }
}

Step 6: Create below Repository Interface

package com.example.healthdemo.repository;

import com.example.healthdemo.entity.Student;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface StudentRepository extends JpaRepository<Student, Long> {

    boolean existsByEmail(String email);
}

Step 7: Create below Service class

package com.example.healthdemo.service;

import com.example.healthdemo.repository.StudentRepository;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;

// Custom health check: can the application actually read the students table?
@Component
public class StudentHealthIndicator implements HealthIndicator {

    private final StudentRepository studentRepository;

    public StudentHealthIndicator(StudentRepository studentRepository) {
        this.studentRepository = studentRepository;
    }

    @Override
    public Health health() {
        try {
            long totalStudents = studentRepository.count();
            return Health.up()
                    .withDetail("message", "Students table is accessible")
                    .withDetail("totalStudents", totalStudents)
                    .build();
        } catch (Exception ex) {
            return Health.down()
                    .withDetail("message", "Cannot read the students table")
                    .withDetail("error", ex.getMessage())
                    .build();
        }
    }
}
package com.example.healthdemo.service;

import com.example.healthdemo.entity.Student;
import com.example.healthdemo.repository.StudentRepository;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;

import java.util.List;

@Service
public class StudentService {

    private final StudentRepository studentRepository;

    public StudentService(StudentRepository studentRepository) {
        this.studentRepository = studentRepository;
    }

    public Student addStudent(Student student) {
        if (studentRepository.existsByEmail(student.getEmail())) {
            throw new ResponseStatusException(HttpStatus.CONFLICT,
                    "Email already registered: " + student.getEmail());
        }
        return studentRepository.save(student);
    }

    public List<Student> getAllStudents() {
        return studentRepository.findAll();
    }

    public Student getStudentById(Long id) {
        return studentRepository.findById(id)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND,
                        "Student not found with id: " + id));
    }

    public void deleteStudent(Long id) {
        if (!studentRepository.existsById(id)) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND,
                    "Student not found with id: " + id);
        }
        studentRepository.deleteById(id);
    }
}

Step 8: Create Controller class

package com.example.healthdemo.controller;

import com.example.healthdemo.entity.Student;
import com.example.healthdemo.service.StudentService;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/students")
public class StudentController {

    private final StudentService studentService;

    public StudentController(StudentService studentService) {
        this.studentService = studentService;
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Student addStudent(@RequestBody Student student) {
        return studentService.addStudent(student);
    }

    @GetMapping
    public List<Student> getAllStudents() {
        return studentService.getAllStudents();
    }

    @GetMapping("/{id}")
    public Student getStudentById(@PathVariable Long id) {
        return studentService.getStudentById(id);
    }

    @DeleteMapping("/{id}")
    public String deleteStudent(@PathVariable Long id) {
        studentService.deleteStudent(id);
        return "Student deleted with id: " + id;
    }
}

Step 9: Main Application

package com.example.healthdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class HealthDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(HealthDemoApplication.class, args);
    }
}

Step 10: Run the HealthDemoApplication

Scroll to Top