반응형
  1.  SLF4J 로깅 처리

  2. 세상에는 여러 Logger들이 존재한다. 무엇을 쓸지는 크게 상관없지만 어플리케이션을 개발할때는 한가지 선택 해야하고, 중요한 것은 의존된 라이브러리가 쓰고 있는 Logger도 잘 확인해야 함. 섞어 쓸 수도 있지만, logging을 제어하려면 하나로 통일 되야 좋다.  여기서 내가 쓰고 싶은 Logger로 통일하고 싶다면 방법은 SLF4J이다.

    SLF4J는 로깅 Facade입니다. 로깅에 대한 추상 레이어를 제공하는것이고 java로 따지면 interface덩어리 라고 보시면 됩니다. artifact이름도 api라고 부릅니다.

    • 사용방법 - pom.xml에 다음과 같은 의존을 추가한다

    <dependency>
      <groupId>org.slf4j</groupId>
       <artifactId>jcl-over-slf4j</artifactId>
      <version>1.7.7</version>
    </dependency>


    실행 코드는 다음과 같다.

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    public class Sample {
      final Logger logger = LoggerFactory.getLogger(Sample.class);
      public void run() {
      logger.debug("debug");
      logger.info("info");
      }
    }


    하지만 이를 실행하면 다음과 같은 오류가 난다.

    SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".SLF4J: Defaulting to no-operation (NOP) logger implementationSLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.


    구현체가 없기 때문이다. 여기서 구현체란  commons logging, log4j, java util logging, logback 이런 종류의 로깅 구현체를 말한다. 



    1. Logback

    여러 구현체가 존재하지만 나는 Logback을 공부해왔고, 우리 프로젝트에 적용하고 싶다. 그 이유는 다음과 같다. 

    - system.out.println 한줄 남기는데 io 리소스를 너무 많이 먹어서 시스템이 느려짐
    - log4j는 메모리 관리를 따로해서 io쪽에서 메모리를 많이 안먹고 빠르다.
    - log4j를 막 쓰다 보니.. system.out.println 만큼 리소스를 먹게 된다..
    - 이를 보안하기 위해 logback이 개발 됨


    즉, “log4j“에서 제공을 하지 않는 기능 외에 다양한 이점이 있음.

    다양한 이점을 더 알고 싶다면 다음을 참고하자!


    1) logback의 3가지 컴포넌트

    “logback-core“는 말 그대로 핵심 코어 컴포넌트 입니다. “logback-classic“은 “slf4j“에서 사용이 가능하도록 만든 플러그인 컴포넌트 입니다.
    “logback-access“는 사용하는 어플리케이션이 “웹 어플리케이션“일 경우 빛을 바라는 컴포넌트 입니다.” HTTP 요청에 대한 강력한 디버깅 기능을 제공 합니다.”

    2) Graceful recovery from I/O failures

    “Log4j” 같은 경우 “JDBC” , “Socket“등 다양한 “Appender“를 지원 합니다.특히. 대다수의 환경에서는 “File”에 제일 많이 로그를 저장 할 것입니다.
    하지만 만약 “파일 서버가 일시적으로 장애가 발생 할경우” , “파일 서버가 복구 될때까지”“어플리케이션을 중지 시켰다가” 다시 파일 서버가 복구되면, 그때 서버를 재기동 할것 입니다. 하지만 “LOGBack“은 “서버 중지 없이, 이전 시점부터 복구를 graceful “하게 지원을 합니다.

    3) Automatic removal of old log archives

    대부분 환경에서는 “하나의 파일”에 기록하는 것이 아니고, “특정 시간” 또는 “특정 파일 사이즈”로 “Rolling” 해서 “Archiving“을 할 것입니다.
    하지만 “Archiving된 로그 파일“을 계속 유지하지 않고, 일정 기간 지나면 서비스에 부담을 주지 않기 위해서” 파일을 삭제 할것 입니다.
    이럴경우 “Crontab” 또는 다른 삭제 스케줄러를 설정 또는 개발을 할것입니다.“LOGBack“은 “maxHistory“라는 설정 정보를 통해서 주기적으로 “Archive”
    파일을 자동 삭제 합니다.“maxHistory“의 값이 “12“일 경우, “12개월 이후 삭제 하라는” 뜻입니다.

    4) Lilith
    “Lilith“은 “현재 발생하는 로그 이벤트에 대한 상태 정보를 볼수 있도록 지원 하는
    뷰어” 입니다.“log4j“를 지원하는 “chainsaw” 와 비슷한 기능을 합니다. 다만 차이점은 “Lilith” “Large data“를 지원 합니다.

    5) Conditional processing of configuration files

    빌드를 해본 사람이라면 아실듯 합니다. 빌드시 제일 골치가 아픈 것이 “config” 정보 와 “log file path” 입니다.
    이유는 어플리케이션이 구동하는 환경이 “로컬, staging, cbt” 마다 틀리기 때문 입니다. 이런점을 해결 하기 위해서 “Maven Filter” 기능을 사용 하거나, “JVM -D환경변수“를 통해서 각 환경 마다 선택적으로 “log 설정파일“을 선택하도록 구성을 할 것입니다.
    “LOGBack“은 이런 이슈를 좀더 “Graceful“하게 지원을 합니다. “JSTL” 같이 분기 스크립트 (<if>, <then> and <else>)를 제공해서 하나의 파일에서 다양한 빌드 환경을 제공 하도록 지원을 합니다.

    6) Filters

    “Filter” 기능은 “Log4j“에서 지원하는 기능입니다. 주 기능은 “로깅을 남길지 , 말지를”핸드링 할수 있는 기능 입니다.
    좀더 이해를 돕기 위해서 “UseCase“를 설명 드리겠습니다.

    만약 “A“라는 사용자에게 비즈니스적으로 문제점이 발견이 되었습니다.이럴경우는 “logical exception“이기 때문에 원인을 잡기가 쉽지가 않습니다.
    또한 현재 “운영 서버“의 “Log LEVEL“이 “ERROR“일 경우 로그를 확인 할수가 없을것입니다. 더욱이  “운영 서버 logical exception“은 “staging” 환경과 데이터가 틀리기 때문에실제 운영 로그를 확인하지 않고서는 재현을 할수가 없습니다.

    그래서 “운영 서버의 로그 레벨“을 “DEBUG“로 수정합니다. 하지만 로그는 보이지만 전체 사용자가해당이 되기 때문에 로그 데이터를 분석하기가 어렵습니다.
    이럴 경우 “Filter“를 사용해서 “다른 사용자“는 “ERROR” 레벨을 유지하고 ,"A” 사용자만 “DEBUG“로 설정을 하면, 분석하는데 많은 도움을 받을 수 있습니다.

    7) SiftingAppender

    “SiftingAppender“는 “Filter“의 기능과 유사하면서 다른 기능을 제공 합니다 “로그 파일을 특정 주제별로 분류“를 할 수 있도록 합니다.
    예를 들어서 “HTTP Session“별로 로그 파일을 저장한다거나, “사용자별“로 별도의 로그파일을 할 수 있습니다.

    8) Logback-access, i.e. HTTP-access logging with brains, is an integral part of logback

    위에서 언급했듯이 “logback-access“는 “웹 어플리케이션” 사용시 유용한 툴로써 제공 됩니다.특히나 요새는 전통적인 “HTML” 출력이 아닌 “REST 서버“로써의 역할을 많이 합니다. 이럴 경우 “HTTP 디버깅“을 제공 합니다.


    3. Logback 실습 

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>3.2.4.RELEASE</version>
    </dependency>

    하지만 spring-context는 jarkarta commons logging을 사용하고 있습니다. 실제 이렇게 두면 commons-logging이 의존성에 추가 됩니다. 여기서는 logback을 쓰려고 하니 관련라이브러리는 제거합니다.

    ** 제거하는 방법

         1) pom.xml




    2) Dependency Hierarchy -> commons-logging 우클릭 -> exclude maven artifact


    3) ok


    4) pom.xml에 추가 됬는지 확인





    5)  logback 은 logback-core, logback-classic, logback-access의 3개의 모듈이 있습니다. core는 classic과 access의 공통라이브러리입니다. maven repository를 쓴다면 classic만 추가하면 관련 라이브러리가 추가 됩니다.

    <dependencies>
      <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.1.2</version>
      </dependency>
    </dependencies>

    이것만 사용하면 기본적인 Log는 사용 가능하다. 

    import ch.qos.logback.classic.LoggerContext;
    import ch.qos.logback.core.util.StatusPrinter;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;

    public class Tutorial {
      private static final Logger logger = LoggerFactory.getLogger(Tutorial.class); 
     // Logger은 꼭 private static이 붙어야 한다. getLogger(클래스명.clss) 적어라.

      public static void main(String[] args) {
      logger.trace("trace");
      logger.debug("debug");
      logger.info("info");
      logger.warn("warn");
      logger.error("error");
      }
    }

    ** Logger들은 Tree Hierarchy 구조로 level을 적용 받을수 있다.




    TRACE > DEBUG > INFO > WARN

    ALL : TRACE와 같음 나중에 확장을 위한것으로 보임.
    TRACE : TRACE, DEBUG, INFO, WARN, ERROR
    DEBUG : DEBUG, INFO, WARN, ERROR
    INFO : INFO, WARN, ERROR
    WARN : WARN, ERROR
    ERROR : ERROR
    OFF : 출력하지 않음

    여기까지 표준이다. 하지만, 좀 더 기능을 확장하고 싶다면 logback.xml을 작성해야 한다.


    Logback.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration> //logback이 구동될때 logback 상태를 확인할 수 있습니다.
      <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
      <layout class="ch.qos.logback.classic.PatternLayout">
      <Pattern>%d{HH:mm} %-5level %logger{36} - %msg%n</Pattern>
      </layout>
      </appender>

      <logger name="example.logback.level.grandparents" level="TRACE"/>
    //example.logback.level.grandparents 이하 모든 Logger들의 level은 TRACE라는 설정입니다.

      <logger name="example.logback.level.grandparents.parents.children" level="INFO"/>
    //example.logback.level.grandparents.parents.children 이하 모든 Logger들의 level은 INFO라는 설정입니다.

      <root level="DEBUG">
                 <appender-ref ref="STDOUT" />
      </root>
    // 모든 대상에 STDOUT Appender를 적용하고 level이 DEBUG이하인것만 처리로 설정됩니다.
    </configuration>



    1. Appender
    Event마다 Log를 기록하는 기능은 Appender가 처리합니다. 그래서 Logger는 어떤 appender에 해당이 되어 처리 되는게 중요합니다. Appender를 설정하더래도 log출력에 해당되지 않으면 작동하지 않습니다.  Appender는 출력될 형식을 직접 가지고 있지 않고, 해당 기능은 Layout과 Encoder에 위임을 합니다.


    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
      <!-- encoders are assigned the type
      ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
      <encoder>
      <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
      </encoder>
    </appender>

    Appender의 종류 - ConsoleAppender (콘솔로 찍음), FileAppender(파일에 로그남김), RollingFileAppender(rolling 정책이용) ,  EventEvaluator(이벤트 기능으로 로그 남김)

    1) ConsoleAppender 

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
      <!-- encoders are assigned the type
      ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
      <encoder>
      <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
      </encoder>
    </appender>

    PatternLayoutEncoder는 pattern으로 받은 값을 이용해서 PatternLayout을 생성하고 PatternLayoutEncoder는 log message를 byte[]로 변환하는 기능도 포함합니다. 이로써 Appender는 Layout기능과 Encoder기능을 모두 가지게 됩니다. 이것을 이용해서 OutputStreamAppender는 byte[]를 OuputStream에 write하게 됩니다.

    2) FileAppender

    <!-- Insert the current time formatted as "yyyyMMdd'T'HHmmss" under
      the key "bySecond" into the logger context. This value will be
      available to all subsequent configuration elements. -->
    <timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/>
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
           <file>log-${bySecond}.txt</file>
           <append>true</append>
      <!-- encoders are assigned the type
      ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
      <encoder>
      <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
      </encoder>
    </appender>

    로그가 저장될 file을 선언하고, ConsoleAppender처럼 encoder,pattern을 선언하게 되면, log event를 지정된 file에 저장할 수 있습니다. 이때 파일 포맷중 날짜형식은 java.text.SimpleDateFormat을 따릅니다.

    3) RollingFileAppender
    롤링 정책 적용. 최대 파일 용량 설정가능

    <appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <file>mylog.txt</file>
      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <!-- rollover daily -->
      <fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
      <timeBasedFileNamingAndTriggeringPolicy
      class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
      <!-- or whenever the file size reaches 100MB -->
      <maxFileSize>100MB</maxFileSize>
      </timeBasedFileNamingAndTriggeringPolicy>
      </rollingPolicy>
      <encoder>
      <pattern>%msg%n</pattern>
      </encoder>
    </appender>

    <fileNamePattern> 확장자에 .zip을 선언하면 새로운 file 이 생성될때 이전 파일은 .zip으로 압축을 할수 있습니다.

    4) EventEvaluator
    이벤트 설정. 아래 예는 이메일 설정한 예이다. 이메일 로그 설정 관련 자세한 내용을 보고 싶다면  https://sonegy.wordpress.com/2014/06/03/235/ 출처 참고!

    <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
      <smtpHost>****</smtpHost>
      <smtpPort>25</smtpPort>
      <to>****</to>
      <to>****</to>
      <from>****</from>
      <subject>PRODUCT-PCS: %logger{20} - %m</subject>
      <layout class="ch.qos.logback.classic.PatternLayout">
      <Pattern>%d{HH:mm} %-5level %logger{36} - %msg%n</Pattern>
      </layout>
    </appender>


    ** 공통으로 적용되었던 Layout 패턴에 대해 알아보자!


    • %logger{length}: Logger name의 이름을 축약할 수 있습니다. {length}는 최대 차릿수 입니다.
    • %thread: 현재 Thread name
    • %-5level: log level -5는 출력 고정폭 값
    • %msg: log message %message은 alias
    • %n: new line

    로그 적용시 에러관련 

    출처: http://madeinjeon.tistory.com/18 [Jeon's blog]



반응형

'개발공부 > Spring' 카테고리의 다른 글

예외처리(1)  (0) 2020.10.01
Spring에서 Bean을 주입하는 방식  (0) 2020.03.16
SPRING AOP  (0) 2018.07.02
Tiles란, Spring tiles3 적용법  (2) 2018.06.14

+ Recent posts