Back-End/Spring Advance & Boot

스프링 고급편 - 로그 추적기

Meluu_ 2024. 8. 16. 16:36

✔️ 로그 추적


 

[TraceId] 깊이 ->(방향) (호출 메서드) (종료시간)  (예외)

 

로그를 남김으로써 어떤 부분에서 문제가 있는지, 예외가 발생하는지 빠르게 확인이 가능하다.

 

TraceIdTraceStatus 클래스를 만들어서 

트랜잭션 ID(DB 트랜잭션X) 와 level(깊이), 메세지를 로그로 남긴다.

 

public class TraceId {

    private String id;
    private int level;

    public TraceId() {
        this.id = createId();
        this.level = 0;
    }

    private TraceId(String id, int level) {
        this.id = id;
        this.level = level;
    }

//    ab99e16f-3cde-4d24-8241-256108c203a2 //생성된 UUID
//    ab99e16f //앞 8자리만 사용
    private String createId() {
        return UUID.randomUUID().toString().substring(0, 8);
    }
    
    public TraceId createNextId() {
        return new TraceId(id, level + 1);
    }

}

 

TraceId, 시작 시간, 메세지를 갖는다. 

메세지에는 로그를 남길 메서드 명 등등이다.

public class TraceStatus {

    private TraceId traceId;
    private Long startTimeMs;
    private String message;
    
}

 

 

@Slf4j
@Component
public class HelloTraceV2 {
	
    // 처음 시작 
    public TraceStatus begin(String message);
    // 동기화 
    public TraceStatus beginSync(TraceId beforeTraceId, String message);
    
    // 로그 정상 종료
    public void end(TraceStatus status);
    
    // 로그를 예외상황으로 종료
    public void exception(TraceStatus status, Exception e);
    
    // 실질적인 로그 종료 처리 
    private void complete(TraceStatus status, Exception e);

 

처음 begin은 TraceId를 만들고 시작 시간을 만든 후 로그를 남긴다.

그리고 TraceStatus를 새로 생성하여 반환한다. 이때 traceId, 시작 시간, 메세지를 생성자 파라미터에 넣는다. 

public TraceStatus begin(String message);

 

파라미터로 TraceId를 넘겨서 동기화한다.

// TraceId 동기화
public TraceStatus beginSync(TraceId beforeTraceId, String message);

 

종료 시간을 측정한 다음 status의 시작시간을 빼서 총 걸린 시간을 얻고

traceId, 로그정보, 걸린 시간을 로그에 출력한다. 

private void complete(TraceStatus status, Exception e);

 

 

사용

 

@RequiredArgsConstructor
public class OrderControllerV2 {

    private final OrderServiceV2 orderService;
    private final HelloTraceV2 trace;

    @GetMapping("/v2/request")
    public String request(String itemId) {

        // 초기화 과정에서 예외가 발생할 수 있기 때문에 여기서 선언
        TraceStatus status = null;

        try {
            status = trace.begin("OrderController.request()");
            orderService.orderItem(status.getTraceId(), itemId);
            trace.end(status);
            return "ok";
        } catch (Exception e) {
            trace.exception(status, e);
            throw e; // 예외를 꼭 다시 던져주어야 한다.
        }
    }
}

 

@Service
@RequiredArgsConstructor
public class OrderServiceV2 {

    private final OrderRepositoryV2 orderRepository;
    private final HelloTraceV2 trace;

    public void orderItem(TraceId traceId, String itemId) {

        TraceStatus status = null;

        try {
            status = trace.beginSync(traceId, "OrderService.orderItem()");
            orderRepository.save(status.getTraceId(), itemId);
            trace.end(status);

        } catch (Exception e) {
            trace.exception(status, e);
            throw e; // 예외를 꼭 다시 던져주어야 한다.
        }

    }
}

 

이런식으로 로그를 남기고 TraceId를 파라미터로 넘겨 동기화한다. 

 

 

문제점

  1. 사용하기에는 복잡하다.
  2. 파라미터를 통한 TraceId 동기화로 인한 메서드 수정
  3. 로그 사용 구분
    1. 로그 처음 시작 begin(), 이후 beginSync() 호출
    2. 다른 곳에서 service를 처음 호출하는 상황 발생시 넘길 TraceId가 없음(beginSync()를 호출하는 Service)

 

 

 

 

 

🔖 학습내용 출처


스프링 핵심 원리 - 고급편 / 김영한