3 minute read

Logging이란

개발자로 일을 시작하면서 자연스럽게 접하게 되는 게 log입니다. log는 코드 사이에 적용할 수 있는 라이브러리로 어플리케이션 동작 기록이라고 보시면 됩니다. 로그는 특정 클래스나 메서드 작동, 흐름, 기능 설명에 사용되며, 에러가 발생했을 때 디버깅 방법 중 하나로도 사용됩니다.

System.out.println 쓰면 안되는 이유

처음 코딩을 시작할 때 입문용 책에는 출력으로 System.out.println을 소개합니다. 추가적인 라이브러리 적용도 필요 없고 자바에 내장된 System 객체로 바로 쓸 수 있어 편리하니 바꿀 이유가 없어 보입니다. 하지만 주변이나 인터넷에서 무조건 sysout을 쓰면 안 된다는 말을 많이 듣고 보셨을 겁니다.

image

이유로는 크게 2가지로, 블로킹 IO멀티 쓰레드 락인(lock-in)이 발생하기 때문입니다. sysout 라인을 실행할 때 현 쓰레드는 sysout이 끝나기 전까지 아무 일을 실행할 수 없고 대기해야 하며 성능을 저하시킵니다. 문제는 현재 쓰레드도 멈추지만 멀티 쓰레드 환경에서는 다른 쓰레드 기동까지 대기 상태로 만듭니다. 그러니 sysout은 테스트나 빠른 확인용으로만 사용하고, 실제 운영에서는 지양하는 게 좋습니다.


log 종류

sysout을 피해 logging을 사용하기로 결정됐다면 어떤 logging을 사용하는 게 좋을까요? 현재 사용 가능한 후보 중 대표적인 로거(logger) 라이브러리를 몇 개 설명하겠습니다.

Log4j

Log4j는 아파치 재단에서 만든 로깅 라이브러리(logging library)입니다. 2015년 8월 5일 기준으로 개발 중단(end of life)가 선언되서 더 이상 업데이트가 되지 않습니다. 개발 중단 외에도 자바 9버전과 호환성 문제와 직렬화/역직렬화 객체 허용 여부를 체크하지 않아 보안 상의 문제가 있는 등 추천되지는 않습니다.

Logback

Logback은 log4j의 개선된 버전으로 소개되며 스프링 부트의 기본 로깅 라이브러리로 채택되었습니다. 특징 몇 개만 나열해 보겠습니다.

  • Tomcat, Jetty와 같은 서블릿 컨테이너와 통합이 가능해 HTTP 엑세스 로기 기능 가능
  • log4j보다 10배 빠르고 메모리 점유율이 낮음
  • 수년 간의 엄격한 테스팅을 거쳐 안정성 보증(logback 개발자가 제일 강조하는 부분이었습니다.)
  • 에러나 IO failure로 인한 재설정(reconfiguration)이 자동으로 실행되면서 빠르게 복구 가능
  • slf4j api 활용 가능
    slf4j는 로깅 라이브러리의 추상화라고 보시면 됩니다. 여러 로깅 프레임워크로 갈아 끼울 수 있게 지원해줘서 기능 적용 유연성을 보장해줍니다. 2 버전은 자바8부터 지원하니 참고하세요.

자세한 내용을 보고 싶으시다면 이 링크를 클릭하시면 됩니다.

Log4j2

Log4j를 만든 아파치 재단에서 Log4j와 Logback의 설계적 결함을 개선한 로깅 API로 소개합니다.

람다 표현식 지원
말 그대로 자바 8버전부터 지원되는 람다 표현식에 로그를 사용할 수 있습니다.


비동기적 로거 지원
멀티 쓰레드 환경에서 Log4j보다 18배의 처리량(throughput)과 지연 시간(latency) 감소를 보입니다. 처리량은 한 번에 수행할 수 있는 개수를 뜻하고 지연 시간은 일 수행에 걸리는 시간을 뜻합니다. 아파치 웹사이트에 소개한 멀티 쓰레드 환경에서 Log4j2의 성능에 대해 자세하게 설명합니다.

image

처리량의 경우 쓰레드 개수가 늘어날 수록 완전한 비동기적 로거의 성능이 혼합 비동기적 로거나 동기적 로거보다 더 빠른 수행률을 보이는 걸 알 수 있습니다.

image

지연 시간의 경우에도 Log4j2 garbage-free 완전한 비동기적 로거가 훨씬 단축된 시간에 일 처리가 가능함을 보여줍니다.


사용자가 지정하는 로그 이벤트
info, debug, error 같은 기존 로그 레벨 외에 사용자가 직접 지정하는 로그 레벨이다. Level.forName() 메서드를 이용해서 사용자가 직접 로그 레벨을 지정할 수 있으며, Logger.log() 메서드에 설정한 로그 레벨을 넣어서 해당 로그를 찍을 수 있다. 공식 문서에서 소개하는 사용자 지정 방식이다

// This creates the "VERBOSE" level if it does not exist yet.
final Level VERBOSE = Level.forName("VERBOSE", 550);

final Logger logger = LogManager.getLogger();
logger.log(VERBOSE, "a verbose message"); // use the custom VERBOSE level

여러 레벨을 한꺼번에 지정하고 싶다면 configuration으로도 정의하는 방법을 공식 문서에서 확인할 수 있다.


가비지 프리(garbage free)
가비지 생성이 낮아 gc 발생 확률을 줄이고 성능을 높일 수 있습니다.


도커 사용 가능
도커는 여러 라인에 걸쳐진 로그(multiline logging)을 하나가 아닌 여러 이벤트에 걸쳐진 로그로 인식해 성능이 느려집니다. 하지만 Log4j2을 사용해 도커/쿠버네티스 환경에서 멀티 로깅이 가능합니다.

이렇게 많은 장점을 가지고 있는 Log4j2이지만 멀티 쓰레드 환경에서 동기적 로거에 비해 에러 핸들링이 불편합니다. 또한 CPU나 코어 개수가 적으면 오히려 성능이 감소하는 특징이 있으니 주의해서 사용해야 합니다.


적용

여러 로깅 라이브러리를 찾아본 결과 Log4j2를 활용하게 되었습니다. 로그를 쓰는 가장 큰 목적인 빠른 속도와 전체 어플리케이션의 부하 최소화에 적격이라 생각했습니다.


제가 현재 진행하는 프로젝트에 적용했는데 굉장히 쉽습니다. 먼저 스프링부트에 메이븐 빌드 툴을 사용하고 있어 pom.xml에 기존 로깅 라이브러리인 Logback 기능을 배제하고 Log4j2를 적용합니다.

<!-- Logback 기능 빼기 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>

...

<!-- Log4j2 적용 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>


그 다음 로깅을 사용하고 싶은 클래스에 Log4j2 어노테이션을 적용하고 log와 로깅 레벨을 지정해주면 끝납니다.

@Log4j2
public class GameController {
    ...
    log.info("...")


마치며

지금까지 로그 종류와 Log4j의 설정 방법에 대해 알아봤습니다. 설명한 종류 외에도 더 많은 라이브러리가 있기에 검색해서 본인에게 적합한 라이브러리를 고르면 됩니다.


긴 글 읽어주셔서 감사합니다.
현재 게임 사이트 프로젝트를 진행 중인데 혹시 관심 있으시다면 한 번 들러주세요.
프로젝트 보기 : 링크


출처

Log4j
http://logging.apache.org/log4j/1.2/
Logback
http://logback.qos.ch/
Log4j2
https://logging.apache.org/log4j/2.x/
slf4j
http://www.slf4j.org/

Categories:

Updated:

Comments