업데이트:

4 분 소요

개요

시스템이 작동 될 때 그 작동 상태의 기록이 보존되어야 하거나 이용자의 습성 분석을 위한 로그를 기록해둘 필요가 있다.
하지만 보통 처음 자바를 시작해서 배우는 “Hello world!”는 System.out 으로 출력하면서 우리는 어떤 일종의 “로깅”를 배운다.
이 포스트는 이렇게 익숙한 출력 방식인 System.out이 아닌 “Logger”를 사용해야 하는지 부터 천천히 알아간다.

왜 Logger ?

알고리즘 문제를 풀 때나 간단한 프로그램을 짤 때 System.out을 사용하는 것은 친숙한 일이다.
하지만 크고 복잡해지는 프로그램일수록 System.out은 지양해야하고 Logger를 사용해야한다는데, 왜그럴까?
이유는 아래와 같다.

  1. 성능
    내부 버퍼링과 멀티 쓰레드를 지원하는 Logger에 비해 System.out은 리소스를 많이 사용해 성능에 부하를 준다.

  2. 정보
    직접 코드를 작성해 에러에 대한 정보를 만들어야 하는 System.out에 비해 Logger는 클래스의 이름, 시간, 레벨 등의 정보를 제공한다.

  3. 기록
    로그의 내용을 콘솔 밖에서 관리하고자 한다면 코드를 통해 로그 파일을 만들어야 하지만 Logger는 Appender를 통해서 쉽게, 또 원하는 조건, 원하는 형태에 따라 로그를 남길 수 있다.

  4. 레벨
    Trace, Debug, Info, Warn, Error 순으로 제공되는 레벨을 로그에 남길 수 있다.

하나 같이 치명적인 이유들로 Logger를 안 쓸 이유가 없는 것 같다. 이러한 기능들을 제공하는 Logger는 자바에서 제공하는 것 이외로 몇 종류가 더 있다.

Logging Framework

Java.util.logging

JDK에 포함되어 있는 logging 프레임워크이다. 하지만 기능이나 성능면에서 부족한점이 많고 그 때문에 다른 프레임워크를 많이 사용한다.

Apache Commons Logging

Commons Logging API는 자체적으로 로깅 기능은 구현하지 않고 로깅 요청을 Log4j나 자바 1.4로깅 API와 같은 이미 존재하는 로깅 API에 전달하는 다리 역할을 한다.

Log4J

Log4j는 가장 많이 사용되는 프레임 워크 중 하나이며 크게 Logger, Appender, Layout로 구성되어 있다.
또한 Log4J는 FATAL, ERROR, WARN, INFO, DEBUG 5단계의 로그 레벨을 가지고 있다. Log4j의 Appender는 로그를 어떻게 기록할 것인지 정할 수 있다.
이곳에서 보면 수많은 appender를 볼 수 있지만 크게 사용하는 것은 다음과 같다.

  • ConsoleAppender 로그를 콘솔에 남긴다.
  • FileAppender 로그를 파일 형태로 남긴다.
  • RollingFileAppender Triggering Policy나 RolloverPolicy(날짜, 크기 등)에 따라 새 파일에 기록한다.
  • SMTPAppener 로그를 이메일 형태로 남긴다.

Layout은 로그를 어떤 포맷에 남길지 설정할 수 있으며 그 종류는 아래와 같다.

  • CSV
  • HTML
  • JSON
  • Pattern 등

이 중 patter Layout을 자주 사용한다.

이어서 Log4j의 특, 장단점을 확인하며 다음으로 Logback에 대해 알아본다.

특징

  • 속도에 최적화
  • 멀티 스레드 환경에서 안전
  • 계층적인 로그 설정과 처리를 지원
  • 다양한 포맷의 출력
  • 로그 메시지 레벨

장점

  • 빠른 문제파악
  • 효율적이고 빠른 디버깅
  • 용이한 로그 파악
  • 여러 형태로 저장 가능

단점

  • 파일 입출력으로 인한 오버헤드 발생
  • 로깅 코드 추가로 인한 전체 코드 증대
  • 너무 많은 로그로 인한 혼란과 성능 저하 야기
  • 개발 중간에 추가하기 어려운 로깅 코드

Logback

Logback은 Log4j의 단점 개선과 기능이 추가된 개선판과 같다.
주요 설정 요소는 Logger, Appender, Encoder가 존재한다.

Logger, Appender와 다르게 Encoder는 log4j에서 볼 수 없었다. Encoder의 경우 Appender에 포함되며 Layout과 비슷하게 포맷과 관련되어 있다.
인코더 안에는 log4j에서와 같이 레이아웃을 사용할 수 있으며 기본적으로 패턴 레이아웃을 제공한다.
패턴에 관한 문서를 통해서 사용법을 익힐 수 있다.

또 Logback에서는 Logback 설정파일을 만들 수 있는데 이는 Gradle과 XML의 형식으로 가능하다.
확장자와 언어의 차이이므로 이 설정 파일들은 logback.(xml/groovy)로 설정해주면 된다.

logback은 다음과 같은 순서로 설정 파일을 찾는다.

  1. logback-test.xml
  2. logback.groovy
  3. logback.xml

위 순서로 파일을 찾아가며(1이 없는경우 2를 찾고 2가 없는 경우 3을 찾는다.) 모든 파일이 없다면 기본 설정 전략을 따른다.

이 설정파일에는 미리 설정한 패턴과 ConversionRule을 프로퍼티로 저장해놓고, 원하는 로그에 설정할 수 있다.

<conversionRule converterClass="org.springframework.boot.logging.logback.ColorConverter"
                    conversionWord = "clr"/>
<property name="CONSOLE_LOG_PATTERN" value="%clr(%d{HH:mm:ss.SSS}){cyan} [%thread] %clr(%-5level) %logger{36} - %msg%n"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>

또한 encoder의 패턴 레이아웃안에서 ConversionRule을 이용하면 converterClass에 지정해 놓은 클래스에 따라 날짜, 로거 이름, 레벨, 쓰레드 이름, 로거 색상 등 패턴 레이아웃에서 사용된 데이터 필드를 제어한다.

logback에서는 log4j와 마찬가지로 레벨을 제공한다.
하지만 log4j와의 레벨과는 다르며 아래와 같이 제공된다.

traceback-level.png

출력 레벨이 trace인 경우에는 모든 레벨을, error로 갈수록 레벨에 따라 출력한다.

SLF4J

SLF4J란 Simple Logging Facade For Java의 약자로 Logging Framework들을 추상화해 놓은 것이다.
여기서 Facade란 프랑스어에서 유래된 단어이며 이는 건물의 외관을 뜻한다. 디자인 패턴의 일종이며 복잡한 내부 구조를 거대한 클래스로 감싸 편리한 인터페이스를 제공하는 패턴이다.

SLF4J는 로깅 프레임워크를 연결하는 다리 역할의 바인딩 모듈을 통해 다양한 로깅 프레임워크를 지원한다.
대표적으로 logback과 log4j2 등이 있다.
아래는 SLF4J에 대한 기본적인 구상도이다.

concrete_bindings.png

출처 : https://www.slf4j.org/manual.html

사용

SLF4J, logback는 아래와 같이 사용될 수 있다.

public class SomeClass{
    private static final Logger logger = LoggerFactory.getLogger(SomeClass.class)
    public static void main(String [] args){
        for(int i = 1; i <= 2; i++){
        logger.trace("this is trace{}",i);
        logger.debug("this is debug{}",i);    
        logger.info("this is info{}",i);
        logger.warn("this is warn{}",i);
        logger.error("this is error{}",i);
        }
    }
============================================
18:36:39.616 [main] DEBUG org.logPractice.log.Main - this is debug1
18:36:39.622 [main] INFO  org.logPractice.log.Main - this is info1
18:36:39.623 [main] WARN  org.logPractice.log.Main - this is warn1
18:36:39.623 [main] DEBUG org.logPractice.log.Main - this is debug2
18:36:39.623 [main] INFO  org.logPractice.log.Main - this is info2
18:36:39.623 [main] ERROR org.logPractice.log.Main - this is error1
18:36:39.623 [main] WARN  org.logPractice.log.Main - this is warn2
18:36:39.623 [main] ERROR org.logPractice.log.Main - this is error2
}

하지만 이상하게도 레벨을 trace로 했음에도 불구하고 TRACE 레벨의 로그는 남지 않았다.
이는 로그의 출력 레벨이 기본적으로 DEBUG로 설정되어 있기 때문이고 이를 변경하기 위해서는 logback 설정 파일이 필요하다.

설정

설정 파일의 확장자는 groovy, xml이 가능하며 경로는 ClassPath이어야 한다.
또 이름은 logback, logback-test를 가질 수 있는데 쉽게 설정 파일은 아래와 같이 만들어야한다.

  1. logback.groovy
  2. logback-test
  3. logback.xml

이유는 당연하게도 위에서 언급했듯, 위 세개의 이름을 가지는 설정 파일만 찾기 때문이다.

설정 파일은 아래와 같은 구성을 통해 만들 수 있다.

<configuration>
    
    <property name="C_LOG_PATTERN" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/> 
    <!-- property 태그를 통해 콘솔에 출력할 패턴을 미리 설정해놓을 수 있다. -->
    <property name="F_LOG_PATTERN" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>
    <!-- property 태그를 통해 콘솔에 파일로 출력할 패턴을 미리 설정해놓을 수 있다. -->
    <timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 
    <!-- appender의 name을 STDOUT으로 지정하면 콘솔에 출력한다. -->
        <encoder>
            <pattern>${C_LOG_PATTERN}</pattern>
            <!-- pattern은  encoder 태그 내부에서 설정해줄 수 있다.-->
        </encoder>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <!-- name에서 유추할 수 있듯 파일 형태로 지정한다. -->
    <file>logs/kdt_${bySecond}.log</file>
    <append>false</append>
    <encoder>
        <pattern>${FILE_LOG_PATTERN}</pattern>
    </encoder>
    </appender>

    <appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs/access-%d{yyyy-MM-dd}.log</fileNamePattern>
        </rollingPolicy>
        
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <logger name = "org.logPractice.practice" level = "debug">
        <appender-ref ref="FILE"/>
    </logger>

    <root level="warn">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

더 자세한 설정 방법은 이곳에 있다.

로깅 프레임워크에 대해 배웠으니, 이제는 System.out 보다는 log를 적극적으로 남기도록 해야겠다.

참고문서

System.out을 지양해야 하는 이유
https://velog.io/@jsj3282/10.-로그는-반드시-필요한-내용만-찍자
https://sjparkk-dev1og.tistory.com/m/78 https://blog.silentsoft.org/archives/13

Log4j, Logback
https://goddaehee.tistory.com/45
https://logback.qos.ch/manual/configuration.html
https://logback.qos.ch/manual/architecture.html

태그:

카테고리:

업데이트:

댓글남기기