Logback은 Java 기반 애플리케이션에서 로깅을 구현하기 위해 널리 사용되는 프레임워크
SLF4J(Simple Logging Facade for Java)의 구현체 중 하나로, 강력한 기능과 성능, 유연성을 제공
Logback은 기존의 로깅 프레임워크인 Log4j의 후속작
ConsoleAppender
: 콘솔에 log를 출력FileAppender
: 파일 단위로 log를 저장RollingFileAppender
: (설정 옵션에 따라) log를 여러 파일로 나누어 저장SMTPAppender
: log를 메일로 전송하여 기록DBAppender
: log를 DB에 저장프로젝트 구조
Spring의 profile 설정을 통해 profile 별로 logging 설정을 다르게 하는 방법
resources
폴더 내부에 로깅에 대한 설정을 하기 위한 logback-spring.xml
파일 생성
logback-spring.xml
<?xml version="1.0" encoding="utf-8" ?>
<!-- 부모 logback xml 파일 -->
<!--기존 Spring의 default logback 설정이 아래로 덮어 쓰여짐 -->
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<!-- spring profile 에 따라 다른 logback 설정이 필요 -->
<include resource="logback-spring-${spring.profiles.active}.xml"/>
</configuration>
<configuration>
을 통해 Spring의 기본 log 설정을 덮어 쓸 수 있다.${spring.profiles.active}
를 통해 실행된 profile의 정보를 받아 온다.defaults.xml
은 log의 기본적인 변수를 제공한다. 내부를 살펴 보면 아래와 같은 변수들이 설정 된 것을 확인할 수 있다. <?xml version="1.0" encoding="UTF-8"?>
<!--
Default logback configuration provided for import
-->
<included>
<conversionRule conversionWord="applicationName" converterClass="org.springframework.boot.logging.logback.ApplicationNameConverter" />
<!-- ... -->
<property name="CONSOLE_LOG_PATTERN" value="${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(%applicationName[%15.15t]){faint} %clr(${LOG_CORRELATION_PATTERN:-}){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="CONSOLE_LOG_THRESHOLD" value="${CONSOLE_LOG_THRESHOLD:-TRACE}"/>
<property name="FILE_LOG_PATTERN" value="${FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- %applicationName[%t] ${LOG_CORRELATION_PATTERN:-}%-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<property name="FILE_LOG_CHARSET" value="${FILE_LOG_CHARSET:-${file.encoding:-UTF-8}}"/>
<property name="FILE_LOG_THRESHOLD" value="${FILE_LOG_THRESHOLD:-TRACE}"/>
<logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR"/>
<!-- ... -->
</included>
Log Test를 위한 DemoController
생성
@Slf4j // logback 사용을 위한 Annotation
@RestController
public class DemoController {
@GetMapping("/demo")
public String demo() {
log.trace("log --> trace");
log.debug("log --> debug");
log.info("log --> info");
log.warn("log --> warn");
log.error("log --> error");
return "demo";
}
}
Local 환경에서 작업하는 경우 Console을 활용하여 Log를 확인하게 된다.
resouces
내부에 local 환경에서의 설정을 위한 logback-spring-local.xml
파일을 생성
logback-spring-local.xml
<included>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<!-- console-appender xml 내부 -->
<!-- <included>-->
<!-- <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">-->
<!-- <filter class="ch.qos.logback.classic.filter.ThresholdFilter">-->
<!-- <level>${CONSOLE_LOG_THRESHOLD}</level>-->
<!-- </filter>-->
<!-- <encoder>-->
<!-- <pattern>${CONSOLE_LOG_PATTERN}</pattern>-->
<!-- <charset>${CONSOLE_LOG_CHARSET}</charset>-->
<!-- </encoder>-->
<!-- </appender>-->
<!-- </included>-->
<!-- Appender 생성 예제 -->
<appender name="CONSOLE2" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<layout>
<pattern>
[CONSOLE2] [%-5level] %d{yyyy-MM-dd HH:mm:ss} [%thread] [%logger{20}:%line] - %msg%n
</pattern>
</layout>
</appender>
<!-- 로깅 레벨에 대한 설정 -->
<!-- 로깅 레벨에 대한 세부 설정은 사용하는 Appender에 따라 달라질 수 있음 -->
<root level="DEBUG">
<!-- 사용할 Appender 지정 (복수 가능) -->
<!-- <appender-ref ref="CONSOLE"/>-->
<appender-ref ref="CONSOLE2"/>
</root>
</included>
console-appender.xml
의 내부를 보면 앞서 있던 defaults.xml
의 변수를 활용하는 것을 확인할 수 있다.ConsoleAppender
클래스가 지정된 것을 확인할 수 있으며, Logging Level과 Pattern이 설정된 것을 볼 수 있다.<root>
에서는 전역으로 사용할 설정을 할 수 있다. logging level과 프로젝트 실행 중 사용할 Appender을 지정할 수 있다.[CONSOLE2] [INFO ] 2024-06-14 23:14:43 [http-nio-8080-exec-1] [w.d.l.c.DemoController:14] - log --> info
[CONSOLE2] [WARN ] 2024-06-14 23:14:43 [http-nio-8080-exec-1] [w.d.l.c.DemoController:15] - log --> warn
[CONSOLE2] [ERROR] 2024-06-14 23:14:43 [http-nio-8080-exec-1] [w.d.l.c.DemoController:16] - log --> error
실제 배포 환경에서 log를 활용한다면 console 보다는 파일을 활용하여 로그를 확인하게 된다. 따라서, FileAppender 을 활용하여 log를 File에 기록하는 법을 알아 보겠습니다.
logback-spring-prod.xml
생성
<included>
<property resource="logback-variables.properties"/>
<appender name="REQUEST1" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>
${LOG_DIR}/request1.log
</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/archive/request1.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
<maxFileSize>1KB</maxFileSize> <!-- 로그 파일 최대 크기 -->
<maxHistory>30</maxHistory> <!-- 로그 파일 최대 보관 주기 (단위: 일) -->
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
[REQUEST1] ${LOG_PATTERN}
</pattern>
<outputPatternAsHeader>true</outputPatternAsHeader>
</encoder>
</appender>
<appender name="REQUEST2" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>
${LOG_DIR}/request2.log
</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/archive/request2.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
<maxFileSize>10KB</maxFileSize> <!-- 로그 파일 최대 크기 -->
<maxHistory>30</maxHistory> <!-- 로그 파일 최대 보관 주기 (단위: 일) -->
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
[REQUEST2] ${LOG_PATTERN}
</pattern>
<outputPatternAsHeader>true</outputPatternAsHeader>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="REQUEST1"/>
<appender-ref ref="REQUEST2"/>
</root>
</included>
File에 log를 남기기 위하여 RollingFileAppender
클래스를 상속하여 Appender 생성
<file>
: log를 저장할 파일명 및 위치를 지정
<rollingPolicy>
: Rolling Log File의 크기 및 보관 시간을 지정하기 위한 policy를 지정
<fileNamePattern>
: 추가적인 로그 파일의 이름(서식) 및 위치 지정
archive
폴더에 이전 로그가 파일로 저장되는 것을 확인해 볼 수 있음<maxFileSize>
: 파일의 최대 크기 지정
<maxHistory>
: 파일의 보관 주기 (단위: day)
<encoder>
: 파일에 로그를 남길 패턴 등을 지정
<outputPatternAsHeader>
: 로그를 남길 때 어떤 패턴으로 남기는지를 확인하기 쉽도록 로그 파일의 상단에 log pattern 정보를 함께 기록<root>
: logging level 및 어떤 Appender를 사용할 것인가 등을 지정
logback-variables.properties
생성
logback-variables.properties
파일을 이용하여 prod 환경에서 사용하는 변수를 공통으로 관리하도록 하겠습니다.
LOG_DIR=logs
LOG_PATTERN=[%-5level] %d{yyyy-MM-dd HH:mm:ss} [%thread] [%logger{20}:%line] - %msg%n
logback-spring-prod.xml
파일의 2번 라인을 보면 property resource
에 logback-variables.properties
를 사용할 것임을 선언REQUEST1 Append의 logging 결과 파일
#logback.classic pattern: [REQUEST1] [%-5level] %d{yyyy-MM-dd HH:mm:ss} [%thread] [%logger{20}:%line] - %msg%n
[REQUEST1] [INFO ] 2024-06-14 23:39:31 [main] [w.d.l.SpringLogbackApplication:50] - Starting SpringLogbackApplication using Java 17.0.10 with PID 24612 (D:\git\cs_diary\spring\spring-logback\build\classes\java\main started by uhyeon in D:\git\cs_diary\spring\spring-logback)
[REQUEST1] [INFO ] 2024-06-14 23:39:31 [main] [w.d.l.SpringLogbackApplication:660] - The following 1 profile is active: "prod"
[REQUEST1] [INFO ] 2024-06-14 23:39:32 [main] [o.s.b.w.e.t.TomcatWebServer:111] - Tomcat initialized with port 8080 (http)
[REQUEST1] [INFO ] 2024-06-14 23:39:32 [main] [o.a.c.c.StandardService:173] - Starting service [Tomcat]
[REQUEST1] [INFO ] 2024-06-14 23:39:32 [main] [o.a.c.c.StandardEngine:173] - Starting Servlet engine: [Apache Tomcat/10.1.24]
[REQUEST1] [INFO ] 2024-06-14 23:39:32 [main] [o.a.c.c.C.[.[.[/]:173] - Initializing Spring embedded WebApplicationContext
[REQUEST1] [INFO ] 2024-06-14 23:39:32 [main] [o.s.b.w.s.c.ServletWebServerApplicationContext:296] - Root WebApplicationContext: initialization completed in 756 ms
[REQUEST1] [INFO ] 2024-06-14 23:39:32 [main] [o.s.b.w.e.t.TomcatWebServer:243] - Tomcat started on port 8080 (http) with context path '/'
[REQUEST1] [INFO ] 2024-06-14 23:39:32 [main] [w.d.l.SpringLogbackApplication:56] - Started SpringLogbackApplication in 1.463 seconds (process running for 1.897)
[REQUEST1] [INFO ] 2024-06-14 23:40:05 [http-nio-8080-exec-1] [o.a.c.c.C.[.[.[/]:173] - Initializing Spring DispatcherServlet 'dispatcherServlet'
[REQUEST1] [INFO ] 2024-06-14 23:40:05 [http-nio-8080-exec-1] [o.s.w.s.DispatcherServlet:532] - Initializing Servlet 'dispatcherServlet'
[REQUEST1] [INFO ] 2024-06-14 23:40:05 [http-nio-8080-exec-1] [o.s.w.s.DispatcherServlet:554] - Completed initialization in 0 ms
[REQUEST1] [INFO ] 2024-06-14 23:40:05 [http-nio-8080-exec-1] [w.d.l.c.DemoController:14] - log --> info
[REQUEST1] [WARN ] 2024-06-14 23:40:05 [http-nio-8080-exec-1] [w.d.l.c.DemoController:15] - log --> warn
[REQUEST1] [ERROR] 2024-06-14 23:40:05 [http-nio-8080-exec-1] [w.d.l.c.DemoController:16] - log --> error
MDC 를 활용하여 로깅하는 방법
MDC Controller의 생성
@Slf4j
@RestController
public class MdcController {
@GetMapping("/mdc")
public String mdc() {
MDC.put("job", "dev"); // key, value
log.trace("log --> trace");
log.debug("log --> debug");
log.info("log --> info");
log.warn("log --> warn");
log.error("log --> error");
MDC.clear();
return "mdc";
}
}
MDC
의 key, value를 이용하여 로깅하는 방법입니다.put
을 이용하여 값을 등록clear
을 사용하여 메소드 종료시 MDC
를 clear 할 필요가 있음logback-spring-prod.xml
에 MDC 설정 추가
<included>
<property resource="logback-variables.properties"/>
<!-- 추가 하는 부분 -->
<appender name="MDC" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>
${LOG_DIR}/mdc.log
</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/archive/mdc.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
<maxFileSize>10KB</maxFileSize> <!-- 로그 파일 최대 크기 -->
<maxHistory>30</maxHistory> <!-- 로그 파일 최대 보관 주기 (단위: 일) -->
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- %X 를 통해 MDC 안의 값을 key 로 조회 가능 -->
<pattern>
[MDC] %X{job}%n
</pattern>
<outputPatternAsHeader>true</outputPatternAsHeader>
</encoder>
</appender>
<root level="INFO">
<!-- MDC Appender 사용 설정 -->
<!-- 추가 하는 부분 -->
<appender-ref ref="MDC"/>
</root>
</included>
<encoder><pattern>
을 보면 [MDC] %X{job}%n
에서 {job}
은 MDC.put
을 이용하여 등록한 값이다.INFO
level로 설정한다면 INFO < WARN < ERROR
의 로그가 전부 출력됨logback-spring-prod.xml
에 Logging Filter 설정 추가
<included>
<!-- 추가 하는 부분 -->
<appender name="WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>
${LOG_DIR}/warn.log
</file>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>warn</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/archive/warn.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
<maxFileSize>10KB</maxFileSize> <!-- 로그 파일 최대 크기 -->
<maxHistory>30</maxHistory> <!-- 로그 파일 최대 보관 주기 (단위: 일) -->
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- %X 를 통해 MDC 안의 값을 key 로 조회 가능 -->
<pattern>
[WARN] ${LOG_PATTERN}
</pattern>
<outputPatternAsHeader>true</outputPatternAsHeader>
</encoder>
</appender>
<appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>
${LOG_DIR}/error.log
</file>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>error</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/archive/error.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
<maxFileSize>10KB</maxFileSize> <!-- 로그 파일 최대 크기 -->
<maxHistory>30</maxHistory> <!-- 로그 파일 최대 보관 주기 (단위: 일) -->
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- %X 를 통해 MDC 안의 값을 key 로 조회 가능 -->
<pattern>
[ERROR] ${LOG_PATTERN}
</pattern>
<outputPatternAsHeader>true</outputPatternAsHeader>
</encoder>
</appender>
<root level="INFO">
<!-- 추가 하는 부분 -->
<appender-ref ref="WARN"/>
<appender-ref ref="ERROR"/>
</root>
</included>
WARN
, ERROR
에 대한 별도의 Appender를 생성<filter>
의 LevelFilter
를 사용하여 WARN
, ERROR
로깅 Level에 대한 필터를 생성<level>
: 필터링할 Logging Level 설정<onMatch>
: 일치하는 조건에 대한 필터링<onMismatch>
: 일치하지 않는 조건에 대한 필터링WARN
Appender 결과에 대한 로깅 파일
#logback.classic pattern: [WARN] [%-5level] %d{yyyy-MM-dd HH:mm:ss} [%thread] [%logger{20}:%line] - %msg%n
[WARN] [WARN ] 2024-06-14 23:40:05 [http-nio-8080-exec-1] [w.d.l.c.DemoController:15] - log --> warn
<root>
이외에 <logger>
을 생성하여 로그를 기록할 수 있다.
logback-spring-prod.xml
에 Logger 생성 설정 추가
<included>
<!-- 추가 하는 부분 -->
<appender name="QUERY1" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>
${LOG_DIR}/query.log
</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/archive/query.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
<maxFileSize>10KB</maxFileSize> <!-- 로그 파일 최대 크기 -->
<maxHistory>30</maxHistory> <!-- 로그 파일 최대 보관 주기 (단위: 일) -->
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
[QUERY] ${LOG_PATTERN}
</pattern>
<outputPatternAsHeader>true</outputPatternAsHeader>
</encoder>
</appender>
<!-- 추가 하는 부분 -->
<!-- additivity 는 상위 로거의 속성 들로 merge 할 것인가 지정 -->
<logger name="SQL_LOG1" level="INFO" additivity="false">
<appender-ref ref="QUERY1"/>
</logger>
<logger name="SQL_LOG2" level="INFO" additivity="false">
<appender-ref ref="QUERY1"/>
</logger>
</included>
<logger>
생성을 보면 Logging Level과 additivity
설정을 확인할 수 있음 additivity를 false로 하여 상위 Logger와의 merge를 막음SQL_LOG1
사용을 위한 QueryController1
생성
@Slf4j(topic = "SQL_LOG1") // topic 으로 logger 를 지정
@RestController
public class QueryController1 {
@GetMapping("/query1")
public String query1() {
log.trace("log --> trace");
log.debug("log --> debug");
log.info("log --> info");
log.warn("log --> warn");
log.error("log --> error");
return "query1";
}
}
@Slf4j
에 topic
을 SQL_LOG1
으로 logger를 지정하여 출력할 수 있음SQL_LOG2
사용을 위한 QueryController2
생성
@RestController
public class QueryController2 {
public static final Logger log = LoggerFactory.getLogger("SQL_LOG2");
@GetMapping("/query2")
public String query2() {
log.trace("log --> trace");
log.debug("log --> debug");
log.info("log --> info");
log.warn("log --> warn");
log.error("log --> error");
return "query2";
}
}
LoggerFactory
에 getLogger
를 통해 logger
를 지정할 수 있음