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

15. 쓰레드로컬 적용

sdafdq 2024. 1. 14. 21:42
@Slf4j
public class ThreadLocalLogTrace implements LogTrace{
    private static final String START_PREFIX = "-->";
    private static final String COMPLETE_PREFIX = "<--";
    private static final String EX_PREFIX = "<X-";

//    private TraceId traceIdHolder;
    private ThreadLocal<TraceId> traceIdHolder = new ThreadLocal<>();

    @Override
    public TraceStatus begin(String message) {
        syncTraceId();
        TraceId traceId = traceIdHolder.get();
        Long startTimeMs = System.currentTimeMillis();
        log.info("[{}] {}{}", traceId.getId(), addSpace(START_PREFIX, traceId.getLevel()), message);

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

    private void syncTraceId(){
        TraceId traceId = traceIdHolder.get();
        if(traceId == null){
            traceIdHolder.set(new TraceId());
        }else{
            traceIdHolder.set(traceId.createNextId());
        }
    }

    @Override
    public void end(TraceStatus status) {
        complete(status, null);
    }

    @Override
    public void exception(TraceStatus status, Exception e) {
        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();
    }

    private void releaseTraceId() {
        TraceId traceId = traceIdHolder.get();
        if(traceId.isFirstLevel()){
            traceIdHolder.remove();
        }else{
            traceIdHolder.set(traceId.createPreviousId());
        }
    }

    private static String addSpace(String prefix, int level){
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i< level; i++){
            sb.append((i==level - 1) ? "|" + prefix : "|   ");
        }
        return sb.toString();
    }
}

비슷하다.

다른 점은, 그저 기본타입 대신 쓰레드로컬 객체로 바뀌었고, 그에 따라 쓰레드로컬을 이용하는 걸로 바뀌었다.

 

get(), set()은 쓰면 되고,

 

private void releaseTraceId() {
    TraceId traceId = traceIdHolder.get();
    if(traceId.isFirstLevel()){
        traceIdHolder.remove();
    }else{
        traceIdHolder.set(traceId.createPreviousId());
    }
}

여기 보면 remove()를 한다.

release, 즉 로그추적을 끝내는 로직에서, 만약 현재 TraceId의 레벨이 맨 처음이라면, 내부 로직을 다 돌고 마지막으로 온거니까, remove()해서 삭제를 해 준다.

 

보통은 쓰레드를 끝낼 때 알아서 처리를 해 줄줄 알았는데, 수동으로 해 줘야 하는 것 같다.

메모리 누수가 발생할 수 있다.

 

테스트 잘 된다.

'스프링 > 스프링 핵심 원리 - 고급편' 카테고리의 다른 글

17. 템플릿 메서드 패턴  (0) 2024.01.14
16. 쓰레드로컬 주의사항  (0) 2024.01.14
14. 쓰레드로컬 예시  (0) 2024.01.14
13. 쓰레드 로컬 소개  (0) 2024.01.14
12. 동시성 문제 예시  (0) 2024.01.14