- 마이크로미터를 사용해서 메트릭을 직접 등록하는 방법을 알아보자
MeterRegistry
마이크로미터 기능을 제공하는 핵심 컴포넌트
스프링을 통해서 주입 받아서 사용하고, 이곳을 통해서 카운터, 게이지 등을 등록한다.
카운터
- 단조롭게 증가하는 단일 누적 측정항목
- 단일값
- 보통 하나씩 증가
- 누적이므로 전체 값을 포함(total)
- 프로메테우스에서는 일반적으로 카운터의 이름 마지막에 _tatal 을 붙여서 my_intersetstock_tatol 과 같이 표현함
- 값을 증가하거나 0으로 초기화 하는 것만 가능
- 예 ) HTTP 요청수
메트릭 등록 - 예제 만들기
관심 주식 등록 서비스에 카운터 메트릭을 적용해보자!

package com.flab.mars.domain.service;
import com.flab.mars.db.entity.InterestStockEntity;
import com.flab.mars.db.entity.PriceDataEntity;
import com.flab.mars.db.entity.StockInfoEntity;
import com.flab.mars.db.repository.InterestStockRepository;
import com.flab.mars.db.repository.PriceDataRepository;
import com.flab.mars.db.repository.StockInfoRepository;
import com.flab.mars.domain.StockCodeValidator;
import com.flab.mars.domain.vo.TokenInfoVO;
import com.flab.mars.domain.vo.response.InterestStockVO;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class InterestStockService {
private final MeterRegistry registry;
private final InterestStockRepository interestStockRepository;
private final StockInfoRepository stockInfoRepository;
private final PriceDataRepository priceDataRepository;
private final StockCodeValidator stockCodeValidator;
@Transactional
public Long registerInterestStock(Long userId, String stockCode, TokenInfoVO token) {
Counter.builder("my.interestStock")
.tag("class", this.getClass().getName())
.tag("method", "register")
.description("registerInterestStock")
.register(registry).increment();
// DB 에 해당 주식 코드 정보가 없는 경우
StockInfoEntity stockInfoEntity = stockInfoRepository.findByStockCode(stockCode)
.orElseGet(() -> {
String stockName = stockCodeValidator.validateAndGetStockName(stockCode, token);
// 해당 주식이 저장되어 있지 않은 경우 insert
return stockInfoRepository.save(new StockInfoEntity(stockCode, stockName));
});
// 중복 관심 종목 등록 방지
Optional<InterestStockEntity> existingInterestStock = interestStockRepository.findByMemberIdAndStockInfo(userId, stockInfoEntity);
if (existingInterestStock.isPresent()) {
return existingInterestStock.get().getId(); // 중복된 관심 종목 엔티티의 아이디를 리턴
}
InterestStockEntity interestStockEntity = InterestStockEntity.builder()
.memberId(userId)
.stockInfo(stockInfoEntity)
.build();
interestStockRepository.save(interestStockEntity);
return interestStockEntity.getId();
}
}
실행
http://localhost:8080/api/interest-stocks
(각각 실행해야 메트릭이 등록된다.)
액츄에이터 메트릭 확인


프로메테우스 포멧 메트릭 확인
localhost:8080/actuator/prometheus

- 카운터이기 때문에 마지막에 _total 이 붙은 것을 확인할 수 있다.
- 프로메테우스는 . -> _ 로 변경한다.
- 메트릭 이름이 my.interestStock -> my_ interestStock _total 로 변경된 것을 확인할 수 있다.
- method 라는 tag, 레이블을 기준으로 데이터가 분류되어 있다.
그라파나 등록 - 관심 주식 등록 수
등록 수 그래프를 추가해보자.
- Panel options
- Title: 관심 주식 등록 수
- PromQL
- increase(my_interestStock_total{method="register"}[1m])
참고 : 카운터는 계속 증가하기 때문에 특정 시간에 얼마나 증가했는지 확인하려면 increase() , rate() 같은 함수와 함께 사용하는 것이 좋다.

메트릭 등록 2 - Counted
코드 개선
InterestStockService 에 메트릭을 계산하는 코드가 추가되었다.
Service 는 비즈니스 로직만 남겨두는 것이 좋다. 어떻게 해결할 수 있을까?
스프링에서 자주 쓰이는 aop 를 활용해보자
직접 필요한 AOP 를 만들어서 적용하여도 좋지만 마이크로미터는 이런 상황에 맞추어 필요한 AOP 구성 요소를 이미 다 만들어 두었다.
@Counted("my.interestStock")
@Transactional
public Long registerInterestStock(Long userId, String stockCode, TokenInfoVO token) {
// DB 에 해당 주식 코드 정보가 없는 경우
StockInfoEntity stockInfoEntity = stockInfoRepository.findByStockCode(stockCode)
.orElseGet(() -> {
String stockName = stockCodeValidator.validateAndGetStockName(stockCode, token);
// 해당 주식이 저장되어 있지 않은 경우 insert
return stockInfoRepository.save(new StockInfoEntity(stockCode, stockName));
});
// 중복 관심 종목 등록 방지
Optional<InterestStockEntity> existingInterestStock = interestStockRepository.findByMemberIdAndStockInfo(userId, stockInfoEntity);
if (existingInterestStock.isPresent()) {
return existingInterestStock.get().getId(); // 중복된 관심 종목 엔티티의 아이디를 리턴
}
InterestStockEntity interestStockEntity = InterestStockEntity.builder()
.memberId(userId)
.stockInfo(stockInfoEntity)
.build();
interestStockRepository.save(interestStockEntity);
return interestStockEntity.getId();
}
` @Counted ` 애노테이션을 측정을 원하는 메서드에 적용한다. 관심 주식 등록에 적용했다.
그리고 메트릭 이름을 지정하면 된다. 여기서는 my.intereststock 를 적용했다.
참고로 이렇게 사용하면 tag 에 method 를 기준으로 분류해서 적용한다.
package com.flab.mars.config;
import io.micrometer.core.aop.CountedAspect;
import io.micrometer.core.instrument.MeterRegistry;
@Configuration
public class RootConfig {
@Bean
public CountedAspect countedAspect(MeterRegistry registry) {
return new CountedAspect(registry); // CountedAspect 를 등록하면 @Counted 를 인지해서 Counter 를 사용하는 AOP 를 적용한다.
}
}
- CountedAspect ` 를 등록하면 ` @Counted 를 인지해서 Counter 를 사용하는 AOP를 적용한다
- CountedAspect 를 빈으로 등록하지 않으면 @Counted 관련 AOP 가 동작하지 않는다.
액츄에이터 메트릭 확인
http://127.0.0.1:8080/actuator/metrics/my.intereststock

프로메테우스 포멧 메트릭 확인
http://127.0.0.1:8080/actuator/prometheus

그라파냐 대시보드 확인

메트릭 이름과 tag 가 기존과 같으므로 같은 대시보드에서 확인할 수 있다.
'Spring' 카테고리의 다른 글
Spring Boot 애플리케이션 실시간 모니터링: Actuator, Prometheus, Grafana 활용법 (0) | 2025.03.19 |
---|---|
Spring Boot + Redis 적용기 (0) | 2025.02.19 |
[스프링 핵심 원리 - 기본편] 의존관계 자동 주입 1 (1) | 2024.11.05 |
[스프링 핵심 원리 - 기본편] 컴포넌트 스캔 2 (0) | 2024.10.28 |
[스프링 핵심 원리 - 기본편] 컴포넌트 스캔 1 (1) | 2024.10.18 |