https://boolynn17.tistory.com/47
ModuPly에서 SSE(Server-Sent Events)와 알림(Notification) 도메인은 특성상 로그만으로는 상태 파악이 까다롭다.
grep으로 눈으로 보기엔 한계가 있다.이럴 때 ELK 스택을 붙이면 SSE 연결 수 추이, heartbeat 실패율, 알림 타입별 발송 성공/실패 카운트를 대시보드 하나로 볼 수 있다.
grok으로 텍스트 로그를 파싱하는 것보다, 애플리케이션에서 처음부터 JSON으로 로그를 찍는 편이 Logstash 파이프라인을 훨씬 단순하게 만든다. 예를 들어 SSE emitter 생명주기 로그를 이렇게 구조화한다고 하자.
{"timestamp":"2026-07-20T10:15:32.123Z","level":"INFO","event":"sse.connect","userId":1024,"emitterId":"emt-9f3a"}
{"timestamp":"2026-07-20T10:15:42.130Z","level":"DEBUG","event":"sse.heartbeat","emitterId":"emt-9f3a","status":"success"}
{"timestamp":"2026-07-20T10:16:05.201Z","level":"WARN","event":"sse.heartbeat","emitterId":"emt-9f3a","status":"fail","reason":"IOException"}
{"timestamp":"2026-07-20T10:16:05.210Z","level":"INFO","event":"sse.disconnect","emitterId":"emt-9f3a","reason":"client_timeout"}
{"timestamp":"2026-07-20T10:16:10.300Z","level":"INFO","event":"notification.publish","notificationType":"NEW_REVIEW","targetUserId":1024,"status":"sent"}
{"timestamp":"2026-07-20T10:16:10.450Z","level":"ERROR","event":"notification.publish","notificationType":"PLAYLIST_UPDATE","targetUserId":2048,"status":"failed","reason":"emitter_not_found"}
Logback의 logstash-logback-encoder 같은 라이브러리를 쓰면 Spring Boot에서 이런 JSON 로그를 코드 변경 최소화로 뽑아낼 수 있다.
# modu-ply-logstash.conf
input {
beats {
port => 5044
}
}
filter {
json {
source => "message"
}
date {
match => ["timestamp", "ISO8601"]
target => "@timestamp"
}
# event 필드 기준으로 분기 처리
if [event] == "sse.heartbeat" and [status] == "fail" {
mutate {
add_tag => ["sse_heartbeat_failure"]
}
}
if [event] == "notification.publish" and [status] == "failed" {
mutate {
add_tag => ["notification_failure"]
}
}
mutate {
remove_field => ["message", "timestamp"]
}
}
output {
if [event] =~ "^sse\." {
elasticsearch {
hosts => ["<http://localhost:9200>"]
index => "modu-ply-sse-%{+YYYY.MM.dd}"
}
} else if [event] =~ "^notification\." {
elasticsearch {
hosts => ["<http://localhost:9200>"]
index => "modu-ply-notification-%{+YYYY.MM.dd}"
}
} else {
elasticsearch {
hosts => ["<http://localhost:9200>"]
index => "modu-ply-app-%{+YYYY.MM.dd}"
}
}
}
포인트는 두 가지다.
json filter: grok 정규식 없이 바로 필드가 생긴다. 파이프라인이 훨씬 단순하고 견고하다.event 필드 기준 인덱스 분리: SSE 로그와 알림 로그를 서로 다른 인덱스에 저장하면, Kibana에서 도메인별로 Data View를 따로 만들어 조회 속도와 가독성을 둘 다 챙길 수 있다.