commons-logging 與 log4j的靈活使用

2020-08-08 20:47:45

前言:

在工作中,日誌功能是必不可少的元件,它爲我們提供了方便快捷的日誌記錄功能。藉此機會,總結下java專案中常用的第三方日誌介面與實現。

1.commons-logging:

commons-logging 是Apache基金會下開發的一款日誌系統介面,通過其API可以整合不同的日誌系統實現,其主要用於爲整個軟件系統提供統一抽象的日誌操作,避免由於不同日誌系統實現所帶來的學習與遷移成本。從廣義的角度來說,即是面向介面開發理念。

既然談到commons-logging是一個介面,那麼我們如何在java專案中爲其指定對應的日誌實現呢。從官方文件中,我們可以看到commons-logging一共提供了五種查詢日誌實現的策略,優先順序從上到下依次遞減:

  1. 查詢LogFactory(後文提到,其是一個日誌物件工場)名爲org.apache.commons.logging.Log的設定屬性。針對該屬性可以通過Java程式碼顯式設定設定屬性,但是更常見的是通過在類路徑中放置一個名爲commons-logging.properties的檔案來進行設定。當存在這樣的檔案時,屬性檔案中的每個條目都將成爲LogFactory的「屬性」。如果類路徑中有多個此類檔案,則1.1版本之前的commons-logging版本僅使用找到的第一個檔案。從版本1.1開始,每個檔案都可以定義優先順序金鑰,並且使用優先順序最高的檔案(沒有優先順序定義表示優先順序爲零)。當多個檔案具有相同的優先順序時,將使用找到的第一個檔案。

    建議在commons-logging.properties檔案中定義此屬性,以明確選擇Log實現。

  2. 查詢一個名爲org.apache.commons.logging.Log的系統屬性 ,並以該屬性值作爲日誌實現。
  3. 檢視類路徑中是否有 Log4j 的包,如果發現,則自動使用 Log4j 作爲日誌實現類。
  4. 如果應用程式在JDK 1.4系統上執行,則使用相應的包裝器類(即採用Java語言自帶的日誌實現)。
  5. 退回到預設的簡單日誌記錄包裝器(Simple日誌,commons-logging自帶的日誌實現)。  

通過以上5種方式,commons-logging便完成了介面與實現的銜接。

接着,我們來談下commons-logging介面的使用方法。由於其是第三方元件,所以第一件事便是通過Maven將其引入到專案依賴中。其設定如下所示:

<dependency>  
    <groupId>commons-logging</groupId>  
    <artifactId>commons-logging</artifactId>  
    <version>1.2</version>  
</dependency>  

然後,便可以採用LogFactory建立一個Log物件,開啓日誌之旅,如下所示:

package org.example;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Commons-logging
 *
 */
public class App 
{
    public static void main( String[] args )
    {
        Log log = LogFactory.getLog(App.class);
        log.info("this is info!");
    }
}

Commons Logging定義了6個日誌級別:

  • FATAL
  • ERROR
  • WARNING
  • INFO
  • DEBUG
  • TRACE

預設級別是INFO,在專案中,我們可以根據實際需求選擇我們需要的日誌登記。

2.log4j:

log4j 全稱爲log for(4) java, 即一個java日誌元件。從使用者角度來看,log4j主要由三大部分組成:Logger, Appender, Formatter。

爲了使用log4j,同樣需要在maven中引入其依賴:

<dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
</dependency>
  • Logger:簡而言之,logger就是一個用於獲取日誌物件,並寫入日誌的類,獲取該物件的方法也有多種,如下所示:

    //Shorthand for getLogger(clazz.getName()).
    static Logger getLogger(Class clazz)
        
    //Retrieve a logger named according to the value of the name parameter.      
    static Logger getLogger(String name)
    
    
    //Like getLogger(String) except that the type of logger instantiated depends on the type returned by the           
    static Logger getLogger(String name, LoggerFactory factory)
    
    // method of the factory parameter.
    LoggerFactory.makeNewLoggerInstance(java.lang.String)
    
    //Return the root logger for the current logger repository.
    static Logger getRootLogger()
    

    Logger類是日誌包的核心,Logger的名稱是大小寫敏感的,並且名稱之間有繼承關係。子名由父名做字首,用點號「.」分隔,如x.y是x.y.z的父親Logger。

    Logger系統中有個根logger,是所有logger的祖先,它總是存在的,並且不可以通過名字獲取,可以通過Logger.getRootLogger()來獲取。

並且一個Logger可以擁有多個Appender,這樣同一條日誌訊息可以輸出到多個地方。每個logger都有一個日誌級別,用來控制日誌的         輸出。未分配級別的logger將自動繼承它最近的父logger的日誌級別。Logger的由低到高級別如下:

       ALL<DEBUG<INFO<WARN<ERROR<FATAL<OFF

  • Appender:在程式語言中,append方法往往出現在容器類中,用於給容器新增特定物件。同樣,在log4j中,可以將不同的日誌輸出目的地看作不同的日誌容器,比如控制檯、檔案、郵箱、數據庫等。這樣當給一個logger物件指定相應的appender時,便可以將相關日誌輸出到對應的目的地中。Log4j提供了以下幾個實現:

    org.apache.log4j.ConsoleAppender(控制檯)
    org.apache.log4j.FileAppender(檔案)
    org.apache.log4j.DailyRollingFileAppender(每天都產生一個日誌檔案)
    org.apache.log4j.RollingFileAppender(檔案大小達到指定尺寸時產生一個新的日誌檔案,檔名稱上會自動新增數位序號。)
    org.apache.log4j.WriterAppender(將日誌資訊以流的格式發送到任意指定的地方)

    預設情況下,子logger將繼承父logger的所有appenders。rootlogger擁有目標爲system.out的consoleAppender,故預設情況下,所有的logger都將繼承該appender。

  • Formatter:爲了便於閱讀,那麼格式化便是必不可少的工作,而這部分功能便由Formatter來提供。log4j提供了以下幾種formatter實現:

    org.apache.log4j.HTMLLayout(以HTML表格形式佈局)
    org.apache.log4j.PatternLayout(可以靈活地指定佈局模式)
    org.apache.log4j.SimpleLayout(包含日誌資訊的級別和資訊字串)
    org.apache.log4j.TTCCLayout(包含日誌產生的時間、執行緒、類別等資訊)
     

     

那麼使用者該怎麼設定來獲取自己想要的日誌輸出物件呢?Log4j爲我們提供來一個叫做log4j.properties檔案(需要放在class-path下)讓我們設定我們想要的日誌功能。首先來看看一個典型的組態檔:

1 ### logger的設定 ###
 2 #設定根logger
 3 log4j.rootLogger=INFO,stdout
 4 #設定子logger:org.lavasoft(在org.lavasoft包中類的日誌在沒有指定子logger名的情況下使用這個logger)
 5 log4j.logger.org.lavasoft=ERROR,file
 6 #設定子logger:org.lavasoft.test(在org.lavasoft.test包中類的日誌在沒有指定子logger名的情況下使用這個logger)
 7 log4j.logger.org.lavasoft.test=ERROR,file1,stdout
 8
 9 ### direct log messages to stdout ### (標準的終端輸出)
10 #控制檯輸出
11 log4j.appender.stdout=org.apache.log4j.ConsoleAppender
12 #自定義輸出佈局
13 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
14 #輸出的格式
15 log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
16
17 ### direct messages to file ttt.log ### (輸入到檔案ttt.log的設定)
18 #輸出到卷動檔案
19 log4j.appender.file=org.apache.log4j.RollingFileAppender
20 #輸出檔案最大爲10M
21 log4j.appender.file.MaxFileSize=10MB
22 #輸出檔案最大序號爲10
23 log4j.appender.file.MaxBackupIndex=10
24 #輸出檔案路徑
25 log4j.appender.file.File=C:/ttt.log
26 #自定義輸出佈局
27 log4j.appender.file.layout=org.apache.log4j.PatternLayout
28 #輸出格式
29 log4j.appender.file.layout.ConversionPattern=%d %-5p [%t] (%13F:%L) %3x - %m%n
30
31 ### direct messages to file ttt.log ### (輸入到檔案ttt1.log的設定)
32 #輸出到卷動檔案
33 log4j.appender.file1=org.apache.log4j.RollingFileAppender
34 #輸出檔案最大爲10M
35 log4j.appender.file1.MaxFileSize=10MB
36 #輸出檔案最大序號爲10
37 log4j.appender.file1.MaxBackupIndex=10
38 #輸出檔案路徑
39 log4j.appender.file1.File=C:/ttt1.log
40 #自定義輸出佈局
41 log4j.appender.file1.layout=org.apache.log4j.PatternLayout
42 #輸出格式
43 log4j.appender.file1.layout.ConversionPattern=%d %-5p [%t] (%13F:%L) %3x - %m%n

下面 下麪對以上的檔案解說,並給出一個應用例子:

logger的設定語法爲:級別,輸入終端1,輸出終端2,…

根logger的設定項爲:log4j.rootLogger

子logger的設定項爲:

log4j.logger.<子logger名>

在上面的設定項中,設定了一個根logger和兩個子logger,名稱爲:org.lavasoft和org.lavasoft.test,通過該名稱,利用Logger.getLogger(String name)可以獲取這兩個logger物件。

子logger的名字一般都以包名來設定,這樣當在程式通過類名獲取logger的物件就是與本類包名相同的子logger,這樣可以方便控制某個包下面 下麪logger的輸出。

但是一般不建議設定子logger,一個通用的根logger足夠用了。由於繼承關係,並且可以在任何地方通過子logger的名字獲取logger物件,這樣容易導致混亂,比如在org.lavasoft包下的類直接獲取了名爲org.lavasoft.test子logger的物件,這是不合理的(雖然日誌可以列印出來),因爲如果直接在類中用Logger.getLogger(Class clazz)方式來獲取Logger物件,則獲取到的應該是名爲org.lavasoft的logger物件,而不會得到名爲org.lavasoft.test的logger物件。

以上的組態檔純粹是爲了說明問題才那麼寫的。

最後再列出一個典型的組態檔,以供大家實際專案中使用:

### 設定根 debug level,後面均爲定義的appender###
log4j.rootLogger = debug,console ,fileAppender,dailyRollingFile,ROLLING_FILE,MAIL,DATABASE

### 設定輸出sql的級別,其中logger後面的內容全部爲jar包中所包含的包名,這樣logger的appender繼承於rootlogger ###
log4j.logger.org.apache=debug
log4j.logger.java.sql.Connection=debug
log4j.logger.java.sql.Statement=debug
log4j.logger.java.sql.PreparedStatement=debug
log4j.logger.java.sql.ResultSet=debug
### 設定輸出到控制檯 ###
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern =  %d{ABSOLUTE} %5p %c{ 1 }:%L - %m%n

### 設定輸出到檔案 ###
log4j.appender.fileAppender = org.apache.log4j.FileAppender
log4j.appender.fileAppender.File = logs/log.log
log4j.appender.fileAppender.Append = true
log4j.appender.fileAppender.Threshold = DEBUG
log4j.appender.fileAppender.layout = org.apache.log4j.PatternLayout
log4j.appender.fileAppender.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### 設定輸出到檔案,並且每天都建立一個檔案 ###
log4j.appender.dailyRollingFile = org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyRollingFile.File = logs/log.log
log4j.appender.dailyRollingFile.Append = true
log4j.appender.dailyRollingFile.Threshold = DEBUG
log4j.appender.dailyRollingFile.layout = org.apache.log4j.PatternLayout
log4j.appender.dailyRollingFile.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n### 設定輸出到檔案,且大小到達指定尺寸的時候產生一個新的檔案 ###log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender log4j.appender.ROLLING_FILE.Threshold=ERROR log4j.appender.ROLLING_FILE.File=rolling.log log4j.appender.ROLLING_FILE.Append=true log4j.appender.ROLLING_FILE.MaxFileSize=10KB log4j.appender.ROLLING_FILE.MaxBackupIndex=1 log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout log4j.appender.ROLLING_FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n

### 設定輸出到郵件 ###
log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender
log4j.appender.MAIL.Threshold=FATAL
log4j.appender.MAIL.BufferSize=10
[email protected]
log4j.appender.MAIL.SMTPHost=mail.hollycrm.com
log4j.appender.MAIL.Subject=Log4J Message
[email protected]
log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout
log4j.appender.MAIL.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n

### 設定輸出到數據庫 ###
log4j.appender.DATABASE=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.DATABASE.URL=jdbc:mysql://localhost:3306/test
log4j.appender.DATABASE.driver=com.mysql.jdbc.Driver
log4j.appender.DATABASE.user=root
log4j.appender.DATABASE.password=
log4j.appender.DATABASE.sql=INSERT INTO LOG4J (Message) VALUES ('[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n')
log4j.appender.DATABASE.layout=org.apache.log4j.PatternLayout
log4j.appender.DATABASE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender
log4j.appender.A1.File=SampleMessages.log4j
log4j.appender.A1.DatePattern=yyyyMMdd-HH'.log4j'
log4j.appender.A1.layout=org.apache.log4j.xml.XMLLayout