반응형
당연히 가장 먼저 할 일은 JCL 라이브러리를 받는 것이다. 아파치 다운로드 페이지로 간다.
현재 최신 버전은 1.0.4 (2004년 11월 8일 기준)
주요 클래스(인터페이스)를 살펴 보면 딸랑 두개다. 로깅을 담당하는 인터페이스인 Log와 Log 인스턴스를 만들어내는 팩토리 클래스인 LogFactory.
물론, JCL은 단지 API 역할만 하기 때문에 Log 인터페이스를 상속한 Log 객체를 쓰게 될 것이다.
JCL에서 지원하는 로그 수위는 Log4J와 JDK의 중간인 6단계다.
- trace (the least serious)
- debug
- info
- warn
- error
- fatal (the most serious)
실제 로깅을 수행할 때, 초보자가 가장 어려운 부분은 설정일 것이고, 그 후에는 효율적인 로깅을 할 수 있는 노하우나 절차를 익히는 것이다. 설정에 대해 배워보자.
JCL을 사용하는 이점 중에 하나는 JCL에 대해서는 설정할 것이 없다는 점이다. 다만, 구현체로 Log4J나 JDK 로깅을 쓰기 위해 이를 위한 설정만 해주면 된다. 어떻게 이것이 가능할까? 물론, JCL에서 이러한 작업을 수행하기 때문에 가능한 것이다. JCL에 포함된 LogFactoryImpl 클래스의 아래와 같은 코드가 이러한 매직을 만드는 것이다.
최대한 개발자의 편의를 위해서 commons-logging.properties 파일에 사용하게될 구현체를 설정하지 않으면, 아래 분기문을 수행하면서 Log4J가 사용 가능한지, JDK1.4가 사용 가능한지, Lumberjack 로깅이 사용 가능한지를 순서대로 살펴본다. 따라서, Log4J, JDK 1.4 로깅을 사용할 경우는 JCL에 대한 별도 설정이 필요없다. 여기서 JDK 1.4 기반에서 Log4J를 사용한다면, Log4J가 사용될 것이라는 점을 짐작할 수 있다. 먼저 logClassName이 설정되고 나면, 이후의 if문에서는 선행조건인 (logClassName == null)에서 걸리기 때문이다.
if ((logClassName == null) && isLog4JAvailable()) {
logClassName = "org.apache.commons.logging.impl.Log4JLogger";
}
logClassName = "org.apache.commons.logging.impl.Log4JLogger";
}
if ((logClassName == null) && isJdk14Available()) {
logClassName = "org.apache.commons.logging.impl.Jdk14Logger";
}
logClassName = "org.apache.commons.logging.impl.Jdk14Logger";
}
if ((logClassName == null) && isJdk13LumberjackAvailable()) {
logClassName = "org.apache.commons.logging.impl.Jdk13LumberjackLogger";
}
logClassName = "org.apache.commons.logging.impl.Jdk13LumberjackLogger";
}
if (logClassName == null) {
logClassName = "org.apache.commons.logging.impl.SimpleLog";
}
logClassName = "org.apache.commons.logging.impl.SimpleLog";
}
return (logClassName);
아래와 같이 로그 문장 하나를 만들어보자.
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.LogFactory;
/**
*
* @author 안영회
* @since 2004. 11. 8
*/
public class CommonsLoggerExample {
private static final Log logger
= LogFactory.getLog(CommonsLoggerExample.class.getName());
public static void main(String[] args) {
logger.info("hello, log");
}
}
= LogFactory.getLog(CommonsLoggerExample.class.getName());
public static void main(String[] args) {
logger.info("hello, log");
}
}
아래와 같은 출력문을 만날 수 있다. JDK 1.4 이상을 사용했고, Log4J를 설치하지 않았기 때문이다.
2004. 11. 8 오후 2:29:46 CommonsLoggerExample main
정보: hello, log
정보: hello, log
출력 형식과 어디에 출력할 것인가는 어떻게 결정된 것일까? JDK 설치 디렉토리 하위의 JRE/lib 디렉토리에서 logging.properties 파일을 찾을 수 있다. 여기서 JDK 로깅 관련 설정을 하게 된다.
############################################################
# Default Logging Configuration File
#
# You can use a different file by specifying a filename
# with the java.util.logging.config.file system property.
# For example java -Djava.util.logging.config.file=myfile
############################################################
# Default Logging Configuration File
#
# You can use a different file by specifying a filename
# with the java.util.logging.config.file system property.
# For example java -Djava.util.logging.config.file=myfile
############################################################
############################################################
# Global properties
############################################################
# Global properties
############################################################
# "handlers" specifies a comma separated list of log Handler
# classes. These handlers will be installed during VM startup.
# Note that these classes must be on the system classpath.
# By default we only configure a ConsoleHandler, which will only
# show messages at the INFO and above levels.
handlers= java.util.logging.ConsoleHandler
# classes. These handlers will be installed during VM startup.
# Note that these classes must be on the system classpath.
# By default we only configure a ConsoleHandler, which will only
# show messages at the INFO and above levels.
handlers= java.util.logging.ConsoleHandler
# To also add the FileHandler, use the following line instead.
#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
# Default global logging level.
# This specifies which kinds of events are logged across
# all loggers. For any given facility this global level
# can be overriden by a facility specific level
# Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console.
.level= INFO
# This specifies which kinds of events are logged across
# all loggers. For any given facility this global level
# can be overriden by a facility specific level
# Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console.
.level= INFO
############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################
# default file output is in user's home directory.
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
# Limit the message that are printed on the console to INFO and above.
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
############################################################
# Facility specific properties.
# Provides extra control for each logger.
############################################################
# For example, set the com.xyz.foo logger to only log SEVERE
# messages:
com.xyz.foo.level = SEVERE
# messages:
com.xyz.foo.level = SEVERE
logging.properties 파일의 내용이다. 천천히 살펴보자.
# Default Logging Configuration File
#
# You can use a different file by specifying a filename
# with the java.util.logging.config.file system property.
# For example java -Djava.util.logging.config.file=myfile
디폴트 로깅 설정 파일이라는 것이고, -D 옵션을 넣어서 실행하여, java.util.logging.config.file 프로퍼티를 키로 설정 파일을 다른 것으로 지정할 수 있다는 것이다.
# Global properties
# "handlers" specifies a comma separated list of log Handler
# classes. These handlers will be installed during VM startup.
# Note that these classes must be on the system classpath.
# By default we only configure a ConsoleHandler, which will only
# show messages at the INFO and above levels.
handlers= java.util.logging.ConsoleHandler
# classes. These handlers will be installed during VM startup.
# Note that these classes must be on the system classpath.
# By default we only configure a ConsoleHandler, which will only
# show messages at the INFO and above levels.
handlers= java.util.logging.ConsoleHandler
# To also add the FileHandler, use the following line instead.
#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
전역 설정. 즉, 로깅 전체에 영향을 미치는 설정을 나타낸다. handlers 프로퍼티는 로그 Handler 클래스 설정에 사용되는데, 로그 핸들러(log handler)가 여러 개인 경우에는 콤마로 구분하여 나열한다. 로그 핸들러 클래스는 당연히 클래스패스에 위치해야 하고, 디폴트는 INFO 이상의 로그 수위만 화면에 출력하는 ConsoleHandler이다.
# Default global logging level.
# This specifies which kinds of events are logged across
# all loggers. For any given facility this global level
# can be overriden by a facility specific level
# Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console.
.level= INFO
# This specifies which kinds of events are logged across
# all loggers. For any given facility this global level
# can be overriden by a facility specific level
# Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console.
.level= INFO
모든 로거에 기본적으로 적용할 로그 수위는 INFO라는 것이다.
############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################
# default file output is in user's home directory.
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
# Limit the message that are printed on the console to INFO and above.
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
특정 핸들러에만 적용시킬 프로퍼티. 자세한 내용은 파일 로거를 쓰면서 확인해보자.
############################################################
# Facility specific properties.
# Provides extra control for each logger.
############################################################
# For example, set the com.xyz.foo logger to only log SEVERE
# messages:
com.xyz.foo.level = SEVERE
# messages:
com.xyz.foo.level = SEVERE
특정 퍼시리티에만 적용시킬 프로퍼티, 핸드러별 설정보다 더 미세하게 특정 로거단위로도 설정이 가능하다.
# show messages at the INFO and above levels.
handlers= java.util.logging.ConsoleHandler
handlers= java.util.logging.ConsoleHandler
# To also add the FileHandler, use the following line instead.
#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
위와 같은 설정을 아래처럼 바꿔서 파일 로그 핸들러도 쓰이도록 해보자.
# show messages at the INFO and above levels.
# handlers= java.util.logging.ConsoleHandler
# handlers= java.util.logging.ConsoleHandler
# To also add the FileHandler, use the following line instead.
handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
어디에 출력이 되었을까? 힌트는 logging.properties 파일의 파일 핸들러 관련 설정이다.
# default file output is in user's home directory.
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
%h는 뭔가? JDK FileHandler API 문서에서 설명을 찾을 수 있다.
%h는 Home directory를 의미하는 것이다. 운영체제 그리고 윈도우의 경우는 버전에 따라 디렉토리가 조금씩 다르다. 기본적으로 NT 기반인 경우(필자는 XP Pro), "C:Documents and Settings로그인한 사용자 이름"이다. 홈 디렉토리 밑에 java로 시작하고, %u에 따라 파일이름에 의한 충돌을 방지하기 위한 일련번호를 붙힌다. 확장자는 log.
그래서, 필자의 경우는 홈 디렉토리에 java0.log라는 파일이 생겼다. 다른 위치에 파일이 출력되도록 변경해보자.
java.util.logging.FileHandler.pattern = mylog_%u.log
위와 같이 변경한 후 로그가 출력되는 프로그램을 실행시키자. 클래스 파일을 실행시킨 위치에 mylog0.log라는 이름으로 파일이 생길 것이다. 나머지 설정들의 의미를 알아보자.
java.util.logging.FileHandler.limit = 50000
하나의 파일에 최대로 기록될 바이트수를 대략적으로 지정한 것이다. 디폴트는 0이며, 무제한이다.
java.util.logging.FileHandler.count = 1
출력파일을 몇 개 사용할 것인가. 디폴트는 1.
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
출력 포맷을 결정하는 포맷터 클래스. 기본적으로 SimpleFormatter와 XMLFormatter가 있으며, 디폴트는 XMLFormatter이다.
SimpleFormatter를 사용한 경우의 출력과 XMLFormatter를 사용한 경우의 출력은 아래와 같다.
SimpleFormatter 사용시
2004. 11. 8 오후 4:49:34 CommonsLoggerExample main
정보: hello, log
정보: hello, log
XMLFormatter 사용시
<?xml version="1.0" encoding="x-windows-949" standalone="no"?>
<!DOCTYPE log SYSTEM "logger.dtd">
<log>
<record>
<date>2004-11-08T16:37:25</date>
<millis>1099899445360</millis>
<sequence>0</sequence>
<logger>CommonsLoggerExample</logger>
<level>INFO</level>
<class>CommonsLoggerExample</class>
<method>main</method>
<thread>10</thread>
<message>hello, log</message>
</record>
</log>
<!DOCTYPE log SYSTEM "logger.dtd">
<log>
<record>
<date>2004-11-08T16:37:25</date>
<millis>1099899445360</millis>
<sequence>0</sequence>
<logger>CommonsLoggerExample</logger>
<level>INFO</level>
<class>CommonsLoggerExample</class>
<method>main</method>
<thread>10</thread>
<message>hello, log</message>
</record>
</log>