✅ 개요
일반적인 로그 데이터는 단순한 텍스트 형식으로 이루어져있다. MySQL, Oracle과 같은 RDBMS에서는 테이블의 컬럼이 존재해 그 컬럼을 기준으로 쿼리를 할 수 있지만 단순한 텍스트 형식에서는 이것이 불가능하다.
Grafana Loki의 쿼리 언어인 LogQL에서는 이를 가능하게 하기 위해 로그 메시지 안에서 필드를 구분시키는 방법을 제공한다.
그 방법은 크게 두 가지이다.
첫 번째는 logfmt라는 별도의 로그포멧팅 방식을 이용하는 것이고
두 번째는 Promtail을 통한 전처리로 로그 수집 과정 중, 메시지에 라벨을 붙이는 것이다.
지금부터 각각에 대해 알아보고 성능 비교까지 한번 해보도록 하겠다.
✅ logfmt
💡 logfmt는 간단하고 사람이 읽기 쉬운 로그 포맷으로 키-값 쌍으로 데이터를 표현하며, 로그 데이터의 구조화를 통해 분석과 디버깅을 보다 쉽게 해준다.
예를 들어, 다음과 같은 로그가 존재할 때,
2024-10-26 17:55:26.617 [http-nio-8080-exec-1] [traceId=32fc2f61-675a-4cd1-aee6-22fbd5990c49]
INFO c.s.t.g.c.aop.HttpLoggingAspect -
HTTP Request: HTTPMethod=GET Path=/test3 from IP=0:0:0:0:0:0:0:1
key=value형태로 된 데이터를 모두 뽑아 필드로 만들어낸다.
위의 예시의 경우 다음의 필드가 추출된다.
- [traceId=5cb495e-008a-4265-87c7-003afd886736]
- HTTPMethod=GET
- Path=/test2
- IP=0:0:0:0:0:0:0:1
이는 띄어쓰기를 기준으로 엄격하게 추출된다.
여기서 엄격이라는 단어를 쓴 이유는 ‘[traceId=5cb495e-008a-4265-87c7-003afd886736]’에서는 ‘traceId=5cb495e-008a-4265-87c7-003afd886736’처럼 대괄호가 제거 되어야 할 것 같은데 붙어있다는 이유로 함께 추출된다.
LogQL에서 logfmt를 통해 추출된 필드를 사용하려면 쿼리에 다음과 같이 logfmt를 추가하면 된다.
{job="logs"} | logfmt | HTTPMethod = `GET`
이렇게 되면 logfmt로 추출된 필드인 HTTPMethod를 통해 쿼리를 실행시킬 수 있다.
✅ Promtail의 라벨링
💡Promtail은 Grafana Loki와 함께 사용되는 로그 수집기 도구로, 시스템 로그를 수집하고 Loki에 전달하여 검색하고 시각화할 수 있도록 해준다.
Promtail의 주요 기능 중 하나는 로그에 라벨을 추가하여 라벨링하는 것으로, 이를 통해 로그 데이터를 더 쉽게 필터링하고 분석할 수 있다.
Loki가 라벨을 기준으로 로그 데이터를 인덱싱하기 때문에 쿼리 속도가 빨라지고, 특정 라벨 값에 따라 로그를 필터링할 수 있어 검색 효율성이 증가한다.
다음 두 가지 종류의 로그 메시지가 있다고 가정해보자.
1. HTTP Request 요청 정보
2024-10-26 17:55:26.617 [http-nio-8080-exec-1] [traceId=32fc2f61-675a-4cd1-aee6-22fbd5990c49]
INFO c.s.t.g.c.aop.HttpLoggingAspect -
HTTP Request: HTTPMethod=GET Path=/test3 from IP=0:0:0:0:0:0:0:1
2. HTTP Request 실행 시간
2024-10-26 17:55:26.617 [http-nio-8080-exec-1] [traceId=32fc2f61-675a-4cd1-aee6-22fbd5990c49]
INFO c.s.t.g.c.aop.HttpLoggingAspect -
HTTP Execution: Path=/test3 ExecutionTime=0ms
Promtail에서는 다음과 같이 라벨링을 할 수 있다.
다음은 Promtail의 설정파일에 해당하는 config.yml의 일부이다.
pipeline_stages:
# traceId 추출 (모든 로그에서 추출 가능)
- regex:
expression: '\\[traceId=(?P<traceId>[^\\]]+)\\]'
# HTTP Request 관련 라벨 추출 (HTTP Request 메시지에서만 추출)
- regex:
expression: 'HTTPMethod=(?P<HTTPMethod>[A-Z]+) Path=(?P<Path>/\\S+) from IP=(?P<IP>[0-9a-fA-F:]+)'
# ExecutionTime 추출 (ExecutionTime이 있는 메시지에서만 추출)
- regex:
expression: 'ExecutionTime=(?P<ExecutionTime>\\d+ms)'
# 라벨 설정
- labels:
traceId: traceId
HTTPMethod: HTTPMethod
Path: Path
IP: IP
ExecutionTime: ExecutionTime
차후 쿼리를 작성할 때 조건으로 많이 활용될 데이터들을 라벨로 선정했다.
- traceId: 로그의 식별값
- HTTPMethod: GET/POST/PUT/PATCH/DELETE
- Path: API 경로
- IP
- ExecutionId: 실행시간
이렇게 라벨링을 진행하고 해당 config파일을 적용시킨다.
그러면 다음과 같이 logfmt를 적용하지 않고 바로 쿼리를 실행할 수 있게 된다.
{job="logs", HTTPMethod = `GET`}
✅ logfmt vs 라벨링 성능 비교
💿 테스트 데이터
HTTP GET 요청 총 400번을 날려서 남겨진 로그 데이터를 가지고 테스트를 진행하였다.
[로그 샘플]
2024-10-26 17:54:26.629 [http-nio-8080-exec-3] [traceId=f5cb495e-008a-4265-87c7-003afd886736]
INFO c.s.t.g.c.aop.HttpLoggingAspect -
HTTP Request: HTTPMethod=GET Path=/test2 from IP=0:0:0:0:0:0:0:1
🔬 테스트 방식
해당 로그들 중 /test2라는 경로로 온 모든 로그들을 뽑는 쿼리를 통해 성능을 비교해보겠다.
1️⃣ logfmt로 쿼리를 했을 때
# pipeline_stages:
# # traceId 추출 (모든 로그에서 추출 가능)
# - regex:
# expression: '\\[traceId=(?P<traceId>[^\\]]+)\\]'
#
# # HTTP Request 관련 라벨 추출 (HTTP Request 메시지에서만 추출)
# - regex:
# expression: 'HTTPMethod=(?P<HTTPMethod>[A-Z]+) Path=(?P<Path>/\\S+) from IP=(?P<IP>[0-9a-fA-F:]+)'
#
# # HTTP Response 관련 라벨 추출 (HTTP Response 메시지에서만 추출)
# - regex:
# expression: 'ResponseCode=(?P<ResponseCode>\\d+) ResponseMessage=(?P<ResponseMessage>[^ ]+) ResponseData="(?P<ResponseData>[^"]*)"'
#
# # ExecutionTime 추출 (ExecutionTime이 있는 메시지에서만 추출)
# - regex:
# expression: 'ExecutionTime=(?P<ExecutionTime>\\d+ms)'
라벨링 하는 부분을 주석처리하고 logfmt만을 이용해서 쿼리를 실행시켜보겠다.
[쿼리 실행]
{job="logs"} | logfmt | HTTPMethod = `GET` | Path = `/test2`
[결과]
[실행 시간]
2️⃣ 라벨링으로 쿼리 했을 때
pipeline_stages:
# traceId 추출 (모든 로그에서 추출 가능)
- regex:
expression: '\\[traceId=(?P<traceId>[^\\]]+)\\]'
# HTTP Request 관련 라벨 추출 (HTTP Request 메시지에서만 추출)
- regex:
expression: 'HTTPMethod=(?P<HTTPMethod>[A-Z]+) Path=(?P<Path>/\\S+) from IP=(?P<IP>[0-9a-fA-F:]+)'
# HTTP Response 관련 라벨 추출 (HTTP Response 메시지에서만 추출)
- regex:
expression: 'ResponseCode=(?P<ResponseCode>\\d+) ResponseMessage=(?P<ResponseMessage>[^ ]+) ResponseData="(?P<ResponseData>[^"]*)"'
# ExecutionTime 추출 (ExecutionTime이 있는 메시지에서만 추출)
- regex:
expression: 'ExecutionTime=(?P<ExecutionTime>\\d+ms)'
라벨링 하는 부분 주석을 해제하고 라벨잉을 이용해서 쿼리를 실행시켜보겠다.
[쿼리 실행]
{job="logs", HTTPMethod="GET", Path="/test2"}
[결과]
[실행 시간]
exec time은 네트워크를 고려하지 않고 쿼리와 데이터의 양을 가지고 계산되기 때문에 여러 번 시도해도 그 값은 일정하다.
📑 테스트 결과
logfmt를 사용했을 때 - 26.0ms
라벨링을 사용했을 때 - 9.84ms
라벨링을 이용했을 때가 약 3배정도 성능이 좋았다.
✅ 참고 자료
Grafana Loki OSS | Log aggregation system
Understand labels | Grafana Loki documentation
'데브코스 > 실습 & 프로젝트' 카테고리의 다른 글
[2-3차 프로젝트] PLG 스택 운영환경에 배포 하기 (1) | 2024.11.05 |
---|---|
[2-3차 프로젝트] Grafana 알림 기능 사용하기 (0) | 2024.11.05 |
[2-3차 프로젝트] 로깅 시스템 개선: 중앙 집중화 로깅 - PLG 스택 (1) | 2024.11.03 |
[2-3차 프로젝트] AOP & Slf4j를 통해 로깅 구현하기 (0) | 2024.11.03 |
[2-3차 프로젝트] Sonar를 통해 코드 정적분석하기 - 3. SonarCloud 활용법 (0) | 2024.10.28 |