본문 바로가기

프로그래밍/JAVA

[JAVA] log4j tutorial 번역

제가 번역한게 아니고 퍼왔습니다. 

출처는 아래에 명시되어 있어요.



================================================================


역자: noct ( http://www.noct.pe.kr/ )
원본 : http://www.vipan.com/htdocs/log4jhelp.html
 
                      System.out.println 을 사용하지 말고 Log4j 를 사용하라.




                      Log4j를 사용하기 위한 빠른 시작


- log4j 소프트웨어(약 2.3MB)를 다운로드해서 log4j.jar (약 156KB)를 추출하라.
logging 메소드가 필요한 클래스들을 찾을 수 있도록 log4j.jar 파일을 어플리케이션의 클래스패스에 포함시켜라.
(필자는 log4j.jar 파일을 자바 설치 디렉토리 밑의 lib/ext 디렉토리에 카피했다. 왜냐하면 자바는 자동적으로
그 디렉토리에서 얻어오고 클래스패스에 추가하기 때문이다.)

- 다음의 예제코드를 TestLogging.java 로 저장해여 클래스패스로 지정된 디렉토리에 위치시키라.

import org.apache.log4j.*;

// log4j 사용방법
public class TestLogging {

    // loggin 카테고리를 초기화한다.  여기서 THE ROOT CATEGORY 를 얻는다
    //static Category cat = Category.getRoot();
    // 또는 사용자 정의 category를 얻는다
    static Category cat = Category.getInstance(TestLogging.class.getName());

    // 여기서부터 로깅한다!  해당메소드들: cat.debug(로그메세지문자열),
    // cat.info(...), cat.warn(...), cat.error(...), cat.fatal(...)

    public static void main(String args[]) {
        // 몇가지 로깅 메소드들.
        cat.debug("Start of main()");
        cat.info("Just testing a log message with priority set to INFO");
        cat.warn("Just testing a log message with priority set to WARN");
        cat.error("Just testing a log message with priority set to ERROR");
        cat.fatal("Just testing a log message with priority set to FATAL");

        // 대체할 수 있지만 불편한 형식
        cat.log(Priority.DEBUG, "Calling init()");
       
        new TestLogging().init();
    }

    public void init() {
        java.util.Properties prop = System.getProperties();
        java.util.Enumeration enum = prop.propertyNames();

        cat.info("***System Environment As Seen By Java***");
        cat.debug("***Format: PROPERTY = VALUE***");

        while (enum.hasMoreElements()) {
            String key = (String) enum.nextElement();
            cat.info(key + " = " + System.getProperty(key));
        }
    }

}

Log4j 는 기본적으로 다섯개의 우선권(이하 Priority) 등급으로 메세지를 로깅할 수 있다.

1. 완성된 어플리케이션에서는 출력되지 않아야 할 디버깅 메세지들을 쓰기위해 debug 를 사용하라.
2. 어플리케이션의 verbose 모드에서 출력될만한 메세지들을 로깅하기 위해 info 를 사용하라.
3. 어플리케이션이 이상없이 계속 실행될 수 있는 정도의 경고메세지를 로깅하기 위해 warn 을 사용하라.
4. 어플리케이션이 그럭저럭 돌아갈만한 정도의 에러베세지를 로깅하기 위해 error 를 사용하라.
   예를들어 관리자에 의해 주어진 설정인자가 올바르지 않아 하드코딩된 기본값을 사용해야 할 경우.
5. 로깅후에 애플리케이션이 비정상적으로 종료될 치명적인 메세지를 로깅하기 위해 fatal 을 사용하라.


- 다음의 몇줄을 log4j.properties 라는 이름의 파일로 저장하고 TestLogging.class 파일과 같은 디렉토리에 저장하라.
(TestLogging.java 파일을 컴파일 한 후에)
Log4j는 기본적으로 getRoot()가 호출되거나 getInstatnce("category_name") 이 코드상에서 호출되었을 때
어플리케이션의 클래스패스에서 이 파일을 찻을 것이다.


log4j.rootCategory=DEBUG, dest1
log4j.appender.dest1=org.apache.log4j.ConsoleAppender
log4j.appender.dest1.layout=org.apache.log4j.PatternLayout


PatternLayout 은 기본적으로 사용자가 작성하는 메세지와 개행을 의미하는 %m %n 의 형태이다.
이것은 Log4j의 메소드들의 여러 장점중에 쓰기에 더 짧다는 것을 제외하고는
Java의 System.out.println(...) 메소드와 정확히 동일하게 출력된다.

- TestLoggin.java 파일을 컴파일 하고 실행했을 때 다음과 유사한 출력을 얻게 될 것이다.

Start of main()
Just testing a log message with priority set to INFO
Just testing a log message with priority set to WARN
Just testing a log message with priority set to ERROR
Just testing a log message with priority set to FATAL
Calling init()
***System Environment As Seen By Java***
***Format: PROPERTY = VALUE***
java.runtime.name = Java(TM) 2 Runtime Environment, Standard Edition
sun.boot.library.path = c:\jdk1.3\jre\bin
java.vm.version = 1.3.0_02
java.vm.vendor = Sun Microsystems Inc.
... and so on



- 만약 메세지 다음에 priority 를 출력하고 싶다면(당신이 지정한 메세지에)
  다음 라인을 log4j.properties 파일에 추가하고 저장하면 된다.

log4j.appender.dest1.layout.ConversionPattern=%-5p: %m%n


기본값인 %m %n 을 오버라이드 한다. %p 는 메세지의 priority를, %m은 메세지 자체를,
%n 은 개행문자를 출력할 것이다.
당신은 어떤 자바코드를 바꿀필요도 없고, TestLogging.java 파일을 재컴파일 할 필요도 없이
단지 properties 파일만 바꾸면 된다.

DEBUG: Start of main()
INFO : Just testing a log message with priority set to INFO
WARN : Just testing a log message with priority set to WARN
ERROR: Just testing a log message with priority set to ERROR
FATAL: Just testing a log message with priority set to FATAL
DEBUG: Calling init()
INFO : ***System Environment As Seen By Java***
DEBUG: ***Format: PROPERTY = VALUE***
INFO : java.runtime.name = Java(TM) 2 Runtime Environment, Standard Edition
INFO : sun.boot.library.path = c:\jdk1.3\jre\bin
INFO : java.vm.version = 1.3.0_02
INFO : java.vm.vendor = Sun Microsystems Inc.
... and so on


만약 DEBUG 나 INFO 메세지에 질려서 이것들이 출력되지 않기를 원하고, 다른 메세지들을 여전히 출력되길 원한다면
(예를 들어, 어플리케이션이 생산이 시작될 준비가 되었을 때) log4j.rootCategory=DEBUF, dest1 행을
log4j.rootCategory=WARN, dest1 으로 수정하면 된다.

위의 행은 WARN 보다 priority가 낮은 등급(DEBUG와 INFO 같은)의 메세지들을 제외하는 것이다.
수정하면 다음과 같이 출력될 것이다.

WARN : Just testing a log message with priority set to WARN
ERROR: Just testing a log message with priority set to ERROR
FATAL: Just testing a log message with priority set to FATAL



                               Log4j의 상세


- Log4j에는 세개의 주요한 컴포넌트가 있다. 이것들은 Categories, appenders 그리고 layouts 이다.
- Category를 초기화 시키고 그 후에 여러 로깅메소드를 호출하여 로그파일로 메세지를 보낸다.
- 하나의 Category는 하나 혹은 그 이상의 대상(목적지)에 로그하도록 설정된다.
   Log4j 에서는 이 대상을 "appenders" 라고 부른다. 왜냐하면 이 클래스들은 기본적으로 메세지 문자열을 로그의 끝에
   더하기(append) 때문일 것이다.
   Log4j 는 한번의 로깅을 호출함으로 로그메세지를 콘솔, 텍스트파일, html 파일, xml 파일, 소켓이나
   심지어는 Windows NT Event Log 등 모든 것으로 보낼 수 있다.
   거기다 로그메세지를 이메일로 전송할 수도 있다.(예를 들어 치명적인 에러가 날 경우 사용하기 적당하다)

- 몇몇 appender 클래스들을 나열하자면 ConsoleAppender, FileAppender, SMTPAppender, SocketAppender,
   NTEventLogAppender, SyslogAppender, JMSAppender, AsyncAppender and NullAppender 가 있다.

- appender 는 실제로 로그에 쓰기 전에 메세지 형식을 만드는 layout 을 사용한다.
   예를들어 HTMLLayout 은 모든 메세지를 보기좋은 HTML 테이블 형식으로 만들것이다.

-  Log4j는 날짜, 시간, 메세지 priority(DEBUG, WARN, FATAL 등), Java 클래스명,
   소스코드의 행번호, 메소드명, Java 스레드의 이름 등등을 당신이 보내는 로그메세지에 더할 수 있다.
   로그하는 것은 appender 에 설정된 layout 에 명시된대로이다.


- Category 는 사용자가 지정하며 대소문자를 구별하는 도트로 구분된 문자열이다. 완전한 이름의 자바 클래스를 얻고
  그것을 Category 이름으로 지정하기 위해 보통 클래스명.class.getName() 을 사용한다.
  (게다가 runtime 시에 category 명을 로깅하는 것이 class 명을 로깅하는 것보다 훨씬 더 빠르다.
  만약 빠르면서도 클래스명을 로깅하고 싶다면, class명을 category의 이름으로 지정하고, 카테고리 이름을 로깅하라!)

- category명의 각각의 단어는 그 다음 이어지는 단어의 ancestor(조상)라 하고, 바로 다음 단어의 parent(부모)이다.
  Log4j 는 특정 category 에서 오버라이드 될때까지 ancestor(조상)으로부터 priority와 appender 를
  상속하는 개념이 있기 때문에, 이는 매우 중요하다.

- XML 문서의 root 와 같이 log4j 에는 언제나 이름이 없는 root category 가 존재한다.
  category 를 초기화하기 위해 Category 클래스의 static 메소드를 사용한다.

Category cat = Category.getRoot();
Category cat2 = Category.getInstance("your.category.name");


\\특정 category 이름을 위해 Category.getInstance(...) 메소드는 항상 정확히 동일한 Category 객체를 리턴한다.
따라서 Category 를 초기화해서 차례로 돌릴 필요가 없다. 대신
필요한 어느곳이든 Category.getInstance("wanted.category.name"); 을 써주면 된다.

- "com.comp"라는 패키지의 "TestApp" 라는 이름의 클래스로 Category를 인스턴스화 하는 전형적인 방법은

static Category cat = Category.getInstance(TestApp.class.getName());


TestApp 는 클래스이름 자체이지 인스턴스가 아니라는 것을 명심하라. 또한 TestApp.class 구문은 TestApp 클래스의
새로운 인스턴스를 생성하지 않는다.

- Priority 클래스의 다섯개 priority(우선권) 상수는 우선권이 감소하는 차례로 FATAL, ERROR, WRAN, INFO, DEBUG 이다.
원한다면 (Priority 클래스를) 상속받음으로써 그 이상의 priority를 만들 수 있다.

- category 들에게 priority 를 지정할 수 있지만 꼭 그럴 필요는 없다.
  만약 priority 를 지정하지 않으면 log4j 는 상속관계의 계층구조를 따라서 가장 먼저 할당된 것을 찾아 사용한다.
  root category는 항상 지정된 priority가 있다.(기본값은 Priority.DEBUG),
  따라서 모든 category 들은 항상 priority를 갖도록 보장되어 있다.

- Category 클래스의 logging 메소드의 사용

// 일반적인 로깅 메소드 (불편함)
public void log(Priority p, Object message);

// 편리하게 사용할 수 있는 일반적인 로깅 메소드
public void debug(Object message);
public void info(Object message);
public void warn(Object message);
public void error(Object message);
public void fatal(Object message);


- log4j는 category의 log(...) 메소드가 지정한 priority가 category가 할당되거나 상속받은 priority와
  같거나 더 높을 때에만 로깅한다.

Category cat = Category.getRoot();
cat.setPriority(Priority.ERROR);
// Later...
//cat.info("Started processing..."); //Will not log
cat.error("User input is erroneous!"); //Will log
cat.fatal("Cannot process user input.  Program terminated!"); //Will log



                             Log4j 설정


- 로깅이 가능해지기 전에, 첫째로 log4j 를 설정해야한다. Log4j 를 설정한다는 것은 category(loggers) 들에
  appender (목적지/대상/핸들러)들을 더하고 각각의 appender 에게 layout(전체구성)을 지정하는 것이다.

- category 들은 어떤 순서로든 생성되고 설정될 수 있다. 특히 category 는 그것의 decendants(자손들)을 찾아
  연결할 수 있고, 심지어 그것들보다 나중에 인스턴스화 될 수 있다.

- 일반적으로 Log4j 는 어플리케이션이 초기화되는 동안 보통 설정파일을 읽어들여 한번만 설정된다.

- 또한 category 객체의 addAppender() 메소드를 사용함으로써 특정 appender 에 로그하도록 프로그래밍적으로
  category를 설정할 수 있다. 원하는 만큼 더할 수 있다(원하지 않는다면 하지 않아도 상관없다)
  이 메소드는 보통 추천하지 않는다 왜냐 하면 소스파일을 수정하여 다시 컴파일 해야 하기 때문이다.
  java 프로퍼티 파일이나 XML 파일 같은 외부 설정파일을 이용하는 것이 더 좋은 방법이다.

- appender 들은 singleton 이 아니라 추가된다는 것을 주의하라.
  category 는 기본적으로 그것의 조상에서 부터의 모든 appender 들을 물려받는다.
  만약 appender 를 category에 더하고 그것의 기반이 되는 동일한 스트림에 쓴다면(콘솔, 같은 파일 등)
  같은 로그메세지가 두번 이상 로그될 것이다.
  게다가 만약 계층구조의 두개의 category 들이 같은 appender 이름을 사용한다면,
  Log4j 는 그 appender 들에 두번 쓸 것이다.
  appender 로부터 물려받는 것을 불가능하게 하기 위해 category의 cat.setAdditivity(false) 메소드를 사용하라.
  그러면 로그 메세지는 해당 category에 특별히 설정된 appender 로만 보내질 것이다.

- 정적 메소드인 BasicConfigurator.configure() 메소드는 System.out.println(...) 구문이 그러하듯 콘솔에 로깅한다.
  이 메소드는 루트 category 에 ConsoleAppender 를 붙여 콘솔에 프린트 하는 기능을 가지고 있다.
  출력은 %-4r [%t] %-5p %c %x - %m%n 으로 세팅된 PatternLayout 을 사용하여 구성된다.
  이 메소드는 log4j.properties 파일을 사용하는 것이 더 간단하고 유연하기 때문에 그다지 필요하지 않다.

- log4j 를 설정하는 기본 파일은 log4j.properties 이다.
  이 파일을 어플리케이션의 클래스패스에 지정된 어느곳이든 위치시킨다.

- Java 프로퍼티 파일에서 설정 정보를 읽어오려면 정적메소드 중 하나인 PropertyConfigurator.configure(...)
  메소드를 사용하라. 이미 설정되어 있는 설정은 지워지지도 재설정되지도 않는다. 이 메소드를 사용하려면
  정적메소드인 BasicConfigurator.resetConfiguration() 메소드를 먼저 호출하라. 이 메소드 사용은 코드를 길고
  복잡하게 할 수도 있다. 초보자는 각각의 어플리케이션의 루트디렉토리에 log4j.properties 파일 하나만 위치시켜라.

- log4j 예제 설정 파일
# root category의 priority 를 to DEBUG 로 설정하고 appender 를 A1 으로 지정한다.
log4j.rootCategory=DEBUG, A1

# A1 을 ConsoleAppender 로 지정(시스템 콘솔에 출력하는).
log4j.appender.A1=org.apache.log4j.ConsoleAppender

# A1 을 PatternLayout 을 사용하도록.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n

이 예제 파일은 BasicConfigurator.configure() 메소드를 사용한것과 완전히 같이 log4j를 설정한다. 그러나 이방법으로 하면
나중에 설정을 수정할 때에, 예를들어 DEBUG 를 FATAL로 프로퍼티 파일을 고치고 어떤 자바파일도 재컴파일 할 필요가 없다.

- 더 복잡한 log4j 설정파일

#### 두개의 appender를 사용하며, 하나는 콘솔에 로깅하고 다른 하나는 파일에 로깅한다.
log4j.rootCategory=debug, stdout, R

# priority가 WARN 보다 같거나 더 높은 메세지만 출력한다.
log4j.category.your.category.name=WARN
# Specifically inherit the priority level
#log4j.category.your.category.name=INHERITED

#### 첫번째 appender는 콘솔에 쓴다.
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

# 호출하는 파일명과 라인넘버를 출력하는 Pattern
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n

#### 두번째 appender 는 파일에 쓴다.
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=example.log

# 최대로그파일 크기를 조절
log4j.appender.R.MaxFileSize=100KB
# 집적하는 로그파일의 수 (여기서는 하나의 백업파일)
log4j.appender.R.MaxBackupIndex=1

log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n


이제 출력은 example.log 라는 파일에 덧붙여질 것이고, 이 파일은 크기가 100KB 가 될때 롤오버 될것이다.
롤오버가 일어날 때 구버전의 example.log 파일은 자동으로 example.log.1 파일로 바뀐다.
DailyRollingFileAppender 를 사용하여 파일을 매분, 매시간, 매일, 매주, 매달 또는 하루 2회씩 롤오버되게 할 수 있다.


- 서버 같이 오랫동안 실행되는 프로그램에서는 Java 프로퍼티 파일을 이용하여 환경설정을 하기 위해
  configureAndWatch(String configFilename, long delay_in_milliseconds)를 사용한다.
  Log4j 는 몇 밀리세컨드 마다 프로퍼티 파일이 변하는지를 계속 모니터링 하다가
  프로퍼티 파일이 변하면 설정을 업데이트할것이다.

- 프로그램 출시단계가 되면 빠른 성능을 위해 계층구조의 모든 category에서 특정 priority 이하의 모든 메세지를
  로깅하지 않도록 할 수 있다. 예를 들어, 아무것도 로깅하지 않으려면, log.diable=FATAL 을
  설정파일(property파일)에 명시해주면 된다.

단 log.disableOverride 프로퍼티가 false 로 세팅되어 있지 않다면 예외가 일어난다.
따라서 시스템 관리자는 log.disable을 오버라이드 할 수 있도록 환경설정 파일에
log.disableOverride=true 를 (일시적으로)명시하고 모든 메세지를 로그해서 생기는 문제를 조사해봐야 한다.




                               유용한 Layout 들


- 몇가지 layout 클래스들을 들자면 TTCCLayout, HTMLLayout, PatternLayout, SimpleLayout 그리고 XMLLayout이 있다.
- SimpleLayout 과 PatternLayout 클래스들은 자바의 Throwable 에러들과 예외를 무시한다.
  HTMLLayout과 XMLLAyout 은 그것들을 다룬다.
- SimpleLayout 은 로그 구문의 property 에 이어 "-" 이나오고 로그메세지가 나오는 구성이다. 예를 들어
 
  DEBUG - Hello world

- PatternLayout 은 C언어의 printf 함수와 유사한 변환 패턴에 따라 출력을 형식을 지정할 수 있게 한다.
  PatternLayout 의 변환 형식을 %r [%t] %-5p %c - %m%n 로 하면 다음과 같이 출력된다.

176 [main] INFO  org.foo.Bar - Located nearest gas station

- 가장 첫 필드는 프로그램이 시작된 후로부터 경과된 시간의 밀리세컨드 값이다.
- 두번째 필드는 로그 요청을 만든 스레드이다.
- 세번째 필드는 로그 구문의 priority 이다.
- 네번째 필드는 로그요청에 관련된 category의 이름이다.
- "-" 뒤의 문자열은 로깅메세지이다.

- 변환 형식에 어떤 문자도 삽입될 수 있다.
- 변환 문자들은 다음과 같다.
- %m : 당신이 지정한 메세지를 출력
- %p : 로깅 이벤트의 priority 를 출력
- %r : 어플리케이션이 시작되어 로깅이벤트가 일어날때까지의 경과시간을 밀리세컨드 값으로 출력
- %c : 로깅이벤트의 category를 출력한다. 예를 들어 category 이름이 "a.b.c" 일때 %c{2} 는 "b.c"를 출력하며 {2}는
        도트(.)로 구분된 category 이름의 마지막 두개의 컴포넌트를 의미한다.
        {n} 이 없으면 기본적으로 카테고리의 이름을 모두 출력한다.
- %t : 로깅 이벤트를 생성한 스레드 이름을 출력
- %x : 로깅이벤트를 발생시킨 스레드에 관련된 내포검사항목(Nested Diagnostic Context : NDC)을 출력한다.
        Java Servlet 과 같이 다수의 클라이언트가 분산된 스레드에 의해 다루어 질 때 유용하다.
- %n : 플랫폼 독립적인 개행문자를 출력한다. "\n" 또는 "\n\r" 등이 지정될 수 있다.
- %% : 하나의 % 기호를 출력한다.
- 경고 : 아래의 형식은 프로그램의 실행속도를 느리게 한다. 실행속도가 중요하지 않은 것이 아니라면 사용을 피하도록 한다.
- %d : 로깅이벤트가 일어난 날자(date)을 출력한다.
        date 변환 지정자는 날짜형식 지정자 뒤에 이어 나오는 brace({..}) 사이에 둘러쌓여 나온다.
        예: %d{HH:mm:ss,SSS} 또는 %d{dd MMM yyyy HH:mm:ss,SSS}
        만약 날자 형식 지정자가 주어져있지 않다면 ISO8601 형식으로 나타난다. 날짜형식 지정자는 자바의 속도가 느린
        SimpleDateFormat 클래스의 시간형식 문자열과 같은 문법을 수용했다.
        더 빠른 성능을 위해 %d{ISO8601}, %d{ABSOLUTE}, %{RELATIVE}(프로그램 시작 후 경과시간의 밀리세컨드값을 나타내며
        가장 빠르다) 또는 %d{DATE} 를 사용하며 각각은
        log4j의 ISO8601DateFormat, AbsoluteTimeDateFormat, RelativeTimeDateFormat, DateTimeDateFormat 날자형식을 사용한다.

- %l : 소스코드의 위치정보를 출력한다. %C. %M(%F:%L) 의 축약형이다.
- %C : 로깅요청을 일으킨 호출자의 완전한 클래스이름을 출력한다.
        예를들어 "org.apache.xyz.SomeClass"라는 클래스 이름이 있을때, %C{1} 은 "SomeClass"를 출력한다.
        {1} 는 "완전한 클래스이름의 마지막 하나의 구성요소를 출력하라" 는 뜻이다. 만약 {n} 이 없다면
        기본적으로 완전한 클래스 이름을 출력한다.
- %M : 로깅요청을 일으킨 메소드를 출력한다.
- %F : 로깅요청을 일으킨 파일 이름을 출력한다.
- %L : 로깅요청을 일으킨 곳의 행번호를 출력한다.


- 선택적으로 퍼센트 기호와 변환 문자 사이에 형식수정자(format modifier)를 사용하여 최소 필드넓이를 바꾸고,
  최대 필드 넓이와 필드 안에서의 문자의 정렬을 바꿀 수 있다.

- 필드 안에서 완쪽 정렬을 하기위해 마이너스 기호(-)를 사용한다. 기본적으로는 오른쪽 정렬이다(왼쪽에 여백을 둠)
- 최소 필드 넓이를 명시하기 위해 양의 정수를 사용한다. 만약 데이타 항목이 더 적은 문자를 가지고 있으면 최소넓이에
   도달할 때까지 오른쪽 똑은 왼쪽에 공백이 생긴다. 만약 데이터 항목의 길이가 최소 필드폭 보다 크면
   필드넓이가 데이터에 맞게 확장된다.

- 마침표 뒤에 나오는 양의 정수는 최대 필드폭을 지정한다. 만약 데이터항목이 최대필드폭보다 길면 나머지 문자들은
   데이터항목의 끝이 아니라 시작에서부터 지워진다.예를 들어 최대필드폭이 8이고 데이터 항목의 길이가 10개문자라면,
   데이터 항목 처음 두개의 문자는 지워진다. 이런 동작은 끝에서부터 잘라내는 C언어의 printf 함수와 차이가 난다.

- 예 :
- %20c : 최소 20문자 공간에서 category 이름을 (기본적인)오른쪽 정렬
- %-20c : 최소 20문자 공간에서 category 이름을 왼쪽 정렬
- %.30c : 만약 category 이름이 30문자보다 길면 (앞에서부터)자른다.
           최소폭이 없으므로 30문자보다 짧다고 해도 여백공간은 없다.
- %20.30c : category 이름이 20문자보다 짧으면 오른쪽 정렬을 하고, 30자보다 길면 시작부터 자른다.
- %-20.30c : category 이름이 20자보다 짧으면 왼쪽 정렬을 하고, 30자보다 길면 시작에서부터 자른다.
- %r [%t] %-5p %c %x - %m\n : 이 형식이 본래 TTCCLayout 이다.
- %-6r [%15.15t] %-5p %30.30c %x - %m\n : TTCCLayout과 다음의 형식을 제외하고 유사하다.
   상대시간이 6자가 안될때에 오른쪽에 여백을 두고, 스레드명이 15자가 안될때에
   오른쪽에 여백을 두고, 더 길때에는 자르며, category명이 30자가 안될때는 오른쪽에 여백을 두고 더 길때는 자른다.

- 고급 : 단지 log로 문자열만을 넘길 필요는 없다. 객체도 로그메세지에 넘길 수 있다.
         ObjectRenderer 를 implement 하면 객체를 문자열로 표시하여 appender 에 로깅한다.



                 Log4j 에서 Appender와 Layout 옵션의 Property keys

log4j 의 전체 옵션들

- log4j.configuration=app_config.properties : Category.getRoot() 나 Category.getInstance(...) 메소드를 처음으로 호출하여
  Log4j 의 초기화 과정을 마친다.("log4j.debug=true" 로 지정하여 초기화가 일어나는 것을 볼 수 있다)
  초기화되는 동안 Log4j 는 어플리케이션의 클래스패스에서 "log4j.properties" 파일이나
  property key를 통해 지정한 파일을 찾는다. 그러나 이것(설정파일을 지정하기 위해 설정한 property key)을 시스템 property로
  설정할 필요가 있다. 예를 들어 다음과 같이 프로그램을 구동한다.
    java -Dlog4j.configuration=app_config.properties ...

  왜냐하면 만약 설정파일에 세팅했다면 너무 늦기 때문이다.
  Log4j 는 기본 설정파일인 log4j.properties 파일을 읽기 위해 이미 시작했을 것이다.

- log4j.debug=true : 기본값은 false. log4j 를 설정하는 상세 정보를 출력한다.
- log4j.disable=INFO : 모든 category에서 여기 지정한 priority 보다 같거나 낮은 priority 메세지는 로깅하지 않는다.
  log4j.disableOverride 프로퍼티가 기본값인 false 일때에만 동작한다.
- log4j.additivity.your.category.name=false : 기본값은 true. appender를 ancestor(조상)으로부터 쌓이게 할지(true)
  그렇게 하지 않을지(false)를 지정한다.
- log4j.defaultInitOverride=false : Category.getRoot() 나 Category.getInstance(...) 메소드를 처음으로 호출하여
  Log4j 의 초기화 과정을 마친다.("log4j.debug=true" 로 지정하여 초기화가 일어나는 것을 볼 수 있다)
  초기화되는 동안 Log4j 는 어플리케이션의 클래스패스에서 "log4j.properties" 파일이나
  "log4j.configuration=app_config.properties" 프로퍼티를 통해 지정한 파일을 찾는다.
  만약 이것(프로퍼티를 통해 지정한 환경파일을 찾는 것)을 원하지 않는다면 이 프로퍼티를 true 로 세팅하라.
  그러나 이것을 시스템 property로 설정할 필요가 있다. 예를 들어 다음과 같이 프로그램을 구동한다.
  java -Dlog4j.defaultInitOverride=true ...
  왜냐하면 설정파일에 세팅했다면 이미 너무 늦기 때문이다.Log4j는 이미 그 파일을 읽기위해 시작되었을 것이다.
- log4j.disableOverride=false: 기본값은 false. 가끔 true로 설정하여 log.disable 프로퍼티를 무시할 수 있다.


ConsoleAppender 옵션들
- Threadhold=WARN: 이 appender는 category의 priority가 더 낮게 지정되어 있다고 할지라도 여기 명시된 priority보다
  낮은메세지들을 로깅하지 않을 것이다. 이것은 콘솔에 모든 메세지가 나타나는 동안 파일에 로깅되는 경우와 같이
  메세지의 숫자를 줄이는데 유용하다.
- ImmediateFlush=true: 기본값은 true. 로그메세지들이 전혀 버퍼되지 않는 것을 의미하며 대부분의 상황에 적당하다.
- Target=System.err: 기본값은 System.out


FileAppender 옵션들
- Threadhold=WARN: 이 appender는 category의 priority가 더 낮게 지정되어 있다고 할지라도 여기 명시된 priority보다
  낮은메세지들을 로깅하지 않을 것이다. 이것은 콘솔에 모든 메세지가 나타나는 동안 파일에 로깅되는 경우와 같이
  메세지의 숫자를 줄이는데 유용하다.
- ImmediateFlush=true: 기본값은 true. 로그메세지들이 전혀 버퍼되지 않는 것을 의미하며 대부분의 상황에 적당하다.
- File=mylog.txt : 로깅할 파일명. 앞부분에 경로를 나타내기 위해 ${some_property_key}
  (예를 들어 java.home 또는 user.home 과 같은 시스템 프로퍼티)를 사용할 수 있다. 사실 모든 옵션들의 프로퍼티키들은
  이런 종류의 값을 설정 가능하다.
- Append=false : 기본값은 true이며 파일 끝에 추가하는 것을 의미한다.
  false 는 각각의 프로그램이 시작할때 파일에 덮어씌운다.


RollingFileAppender 옵션들
- Threadhold=WARN: 이 appender는 category의 priority가 더 낮게 지정되어 있다고 할지라도 여기 명시된 priority보다
  낮은메세지들을 로깅하지 않을 것이다. 이것은 콘솔에 모든 메세지가 나타나는 동안 파일에 로깅되는 경우와 같이
  메세지의 숫자를 줄이는데 유용하다.
- ImmediateFlush=true: 기본값은 true. 로그메세지들이 전혀 버퍼되지 않는 것을 의미하며 대부분의 상황에 적당하다.
- File=mylog.txt : 로깅할 파일명. 앞부분에 경로를 나타내기 위해 ${some_property_key}
  (예를 들어 java.home 또는 user.home 과 같은 시스템 프로퍼티)를 사용할 수 있다. 사실 모든 옵션들의 프로퍼티키들은
  이런 종류의 값을 설정 가능하다.
- Append=false : 기본값은 true이며 파일 끝에 추가하는 것을 의미한다.
  false 는 각각의 프로그램이 시작할때 파일에 덮어씌운다.
- MaxFileSize=100KB : 끝에 KB, MB 또는 GB를 붙인다. 지정한 크기에 도달하면 로그파일을 교체한다(roll).
- MaxBackupIndex=2 : 최대 2개(예)의 백업 파일들을 유지시킨다. 오래된 파일들은 삭제한다. 0 은 백업파일을 만들지 않는다.


DailyRollingFileAppender 옵션들
- Threadhold=WARN: 이 appender는 category의 priority가 더 낮게 지정되어 있다고 할지라도 여기 명시된 priority보다
  낮은메세지들을 로깅하지 않을 것이다. 이것은 콘솔에 모든 메세지가 나타나는 동안 파일에 로깅되는 경우와 같이
  메세지의 숫자를 줄이는데 유용하다.
- ImmediateFlush=true: 기본값은 true. 로그메세지들이 전혀 버퍼되지 않는 것을 의미하며 대부분의 상황에 적당하다.
- File=mylog.txt : 로깅할 파일명. 앞부분에 경로를 나타내기 위해 ${some_property_key}
  (예를 들어 java.home 또는 user.home 과 같은 시스템 프로퍼티)를 사용할 수 있다. 사실 모든 옵션들의 프로퍼티키들은
  이런 종류의 값을 설정 가능하다.
- Append=false : 기본값은 true이며 파일 끝에 추가하는 것을 의미한다.
  false 는 각각의 프로그램이 시작할때 파일에 덮어씌운다.
- DatePattern='.'yyyy-ww : 매주마다 파일을 교체(roll)한다. 교체주기를 월, 주, 일, 하루 2회, 시간, 분 별로 정할수 있다.
  이 값은 교체주기를 설정할 뿐만 아니라 백업파일의 붙는 문자열도 정한다. 콜론(:) 문자를 값의 어디에도 사용하지 말라.
  그것 말고는 자바의 SimpleDateFormat 의 어떤 형식 문자열을 사용할 수 있다.
  특히 한쌍의 작은따옴표(single quote) 안에 있는 문자를 반드시 제어해주어야 한다.(예의 '.' 와 같이)
- '.'yyyy-MM: 매달의 첫날에 로그파일을 교체한다.
- '.'yyyy-ww: 매주의 첫날에 로그파일을 교체한다.
- '.'yyyy-MM-dd: 매일 자정에 로그파일을 교체한다.
- '.'yyyy-MM-dd-a: 매일 자정과 정오에 로그파일을 교체한다.
- '.'yyyy-MM-dd-HH: 시간마다(시간이 시작할때) 로그파일을 교체한다.
- '.'yyyy-MM-dd-HH-mm: 분마다(분이 시작할때) 로그파일을 교체한다.


PatternLayout 옵션들
- ConversionPattern=%m%n : 각각의 로그메세지의 형식을 나타내는 방법(포함하는 정보)


HTMLLayout 옵션들
- LocatoinInfo=true: 기본값은 false. 자바파일명과 행번호를 출력한다.
- Title=My app title : 기본값은 Log4j Log Message. HTML의 <title>태그에 주어진다


XMLLayout 옵션들
- LocatoinInfo=true: 기본값은 false. 자바파일명과 행번호를 출력한다.


TTCCLayout 옵션들(이것 대신 더 유연한 PatternLayout을 사용하라)
- DateFormat=ISO8601: 자바의 SimpleDateFormat 의 생성자이거나 NULL, RELATIVE, ABSOLUTE, DATE, ISO8601 중 하나.
- TimeZoneID=GMT-8:00: TimeZone.getTimeZone(java.lang.String) 메소드에 의해 나올만한 형식의 문자열
- CategoryPrefixing=false: 기본값은 true. category 이름을 출력한다.
- ContextPrinting=false: 기본값은 true. 현재 스레드에 속하는 내포검사항목 정보를 출력한다.
- ThreadPrinting=false: 기본값은 true. 스레드명을 출력한다.





                            종합적인 Log4j 설정 프로퍼티 파일

#log4j.debug=true
#log4j.disable=fatal
#log4j.additivity.TestLogging=false

log4j.rootCategory=, dest1
log4j.category.TestLogging=DEBUG, dest1
log4j.appender.dest1=org.apache.log4j.ConsoleAppender
#log4j.appender.dest1.layout=org.apache.log4j.SimpleLayout
log4j.appender.dest1.layout=org.apache.log4j.PatternLayout
#log4j.appender.dest1.layout.ConversionPattern=%-5p %l %x: %m%n


!----------------------####### END OF PROPERTIES #######----------------------!


###############################################################################
# 밑으로 log4j 설정파일을 어떻게 써야하는지 더 자세히 기록해놓았다.           #
# #으로 시작하는 행을 선택적으로 복사해서 주석을 제거하고 붙여라.             #
###############################################################################

!-----------------------------------------------------------------------------!
! 이 파일을 클래스패스가 지정된 어느곳이든 위치시켜라.                        !
! Appender 들은 기본적으로 추가된다.                                          !
! 프로퍼티들은 category가 오버라이드 될때까지 상속된다.                       !
! ${property_key} 에서 키의 값은 시스템 프로퍼티나 파일 자체에서 정의될 수    !
! 있다. 시스템 프로퍼티들은 이 파일보다 먼저 검색된다.                        !
!-----------------------------------------------------------------------------!


!-----------------------------------------------------------------------------!
! log4j의 상위 수준에서의 동작을 설정한다.                                    !
!-----------------------------------------------------------------------------!
! log4j가 이파일을 파싱하는 것을 볼 수 있다.
#log4j.debug=true
! 이것을 false로 지정하면 실제적으로 log4j.disable 프로퍼티를 따르게된다.
#log4j.disableOverride=false
! 주어진 priority 보다 같거나 낮은 priority 를 가진 메세지의 로깅을
! 모든 category에서 불가능 하게 한다.
#log4j.disable=INFO



!-----------------------------------------------------------------------------!
! category(logger) 설정                                                       !
!-----------------------------------------------------------------------------!

! ROOT CATEGORY (보통 이것만 세팅하는 것으로 충분하다)
! 여기서는 priority가 DEBUG(기본값) 이상되는 메세지를 로깅한다.
#log4j.rootCategory=, dest1
! 또는,
#log4j.rootCategory=debug, dest1, dest2

! 당신이 설정하는 CATEGORY들
! (클래스/패키지/프로젝트/기타 별로 로깅을 사용자설정한다)
! 조상의 priority를 오버라이드 하고, 이 카테고리에서 WARN 또는 그보다 높게설정
#log4j.category.TestLogging=WARN, dest3
! 또는,
#log4j.category.TestLogging=DEBUG, dest3

!--------다음을 하지마라!!  APPENDER들은 기본적으로 추가된다!!!---------------!
! 같은 로그 메세지들을 dest1에 두번 이상 쓸것이다. 한번은 root,               !
! 그리고는 이 category 로                                                     !
!#log4j.category.TestLogging=DEBUG, dest1, dest3                              !
! 만약 이 category에서 추가(additive)되기를 원하지 않으려면 다음과 같이 하라  !
!#log4j.additivity.TestLogging=false                                          !
!-----------------------------------------------------------------------------!


!-----------------------------------------------------------------------------!
! appender(log destinations/targets) 와 옵션들을 설정                         !
!-----------------------------------------------------------------------------!

! CONSOLE에 쓰기 (stdout 또는 stderr)
#log4j.appender.dest1=org.apache.log4j.ConsoleAppender
#log4j.appender.dest1.ImmediateFlush=true

! FILE에 로그 쓰기, 지정한 크기를 넘어서면 파일을 교체
#log4j.appender.dest2=org.apache.log4j.RollingFileAppender
! 이 appender 는 여기 명시된 priority 와 같거나 높은 메세지만 로깅한다.
#log4j.appender.dest2.Threshold=ERROR
!  파일명을 명시한다 (${property_key} 는 대신 사용가능한 값을 얻어온다)
#log4j.appender.dest2.File=${java.home}/log4j.log
! 추가하지 않고, 덮어씌운다.
#log4j.appender.dest2.Append=false
! 최대 파일크기를 조절
#log4j.appender.dest2.MaxFileSize=100KB
! 백업파일 갯수 지정 (백업 파일은 파일이름.1, .2 등으로 생성된다)
#log4j.appender.dest2.MaxBackupIndex=2

! 파일에 로그를 쓰고, 매주 교체된다.
#log4j.appender.dest3=org.apache.log4j.DailyRollingFileAppender
! 파일명을 명시.
#log4j.appender.dest3.File=log4TestLogging2.html
! 최대 파일크기를 조절
#log4j.appender.dest3.MaxFileSize=300KB
! 각 주를 시작하며 로그파일을 교체
#log4j.appender.dest3.DatePattern='.'yyyy-ww



!-----------------------------------------------------------------------------!
! appender의 layout(log 형식, 구성) 과 옵션들을 설정                          !
!-----------------------------------------------------------------------------!

! 간단한 로그 형식을 사용 (예 INFO - your log message)
#log4j.appender.dest1.layout=org.apache.log4j.SimpleLayout

! 로그 메세지 구성을 C의 printf 스타일의 형식을 사용
#log4j.appender.dest1.layout=org.apache.log4j.PatternLayout
! pattern layout 에서 패턴을 지정 (기본값은 %m%n 이며 가장 빠르다)
#log4j.appender.dest1.layout.ConversionPattern=%-5p: %m%n
! 또는,
#log4j.appender.dest1.layout.ConversionPattern=%-5p %6.10r[%t]%x(%F:%L) - %m%n

#log4j.appender.dest2.layout=org.apache.log4j.PatternLayout
#log4j.appender.dest2.layout.ConversionPattern=[%d{ISO8601}]%5p%6.6r[%t]%x(%F:%L) - %m%n
! 또는, (아래의 형식은 어플리케이션을 느리게 할 것이다)
#log4j.appender.dest2.layout.ConversionPattern=[%d{yyyy-mm-dd hh:mm},%6.6r]%-5p[%t]%x(%F:%L) - %m%n


! HTML 테이블 형태로 로그메세지를 구성
#log4j.appender.dest3.layout=org.apache.log4j.HTMLLayout
! 자바 file명과 행번호를 출력 (기본값은 false)
#log4j.appender.dest3.layout.LocationInfo=true
! <title>태그 설정 (기본값: Log4J Log Messages)
#log4j.appender.dest3.layout.Title=My App Log


!-----------------------------------------------------------------------------!
!                          PATTERN 형식 용어들                                !
!-----------------------------------------------------------------------------!
! %n - 개행                                                                   !
! %m - 로그 메세지                                                            !
! %p - 메세지 priority (FATAL, ERROR, WARN, INFO, DEBUG 또는 사용자정의)      !
! %r - 프로그램 구동이 시작한 이후 경과시간의 밀리세컨드값                    !
! %% - 퍼센트 기호 출력                                                       !
!                                                                             !
!----------------------- 로그에서의 몇가지 추가요소들-------------------------!
! %c - category(logger)이름, %c{2}는 마지막 두개의 요소를 출력한다.           !
! %t - 현재 스레드 이름                                                       !
! %x - 내포검사항목 (NDC)    \\                                               !
!                                                                             !
!------------------------- 성능 저하시키는 형식들 ----------------------------!
! %d - 날자와 시간, 또한 %d{ISO8601}, %d{DATE}, %d{ABSOLUTE},                 !
!        %d{HH:mm:ss,SSS}, %d{dd MMM yyyy HH:mm:ss,SSS} 등                    !
! %l - %F%L%C%M 의 축약형                                                     !
! %F - 자바 소스 파일명                                                       !
! %L - 자바 소스의 행번호                                                     !
! %C - 자바 클래스 이름, %C{1} 마지막 하나의 요소를 출력                      !
! %M - 자바 메소드 이름                                                       !
!                                                                             !
!------------------------------  형식 변경자   -------------------------------!
! %-어떤형식문자 -  최소폭 이하에서 왼쪽정렬 (기본값은 오른쪽 정렬)           !
! %20어떤형식문자 - 20 글자 최소폭 (가능할 때에는 왼쪽에 여백이 가능)         !
! %.30어떤형식문자 - 최대 30글자 (필요하면 앞에서부터 잘린다)                 !
! %-10.10r - 예.  경과시간이 10개문자 이하의 넓이이면 왼쪽정렬                !
!            10개문자 이상의 길이이면 앞에서 부터 잘린다.                     !
!-----------------------------------------------------------------------------!


!-----------------------------------------------------------------------------!
!                             OPTIONS GLOSSARY                                !
!-----------------------------------------------------------------------------!
!-------------------------OVERALL OPTIONS FOR log4j---------------------------!
! 커맨드 라인 option으로 명시: -Dlog4j.defaultInitOverride=false
! 커맨드 라인 option으로 명시: -Dlog4j.configuration=app_config.properties
!#log4j.debug=true
!#log4j.disable=INFO
!#log4j.disableOverride=false
!#log4j.additivity.your.category.name=false
!
!----------------------------NullAppender OPTIONS-----------------------------!
!#log4j.appender.dest1.Threshold=INFO
!
!---------------------------ConsoleAppender OPTIONS---------------------------!
!#log4j.appender.dest1.Threshold=INFO
!#log4j.appender.dest1.ImmediateFlush=true
!#log4j.appender.dest1.Target=System.err
!
!-----------------------------FileAppender OPTIONS----------------------------!
!#log4j.appender.dest2.Threshold=INFO
!#log4j.appender.dest2.ImmediateFlush=true
!#log4j.appender.dest2.File=mylog.txt
!#log4j.appender.dest2.Append=false
!
!-------------------------RollingFileAppender OPTIONS-------------------------!
!#log4j.appender.dest2.Threshold=INFO
!#log4j.appender.dest2.ImmediateFlush=true
!#log4j.appender.dest2.File=mylog.txt
!#log4j.appender.dest2.Append=false
!#log4j.appender.dest2.MaxFileSize=100KB
!#log4j.appender.dest2.MaxBackupIndex=2
!
!-----------------------DailyRollingFileAppender OPTIONS----------------------!
!#log4j.appender.dest2.Threshold=INFO
!#log4j.appender.dest2.ImmediateFlush=true
!#log4j.appender.dest2.File=mylog.txt
!#log4j.appender.dest2.Append=false
!#log4j.appender.dest2.DatePattern='.'yyyy-ww
!
!-----------------------------SimpleLayout OPTIONS----------------------------!
!**None**
!
!-------------TTCCLayout OPTIONS (PatternLayout is more flexible)-------------!
!#log4j.appender.dest1.layout.DateFormat=ISO8601
!#log4j.appender.dest1.layout.TimeZoneID=GMT-8:00
!#log4j.appender.dest1.layout.CategoryPrefixing=false
!#log4j.appender.dest1.layout.ThreadPrinting=false
!#log4j.appender.dest1.layout.ContextPrinting=false
!
!-----------------------------PatternLayout OPTIONS---------------------------!
!#log4j.appender.dest1.layout.ConversionPattern=%m%n
!
!-------------------------------HTMLLayout OPTIONS----------------------------!
!#log4j.appender.dest3.layout.LocationInfo=true
!#log4j.appender.dest3.layout.Title=My app title
!
!--------------------------------XMLLayout OPTIONS----------------------------!
!#log4j.appender.dest3.layout.LocationInfo=true
!-----------------------------------------------------------------------------!



- 코드상에서 성능이 매우 중요한 곳에서는 로깅메소드의 파라미터를 만드는 시간을 절약하기 위해 다음과 같이 사용한다.
if (cat.isDebugEnabled() {
    cat.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
}

- SimpleLayout을 사용하였거나 %p, %m, %n의 패턴만 사용하여 설정한 경우 System.out.println(...) 만큼 성능이
  빠르다고 테스트됐다.

- 내포검사항목(NDC)를 사용한다 : 가끔 서블릿처럼 하나의 코드가 각각의 클라이언트를 서비스 하기위해
  여러개의 스레드로 인스턴스화 되어 많은 클라이언트를 서비스한다.
  이때 다른 클라이언트로 부터의 로깅 요청을 차별화하기 위해서 내포검사항목(NDC)를 사용할 수 있다.
  할 수 있는 것은 로깅하기 전에 클라이언트의 고유한 정보를 NDC로 넣는다. 고유한 정보는 클라이언트의 IP주소,
  호스트이름 또는 요청과 관련된 어떤 것도 될 수 있다. %x를 layout 패턴에 명시하면 요청한 로그 메세지에
  그 문자열(고유한 정보)을 찍는다.

import org.apache.log4j.*;

public class TestNDC {

  // !log4j.properties 파일이 클래스패스 상에 존재하는지 확인하라.

  static Category cat = Category.getInstance(TestNDC.class.getName());

  public static void main(String[] args) {

    // 내포검사 설정은 서블릿과 같이 멀티스레드로 이루어진 클라이언트의 요청들을 구별한다.
    NDC.push("Client #45890");

    cat.info("Testing Nested Diagnostic Context (NDC).");
    cat.info("Make sure %x is in layout pattern!");
    cat.info("Client #45890 should appear in this log message.");

    switchNDC();

    cat.info("Finished.");
  }

  static void switchNDC() {
    NDC.push("Client #99999");
    cat.debug("Client #99999 should appear nested in this log message.");
    NDC.pop();
  }

}