[ Spring ] 자바 로깅( Slf4j, Logback, Log4j, Log4j2 )과 logback 설정 방법
목차
1. 로깅 프레임워크
SLF4J
SLF4J는 Simple Logging Facade의 약자로 프레임워크라기 보다는 여러 로깅 프레임워크의 인터페이스 역활을 합니다.
로깅 프레임워크는 SLF4J 규약에 맞게 구현을 하고, 개발자는 SLF4J 인터페이스를 호출하여 원하는 프레임워크를 사용하면 됩니다.
직접 로깅 프레임워크를 사용하지 않고, SLF4J라는 인터페이스를 사용하는 이유는 여러 이유로 로깅 프레임워크를 변경하게 될 경우
로깅 프레임워크 의존성만 변경하면 코드 변경 없이 전환이 가능하기 때문입니다.
Log4j
가장 오래된 로깅 프레임워크지만, 15년도 부터 더 이상 개발이 되지 않고 있는 프레임워크로 사용하지 않는 것이 좋습니다.
Logback
Log4j 이후에 출시한 프레임워크로 성능이 더 향상되어 가장 널리 사용되고 있는 로깅 프레임워크 중 하나입니다.
SpringBoot의 spring-boot-start-web에 포함되어 있는 프레임워크 입니다.
Log4j 2
가장 최근에 나온 로깅 프레임워크로 이름에서 알수 있듯이 Log4j의 후속 버전입니다.
Logback과 마찬가지로 많은 기능들을 지원하며, 가장 큰 차별점은 멀티 쓰레드 환경에서 Async Logger의 경우
다른 프레임워크보다 처리량이 월등하여, 대기시간이 훨씬 짧습니다.
그리고 람다식 및 Lazy Evaluation도 지원합니다.
2. 로그 레벨
FATAL | 아주 심각한 에러가 발생한 상태. 시스템적으로 심각한 문제가 발생해서 어플리케이션작동이 불가능할 경우가 해당하는데, 일반적으로는 어플리케이션에서는 사용할 일이 없음 |
ERROR | 요청을 처리하는 중 문제가 발생한 상태를 나타냄 |
WARN | 처리 가능한 문제이지만, 향후 시스템 에러의 원인이 될 수 있는 경고성 메시지를 나타냄 |
INFO | 로그인, 상태변경과 같은 정보성 메시지를 나타냄 |
DEBUG | 개발시 디버그 용도로 사용한 메시지를 나타냄 |
TRACE | log4j1.2.12에서 신규 추가된 레벨로서, DEBUG 레벨이 너무 광범위한 것을 해결하기 위해서 좀 더 상세한 상태를 나타냄 |
로그 레벨의 순서는 FATAL > ERROR > WARN > INFO > DEBUG > TRACE 와 같습니다.
로그 레벨을 DEBUG로 설정하면 DEBUG ~ FATAL까지( TRACE제외 ) 모두 logging이 되어집니다.
3. 의존성
Spring Boot 프레임워크 에서는 Common Logging Facade 를 기본으로 사용하고 있고,
SLF4j Facade 사용을 위해서는 Common Logging을 exclude 해주고, SLF4j를 추가해주는 작업을 해주어야합니다.
하지만 spring 5부터는, Spring-JCL 모듈이 추가되어, 별도로 exclude를 해줄 필요 없이 SLF4j를 사용할 수 있게 되었습니다.
그래서 Spring Boot starter web 의존성만 추가하면 SLF4j와 Logback을 사용할 수 있게 되었습니다.
( starter web에 Spring-JCL 의존성이 있습니다. )
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
4. 로그 출력
간단하게 lombok의 @Slf4j 를 선언하여 logger를 만들어, logback으로 로그를 출력할 수 있습니다.
package com.example.logbacksample;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class DemoApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
log.error("error log");
log.warn("warn log");
log.info("info log");
log.debug("debug log");
log.trace("trace log");
}
}
2022-01-13 10:14:26.855 INFO 44317 --- [ main] c.e.logbacksample.DemoApplicationRunner : info log
5. 로그 설정
위의 예제를 보면, 5개의 로그 출력 중 Info 로그만 출력되고 있습니다.
별도 로그 레벨을 지정하지 않은 경우에는 Info 레벨의 로그만 출력이 되고,
로그 레벨을 설정하게 되면, 위에서 언급된 것과 같이 해당 레벨과 그 보다 높은 레벨의 로그가 출력이 됩니다.
5-1. application.yaml 설정
logback 일반 설정은 application.properties나 yml 파일에서 설정 가능합니다.
App의 전체 로그 레벨을 debug로 설정을 하면, App에서 구현한 로그 뿐만 아니라 App에서 포함하고 있는 여러 의존성에 있는 DEBUG 레벨 이상의 로그들이 모두 출력되게 됩니다.
logging:
level:
root: debug
2022-01-13 15:34:45.200 ERROR 74413 --- [ main] c.e.logbacksample.DemoApplicationRunner : error log
2022-01-13 15:34:45.200 WARN 74413 --- [ main] c.e.logbacksample.DemoApplicationRunner : warn log
2022-01-13 15:34:45.200 INFO 74413 --- [ main] c.e.logbacksample.DemoApplicationRunner : info log
2022-01-13 15:34:45.200 DEBUG 74413 --- [ main] c.e.logbacksample.DemoApplicationRunner : debug log
2022-01-13 15:34:45.201 DEBUG 74413 --- [ main] o.s.b.a.ApplicationAvailabilityBean : Application availability state ReadinessState changed to ACCEPTING_TRAFFIC
2022-01-13 15:34:45.521 DEBUG 74413 --- [3)-192.168.0.14] sun.rmi.transport.tcp : RMI TCP Connection(3)-192.168.0.14: (port 65156) op = 82
아래와 같이 특정 패키지의 로그 레벨을 설정해줄 수도 있습니다.
logging:
level:
com:
example:
logbacksample: warn
2022-01-13 15:40:28.553 INFO 75213 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 692 ms
2022-01-13 15:40:28.817 INFO 75213 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2022-01-13 15:40:28.826 ERROR 75213 --- [ main] c.e.logbacksample.DemoApplicationRunner : error log
2022-01-13 15:40:28.826 WARN 75213 --- [ main] c.e.logbacksample.DemoApplicationRunner : warn log
그 외에도, 파일 이름이나 경로 롤링 정책을 정의할 수 있습니다.
logging:
level:
com:
example:
logbacksample: warn
file:
path:
name:
logback:
rollingpolicy:
max-history:
max-file-size:
file-name-pattern:
5-2. logback-spring.xml
더 세부적인 Custom 설정은 logback.xml 과 logback-spring.xml 파일에서 설정할 수 있습니다.
logback.xml 과 logback-spring.xml은 서로 로딩되는 시점이 다른데 상세한 부분은 나중에 또 설명하도록 하겠습니다.
5-2-1. Log Pattern( 작성 중 )
appender > encoder > patter에 로그를 어떻게 출력 할지 패턴을 정의할 수 있습니다.
실제 xml 예제는 다음 챕터에서 확인 할 수 있고, 어떻게 정의하는지 먼저 살펴보도록 하겠습니다.
%d{yyyyMMdd HH:mm:ss.SSS, Asia/Seoul} [%thread] %-3level %logger{5} - %msg %n
5-2-1. CONSOLE 출력
일단 logback-spring.xml 파일을 resources 폴더 아래에 생성하여, 아래와 같이 로그 설정을 합니다.
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyyMMdd HH:mm:ss.SSS} [%thread] %-3level %logger{5} - %msg %n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
20220117 14:32:18.938 [main] INFO o.s.b.w.e.t.TomcatWebServer - Tomcat started on port(s): 8080 (http) with context path ''
20220117 14:32:18.946 [main] INFO c.e.l.LogbackSampleApplication - Started LogbackSampleApplication in 1.454 seconds (JVM running for 1.964)
20220117 14:32:18.947 [main] ERROR c.e.l.DemoApplicationRunner - error log
20220117 14:32:18.947 [main] WARN c.e.l.DemoApplicationRunner - warn log
20220117 14:32:18.947 [main] INFO c.e.l.DemoApplicationRunner - info log
먼저 appender 에 어떻게 로그를 출력할지 정의를 합니다.
appender는 Logback이 Logging event를 발행하도록 위임하는 객체로 로그를 어떻게 출력하고 관리를 할지 정의하는 객체라고 할 수 있습니다.
appender의 이름은 CONSOLE, class는 콘솔에 로그를 출력하기위한 값을 명시합니다.( ~~.ConsoleAppender )
그리고 콘솔에 어떤 식으로 로그를 출력할지 encoder의 pattern에 정의를 하였습니다.
그리고, root에 로그 레벨과 appender-ref로 사용할 appender를 명시해줍니다.
여기서는 먼저 정의한 CONSOLE이라는 appender를 참조하고 있습니다.
5-2-2. FILE 출력
RollingFileAppender class를 가지는 appender를 추가하여 파일에 로그를 출력을 해보겠습니다.
아래 설정은 CONSOLE, FILE 2 개의 appender를 사용하고 있기 때문에 로그는 콘솔과 파일에 출력이 됩니다.
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyyMMdd HH:mm:ss.SSS} [%thread] %-3level %logger{5} - %msg %n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>%d{yyyyMMdd HH:mm:ss.SSS} [%thread] %-3level %logger{5} - %msg %n</pattern>
</encoder>
<file>logs/logback.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logback.%d{yyyy-MM-dd}.gz</fileNamePattern>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
file에 로그를 쓸 파일 이름을 정의하고, rollingPolicy를 정의해줍니다.
rollingPolicy에는 TimeBasedRollingPolicy, SizeAndTimeBasedRollingPolicy,FixedWindowRollingPolicy 등이 있는데, 그 중에 TimeBasedRollingPolicy를 사용하였습니다.
이 policy는 가장 많이 사용되고 있는 rolling policy로 day, month와 같이 시간에 기반하여 rollover policy를 정의합니다.
위에서는 일자 별로 로그가 저장되며 최대 30개의 로그가 3GB까지 저장할 수 있도록 정의 하였습니다.
- fileNamePattern
: rollover된(저장된) 로그 파일의 이름을 정의합니다.
: %d는 conversion specifier로 {}안에 java.text.SimpleDateFormat 클래스에 정의된 date-and-time 패턴을 정의합니다.
: rollover period는 패턴의 값에 따라 정해집니다.
예를 들어 %d{yyyy-MM-dd} 인 경우 매일 로그가 저장 되고, %d{yyyy-MM-dd_HH} 인 경우 매 시간 저장 됩니다. - maxHistory( optional ) : 최대로 보관할 로그 파일의 개수 입니다.
- totalSizeCap( optional ) : 최대 저장할 수 있는 전체 로그의 크기 입니다. 저장된 로그의 크기가 이 크기보다 커지면 오래된 로그부터 삭제가 됩니다. 이 property를 사용하기 위해서는 maxHistory property 도 설정이 되어 있어야 합니다.
로그 파일에 대한 제한은 maxHistory가 먼저 적용이 되고, totalSizeCap이 두번째로 적용이 됩니다.
5-2-3. Default XML 사용하기
org.springframeworkboot.logging.logback 패키지에 4개의 사전 정의된 xml 파일이 있습니다.
base.xml, default.xml, console-appender.xml, file-appender.xml 입니다.
- defaults.xml
말그대로 기본 설정을 해주고 있으며, 콘솔과 파일 로그의 패턴을 property로 설정되어있습니다.
<?xml version="1.0" encoding="UTF-8"?> <included> ... <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/> <property name="CONSOLE_LOG_CHARSET" value="${CONSOLE_LOG_CHARSET:-${file.encoding:-UTF-8}}"/> <property name="FILE_LOG_PATTERN" value="${FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/> <property name="FILE_LOG_CHARSET" value="${FILE_LOG_CHARSET:-${file.encoding:-UTF-8}}"/> ... </included>
- console-appender.xml
콘솔 출력을 위한 appender가 정의되어 있습니다.
패턴과 문자셋은 defaults.xml에 정의되어 있습니다.
<?xml version="1.0" encoding="UTF-8"?> <included> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>${CONSOLE_LOG_PATTERN}</pattern> <charset>${CONSOLE_LOG_CHARSET}</charset> </encoder> </appender> </included>
- file-appender.xml
파일 출력을 위한 appender가 정의되어 있습니다.
<?xml version="1.0" encoding="UTF-8"?> <included> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <encoder> <pattern>${FILE_LOG_PATTERN}</pattern> <charset>${FILE_LOG_CHARSET}</charset> </encoder> <file>${LOG_FILE}</file> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <fileNamePattern>${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz}</fileNamePattern> <cleanHistoryOnStart>${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false}</cleanHistoryOnStart> <maxFileSize>${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB}</maxFileSize> <totalSizeCap>${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0}</totalSizeCap> <maxHistory>${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-7}</maxHistory> </rollingPolicy> </appender> </included>
- base.xml
위에서 언급한 기본 property 값과 console, file appender를 포함하여, 로그를 INFO 레벨로하여 두 appender를 사용하고 있습니다.
<?xml version="1.0" encoding="UTF-8"?> <included> <include resource="org/springframework/boot/logging/logback/defaults.xml" /> <property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}"/> <include resource="org/springframework/boot/logging/logback/console-appender.xml" /> <include resource="org/springframework/boot/logging/logback/file-appender.xml" /> <root level="INFO"> <appender-ref ref="CONSOLE" /> <appender-ref ref="FILE" /> </root> </included>
앞서 보신것과 같이 default로 콘솔과 파일에 로그를 출력할 수 있도록 기본적인 설정이 정의되어 있습니다.
base.xml만 포함을 시키면, 콘솔과 파일에 로그를 출력하게 되고
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
</configuration>
defaults.xml과 console-appender.xml를 포함하면 콘솔에 로그를 출력하게 됩니다.
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
5-2-4. profile 로그 정책 정의
위에서 설명했던 부분을 종합해서 프로파일 별로 로그 정책을 정의해보겠습니다.
일반적으로 로컬에는 콘솔, 개발/스테이징/상용에는 파일에 출력을 합니다.
콘솔, 파일 출력을 위한 사전 정의된 xml 파일들을 include 하고,
springProfile 로 profile 이름을 정의해 줍니다.
그리고, 각 profile에 사용할 appender 이름을 정의해주면 됩니다.
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<property name="LOG_FILE" value="logs/spring.log"/>
<include resource="org/springframework/boot/logging/logback/file-appender.xml"/>
<springProfile name="local">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</springProfile>
<springProfile name="dev">
<root level="INFO">
<appender-ref ref="FILE"/>
</root>
</springProfile>
</configuration>
LOG_FILE은 위와 같이 xml에 정의를 해도 되고, 아니면 application.yaml에 정의를 해줘도 됩니다.
logging:
file:
path: logs/spring.log
사용자 정의 appender를 별도로 정의하게 되면 xml에서 property 를 별도 지정하여, 로그 경로나 파일 이름을 설정할 수 있습니다.
5-2-5. 로그 파일 분리
마지막으로, default xml 파일들 처럼 로그 출력 방식을 각 파일에 정의하여 분리하여 구현을 해보겠습니다.
log폴더에 아래와 같이 파일을 생성하여, default xml에 있는 콘솔, 파일 appender 코드를 그대로 사용하도록 하겠습니다.
그리고, log폴더 아래에 별도로 추가한 xml을 include 하면 됩니다.
기본적으로 위에서 설명했던 default xml에서 사용했던 코드와 다를 것이 없습니다.
default xml 외에 별도 정의를 하고 싶으면 appender 별로 파일을 분리하여 생성/구현을 하면 된다는 것을 말씀드리기 위해 추가를 하였습니다.
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="log/console-appender.xml"/>
<property name="LOG_PATH" value="./logs/spring.log"/>
<include resource="log/file-appender.xml"/>
<springProfile name="local">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</springProfile>
<springProfile name="dev">
<root level="INFO">
<appender-ref ref="FILE"/>
</root>
</springProfile>
</configuration>
참조
https://logback.qos.ch/manual/appenders.html#TimeBasedRollingPolicy
https://www.tutorialspoint.com/log4j/log4j_logging_levels.htm
https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto-logging