대략 내용을 살펴보면 log4j.rootLogger는 최상위 로거입니다. 모든 INFO레벨이상의 로그는 다 console로 찍겠다는 겁니다. (레벨에는 DEBUG, INFO, WARN, ERROR, FATAL 순인데, 예를 들어 INFO레벨로 지정해두면 logger.debug로 찍는 로그는 나타나지 않습니다. INFO레벨 이상것만 나타납니다.)
console은 아래 #Console log쪽에 보시면 log4j.appender.console <- 요 이름입니다. 요 console은 자세히보면 ConsoleAppender라는 클래스입니다. 이건 말그대로 콘솔에 로그를 찍어준다는 겁니다. layout에는 PatternLayout을 지정할 수 있는데 저 패턴은 뭐 레벨이 뭐고, 클래스가 뭐고, 메시지찍고 뭐 그런 내용입니다. 검색 고고싱-_-;
그리고, 파일에다가 출력 할 수 있는데, DailyRollingFileAppender클래스를 이용합니다. 이눔은 말그대로 매일매일 다른로그를 사용하게 만듭니다. 로그이름이 위와 같이 glv.log라면, 해당로그가 어제날짜인데 로그를 찍으려고 하면 기존에 있던 파일은 glv.log.2008-04-17 이렇게 바꿔줍니다.
아래부분에 보면 log4j.logger. 다음에 패키지명이나 클래스명을 지정해놓고, 로그레벨과 출력할 로그를 지정할 수 있는데요. 해당 클래스나 패키지의 로그는 저걸로 찍겠다는 겁니다. Test클래스는 logfile로 찍힌다는 겁니다. 그리고, rootLogger가 colsole로 지정되어 있기 때문에 console에도 찍히겠죠? ^^
자 그럼 콘솔에는 INFO com.mudchobo.Test.println(Test.java:10) - 안녕하세요! Test입니다. INFO com.mudchobo.Test.println(Test.java:10) - 안녕하세요! Test입니다. INFO com.mudchobo.Test2.println(Test2.java:10) - 안녕하세요! Test2입니다. INFO com.mudchobo.Test2.println(Test2.java:10) - 안녕하세요! Test2입니다. 이렇게 출력이 될 것이고 로그파일에는 [19:56:35][INFO ](Test.java:10) - 안녕하세요! Test입니다. 이것만 출력될 것입니다. 위에 콘솔에 두번 찍힌 이유는 Rootlogger도 찍고, 아래 패키지를 지정한 로그도 찍었기 때문이죠. 그리고, 파일에는 한번만 쓰여진 이유는 파일에 쓰는건 log4j.logger.com.mudchobo.Test=INFO, logfile 여기 이 Test클래스 하나죠-_-; 이상입니다-_-;
2. 구조
Log4j는 크게 3가지 요소로 구성되어 있습니다.
① Logger : logging 메시지를 Appender에 전달합니다.
② Appender : 전달받은 logging 메시지를 원하는 곳으로 보내는 매개체의 역할을 합니다.
아래 표는 Appender의 종류입니다. API에서 보고 이해가 된 선에서 적었습니다.
ConsoleAppender 로그 메시지를 콘솔에 출력합니다.
DailyRollingFileAppender 로그 메시지를 파일로 저장합니다.
DatePattern 옵션에 따라 원하는 기간마다 로그파일을 갱신합니다.
ExternallyRolledFileAppender
FileAppender 직접적으로 사용되지 않고 DailyRollingFileAppender와 RollingFileAppender의 superclass로 사용되는듯 합니다.
JDBCAppender 로그 메시지를 DB에 저장합니다. 현재는 완벽하지 않으니 왠만하면 차기 버전에서 사용하라고 하는 것 같습니다.
JMSAppender 로그 메시지를 JMS Topic으로 보냅니다.
NTEventLogAppender NT 이벤트 로그를 위한 Appender. 윈도우에서만 사용가능합니다.
NullAppender 내부적으로만 사용되는 Appender입니다.
RollingFileAppender 로그 메시지를 파일로 저장합니다. 설정된 size를 초과하면 로그파일이 갱신됩니다.
SMTPAppender 로그 메시지를 지정된 이메일로 발송합니다.
SocketAppender 로그 메시지를 socket을 이용해서 지정된 곳으로 보냅니다.
SocketHubAppender 위와 비슷하게 사용하는듯 합니다.
SyslogAppender 로그 메시지를 원격 syslog deamon으로 보냅니다.
TelnetAppender 로그 메시지를 telnet을 통해 보낸다는 것 같습니다. 원격 모니터링, 특히 servlet의 모니터링에 유용하다고 합니다.
WriterAppender FileAppender처럼 주로 superclass로서 사용되는듯 합니다.
③ Layout : logging 메시지의 출력 형식을 지정합니다.
- 아래에서 설명.
3. 로깅레벨
FATAL : 가장 크리티컬한 에러가 발생했을 때 사용합니다.
ERROR : 일반적인 에러가 발생했을 때 사용합니다.
WARN : 에러는 아니지만 주의가 필요할 때 사용합니다.
INFO : 일반적인 정보가 필요할 때 사용합니다.
DEBUG : 일반적인 정보를 상세히 나타낼 때 사용합니다.
로깅레벨의 우선순위는 FATAL이 가장 높고 DEBUG가 가장 낮습니다.
예를 들어 레벨을 WARN으로 설정하면 WARN이상되는 로그(FATAL, ERROR, WARN)만
출력합니다.
4. 환경설정
- Log4j의 환경설정은 직접 코드에서 메서드를 이용하는 방법과 properties 파일을 이용하는 방법, XML파일을 이용하는 방법이 있습니다.
① 코드에서 설정
String layout = "%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n";
String logfilename = "DailyLog.log";
String datePattern = ".yyyy-MM-dd ";
PatternLayout patternlayout = new PatternLayout(layout);
DailyRollingFileAppender appender = new DailyRollingFileAppender(patternlayout, logfilename, datePattern);
logger.addAppender(appender);
logger.setLevel(Level.INFO);
logger.fatal("fatal!!");
위 코드처럼 설정하시면 됩니다.
② properties 파일로 설정
#---------- file logging ----------
log4j.rootLogger=INFO, rolling
#---------- consol logging -----------
#log4j.rootLogger=INFO, stdout
#---------- file, console logging -----------
#log4j.rootLogger=INFO, stdout, rolling
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d] %-5p at %C{3}.%M(%13F:%L) %3x - %m%n
log4j.appender.rolling=org.apache.log4j.DailyRollingFileAppender
log4j.appender.rolling.File=/WEB_BACKUP1/pgw_log/webchannel.log
log4j.appender.rolling.Append=true
#---------- every day renew ------------
log4j.appender.rolling.DatePattern='.'yyyy-MM-dd
#---------- every month renew ------------
#log4j.appender.rolling.DatePattern='.'yyyy-MM
#---------- every week renew ------------
#log4j.appender.rolling.DatePattern='.'yyyy-MM-ww
#---------- every 12hours renew -------------
#log4j.appender.rolling.DatePattern='.'yyyy-MM-dd-a
#---------- every hour renew --------------
#log4j.appender.rolling.DatePattern='.'yyyy-MM-dd-HH
#---------- every min renew --------------
#log4j.appender.rolling.DatePattern='.'yyyy-MM-dd-HH-mm
log4j.appender.rolling.layout=org.apache.log4j.PatternLayout
log4j.appender.rolling.layout.ConversionPattern=[%d] %-5p at %C{3}.%M(%13F:%L) %3x - %m%n
위 properties 파일은 실제 WebChannel에 적용한 파일입니다.
- log4j.rootLogger=INFO, rolling
: 로깅레벨을 ‘INFO’로 하고 ‘rolling’이라는 이름의 Appender를 사용한다.
위 properties파일에는 ConsoleAppender(stdout)와 DailyRollingFileAppender(rolling)가
정의되어 있습니다.
- log4j.rootLogger=INFO, stdout : console에만 출력
- log4j.rootLogger=INFO, stdout, rolling : console과 file 로 출력
위처럼 설정이 가능합니다.
- log4j.appender.stdout=org.apache.log4j.ConsoleAppender
: ConsoleAppender의 이름은 ‘stdout’으로 한다.
- log4j.appender.rolling=org.apache.log4j.DailyRollingFileAppender
: DailyRollingFileAppender의 이름은 ‘rollong’으로 한다.
- log4j.appender.rolling.File=/WEB_BACKUP1/pgw_log/webchannel.log
: 로그파일의 위치와 파일명을 지정한다.
- log4j.appender.rolling.Append=true
: 서버 restart시에도 파일이 reset되지 않는다.
- log4j.appender.rolling.DatePattern='.'yyyy-MM-dd
: DatePattern 을 ‘매일갱신’으로 설정. 매일 자정이 지나면
파일명 뒤에 날짜가 붙는다.
ex) webchannel.log.2005-11-21
- log4j.appender.rolling.layout=org.apache.log4j.PatternLayout
: layout을 PatternLayout으로 설정.
- log4j.appender.rolling.layout.ConversionPattern=[%d] %-5p at %C{3}.%M(%13F:%L) %3x - %m%n
: 로그의 출력 형식을 설정. 아래 설명.
# log4j.appender.rolling.MaxFileSize=500KB
: 파일의 최대size 설정하는 부분인데 서버 기동시 최초에 이 부분의 property를 읽지 못했다는 경고가 자꾸 떠서 삭제 했습니다. 설정하지 않으면 Default로 10MB가 설정된다고 합니다.
#### properties 파일의 변경사항은 server restart시에 적용됩니다. ####
③ XML 파일로 설정
현재 잘 모르니 넘어가겠습니다.-_-
5. 설정 포맷
① DatePattern 설정 포맷
'.'yyyy-MM 매달 첫번째날에 로그파일을 변경합니다
'.'yyyy-ww 매주의 시작시 로그파일을 변경합니다.
'.'yyyy-MM-dd 매일 자정에 로그파일을 변경합니다.
'.'yyyy-MM-dd-a 자정과 정오에 로그파일을 변경합니다.
'.'yyyy-MM-dd-HH 매 시간의 시작마다 로그파일을 변경합니다.
'.'yyyy-MM-dd-HH-mm 매분마다 로그파일을 변경합니다.
② PatternLayout 설정 포맷
%p debug, info, warn, error, fatal 등의 로깅레벨이 출력된다.
%m 로그내용(코드상에서 설정한 내용)이 출력됩니다.
ex) logger.info("log"); 라고 코딩했다면 ‘log’가 로그 내용임.
%d 로깅 이벤트가 발생한 시간을 기록합니다.
포맷은 %d{HH:mm:ss, SSS}, %d{yyyy MMM dd HH:mm:ss, SSS}
같은 형태로 사용하며 SimpleDateFormat에 따른 포맷팅을 하면 된다
%t 로그이벤트가 발생된 쓰레드의 이름을 출력합니다.
%% % 표시를 출력하기 위해 사용한다.
%n 플랫폼 종속적인 개행문자가 출력된다. \r\n 또는 \n 일것이다.
%c 카테고리를 표시합니다.
ex) 카테고리가 a.b.c 처럼 되어있다면
%c{2}로 설정하면 b.c 가 출력됩니다.
%C 클래스명을 포시합니다.
ex) 클래스구조가 org.apache.xyz.SomeClass 처럼 되어있다면
%C{2}는 xyz.SomeClass 가 출력됩니다
%F 로깅이 발생한 프로그램 파일명을 나타냅니다.
%l 로깅이 발생한 caller의 정보를 나타냅니다
%L 로깅이 발생한 caller의 라인수를 나타냅니다
%M 로깅이 발생한 method 이름을 나타냅니다.
%r 어플리케이션 시작 이후 부터 로깅이 발생한 시점의 시간(milliseconds)
%x 로깅이 발생한 thread와 관련된 NDC(nested diagnostic context)를
출력합니다.
%X 로깅이 발생한 thread와 관련된 MDC(mapped diagnostic context)를
출력합니다.
ex) [%d] %-5p at %C{3}.%M(%13F:%L) %3x - %m%n
[2005-11-23 10:43:21,560] INFO at
pgw.database.PGWBoardDAO.selectList(PGWBoardDAO.java:146) -
========== PGWBoardDAO#selectList ==========
포맷의 각 색깔별로 출력되는 실제 예입니다. 포맷 중간에 원하는 단어(at)나
기호(`.` , `-`)등을 넣으면 그대로 출력됩니다.
- Throwble 타입의 변수를 parameter로 받는 메서드를 이용하면 원하는 위치에서
원하는 Exception을 발생시킬 수도 있습니다.
- 위 코드에서 INFO 레벨의 로그는 주어진 내용를 출력하고,
ERROR 레벨의 로그는 발생한 Exception을 로그로 출력합니다.
② LoggableStatement.java
- 이 클래스는 query를 로그로 출력할 때 부가적으로 필요한 클래스로 PreparedStatement의 ‘?’를 실제 데이터로 치환해서 출력하는 기능을 합니다.
이 클래스는 Interface인 PreparedStatement를 구현하는 클래스로 파일이름은 임의로 정하셔도 됩니다.
클래스내에는 PrepareddStatement의 메서드를 오버라이딩한 메서드와 넘어온 데이터를 ArrayList에 넣어주는 메서드, 그리고 query의 ‘?’를 치환해 리턴해주는 메서드를 구현합니다.
//PreparedStatement 와 ArrayList를 import 해줍니다.
//메서드 오버라이딩시에 필요한 클래스도 추가적으로 import 해줍니다.
import java.sql.PreparedStatement;
import java.util.ArrayList;
public class LoggableStatement implements PreparedStatement {
private ArrayList parameterValues;
private String sqlTemplate;
private PreparedStatement wrappedStatement;
//connection.prepareStatement(String sql) 대신에 사용할 생성자 입니다.
//PreparedStatement Object를 생성, query를 String에 담고 ArrayList를 생성합니다.
public LoggableStatement(Connection connection, String sql)
throws SQLException {
wrappedStatement = connection.prepareStatement(sql);
sqlTemplate = sql;
parameterValues = new ArrayList();
}
.
//중략/
.
//실제로 필요한 메서드만 오버라이딩 하고, 나머지는 auto generate하시면 됩니다.
//여기서는 query문 실행관련 메서드와 setInt, setString, setDate, setCharacterStream 을 오버라이딩 했습니다.
public boolean execute() throws java.sql.SQLException {
return wrappedStatement.execute();
}
public boolean execute(String sql) throws java.sql.SQLException {
return wrappedStatement.execute(sql);
}
public int[] executeBatch() throws java.sql.SQLException {
return wrappedStatement.executeBatch();
}
public java.sql.ResultSet executeQuery() throws java.sql.SQLException {
return wrappedStatement.executeQuery();
}
public java.sql.ResultSet executeQuery(String sql)
throws java.sql.SQLException {
return wrappedStatement.executeQuery(sql);
}
public int executeUpdate() throws java.sql.SQLException {
return wrappedStatement.executeUpdate();
}
public int executeUpdate(String sql) throws java.sql.SQLException {
return wrappedStatement.executeUpdate(sql);
}
public java.sql.Connection getConnection() throws java.sql.SQLException {
return wrappedStatement.getConnection();
}
public void setCharacterStream(
int parameterIndex,
java.io.Reader reader,
int length)
throws java.sql.SQLException {
wrappedStatement.setCharacterStream(parameterIndex, reader, length);
saveQueryParamValue(parameterIndex, reader);
public void setDate(
int parameterIndex,
java.sql.Date x,
java.util.Calendar cal)
throws java.sql.SQLException {
wrappedStatement.setDate(parameterIndex, x, cal);
saveQueryParamValue(parameterIndex, x);
}
public void setInt(int parameterIndex, int x)
throws java.sql.SQLException {
wrappedStatement.setInt(parameterIndex, x);
saveQueryParamValue(parameterIndex, new Integer(x));
}
//instance생성시 String에 넣어둔 query의 ‘?’를 ArrayList에 담긴 실제 데이터로
//치환해서 리턴해 줍니다.
public String getQueryString() {
//여기서 query를 String에도 담아준 이유는 webLogic의 jdk가 1.3 버전으로
//StringBuffer의 indexOf(String str) 메서드를 사용할 수 없었기 때문입니다.
//다른 방법이 있으시면 알려주세요..
String sql = sqlTemplate;
StringBuffer query = new StringBuffer(sqlTemplate);
int idx = 0;
The following class is a very simple example that initializes, and then uses, the Log4J logging library for Java applications. As you can see the configuration is pretty simple.
package com.devdaily.log4jdemo;
import org.apache.log4j.Category;
import org.apache.log4j.PropertyConfigurator;
import java.util.Properties;
import java.io.FileInputStream;
import java.io.IOException;
public class Log4JDemo
{
static final Category log = Category.getInstance(Log4JDemo.class);
static final String LOG_PROPERTIES_FILE = "lib/Log4J.properties";
public static void main(String[] args)
{
// call our constructor
new Log4JDemo();
// Log4J is now loaded; try it
log.info("leaving the main method of Log4JDemo");
}
public Log4JDemo()
{
initializeLogger();
log.info( "Log4JDemo - leaving the constructor ..." );
}
private void initializeLogger()
{
Properties logProperties = new Properties();
try
{
logProperties.load(new FileInputStream(LOG_PROPERTIES_FILE));
PropertyConfigurator.configure(logProperties);
log.info("Logging initialized.");
}
catch(IOException e)
{
throw new RuntimeException("Unable to load logging property " + LOG_PROPERTIES_FILE);
}
}
}
After a few class level fields are created, the action begins with the main method, which first calls the constructor for this class. The constructor then calls the initializeLogger method. This method actually does the work of loading the Log4J properties file. It then calls the configure method of the PropertyConfigurator class.
Once this is done I call the info method of the log object several times. Notice that I could have also called other methods like logger.warn(), log.debug(), log.error(), or log.fatal(), but to keep it simple I'm just showing log.info().
The Log4J Properties File
Before I leave this quick tip I also need to show the Log4J properties file that I'm using. My file is named Log4J.properties, and for the purpose of this demonstration I'm keeping it in a sub-directory of my project named lib. Here are the contents:
# STDOUT appender
log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender
log4j.appender.STDOUT.layout=org.apache.log4j.PatternLayout
log4j.appender.STDOUT.layout.ConversionPattern=%d %p [%t] %C{1} - %m\n
# use the STDOUT appender. set the level to INFO.
log4j.category.com.devdaily.log4jdemo.Log4JDemo=INFO, STDOUT