404 Not Found

404 Not Found


nginx

Spring Boot Actuator

Actuator is Spring Boot's dashboard—providing health status, metrics, and configuration information—and offers operations teams a "bird's-eye view."

1. What You'll Learn


2. A True Story of an Operations Engineer

(1) Pain Point: The production environment is like a black box

Bob is responsible for the operations and maintenance of the OrderFlow production environment, but the application is a complete black box to him: Are the database connections working properly? How much memory is being used? Which APIs are responding slowly? Every time a problem arises, he has to ask Alice to add logs or restart the application to troubleshoot it, resulting in an average Mean Time to Recovery (MTTR) of over 1 hour. Charlie has demanded that the MTTR be reduced to 10 minutes.

(2) Solution for the Actuator

Actuator is ready to use right out of the box and offers a wide range of operations and maintenance endpoints:

YAML
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
BASH
curl http://localhost:8080/actuator/health
# {"status":"UP","components":{"db":{"status":"UP"},"diskSpace":{"status":"UP"}}}

(3) Revenue

After Bob started using Actuator, /health monitored database connectivity, /metrics tracked API response times, and a custom HealthIndicator checked the external payment gateway, reducing the MTTR from 1 hour to 5 minutes.


3. Overview of Actuator Endpoints

(1) List of Built-in Endpoints

Endpoint Description Default Exposure
/actuator/health App Health Status ✅ Yes
/actuator/info App Information ✅ Yes
/actuator/metrics List of Indicators ❌ No
/actuator/metrics/{name} Specific metric value ❌ No
/actuator/env Environment Configuration ❌ No
/actuator/beans Bean List ❌ No
/actuator/loggers Log Level ❌ No
/actuator/threaddump Thread Dump ❌ No
/actuator/heapdump Heap Dump ❌ No
/actuator/prometheus Prometheus-formatted metrics ❌ No

(1) ▶ Example: Enabling the Actuator dependency

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

Output:

TEXT
// Execution successful

(2) ▶ Example: Configuring Endpoint Exposure

YAML
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,env,loggers
      base-path: /actuator
  endpoint:
    health:
      show-details: when-authorized
    metrics:
      enabled: true
  info:
    env:
      enabled: true

Output:

TEXT
The configuration has taken effect.
Exposure Policy Configuration Value Description
Health and Information Only include: health,info Safest, Default
On-Demand Exposure include: health,info,metrics Recommended
Show All include: "*" Development Environment Only
Exclude Specific exclude: env,beans Exclude from All

4. Detailed Explanation of the Health Endpoint

(1) Health Status Aggregation Rules

100%
graph TD
    A["Health Status<br/>Aggregator"] --> B["DiskSpace<br/>UP"]
    A --> C["DataSource<br/>UP"]
    A --> D["PaymentGateway<br/>DOWN"]
    A --> E["Redis<br/>UP"]
    D --> F["Overall: DOWN<br/>Any DOWN → DOWN"]
Status Meaning Aggregation Rules
UP Normal
DOWN Exception Any single DOWN → Overall DOWN
DEGRADED Degraded Non-critical component degradation
OUT_OF_SERVICE Service Not Available Any one → All OUT_OF_SERVICE
UNKNOWN Unknown Default

(1) ▶ Example: Custom HealthIndicator

JAVA
@Component
public class PaymentGatewayHealthIndicator implements HealthIndicator {

    private final RestTemplate restTemplate;

    public PaymentGatewayHealthIndicator(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @Override
    public Health health() {
        try {
            ResponseEntity<String> response = restTemplate.getForEntity(
                "https://api.payment-gateway.com/ping", String.class);
            if (response.getStatusCode().is2xxSuccessful()) {
                return Health.up()
                    .withDetail("gateway", "reachable")
                    .withDetail("responseTime", response.getHeaders().getDate())
                    .build();
            }
            return Health.down()
                .withDetail("gateway", "unhealthy")
                .withDetail("statusCode", response.getStatusCode())
                .build();
        } catch (Exception e) {
            return Health.down()
                .withDetail("gateway", "unreachable")
                .withDetail("error", e.getMessage())
                .build();
        }
    }
}

Output:

TEXT
// Execution successful
💻 Output:

JSON
{
  "status": "DOWN",
  "components": {
    "diskSpace": {"status": "UP", "details": {"total": 536870912000, "free": 268435456000}},
    "db": {"status": "UP", "details": {"database": "MySQL", "validationQuery": "SELECT 1"}},
    "paymentGateway": {"status": "DOWN", "details": {"gateway": "unreachable", "error": "Connection refused"}}
  }
}

5. Detailed Explanation of Metrics Endpoints

(1) ▶ Example: View HTTP request metrics

BASH
# List all available metrics
curl http://localhost:8080/actuator/metrics

# Get specific metric: HTTP server requests
curl http://localhost:8080/actuator/metrics/http.server.requests

# Get filtered metric
curl "http://localhost:8080/actuator/metrics/http.server.requests?tag=uri:/api/v1/orders&tag=status:200"

Output:

TEXT
{"status":"ok","data":{}}
Common Metrics Description
http.server.requests Distribution of HTTP Request Durations
jvm.memory.used JVM Memory Usage
jvm.threads.live Number of Active Threads
process.cpu.usage Process CPU Usage
disk.total / disk.free Disk Space
hikaricp.connections.active Database Connection Pool

(2) ▶ Example: Custom Business Metrics

JAVA
@Service
public class OrderMetrics {

    private final Counter orderCreatedCounter;
    private final Timer orderCreationTimer;
    private final AtomicLong pendingOrders;

    public OrderMetrics(MeterRegistry registry) {
        this.orderCreatedCounter = Counter.builder("orderflow.orders.created")
            .description("Total orders created")
            .tag("service", "orderflow")
            .register(registry);

        this.orderCreationTimer = Timer.builder("orderflow.orders.creation.time")
            .description("Order creation time")
            .register(registry);

        this.pendingOrders = registry.gauge("orderflow.orders.pending",
            new AtomicLong(0));
    }

    public void recordOrderCreated() {
        orderCreatedCounter.increment();
        pendingOrders.incrementAndGet();
    }

    public void recordOrderCompleted() {
        pendingOrders.decrementAndGet();
    }

    public Timer getCreationTimer() {
        return orderCreationTimer;
    }
}

Output:

TEXT
// Execution successful

6. Custom Endpoints

(1) ▶ Example: Custom OrderStats Endpoint

JAVA
@Endpoint(id = "orderstats")
@Component
public class OrderStatsEndpoint {

    private final OrderRepository orderRepository;

    public OrderStatsEndpoint(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    @ReadOperation
    public Map<String, Object> orderStats() {
        long total = orderRepository.count();
        long pending = orderRepository.countByStatus("PENDING");
        long completed = orderRepository.countByStatus("COMPLETED");
        long cancelled = orderRepository.countByStatus("CANCELLED");
        return Map.of(
            "totalOrders", total,
            "pendingOrders", pending,
            "completedOrders", completed,
            "cancelledOrders", cancelled,
            "completionRate", total > 0
                ? String.format("%.2f%%", (double) completed / total * 100)
                : "N/A"
        );
    }

    @ReadOperation
    public Map<String, Object> orderStatsByStatus(
            @Selector String status) {
        long count = orderRepository.countByStatus(status.toUpperCase());
        return Map.of("status", status, "count", count);
    }
}

Output:

TEXT
// Execution successful
YAML
management:
  endpoint:
    orderstats:
      enabled: true
  endpoints:
    web:
      exposure:
        include: health,info,orderstats
💻 Output:

BASH
curl http://localhost:8080/actuator/orderstats
# {"totalOrders":1523,"pendingOrders":45,"completedOrders":1420,"cancelledOrders":58,"completionRate":"93.30%"}

curl http://localhost:8080/actuator/orderstats/PENDING
# {"status":"PENDING","count":45}

7. Security Hardening

(1) Endpoint Security Policy

Level Policy Description
Network Layer Dedicated Management Port management.server.port=8081
Network Layer Bind to Internal IP management.server.address=127.0.0.1
Application Layer Spring Security Control .requestMatchers("/actuator/**").hasRole("ADMIN")
Endpoint Layer Disable Sensitive Endpoints management.endpoint.env.enabled=false

(1) ▶ Example: Production-Level Security Configuration

YAML
# application-prod.yml
management:
  server:
    port: 8081                    # Separate management port
    address: 127.0.0.1            # Bind to localhost only
  endpoints:
    web:
      exposure:
        include: health,prometheus,orderstats
      base-path: /actuator
  endpoint:
    health:
      show-details: when-authorized
    env:
      enabled: false
    beans:
      enabled: false
    heapdump:
      enabled: false

Output:

TEXT
Monitoring config loaded
Prometheus targets: 3 active
Grafana dashboard: ready
Environment Exposure Policy Management Port
Development include: "*" Port 8080
Test include: health,info,metrics,env Same port 8080
Production include: health,prometheus Dedicated Port 8081 + Internal Network

8. Comprehensive Example: Complete Configuration of the OrderFlow Actuator

YAML
# application.yml
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus,orderstats
  endpoint:
    health:
      show-details: when-authorized
    orderstats:
      enabled: true
  metrics:
    tags:
      application: ${spring.application.name}
  info:
    env:
      enabled: true

info:
  app:
    name: OrderFlow Service
    version: @project.version@
    description: E-commerce order management microservice
JAVA
// PaymentGatewayHealthIndicator.java
@Component
public class PaymentGatewayHealthIndicator implements HealthIndicator {
    private final RestTemplate restTemplate;
    public PaymentGatewayHealthIndicator(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }
    @Override
    public Health health() {
        try {
            ResponseEntity<String> resp = restTemplate
                .getForEntity("https://api.payment.com/ping", String.class);
            return resp.getStatusCode().is2xxSuccessful()
                ? Health.up().withDetail("gateway", "reachable").build()
                : Health.down().withDetail("statusCode", resp.getStatusCode()).build();
        } catch (Exception e) {
            return Health.down().withDetail("error", e.getMessage()).build();
        }
    }
}

// OrderStatsEndpoint.java
@Endpoint(id = "orderstats")
@Component
public class OrderStatsEndpoint {
    private final OrderRepository orderRepository;
    public OrderStatsEndpoint(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }
    @ReadOperation
    public Map<String, Object> stats() {
        return Map.of(
            "total", orderRepository.count(),
            "pending", orderRepository.countByStatus("PENDING"),
            "completed", orderRepository.countByStatus("COMPLETED")
        );
    }
}

❓ FAQ

Q Do Actuator endpoints affect performance?
A Read operations (health, metrics) have minimal overhead. Heapdump and threaddump operations pause the application and are disabled by default in production environments. The recommended polling interval for Prometheus endpoints is 15-30 seconds.
Q Who should use the health check endpoint?
A /health is for load balancers and K8s probes; it does not expose sensitive details. /health with details is intended for operations teams and requires ADMIN permissions. Use show-details: when-authorized in production environments.
Q How do I customize health status aggregation rules?
A Implement the HealthAggregator or use the StatusAggregator bean. The default rule is: Any DOWN → Overall DOWN. You can customize the priority using statusOrder.
Q Does /actuator/env expose passwords?
A It displays configuration values, but by default, Spring Boot 3.x masks key-value pairs containing password, secret, key, and token (displaying •••••• instead). It is still recommended to disable this endpoint in production environments.
Q What are the benefits of separating management ports from application ports?
A 1) Management ports are bound only to the internal network and are not exposed to the public internet; 2) Application traffic and management traffic are isolated from each other and do not interfere with one another; 3) Different security policies can be configured for management ports.
Q How do I integrate Actuator metrics with Prometheus?
A Add the micrometer-registry-prometheus dependency, expose the prometheus endpoint, and add a collection target in your Prometheus configuration. This will be covered in detail in a later lesson (L23).

📖 Summary


📝 Exercises

  1. Basic Exercise (Difficulty ⭐): Add an Actuator to OrderFlow, configure it to expose the health, info, and metrics endpoints, and verify that /actuator/health returns the "UP" status.

  2. Advanced Exercise (Difficulty: ⭐⭐): Implement a custom PaymentGatewayHealthIndicator and OrderStatsEndpoint, and configure a separate management port for the production environment.

  3. Challenge (Difficulty: ⭐⭐⭐): Add the micrometer-registry-prometheus dependency, expose the /actuator/prometheus endpoint, write custom business metrics (order creation count, order placement time), and consider naming conventions and tag design for the metrics.

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏