실무에서 모니터링을 본격적으로 들어가기 전 현재 로그를 이 기회에 수정해 보려고 한다.
<기존 방식>
문제점
HTTP_METHOD
의 정보는URL
과 함께 사용으로 특정 API 추출이 쉽지만 실제로 우리 서비스는 같은URL
에 다른HTTP_METHOD
가 사용되는 일이 없기 때문에 불필요한 정보IP
는 좋은 정보지만 실제 로그 데이터를 사용함에 있어서는 IP 보다는 특정 유저를 나타낼 수 있는 아이디를 남기는게 나아보임CLASS_METHOD
-> 불필요- 한 로그 당 (3 + ?)줄씩 출력 되는 구조
이를 개선하려 한다
우리는 현재 MSA 아키텍처? 라고 하기는 어렵지만 다른 웹서비스가 연동되어있다. 이후에 모니터링과 로그 수집에 있어서 1 : N
로 하는데 추적을 용이하게하기 위해 TraceId
를 필수로 넣고 추가적으로 넣으려 한다.
그래서 하나의 로그에 담기는 정보는 다음과 같다.
- 시간
- traceId
- userId
- api + message
(더 필요한 정보가 있다면 수정될 일이 있을 수 있다.)
일단 LogTracer
클래스를 간단하게 새로 만들어보자.
@Aspect
: AOP 사용@Component
: Bean으로 등록@Around
: “ ”
안에 포인트컷을 지정하여 Advice가 실행하도록 함이제 안에 쉬운 정보부터 담아보자.
위와 같이 proceed()
진행전 로그에서 url
정보를 담으니
Url
이 잘 나오는걸 확인할 수 있다.
다음은 소요된 시간을 proceed()
종료 후에 출력해보자
이제 요구사항의 두가지를 출력하는데 성공했고, 남은 두가지가 남았다.
시간- traceId
- userId
api + message
다음으로 traceId
를 먼저 남겨보자
Stateless
성질로 모든 요청은 각자의 쓰레드 로컬이 배정되고 모든 명령이 종료되면 쓰레드 로컬을 반납한다. 그래서 하나의 비즈니스 요청에만 같은 TraceId
를 놓을 수 있다.
이번에는 유저의 행동을 추적하기 위해서 userId
를 남겨보자
일단 현재 사용하고 있는 로그인 방식인 Jwt의 AccessToken
에 userId
claim
을 만들어준다.
그리고 getAccessToken
메서드를 호출하는 부분에는 모두 파라미터로 userId
를 추가해준다.
그리고 api 호출에서 accessToken
parse
부분에 디버그를 찍어 userId
가 제대로 들어갔는지 디버그를 통해 확인한다.
성공적으로 들어간걸 확인했다.
그렇다면 LogTrace
에 accessToken
을 파싱한 claims
정보를 불러오자
JwtTokenProvider
를 우선 주입시키고, accessToken
을 헤더에서 받아 claims
를 파싱하는 메서드를 호출한 결과를 디버그를 찍어 확인해보자
오류가 발생했다. accessToken
의 앞부분을 파싱 안한 문제였다.
substring
을 통해 해결할 수 있는 오류였다. 오류를 해결하니 claims
에서 userId
를 불러올 수 있었다.
그리고 새로운 문제를 찾았다. 로그인과 같은 accessToken
이 들어가지 않는 url에서는 NullException
이 발생했다.
이 문제는 Security
설정에서 permitAll()
되어있는 url에서는 예외처리를 만들어주어서 해결했다.
이제 로그인도 잘 되고, userId
도 잘 가져오니 로그를 남겨보면
잘 나타난다.
이제 로그인 과정과 같은 permitAll
url 메서드에서의 로그를 어떻게 남겨야 되냐는 문제가 있다.
따로 모아서 개별로 로그를 만들었고, 아래와 같이 리팩토링했다.
좀 더 리팩토링 한다면 코드 아래 이미지처럼 만들었고,
로그는 결과적으로 아래와 같이 출력됐다.
로그가 잘 출력되는 것을 확인할 수 있는데
초기 목표를 만족하고 나니 로그를 후에 파싱해서 사용하기 위해 정형화된 형식인
JSON 형식으로 저장하도록 목표가 변경되었다. (이외에 로그를 활용할 때 JSON이 유리한 이유 포함)
Build.gradle
에 logback
라이브러리를 추가해서 로그를 json으로 바꾸어 보았지만 로그가 콘솔 로그 한번과 json 형태로 두번 남기에 로그를 남기는 방식을 log를 남길 때 jsonObject
로 만들기로 결정했다.
위 이미지와 같이 JsonObject
로 만드는 부분을 구현했고,
실행했을때 아래와 같은 결과를 얻을 수 있었다.
하지만 아쉬운 점은 내가 원하는 로그의 순서는
- TraceId
- UserId
- spendTime
- Url
- Message
순서인데 나름 정렬돼있어 보이지만 정렬되어있지 않다.
순서를 보장하기 위해 Gson
으로 바꾸려고도 해봤지만 JsonObject
를 Gson
으로 바꾸는 형태이기에 순서는 변화가 없었다.
그래서 찾아보다가 처음 JsonObject
를 import
할 때 많은 라이브러리가 있던 것이 떠올라 찾아본 결과 Gson
의 JsonObject
에서 addProperty
로 파라미터를 추가한 경우에는 순서가 보장되는 것을 확인해서 직접해보았다.
위에 내가 썼던 minidev.json.JSONObject
를 gson.JsonObject
로 변경했다.
위 처럼 코드를 변경했고, 실행해본 결과
이전에 작은 이슈였던 url에 \
가 생기는 이슈도 없어졌고
내가 원하는 형식과 순서의 로그를 출력할 수 있었다.