스프링에서 로그를 남겨보자! SLF4J와 Logback
- 로그 라이브러리
기록을 남기는 로깅은 여러모로 필수다. 그러나 자바를 배울 때부터 사용해 익숙할 System.out.println() 을 사용해 로깅을 하게 되면 웹의 속도가 상당히 느려진다. 이 때문에 다양한 로그 라이브러리들이 존재한다. 스프링에서 사용할 수 있는 로그 라이브러리의 종류는 아래와 같다.
- java.util.logging
JDK 1.4부터 포함된 표준 로깅 API
별도 라이브러리 추가 불필요
기능이 많이 부족해 다른 로그 라이브러리를 더 많이 사용
- Apache Commons logging
아파치 재단에 Commons 라이브러리 중에 로그 출력을 제공하는 라이브러리
- Log4j
아파치 제단에서 제공하며 가장 많이 사용되는 로깅 라이브러리
- Logback
Log4j를 개발한 Ceki Gulcu가 Log4j의 단점 개선 및 기능을 추가하여 개발한 로깅 라이브러리
뭘 써야 할까 고민되는가? 걱정할 것 없다. slf4j와 함께라면!
- SLF4J (Simple Logging Facade for Java)
SLF4J는 위처럼 다양한 라이브러리들을 하나의 통일된 방식으로 사용할 수 있게 해준다. 로깅 Facade라고도 하는데 디자인 패턴 개념인 듯 ( 이에 대한 설명을 덧붙이면 너무 길어질 듯 해 링크로 대체 - https://devtimothy.tistory.com/139 )
로깅에 대한 추상 레이어를 제공하는 것이고 interface의 모음이라고 하는데 사실 아래 그림을 보는 게 이해가 빠르다.
그림을 봐도 이해가 어렵다면 일단 써보자. 사실 언제나 그 편이 제일 빠르다
- 사용법
SLF4J와 logback을 사용해보자. 메이븐 기준으로 설명하자면 pom.xml에서 일단 필요한 라이브러리들을 받아야 한다
이때 주의할 부분이 있는데.. 일단 코드를 보자
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
<!-- 로깅 관련 -->
<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.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.25</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>
|
cs |
위에 slf4-api나 logback-classic, jcl-over-slf4j 부분은 그냥 추가하면 되는거지만 spring-context에 주석으로 표시한 commons-logging이 혹시 남아있다면 지워줘야 한다. commons-logging이 Spring이 기본적으로 사용하는 로그 라이브러리이기 때문에 적혀있는데 저게 적혀있으면 logback을 쓸 수 없다.
그리고 commons-logging을 지우려면 위에 추가한 jcl-over-slf4j도 필수로 추가해줘야 한다. 저게 없이 commons-logging만 지우면 스프링이 commons-logging 라이브러리를 찾다가 실패하고 에러가 발생하기 때문
메이븐은 이러면 되고.. 이제 logback 관련 설정을 해주자.

설정을 위해선 일단 logback.xml 파일을 resources 폴더 안에 만들어줘야 한다. 이 파일을 어떻게 찾는건가 궁금했는데 아마도 logback-test.xml, logback.groovy, logback.xml, logback-spring.xml 등의 이름을 찾는 것 같으니 파일명은 정확히 적어줘야 한다. 파일을 생성했다면 세부 설정을 해주자
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
<?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/log/access.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/tmp/log/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>
|
cs |
뭔가 복잡해보이지만 어렵지 않다. appender name="CONSOLE" 은 IDE 내부 콘솔에 찍힐 로그 관련 설정을 해준 것이고 appender name="FILE" 은 파일로 출력할 로그 관련 설정을 해준 것이다. 보다시피 pattern, filenamepattern 등을 설정해줄 수 있고 rollingPolicy 를 활용해 하루 단위로 로그를 관리해 줄 수 도 있다
appender 설정 아래에선 로그 레벨 관련 설정을 해줄 수 있는데 로그 레벨은 아래와 같다.
- Log Level
trace : debug보다 세분화된 정보
debug : 디버깅하는데 유용한 세분화된 정보
info : 진행상황 같은 일반 정보
warn : 오류는 아니지만 잠재적인 오류 원인이 될 수 있는 경고성 정보
error : 요청을 처리하는 중 문제가 발생한 오류 정보
이렇게 설정을 마쳤다면 필요한 곳에서 쓰면된다. 필요한 메소드마다 하나씩 logger 객체를 추가해주는 건 미련한 짓이니 ( 딱 그 부분만 필요한 게 아니라면 ) Interceptor의 preHandle 메소드에 설정을 해서 전체 로그를 확보하는 게 좋다
사용 코드는 아래와 같다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
package kr.or.connect.reservation.interceptor;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
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 boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
logger.info("요청 URL : {}",request.getRequestURL());
logger.info("요청시간 : {}",LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss")));
logger.info("클라이언트 IP : {}",request.getRemoteAddr());
//System.out.println(handler.toString() + " 를 호출했습니다.");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
}
|
cs |
Logger 객체 생성해서 쓰는 딱히 특이할 것 없는 코드지만 주의할 점이 하나 있는데
예를 들자면 "요청 URL" + request.getRequestURL(); 처럼 + 를 사용해 문자열을 연결하지 않고
{}를 활용하고 거기에 출력할 내용을 두번째 인자로 넣는다는 것이다.
로그를 찍을 때 + 를 활용하면 성능이 느려지기 때문이라고 하니 항상 위와 같은 방법을 활용하자
ps. slf4j의 Logger와 LoggerFactory를 활용하고 있는 것도 볼 수 있다. 즉 logback과 관련된 뭔가를 불러와서 로깅 처리를 하지 않는다는 것. 초반에 중간에 다른 로그 라이브러리로 교체하기 쉽다고 적은 것도 이 때문이다
참고
Chapter 3: Configuration
Chapter 3: Logback configuration 和訳 (Japanese translation) In symbols one observes an advantage in discovery which is greatest when they express the exact nature of a thing briefly and, as it were, picture it; then indeed the labor of thought is wonderfull
logback.qos.ch
[LECTURE] 1) 로깅이란? : edwith
들어가기 전에 운영 중인 웹 애플리케이션이 문제가 발생했을 경우, 문제의 원인을 파악하려면 문제가 발생했을 때 당시의 정보가 필요합니다. 이런 정보를 얻기 위해서 Exception... - moons
www.edwith.org
[LECTURE] 2) slf4j 설정하기 : edwith
들어가기 전에 로그 라이브러리는 다양한 종류가 있다고 앞 시간에 배웠습니다. 이번 시간엔 이러한 다양한 라이브러리를 같은 방법으로 사용할 수 있도록 도와주는 SLF4J에 대해 알아... - moons
www.edwith.org
[LECTURE] 3) slf4j를 이용한 로그남기기 : edwith
들어가기 전에 이번 시간엔 앞에서 배웠던 인터셉터의 출력 코드를 SLF4J와 logback라이브러리를 이용해 출력하도록 수정하도록 하겠습니다. 학습 목표 slf4j와 logback... - 부스트코스
www.edwith.org