Spring/Spring Boot

[Spring] 로그 프레임워크와 로그백이란 - 로깅에 대해 알아보자

민돌v 2022. 10. 24. 14:57

 

이번 포스팅은,

CS 요청건 처리 시, 과거 이력을 확인해야할 때, 로그를 효율적으로 남기지 못해 명확한 결과를 전달하지 못한 아쉬운 경험을 토대로

Spring Boot 를 이용하여 로깅을 설정해보기위한 시도 글입니다..ㅠ

 

실스코드 Github

 


📌 하고싶은 것

  • spring 으로 서버를 띄울 시 로그에, 요청 들어온
  • "API Method, API 주소, 요청 응답값" 을 로그로 찍고싶었다.

📌 현재

  • 현재는 default 로, sql 문과 hibernate 쿼리만 로그로 나오는 상황입니다.

📌 시도해 볼 것 및 키워드

  • logback-spring.xml 과 interceptor, 혹은 aop 를 이용해 요청을 가로체서 로그 남기기

 

 

 

🙏  내용이 길어져서 Spring로깅 인터페이스와, 구현체 및 로그백 설정에 대해서만 이번 포스팅에서 다뤘습니다. 

[순서]

  1. 로그란, 로글 레벨이란, slf4j 란
  2. logback-spring.xml 작성
    • 왜 logback-spring.xml 로 작성?
    • 어떻게 작성?
  1. 내가 원하는 로그 띄어주기

 

1. 📗 Spring 로그 남기기

Spring 에서 로그는 Sout(System,out.pringln()) 혹은 로깅 프레임워크로 남길 수 있습니다.

 

로깅에대해서 먼저 알아봅시다..!

 

로깅 프레임워크로는

  • Slf4j (인터페이스)
  • Logback
  • Lof4
  • nlog 등등이 존재합니다.

❓ 로깅 프레임워크 vs Sout

  • 로그 프레임워크를 사용하는 것이 아래와 같은 장점이 존재합니다.
  1. 츌룍 형식을 지정할 수 있음
  2. 로그 레벨에 따라 남기고 싶은 로그를 별도로 지정할 수 있음
  3. 콘솔뿐 아니라 파일이나, 네트퉈크등 로그를 별도의 위치에 남길 수 있음
  4. log 성능이 System.out 보다도 좋다고 합니다.

로그 레벨

  • 로깅과 sout 의 또다른 차이점은 로그 레벨입니다.
  • 로깅을 사용한다면 해당 로그가 어떤 로그레벨에 해당하는지 명시함으로써 비교적 명확한 구분을 가능토록 합니다.
레벨 설명
🔥 Fatal 매우 심각한 에러, 프로그램이 종료되는경우가 많아 거의 사용되지 않음
(logback 에는 fatal 설정이 아예 존재하지 않고, error 에 맵핑됩니다.)
⛔️ Error 의도하지 않은 에러가 발생한 경우
프로그램이 종료되지 않음
프로그램 내에서 개발자가 의도하지 않은 예외를 나타날 때 사용
⚠️ Warn 에러가 될 수 있는 잠재적 가능성이 있는 경우
Warn 로그가 발생했을 시, 알람을 통해 개발자가 크리티컬한 에러를 맞닥뜨리기 전에 확인할 수 있는 역할을 겸함
✅ Info 명확한 의도가  있는 정보성 로그
요구사항에 따라 시스템 동작을 보여줄 떄
⚙️ Debug Info 레벨보다 더 자세한 정보가 필요한 경우
주로 Develop 환경에서 사용
📑 Trace  Debug 레벨보다 더 자세한 내용을 포함
Dev 환경에서 버그를 해결하기위해 사용
최종 프로덕션이나 커밋에 포함되면 안된다고 합니다.

 


👏🏻 로깅 프레임워크에 대해서

Slf4j 란

  • Slf4j (Simple Logging Facade for Java) 라는 이름으로 java.util.logging, logbacklog4j 와 같은 로깅 프레임 워크에 대한 추상화(인터페이스) 역할을 하는 라이브러리 입니다.
  • 인터페이스 이기떄문에 단독으로 사용이 불가능합니다.
  • 최종 사용자가 배포시 원하는 구현체(logback, log4j 등)을 선택하여 사용가능합니다.

 

Slf4j 동작과정

  1. 개발할 때, slf4j api 를 사용하여 로깅 코드를 작성하면
  2. 배포할 때, 바인딩 된 Logging Framwork (구현체) 가 실제 로깅 코드를 수행합니다.

https://www.youtube.com/watch?v=1MD5xbwznlI&t=650s

Slfjj 자세한 동작 과정 및 각 모듈의 역할

Slf4j 사용하기

1) 의존성 추가

<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.3</version>
</dependency>

2) Slf4j 사용

  • 아래 코드를 베이스로 로그를 작성할 수 있지만,
  • 현재 slf4j 의 구현체가 없기 때문에 로그가 남겨지진 않습니다.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger(DemoApplication.class);
        
        logger.info("인포 로그! {}", "인포인포!");
        SpringApplication.run(DemoApplication.class, args);
    }
}

3) 구현체 추가

  • 의존성을 추가해서 라이브러리를 깔아두면, slf4j 에 맞는 구현체를 찾아서 바인딩합니다.
  • logback-classic 구현체를 사용하였는데, 해당 라이브러리는 자신에게 의존하는 logback-core-1.2.3.jar뿐만 아니라 slf4j-api-1.7.31.jar도 자동으로 가져온다고합니다.
  • 해당 artifact의 올바른 버전을 사용하는데 필요하기 때문에 모두 명시적으로 선언하는 것이 좋다고하는데 저는 연습할때는 귀찮아서 하지 않았습니다 !! ㅎㅎ^^
  <!-- 로깅 라이브러리 -->
  
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.3</version>
  </dependency>
  
  <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.4.4</version>
    <scope>test</scope>
  </dependency>
</dependencies>

4) 결과

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger(DemoApplication.class);

        logger.trace("trace 로깅이야!!! {}", "트레이스!");
        logger.debug("debug 로깅이야!!! {}", "디버그!");
        logger.info("info 로깅이야!!! {}", "인포인포!");
        logger.warn("warn 로깅이야!!! {}", "워닝!");
        logger.error("error 로깅이야!!! {}", "에러!");

        SpringApplication.run(DemoApplication.class, args);
    }
}

@slf4j 어노테이션을 사용해도 된다.

@Slf4j
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        log.trace("trace 로깅이야!!! {}", "트레이스!");
        log.debug("debug 로깅이야!!! {}", "디버그!");
        log.info("info 로깅이야!!! {}", "인포인포!");
        log.warn("warn 로깅이야!!! {}", "워닝!");
        log.error("error 로깅이야!!! {}", "에러!");

        SpringApplication.run(DemoApplication.class, args);
    }
}

 

slf4j는 기본적으로 debug가 default 값으로 잡혀있어 trace 로그를 띄어주기 위해서는 별도의 설정이 필요하다고 합니다.
// 변경 방법 출처
https://www.baeldung.com/spring-boot-logging

 

 

 

 

 


2. 📗 Logback

logback은 로깅 프레임워크 중하나로 slf4j 의 구현체 라이브러리 입니다.

Log4j 를 토대로 만든 프레임워크로, 현제 Spring Framwork 에서도 Slf4j 와 logback을 기본 라이브러리로 채택하고 있습니다.

 

 

1) 로그백 구성 모듈

로그백은 3개의 모듈로 나누어집니다.
  • logback-core 
    • logback-classic, logback-access 두 모듈의 기반 역할을 담당하며
    • appender, layout 인터페이스가 이 모듈에 속해 있습니다.
  • logback-classic 
    • logback-core를 가지며 slf4j API 를 구현하여, log4j 또는 java.util.logging(JUL)과 같은 다른 로깅 프레임워크 간에 쉽게 전환 가능하다록하며
    • Logger 클래스가 이 모듈에 속해 있습니다.
  • logback-access 
    • Servlet Container 와 통합되어 Tomcat 및 jetty 등 Servket 컨테이너에 HTTP Access 로그 기능을 제공합니다.
    • 웹 어플리케이션 레벨이 아닌 컨테이너 레벨에서 설치되어야 합니다.

 


2) 로그파일 작성하기 - logback-spring.xml 이란

slf4j 인터페이스로 log.info 콘솔에 찍을 수 있지만, 이 콘솔을 원하는대로 커스텀하기위해서는 별도의 설정파일을 작성해야합니다.

 

📌 application.yml 로도 설정이 가능하지만, 로그백 설정만 따로 분리해서 관리하기 위해 별도의 파일을 작성하는게 좋다고 합니다.

 

스프링 부트에서 로그백을 사용하기 위해서 다음과 같은 파일들을 로드해서 사용합니다.

  • logback-spring.xml
  • logback-spring.groovy
  • logback.xml
  • logback.groovy

클래스 경로의 파일에 다음 이름 중 하나가 있으면 Spring Boot 는 자동으로 기본 구성 위에 파일을 로드합니다.


💡 logback vs logback-spring

스프링 공식문서를 살펴보면 가능하다면 표준보다 "-spring" 을 붙여서 사용한느걸 추천한다고 합니다.

  • 표준 버전 즉, logback.xml 을 사용한다면 Spring 이 완변하게 로그 초기화를 제어하지 못한다고 합니다.
  • 아마도, logback.xml 파일은 너무 빨리 로드되기 때문에, 이 파일 안에선 Extensions을 사용할 수 없다고하는데 이 때문인 것 같습니다.
When possible, we recommend that you use the -spring variants for your logging configuration (for example, logback-spring.xml rather than logback.xml). If you use standard configuration locations, Spring cannot completely control log initialization.

 


 

logback-spring.xml 작성 방법

공식문서의 추천대로, 로그백 설정파일은 logback-spring,xml 파일을 "src/main/resources/logback-spring.xml" 경로에 만들면서 시작합니다.

 

logback-spring.xml

  • 기본 시작은 아래 틀에서 시작합니다.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!--  여기애 로그 설정 부분이 들어간다.  -->
</configuration>

logback-spring.xml 구성 요소

1) appender

  • appender는 로그가 출력되는 위치를 나타냅니다. XXXAppender 로 끝나는 클래스들이 존재하며 다양한 로그 출력 위치 및 방법을 제공합니다.
  • appender 의 종류는 다양하지만, 지금은 ConsoleAppender 만 사용하겠습니다.
  • ConsoleAppender는 콘솔에 System.out 또는 System.err를 이용하여 로그 이벤트를 append 합니다.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] %-5level %logger{36} - %msg%n</Pattern>
        </layout>
    </appender>
</configuration>

🔥 appender 블록을 이용하여 어디에 콘솔을 찍을건지 class 를 명시해주고, name 속성을 이용해서 변수처럼 사용할 수 있습니다,

➡️ layout은 콘솔의 형식을 정의합니다.

 

2) layout (encoder)

사실 위에서 사용되던 layout class 는 deprecated 된 방식입니다.

DEPRECATED (PatternLayout)
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
  <file>testFile.log</file>
  ...
  <layout class="ch.qos.logback.classic.PatternLayout">
    <pattern>%msg%n</pattern>
  </layout>
</appender>

 

📗 logback 버전 0.9.19 이후에로 로그의 모든 wirte 권한을 제어할 수 있는 encoder 등장 하였습니다.

  • Encoder는 로그 이벤트를 바이트 배열로 변환하고, 해당 바이트 배열을 OutputStream 에 쓰이는 작업을 담당합니다.
  • 이전 버전에서 appender는 이벤트 메시지를 문자열로 변환하는데 layout을, write 하는데 java.io.Writer를 사용해 왔습니다.
to (PatternLayoutEncoder)
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
  <file>testFile.log</file>
  ...
  <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
    <pattern>%msg%n</pattern>
  </encoder>
</appender> 

 

➡️ encoder 로 바꾼 예시 (간단)

<?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} %highlight(%-5level) %cyan(%logger{0}) - %msg%n
      </pattern>
    </encoder>
  </appender>

여기서 패턴은 다루지 않겠습니다. 구글링해보면 자세한 양식이 나옵니다!

 

3) springProfile

스프링 배포 버전 profile 에 따라서 로그 설정을 세분화할수도 있습니다.

전체 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} %highlight(%-5level) %cyan(%logger{0}) - %msg%n
      </pattern>
    </encoder>
  </appender>

  <property name="LOG_DIR" value="/var/log/was"/>

  <springProfile name="prod">
    <appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="FILE">
      <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} %highlight(%-5level) %logger{0} - %msg%n</pattern>
      </encoder>
      <file>${LOG_DIR}/app.log</file>

      <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <fileNamePattern>${LOG_DIR}-%d{yyyy-MM-dd}-%i-log.zip</fileNamePattern>
        <maxFileSize>100MB</maxFileSize>
        <maxHistory>90</maxHistory>
        <totalSizeCap>3GB</totalSizeCap>
      </rollingPolicy>
    </appender>
  </springProfile>

  <root level="info">
    <springProfile name="dev">
      <appender-ref ref="STDOUT"/>
    </springProfile>
    <springProfile name="prod">
      <appender-ref ref="STDOUT"/>
      <appender-ref ref="FILE"/>
    </springProfile>
  </root>
</configuration>

이렇게하면 prod 버전의 배포 환경일 때만, 그 하위 내용들이 적용되며, appender 또한 해당 profile이 아닐 때는 무시됩니다.

👏🏻 정리

  1. root 태그를 이용해 만들어둔 appender 들을 조합해서 사용할 수 있습니다.
    • <root level = "off"> 를 하게된다면 모든 로거가 무시됩니다.
  2. <springProfile> 속성을 이용해 배포 버전에 마다 다른 로거 설정을 세분화할 수 있습니다,.
  3. <appneder-ref> 를 이용해 만들어둔 appender 를 여기서 마치 변수처럼 사용할 수 있습니다.

 

 

 

 

 


*참고