[Spring] 로그 프레임워크와 로그백이란 - 로깅에 대해 알아보자
이번 포스팅은,
CS 요청건 처리 시, 과거 이력을 확인해야할 때, 로그를 효율적으로 남기지 못해 명확한 결과를 전달하지 못한 아쉬운 경험을 토대로
Spring Boot 를 이용하여 로깅을 설정해보기위한 시도 글입니다..ㅠ
📌 하고싶은 것
- spring 으로 서버를 띄울 시 로그에, 요청 들어온
- "API Method, API 주소, 요청 응답값" 을 로그로 찍고싶었다.
📌 현재
- 현재는 default 로, sql 문과 hibernate 쿼리만 로그로 나오는 상황입니다.
📌 시도해 볼 것 및 키워드
- logback-spring.xml 과 interceptor, 혹은 aop 를 이용해 요청을 가로체서 로그 남기기
🙏 내용이 길어져서 Spring로깅 인터페이스와, 구현체 및 로그백 설정에 대해서만 이번 포스팅에서 다뤘습니다.
[순서]
- 로그란, 로글 레벨이란, slf4j 란
- logback-spring.xml 작성
- 왜 logback-spring.xml 로 작성?
- 어떻게 작성?
- 내가 원하는 로그 띄어주기
1. 📗 Spring 로그 남기기
Spring 에서 로그는 Sout(System,out.pringln()) 혹은 로깅 프레임워크로 남길 수 있습니다.
로깅에대해서 먼저 알아봅시다..!
로깅 프레임워크로는
- Slf4j (인터페이스)
- Logback
- Lof4
- nlog 등등이 존재합니다.
❓ 로깅 프레임워크 vs Sout
- 로그 프레임워크를 사용하는 것이 아래와 같은 장점이 존재합니다.
- 츌룍 형식을 지정할 수 있음
- 로그 레벨에 따라 남기고 싶은 로그를 별도로 지정할 수 있음
- 콘솔뿐 아니라 파일이나, 네트퉈크등 로그를 별도의 위치에 남길 수 있음
- 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, logback 및 log4j 와 같은 로깅 프레임 워크에 대한 추상화(인터페이스) 역할을 하는 라이브러리 입니다.
- 인터페이스 이기떄문에 단독으로 사용이 불가능합니다.
- 최종 사용자가 배포시 원하는 구현체(logback, log4j 등)을 선택하여 사용가능합니다.
Slf4j 동작과정
- 개발할 때, slf4j api 를 사용하여 로깅 코드를 작성하면
- 배포할 때, 바인딩 된 Logging Framwork (구현체) 가 실제 로깅 코드를 수행합니다.
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이 아닐 때는 무시됩니다.
👏🏻 정리
- root 태그를 이용해 만들어둔 appender 들을 조합해서 사용할 수 있습니다.
- <root level = "off"> 를 하게된다면 모든 로거가 무시됩니다.
- <springProfile> 속성을 이용해 배포 버전에 마다 다른 로거 설정을 세분화할 수 있습니다,.
- <appneder-ref> 를 이용해 만들어둔 appender 를 여기서 마치 변수처럼 사용할 수 있습니다.
*참고
- 스프링부트 로깅 공식 문서 : https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.logging
- 우테코 로거 : https://tecoble.techcourse.co.kr/post/2021-08-07-logback-tutorial/
- 10 테크톡 검프님 블로그 : https://lovethefeel.tistory.com/90
- Spring logger Baeldung : https://www.baeldung.com/spring-boot-logging
- 로그백 시리즈별로 상세 정리 : https://lovethefeel.tistory.com/88#recentEntries