[6주차] 로그 시스템 고도화 (feat. 비동기 Logstash 파이프라인)

5주차에는 프론트엔드 작업하느라 글을 쓸 만한 주제도 없었고, 시간도 없었다.
그래도 이번 주 수요일까지는 프론트를 마무리하고 로그 쪽 고도화할 시간이 있었고, 오랜만에 행복 개발 했다.

기술 블로그 글을 어떤 형식으로 작성해야 할지 아직도 고민이다.

  • 많은 독자를 상정하고 입니다체를 사용하며 내용을 추상화하여 작성할지
  • 지금처럼 회고성으로 이다체를 사용하며 어느 정도 구체적으로 작성할지

최근에 후자로 작성한 기술 블로그 글을 보는데 깊이도 있고 사고 과정이 잘 느껴져서 좋았던 것 같다.
그래서 이번 글은 후자로 작성해보려고 한다.

아무튼 잡설은 여기까지 하고, 6주차에 로깅 시스템 고도화한 내용을 기록/공유하려 한다.
(그마저도 절반은 프론트엔드에 시간을 갈아 넣긴 하였다.)


📌 사전 참고 글

이 글을 읽기 전에 아래 글을 읽지 않았다면 읽는 것을 추천한다.
아래 글의 후속 내용을 다룬 것이 이 글이다.

👉 [3주차] MongoDB를 활용한 로깅 시스템 구축 (시스템/데이터 로그 저장)
👉 https://ti2soon.tistory.com


🧠 의사결정

SODA에서는 두 가지 종류의 로그를 저장 중이다.

  • 시스템 로그: 콘솔에 찍히는 LEVEL(INFO, ERROR 등) 로그
  • 데이터 로그: 데이터의 생성/변경/삭제 사항을 JSON 형태로 기록한 로그

각 로그의 특성

  • 데이터 로그: 수가 많지 않음 (CRUD 발생 시에만 로깅됨)
  • 시스템 로그: 수가 많음 (컨트롤러/서비스 전반, 프레임워크 레벨 로그 포함)

기존 문제점

기존에는 동기식으로 MongoDB에 로깅했기 때문에
시스템 로그처럼 양이 많을 경우 I/O로 인한 지연이 발생 → 서비스 응답 시간에 영향

개선 목표

  1. 파일로 먼저 저장
  2. Logstash 등 수집기를 통해 비동기적으로 MongoDB 저장
  3. Spring은 파일만 던지고 로깅에 대해 책임 없음
  4. 로깅이 WAS 성능에 영향 X

⚙️ 최종 구조

  • 비동기 로그 수집기로 Logstash 사용
  • 처음에는 Filebeat 사용하려다 실패 (MongoDB output 미지원)
Exiting: error initializing publisher: output type mongodb undefined

결국 Logstash를 Spring 서버 안에 설치

원칙적으로는 별도 서버로 분리하는 게 맞음 그러나 마감 임박 + 서버 비용 문제로 동일 서버에 설치

🛠️ 구현

  1. logback-spring.xml 설정 ```java
%green(%d{yyyy-MM-dd HH:mm:ss.SSS}) %magenta([%thread]) %highlight(%5level) %cyan(%logger) - %msg%n ${LOG_PATH}/${LOG_FILE_NAME}.json ${LOG_PATH}/${LOG_FILE_NAME}.%d{yyyy-MM-dd}.json 30 time yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ logger level message _class method line INFO

2. logstash.conf 작성
```lombok.config
input {
  file {
    path => "/usr/share/logstash/logs/*.json"
    start_position => "beginning"
    sincedb_path => "/usr/share/logstash/data/.sincedb"
    codec => "json"
  }
}

filter {
  mutate {
    remove_field => ["@version", "path", "host", "thread"]
  }
  date {
    match => ["time", "yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ"]
    target => "@timestamp"
  }
  mutate {
    rename => { "time" => "created_time" }
    rename => { "@timestamp" => "logging_time" }
  }
}

output {
  if [level] == "ERROR" {
    mongodb {
      uri => "${MONGODB_URI}"
      database => "log"
      collection => "error_log"
      isodate => true
    }
  } else {
    mongodb {
      uri => "${MONGODB_URI}"
      database => "log"
      collection => "system_log"
      isodate => true
    }
  }
}

  1. Dockerfile
    FROM docker.elastic.co/logstash/logstash:7.17.16
    RUN bin/logstash-plugin install logstash-output-mongodb
    COPY logstash.conf /usr/share/logstash/pipeline/logstash.conf
    
  2. Jenkins Shell Script ```bash docker run -d
    –env-file .env
    -p 8085:8080
    –name soda
    –network ${DOCKER_NETWORK}
    -v “/home/ubuntu/logs:/logs”
    do2soon/soda:latest

docker pull do2soon/logstash:latest

if [ “$(docker ps -a -q –filter name=logstash)” ]; then docker rm -f logstash fi

docker run -d
–name logstash
–network soda-network
-m 1g –memory-swap=1g
-v “/home/ubuntu/logs:/usr/share/logstash/logs”
-v “/home/ubuntu/sincedb:/usr/share/logstash/data”
–env-file .env
-e LS_JAVA_OPTS=”-Xms512m -Xmx512m”
do2soon/logstash:latest ```

  1. 로그 TTL 설정
    • logback-spring.xml에서 30로 파일 로그 관리
    • MongoDB에서는 TTL 인덱스로 로그 수명 관리

🧯 트러블슈팅

1. Logstash RAM 과점유

t2.micro에 Logstash → RAM/CPU 전부 점유, SSH도 불가

t2.small로 교체 + -m 1g –memory-swap=1g 옵션으로 해결

2. 로그 시간 문자열 저장

로그의 시간 필드가 문자열로 저장되어 TTL/인덱싱 불가

해결:

  • logback에서 ISO 포맷 저장

  • logstash에서 date 필터로 변환

3. 중복 로깅

로그가 초당 50개씩 증가하는 기이한 현상

sincedb_path + 외부 마운트로 중복 로깅 방지

✅ 정상 작동 확인

로그가 파일 → Logstash → MongoDB로 비동기 전달

오류 로그와 일반 로그를 다른 컬렉션으로 구분 저장

TTL 및 로그 순환 정책도 정상 작동