BLOG POSTS
    MangoHost Blog / Spring Boot Actuator Endpoints – Monitoring Your Application
Spring Boot Actuator Endpoints – Monitoring Your Application

Spring Boot Actuator Endpoints – Monitoring Your Application

Spring Boot Actuator provides production-ready features for monitoring and managing your applications through HTTP endpoints and JMX beans. These endpoints expose crucial information about your application’s health, metrics, configuration, and runtime behavior – making it indispensable for production deployments where visibility into application performance is critical. This guide walks you through setting up, configuring, and leveraging Spring Boot Actuator endpoints for comprehensive application monitoring, covering security considerations, custom endpoint creation, and integration with external monitoring systems.

How Spring Boot Actuator Works

Spring Boot Actuator operates by auto-configuring a set of endpoints that expose operational information about your running application. When you include the actuator dependency, Spring Boot automatically registers these endpoints and makes them available through HTTP or JMX. The framework uses conditional configuration to determine which endpoints to enable based on your application’s dependencies and configuration properties.

Under the hood, Actuator uses Spring MVC or Spring WebFlux to create REST endpoints, depending on your application type. Each endpoint is implemented as a Spring component that collects and returns specific operational data. The health endpoint, for example, aggregates status information from various health indicators like database connections, disk space, and custom health checks.

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

Step-by-Step Implementation Guide

Setting up Spring Boot Actuator requires minimal configuration, but proper setup involves security considerations and endpoint customization. Here’s a comprehensive implementation approach:

Step 1: Add Dependencies and Basic Configuration

# application.properties
management.endpoints.web.exposure.include=health,info,metrics,prometheus
management.endpoints.web.base-path=/actuator
management.endpoint.health.show-details=when-authorized
management.endpoint.health.show-components=always
management.info.env.enabled=true

Step 2: Configure Security

@Configuration
@EnableWebSecurity
public class ActuatorSecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
            .requestMatcher(EndpointRequest.toAnyEndpoint())
            .authorizeHttpRequests(requests -> 
                requests.requestMatchers("/actuator/health/**").permitAll()
                       .requestMatchers("/actuator/info").permitAll()
                       .anyRequest().hasRole("ADMIN"))
            .httpBasic(withDefaults())
            .build();
    }
}

Step 3: Create Custom Health Indicators

@Component
public class DatabaseHealthIndicator implements HealthIndicator {
    
    @Autowired
    private DataSource dataSource;
    
    @Override
    public Health health() {
        try (Connection connection = dataSource.getConnection()) {
            if (connection.isValid(1)) {
                return Health.up()
                    .withDetail("database", "Available")
                    .withDetail("validationQuery", "SELECT 1")
                    .build();
            }
        } catch (Exception e) {
            return Health.down()
                .withDetail("database", "Unavailable")
                .withException(e)
                .build();
        }
        return Health.down().build();
    }
}

Step 4: Add Custom Metrics

@RestController
public class OrderController {
    
    private final MeterRegistry meterRegistry;
    private final Counter orderCounter;
    private final Timer orderProcessingTime;
    
    public OrderController(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
        this.orderCounter = Counter.builder("orders.created")
            .description("Number of orders created")
            .register(meterRegistry);
        this.orderProcessingTime = Timer.builder("orders.processing.time")
            .description("Order processing time")
            .register(meterRegistry);
    }
    
    @PostMapping("/orders")
    public ResponseEntity<Order> createOrder(@RequestBody Order order) {
        Timer.Sample sample = Timer.start(meterRegistry);
        try {
            // Process order
            Order savedOrder = orderService.save(order);
            orderCounter.increment();
            return ResponseEntity.ok(savedOrder);
        } finally {
            sample.stop(orderProcessingTime);
        }
    }
}

Key Actuator Endpoints and Their Uses

Endpoint Purpose Production Safe Common Use Case
/health Application health status Yes Load balancer health checks
/metrics Application metrics and counters With auth Performance monitoring
/info Application information Yes Version and build info
/env Environment properties No Configuration debugging
/loggers View and modify logging levels With auth Runtime log level adjustment
/threaddump Thread dump information No Performance troubleshooting
/heapdump Heap dump file No Memory leak analysis

Real-World Use Cases and Examples

Load Balancer Integration

Most production deployments integrate the health endpoint with load balancers for automatic failover. Here’s how to configure a detailed health check that considers multiple dependencies:

management.endpoint.health.group.readiness.include=db,redis,external-service
management.endpoint.health.group.readiness.show-details=always
management.endpoint.health.group.liveness.include=ping
management.endpoint.health.group.liveness.show-details=always

Prometheus Integration for Metrics Collection

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
# application.properties
management.endpoints.web.exposure.include=prometheus
management.metrics.export.prometheus.enabled=true
management.metrics.distribution.percentiles-histogram.http.server.requests=true

Custom Info Endpoint with Git Information

@Component
public class CustomInfoContributor implements InfoContributor {
    
    @Override
    public void contribute(Info.Builder builder) {
        builder.withDetail("app", Map.of(
            "name", "Order Management System",
            "version", "2.1.0",
            "environment", System.getProperty("spring.profiles.active", "default")
        ));
        
        builder.withDetail("features", List.of(
            "payment-processing",
            "inventory-management", 
            "customer-notifications"
        ));
    }
}

Graceful Shutdown Configuration

server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=30s
management.endpoint.shutdown.enabled=true

Comparison with Alternative Monitoring Solutions

Feature Spring Boot Actuator Micrometer + Prometheus New Relic Application Insights
Setup Complexity Very Low Medium Low Low
Cost Free Free (self-hosted) Paid Paid
Custom Metrics Yes Excellent Yes Yes
Alerting No With Alertmanager Built-in Built-in
Distributed Tracing Basic With Jaeger/Zipkin Yes Yes

Spring Boot Actuator excels as a foundation for application monitoring, especially when you need basic health checks and metrics without external dependencies. For comprehensive monitoring solutions, combining Actuator with Prometheus and Grafana provides enterprise-level capabilities while maintaining cost effectiveness, particularly for applications running on VPS or dedicated servers.

Best Practices and Common Pitfalls

Security Best Practices

  • Never expose sensitive endpoints like /env, /configprops, or /heapdump in production without authentication
  • Use different management ports for internal monitoring tools
  • Implement IP-based access control for actuator endpoints
  • Sanitize sensitive information in health details and info endpoints
# Separate management port configuration
management.server.port=8081
management.server.address=127.0.0.1

Performance Considerations

  • Health checks should complete within 1-2 seconds to avoid load balancer timeouts
  • Avoid expensive operations in health indicators – use cached results when possible
  • Configure appropriate timeouts for external service health checks
  • Monitor the performance impact of metrics collection, especially high-cardinality metrics
@Component
public class ExternalServiceHealthIndicator implements HealthIndicator {
    
    private final WebClient webClient;
    private volatile Health cachedHealth = Health.up().build();
    private volatile long lastCheck = 0;
    private static final long CACHE_DURATION = 30000; // 30 seconds
    
    @Override
    public Health health() {
        long now = System.currentTimeMillis();
        if (now - lastCheck > CACHE_DURATION) {
            updateHealthStatus();
            lastCheck = now;
        }
        return cachedHealth;
    }
    
    private void updateHealthStatus() {
        try {
            String response = webClient.get()
                .uri("/health")
                .retrieve()
                .bodyToMono(String.class)
                .timeout(Duration.ofSeconds(2))
                .block();
            cachedHealth = Health.up().withDetail("response", "OK").build();
        } catch (Exception e) {
            cachedHealth = Health.down().withException(e).build();
        }
    }
}

Common Configuration Mistakes

  • Exposing all endpoints by default using management.endpoints.web.exposure.include=*
  • Not configuring proper health check groups for Kubernetes readiness/liveness probes
  • Ignoring actuator endpoint performance in high-traffic applications
  • Failing to customize sensitive data exposure in production environments

Monitoring Integration Pattern

# Docker health check integration
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
  CMD curl -f http://localhost:8080/actuator/health || exit 1

Advanced Custom Endpoint Example

@Component
@Endpoint(id = "database-stats")
public class DatabaseStatsEndpoint {
    
    @Autowired
    private DataSource dataSource;
    
    @ReadOperation
    public Map<String, Object> databaseStats() {
        Map<String, Object> stats = new HashMap<>();
        
        try (Connection conn = dataSource.getConnection()) {
            DatabaseMetaData metaData = conn.getMetaData();
            stats.put("databaseProductName", metaData.getDatabaseProductName());
            stats.put("databaseProductVersion", metaData.getDatabaseProductVersion());
            stats.put("driverName", metaData.getDriverName());
            stats.put("maxConnections", metaData.getMaxConnections());
            
            // Add connection pool stats if using HikariCP
            if (dataSource instanceof HikariDataSource) {
                HikariDataSource hikari = (HikariDataSource) dataSource;
                HikariPoolMXBean pool = hikari.getHikariPoolMXBean();
                stats.put("activeConnections", pool.getActiveConnections());
                stats.put("idleConnections", pool.getIdleConnections());
                stats.put("totalConnections", pool.getTotalConnections());
                stats.put("threadsAwaitingConnection", pool.getThreadsAwaitingConnection());
            }
        } catch (SQLException e) {
            stats.put("error", e.getMessage());
        }
        
        return stats;
    }
}

Spring Boot Actuator transforms application monitoring from complex custom implementations to standardized, production-ready endpoints. The key to successful implementation lies in understanding the security implications, properly configuring health checks for your infrastructure, and leveraging custom metrics to gain insights specific to your application’s business logic. When deployed correctly on robust infrastructure, Actuator endpoints provide the foundation for comprehensive application observability that scales with your monitoring needs.

For additional configuration options and advanced features, consult the official Spring Boot Actuator documentation and the Micrometer metrics documentation for detailed metrics integration patterns.



This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.

This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.

Leave a reply

Your email address will not be published. Required fields are marked