모든 PUBLIC 메서드의 호출과 응답 정보를 로그로 출력
애플리케이션의 흐름을 변경하면 안됨 (로그를 남긴다고해서 비즈니스 로직의 동작에 영향을 주면 안됌)
메서드 호출에서 걸린 시간
정상흐름과 예외 흐름 구분 (예외발생시 예외정보가 남아야함)
메서드 호출의 깊이 표현
HTTP 요청을 구분
-TraceID 클래스
로그추적기는 트랜잭션 ID와 같이 표현하는 방법이 필요하다.
여기서는 트랜잭션ID와 깊이를 표현하는 level을 묶어서 TraceId 라는 개념을 만들었다.
TraceId는 단순히 id(트랜잭션ID)와 level 정보를 함께 가지고 있다.
study.advanced.trace 패키지생성
package study.advanced.trace;
import java.util.UUID;
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;
}
private String createId() {
return UUID.randomUUID().toString().substring(0,8);
}
public TraceId createNextId() {
return new TraceId(id, level +1);
}
public TraceId createPreviousId() {
return new TraceId(id, level -1);
}
public boolean isFirstLevel() {
return level == 0;
}
public String getId() {
return id;
}
public int getLevel() {
return level;
}
}
TraceStatus는 로그를 시작할 때의 상태정보를 가지고 있다.
상태정보는 로그를 종료할 때 사용된다.
OrderController.request() //로그시작
OrderController.request() time=1016ms //로그 종료
package study.advanced.trace;
// 로그의 상태정보
public class TraceStatus {
private TraceId traceId;
private Long startTimeMs;
private String message;
public TraceStatus(TraceId traceId, Long startTimeMs, String message) {
this.traceId = traceId;
this.startTimeMs = startTimeMs;
this.message = message;
}
public TraceId getTraceId() {
return traceId;
}
public Long getStartITmeMs() {
return startTimeMs;
}
public String getMessage() {
return message;
}
}
TraceId, TraceStatus를 사용해서 실제 로그를 생성하고, 처리하는 기능을 개발.
package study.advanced.trace.hellotrace;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
import study.advanced.trace.TraceId;
import study.advanced.trace.TraceStatus;
@Slf4j
@Component
public class HelloTraceV1 {
private static final String START_PREFIX = "-->";
private static final String COMPLETE_PREFIX ="<--";
private static final String EX_PREFIX = "<X-";
public TraceStatus begin(String message) {
TraceId traceId = new TraceId();
Long startTimeMs = System.currentTimeMillis();
log.info("[{}] {} {} ", traceId.getId(), addSpace(START_PREFIX, traceId.getLevel()), message);
//로그출력
return new TraceStatus(traceId, startTimeMs, message);
};
public void end(TraceStatus status) {
complete(status, null);
}
//예외로 처리될때
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.getStartITmeMs();
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());
}
}
private Object 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();
}
}
HelloTrace1
을 사용해서 실제 로그를 시작하고 종료할 수 있다. 그리고 로그를 출력하고 실행시간도 측정할 수 있다.
@Component:
싱글톤으로 사용하기 위해 스프링 빈으로 등록한다. 컴포넌트 스캔의 대상이 된다.
공개 메서드
로그추적기에서 사용되는 공개 메서드는 다음 3가지.
begin(..)
end(..)
exception(..)
하나씩 자세히 알아보기
package study.advanced.trace.hellotrace;
import org.junit.jupiter.api.Test;
import study.advanced.trace.TraceStatus;
public class HelloTraceV1Test {
@Test
void begin_end() {
HelloTraceV1 trace = new HelloTraceV1();
TraceStatus status = trace.begin("hello");
trace.end(status); //trace 종료
}
}