Spring
Spring Boot
Monitoring

스프링 모니터링

SLF4J

Java의 로깅을 위한 추상화 계층, 해당 추상화를 구현한(logback(spring boot default), log4j(옛날에 쓰던거)) 구현체를 개발자가 선택 하여서 로그 시스템을 일관적으로 적용할 수 있도록 해줍니다.

Logback

slf4j의 구현체,
logback은 비동기 로깅을 한다는 특징을 갖고 있는데 이는 별도의 스레드가 메시지를 처리하도록 하여, 로그 작성에 대한 지연이 애플리케이션의 주요 스레드에 영향을 미치지 않도록 합니다.

ℹ️

sout()에 비해서 빠르다. sout()으로 로그를 찍으면 메인 스레드와 동일한 스레드에서 로그를 출력하기 때문

기본 파일은 logback-spring.xml이며, logback.xml도 사용 가능합니다. 둘 중에 하나라도 있으면 그것을 쓰겠다는 의미입니다.

pattern

로그백의 PatternLayout.java를 들어가보면 patern 설정을 어떻게 해줘야할지에 대한 힌트를 얻을 수 있다.

위의 java 파일을 활용해서

<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} %class %method %line %msg%n</pattern>

패턴에 class, method, line을 추가해줄 수 있다.

Appender

로그를 어디에 출력할지에 대한 설정을 담당한다.

FileAppender

Rolling File Appender (opens in a new tab)를 사용한다.

ℹ️

👇 전체 설정 중에서 Rolling File Appender에 해당하는 부분, 파일 이름 형식, 파일 저장 기간 등을 설정하는데 이 때,
파일 이름 형식에 시간 단위를 넣어주게 되면 <maxHistory>에서 설정한 시간 30은 30일이 아니라 30시간이 된다.
파일 이름 형식에 .zip을 맨 마지막에 붙여주면 압축된 파일로도 저장을 해준다.

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
  <!-- Support multiple-JVM writing to the same log file -->
  <prudent>true</prudent>
  <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
    <maxHistory>30</maxHistory> 
    <totalSizeCap>3GB</totalSizeCap>
  </rollingPolicy>
 
  <encoder>
    <pattern>%-4relative [%thread] %-5level %logger{35} -%kvp -%msg%n</pattern>
  </encoder>
</appender> 
서버 이름 받아오기💁

최상단의 configuration 바로 아래에 springProperty를 추가해준다.

<springProperty scope="context" name="server-name" source="server.name"/>

server-name이라는 변수에 server 명을 받아오는데 여기에서 server.nameapplication.yml에 설정해준 server.name이다.

Appender별 출력설정⭐️

logback 설정파일에 보면 맨 아래에 \<root level="Some Level"\>이란 설정이 있는데, 여기에 File Appender, STDOUT Appender 등 추가해준 Appender의 name을 입력해줘야 출력 설정이 완료되는 것이다.

<root level="DEBUG">
    <appender-ref ref="STDOUT"/>
    <appender-ref ref="FILE"/>
    <appender-ref ref="STASH"/>
</root>

전체 코드👇

<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <springProperty scope="context" name="server-name" source="server.name"/>
        <!-- encoders are assigned the type
             ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
        <encoder>
            <!-- logger{36} : 36자 이내로 글자를 자르겠다 -->
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%class] [%method] [%line] %msg%n</pattern>
        </encoder>
    </appender>
 
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- Support multiple-JVM writing to the same log file -->
        <prudent>true</prudent>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logFile.%d{yyyy-hh-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
            <totalSizeCap>3GB</totalSizeCap>
        </rollingPolicy>
 
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%class] [%method] [%line] %msg%n</pattern>
        </encoder>
    </appender>
    <appender name="STASH" class="net.logstash.logback.appender.LogstashAccessTcpSocketAppender">
        <destination>localhost:6500</destination>
        <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
            <providers>
                <pattern>
                    <omitEmptyFields>true</omitEmptyFields>
                    <pattern>
                        {
                        "level": "%level",
                        "thread": "%thread",
                        "logger": "%logger",
                        "class": "%class",
                        "method": "%method" ,
                        "line": "%line" ,
                        "message": "%message",
                        "server-name": "${server-name}"
                        }
                    </pattern>
                </pattern>
            </providers>
        </encoder>
    </appender>
    <!-- 이 레벨에 따라서 출력되는 로그가 다름 ex : INFO, debug -->
    <root level="DEBUG">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
        <appender-ref ref="STASH"/>
    </root>
</configuration>

ELK

ElastciSearch, Logstash, Kibana의 약자로 로그를 수집하고 분석하고 시각화하는데 사용하는 오픈소스 도구이다.

  • ElastciSearch : 대용량 데이터를 실시간으로 처리해서 풀 텍스트 검색을 할 수 있게 해주는 라이브러리이다.
  • Logstash : 다양한 소스의 로그 또는 이벤트 데이터를 수집, 처리해서 ElastciSearch 같은 스토리지로 전송하는 파이프라인 도구이다.
  • Kibana : 저장된 데이터를 시각화하고 이 데이트를 기반으로 고급 데이터 분석을 수행하는 웹 인터페이스 도구이다.

Logback과 연동

Logstach를 통해서 Logback 로그를 보내주게 되면 (opens in a new tab) Logstash가 Kibana를 통해서 데이터를 표출해줄 수 있게 된다.
Maven Repository에 logstash logback을 검색해서 적당한 버전으로 불러와주고

Logback에 LogstashAccessTcpSocketAppender를 추가해준다.

Encoder 설정

Custom Encoder를 가져와서 설정에 추가 해준다. 이 때, 기존에 있던 설정에 encoder 부분만 바꿔줘야 한다.

기존 코드 👇

</appender>
<appender name="stash" class="net.logstash.logback.appender.LogstashAccessTcpSocketAppender">
    <destination>localhost:6500</destination>
 
    <!-- encoder is required -->
    <encoder class="net.logstash.logback.encoder.LogstashAccessEncoder"/>
</appender>

Encoder 부분만 수정 ✍️

<appender name="stash" class="net.logstash.logback.appender.LogstashAccessTcpSocketAppender">
    <destination>localhost:6500</destination>
    <!-- encoder is required -->
    <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
        <providers>
        </providers>
    </encoder>
</appender>

그리고 Encoder의 패턴을 지정해준다. 지금은 아래에 써져잇는 패턴을 이용해서 코드를 완성해주겠다.

결과적으로 완성된 코드 👇

<appender name="stash" class="net.logstash.logback.appender.LogstashAccessTcpSocketAppender">
    <destination>localhost:6500</destination>
    <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
        <providers>
            <pattern>
                <omitEmptyFields>true</omitEmptyFields>
                <pattern>
                    {
                      "logger": "%logger",
                      "level": "%level",
                      "thread": "%thread",
                      "message": "%message",
                      "traceId": "%mdc{traceId}"
                    }
                </pattern>
            </pattern>
        </providers>
    </encoder>
</appender>

Kibana 인덱스 설정

menu 👉 Analytics 👉 Discover



다음과 같이 설정하고 다시 Discover로 들어가보면 아래와 같이 로그가 잘 찍히는 것을 확인할 수 있다.

Spring Boot Actuator

Spring Admin

Spring Boot Actuator의 하위 프로젝트 같은 개념으로 대시보드를 만들 필요 없이 Default로 제공되는 대시보드 화면이다.
Dependency를 추가해주면 기본적으로 동작하게 된다.

ℹ️

Spring Admin은 Client가 Server로 데이터를 전송하는 방식이다.

서버 설정

서버는 설정할게 별로 없다. 디펜던시 추가만 해주고 SpringBootApplication을 시작하는 메인 클래스에 @EnableAdminServer를 추가해주면 된다.

@EnableAdminServer
@SpringBootApplication
class BootadminApplication
 
fun main(args: Array<String>) {
	runApplication<BootadminApplication>(*args)
}

Client 설정

참고 Commit (opens in a new tab)

모니터링 방식

ℹ️

PUSH 방식의 telegraflogstash에 해당한다고 생각하면 편하다.

Prometheus + Grafana☄️

  • Prometheus : 모니터링 시스템
  • Grafana : 시각화 도구(반드시 Prometheus와 함께 쓰는 것은 아니다.)

구성 방법 요약.jpg

Prometheus

Docker-Compose

docker-compose.yaml
version: '3'
 
services:
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    user: "$UID:$GID"
    ports:
      - "3000:3000"
    volumes:
      - ./grafana-data:/var/lib/grafana
    depends_on:
      - prometheus
prometheus.yml
scrape_configs:
  - job_name: 'rookedsysc-dev'
    metrics_path: '/actuator/prometheus'
    scrape_interval: 5s
    static_configs:
      - targets: ['192.168.0.11:8080']

Grafana

Prometheus + Grafana 연동

메뉴 -> Admin -> Data Sources -> Add data source -> Prometheus 선택

Dashboard

메뉴 -> Dash Board -> New -> Add Visualization

Default 계정

admin / admin

TICK Stack

  • Telegraf : InfluxData에서 개발한 플러그인 기반 서버 에이전트. 이 에이전트는 데이터를 수집하고, 변환하고, 여러 소스로 보내는 역할을 한다. 수백 개의 미리 만들어진 플러그인을 사용하여 여러 소스로부터 데이터를 수집하거나 자신의 플러그인을 작성할 수 있다.

  • InfluxDB : TICK 스택의 핵심 요소로, 고성능의 시계열 데이터베이스이다.

  • Chronograf : InfluxDB 데이터를 시각화하고 대시보드를 만드는 도구이다.

  • Kapacitor : InfluxDB 데이터를 처리하고, 경고를 생성하고, 데이터를 다른 시스템으로 보내는 역할을 한다.