You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
참고: 이것은 온전한 테스트 코드가 아니다. 일반적으로 테스트라고 하면 자동으로 검증하는 과정이 필요하다. 이 테스트는 검증하는 과정이 없고 결과를 콘솔로 직접 확인해야 한다. 이렇게 응답값이 없는 경우를 자동으로 검증하려면 여러가지 테스트 기법이 필요하다. 이번 강의에서는 예제를 최대한 단순화하기 위해 검증 테스트를 생략했다.
packagehello.advanced.v1;
importhello.advanced.trace.TraceStatus;
importhello.advanced.trace.hellotrace.HelloTraceV1;
importlombok.RequiredArgsConstructor;
importorg.springframework.web.bind.annotation.GetMapping;
importorg.springframework.web.bind.annotation.RestController;
@RestController// @Controller + @ResponseBody@RequiredArgsConstructorpublicclassOrderControllerV1 {
privatefinalOrderServiceV1orderService;
privatefinalHelloTraceV1trace;
@GetMapping("/v1/request")
publicStringrequest(StringitemId) {
TraceStatusstatus = null;
try {
status = trace.begin("OrderController.request()");
orderService.orderItem(itemId);
trace.end(status); //예외터졌을때 이 부분 안나옴return"OK"; // @RestController 이기 때문에 문자
} catch (Exceptione) {
trace.exception(status, e); // 예외를 가지고 있음throwe;//예외를 다시 던져줘야 한다.
}
}
}
HelloTraceV1 trace : HelloTraceV1을 주입받는다. 참고로 HelloTraceV1 은 @Component 애노테이션을 가지고 있기 때문에 컴포넌트 스캔의 대상이 된다. 따라서 자동으로 스프링 빈으로 등록된다.
trace.begin("OrderController.request()") : 로그를 시작할 때 메세지 이름으로 컨트롤러 이름 + 메서드 이름을 주었다. 이렇게 하면 어떤 컨트롤러와 메서드가 호출되었는지 로그로 편리하게 확인할 수 있다. 물론 수작업이다.
단순하게 trace.begin() , trace.end() 코드 두줄만 적용하면 될 줄 알았지만 실상은 그렇지 않다. trace.exception() 으로 예외까지ㅏ 처리해야 하므로 지저분한 try, catch 코드가 추가된다.
begin()의 결과값으로 받은 TraceStatus status 값을 end() , exception() 에 넘겨야 한다. 결국 try~ catch 블록 모두에 이 값을 넘겨야한다. 따라서 try 상위에 TraceStatus status 코드를 선언해야 한다. 만약 try, 안에서 TraceSatus staus 를 선언하면 try 블록안에서만 해당 변수가 유효기 때문에 catch 블록에 넘길 수 없다.
따라서 컴파일 오류가 발생한다.
throw e : 예외를 꼭 다시 던져주어야 한다. 그렇지 않으면 여기서 예외를 막아버리고 이후에 정상흐름으로 동작한다. 로그는 애플리케이션의 흐름에 영향을 주면 안된다. 로그 때문에 예외가 사라지면 안된다.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
로그추적기 V1 - 프로토타입개발
애플리케이션의 모든 로직에 로그를 남겨도 됨
그것보다는 효율적인 방법이 필요함
깊이를 표현하는 방법을 기존 정보에 이어 받아야하기 때문에 단순히 로그만 남긴다고 해결할 수 있는건 아니다.
요구사항에 맞추어 애플리케이션에 효과적으로 로그를 남기기 위한 로그 추적기를 개발해보자
먼저 로그 추적기를 위한 기반 데이터를 가지고 있는 ‘TraceId’, ‘TraceStatus’ 클래스를 만들어 보자.
TraceId 클래스
로그 추적기는 트랜잭션 ID와 깊이를 표현하는 방법이 필요하다
여기서는 트랜잭션ID 와 깊이를 표현하는 level을 묶어서 ‘TraceId’라는 개념을 만들었다.
‘TraceId’는 단순히 ‘id’(트랜잭션ID)와 ‘level’정보를 함께 가지고 있다.

1. createNextId()
다음 TranceId 를 만든다. 예제 로그를 보면 깊이가 증가해도 트랜잭션 ID 는 같다. 대신에 깊이가 하나 증가한다
new TraceId(jd, level + 1)따라서
createNextId()를 사용해서 현재TraceId를 기반으로 다음TraceId를 만들면 ‘id’는 기존과 같고 ‘level은 하나 증가한다.2. createPreviousId()
createNextId()의 반대 역할을 한다.id는 기존과 같고,level은 하나 감소한다.3. isFirstLevel()
첫번째 레벨 여부를 편리하게 확인할 수 있는 메서드
TraceStatus클래스
로그의 상태 정보를 만드는것
로그를 시작하면 끝이 있어야 한다.

TraceStatus는 로그를 시작할때의 상태 정보를 가지고 있다. 이 상태 정보는 로그를 종료할때 사용된다.traceId: 내부에 트랜잭션 ID와 level을 가지고 있다.startTimeMs: 로그 시작 시간이다. 로그 종료시 이 시작 시간을 기준으로 시작~종료까지 전체 수행 구간을 구할 수 있다.message: 시작시 사용한 메세지이다. 이후 로그 종료시에도 이 메세지를 사용해서 출력한다.TraceId,TraceStatus를 사용해서 실제 로그를 생성하고 처리하는 기능을 개발해보자.HelloTraceV1을 사용해서 실제 로그를 시작하고 종료할 수 있다. 그리고 로그를 출력하고 실행시간도 측정할 수 있다.@Component: 싱글톤으로 사용하기 위해 스프링 빈으로 등록한다. 컴포넌트 스캔의 대상이 된다.공개 메서드
로그 추적기에서 사용되는 공개 메서드는 다음 3가지이다.
하나씩 알아보자
TraceStatus begin(String message)TraceStatus를 반환한다.void end(TraceStatus status)TraceStatus)를 전달받는다. 이 값을 활용해서 실행시간을 계산하고 종료시에도 시작할때와 동일한 로그 메시지를 출력할 수 있다.void exception(TraceStatus status, Exception e)TraceStatus,Exception정보를 함께 전달받아서 실행시간, 예외정보를 포함한 결과 로그를 출력한다.비공개 메서드
complete(TraceStatus status, Exception e)String addSpace(String prefix, int level): 다음과 같은 결과를 출력한다.prefix :
-->prefix: ‘←-’
prefix: ‘<X—’
참고로
HelloTraceV1는 아직 모든 요구사항을 만족하지는 못한다. 이후에 기능을 하나씩 추가할 예정이다.테스트 작성
이제 다음시간에 실제 애플리케이션에 적용해볼 것임
로그추적기V1 - 적용
v0 폴더를 v1으로 변경
controller 부분
/v1/requestController변경HelloTraceV1 trace:HelloTraceV1을 주입받는다. 참고로HelloTraceV1은@Component애노테이션을 가지고 있기 때문에 컴포넌트 스캔의 대상이 된다. 따라서 자동으로 스프링 빈으로 등록된다.trace.begin("OrderController.request()"): 로그를 시작할 때 메세지 이름으로 컨트롤러 이름 + 메서드 이름을 주었다. 이렇게 하면 어떤 컨트롤러와 메서드가 호출되었는지 로그로 편리하게 확인할 수 있다. 물론 수작업이다.trace.begin(),trace.end()코드 두줄만 적용하면 될 줄 알았지만 실상은 그렇지 않다.trace.exception()으로 예외까지ㅏ 처리해야 하므로 지저분한 try, catch 코드가 추가된다.begin()의 결과값으로 받은TraceStatus status값을end(),exception()에 넘겨야 한다. 결국 try~ catch 블록 모두에 이 값을 넘겨야한다. 따라서 try 상위에TraceStatus status코드를 선언해야 한다. 만약try, 안에서TraceSatus staus를 선언하면 try 블록안에서만 해당 변수가 유효기 때문에 catch 블록에 넘길 수 없다.throw e: 예외를 꼭 다시 던져주어야 한다. 그렇지 않으면 여기서 예외를 막아버리고 이후에 정상흐름으로 동작한다. 로그는 애플리케이션의 흐름에 영향을 주면 안된다. 로그 때문에 예외가 사라지면 안된다.실행
정상 : http://localhost:8080/v1/request?itemId=hello
예외 : http://localhost:8080/v1/request?itemId=ex
실행해보면 정상흐름과 예외 모두 로그로 잘 출력된다.
OrderServiceV1OrderRepositoryV1흐름: id 가 생성, level=0 → OrderService로 가서 begin()을 실행 level=0 → OrderRepository로 가서 status =33333으로 찍히고 → begin() 22222 → 111111
HelloTraceV1 덕분에 직접 로그하나하나 남기는 것보다는 편리하게 여러가지 로그를 남길 수 있었다. 하지만 로그를 남기기 위한 코드가 생각보다 너무 복잡하다. 지금은 우선 요구사항과 동작하는 것에만 집중하자.
남은 문제
요구사항
→ 결국 현재 로그의 상태 정보인 “트랜잭션 ID와 level”이 다음으로 전달되어야 한다.
“정리하면 로그에 대한 문맥(Context) 정보가 필요하다”
로그 추적기 V2 - 파라미터로 동기화 개발
HelloTraceV2를 개발해보자.이부분 추가해서 V2 완성
TraceId에서createdNextId()를 통해 다음 ID를 구한다.createNextId()의TraceId생성 로직은 다음과 같다.HelloTraceV2Test결과
결과
처음에는
begin()을 사용하고, 이후에는beginSync()를 사용하면 된다.beginSync를 호출할때 직전 로그의 traceId 정보를 넘겨주어야 한다.실행로그를 보면 같은
트랜잭션ID를 유지하고level을 통해 메서드 호출 깊이를 표현하는 것을 확인할 수 있다.로그 추적기 V2 - 적용
이제 로그 추적기를 애플리케이션에 적용해보자.
OrderController.request()에서 로그를 남길때 어떤 깊이와 어떤 트랜잭션ID 를 사용했는지 다음 차례인OrderService.orderItem()에서 로그를 남기는 시점에 알아야 한다.트랜잭션ID와level이 다음으로 전달되어야 한다.TraceStatus.traceId에 담겨있다. 따라서traceId를 컨트롤러에서 서비스를 호출할때 넘겨주면 된다.traceId를 넘기도록 V2 전체코드를 수정한다.refactoring :
alt + enterOrderControllerV2TraceStatus status = trace.begin()에서 반환 받은TraceStatus에는 트랜잭션 ID와 level 정보가 있는 TraceId가 있다.orderService.orderItem()을 호출할때 TraceId를 파라미터로 전다라한다.TraceId를 파라미터로 전달하기 위해OrderServiceV2.orderItem()의 파라미터에 TraceId를 추가해야 한다.OrderServiceV2orderItem()은 파라미터로 전달 받은traceId를 사용해서trace.beginSync()를 실행한다.beginSync()는 내부에서 다음traceId를 생성하면서 트랜잭션ID를 유지하고level은 하나 증가시킨다.beginSync()가 반환한 새로운TraceStatus를orderRepository.save()를 호출하면서 파라미터로 전달한다TraceId를 파라미터로 전달하기 위해orderRepository.save()의 파라미터에TraceId를 추가해야한다.OrderRepositoryV2save()는 파라미터로 전달받은traceId를 사용해서trace.beginSync()를 실행한다.begin.Sync()는 내부에서 다음traceId를 생성하면서 트랜잭션ID는 유지하고 level은 하나 증가시킨다.begin.Sync()는 이렇게 갱신된traceId로 새로운TraceStatus를 반환한다.trace.end(status)를 호출하면서 반환된TraceStatus를 전달한다.정상실행
http://localhost:8082/v2/request?itemId=hello

예외실행
http://localhost:8082/v2/request?itemId=ex

정리
요구사항
모든 PUBLIC 메서드의 호출과 응답정보를 로그로 출력
애플리케이션의 흐름을 변경하면 안됨
메서드 호출에 걸린 시간
정상 흐름과 예외 흐름 구분
메서드 호출의 깊이 표현
HTTP 요청을 구분
이 모든 요구사항을 만족했다.
남은문제
begin()을 호출하고 처음이 아닐때는beginSync()를 호출해야 한다.TraceId가 없다.TraceId를 파라미터로 넘기는 것 말고 다른 대안은 없을까???All reactions