Spring - 로깅
로깅이란?
- 정보를 제공하는 일련의 기록인 로그를 생성하도록 시스템을 작성하는 활동
- 프린트 줄 넣기는 간단히, 일시적인 로그만을 생성한다.
- 시스템 설계자들은 시스템의 복잡성 때문에 로그를 이해하고 사용해야 함
- 로그가 제공하는 정보의 양은 이상적으로는 프로그램이 실행되는 중에도 설정 가능해야 함
- 일반적인 로그 기록의 이점 (버그에 대한 유용한 정보, 성능에 관한 통계와 정보, 일반적 정보)
로그를 출력하는 방법
- System.out.print() 이용 (성능과 효율이 떨어짐)
- 로깅 라이브러리 이용
-- ex) java.util.logging, Apache Commons logging, Log4j, Logback
SLF4J
- logging 관련 라이브러리는 다양해서 이 라이브러리들을 통일된 방식으로 사용하는 방법
- 로깅 Facade, 로깅에 대한 추상 레이어를 제공, interface들의 모음
maven에 SLF4J와 logback 의존성 추가하기
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.25</version>
</dependency>
spring 라이브러리에서 commons-logging을 제거하면, spring을 사용할 때 commons-logging을 찾으며 오류 발생. 이러한 오류를 제거하기 위해 jcl-over-slf4j를 추가함
logback.xml (설정 방법)
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<Pattern>%d{HH:mm} %-5level %logger{36} - %msg%n</Pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/tmp/access.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/tmp/access-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<Pattern>%d{HH:mm} %-5level %logger{36} - %msg%n</Pattern>
</encoder>
</appender>
<logger name="org.springframework" level="info"/>
<logger name="kr.or.connect" level="debug"/>
<root level="debug">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
Appender (어디에 어떤 폼으로 로그를 남길 지 기록)
- ConsoleAppender : 콘솔에 로그를 어떤 포맷으로 출력할 지
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<Pattern>%d{HH:mm} %-5level %logger{36} - %msg%n</Pattern>
</encoder>
</appender>
- FileAppender : 파일에 로그를 어떤 포맷으로 출력할 지
- RollingFileAppender : 로그의 양이 많아지면 관리가 어려워 하루단위로 로그를 관리하는 법
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>access.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>access-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<Pattern>%d{HH:mm} %-5level %logger{36} - %msg%n</Pattern>
</encoder>
</appender>
Log Level (로그 중요성의 단계)
1. trace 2. debug 3. info 4. warn 5. error
아래처럼 사용 가능 (어떤 패키지 이하의 클래스에서 어떤 level 이상의 log를 저장할 지)
<logger name="org.springframework" level="info"/>
<logger name="kr.or.connect" level = "debug" />
<root>
<appender-ref ref = "CONSOLE" />
<appender-ref ref = "FILE" />
</root>
위의 root 태그는 모든 대상에 콘솔과 파일 어팬더를 적용한다는 의미
Logger 객체 선언 (로그를 남기고자 하는 클래스에 로거 객체를 필드로 선언)
import.org.slf4j.Logger;
import org.slf4j.LoggerFactory;
...
private Logger logger = LoggerFactory.getLogger(this.getClass());
로그 출력 메소드 (문자열 결합을 위해 + 연산자 사용 X, 로고의 수준에 따라 debug(),info()등 사용)
logger.trace("{} {} 출력", "값1", "값2");
logger.debug("{} {} 출력", "값1", "값2");
~~
LogInterceptor.java 수정 (기존 print -> logger 사용)
package kr.or.connect.guestbook.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class LogInterceptor extends HandlerInterceptorAdapter{
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// System.out.println(handler.toString() + " 가 종료되었습니다. " + modelAndView.getViewName() + "을 view로 사용합니다.");
logger.debug("{} 가종료되었습니다. {} 를 view로 사용합니다.", handler.toString(), modelAndView.getViewName());
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// System.out.println(handler.toString() + " 를 호출했습니다.");
logger.debug("{} 를 호출했습니다.", handler.toString());
return true;
}
}