ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 스프링에서 로그를 남겨보자! SLF4J와 Logback
    Knowledge/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 를 거쳐서 쓰이는 걸 볼 수 잇다. 이 때문에 중간에 로깅 라이브러리를 교체하기도 쉽다고

    그림을 봐도 이해가 어렵다면 일단 써보자. 사실 언제나 그 편이 제일 빠르다

     

    - 사용법

     

    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

     

    반응형

    댓글

Designed by Tistory.