임도현의 성장
[Spring-Boot] Promtail + Loki + Logback 모니터링 본문
👀로그 수집하는 이유
제가 이전 프로젝트에서 배포를 했을 때 프로그램에 문제가 생겼는데 로그를 확인 할려면 EC2에 접속해 로그 파일에 접속해서 로그를 하나하나 확인을 했다. 하지만 모니터링에 로그를 수집하면 error 정보만 볼 수 있게 할 수 있어 원인을 빠르게 파악하고 문제를 해결 할 수 있게 만들어 보기 위해 사용하였습니다.
🦖Promtail이란?
Promtail은 로그 수집기로, 주로 Loki와 함께 사용됩니다. Promtail은 로그 파일을 읽고, 그 내용을 규칙에 맞게 처리하여 로그 데이터를 수집한 뒤 Loki에 전달하는 역할을 합니다.
🥐Loki란?
Loki는 로그 수집 및 저장 시스템으로, Grafana와 함께 사용됩니다. Loki는 로그 데이터를 시계열 데이터로 처리하여 로그를 빠르고 효율적으로 검색하고 분석할 수 있게 도와주며 로그 데이터를 구조화된 데이터로 저장하지 않고 텍스트 형식 그대로 저장하여 설정과 사용이 간편하고 빠른 처리 성능을 보장 합니다.
👻Architecture
Promtail은 로그 파일을 수집하고, 이를 Loki로 전송하는 역할을 합니다. Promtail은 주어진 로그 파일 경로를 모니터링하면서 새로운 로그가 추가될 때마다 이를 감지하고 Loki로 전달합니다.
😟Logback 설정
Logback은 resources하위 폴더에 XML형식으로 작성합니다. 로그 수집 할 때 log level을 나누어 아래와 같이 파일을 정리하였습니다. LOG_PATH에 담은 /home/log 경로는 로컬 기준으로"C:드라이브" 부터 시작이다.
- INFO => /home/log/info
- WARN, ERROR => /home/log/warn_error
pattern을 보면 알다 시피 [날짜] [레벨] [스레드] 로거명 - 메시지 형식으로 출력합니다. rollingPolicy태그를 보면 로그 파일이 하루가 지나면 로그 파일이 날짜별로 (info-YYYY-MM-DD.log) 롤링되도록 설정 하였습니다. maxHistory는 보존할 최대 로그 파일의 개수를 3개로 설정 3개가 넘어가면 오랜된 로그 는 자동으로 삭제됩니다. filter를 통해 level별로 기록하도록 설정했습니다.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 로그 파일 저장 위치 설정 -->
<property name="LOG_PATH" value="/home/log" />
<!-- 콘솔 로그 패턴 설정 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss}] [%level] [%thread] %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- INFO 로그를 저장하는 파일 -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/info/info.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/info/info-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>3</maxHistory>
</rollingPolicy>
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss}] [%level] %logger{36} - %msg%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
</appender>
<!-- WARN & ERROR 로그를 저장하는 파일 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/warn_error/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/warn_error/error-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>3</maxHistory>
</rollingPolicy>
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss}] [%level] %logger{36} - %msg%n</pattern>
</encoder>
<!-- WARN & ERROR 모두 저장 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>NEUTRAL</onMismatch>
</filter>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 로거 설정 -->
<logger name="com.example" level="INFO" additivity="false">
<appender-ref ref="INFO_FILE" />
<appender-ref ref="ERROR_FILE" />
</logger>
<!-- 기본 root 로거 설정 -->
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="ERROR_FILE" />
</root>
</configuration>
👊Log 파일 확인
Logback에서 설정 해 준 데로 파일이 잘 생성 되었다. 자정이 지나면 자동으로 날짜가 기입되고 3개까지는 저장되고 3개 이상부터는 삭제 된다. 한번더 강조하자면 LOG_PATH에 담은 /home/log 경로는 로컬 기준으로"C:드라이브" 부터 시작이다.
🐳docker-compose
docker-compose에 Promtail이랑 Loki 추가 Config부분이랑 volumes 부분 경로를 꼭 확인해야 해
services:
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000" # Grafana 웹 UI
volumes:
- grafana-storage:/var/lib/grafana # Grafana 데이터 유지
depends_on:
- prometheus
networks:
- monitoring
loki:
image: grafana/loki:latest
container_name: loki
ports:
- "3100:3100"
command: -config.file=/etc/loki/loki-config.yml
volumes:
- ./loki-config.yml:/etc/loki/loki-config.yml
networks:
- monitoring
promtail:
image: grafana/promtail:latest
container_name: promtail
ports:
- "9080:9080"
volumes:
- //c/home/log:/home/log
- ./promtail-config.yml:/etc/promtail/promtail-config.yml
command: -config.file=/etc/promtail/promtail-config.yml
networks:
- monitoring
volumes:
grafana-storage:
driver: local
networks:
monitoring:
driver: bridge
👾promtail-config.yml
- job_name : 로그 수집 작업을 구분하는 이름입니다. 저는 info_logs와 error_logs로 두 개의 로그 수집 작업을 설정했습니다.
- targets : 로그를 수집할 서버를 설정합니다. 여기서는 localhost로 설정하여 로컬 서버에서 로그를 수집합니다.
- labels : 로그에 추가할 레이블을 설정합니다. job과 level은 로그의 특성을 나타내는 태그로 활용됩니다.
- __path__ : 수집할 로그 파일 경로를 설정합니다.
- regex_expression : 정규표현식을 사용하여 로그에서 시간, 레벨, 클래스, 메시지 등의 정보를 추출합니다.
- timestamp : 시간정보를 RFC3339 형식으로 변환하여 Loki가 적절하게 처리할 수 있도록 합니다.
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml # 마지막으로 읽은 로그 위치를 저장하는 파일
clients:
- url: http://loki:3100/loki/api/v1/push # Loki 서버 주소 (Promtail에서 Loki로 로그 전송)
scrape_configs:
- job_name: "info_logs"
static_configs:
- targets:
- localhost
- labels:
job: "spring-boot-logs"
level: "info"
__path__: /home/log/info/info.log
pipeline_stages:
- regex:
expression: '^\[(?P<time>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] \[(?P<level>INFO|ERROR|WARN|DEBUG)\] (?P<class>[^-]+) - (?P<message>.*)$'
- timestamp:
source: time
format: RFC3339
- job_name: "error_logs"
static_configs:
- targets:
- localhost
- labels:
job: "spring-boot-logs"
level: "error"
__path__: /home/log/warn_error/error.log
pipeline_stages:
- regex:
expression: '^\[(?P<time>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] \[(?P<level>INFO|ERROR|WARN|DEBUG)\] (?P<class>[^-]+) - (?P<message>.*)$'
- timestamp:
source: time
format: RFC3339
🍍loki-config.yml 작성
auth_enabled: false
server:
http_listen_port: 3100
grpc_listen_port: 9096
common:
instance_addr: loki # 도커 컴포즈로 구성했으므로 컨테이너 이름 지정
path_prefix: /tmp/loki
storage:
filesystem:
chunks_directory: /tmp/loki/chunks
rules_directory: /tmp/loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory
query_range:
results_cache:
cache:
embedded_cache:
enabled: true
max_size_mb: 100
schema_config:
configs:
- from: 2020-10-24
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
ruler:
alertmanager_url: http://localhost:9093
# By default, Loki will send anonymous, but uniquely-identifiable usage and configuration
# analytics to Grafana Labs. These statistics are sent to https://stats.grafana.org/
#
# Statistics help us better understand how Loki is used, and they show us performance
# levels for most users. This helps us prioritize features and documentation.
# For more information on what's sent, look at
# https://github.com/grafana/loki/blob/main/pkg/analytics/stats.go
# Refer to the buildReport method to see what goes into a report.
#
# If you would like to disable reporting, uncomment the following lines:
#analytics:
# reporting_enabled: false
🥕Promtail Targets 확인
http://localhost:9080/targets 로 접속을 해보면 Promtail이 Targets하고있는 상태를 알 수 있다. 나는 접속이 잘되어 TRUE 상태이지만 FALSE상태이면 문제가 있어 하나씩 찾아 봐야 한다.
💣트러블 슈팅 1
나 같은 경우에는 Promtail에 접속을 해보니깐 계속 FALSE가 떴다. 이유는 로그 정보를 /home/log에 저장 하고 있는데 Docker Desktop이 WSL2(Windows)에서 파일을 못 읽는 문제가 발생했다. 그래서 파일 경로 앞에 //C 를 붙쳐 절대 경로를 사용했다.
promtail:
image: grafana/promtail:latest
container_name: promtail
ports:
- "9080:9080"
volumes:
- //c/home/log:/home/log # 절대 경로로 설정
- ./promtail-config.yml:/etc/promtail/promtail-config.yml
command: -config.file=/etc/promtail/promtail-config.yml
networks:
- monitoring
💣트러블 슈팅 2
로그를 수집 잘 되었는데 log가 다 찍히지 않고 몇개만 찍히는 에러를 발견했다. 이유는 스프링부트는 Apache Tomcat 로그 형식으로 찍히고 있었다. 근데 나는 텍스트 형식으로 읽고 있으니 정규식이 맞지 않아 log를 제대로 읽지 못하였다. Logback이 텍스트 형식이라면 해당 형식에 맞는 정규식을 사용하면 해결 완료
💥Loki Grafna 연결
Data Sources에 Loki를 등록한 후 Explore에 들어 가 Label browser를 누르면 우리가 아까 설정한 level 태그, JOB, 파일 등 내가 보고 싶은 거를 선택하면 로그들을 확인 할 수 있다.
🦞Dashboards에 추가
Dashboards에 들어가 Add => Visualization에 들어가면 밑에 이미지 처럼 맞춰주면 된다. Time series을 Logs로 수정 Data source를 Loki로 수정 이제 원하는 로그 를 선택해서 저장 하면 된다.
🥳최종 결과
'Spring Boot' 카테고리의 다른 글
OpenCV 얼굴인식(Face Detection) 사용해보기 (0) | 2025.04.03 |
---|---|
[Spring-Boot] JUnit & Mockito기반테스트 코드 작성 (0) | 2025.03.22 |
[Spring-Boot] 객체 지향 설계 원칙 SOLID (0) | 2025.02.25 |
[Spring-Boot] QueryDsl 적용해보기 (0) | 2025.02.17 |
[Spring-Boot] Prometheus + Grafana 모니터링 구축 (1) | 2025.02.09 |