티스토리 뷰
배경
현재 WAS에서 로그를 기록하고 Promtail이 로그를 다른 VM에 있는 Loki로 push해주고 있습니다.
하지만 단순히 로그를 보여주기만 할 뿐이였습니다.
따로 라벨링이 되어 있지 않아 원하는 로그를 찾기 위해서는 일일이 검색으로 찾아야했습니다.
에러가 발생한 로그만 살펴보고 싶거나, 특정 요청에 대한 로그를 살펴보고 싶다면 매번 검색으로 찾아야했습니다.
매번 검색을 통해 로그를 찾는 것의 불편함이 느껴졌고 단순히 문자열을 검색으로 원하는 로그를 찾는데 한계점이 존재했습니다.
로그를 파싱하자!
Loki에서는 LogQL이라는 쿼리문으로 원하는 로그를 필터링하거나 파싱하는 방법이 있습니다.
이를 활용한다면 원하는 로그를 쉽게 필터링 할 수 있었습니다.
이를 위해 우선 로그를 파싱하기로 했습니다.
우선 WAS의 로그 방식을 조금 수정하겠습니다.
스프링을 띄우면 나오는 로그를 살펴보면 모두 일정한 패턴을 통해 로그를 출력하고 있었습니다.
이러한 로그 패턴은 환경 설정 파일을 통해 커스텀하게 정의할 수 있습니다.
logging:
pattern:
file: "[%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}][%-5level][%logger.%method:line%line] - %msg%n"
저는 위와 같은 패턴을 통해 아래와 같은 로그를 출력하고 있었습니다.
[2025-06-27T13:16:53.527+09:00][INFO ][com.yapp.yapp.common.logging.DefaultLoggingInterceptor.afterCompletion:line69] - {"type":"RESPONSE", "requestId":"7308909a", "method":"GET", "uri":"/check/health", "status":"SUCCESS ✅", "statusCode":200, "duration":1, "body":I'm OK}
각 메타데이터들은 [] 를 통해 감싸져 있는 형태였고 이를 활용하면 원하는 데이터를 파싱할 수 있습니다.
Pattern
Loki에서는 정규표현식, json등 여러가지 방식으로 로그를 파싱할 수 있습니다. 저는 Pattern이라는 방식으로 로그를 파싱했습니다.
Log queries | Grafana Loki documentation
Pattern 방식은 <> 괄호를 사용해서 데이터를 파싱하는 방식입니다. 사용 방법은 생각보다 간단합니다. 추출하고자 하는 데이터를 <> 안에 정의하면 됩니다.
예시로 로그의 시간 부분을 파싱해봤습니다. 대괄호안에 있기에 [<time>] 와 같이 패턴을 지정하게 되면 필드로 time 이라는 필드가 새롭게 생기는 것을 확인할 수 있습니다. 이와 같은 방식으로 추출하고자 하는 필드를 정의하면 됩니다.
저는 json 형태로 요청 ID, body 등을 로깅하고 있어서 해당 값들도 포함해서 파싱했습니다.
{job="라벨링한 값"}
| pattern `<_> {"type":"<type>", <_>}`
| type="REQUEST "
| pattern `[<time>][<lev>][<source>] - {"type":<_>, "requestId":<requestId>, "method":"<method>", "uri":"<uri>", "body":<body>}`
[참고] ⚠️ 변수 이름을 level로 정의하면, 로그 레벨을 색상으로 확인할 수 없습니다.
level 변수를 사용하면 아래 사진과 같이 모든 로그가 회색으로 보입니다.
만약 변수명을 level 에서 다른 변수명으로 변경하면 INFO 레벨 로그에 대해 초록색으로 제대로 보입니다.
(WARN 은 주황색, ERROR 는 빨간색으로 보입니다.)
아마 Loki에서 기본적으로 level 변수명으로 로그 색상을 지정해주고 있는 것 같습니다. 그래서 변수를 재정의하면서 오버라이딩되는 것으로 생각됩니다.
로그 레벨이 색상으로 보이는 것이 가시성에 좋기 때문에 level 대신 다른 변수명을 사용하실 것을 추천합니다!
파싱 이어서 하기
다시 본문으로 돌아옵시다.
아래 사진을 통해 제가 추출한 필드값들을 확인할 수 있습니다.
{job="라벨링한 값"}
| pattern `<_> {"type":"<type>", <_>}`
| type="RESPONSE"
| pattern `[<time>][<lev>][<source>] - {"type":<_>, "requestId":<requestId>, "method":"<method>", "uri":"<uri>", "status":<status>", "statusCode":<statusCode>, "duration":<duration>, "body":<body>}`
응답 로그의 경우, statusCode, duration 정보를 추가적으로 로깅하고 있어서 해당 메타 데이터도 추가하고 쿼리문을 분리해주었습니다.
{job="라벨링한 값"}
| pattern `<_> {"type":"<type>", <_>}`
| type="REQUEST "
| pattern `[<time>][<lev>][<source>] - {"type":<_>, "requestId":<requestId>, "method":"<method>", "uri":"<uri>", "body":<body>}`
| lev="INFO"
만약 동일한 형식으로 로깅하고 있다면 로그를 분리할 필요가 없을 것 같습니다.
이제 추출된 정보를 바탕으로 원하는 로그만 필터링할 수 있습니다. 만약 INFO 레벨 로그만 본다면 다음과 같이 구성할 수 있습니다.
'개발 스토리' 카테고리의 다른 글
RESTful API 엔드포인트에 대한 고민 (0) | 2025.06.24 |
---|---|
기술 블로그 아카이빙 서비스 - TechLog (5) | 2025.06.02 |
레디스 Stream으로 분산 서버에서 푸시 알림 중복 전송 문제 해결하기 (0) | 2025.04.11 |
코루틴 기반 Watchdog으로 분산 서버에서 푸시 알림 중복 전송 문제 해결 (0) | 2025.04.08 |
비동기 작업을 빠르게 도와주는 코루틴 (0) | 2025.04.05 |
- Total
- Today
- Yesterday
- 우아한테크코스 6기
- 레디스 분산락
- 우아한테크코스 자소서
- logql
- 우테코 준비
- 카카오 기술 블로그
- 분산락
- setnx
- 알고리즘
- 토큰
- 기술 블로그 아카이빙
- 파이썬
- 회사 기술 블로그
- 코루틴
- 토큰 블랙리스트
- 레디스
- 우아한테크코스 후기
- 기술 블로그 모음
- 우아한테크코스
- redis
- 우테코 프리코스
- 게임개발
- 6기
- contextwith
- JWT
- 우테코
- 자바
- Assertions
- 우테코 6기
- 네이버 기술 블로그
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |