로그 개선기

크리링·2024년 3월 15일
0
post-thumbnail

실무에서 모니터링을 본격적으로 들어가기 전 현재 로그를 이 기회에 수정해 보려고 한다.

기존 로그 문제와 개선 방안

<기존 방식>

문제점

  • 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의 AccessTokenuserId claim을 만들어준다.

그리고 getAccessToken메서드를 호출하는 부분에는 모두 파라미터로 userId를 추가해준다.

그리고 api 호출에서 accessToken parse 부분에 디버그를 찍어 userId가 제대로 들어갔는지 디버그를 통해 확인한다.

성공적으로 들어간걸 확인했다.


그렇다면 LogTraceaccessToken을 파싱한 claims 정보를 불러오자

JwtTokenProvider 를 우선 주입시키고, accessToken을 헤더에서 받아 claims를 파싱하는 메서드를 호출한 결과를 디버그를 찍어 확인해보자

오류가 발생했다. accessToken의 앞부분을 파싱 안한 문제였다.

substring을 통해 해결할 수 있는 오류였다. 오류를 해결하니 claims에서 userId를 불러올 수 있었다.

그리고 새로운 문제를 찾았다. 로그인과 같은 accessToken이 들어가지 않는 url에서는 NullException이 발생했다.

이 문제는 Security 설정에서 permitAll() 되어있는 url에서는 예외처리를 만들어주어서 해결했다.

이제 로그인도 잘 되고, userId도 잘 가져오니 로그를 남겨보면

잘 나타난다.
이제 로그인 과정과 같은 permitAll url 메서드에서의 로그를 어떻게 남겨야 되냐는 문제가 있다.

따로 모아서 개별로 로그를 만들었고, 아래와 같이 리팩토링했다.

좀 더 리팩토링 한다면 코드 아래 이미지처럼 만들었고,

로그는 결과적으로 아래와 같이 출력됐다.






JSON 형식

로그가 잘 출력되는 것을 확인할 수 있는데
초기 목표를 만족하고 나니 로그를 후에 파싱해서 사용하기 위해 정형화된 형식인
JSON 형식으로 저장하도록 목표가 변경되었다. (이외에 로그를 활용할 때 JSON이 유리한 이유 포함)

Build.gradlelogback 라이브러리를 추가해서 로그를 json으로 바꾸어 보았지만 로그가 콘솔 로그 한번과 json 형태로 두번 남기에 로그를 남기는 방식을 log를 남길 때 jsonObject로 만들기로 결정했다.

위 이미지와 같이 JsonObject로 만드는 부분을 구현했고,
실행했을때 아래와 같은 결과를 얻을 수 있었다.

하지만 아쉬운 점은 내가 원하는 로그의 순서는

  1. TraceId
  2. UserId
  3. spendTime
  4. Url
  5. Message

순서인데 나름 정렬돼있어 보이지만 정렬되어있지 않다.
순서를 보장하기 위해 Gson으로 바꾸려고도 해봤지만 JsonObjectGson으로 바꾸는 형태이기에 순서는 변화가 없었다.

그래서 찾아보다가 처음 JsonObjectimport할 때 많은 라이브러리가 있던 것이 떠올라 찾아본 결과 GsonJsonObject에서 addProperty로 파라미터를 추가한 경우에는 순서가 보장되는 것을 확인해서 직접해보았다.

위에 내가 썼던 minidev.json.JSONObjectgson.JsonObject로 변경했다.

위 처럼 코드를 변경했고, 실행해본 결과

이전에 작은 이슈였던 url에 \ 가 생기는 이슈도 없어졌고
내가 원하는 형식과 순서의 로그를 출력할 수 있었다.






참고 : 토스ㅣSLASH 23 - 분산 추적 체계 & 로그 중심으로 Observability 확보하기

0개의 댓글