G코딩 스토리

[Spring Boot + ELK] logback을 통해서 ELK에 로그 저장 (feat.docker) 본문

Develop/ELK

[Spring Boot + ELK] logback을 통해서 ELK에 로그 저장 (feat.docker)

Jiione 2024. 8. 4. 15:07

+ 참고)

[[Spring Boot] Log4j2 로그를 여러 개의 파일에 저장하는 방법

log4j란Log4j는 Java 기반의 로깅 프레임워크입니다. Log4j는 다양한 로깅 기능을 제공하여 애플리케이션의 디버깅과 모니터링을 돕습니다. Log4j는 그 유연성과 확장성 덕분에 많은 Java 애플리케이션

jiione.tistory.com](https://jiione.tistory.com/13)

1. logback-spring.xml 설정 추가

  • 보내고 싶은 log의 Appender를 작성하여 class="net.logstash.logback.appender.LogstashTcpSocketAppender"를 넣습니다.
  • destination은 로그를 보낼 ip 주소와 port를 넣습니다. 저는 docker끼리 통신할 예정이라 <컨테이너명>:<포트번호> 으로 지정했습니다.
  • 아래는 경매 로그와 사용자 로그를 따로 저장하기 위해서 다른 포트 번호를 사용했습니다.

logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="AUCTION_LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">

        <destination>logstash:5044</destination>

        <encoder class="net.logstash.logback.encoder.LogstashEncoder" />
        <keepAliveDuration>5 minutes</keepAliveDuration>
    </appender>

    <appender name="USER_LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">

        <destination>logstash:5050</destination>

        <encoder class="net.logstash.logback.encoder.LogstashEncoder" />
        <keepAliveDuration>5 minutes</keepAliveDuration>
    </appender>

    <root level="info">
        <appender-ref ref="CONSOLE" />
    </root>


    <logger name="AuctionServiceLogger" level="debug" additivity="false">

        <appender-ref ref="AUCTION_LOGSTASH" />
        <appender-ref ref="CONSOLE" />
    </logger>


    <logger name="UserServiceLogger" level="debug" additivity="false">
        <appender-ref ref="USER_LOGSTASH" />
        <appender-ref ref="CONSOLE" />
    </logger>
</configuration>

2. Logstash 설정 추가

  • input
    위의 logback-spring.xml 에서 5044port와 5050port로 보냈으므로 해당 포트들을 열어줍니다.
    또한, log들을 elastic search에 각각 따로 저장하기 위해서 type을 지정해주었습니다.
  • filter
    logstash console을 보고 전해준 데이터가 전부 message에 저장되어서, grok을 활용하여 message를 파싱해주고 필요없는 message는 삭제 하였습니다.
  • output
    if문을 통해서 input에서 지정해준 type을 통해서 각각 다른 index에 저장해주었습니다.
    마찬가지로 저는 docker를 사용하여 포트번호를 컨테이너 명으로 해주었습니다.

logstash.conf

input {
  tcp {
    port => 5044
    codec => json_lines
    type => "auction_log"
  }

  tcp {
    port => 5050
    codec => json_lines
    type => "user_log"
  }
}

filter {
  if [type] == "auction_log" {
    if "BidLogDTO" in [message] {
      grok {
        match => {
          "message" => "BidLogDTO\(userId=%{NUMBER:user_id}, postId=%{NUMBER:post_id}, bidAmount=%{NUMBER:bid_amount}, category=%{WORD:category}\)"
        }
      }

      mutate {
        convert => {
          "user_id" => "integer"
          "post_id" => "integer"
          "bid_amount" => "integer"
        }
        remove_field => ["message"]
      }
    } else {
      drop { }
    }
  }

  if [type] == "user_log" {
    if "UserExchangeLogDTO" in [message] {
      grok {
        match => {
           "message" => "UserExchangeLogDTO\(userId=%{NUMBER:user_id}, exchangeAmount=%{NUMBER:exchange_amount}, payType=%{WORD:pay_type}, payStatus=%{WORD:pay_status}\)"
        }
      }

      mutate { 
        remove_field => ["message"]  
      }
    } else {
      drop { }
    }
  }
}

output {
  if [type] == "auction_log" {
    elasticsearch {
      hosts => ["http://elasticsearch:9200"]
      index => "auction_log"
    }
  }

  if [type] == "user_log" {
    elasticsearch {
      hosts => ["http://elasticsearch:9200"]
      index => "user_log"
    }
  }
}

3. Service Class

그리고 아래와 같이 log를 작성해주면 끝입니다.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public void recordBidLog(BidLogDTO bidLogDTO) {
        Logger logger = LoggerFactory.getLogger("AuctionServiceLogger");
        logger.info("{}", bidLogDTO);
    }