1. 소개

Apache log4j - Apache Logging Services

자바의 System 클래스를 사용하면 System.out.print (혹은 println) 메소드를 이용해서 정보를 기록할 수 있다.

그런데.. 보통 체계적으로(?) 코딩을 하게 되면 System.out.print를 직접 사용하는 경우는 드물다. (급할 때는 쓰겠지만)

아무래도 log4j를 많이 쓴다. 요즘에도 활발히 업데이트가 된다. 지난 3월에 2.8.1이 나왔다는 정보도 있었다.. 단순히 개발 단계에만 사용하는건 아니다. 개발 단계에서는 디버그나 트레이스를 위해 사용하고 운영 단계에서는 정보 확인 및 성능 개선을 위해서도 쓴다.


2. slf4j와 log4j의 차이점은?

간혹 slf4j, log4j가 서로를 대체할 수 있냐는 질문들이 있다. 결론적으로 옳지 않다. slf4j는 abstract layer이므로 서로는 상생관계이다.


3. logger와 appender의 차이점은?

먼저 Log4j의 구조적인 부분을 이해하는 것이 필요하다. Logger와 Appender를 각각 등록하고 Logger별로 필요한 Appender를 주입하여 하나의 시스템에서 다양한 로그를 기록하는 방식이다.

  • Logger는 로그 파일을 작성하는 클래스 자체이다. 즉, 로그의 주체라고 할 수 있다. 설정을 제외한 거의 모든 기능이 Logger를 통해 처리된다. 애플리케이션마다 사용할 로거를 정하고 로그 레벨과 Appender를 지정할 수 있다.
  • Appender는 로그를 받을 대상이나 방법을 정의하는 것이다. 로그를 받을 대상이라고 하면 콘솔, 파일, Socket, Mail, Message, DB 등 다양하다. (CoonsoleAppender, FileAppender, RollingFileAppender, DailyRollingFileAppencer 등이 구현체의 예다)

4. 로그 레벨

TRACE, DEBUG, INFO, WARN, ERROR, FATAL 등의 레벨이 있다.

현재 로그 레벨이 WARN이라면,

  • logger.debug("string"); -> 출력안됨
  • logger.warn("string"); -> 출력됨
  • logger.error("string"); -> 출력됨

로그 출력 패턴은 다음과 같다.

%d 날짜
%p 로그레벨
%C 클래스명
%m 메시지 문자열
%n 줄바꿈
%F 파일명
%I caller 정보
%L caller 라인수
%M 메소드명
%t 스레드명

5. MDC란?

MDC는 message diagnostic context를 뜻한다. 한마디로 말하면 좀 더 특별한 내용을 기록하기 위해 사용하는 것이다.

샘플 코드는 https://veerasundar.com/blog/2009/11/log4j-mdc-mapped-diagnostic-context-example-code/ 와 같은 링크를 읽어보자.

MDC와 Filter 기능을 이용하면 사용자에 따라 로그 레벨을 다르게 설정할 수 있다. 이는 사용자가 Cookie에 로그 레벨을 실어 보낸 후 log4j의 ThreadContext에 설정하는 방식이다. 실제 로깅 시에는 DynamicThresholdLevelFilter가 Cookie의 로그 레벨과 로깅 메소드의 로그 레벨을 비교한 후 로깅 여부를 판단한다.


6. 파일 변경 감지는?

Log4jConfigListener가 존재한다. (org.springframework.web.util.Log4jConfigListener)

각 파일 위치가 파일에 대한 refresh 주기를 지정한다.

param-name이 log4jRefreshInterval이라면, param-value가 초 단위 숫자이다. 예를 들어 3600초면 1시간이다.


7. 비동기

7.1. 비동기 로깅 지원

Log4j는 비동기 로깅을 지원한다. 

  • 1.x : Asynchronous Appenders
  • 2.x : Asynchronous Loggers

단, 비동기 로깅이 무조건 좋은 것은 아니다.

  • 만약 로그를 기록하는 중에 에러가 발생할 때 ExceptionHandler를 사용하여 Exception을 전달하겠지만 보장되지는 않는다.
  • 동기와 달리 로깅 시점이 달라지기 때문에 만약 객체를 로깅하는 경우는 로깅 시점에 객체의 값이 달라져있을 수 있다. 

7.2. log4j 2.x 버전의 비동기 로깅 설정

물론 비동기 로깅만 사용할 수도 있고, 동기+비동기 로깅을 혼용할 수도 있다.

비동기 로깅만 사용하고자 한다면 기존 설정을 수정할 필요 없이 다음과 같은 System Property만 추가한다.

-DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

혼용하고자 한다면 설정 파일을 수정한다. Logger 태그를 AsyncLogger, Root 태그를 AsyncRoot로 변경한다.

7.3. log4j 1.x 버전의 비동기 로깅 설정

다음은 AsyncAppender 설정 예이다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd" >
<log4j:configuration>
    <appender name="stdout" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{yyyy/MM/dd HH:mm:ss,SSS} [%t] %-5p %c %x - %m%n"/>
        </layout>
    </appender>
    <appender name="ASYNC" class="org.apache.log4j.AsyncAppender">
        <param name="BufferSize" value="500"/>
        <param name="locationInfo" value="true"/>
        <appender-ref ref="stdout"/>
    </appender>
    <root>
        <priority value="error"></priority>
        <appender-ref ref="ASYNC"/>
    </root>
</log4j:configuration>

8. 기타

log4j와 System.out.print(println)을 혼용하지 않는다. 관리상의 이슈가 존재한다.