JAVA

[Java] ThreadLocal, 템플릿메서드 디자인패턴을 사용한 LogTrace 개발

sian han 2025. 4. 2. 19:01

 

✅ 목표

  • 각 요청별 실행 흐름을 추적하고, 예외 발생 시 원인을 정확히 파악할 수 있도록 하는 LogTrace 기능을 개발한다.
  • 로깅은 애플리케이션의 전반적인 로직에 포함되어야 하는 부가기능이다. 최대한 중복되는 코드를 작성하지 않도록 개발한다. 
  • 멀티스레드 환경에서 동시성 문제를 방지하고, 요청별 독립적인 로깅이 가능하도록 한다.

 

LogTrace

  • 로그 추적 로직이 실행될 때 사용해야하는 메서드를 정의해놓은 인터페이스
public interface LogTrace {
    TraceStatus begin(String message);
    void end(TraceStatus status);
    void exception(TraceStatus status, Exception e);
}

 

  • begin(): 실행 시작 시 로그를 남기고, 상태 정보를 반환함
  • end(): 실행이 정상 종료될 때 로그를 남김
  • exception(): 예외가 발생했을 때 예외 정보를 포함한 로그를 남김

 

ThreadLocalLogTrace

  • LogTrace 인터페이스를 구현한 로그 추적 기능 클래스
  • 로그 출력 시 트랜잭션의 깊이(level)를 이용하여 계층 구조를 시각적으로 표현할 수 있도록 메서드 구현
  • ThreadLocal 을 활용하여 멀티쓰레드 환경에서 동시성 문제 방지
public class ThreadLocalLogTrace implements LogTrace {
    private final ThreadLocal<TraceStatus> traceHolder = new ThreadLocal<>();

    @Override
    public TraceStatus begin(String message) {
        TraceStatus status = new TraceStatus(message);
        traceHolder.set(status);
        System.out.println("[BEGIN] " + message);
        return status;
    }

    @Override
    public void end(TraceStatus status) {
        System.out.println("[END] " + status.getMessage());
        traceHolder.remove();
    }

    @Override
    public void exception(TraceStatus status, Exception e) {
        System.out.println("[EXCEPTION] " + status.getMessage() + " - " + e.getMessage());
        traceHolder.remove();
    }
}

 

  • ThreadLocal<TraceStatus>를 사용하여 각 요청의 로깅 상태를 독립적으로 관리할 수 있음
  • ⭐️⭐️⭐️ 요청이 끝나면 remove()를 호출 ⭐️⭐️⭐️

 

LogTraceConfig

  • ThreadLocalLogTrace 빈 등록
  • config 파일을 만들어 LogTrace의 구현체인 ThreadLocalLogTrace 객체를 Spring 빈으로 등록함 -> 서비스 / 컨트롤러 레이어에서 LogTrace 를 사용하기 위해 의존성을 주입받아야 함. (Bean 으로 등록되면 스프링 컨테이너가 의존성을 주입해줌)
@Configuration
public class LogTraceConfig {

    @Bean
    public LogTrace logTrace() {
        return new ThreadLocalLogTrace();
    }
}

 

 

AbstractLogTraceTemplate

  • 템플릿 메서드 패턴을 활용한 AbstractLogTraceTemplate
  • LogTrace 를 적용할 때 공통되는 부분을 빼내어서 템플릿에 넣음 -> LogTrace 사용 시 코드 중복이 없어짐
  • execute() 메서드 내부에서 call() 을 호출한다.
  • call() 은 추상 메서드, call() 메서드는 자식 클래스에서 구현해야 하며, 비즈니스 로직에 집중할 수 있도록 분리됨
public abstract class AbstractLogTraceTemplate<T> { 

    private final LogTrace trace;

    public AbstractLogTraceTemplate(LogTrace trace) {
        this.trace = trace;
    }

    public T execute(String message) {

        TraceStatus status = null;
        try {
            status = trace.begin(message);
            //로직 호출
            T result = call();

            trace.end(status);
            return result;
        } catch (IOException e) {
            trace.exception(status, e);  // 예외를 로그로 기록
            throw new RuntimeException("IOException occurred during execution", e); // 런타임 예외로 감싸서 던짐
        } catch (Exception e) {
            trace.exception(status, e);
            throw e;
        }
    }

    protected abstract T call() throws IOException;
}

 

 

Controller

  • controller 에서 템플릿을 적용하여 로깅 기능 호출
@PostMapping
public ResponseEntity<InvitationMaster> createInvitation(
        @Valid @RequestBody InvitationMasterRequestDto dto) {

    AbstractLogTraceTemplate<ResponseEntity<InvitationMaster>> template = new AbstractLogTraceTemplate<>(trace) {
        @Override
        protected ResponseEntity<InvitationMaster> call() {
            InvitationMaster savedInvitation = invitationService.saveInvitationMaster(dto);
            return ResponseEntity.ok(savedInvitation);
        }
    };
    return template.execute("InvitationController.createInvitation()");
}
  • 컨트롤러에서는 로깅과 관계없이 핵심 로직만 작성하면 됨
  • AbstractLogTraceTemplate 를 활용하여 execute() 메서드를 실행함 -> 로그 추적을 시작
  • 오버라이딩 된 call() 메서드가 실행되며 실제 비즈니스 로직을 수행한다.
  • execute()를 호출하면 자동으로 로그가 기록되고, 예외가 발생하면 로그를 남긴 후 다시 던짐

 

LogTrace 예시

[71701732] OrderController.createInvitation()
[71701732] |-->InvitationService.saveInvitationMaster
[71701732] |<--InvitationService.saveInvitationMaster time=34ms
[71701732] OrderController.createInvitation() time=40ms

 

 

 

📍 프로젝트에 실제 적용한 Github Issue

https://github.com/HAN-SEOHYUN/wedlessInvite/issues/121

 

[task] 템플릿메서드 디자인패턴을 사용한 LogTrace 개발 · Issue #121 · HAN-SEOHYUN/wedlessInvite

로그 추적(LogTrace) 로그는 비즈니스 로직과 직접적인 관계가 없는 공통기능 같은 로직을 여러곳에서 사용할 수 있으므로, 객체를 매번 생성할 필요 없이 한 개만 공유하면 됨 로깅 기능은 여러

github.com

 

https://github.com/HAN-SEOHYUN/wedlessInvite/issues/123

 

[task] LogTrace의 로그 파일 생성 작업 · Issue #123 · HAN-SEOHYUN/wedlessInvite

관련 이슈 #121 AS-IS 각 서비스 메서드 별로 실행 시간을 추적하는 LogTrace를 개발했다. LogTrace 클래스는 서비스 메서드 별로 실행 시간을 추적하고 있지만, 로그가 파일로 저장되지 않고 휘발성 로

github.com

 

'JAVA' 카테고리의 다른 글

[JAVA] 이것이 자바다 !_6장 클래스  (0) 2023.11.29
[JAVA] 네트워킹 NetWorking  (0) 2022.04.17
[JAVA] Thread 스레드  (0) 2022.04.14
[JAVA] IO  (0) 2022.04.13
[JAVA] GUI / 계속 추가예정  (0) 2022.04.11