업무요약
이번 업무는 화재경보 API 관련 테스트 및 연동이에요. 빠르게 코드를 구현해서(목요일에 업무받아서 월요일 마감인 업무...) 기능 구현을 마쳤고, 배포와 로그 기록 세팅이 남았어요.
갑자기 받은 업무라서 기능 구현을 위한 설계를 할 시간도 없어서 빠르게 기능만 돌아가도록 구현을 했고, 여기서 요구사항은 log파일의 저장과 로그 파일의 이름을 날짜로 지정해달라는 내용이었어요.
그래서! 그냥 간단하게 yml file 세팅하면 되겠지~ 생각하고 시작했지만... 그게 아니더군요...
yml 세팅 만으로는 간단한 설정 밖에 할 수가 없다더군요...
그래서 Logback과 Log4J, Log4J2, SLF4J를 알아보고, 작업을 진행했어요!
SLF4J는 뭔가?
알아보니 대충 SLF4J는 spring logging facade for java의 약자더라구요.
즉, 파사드 구조를 가진 복잡한 서브 클래스들의 공통적인 기능을 정의하는 상위 수준의 인터페이스를 제공하는 것이라고 해요.
그래서 Logback이나 Log4J2를 바꾸기 쉽도록 해둔 상위 인터페이스의 구조 패턴으로 사용하는 것 같아요.
spring에서는 annotation으로 이들을 제공해서 사용하기 더 쉬워요!
덧셈 연산자를 로그에 찍는건 좋지 않다.
spring boot의 경우에는 spring-boot-starter-web에 logback과 log4j의 의존성을 이미 가지고 있어요.
그리고 부가적인 내용인데 log를 찍을 때는 '+' 연산자를 사용하는 것은 좋지 않아요.
이건 문자열 덧셈 연산을 하게 만들어서 많은 양의 정보를 찍어내는 로그에는 좋지 않은 효율을 만들어내요.
그래서 꼭 '{}'을 사용하는 것이 좋아요.
어노테이션을 사용하지 않는다면, 아래와 같은 LoggerFactory를 통해서 log를 찍어요.
public class LogSample{
private static final Logger log = LoggerFactory.getLogger(LogSample.class);
public void testLog() {
log.info("log test");
}
}
로그 레벨
아래는 로그 레벨을 나타낸 것이에요. 스프링 프로젝트를 돌리다보면 defaults.xml을 사용해서 찍히는 기본 로그 구조로 많이 봤을거에요.
- FATAL : 심각한 에러
- ERROR : 에러가 일어 났을 때 사용, 개발자가 의도하지 않은 에러
- WARN : 에러는 아니지만 주의할 필요한 로그에 사용
- INFO : 운영에 참고할만한 사항 또는 중요 정보를 나타낼 때 사용
- DEBUG : 개발 단계에서 사용하며, 일반 정보를 나타낼 때 사용
- TRACE : 모든 레벨에 대한 로깅이 추적
Configuration 구성
이제 logback-spring.xml을 작성해볼거에요. 여기에는 Configuration안에 태그들을 사용해서 로그의 상세한 설정을 해요.
구성 요소는 property, appender, logger, root 등으로 크게 구성돼요.
property
property는 xml파일에서 상수변수로 쓸 값을 선언하는 태그에요.
name과 value 속성을 통해서 변수명과 값을 지정할 수 있어요.
예를 들어볼게요.
<property name="FILE_NAME" value="./log/application.log" />
위의 property 태그는 파일 이름을 지정하기 위해서 선언한 변수에요. 파일의 경로와 파일 이름을 value에 지정했어요.
아래에서 xml 파일을 작성하는 과정에서 appender 태그에서 file이름을 지정할 때 사용할거에요.
appender
appender태그는 로그 출력 대상을 지정하는 태그에요.
출력 대상을 지정한다는게 무슨말인지 이해 못 할 수 있어요.
여기서 출력 대상이 의미하는 것은 로그가 찍히는 위치에요. IDE를 통해서 Application을 실행했다고 했을 때, Running 창에 로그가 찍히는걸 매번 봤을거에요. 그건 출력 대상이 Console인걸 의미해요. 그리고 로그 파일이 저장되는 것은 출력 대상이 파일인거에요.
그리고 메일이나 DB에 로그를 보낼 수도 있어요. 그럼 출력 대상이 메일, DB인거죠.
그래서 저는 이번 업무에서 Console창과 File에 로그를 남길거기 때문에 두 가지 appender를 지정하려고 해요.
그럼, 어떤 종류가 있는지 알아봐야겠죠?
- ch.qos.logback.core.ConsoleAppender : 콘솔에 로그를 찍음, 로그를 OutputStream에 작성하여 콘솔에 출력되도록 해요.
- ch.qos.logback.core.FileAppender : 파일에 로그를 찍음, 최대 보관 일 수 등을 지정할 수 있어요.
- ch.qos.logback.core.rolling.RollingFileAppender : 여러개의 파일을 롤링, 순회하면서 로그를 찍어요.(FileAppender를 상속 받아요. 지정 용량이 넘어간 Log File을 넘버링 하여 나누어 저장할 수 있어요.)
- ch.qos.logback.classic.net.SMTPAppender : 로그를 메일에 찍어 보내요.
- ch.qos.logback.classic.db.DBAppender : DB(데이터베이스)에 로그를 찍어요.
저는 여기서 RollingFileAppender를 사용해서 날짜별로 로그 파일을 관리하도록 할거에요.
이제 아까 property에서 지정한 FILE_NAME을 사용할거에요.
<appender name="ROLLING_LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>${ROLLING_PATTERN}</pattern>
</encoder>
<file>${FILE_NAME}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${FILE_NAME_PATTERN}</fileNamePattern>
<maxHistory>${MAX_HISTORY}</maxHistory>
<maxFileSize>${MAX_FILE_SIZE}</maxFileSize>
<totalSizeCap>${TOTAL_SIZE}</totalSizeCap>
</rollingPolicy>
</appender>
위 태그를 살펴보면 file태그에 FILE_NAME을 지정한게 보이죠? 이런식으로 property를 사용해요.
추가로 지정했던 property를 적어둘게요.
<property name="ROLLING_PATTERN" value="%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %-5level ${PID} --- [%thread] %logger{30} : %msg%n" />
<property name="FILE_NAME" value="/log/application.log" />
<property name="FILE_NAME_PATTERN" value="/log/application-%d{yyyy-MM-dd}.%i.log" />
<property name="MAX_FILE_SIZE" value="10MB" />
<property name="TOTAL_SIZE" value="5GB" />
<property name="MAX_HISTORY" value="30" />
그리고 이 rollingPolicy에 대해서도 알아볼게요.
rollingPolicy
rollingPolicy는 여러 종류가 있다고 해요.
- TimeBasedRollingPolicy: 이름에서 알 수 있듯이 시간에 기반하여 rollover 정책을 정의할 수 있어요.
- SizeAndTimeBasedRollingPolicy: TimeBasedRollingPolicy 상속받아 maxFileSize 을 통해 파일의 크기까지 고려할 수 있어요.
- FixedWindowRollingPolicy : Fixed Window 알고리즘에 따라 로그 파일의 이름을 지정해요.
여기서는 SizeAndTimeBasedRollingPolicy를 사용했어요. TimeBasedRollingPolicy를 보통 많이 사용한다고 해요.
하위요소
- fileNamePattern : 아카이브 되는 파일의 패턴을 지정할 수 있어요.
- maxHistory : 보관 할 최대 파일 수를 제어하여 이전 파일을 삭제해요.
- maxFileSize : 분할 할 용량 사이즈를 의미해요.
- totalSizeCap : 전체 파일 크기를 제어하며, 전체 크기 제한을 초과하면 가장 오래된 파일을 삭제해요
- totalSizeCap을 사용하기 위해서는 maxHistory 속성이 필수이며, maxHistory와 totalSizeCap의 우선순위는 maxHistory가 더 높다고 해요.
저는 빠른 테스트를 위해서 fileNamePattern에 분단위로 지정해서 로그파일이 저장되도록 만들어봤어요.
이제는 logger태그를 통해서 로그의 지역구분을 할거에요.
logger
logger는 애플리케이션이 로그 메시지를 생성하기 위해 상호작용하는 클래스로, class 별로 지역을 구분하고, additivity 설정을 통해서 상위 레벨의 로그를 상속할 수 있어요.
예시를 봐볼게요.
<logger name="com.example.controller" level="DEBUG" additive="false" >
<appender-ref ref="CONSOLE"/>
</logger>
<logger name="com.example.service" level="DEBUG" additive="false" >
<appender-ref ref="CONSOLE"/>
</logger>
<logger name="com.example.domain" level="DEBUG" additive="false" >
<appender-ref ref="CONSOLE"/>
</logger>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
해당 태그들은 log를 class 별로 구분해놓은 것들이에요.
level 속성을 통해서 지정한 level보다 높은 단계의 log들만이 보이도록 할 수 있고, additive를 통해서 true로 지정할 시에(기본값) 상위 logger 태그의 상속 여부를 지정할 수 있어요.
아래는 additive의 예시에요.
<configuration>
<!-- 콘솔 출력 Appender -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 파일 출력 Appender -->
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>logs/example.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 기본 루트 로거: 모든 로그를 콘솔에 출력 -->
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>
<!-- 특정 패키지 로거 설정 -->
<logger name="com.example.service" level="DEBUG" additive="true">
<!-- 이 로거는 상위 로거로 로그를 전파함 -->
<appender-ref ref="FILE" />
</logger>
</configuration>
이렇게 root 태그를 지정하고, logger 태그에 additive를 따로 지정하지 않거나, 명시적으로 true로 지정하는 경우.
콘솔창에 로그를 찍도록 되어있는 root태그를 상속받은 service의 logger태그는 파일과 콘솔에 모두 로그를 찍도록 동작해요.
여기서 level 지정의 경우, logger가 우선순위가 높고, 만약 지정되어 있지 않다면 root의 level을 상속받아요.
완성된 logback-spring.xml 파일
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
<property name="ROLLING_PATTERN" value="%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %-5level ${PID} --- [%thread] %logger{30} : %msg%n" />
<property name="FILE_NAME" value="/log/application.log" />
<property name="FILE_NAME_PATTERN" value="/log/application-%d{yyyy-MM-dd}.%i.log" />
<property name="MAX_FILE_SIZE" value="10MB" />
<property name="TOTAL_SIZE" value="5GB" />
<property name="MAX_HISTORY" value="30" />
<appender name="ROLLING_LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>${ROLLING_PATTERN}</pattern>
</encoder>
<file>${FILE_NAME}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${FILE_NAME_PATTERN}</fileNamePattern>
<maxHistory>${MAX_HISTORY}</maxHistory>
<maxFileSize>${MAX_FILE_SIZE}</maxFileSize>
<totalSizeCap>${TOTAL_SIZE}</totalSizeCap>
</rollingPolicy>
</appender>
<logger name="jdbc" level="OFF" additive="false">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ROLLING_LOG_FILE"/>
</logger>
<logger name="jdbc.sqlonly" level="DEBUG" additive="false" >
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ROLLING_LOG_FILE"/>
</logger>
<logger name="jdbc.sqltiming" level="OFF" additive="false" >
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ROLLING_LOG_FILE"/>
</logger>
<logger name="org.hibernate.SQL" level="DEBUG" additive="false">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ROLLING_LOG_FILE"/>
</logger>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ROLLING_LOG_FILE"/>
</root>
</configuration>
이를 통해서 테스트를 진행했고, 로그파일이 잘 생성되는 것도 확인했어요.
이제 파일을 업무를 진행한 프로젝트에 적용해서 로그가 저장되도록 했어요.
이렇게 업무를 마무리 했습니다.
추가적으로 application.yml파일의 설정만으로 파일 위치와 정적인 파일 이름을 지정 할 수도 있어요.
logging:
file:
name: 이름지정
'노예 일지' 카테고리의 다른 글
작업 스케줄러 만들기 (0) | 2024.11.27 |
---|---|
Tomcat Init Setting Shell Script 작성하기 (0) | 2024.11.26 |
외부 Tomcat 설정하기 (1) | 2024.11.15 |