✅ 목표
- 각 요청별 실행 흐름을 추적하고, 예외 발생 시 원인을 정확히 파악할 수 있도록 하는 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 |