728x90
파라미터로 동기화 개발
- 첫 로그에서 사용한 트랜잭션 ID 와 level을 다음 로그에 넘겨주면 된다
- 현재 로그의 상태 정보인 트랜잭션ID 와 level 은 TraceId에 포함되어 있다. 따라서 TraceId를 다음 로그에 넘겨주면 된다. 이 기능을 추가한 HelloTraceV2를 개발해보자.
HelloTraceV2
@Slf4j
@Component
public class HelloTraceV2 {
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);
}
//V2 추가
public TraceStatus beginSync(TraceId beforeTraceId, String message){
//TraceId traceId = new TraceId();
TraceId nextId = beforeTraceId.createNextId();
long startTimeMs = System.currentTimeMillis();
//로그 출력
log.info("[{}] {}{}", nextId.getId(), addSpace(START_PREFIX, nextId.getLevel()), message);
return new TraceStatus(nextId, 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.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());
}
}
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();
}
}
- 추가된 메소드 : beginSync()
- 파라미터로 기존 생성된 TraceStatus를 매개변수로 받는다.
- 기존 TraceId 에서 createNextId()를 통해 다음 ID를 구한다.
- 트랜잭션 ID는 기존과 같이 유지
- 깊이를 표현하는 Level은 하나 증가( 0 -> 1)
Test
class HelloTraceV2Test {
@Test
public void beginEnd(){
HelloTraceV2 trace = new HelloTraceV2();
TraceStatus status1 = trace.begin("hello");
TraceStatus status2 = trace.beginSync(status1.getTraceId(), "hello2");
trace.end(status2);
trace.end(status1);
}
@Test
public void beginException(){
HelloTraceV2 trace = new HelloTraceV2();
TraceStatus status1 = trace.begin("hello1");
TraceStatus status2 = trace.beginSync(status1.getTraceId(), "hello2");
trace.exception(status2, new IllegalStateException());
trace.exception(status1, new IllegalStateException());
}
}
적용
controller
@RestController
@RequiredArgsConstructor
public class OrderControllerV2 {
private final OrderServiceV2 orderService;
private final HelloTraceV2 trace;
@GetMapping("/v2/request")
public String request(String itemId){
TraceStatus status = null;
try{
status = trace.begin("OrderController.request()");
orderService.orderItem(status.getTraceId(), itemId);
trace.end(status);
return "ok";
}catch (Exception e){
trace.exception(status, e);
throw e; //예외를 꼭 다시 던져주어야 한다.
}
}
}
service
@Service
@RequiredArgsConstructor
public class OrderServiceV2 {
private final OrderRepositoryV2 orderRepository;
private final HelloTraceV2 trace;
public void orderItem(TraceId traceId, String itemId){
TraceStatus status = null;
try{
status = trace.beginSync(traceId,"OrderController.request()");
orderRepository.save(status.getTraceId() ,itemId);
trace.end(status);
}catch (Exception e){
trace.exception(status, e);
throw e; //예외를 꼭 다시 던져주어야 한다.
}
}
}
repository
@Repository
@RequiredArgsConstructor
public class OrderRepositoryV2 {
private final HelloTraceV2 trace;
public void save(TraceId traceId, String itemId){
TraceStatus status = null;
try{
status = trace.beginSync(traceId, "OrderController.request()");
//저장 로직
if(itemId.equals("ex")){ //예외 발생
throw new IllegalStateException("예외 발생");
}
sleep(1000);
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();
}
}
}
- 현재 로그의 상태 정보인 트랜잭션ID 와 level이 다음으로 전달되어야 한다.
- traceId를 컨트롤러에서 서비스를 호출할 때, 서비스에서 레포지토리로 넘겨주면 된다.

남은 문제
- HTTP 요청을 구분하고 깊이를 표현하기 위해서 TraceId 동기화가 필요하다.
- TraceId 의 동기화를 위해서 관련 메서드의 모든 파라미터를 수정해야 한다.
- 만약 인터페이스가 있다면 인터페이스까지 모두 고쳐야 하는 상황이다.
- 로그를 처음 시작할 때는 begin() 을 호출하고, 처음이 아닐때는 beginSync() 를 호출해야 한다.
- 만약에 컨트롤러를 통해서 서비스를 호출하는 것이 아니라, 다른 곳에서 서비스를 처음으로 호출하는 상황이라면 파리미터로 넘길 TraceId 가 없다.
- HTTP 요청을 구분하고 깊이를 표현하기 위해서 TraceId 를 파라미터로 넘기는 것 말고 다른 대안은 없을까??
728x90
'스프링 핵심 원리(고급편)' 카테고리의 다른 글
| Ch02. 쓰레드 로컬(ThreadLocal) - 동시성 문제(예제 코드) (0) | 2022.04.08 |
|---|---|
| Ch02. 쓰레드 로컬(ThreadLocal) - 필드 동기화 (0) | 2022.04.08 |
| Ch01. 예제 만들기 - 로그 추적기(2) (0) | 2022.04.07 |
| Ch01. 예제 만들기 - 로그 추적기(1) (0) | 2022.04.07 |
| Ch01. 예제 만들기 - 프로젝트 생성 (0) | 2022.04.07 |