들어가기 전: 이 글은 [인프런] 에서 '김영한님의 스프링 부트 - 핵심 원리와 활용'을 수강하고 일부를 요약한 글입니다.

개발자가 애플리케이션에서 지표를 수집해서 이를 마이크로미터가 제공한느 표준방법에 따라 등록하면 actuator 에서 확인할 수 있다.
@AutoConfiguration 을 통해 자동으로 등록해준다.{
"names": [
"application.ready.time",
"application.started.time",
"disk.free",
"disk.total",
"hikaricp.connections",
"hikaricp.connections.acquire",
"hikaricp.connections.active",
"hikaricp.connections.idle",
"hikaricp.connections.max",
"hikaricp.connections.usage",
"http.server.requests",
"http.server.requests.active",
"jdbc.connections.active",
"jdbc.connections.idle",
"jdbc.connections.max",
"jdbc.connections.min",
"jvm.buffer.count",
"jvm.buffer.memory.used",
"jvm.memory.used",
"jvm.memory.max",
"logback.events",
...
]
}
{
"name": "jvm.memory.used",
"description": "The amount of used memory",
"baseUnit": "bytes",
"measurements": [
{
"statistic": "VALUE",
"value": 131172848
}
],
"availableTags": [
{
"tag": "area",
"values": [
"heap",
"nonheap"
]
},
{
"tag": "id",
"values": [
"G1 Survivor Space",
"Compressed Class Space",
"Metaspace",
"CodeCache",
"G1 Old Gen",
"G1 Eden Space"
]
}
]
}
위에서 다음과 같은 항목을 찾아볼 수 있다.
tag:area, values[heap, nonheap]tag:id, values[G1 Survivor Space, ...]그렇다면 이번에는 metrics 내부에 Tag 정보를 필터링해서 정보를 확인해보자.
tag=KEY:VALUEtag 를 사용하면 힙 메모리, 힙이 아닌 메모리로 분류해서 데이터를 확인할 수 있다.
마이크로미터와 actuator 가 기본적으로 제공하는 다양한 메트릭을 확인해보자.
JVM 관련 메트릭을 제공한다. jvm. 으로 시작한다.
시스템 메트릭을 제공한다. system., process., disk. 으로 시작한다.
애플리케이션 시작 시간 메트릭을 제공한다.
스프링은 내부에 여러 초기화 단계가 있고 각 단계별로 내부에서 애플리케이션 이벤트를 발행한다.
스프링 MVC 컨트롤러가 처리하는 모든 요청을 다룬다.
메트릭 이름: http.server.requests
TAG 를 사용해서 다음 정보를 분류해서 확인할 수 있다.
커넥션 풀에 관한 메트릭을 확인할 수 있따.
jdbc.connections 으로 시작한다.logback.events: logback 로그에 대한 메트릭을 확인할 수 있다.
마이크로미터를 사용하면, 애플리케이션 내에서 비즈니스 모니터링에 알맞은 메트릭을 자유롭게 추가하고 actuator/metrics 에서 확인할 수 있다.
사용자가 정의할 수 있는 메트릭은 3가지로 다음과 같다.
@Counted 를 사용하면 result, exception, method, class 같은 다양한 tag 를 자동으로 적용한다.
@Counted 애노테이션 측정을 원하는 메서드에 적용한다.tag 에 method 를 기준으로 분류해서 적용한다.@slf4j
public class OrderService implements OrderService {
private AtomicInteger stock = new AtomicInteger(100);
@Override
@Counted("my.order")
public void order() {
log.info("주문");
stock.decrementAndGet();
}
@Override
@Counted("my.order")
public void cancel() {
log.info("취소");
stock.incrementAndGet();
}
@Override
public AtomicInteger getStock() {
return stock;
}
}
@Configuration
public class OrderConfig {
@Bean
public CountedAspect countedAspect(MeterRegistry registry) {
return new CountedAspect(registry);
}
}
시간을 측정하는데 사용된다. 카운터와 유사한데, Timer 를 사용하면 실행시간도 함께 측정할 수 있다. 따라서, 다음과 같은 내용을 한 반에 측정할 수 있다.
seconds_count: 누적 실행 수
seconds_sum: 실행시간의 합
seconds_max: 최대 실행 시간(가장 오래 걸린 실행시간 (가장 오래걸린 실행시간). 내부에 타임윈도우라는 개념이 있어서 1 ~ 3분마다 최대 실행시간이 다시 계산된다.
애노테이션 적용
class 에 적용하면 해당 타임의 모든 public 메서드 타이머가 적용된다.
@Slf4j
@Timed("my.order")
public class OrderService implements OrderService {
private AtomicInteger stock = new AtomicInteger(100);
@Override
public void order() {
log.info("주문");
stock.decrementAndGet();
sleep(500);
}
@Override
public void cancel() {
log.info("취소");
stock.incrementAndGet();
sleep(200);
}
private static void sleep(int l) {
try {
Thread.sleep(l + new Random().nextInt(200));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
@Override
public AtomicInteger getStock() {
return stock;
}
}
@Configuration
public class ORderConfig {
@Bean
public TimedAspect timedAspect(MeterRegistry registry) {
return new TimedAspect(registry);
}
}
참고: 카운터와 게이지를 구분할 때는 값이 감소할 수 있는가를 고민해보면 도움이 된다.
@Configuration
public class StockConfig {
@Bean
public MyStockMetric myStockMetric(
OrderServcie orderService,
MeterRegistry registry
) {
return new MyStockMetric(orderService.getStock(), registry);
}
@Slf4j
static class MyStockMetric {
private OrderService orderService;
private MeterREgistry registry;
public MyStockMetric(
OrderService orderService,
MeterRegistry registry
) {
this.orderService = orderService;
this.registry = registry;
}
@PostContstruct
public void init() {
Gauge.builder("my.stock", orderService, serivce -> {
log.info("stock gauge call");
return service.getStock().get();
}).registry(registry);
}
}
}
@Slf4j
@Configuration
public class StockConfig {
@Bean
public MeterBuilder stockSize(OrderService orderService) {
return registry -> Gauge.bulider("my.stock", orderService, service -> {
log.info("stock gauge call");
return service.getStock().get();
}).registry(registry):
}
}