상세 컨텐츠

본문 제목

log4j (log4j.xml)

프로그래밍/JAVA

by 라제폰 2011. 6. 2. 11:03

본문


Logging Service

개발자가 Log을 출력하기 위해 일반적으로 사용하는 방식은 System.out.println()이다. 그러나 이 방식은 간편한 반면에 다음과 같은 이유로 권장하지 않는다.
  • System.out.println에 대한 호출은 disk I/O동안 동기화(synchronized)처리가 되므로 시스템의 throughput을 떨어뜨린다.
  • 기본적으로 stack trace 결과는 콘솔에 남는다. 하지만 시스템 운영중 콘솔을 통해 Exception을 추적하는 것은 바람직하지 못하다.
  • 운영시스템에서 시스템 관리자가 System.out과 system.errs에 대하여 ‘[>null]’(NT의 경우) 혹은 dev/null(Unix의 경우)와 같이 매핑을 할 경우 Exception 로그에 대한 출력이 나타나지 않을 수도 있다. 또한 NT 서비스로 실행될 경우 콘솔 자체가 안보일 수 도 있다.
  • 콘솔 로그를 출력 파일로 리다이렉트 할 지라도, J2EE App Server가 재 시작할 때 파일이 overwrite될 수도 있다.
  • 개발/테스팅 시점에만 System.out.println을 사용하고 운영으로 이관하기 전에 삭제하는 것은 좋은 방법이 아니다. 운영시의 코드가 테스트시의 코드와 다르게 동작할 수 있기 때문이다.
따라서, 테스팅 코드와 운영 코드를 동일하게 가져가면서 로깅을 선언적으로 관리할 수 있고, 운영시 성능 오버헤드를 최소화할 수 있는 메커니즘이 필요하다. 이런 기능을 위해 Anyframe Framework은 Log4j 를 이용하여 로그를 남길 수 있는 방법을 가이드하고자 한다.

Logging Service Configuration 정의하기

이번 절에서는 log4j.xml 파일을 구성하는 Tag 중, 주로 사용될 일부 Tag에 대해 설명하고자 한다. 보다 자세한 내용에 대해서는 Log4j 를 참조하도록 한다. log4j.xml 파일의 root tag인 <log4j:configuration>은 하위에 appender, logger, root등의 tag를 가질 수 있다.
Tag 명
설명
필수 여부
appender 로그가 출력될 대상과 방법을 정의한다. 여러 appender 정의 가능.
N
logger 어플리케이션에서 사용될 Logger를 정의한다. 여러 logger 정의 가능
N
root 모든 logger의 상위 logger를 정의한다.
N

위 표에서 열거한 각 Tag에 대해 보다 자세히 알아보도록 하자.

appender

Log4j는 다양한 로그 방식을 지원한다. 가장 단순한 Console부터 시작해서 파일, DB, SMTP 등의 방식들을 지원한다.
  • org.apache.log4j.ConsoleAppender : Console 화면으로 출력하기 위한 Appender. 다음은 log4j.xml 파일 내의 ConsoleAppender에 대한 속성 정의 내용이다.
    <appender name="console" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %p [%c] - &lt;%m&gt;%n" />
        </layout>   
    </appender>
  • org.apache.log4j.FileAppender : 특정 파일에 로그를 출력하기 위한 Appender로 하위에 File, Append와 같은 parameter를 정의할 수 있다. 다음은 log4j.xml 파일 내의 FileAppender에 대한 속성 정의 내용이다.
    <appender name="file" class="org.apache.log4j.FileAppender">
    	<!-- 로그 파일명 정의를 위한 parameter -->
    	<param name="File" value="./logs/file/sample.log"/>
    	<!-- 이전 로그 파일에 로그를 덧붙여 쓸 것인지를 정의하기 위한 parameter -->
    	<param name="Append" value="true"/>    	  
    	<layout class="org.apache.log4j.PatternLayout">
    	    <param name="ConversionPattern" value="%d %p [%c] - &lt;%m&gt;%n" />
    	</layout>   
    </appender>
  • org.apache.log4j.RollingFileAppender : FileAppender는 지정한 파일에 로그가 계속 남으므로 한 파일의 크기가 지나치게 커질 수 있으며 계획적인 로그 관리가 어렵다. 따라서 파일의 크기 또는 파일 백업 인덱스 등의 지정을 통해 특정 크기 이상 파일의 크기가 커지게 되면 기존 파일을 백업 파일로 바꾸고 처음부터 다시 로깅을 시작한다. 하위에 File, Append, MaxFileSize, MaxBackupIndex와 같은 parameter를 정의할 수 있다. 다음은 log4j.xml 파일 내의 RollingFileAppender에 대한 속성 정의 내용이다.
    <appender name="rollingFile" class="org.apache.log4j.RollingFileAppender">
    	<!-- 로그 파일명 정의를 위한 parameter -->
    	<param name="File" value="./logs/rolling/sample.log"/>
    	<!-- 이전 로그 파일에 로그를 덧붙여 쓸 것인지를 정의하기 위한 parameter -->
    	<param name="Append" value="true"/> 
    	<!-- 로그 파일의 최대 크기를 정의하기 위한 parameter -->   	  
    	<param name="MaxFileSize" value="1KB"/>    
    	<!-- 로그 파일 백업 인덱스를 정의하기 위한 parameter -->
    	<param name="MaxBackupIndex" value="2"/>    
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %p [%c] - &lt;%m&gt;%n" />
        </layout>   
    </appender>
  • org.apache.log4j.DailyRollingFileAppender : 설정한 날짜 또는 조건에 맞춰 로깅을 수행하기 위한 Appender. DailyRollingFileAppender 파일에서 사용할 수 있는 몇가지 날짜 포맷은 다음과 같다. 하위에 File, DatePattern과 같은 parameter를 정의할 수 있다.
    • .yyyy-MM : 매달 첫번째 날에 로그파일 변경
    • .yyyy-ww : 매주 시작시 로그파일 변경
    • .yyyy-MM-dd : 매일 자정에 로그파일 변경
    • .yyyy-MM-dd-a : 자정과 정오에 로그파일 변경
    • .yyyy-MM-dd-HH : 매시간의 시작마다 로그파일 변경
    • .yyyy-MM-dd-HH-mm : 매분마다 로그파일 변경
    보다 자세한 사항은 Log4j API 를 참조한다.
    다음은 log4j.xml 파일 내의 DailyRollingFileAppender에 대한 속성 정의 내용이다.
    <appender name="dailyRollingFile" class="org.apache.log4j.DailyRollingFileAppender">
    	<!-- 로그 파일명 정의를 위한 parameter -->
    	<param name="File" value="./logs/daily/sample.log"/>
    	<!-- 로그 파일을 Rolling하기 위한 날짜 조건을 정의하기 위한 parameter -->
    	<param name="DatePattern" value=".yyyy-MM-dd-HH-mm"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %p [%c] - &lt;%m&gt;%n" />
        </layout>   
    </appender>
  • org.apache.log4j.jdbc.JDBCAppender : DB에 로그를 출력하기 위한 Appender로 하위에 Driver, URL, User, Password, Sql과 같은 parameter를 정의할 수 있다. 다음은 log4j.xml 파일 내의 JDBCAppender에 대한 속성 정의 내용이다.
    <appender name="db" class="org.apache.log4j.jdbc.JDBCAppender">
    	<!-- JDBC Driver를 정의하기 위한 parameter -->
    	<param name="Driver" value="oracle.jdbc.driver.OracleDriver"/>
    	<!-- DB URL을 정의하기 위한 parameter -->
    	<param name="URL" value="jdbc:oracle:thin:@107.108.150.108:1521:ora10"/>
    	<!-- DB User를 정의하기 위한 parameter -->
    	<param name="User" value="anyframe"/>
    	<!-- DB Password를 정의하기 위한 parameter -->
    	<param name="Password" value="anyframe"/>
    	<!-- 로그를 남길때 수행할 쿼리를 정의하기 위한 parameter -->
    	<param name="Sql" value="insert into STMR_LOG (msg)
    	                        values('%d %p [%c] - &lt;%m&gt;%n')"/>
    </appender>
Appender Layout
로그를 남길때 단순한 메시지 외에도 현재 로그 대상의 쓰레드명, 로그 시간 등 많은 정보들을 조합할 수 있다. Layout에는 org.apache.log4j.HTMLLayout, org.apache.log4j.PatternLayout, org.apache.log4j.SimpleLayout, org.apache.log4j.xml.XMLLayout 등이 있다. 이중 가장 많이 사용하는 Layout은 PatternLayout으로서 C 함수의 printf처럼 다양한 로그 메시지 조합을 만들어 낼 수 있다.
  • %p : debug, infor, warn, error, fatal 등의 Priority 출력
  • %m : debug(), info(), warn(), error(), fatal() 등의 함수로 지정한 로그 내용 출력
  • %d : 로깅 이벤트가 발생한 시간 기록. 출력 포맷은 %d 후의 {}내에 지정된 형태를 따른다. %d{HH:mm:ss, SSS} 라든가 %d{yyyy MMM dd HH:mm:ss, SSS}와 같은 형태로 사용할 수 있다. Java의 SimpleDateFormat의 형식을 따라 정의할 수 있다.
  • %t : 로깅 이벤트가 발생된 쓰레드의 이름 출력
  • %% : % 표시를 출력하기 위해 사용
  • %n : 플랫폼 종속적인 개행 문자 출력. \r\n 또는 \n이 될 것이다.

logger

로깅 이벤트 발생시 같은 이름으로 선언된 logger를 찾아 해당 logger에게 로그 메시지를 보내고 additivity가 true일 경우, 상위 logger에게도 로그 메시지를 보낸다. 다음은 log4j.xml 파일 내의 logger에 대한 속성 정의 내용이다.
<!-- 해당 logger명이 anyframe.services로 시작할 경우 Console에 DEBUG level로 로그를 남긴다.-->
<logger name="anyframe.services">
	<!-- DEBUG, INFO, WARN, ERROR, FATAL, OFF 중 택일 -->
	<level value="DEBUG"/>
	<!-- 여러 appender-ref 정의 가능 -->
	<appender-ref ref="console"/>
</logger>

root

해당 logger가 존재하지 않거나 상위 logger가 존재하지 않을 경우 모든 로그는 root logger의 정책에 따라 출력된다. 다음은 log4j.xml 파일 내의 root에 대한 속성 정의 내용이다.
<root>
    <level value="INFO"/>
    <appender-ref ref="console"/>
</root>

Logging Service 사용하기

로그의 내용에 따라 다양한 레벨(DEBUG, INFO, WARN, ERROR, FATAL)로 선택 가능하다. 각각은 메소드 debug(), info(), warn(), error(), fatao()라는 5가지 메소드를 이용해서 로그를 남길 수 있다. 다만 이때 Logger에 지정된 로그 레벨이 있다면, 지정된 로그 레벨 이하의 로깅 이벤트는 무시된다. 따라서 로그도 남지 않는다. 또한, 로그 메시지는 별도 Resource 파일에 정의된 message key를 이용하여 남기면 메시지 변경 및 다국어 지원이 용이하다. 다음에서는 로그 메시지를 남기기 위한 기본 방법ResourceBundle을 이용하는 방법 에 대해서 알아보고자 한다.

기본적인 사용 방법

다음은 기본적인 방법을 사용하여 로그 메시지를 남기는 LoggingServiceTest.java 코드의 일부이다.
/**
 * resources/test/resources/common/conf/log4j.xml 파일 설정에 따라 Logger명이
 * integration.anyframe.services.logging.LoggingServiceTest인 Logger를 찾고, 해당
 * Logger를 통해 로그 메시지를 남겨보고 해당 Logger의 레벨이 DEBUG 가능한지 체크해보는 테스트
 */
public void testLogging() throws Exception {
	Log logger = LogFactory.getLog(LoggingServiceTest.class);
	logger.debug("Sample DEBUG Message.");
	logger.info("Sample Info Message.");
	logger.warn("Sample Warning Message.");
	logger.error("Sample Error Message.");
	logger.fatal("Sample Fatal Message.");
	if (!logger.isDebugEnabled())
		throw new Exception("fail to get Logger");
}

ResourceBundle을 이용하는 방법

특정 서비스의 구현 클래스에서 ResourceBundle을 이용하여 로그 메시지를 남기기 위해서는 다음과 같은 절차를 따르도록 한다.
1. ResourceBundle을 관리하는 기능을 제공하는 MessageSource Bean을 정의한다.
다음은 MessageSource Bean을 정의하고 있는 applicationContext-common.xml 파일의 일부이다.
<bean name="messageSource" 
    class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basenames">
        <list>
            <!-- 중략 -->
            <value>
               classpath:/message/message-sample
            </value>
        </list>
        <!-- 중략 -->
    </property>
</bean>
2. 특정 서비스의 구현 클래스는 MessageSource Bean을 인식하기 위하여 implements ApplicationContextAware해야 한다.
다음은 MessageSource Bean을 이용하여 로그 메시지를 남기는 LoggingSampleServiceImpl.java 의 일부 코드이다.
public class LoggingSampleServiceImpl implements LoggingSampleService,
		ApplicationContextAware {
	private MessageSource messageSource = null;
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		this.messageSource = (MessageSource) applicationContext
				.getBean("messageSource");
	}
	
	// 중략
}
3. Logging Service를 이용하여 로그를 남길 때 MessageSource Bean을 이용한다.
다음은 Service 구현 클래스에서 사용할 Logger를 정의한 인터페이스 클래스 LoggingSampleService.java 코드이다.
public interface LoggingSampleService {
	String ROLE = LoggingSampleService.class.getName();
	// LoggingSampleServiceImpl에서 사용할 Logger 정의
	Log LOGGER = LogFactory.getLog(LoggingSampleService.class);
	public String greet();
}
다음은 ResourceBundle을 이용하여 로그 메시지를 남기는 구현 클래스 LoggingSampleServiceImpl.java 의 일부 코드이다.
public String greet() {
	// ResourceBundle을 이용하여 로그 메시지를 남긴다. (argument가 없는 경우)
	LoggingSampleService.LOGGER.debug(messageSource.getMessage(
			"sample.default.msg", new String[] {}, Locale.getDefault()));
	// ResourceBundle을 이용하여 로그 메시지를 남긴다. (argument가 1개인 경우)
	LoggingSampleService.LOGGER.debug(messageSource.getMessage(
			"sample.msg", new String[] { "GilDong" }, Locale.getDefault()));
	return "Hello";
}
* 위의 코드에서 참조하고 있는 Resource 파일 message-sample.properties 의 내용은 다음과 같이 key=value 형태로 정의한다.
sample.default.msg=Hello Guest
sample.msg=Hello {0}

관련글 더보기