Spring

Spring boot 커스텀 메트릭 (프로메테우스/그라파나)

쭈녁 2024. 5. 6. 00:00

엑츄에이터와 마이크로미터

스프링 엑츄에이터를 기반으로 한 많은 모니터링 툴들이 있다. 하지만 이러한 모니터링 툴들은 쌓인 엑츄에이터의 정보를 받아올 때 여러 다른 형식으로 데이터를 받아간다. 스프링은 이러한 환경에서 항상 추상화를 통하여 표준 방식을 개발자에게 제공한다. 

이 경우에도 마이크로미터를 통한 추상화를 통하여 여러 다른 형식의 포맷을 표준화하였다.  

 

마이크로미터와 액츄에이터가 제공하는 메트릭에는 매우 다양한 정보들이 있다. 전체적인 애플리케이션의 health 체크 및 시스템에 대한 메트릭은 기본적으로 제공해 준다.

 

하지만 애플리케이션의 비즈니스 로직에서의 의미 있는 지표를 뽑아내고 싶다면 어떻게 해야 할까??

 

사용자 메트릭일 정의하여 수집 설정을 할 수 있다.

 

카운트 예제 1)

- registry를 의존하여 로직 안에서 직접 생성

@Slf4j
@RequiredArgsConstructor
public class OrderServiceV1 implements OrderService {
    private final MeterRegistry registry;
    private AtomicInteger stock = new AtomicInteger(100);

    @Override
    public void order() {
        log.info("주문");
        stock.decrementAndGet();
        Counter.builder("my.order")
                .tag("class", this.getClass().getName())
                .tag("method", "order")
                .description("order")
                .register(registry).increment();
    }

    @Override
    public void cancel() {
        log.info("취소");
        stock.incrementAndGet();
        Counter.builder("my.order")
                .tag("class", this.getClass().getName())
                .tag("method", "cancel")
                .description("order")
                .register(registry).increment();
    }
}

 

카운터 예제 2)

- 위 방식은 서비스 로직과 다른 기능을 하는(모니터 로그를 수집하는) 기능이 로직안에 포함되어 있다.

- 마이크로미터의 AOP 방식으로 관점 분리할 수 있으며 Bean을 생성해주어야 한다.

@Configuration
public class OrderConfigV2 {
    @Bean
    public CountedAspect countedAspect(MeterRegistry registry) {
        return new CountedAspect(registry);
    }
    @Bean
    OrderService orderService() {
        return new OrderServiceV2();
    }
}

 

@Slf4j
@RequiredArgsConstructor
public class OrderServiceV2 implements OrderService {
    private AtomicInteger stock = new AtomicInteger(100);

    @Counted("my.order")
    @Override
    public void order() {
        log.info("주문");
        stock.decrementAndGet();
    }

    @Counted("my.order")
    @Override
    public void cancel() {
        log.info("취소");
        stock.incrementAndGet();
    }
}

 

타이머 예제 1)

- registry를 의존하여 로직 안에서 직접 생성

@RequiredArgsConstructor
public class OrderServiceV3 implements OrderService {
    private final MeterRegistry registry;
    private AtomicInteger stock = new AtomicInteger(100);

    @Override
    public void order() {
        Timer timer = Timer.builder("my.order")
                .tag("class", this.getClass().getName())
                .tag("method", "order")
                .description("order")
                .register(registry);
        timer.record(() -> {
            log.info("주문");
            stock.decrementAndGet();
            sleep(500);
        });
    }


    @Override
    public void cancel() {
        Timer timer = Timer.builder("my.order")
                .tag("class", this.getClass().getName())
                .tag("method", "cancel")
                .description("order")
                .register(registry);
        timer.record(() -> {
            log.info("취소");
            stock.incrementAndGet();
            sleep(200);
        });
    }
}

 

타이머 예제 2)

- 카운터 예제와 동일

@Configuration
public class OrderConfigV4 {
    @Bean
    public TimedAspect countedAspect(MeterRegistry registry) {
        return new TimedAspect(registry);
    }
    @Bean
    OrderService orderService() {
        return new OrderServiceV4();
    }
}

 

@Timed(value = "my.order")
@RequiredArgsConstructor
public class OrderServiceV4 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);
    }
}

 

참고로 진행 중인 프로젝트에 적용하였을 때 정의한 이름의 메트릭이 생성되지 않아 진짜 몇 시간 동안 삽질을 했는데 혹시나 해서 api를 하나 찍어보니 메트릭이 생성되었다. config와 어노테이션으로 정의만 하는 것이 아니라 실제 호출 되었을 때 DB에 정보가 쌓여야 메트릭 정보도 확인이 가능하였다.... 자다가도 생각날 만큼 왜 안될까 고민을 너무 많이 함 ㅋㅋㅋ

 

마지막으로... 

김영한 강사님의 로드맵을 끝 마치며 정말 항상 감사하고 마지막 말씀이 너무 감동이라 (T인데도 불구하고) 모든 로드맵 강의에 별점을 5점 박을수 밖에 없었다... 더욱 정진하겠습니다.

 

참고자료

김영한 : 스프링 부트 - 핵심 원리와 활용

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%ED%95%B5%EC%8B%AC%EC%9B%90%EB%A6%AC-%ED%99%9C%EC%9A%A9/dashboard

 

스프링 부트 - 핵심 원리와 활용 | 김영한 - 인프런

김영한 | 실무에 필요한 스프링 부트는 이 강의 하나로 모두 정리해드립니다., 백엔드 개발자를 위한 스프링 부트 끝판왕! 실무에 필요한 내용을 모두 담았습니다.  [임베딩 영상] 김영한의 스

www.inflearn.com

 

 

'Spring' 카테고리의 다른 글

@FeignClient 기본 세팅  (0) 2024.05.21
Spring 외부설정 2 (Profile 사용 예)  (0) 2024.05.06
배포 서버 ClassNotFoundException 트러블 슈팅  (0) 2024.05.02
커스텀 익셉션 적용  (0) 2024.04.28
Spring boot 의 외부 설정  (1) 2024.04.27