운영을 위해서는 로그는 반드시 필요하다
데이터는 돈이고 로그는 값비싼 자산이다. 유저들의 패턴이나 행동을 파악 데이터가 된다
📌 로그를 찍는것은 데이터 내용이 외부로 노출되지 않아야한다.
내부에서 개발을 위한 데이터의 경우 article id 같은 내용을 찍어도 괜찮지만
아니면 외부로 노출하지 말아야함.
logging framwork이다.
resources 폴더에 logback-spring.xml 파일을 생성한다
xml파일에 <? 자동완성, configuration 설정을 하게 되면 springboot 서버를 처음 띄울때 나오는 log를 덮어씌우게 된다. 이는 해당 xml 파일을 만들경우 기존의 logback를 무시하고 가장 최우선으로 적용되기 때문이다
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
기본 스프링 디폴트 로그백 (shift 두번 누르고 path 붙여서 확인해볼것)
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="logback-spring-${spring.profiles.active}.xml"/>
</configuration>
<included>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"></include>
<!--<included>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>${CONSOLE_LOG_CHARSET}</charset>
</encoder>
</appender>
</included>-->
<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{0}:%line] -%msg%n
</pattern>
</layout>
</appender>
<root level="DEBUG">
<!-- <appender-ref ref="CONSOLE"/>-->
<!--spring logback에 있는 console name을 찾아서 실행-->
<appender-ref ref="CONSOLE2"/>
</root>
</included>
package dev.be.logback.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@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";
}
}
설정한 log level에 맞게 로그가 출력되는지 확인할 것
https://medium.com/dong-gle/로깅-정책-768c658f0f8d
https://ckddn9496.tistory.com/82
logback-variables.properties
# 중복되는 출력 패턴값과 경로를 지정하여 사용해준다
LOG_DIR=logs
LOG_PATTERN=[%-5level] %d{yyyy-MM-dd HH:mm:ss} [%thread] [%logger{0}:%line] - %msg%n
<included>
<property resource="logback-variables.properties">
</property>
<appender name="REQUEST1" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--파일은 관리를 위해서 동일한 경로에서 관리를 해야한다-->
<file>**${LOG_DIR}**/request1.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--지정한 시간이 지나면 archive 디렉토리 하위 아래 reqeust1, 날짜, 01234 ~ 순서를 지정해주고 하나의 파일로 압축하는 것-->
<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">
<!--지정한 시간이 지나면 archive 디렉토리 하위 아래 reqeust1, 날짜, 01234 ~ 순서를 지정해주고 하나의 파일로 압축하는 것-->
<fileNamePattern>${LOG_DIR}/archive/request2.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
<!--로그 파일 최대 크기-->
<maxFileSize>1KB</maxFileSize>
<!--로그 파일 최대 보관주기 단위: 일-->
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
[REQUEST2] [%-5level] ${LOG_PATTERN}
</pattern>
<outputPatternAsHeader>true</outputPatternAsHeader>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="REQUEST1"/>
<appender-ref ref="REQUEST2"/>
</root>
</included>
<outputPatternAsHeader>true</outputPatternAsHeader>
ref : https://logback.qos.ch/manual/encoders.html#outputPatternAsHeader
header 부분에 출력 패턴을 확인할 수 있다
https://velog.io/@kjy0302014/Spring-Boot-LogBack-적용하기#logfj-vs-logback
https://velog.io/@kjy0302014/Spring-Boot-LogBack-적용하기#logfj-vs-logback
Slf4j를 사용하기 때문에 바로 MDC를 호출하여 사용할 수 있다
//멀티스레드 환경에서 로그를 남길때 사용하는 개념
//스레드마다 고유한 로그값을 가지고 있는데 그걸 로그백에 전달해주기 위한 개념
package dev.be.logback.controller;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
public class MdcController {
@GetMapping("/mdc")
public String mdc() {
//멀티스레드 환경에서 로그를 남길때 사용하는 개념
//스레드마다 고유한 로그값을 가지고 있는데 그걸 로그백에 전달해주기 위한 개념
MDC.put("job", "dev");
log.trace("log -> trace");
log.debug("log -> debug");
log.info("log -> info");
log.warn("log -> warn");
log.error("log -> error");
MDC.clear();
return "mdc";
}
}
<included>
<!-- logback-variables.properties에 정의되어 있는 Key를 사용하기 위한 코드 -->
<property resource="logback-variables.properties">
</property>
<appender name="REQUEST1" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--파일은 관리를 위해서 동일한 경로에서 관리를 해야한다-->
<file>${LOG_DIR}/request1.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--지정한 시간이 지나면 archive 디렉토리 하위 아래 reqeust1, 날짜, 01234 ~ 순서를 지정해주고 하나의 파일로 압축하는 것-->
<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">
<!--지정한 시간이 지나면 archive 디렉토리 하위 아래 reqeust1, 날짜, 01234 ~ 순서를 지정해주고 하나의 파일로 압축하는 것-->
<fileNamePattern>${LOG_DIR}/archive/request2.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
<!--로그 파일 최대 크기-->
<maxFileSize>1KB</maxFileSize>
<!--로그 파일 최대 보관주기 단위: 일-->
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
[REQUEST2] [%-5level] ${LOG_PATTERN}
</pattern>
<outputPatternAsHeader>true</outputPatternAsHeader>
</encoder>
</appender>
<appender name="MDC" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--파일은 관리를 위해서 동일한 경로에서 관리를 해야한다-->
<file>${LOG_DIR}/mdc.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--지정한 시간이 지나면 archive 디렉토리 하위 아래 reqeust1, 날짜, 01234 ~ 순서를 지정해주고 하나의 파일로 압축하는 것-->
<fileNamePattern>${LOG_DIR}/archive/mdc.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
<!--로그 파일 최대 크기-->
<maxFileSize>1KB</maxFileSize>
<!--로그 파일 최대 보관주기 단위: 일-->
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
[MDC] %X{job}%n
</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">
<!--지정한 시간이 지나면 archive 디렉토리 하위 아래 reqeust1, 날짜, 01234 ~ 순서를 지정해주고 하나의 파일로 압축하는 것-->
<fileNamePattern>${LOG_DIR}/archive/error.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
<!--로그 파일 최대 크기-->
<maxFileSize>1KB</maxFileSize>
<!--로그 파일 최대 보관주기 단위: 일-->
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
[ERROR] ${LOG_PATTERN}
</pattern>
<outputPatternAsHeader>true</outputPatternAsHeader>
</encoder>
</appender>
<root level="INFO">
<!--<appender-ref ref="REQUEST1"/>
<appender-ref ref="REQUEST2"/>-->
<!--<appender-ref ref="MDC"/>-->
<appender-ref ref="ERROR"/>
</root>
</included>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>error</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
filter를 설정하여 log level을 지정해준다.
onMatch를 설정하는 이유는 warn으로 할 경우 더 높은 레벨이 존재하기 때문에 지정한 로그만 남기기 위해서 지정 로그는 남기고 onMismatch인 경우에는 무시하는 설정이다
뱔도의 고립된 logger를 지정해주는 방법
Slf4j topic을 지정해 줘야한다.
package dev.be.logback.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j(topic = "SQL_LOG1")
@RestController
public class QueryController {
@GetMapping("/query1")
public String query() {
log.trace("log -> trace");
log.debug("log -> debug");
log.info("log -> info");
log.warn("log -> warn");
log.error("log -> error");
return "query1";
}
}
<included>
<!-- logback-variables.properties에 정의되어 있는 Key를 사용하기 위한 코드 -->
<property resource="logback-variables.properties">
</property>
<appender name="REQUEST1" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--파일은 관리를 위해서 동일한 경로에서 관리를 해야한다-->
<file>${LOG_DIR}/request1.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--지정한 시간이 지나면 archive 디렉토리 하위 아래 reqeust1, 날짜, 01234 ~ 순서를 지정해주고 하나의 파일로 압축하는 것-->
<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">
<!--지정한 시간이 지나면 archive 디렉토리 하위 아래 reqeust1, 날짜, 01234 ~ 순서를 지정해주고 하나의 파일로 압축하는 것-->
<fileNamePattern>${LOG_DIR}/archive/request2.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
<!--로그 파일 최대 크기-->
<maxFileSize>1KB</maxFileSize>
<!--로그 파일 최대 보관주기 단위: 일-->
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
[REQUEST2] [%-5level] ${LOG_PATTERN}
</pattern>
<outputPatternAsHeader>true</outputPatternAsHeader>
</encoder>
</appender>
<appender name="MDC" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--파일은 관리를 위해서 동일한 경로에서 관리를 해야한다-->
<file>${LOG_DIR}/mdc.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--지정한 시간이 지나면 archive 디렉토리 하위 아래 reqeust1, 날짜, 01234 ~ 순서를 지정해주고 하나의 파일로 압축하는 것-->
<fileNamePattern>${LOG_DIR}/archive/mdc.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
<!--로그 파일 최대 크기-->
<maxFileSize>1KB</maxFileSize>
<!--로그 파일 최대 보관주기 단위: 일-->
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
[MDC] %X{job}%n
</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">
<!--지정한 시간이 지나면 archive 디렉토리 하위 아래 reqeust1, 날짜, 01234 ~ 순서를 지정해주고 하나의 파일로 압축하는 것-->
<fileNamePattern>${LOG_DIR}/archive/error.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
<!--로그 파일 최대 크기-->
<maxFileSize>1KB</maxFileSize>
<!--로그 파일 최대 보관주기 단위: 일-->
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
[ERROR] ${LOG_PATTERN}
</pattern>
<outputPatternAsHeader>true</outputPatternAsHeader>
</encoder>
</appender>
<appender name="QUERY" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--파일은 관리를 위해서 동일한 경로에서 관리를 해야한다-->
<file>${LOG_DIR}/query.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--지정한 시간이 지나면 archive 디렉토리 하위 아래 reqeust1, 날짜, 01234 ~ 순서를 지정해주고 하나의 파일로 압축하는 것-->
<fileNamePattern>${LOG_DIR}/archive/query.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
<!--로그 파일 최대 크기-->
<maxFileSize>1KB</maxFileSize>
<!--로그 파일 최대 보관주기 단위: 일-->
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
[QUERY] [%-5level] ${LOG_PATTERN}
</pattern>
<outputPatternAsHeader>true</outputPatternAsHeader>
</encoder>
</appender>
<root level="INFO">
<!--<appender-ref ref="REQUEST1"/>
<appender-ref ref="REQUEST2"/>-->
<!--<appender-ref ref="MDC"/>-->
<!--<appender-ref ref="ERROR"/>-->
</root>
<!--logger를 지정을 했지만 이것보다 상위 logger 속성을 받는다고 하면 merge가 되는것
따라서 합집한 느낌으로 되기때문에 아예 별도의 고립된 설정을 지정한다.-->
<logger name="SQL_LOG1" level="INFO" additivitty="false">
<appender-ref ref="QUERY"/>
</logger>
<logger name="SQL_LOG2" level="INFO" additivitty="false">
<appender-ref ref="QUERY"/>
</logger>
</included>
Slf4j를 사용하지 않고 Logger를 설정하는 방법
package dev.be.logback.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class QueryController2 {
public static final Loggerlog= 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";
}
}