2) Spring Boot Actuator
2-1) Actuator 설정
의존성:
// build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'io.micrometer:micrometer-registry-prometheus' // Prometheus 메트릭
}
application.yml:
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus # 노출할 엔드포인트
endpoint:
health:
show-details: always # 헬스 체크 상세 정보
metrics:
tags:
application: myapp # 메트릭에 태그 추가
2-2) 헬스 체크 (Health Check)
기본 헬스 체크:
# http://localhost:8080/actuator/health
curl http://localhost:8080/actuator/health
# 응답:
{
"status": "UP",
"components": {
"db": {
"status": "UP",
"details": {
"database": "MySQL",
"validationQuery": "isValid()"
}
},
"diskSpace": {
"status": "UP",
"details": {
"total": 499963174912,
"free": 123456789012
}
},
"ping": {
"status": "UP"
}
}
}
커스텀 헬스 체크:
@Component
public class ExternalApiHealthIndicator implements HealthIndicator {
@Autowired
private RestTemplate restTemplate;
@Override
public Health health() {
try {
// 외부 API 호출
ResponseEntity<String> response = restTemplate.getForEntity(
"https://api.example.com/health",
String.class
);
if (response.getStatusCode().is2xxSuccessful()) {
return Health.up()
.withDetail("api", "External API is healthy")
.build();
} else {
return Health.down()
.withDetail("api", "External API returned " + response.getStatusCode())
.build();
}
} catch (Exception e) {
return Health.down()
.withDetail("error", e.getMessage())
.build();
}
}
}
2-3) 메트릭 (Metrics)
기본 메트릭 확인:
# 모든 메트릭 목록
curl http://localhost:8080/actuator/metrics
# 응답:
{
"names": [
"jvm.memory.used",
"jvm.gc.pause",
"http.server.requests",
"system.cpu.usage",
"hikaricp.connections.active"
]
}
# 특정 메트릭 상세
curl http://localhost:8080/actuator/metrics/http.server.requests
# 응답:
{
"name": "http.server.requests",
"measurements": [
{
"statistic": "COUNT",
"value": 1234
},
{
"statistic": "TOTAL_TIME",
"value": 12.5
},
{
"statistic": "MAX",
"value": 0.5
}
],
"availableTags": [
{
"tag": "uri",
"values": ["/api/users", "/api/orders"]
},
{
"tag": "method",
"values": ["GET", "POST"]
},
{
"tag": "status",
"values": ["200", "404", "500"]
}
]
}
커스텀 메트릭:
@Service
public class OrderService {
private final MeterRegistry meterRegistry;
private final Counter orderCounter;
private final Timer orderTimer;
public OrderService(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
// 카운터 (누적 횟수)
this.orderCounter = Counter.builder("orders.created")
.description("Number of orders created")
.tag("type", "online")
.register(meterRegistry);
// 타이머 (실행 시간)
this.orderTimer = Timer.builder("orders.processing.time")
.description("Time to process an order")
.register(meterRegistry);
}
public Order createOrder(CreateOrderRequest request) {
return orderTimer.record(() -> {
// 주문 처리
Order order = orderRepository.save(request.toEntity());
// 카운터 증가
orderCounter.increment();
// 게이지 (현재 값)
meterRegistry.gauge("orders.total.amount", order.getAmount());
return order;
});
}
}
메트릭 타입:
// 1. Counter (누적 증가)
Counter counter = Counter.builder("user.login.count")
.tag("status", "success")
.register(meterRegistry);
counter.increment();
// 2. Gauge (현재 값)
meterRegistry.gauge("connection.pool.size", connectionPool, ConnectionPool::getSize);
// 3. Timer (실행 시간)
Timer timer = Timer.builder("api.response.time")
.register(meterRegistry);
timer.record(() -> {
// 측정할 코드
});
// 4. Distribution Summary (분포)
DistributionSummary summary = DistributionSummary.builder("order.amount")
.register(meterRegistry);
summary.record(order.getAmount());
📚 다음 편: 준비 중입니다.
💬 댓글