[Java] Log..그리고 slf4j와 logback

lin·2023년 9월 20일
2

00씨 개발 서버 서비스 에러나던데 확인 좀 해볼래?
넵. 로그 좀 확인해볼게요~

그 로그..로그란 무엇인가..?

Log란?

애플리케이션 또는 운영체제의 실행 중 발생하는 다양한 이벤트에 대한 기록을 뜻한다. logging의 대상으로는 콘솔, 파일, 데이터 베이스 등이 있따.

Log가 필요한 이유!
애플리케이션의 문제 발생 시, 원인 분석을 위한 정보로 활용하기 위함
애플리케이션의 성능 분석
애플리케이션 사용 분석 및 통계를 위해서..

이 Log 그냥 생기는 것인가? 노노 기록을 해야지 기록이 되는 것..!

Log 관련 framework

초기 스프링은 JCL(Jakarta commons Logging)을 사용해 로깅을 구현했다. 요즘은 Log4j랑 Logback을 구현체로 많이 사용한다.

  • log4j : 자바 기반의 로깅 유틸리티로 Apache에서 만든 오픈소스 라이브러리
  • slf4j(Simple Logging Facade For Java) : 로깅 추상화 framework로 Facade 패턴으로 일관된 로깅 코드 작성이 가능함
  • logback: Log4j를 개발한 Ceki Gulcu가 기존에 사용되던 Log4j를 더 발전시킨 것, SLF4j 의 구현체

Logback은 스프링 부트의 기본으로 설정되어 있어서 사용시 별도의 라이브러리를 추가하지 않아도 된다.
spring-boot-starter-web 안에 spring-boot-starter-logging에 구현체가 있다.

SLF4J

slf4j(Simple Logging Facade For Java)는 로깅 추상화 라이브러리이다. 이 라이브러리는 로깅 시스템과의 결합을 줄이고, 로깅 시스템을 유현하게 교체할 수 있는 기능을 제공하여 편의성을 제공한다.

slf4j는 세가지 모듈로 구성되어 있다.

  • api : 로깅 인터페이스

  • binding : api의 구현체로 연결해주는 일을 한다.

  • bridging : jcl, log4j 등 로거들을 slf4j로 바꿔주는 역할을 한다.

slf4j는 인터페이스를 제공하여 개발자가 코드를 작성할 때 일관된 로깅 메서드를 사용할 수 있도록 한다. 추상화를 제공하고, 다양한 로깅 시스템을 지원하기 위해 설계되어있다.

slf4j 바인딩은 slf4j 인터페이스 호출을 특정 로깅 시스템의 호출로 변환하여 실제 로그를 기록한다. 따라서 slf4j 바인딩을 이용하여 Logback, Logj, java.util.logging 등 다양한 로깅 시스템을 지원할 수 있다!

따라서 로깅 시스템에 종속되지 않을 수 있는 것이다 ~~
보통 자바 애플리케이션을 만들 때 SLF4J를 사용하되 실제 구현체로는 logback을 자주 사용한다고 한다.

이런 식으로 사용할 수 있다.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.GetMapping;

@RestController
public class LogController {

    private final Logger logger = LoggerFactory.getLogger("LoggerController 의 로그");

    @GetMapping("/log")
    public void log() {
        logger.info("---------- Log 테스트 ---------");
    }
}
@Slf4j
public class Slf4jSample {

    public static void main(String[] args) {
        log.info("---------- Log 테스트 ---------");
    }

}

Logback

slf4j 오키오키....그래서 구현체 logback?

Logback을 이용하여 로깅을 수행하기 위해서 필요한 주요 설정요소로는 Logger, Appender, Encoder가 있다.

Logger

Logger는 로그의 주체로 메세지를 전달한다.
Logger는 실제 로그 기능을 수행하는 개체로 각 Logger마다 Name을 부여하여 사용한다. 각 Logger 마다 원하는 출력 레벨값을 설정할 수 있으며, 0개 이상의 Appender를 지정할 수 있다.

Appender

Appender는 로그를 출력할 위치, 출력 형식 등을 설정한다.
Logback-Core를 통해 사용할 수 있는 기본적인 Appender는 3가지이다.

  • ConsleAppender : 로그를 OutputStream에 기록하여, 최종적으로 콘솔에 출력
  • FileAppender : 로그의 내용을 지정된 파일에 기록
  • RollingFileAppender : FileAppender를 부터 상속받은 것으로, 날짜, 최대 용량 등을 설정하여 지정한 파일명 패턴에 따라 로그가 다른 파일에 기록되도록 한다.

Appender들의 하위 항목으로 출력 형식(Layout Pattern)을 지정하여 각 Appender마다 원하는 내용을 출력시킬 수 있다.

Encoder

어떻게 출력할지를 기술한다.
appender, encoder, layout의 조합으로 로그를 원하는 형식대로 구성할 수 있다.

Logback 관련 환경 설정

먼저 설정 파일 읽는 순서는 ~~
resources 밑에 logback-spring.xml 파일이 있으면 읽어간다.
없으면 .yml파일 읽어간다.
둘다 있으면 yml먼저 읽고 xml이 적용된다.

그래 로그 다 좋다 이말이지 그러면 로그를 어떻게 이쁘게 쌓을 수 있을까??
logback.xml이나 application.properties나...logback-spring.xml..을 통해서 이다.

logback-spring.xml에서는 Spring이 구동된 후라 application.properties에 있는 값들을 불러올 수 있다. logback.xml에서는 안됨!

따라서 logback-spring.xml로 보겠다.

configuration 태그는 내부에 최대 1개의 root 태그를 갖고, 0개 이상이 appender와 logger 태그를 가질 수 있다.

<logger>

Logger는 <logger> 태그를 통해서 구성된다. Logger는 필수 name 속성과 선택적으로 level과 additivity 속성을 가진다.
<logger> 태그는 0개 이상의 <appender-ref> 태그를 포함할 수 있다. 각 <appender-ref> 태그로 포함된 appender는 명명된 로거에 추가된다.

<root>

루트 로거는 <root> 태그를 통하여 구성된다. 루트 로거는 한 개의 level 속성만 허용된다. root니까 name도 없다.

<appender>

Appender는 name과 class 속성을 필수적으로 가져야만 한다. appender는 0개 또는 1개의 layout 태그와 encoder, filter 태그를 가질 수 있다. class 속성은 인스턴스화 시킬 appender 클래시를 명시하여야 한다.

기본적으로 appender는 누적되는 성질을 갖고 있다. logger는 자신에게 부착된 appender 이외에 자신의 상위 logger의 appender에게 로그를 전달한다. 따라서 같은 appender를 다른 logger에 등록하면 로그가 중복 충력될 수 있으므로 주의해야한다.

그래서 additivity=false로 설정하면 상위 로거의 appender로 전달되지 않는다.

변수 선언 및 치환 (Variable substitution)

configuration 파일에 변수를 선언하고 태그 구성에 사용할 수 있다. 변수는 <property> 태그를 이용하여 선언할 수 있다.

<configuration>
  <property name ="USER_HOME" value="/home" />
  <appender name = "FILE" class = "ch.qos.logback.core.FileAppender">
    <file>${USER_HOME}/main.log</file>
    <encoder><pattern>%msg%n</pattern></encoder>
  </appender>
  
  <root level = "debug">
    <appender-ref  ref="FILE" />
  </root>
</configuration>

[ 로깅 패턴 ]

%-5level : 로그 레벨, -5는 출력의 고정폭 값(5글자) (INFO, ERROR, DEBUG , 기타 등등이다.)
%d{날짜 형식 포맷} : 로그 기록시간 %d{yyyy-MM-dd HH:mm:ss} 을 사용하면 된다.
%thread : 현재 Thread 명
%F : 로깅을 발생시킨 파일 명
%M : 로깅을 발생시킨 메소드 명
%logger{length} : Logger name을 축약할 수 있다. length는 최대 자릿수이다 (0 = 무제한)
%line : 로깅이 발생된 라인 넘버%msg : - 로그 메시지
%n : 줄바꿈(new line)

로그 레벨
1. FATAL : 가장 크리티컬한 에러가 일어났을 때 사용
2. ERROR : 일반적인 에러가 났을 때 사용
3. WARN : 에러는 아니지만 주의할 필요가 있을 때 사용
4. INFO : 일반 정보를 나타낼 때 사용
5. DEBUG : 일반 정보를 상세히 나타낼 때 사용
6. TRACE : 쓰는 곳을 못봤다는데....난 왜 봤지? DEBUG보다 밑

나는 이걸 가지고 logack-spring 구경을 해보겠다.
git 출처 : https://github.com/eGovFramework/egovframe-msa-edu/blob/contribution/backend/user-service/src/main/resources/logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) %magenta(%-4relative) - [%thread{10}, %X{traceId:-}, %X{spanId:-}] %cyan(%logger{20}): %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 로컬에서는 로그를 전송하지 않도록 설정 -->
    <springProfile name="default">
        <root level="INFO">
            <appender-ref ref="STDOUT" />
        </root>
    </springProfile>
    <springProfile name="!default">
        <!-- java -Ddestination="localhost:5001" 와 같이 변경할 수 있다. cf 환경에서는 manifest.yml 파일에 환경변수로 추가 -->
        <property name="destination" value="${logstash_hostname:-localhost:5001}" />
        <property name="app_name" value="${app_name:-user-service}" />

        <!-- ELK - Logstash 로 로그를 전송하기 위한 appender -->
        <appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
            <destination>${destination}</destination>
            <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
                <providers>
                    <mdc/>
                    <context/>
                    <logLevel/>
                    <loggerName/>
                    <pattern>
                        <pattern>
                            {"appName": "${app_name}"}
                        </pattern>
                    </pattern>
                    <threadName/>
                    <message/>
                    <logstashMarkers/>
                    <stackTrace/>
                </providers>
            </encoder>
        </appender>
        <root level="WARN">
            <appender-ref ref="LOGSTASH" />
            <appender-ref ref="STDOUT" />
        </root>
    </springProfile>

</configuration>

요거는 콘솔에 균일하게 로그를 찍기위한 ConsoleAppender다.

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) %magenta(%-4relative) - [%thread{10}, %X{traceId:-}, %X{spanId:-}] %cyan(%logger{20}): %msg%n</pattern>
        </encoder>
    </appender>

2023-09-19 12:19:47.101 INFO 170258 - [boundedElastic-1, , ] o.e.c.a.f.GlobalFilter: [GlobalFilter Start] request ID: e0e1b17b-1, method: POST, path: /user-service/login

이런식으로 설정한 pattern대로 로그가 출력이 되는것! 예전엔 layout이나 pattern encoder class를 지정해서 사용했다고 한다.

그리고나서 보면 다른 appender들이 ref를 한다
<appender-ref ref="STDOUT" />

    <springProfile name="default">
        <root level="INFO">
            <appender-ref ref="STDOUT" />
        </root>
    </springProfile>

default에서는 콘솔에다가만 찍고 뭐 따로 file이나 어디 보내진 않겠다! 그리고 info 레벨부터 보겠다오

  <springProfile name="!default">
        <!-- ELK - Logstash 로 로그를 전송하기 위한 appender -->
        <appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
            ...
        </appender>
        <root level="WARN">
            <appender-ref ref="LOGSTASH" />
            <appender-ref ref="STDOUT" />
        </root>
    </springProfile>

default 아닐 때는 콘솔도 찍고 logstash도 보내자~~ 대신 warn부터만 보자!

이렇게 logback-spring.xml 파일의 내용이 얼추 보이기 시작했다.

spring boot 기본 설정

근데 spring boot 프로젝트로 플젝 실행하면 콘솔에 로그 다 찍히지않나??
xml yml 파일 없는데??

그거시

java/org/springframework/boot/logging/logback /DefaultLogbackConfiguration.java

class DefaultLogbackConfiguration {
	...
    .putProperty("CONSOLE_LOG_PATTERN", resolve(config, "${CONSOLE_LOG_PATTERN:-"
					+ "%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) "
					+ "%clr(${PID:- }){magenta} %clr(---){faint} %clr(${LOGGED_APPLICATION_NAME:-}[%15.15t]){faint} "
					+ "%clr(${LOG_CORRELATION_PATTERN:-}){faint}%clr(%-40.40logger{39}){cyan} "
					+ "%clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"));
}

이이렇게 기본으로 세팅된 것이 있기 때문에 ~~ 화면에 보인다 이말이다!
기본 세팅은 info구나~~~ 패턴은 저렇구나! 아하아하

이제 logstash...filebeat....elk에 대해서 나머지 정리를 해보겠다.

참고 자료::
https://hochoon-dev.tistory.com/entry/JAVA-Logback-%EC%82%AC%EC%9A%A9%EB%B2%95
https://chinggin.tistory.com/414
https://yangbongsoo.gitbook.io/study/undefined/log
https://ckddn9496.tistory.com/79
https://oingdaddy.tistory.com/78

profile
BE

0개의 댓글