[ 로그 추적기 구현 - 필드 사용 ]

public interface LogTrace {

    TraceStatus begin(String message);

    void end(TraceStatus status);

    void exception(TraceStatus status, Exception e);
}
@Slf4j
@Component
public class FieldLogTrace implements LogTrace{

    private static final String START_PREFIX = "-->";
    private static final String COMPLETE_PREFIX = "<--";
    private static final String EX_PREFIX = "<X-";

    private TraceId traceIdHolder; // 파라미터로 넘기는 게 아닌 필드로 보관한다.

    @Override
    public TraceStatus begin(String message) {
        syncTraceId();
        TraceId traceId = traceIdHolder; // syncTraceId()를 수행하고 난 후이기 때문에 값이 설정되어 있다.
        // 시간 정보
        Long startTimeMs = System.currentTimeMillis();
        // 로그 출력
        log.info("[{}] {}{}", traceId.getId(), addSpace(START_PREFIX, traceId.getLevel()), message);

        return new TraceStatus(traceId, startTimeMs, message);
    }

    private void syncTraceId(){
        if(traceIdHolder == null){ // 최초 호출 시 초기 값 생성
            traceIdHolder = new TraceId();
        }else { // 직전 로그가 있으면 해당 로그의 traceId를 참고해서 동기화하고, level도 하나 증가
            traceIdHolder = traceIdHolder.createNextId();
        }
    }
		
		@Override
    public void end(TraceStatus status){ // 로그를 정상 종료
		    // 파라미터로 시작 로그의 상태(TraceStatus)를 전달 받는다.
		    complete(status, null);
    }

		@Override
    public void exception(TraceStatus status, Exception e){ // 로그를 예외 상황으로 종료
        // 파라미터로 시작 로그의 상태(TraceStatus)와 발생 예외 정보를 전달 받는다.
        complete(status, e);
    }

    private void complete(TraceStatus status, Exception e) {
        Long stopTimeMs = System.currentTimeMillis(); // 종료 시간
        long resultTimeMs = stopTimeMs - status.getStartTimeMs(); //  걸린 총 시간 = 종료 시간 - 시작 시간
        TraceId traceId = status.getTraceId();

        if (e == null) { // 정상 종료
            log.info("[{}] {}{} time={}ms", traceId.getId(),
		            addSpace(COMPLETE_PREFIX, traceId.getLevel()), status.getMessage(), resultTimeMs);
        } else { // 예외 발생
            log.info("[{}] {}{} time={}ms ex={}", traceId.getId(),
		            addSpace(EX_PREFIX, traceId.getLevel()), status.getMessage(), resultTimeMs, e.toString());
        }
				releaseTraceId(); // 메서드를 호출이 끝나고 level을 하나 감소해주는 메서드
    }

		private void releaseTraceId() {
        if(traceIdHolder.isFirstLevel()){ // 만약 최초 호출 level == 0에 도달하면 내부에서 관리하는 traceId를 제거
            traceIdHolder = null; // destroy
        } else { // level == 0 아니라면 해당 로그의 traceId를 참고해서 동기화하고, level도 하나 감소시킨다.
            traceIdHolder = traceIdHolder.createPreviousId();
        }
    }

		private static String addSpace(String prefix, int level) {...}

[ 로그 추적기 적용 - 필드 사용 ]

Controller

@Controller
@RequiredArgsConstructor
public class OrderControllerV3 {

    private final OrderServiceV3 orderService;
    private final LogTrace trace; // 인터베이스(역할) 주입

    @GetMapping("/v3/request")
    @ResponseBody
    public String request(String itemId){

        TraceStatus status = null;
        try {
            status = trace.begin("OrderController.request()");
            orderService.orderItem(itemId);
            trace.end(status);
            return "ok";
        }catch (Exception e){
            trace.exception(status, e);
            throw e;
        }
    }
}

Service

@Service
@RequiredArgsConstructor
public class OrderServiceV3 {

    private final OrderRepositoryV3 orderRepository;
    private final LogTrace trace;

    public void orderItem(String itemId){

        TraceStatus status = null;
        try {
            status = trace.begin("OrderService.orderItem()");
            orderRepository.save(itemId);
            trace.end(status);
        }catch (Exception e){
            trace.exception(status, e);
            throw e;
        }
    }
}

Repository

@Repository
@RequiredArgsConstructor
public class OrderRepositoryV3 {

    private final LogTrace trace;

    public void save(String itemId){

        TraceStatus status = null;
        try {
            status = trace.begin("OrderRepository.save()");

            //저장 로직
            if(itemId.equals("ex")){ // 만약 상품의 아이디가 "ex" 이면 문제 발생
                throw new IllegalStateException("예외 발생!");
            }
            sleep(1000); // 상품 저장 소요 시간 지정(1초)
            trace.end(status);
        }catch (Exception e){
            trace.exception(status, e);
            throw e;
        }
    }

    private void sleep(int millis){
        try {
            Thread.sleep(millis);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

Untitled