-
스프링에서 로그를 남겨보자! SLF4J와 LogbackKnowledge/Spring 2019. 9. 18. 07:04반응형
- 로그 라이브러리
기록을 남기는 로깅은 여러모로 필수다. 그러나 자바를 배울 때부터 사용해 익숙할 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에서 일단 필요한 라이브러리들을 받아야 한다
이때 주의할 부분이 있는데.. 일단 코드를 보자
123456789101112131415161718192021222324252627282930<!-- 로깅 관련 --><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 등의 이름을 찾는 것 같으니 파일명은 정확히 적어줘야 한다. 파일을 생성했다면 세부 설정을 해주자
12345678910111213141516171819202122232425262728<?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 메소드에 설정을 해서 전체 로그를 확보하는 게 좋다
사용 코드는 아래와 같다
1234567891011121314151617181920212223242526272829303132333435package 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());@Overridepublic 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;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {}}cs Logger 객체 생성해서 쓰는 딱히 특이할 것 없는 코드지만 주의할 점이 하나 있는데
예를 들자면 "요청 URL" + request.getRequestURL(); 처럼 + 를 사용해 문자열을 연결하지 않고
{}를 활용하고 거기에 출력할 내용을 두번째 인자로 넣는다는 것이다.
로그를 찍을 때 + 를 활용하면 성능이 느려지기 때문이라고 하니 항상 위와 같은 방법을 활용하자
ps. slf4j의 Logger와 LoggerFactory를 활용하고 있는 것도 볼 수 있다. 즉 logback과 관련된 뭔가를 불러와서 로깅 처리를 하지 않는다는 것. 초반에 중간에 다른 로그 라이브러리로 교체하기 쉽다고 적은 것도 이 때문이다
참고
반응형'Knowledge > Spring' 카테고리의 다른 글
스프링에서 파일다운로드 구현하는 방법 (0) 2019.09.20 스프링에서 컨트롤러로 넘어온 파일 저장하는 방법 (0) 2019.09.18 로그인 기능은 어떻게 구현할까? 세션에 대해 알아보자 (0) 2019.09.04 스프링 controller에서 파라미터를 받는 다양한 방법 ( @RequestParam, @RequestBody, @PathVariable) (0) 2019.09.04 Spring에서 JSON을 편하게 리턴해보자! Jackson 라이브러리와 RestController (0) 2019.08.21