용어 뜻:
1. Automatic Reloading Configuration file
- logback을 도입해야 하는 제일 중요한 이유라고 생각되는 기능이다.
- log4j는 로깅 레벨을 WARN -> INFO 등으로 변경하게 되면 Application(WAS)를 다시 시작했어야 했지만 logback 에서는 그럴 필요가 없다.
설정 파일을 변경하면 지정된 시간이 지나게 되면 파일의 변경을 감지하고 다시 읽어들인다.
출처:
너무 길어서 복사하면서 보는 걸로 합니다.
log4j개발자가 거의 지난 10년동안 log4j가 사용되면서 불편(?)했던 내용들을 모두 개선해서 logback이라는 제품을 만들어 낸 듯한 느낌이다.
개발자가 소개하는 log4j 에서 logback으로 옮겨야 하는 여러 이유들 중 특히 내가 관심이 가는 몇가지 항목들은 다음과 같다.
1. Automatic Reloading Configuration file
- logback을 도입해야 하는 제일 중요한 이유라고 생각되는 기능이다.
- log4j는 로깅 레벨을 WARN -> INFO 등으로 변경하게 되면 Application(WAS)를 다시 시작했어야 했지만 logback 에서는 그럴 필요가 없다.
설정 파일을 변경하면 지정된 시간이 지나게 되면 파일의 변경을 감지하고 다시 읽어들인다.
2. Graceful Recovery from I/O Failures
- 기존 log4j에서 FileAppender 를 사용하여 로그를 파일서버에 저장하는 경우,
파일서버에 문제가 있어 I/O fail이 나면 Application(was)를 재시작 했어야 했지만
logback에서는 파일 서버가 정상으로 돌아오면 에러 상황에서 빠르게 자동으로 복구가 된다.
- 기존 log4j에서 FileAppender 를 사용하여 로그를 파일서버에 저장하는 경우,
파일서버에 문제가 있어 I/O fail이 나면 Application(was)를 재시작 했어야 했지만
logback에서는 파일 서버가 정상으로 돌아오면 에러 상황에서 빠르게 자동으로 복구가 된다.
3. Automatic Compress
- 아마도 기존에 log4j를 사용해서 파일로 로그를 떨어뜨려 놓는 경우 별도의 배치 프로그램을 이용해서
로그파일을 압축하고 다른 곳으로 옮겨놓는 작업을 많이 해 보았을 것이라 생각된다.
logback에서는 로그 파일의 자동 압축을 지원하고, 시간(파일의 갯수)이 지난 파일을 자동으로 삭제하는 기능도 제공을 한다.
- 로그 파일이 거대해서 압축하는데 시간이 오래 걸린다 해도 비동기 방식으로 동작하므로 application의 성능에는 영향을 주지 않는다.
- 아마도 기존에 log4j를 사용해서 파일로 로그를 떨어뜨려 놓는 경우 별도의 배치 프로그램을 이용해서
로그파일을 압축하고 다른 곳으로 옮겨놓는 작업을 많이 해 보았을 것이라 생각된다.
logback에서는 로그 파일의 자동 압축을 지원하고, 시간(파일의 갯수)이 지난 파일을 자동으로 삭제하는 기능도 제공을 한다.
- 로그 파일이 거대해서 압축하는데 시간이 오래 걸린다 해도 비동기 방식으로 동작하므로 application의 성능에는 영향을 주지 않는다.
4. Prudnect Mode
- 다수의 JVM 인스턴스에서 같은 로그 파일을 사용하여 로그를 “안전하게” 기록할 수 있는 기능을 제공한다고 한다.
어떤 제한이 있는것 같다고 하는데 내부적으로 비동기 큐를 사용하고 있는 것이 아닌가 추측이 된다.
정확하게 확인해 보지는 않았지만 이런 모델들이 보통 문제를 많이 일으키는 관계로 꼭 필요하지 않다면 되도록 사용하지 않는 것이 좋을 것 같다는 생각이다.
- 다수의 JVM 인스턴스에서 같은 로그 파일을 사용하여 로그를 “안전하게” 기록할 수 있는 기능을 제공한다고 한다.
어떤 제한이 있는것 같다고 하는데 내부적으로 비동기 큐를 사용하고 있는 것이 아닌가 추측이 된다.
정확하게 확인해 보지는 않았지만 이런 모델들이 보통 문제를 많이 일으키는 관계로 꼭 필요하지 않다면 되도록 사용하지 않는 것이 좋을 것 같다는 생각이다.
5. Conditional Processing Configurations
- 기존에는 개발환경과 운영환경에 별도의 설정파일을 적용하고
개발환경에서는 로깅레벨을 DEBUG, 운영환경에서는 WARN-시키기 위해 로깅 설정파일을 분리해 놓고 사용하던 경험들이 있을 것이라 생각된다.
- logback에서는 설정 파일에 if-then-else 의 조건문을 사용할 수 있어 설정 파일 하나로만 개발-운영 환경을 모두 커버할 수 있도록 만드는 것이 가능한다.
- 기존에는 개발환경과 운영환경에 별도의 설정파일을 적용하고
개발환경에서는 로깅레벨을 DEBUG, 운영환경에서는 WARN-시키기 위해 로깅 설정파일을 분리해 놓고 사용하던 경험들이 있을 것이라 생각된다.
- logback에서는 설정 파일에 if-then-else 의 조건문을 사용할 수 있어 설정 파일 하나로만 개발-운영 환경을 모두 커버할 수 있도록 만드는 것이 가능한다.
6. Stack Traces with Packaging Data
- Logback을 사용해서 Stack Trace를 남기게 되면 각 패키지의 아티팩트 정보와 버전정보를 출력해 준다.
배포가 문제가 있다면 바로 확인이 가능하게 해주는 유용한 기능이라 생각된다.
- Logback을 사용해서 Stack Trace를 남기게 되면 각 패키지의 아티팩트 정보와 버전정보를 출력해 준다.
배포가 문제가 있다면 바로 확인이 가능하게 해주는 유용한 기능이라 생각된다.
프로젝트 적용시에 사용 방법을 복사 후 수정합니다.
1. 디펜던시 설정
logbook 응 사용하기 위해서는 logback-core, logback-classic, slf4j-api jar들이 필요한다. 디펜던시는 아래와 같이 설정한다.
1) 메이븐을 사용하고 있다면 – [ pom.xml ]
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.10</version>
</Dependency>
<!— 조건부 설정을 사용하려면 아래의 디펜던시를 추가해 주어야 한다. —>
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
<version>2.7.7</version>
</dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.10</version>
</Dependency>
<!— 조건부 설정을 사용하려면 아래의 디펜던시를 추가해 주어야 한다. —>
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
<version>2.7.7</version>
</dependency>
2) 그래들을 사용하고 있다면 – [build.gradle]
compile 'ch.qos.logback:logback-classic:1.1.2'
compile 'ch.qos.logback:logback-core:1.1.2'
compile 'org.slf4j:slf4j-api:1.7.10'
// 조건부 설정을 사용하려면 아래의 디팬던시를 추가해 주어야 한다
compile 'org.codehaus.janino:janino:2.7.7'
compile 'ch.qos.logback:logback-core:1.1.2'
compile 'org.slf4j:slf4j-api:1.7.10'
// 조건부 설정을 사용하려면 아래의 디팬던시를 추가해 주어야 한다
compile 'org.codehaus.janino:janino:2.7.7'
2. logback.xml 파일의 설정은 아래와 같이 한다. 주석으로 처리한 부분을 잘 읽어보자.
<?xml version="1.0" encoding="UTF-8"?>
<!-- 30초마다 설정 파일의 변경을 확인한다. 파일이 변경되면 다시 로딩한다 -->
<configuration scan="true" scanPeriod="30 seconds">
<!-- 외부 설정파일을 사용할 수 있다. -->
<property resource="resource.properties"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern> %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${APP_HOME}/sujemall-webapp.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 파일이 하루에 한개씩 생성된다 -->
<fileNamePattern>sujemall-webapp.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- maxHIstory 설정은 위 부분에 롤링 정책에 따라 적용되 된다고 보면된다.
위 설정대로 라면 30일이 지난 파일은 삭제가 된다.-->
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.springframework" level="info"/>
<logger name="org.hibernate" level="debug"/>
<logger name="com.sujemall.webapp" level="debug"/>
<if condition='property("RUN_MODE").equals("SERVICE")'>
<then>
<!-- 설정파일에 RUN_MODE 가 SERVICE로 정의되어 있으면 로깅 레벨을 INFO로 지정 -->
<root level="info">
<appender-ref ref="console"/>
<appender-ref ref="FILE"/>
</root>
</then>
<!-- 설정파일에 RUN_MODE 가 SERVICE로 정의되어 있지 않으면 로깅 레벨을 DEBUG 지정 -->
<else>
<root level="debug">
<appender-ref ref="console"/>
<appender-ref ref="FILE"/>
</root>
</else>
</if>
</configuration>
<!-- 30초마다 설정 파일의 변경을 확인한다. 파일이 변경되면 다시 로딩한다 -->
<configuration scan="true" scanPeriod="30 seconds">
<!-- 외부 설정파일을 사용할 수 있다. -->
<property resource="resource.properties"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern> %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${APP_HOME}/sujemall-webapp.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 파일이 하루에 한개씩 생성된다 -->
<fileNamePattern>sujemall-webapp.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- maxHIstory 설정은 위 부분에 롤링 정책에 따라 적용되 된다고 보면된다.
위 설정대로 라면 30일이 지난 파일은 삭제가 된다.-->
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.springframework" level="info"/>
<logger name="org.hibernate" level="debug"/>
<logger name="com.sujemall.webapp" level="debug"/>
<if condition='property("RUN_MODE").equals("SERVICE")'>
<then>
<!-- 설정파일에 RUN_MODE 가 SERVICE로 정의되어 있으면 로깅 레벨을 INFO로 지정 -->
<root level="info">
<appender-ref ref="console"/>
<appender-ref ref="FILE"/>
</root>
</then>
<!-- 설정파일에 RUN_MODE 가 SERVICE로 정의되어 있지 않으면 로깅 레벨을 DEBUG 지정 -->
<else>
<root level="debug">
<appender-ref ref="console"/>
<appender-ref ref="FILE"/>
</root>
</else>
</if>
</configuration>
3. 로그 찍기는 slf4j api를 사용하는 기존의 방식과 크게 다르지 않다. 마커를 이용해서 로그를 찍어봤다.
package com.sujemall.webapp.service;
import com.sujemall.webapp.model.User;
import com.sujemall.webapp.repository.UserRepository;
import com.sujemall.webapp.utils.CommonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* Created by yhlee on 15. 1. 12..
*/
@Service
public class UserServiceImpl implements UserService {
static final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);
@Autowired
private UserRepository userRepository;
@Override
@Transactional
public User enrollUser(User user) {
user.setPassword(CommonUtils.hashString(user.getPassword()));
user = userRepository.save(user);
LOGGER.info("#### Success Save User : UserName is {}, Email is {} " ,user.getUserName(), user.getMainEmail());
return user;
}
}
import com.sujemall.webapp.model.User;
import com.sujemall.webapp.repository.UserRepository;
import com.sujemall.webapp.utils.CommonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* Created by yhlee on 15. 1. 12..
*/
@Service
public class UserServiceImpl implements UserService {
static final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);
@Autowired
private UserRepository userRepository;
@Override
@Transactional
public User enrollUser(User user) {
user.setPassword(CommonUtils.hashString(user.getPassword()));
user = userRepository.save(user);
LOGGER.info("#### Success Save User : UserName is {}, Email is {} " ,user.getUserName(), user.getMainEmail());
return user;
}
}
위와같이 설정하고 application을 돌려보면 로그가 잘 찍히는 것을 확인할 수 있다.
용이의 블로그, 2017-10-28, http://knot.tistory.com/92
댓글
댓글 쓰기