로그를 기록하도록 시스템을 구성하는 작업
목적
println : 로깅의 일종
동기적 실행으로 인한 성능 저하 유발, 관리의 번거로움, 정보 제공의 편의성 떨어짐 으로 인해 실무에서 사용되지 않고 있는 방법
추가
동기 로깅 특징
System.out 느려지면 코드 실행 지연비동기 로깅(Logback + AsyncAppender)
Logback 자체는 기본적으로 동기방식이지만 AsyncAppender 등 붙이면 비동기 동작 설계 가능
큐에 기록 요청만 넣음
백그라운드 쓰레드가 실제 출력
성능, 안정성 장점
Java 로깅 라이브러리(logback) 이용


- 예시 : DBAppender 정의됨
- Logger가 DBAppender를 참고해서 DB에 로그를 출력하는 모습

"이것은 디버그 로그다" 문구를 형식에 맞게 출력하는 예제 (색깔)


로그 레벨 : 로그 메세지가 나타내는 중요도
Logback 제공하는 Log level 5가지
| Level | 설명 |
|---|---|
| TRACE | DEBUG 보다 훨씬 상세 정보 표시 |
| DEBUG | 프로그램을 디버깅하기 위한 정보 표시 |
| INFO | 상태 변경과 같은 정보성 로그 표시 |
| WARN | 처리 가능한 문제. 향후 시스템 에러 원인이 될 수 있는 경고성 메시지 표시 |
| ERROR | 요청을 처리하는 중 오류가 발생한 경우 표시 |
중요도 : TRACE < DEBUG < INFO < WARN < ERROR
로그 레벨을 설정할 경우 특정 레벨 이상의 로그만 출력됨

Spring Boot는 기본적으로 Logback 사용하도록 설계
사용자 생성 로그를 직접 출력 가능
@RestController
@RequiredArgsConstructor
public class WebController {
private final WebService racingService;
@GetMapping
public void log() {
// 1. 로거 객체 정의
Logger logger = LoggerFactory.getLogger(WebController.class);
// 2. 메서드 호출
logger.info("info 로깅이야!!!");
}
@Slf4j
@RestController
@RequiredArgsConstructor
public class WebController {
private final WebService racingService;
@GetMapping
public void log() {
// 로거 정의를 자동으로 해주기 때문에 메소드만 호출하면 됨
log.trace("trace 로깅이야!!!");
log.debug("debug 로깅이야!!!");
log.info("info 로깅이야!!!");
log.warn("warn 로깅이야!!!");
log.error("error 로깅이야!!!");
}
스프링 부트 기본 로그 레벨 설정 INFO
로그레벨 설정, Appender 및 Filter 적용 위해서는 logback-spring.xml 설정 파일 작성 필요
/resources/logback-spring.xml<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
<!-- Appender : ConsoleAppender -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- Encoder : PatternLayoutEncoder -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- 출력하고자 하는 로그의 패턴 지정 -->
<pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative] %-5level ${PID:-} --- [%15.15thread] %-40.40logger{36} :</pattern>
</encoder>
</appender>
<!-- 로거의 일종으로 로그 중 최상위 계층의 로그(모든 경로에서 발생하는 로드 루트 로거 출력) -->
<!-- STDOUT 이라는 이름의 Appender를 Root Logger에서 참조하고 있는 형식 -->
<!-- Root Level을 WARN으로 지정했기에 WARN과 Error 로그만 출력 -->
<root level="WARN">
<appender-ref ref="STDOUT" />
</root>
</configuration>
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
<!-- Appender : ConsoleAppender -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- filter 추가 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- WARN만 허가하고 나머지 거부하는 설정 -->
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- Encoder : PatternLayoutEncoder -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- 출력하고자 하는 로그의 패턴 지정 -->
<pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative] %-5level ${PID:-} --- [%15.15thread] %-40.40logger{36} :</pattern>
</encoder>
</appender>
<!-- 로거의 일종으로 로그 중 최상위 계층의 로그(모든 경로에서 발생하는 로드 루트 로거 출력) -->
<!-- STDOUT 이라는 이름의 Appender를 Root Logger에서 참조하고 있는 형식 -->
<!-- Root Level을 WARN으로 지정했기에 WARN과 Error 로그만 출력 -->
<root level="WARN">
<appender-ref ref="STDOUT" />
</root>
</configuration>
<!-- RollingFileAppender : 특정 기준으로 로그를 작성하는 타깃 파일을 변경해주는 Appender -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<!-- Encoder : PatternLayoutEncoder -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- 출력하고자 하는 로그의 패턴 지정 -->
<pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative] %-5level ${PID:-} --- [%15.15thread] %-40.40logger{36} :</pattern>
</encoder>
<!-- 타깃을 변경하는 기준 rollingPolicy -->
<!-- TimeBasedRollingPolicy를 사용하여 일정 주기로 새로운 파일 생성 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 생성한 로그 파일의 이름 정의, "%d" 는 로그 파일 주기설정 => 현재 매일 생성 -->
<fileNamePAttern>${LOGS_ABSOLUTE_PATH}/logFile_%d{yyyy-MM-dd}.log</fileNamePAttern>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
</appender>
<!-- racingcar 이름의 logger 정의, INFO 레벨 가짐 -->
<!-- logfile에는 racingcar package 내부에서 발생하는 로그만 출력됨 -->
<logger name="racingcar" level="INFO">
<appender-ref ref="FILE" />
</logger>
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
<!-- Appender : ConsoleAppender -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- filter 추가 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- WARN만 허가하고 나머지 거부하는 설정 -->
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- Encoder : PatternLayoutEncoder -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- 출력하고자 하는 로그의 패턴 지정 -->
<pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative] %-5level ${PID:-} --- [%15.15thread] %-40.40logger{36} :</pattern>
</encoder>
</appender>
<!-- RollingFileAppender : 특정 기준으로 로그를 작성하는 타깃 파일을 변경해주는 Appender -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<!-- Encoder : PatternLayoutEncoder -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- 출력하고자 하는 로그의 패턴 지정 -->
<pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative] %-5level ${PID:-} --- [%15.15thread] %-40.40logger{36} :</pattern>
</encoder>
<!-- 타깃을 변경하는 기준 rollingPolicy -->
<!-- TimeBasedRollingPolicy를 사용하여 일정 주기로 새로운 파일 생성 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 생성한 로그 파일의 이름 정의, "%d" 는 로그 파일 주기설정 => 현재 매일 생성 -->
<fileNamePAttern>${LOGS_ABSOLUTE_PATH}/logFile_%d{yyyy-MM-dd}.log</fileNamePAttern>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
</appender>
<!-- 로거의 일종으로 로그 중 최상위 계층의 로그(모든 경로에서 발생하는 로드 루트 로거 출력) -->
<!-- STDOUT 이라는 이름의 Appender를 Root Logger에서 참조하고 있는 형식 -->
<!-- Root Level을 WARN으로 지정했기에 WARN과 Error 로그만 출력 -->
<root level="WARN">
<appender-ref ref="STDOUT" />
</root>
<!-- racingcar 이름의 logger 정의, INFO 레벨 가짐 -->
<!-- logfile에는 racingcar package 내부에서 발생하는 로그만 출력됨 -->
<logger name="racingcar" level="INFO">
<appender-ref ref="FILE" />
</logger>
</configuration>
<logger name="racingcar" …> 에서 name 속성은 로그 카테고리를 지정하는 부분
name="com.example.myapp.service" 로 설정하면, 그 패키지(및 하위 패키지)에 속한 클래스들이 생성하는 로그만 이 로거에 매핑됩니다.name="racingcar" 처럼 패키지의 최상위 부분만 지정해도, 실제 클래스 로거 이름이 racingcar.TrainService 같은 식으로 racingcar. 로 시작하면 모두 포함됩니다.RollingFileAppender는 기본적으로 append="true"로 동작
<rollingPolicy> 내부 태그 설명
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">%d), 시간(%d{HH}) 단위로 새 파일을 만들때 유용합니다.<<fileNamePattern>…</fileNamePattern>
${..} : 환경변수나 시스템 프로퍼티 치환%d{..} : 날짜 포맷<maxHistory>30</maxHistory>
FixedWindowRollingPolicy + SizeBasedTriggeringPolicy 정책 클래스를 사용해야 함<totalSizeCap>3GB</totalSizeCap>
spring
profiles
active: dev
<springProfile name="dev">
<logger name="racingcar" level="DEBUG">
<appender-ref ref="ASYNC_SLACK_APPENDER" />
</logger>
</springProfile>