廣告聯播

2008年10月23日 星期四

[Java] log4j 的說明及實作

From: Polin Wei

簡介 Introduction
  Log4j 是一套開放源碼的工具,方便編程人員在程式中加入 log 機制,並輸出到各種目標上。Log4j 能夠透過外部的設定檔(properites 或 XML)進行設定。Log4j 能夠將 log message 寫到 console, 檔案,串流,TCP 協定的伺服器, Unix Syslog daemon 等。詳細的說明可以參考官方網 log4j

組成 Log4j 的三大元件(Loggers, Appenders and Layouts) :
  • Logger - 由編程人員在程式中使用,進行 logging 的元件
  • Appender - 負責將 log message 輸出到各種裝置上
  • Layout - 決定 log message 的格式
Loggers:
  Logger 可以被指派等級。能夠指派給 Logger 的等級有 : DEBUG, INFO, WARN, ERROR, FATAL 5 種,定義在 org.apache.log4j.Level 類別中。這 5 種等級的高低順序為 FATAL > ERROR > WARN > DEBUG > INFO 。

  Logger 的等級決定它產生 log message 的數量 : Logger 只寫"出高於或等於本身等級"的 log message。例如某個 Logger 的等級被設定為 WARN,那麼它只會寫出等級為 WARN, ERROR, FATAL 的 log message,對於 DEBUG, INFO 的 log message 則不予理會。

  若是 Logger 的等級未被設定,則會自動使用 parent(上一層) 的等級。如果程式中所有的 Logger 都未設定等級,則由 root logger 決定。

  Logger 之間以名稱區分,所以在程式中任何地方,呼叫 Logger.getLogger(),並傳入同一個 Logger 名稱,則會得到同一個 Logger 的 reference。

  Logger 之間以名稱區分出階層。即使父階層在程式中出現的時機比子階層晚,例如 "com.foo" logger 比 "com.foo.bar" 被取得的時間來得晚,"com.foo" 仍然是 "com.foo.bar" 的父階層(會影響到子階層 logger 未被定義的屬性, log 等級, appender, layout )。

Appenders:
  透過 Appender, Logger 能夠將 log message 輸出到指定的裝置上。一個 Logger 能夠擁有多個 Appender,所以 Logger 能夠同時將 log message 輸出到多個個裝置上。

  Appender 的設定亦會反映在 Logger 的階層中。當 Logger 輸出一筆 log message 時,父階層的 Appender 和自己的 Appender(如果有的話)都會記錄到這筆 log message;

  例如 "com.foo" Logger 有一個 Appender 將 log message 輸出到 console,而 "com.foo.bar" 有一個 Appender 將 log message 輸出到檔案;當 "com.foo.bar" Logger 輸出一筆 log message 時, console 和檔案都會出現這筆 log message。

  而最簡單的例子,就是當 root logger 擁有一個輸出到 console 的 Appender 時,則程式中所有的 logger 所產生的 log message 都會輸出到 console。唯一個例外的情況,就是當某個 logger 將自己的 additivity 屬性設為 false(Logger.setAdditivity(false)),則此 logger 與隸屬於它的子 logger 都不會將 log message 寫到 console。

Layouts:
  可以透過 Layout 的配置,自由改變 Logger 寫出 log message 的格式。例如,為 Logger 加入一個 conversion pattern 為 "%r [%t] %-5p %c - %m%n" 的 PatternLayout,則輸出的 log message 就可能會像下列這樣:

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

PatternLayout 的 格式字元列表如下:

# %c 輸出日誌訊息所屬的類別的全名
# %d 輸出日誌時間點的日期或時間,指定格式的方式:%d{yyy-MM-dd HH:mm:ss }。
# %l 輸出日誌事件的發生位置,即輸出日誌訊息的語句處於它所在的類別的第幾行。
# %m 輸出訊息,如log(message)中的message。
# %n 輸出一個列尾符號。
# %p 輸出優先階層,即DEBUG,INFO,WARN,ERROR,FATAL。如果是調用debug()輸出的,則為DEBUG,依此類推。
# %r 輸出自應用啟動到輸出該日誌訊息所耗費的毫秒數。
# %t 輸出產生該日誌事件的線程名。
# %r 輸出自應用啟動到輸出該日誌訊息所耗費的毫秒數。
# %f 輸出日誌訊息所屬的類別的類別名。

Layout 亦會反映在 Logger 的階層上。


實作:

1. 修改 log4j.xml 檔
  在JBOSS(version:4.0.3SP1) 上的 log4j.xml 實作,可以分兩個層次來看:
  • 一是修改 Server 端的 log4j.xml ,檔案在:$JBOSS_HOME/server/default/conf/log4j.xml
  • 一是修改每個Prjoject中的 $Java Resource/src 程式原始檔目錄下的 log4j.xml
這兩個檔案內容大致相同,差異只在 Project 端的 logger 參數在 Server 端要改成 category

Server 端的 log4j.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<!-- ============================================================ -->
<!--  Log4j Configuration -->
<!-- ============================================================ -->


<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">

   <!-- ======================== -->
   <!-- Preserve messages in a local file -->
   <!-- ======================== -->

   <!-- A time/date based rolling appender 定義 log message 輸出FILE檔案 -->
   <appender name="FILE" class="org.jboss.logging.appender.DailyRollingFileAppender">
      <errorHandler class="org.jboss.logging.util.OnlyOnceErrorHandler"/>
      <param name="File" value="${jboss.server.home.dir}/log/server.log"/>
      <param name="Append" value="false"/>

      <!-- Rollover at midnight each day -->
      <param name="DatePattern" value="'.'yyyy-MM-dd"/>

      <!-- 定義 log message 的格式 -->
      <layout class="org.apache.log4j.PatternLayout">
         <!-- The default pattern: Date Priority [Category] Message\n -->
         <param name="ConversionPattern" value="%d %-5p [%c] %m%n"/>

      </layout>
   </appender>


   <!-- ===================================================== -->
   <!-- Append messages to the console 定義 log message 輸出 Console -->
   <!-- ===================================================== -->

   <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
      <errorHandler class="org.jboss.logging.util.OnlyOnceErrorHandler"/>
      <param name="Target" value="System.out"/>
      <param name="Threshold" value="INFO"/>

      <!-- 定義 log message 的格式 -->
      <layout class="org.apache.log4j.PatternLayout">
         <!-- The default pattern: Date Priority [Category] Message\n -->
         <param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c{1}] %m%n"/>
      </layout>
   </appender>

   
   <!-- ========================================= -->
   <!-- Limit categories 對 class: com.demos 作偵錯觀察 -->
   <!-- ========================================= -->
   <category name="com.demos">
      <priority value="DEBUG"/>
   </category>

   <!-- ============================ -->
   <!-- Setup the Root category 設定 log 要在那裡產生 -->
   <!-- ============================ -->

   <root>
      <appender-ref ref="CONSOLE"/>
      <appender-ref ref="FILE"/>
   </root>

</log4j:configuration>




Project 端的 log4j.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

   <!-- ===================================================== -->
   <!-- Append messages to the console 定義 log message 輸出至 Console 及 log 內容格式 -->
   <!-- ===================================================== -->
    <appender name="default" class="org.apache.log4j.ConsoleAppender">
        <param name="target" value="System.out" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern"
                value="[%p] %d{dd MMM hh:mm:ss.SSS aa} %t [%C-%M]%n%m%n%n" />
        </layout>
    </appender>

    <!-- 設定要偵錯的 calss: com.demos 及 log 要輸出到那一個裝置-->
    <logger name="com.demos">
        <level value="debug" />
        <appender-ref ref="default" />
    </logger>

</log4j:configuration>


2. 在 java 程式 com.demos.LookupFormF.java 中放入偵錯點

package com.demos;

import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.log4j.Logger;

public class LookupFormF extends ActionForm{

    static Logger loger = Logger.getLogger(LookupFormF.class.getName());
    private String symbol = null;
   
    public String getSymbol() {
        return (symbol);
    }
   
    public void setSymbol(String symbol) {
        this.symbol = symbol;
        loger.debug(symbol);
    }
}



3. 成果如下

16:54:23,733 INFO  [STDOUT] [DEBUG] 24 十月 04:54:23.733 下午 http-0.0.0.0-8043-
1 [com.demos.LookupFormF-setSymbol]
polinwei


3 則留言:

  1. 請問一下…
    我是java新手,
    import org.apache.log4j.Logger;
    它說package org.apache.log4j does not exist
    請問log4j-1.2.15.jar這個檔要放在哪邊才對。

    回覆刪除
  2. 如果是依 Project 的話可以放在 WebContent/WEB-INF/lib 這裡目錄; 如果是適用於每個 Project , 則可以放在 $TOMCAT_HOME/lib 這個目錄

    回覆刪除
  3. 等級應該是這樣哦,後面二個寫錯了。
    FATAL > ERROR > WARN > INFO > DEBUG

    回覆刪除