.Java中的異常、斷言、日誌【草稿下,Log4j專題】

(本章主要講解Java裏面比較核心的一塊內容——異常處理,Java異常處理機制,一致都是比較複雜的一塊,而不少時候若是寫程序的時候可以適當地注意對應的一些異常處理狀況,那麼就會在開發過程節省一大部分時間,最多見的狀況就是輔助進行調試以及維護工做以及提升系統的容錯性和穩定性。這一章和前邊類和對象章節不同,這一章可能涵蓋的內容沒有前邊那章多,可是我會盡可能保證在整篇文章裏面把開發過程當中須要注意到的與異常有關的細節問題以及對應的開發經驗寫入本文,而本章的出發點是異常處理,核心內容還涵蓋了測試、調試、以及部署等相關內容以及。若是有筆誤的地方,請來Email指點:silentbalanceyh@126.com,謝謝)
本章目錄
1.Java異常處理
2.異常處理心得
3.斷言的使用
4.Java中的日誌(JDK1.4 Logging Framework)
5.第三方日誌庫(Log4j、Commons Logging Framework)
5.第三方日誌庫(Log4j,Commons Logging Framework)

  前邊介紹了官方的日誌框架JDK 1.4 Logging Framework,接下來介紹兩個第三方的日誌框架Log4j和Commons Logging Framework,這三個日誌框架也是整個Java界使用最多的日誌框架。
  下載地址:【直接複製就可下載】
  http://labs.xiaonei.com/apache-mirror/logging/log4j/1.2.15/apache-log4j-1.2.15.zip
  http://apache.etoak.com/logging/log4j/1.2.15/apache-log4j-1.2.15.zip
  i.Log4j:
  1)基本簡介:
  Log4j是Apache的一個開放源代碼項目,經過使用Log4j,咱們能夠控制日誌信息輸送的目的地是控制檯、文件、GUI組件、甚至是套接口服務器、NT的事件記錄器、UNIX Syslog守護進程等;咱們也能夠控制每一條日誌的輸出格式;經過定義每一條日誌信息的級別,咱們可以更加細緻地控制日誌的生成過程。最使人感興趣的就是,這些能夠經過一個配置文件來靈活地進行配置,而不須要修改應用的代碼。 此外,經過Log4j其餘語言接口,您能夠在C、C++、.Net、PL/SQL程序中使用Log4j,其語法和用法與在Java程序中同樣,使得多語言分佈式系統獲得一個統一一致的日誌組件模塊。並且,經過使用各類第三方擴展,您能夠很方便地將Log4j集成到J2EE、JINI甚至是SNMP應用中。
  總體上講,Log4j中有三個最主要的核心組件:LoggerAppenderLayout,不只僅如此,它一樣容許開發人員自定義多個Logger,每一個Logger有本身的名字,Logger之間經過名字來表示隸屬關係。可是有一個Logger稱爲Root,這個Logger不能經過名字來檢索,可是能夠直接使用Logger.getRootLogger()獲取,其餘的Logger均可以使用Logger.getLogger(String name)方法獲取
  Logger:
  在執行應用程序時,接收日誌語句生成的日誌請求。它是一種重要的日誌處理組件, 能夠經過 log4j API 的 logger 類對其進行訪問。它的方法有:debug、info、warn、error、fatal 和 log。這些方法用於記錄消息。
  Appender:
  管理日誌語句的輸出結果。執行日誌語句時,Logger 對象將接收來自日誌語句的記錄請求。此請求是經過 logger 發送至 appender 的。而後,Appender 將輸出結果寫入到用戶選擇的目的地。對於不一樣的日誌目的地,提供不一樣的 appender 類型。這些 appender 包括:用於文件的 file appender、用於數據庫的 JDBC appender 和用於 SMTP 服務器的 SMTP appender。
  Layout:
  用於指定 appender 將日誌語句寫入日誌目的地所採用的格式。appender 能夠用來格式化輸出結果的各類佈局包括:簡單佈局、模式佈局和 HTML 佈局。
  [1]先看看Log4j的包目錄結構:
org.apache.log4j:log4j主要的類、接口以及特殊的Appender類、Layout、Level和Logger
org.apache.log4j.spi:包含了針對SPI的一部分擴展【SPI——System Programming Interface】
org.apache.log4j.chainsaw:基於Java Swing的一個GUI日誌查看器:

org.apache.log4j.config:用來設置或者獲取某些組件的相關屬性
org.apache.log4j.helpers:僅僅被log4j庫內聯使用的多個Class的集合,通常狀況下不對外
org.apache.log4j.jdbc:一個Appender用來記錄JDBC鏈接的相關事件
org.apache.log4j.jmx:在JMX開發的時候能夠配置的基於JMX的日誌記錄,可是目前該包裏面的類還不是特別穩定
org.apache.log4j.lf5【目前不多用,我也不知道這部分用途,沒用過】
org.apache.log4j.net:用來進行遠程日誌記錄的Appender,主要用於JMS、SMTP以及基於Socket的日誌記錄,用於向一個log4j服務器發送日誌進行遠程的日誌記錄
org.apache.log4j.nt:Java本地接口的應用【JNI】,接口用Java,實現用C代碼,用來記錄Windows NT系統事件日誌的Appender組件
org.apache.log4j.or:根據對象類型來對對象進行Render操做的幫助類
org.apache.log4j.performance:性能測試代碼
org.apache.log4j.xml:包含了多個XML組件,使用DOM Tree的結構在Log4j環境裏面用來記錄XML格式的日誌
org.apache.log4j.varia:包含了多個Appender以及Filters和其餘相關組件
  [2]Log4j的安裝:
  Log4j下載下來事後須要進行簡單的安裝,先保證原來的系統環境和Java環境是正常的:
  • 下載log4j的jar包,上邊已經提供了下載地址
  • 將這些包解壓到一個目錄裏面,【*:通常對於開發人員最好將jar的包分類放在不一樣的目錄下邊方便使用。】
  • 將包下邊的log4j-1.2.15.jar放入開發的CLASSPATH裏面,關於Java裏面CLASSPATH的一些問題留到開發用Java裏面去講解
  • 還須要下載兩個輔助解析XML的jar,下載地址http://archive.apache.org/dist/xml/xerces-j/,這裏我下載的最新版本2.9.0,取出裏面的xercesImpl.jarxml-apis.jar兩個jar文件放到CLASSPATH裏面。【*:在IDE環境下有專程針對CLASSPATH的設置,這種狀況防止jar衝突最好的方式就是保持CLASSPATH裏面只有java標準的環境】
  2)Log4j的核心概念:
  [1]Log4j中的Logger:
  Logger在Log4j中是一個很核心的概念,它是日誌進程裏面的核心組件,在Log4j裏面,Logger總共分爲六個日誌記錄級別,其定義位於org.apache.log4j.Level類裏:
  • TRACE:這種細粒度信息時間不只僅針對調試,
  • DEBUG:指出細粒度信息事件對調試應用程序是很是有幫助的,該級別的日誌主要用於輔助開發人員調試;
  • INFO:該級別代表消息上細粒度上突出強調了應用程序的運行過程
  • WARN:代表有可能會出現潛在錯誤的情形
  • ERROR:代表雖然程序發生了錯誤事件,可是仍然不影響系統的繼續運行
  • FATAL:該級別代表了每一個嚴重的錯誤時間,將會致使應用程序的退出。
  根據API裏面的描述還存在兩個很特殊的級別:
  • ALL:這是最低級別,用於打開全部的日誌記錄
  • OFF:這是最高級別,用於關閉全部的日誌記錄
  通常狀況下,推薦開發過程只使用ERROR、WARN、INFO、DEBUG四個級別,下圖說明了這幾個級別相互之間的關係:

  從上圖能夠知道,只有當日志記錄器Logger的日誌級別高於或者等於輸出消息的等級的時候,該日誌消息纔會被輸出。默認狀況下,Logger的級別是Debug,若是咱們須要建立一個Logger,有如下幾種方式:
  建立根日誌器:
Logger logger = Logger.getRootLogger();
  建立一個新的日誌器:
Logger logger = new Logger("MyLogger");
  建立一個基於類的日誌器:
Logger logger = new Logger(MyClass.class);
  在Log4j裏面設置一個日誌的級別使用語句:
logger.setLevel(Level.DEBUG);
  【*:在1.2版本以前,Log4j裏面沒有使用Logger類,主要是使用Category類,從版本1.2開始才使用Logger類,就上邊的方式建立Logger
  這裏先看一段代碼,而後對Category進行一個詳細說明,實際上從版本1.2開始Category和Logger是父子關係,Logger從Category類繼承而來;在該版本以前咱們通常使用Category來建立日誌記錄器,在前邊的版本里面Category就等同於如今咱們所見到的Logger。在版本1.2中,Category被標記爲deprecated,而直接使用Logger替代它,通常狀況下Java裏面被標記爲deprecated的方法和類都有替代方法以及類來進行對應功能的版本更新,咱們通常在開發過程當中儘可能避免使用標記爲deprecated的類或者方法。下邊的代碼是升級的改動:
// 被拋棄的代碼形式:
Category cat = Category.getInstance("foo.bar");
// 目前使用的建立Logger的形式:
Logger logger = Logger.getInstance("foo.bar");
  爲了使得上邊的圖形裏面的內容更加容易理解,這裏提供另一段代碼:
package org.susan.java.logging;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

class Foo{}

public class Log4jLevelTester {
public static void main(String args[]){
Logger logger = Logger.getLogger(Foo.class);
logger.setLevel(Level.INFO);

// 該請求是被容許的,由於 WARN >= INFO
logger.warn("Low fuel level.");

// 該請求時背禁止的,由於 DEBUG < INFO
logger.debug("Starting search for nearest gas station.");
}
}
  上邊這段代碼很好說明了logger在對待LEVEL的時候的用法,在setLevel的時候Logger設置了日誌器自己的Level,而後在使用裏面對應的warn、debug方法的時候會判斷使用的方法和日誌記錄器設置的Level進行詳細的比較,當知足上邊圖示的判斷條件的時候纔會輸出日誌信息。在這裏還有一點須要注意的是方法warn和debug,這兩個方法都不是Logger類所具備的方法,而是從它的父類繼承過來的Category的方法,在使該方法的時候會針對咱們最初設置的日誌的Level進行一個比較和判斷,最終決定是否要被記錄下來。那麼在環境配置好的狀況下,咱們執行該程序。
  【異常】這裏會遇到一個初學Log4j的常見問題,咱們會發現控制檯只有這句話輸出:
log4j:WARN No appenders could be found for logger (org.susan.java.logging.Foo).
log4j:WARN Please initialize the log4j system properly.
  這句話的意思是在系統環境裏面沒有找到咱們所須要的配置文件,通常理解是缺少log4j.properties屬性文件,在此處,若是咱們不使用配置文件的方式就將下邊的代碼修改爲:
package org.susan.java.logging;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

class Foo{}

public class Log4jLevelTester {
public static void main(String args[]){
BasicConfigurator.configure();
Logger logger = Logger.getLogger(Foo.class);
logger.setLevel(Level.INFO);

// 該請求是被容許的,由於 WARN >= INFO
logger.warn("Low fuel level.");

// 該請求時背禁止的,由於 DEBUG < INFO
logger.debug("Starting search for nearest gas station.");
}
}
  修改爲上邊這段代碼事後,咱們就能夠直接在沒有log4j.properties配置文件的狀況下直接運行上邊的程序,該代碼將會有如下輸出:
0 [main] WARN org.susan.java.logging.Foo - Low fuel level.
  經過這裏咱們就能夠知道Log4j裏面的Level的排序爲:DEBUG < INFO < WARN < ERROR < FATAL,等到後邊再來分析上邊的代碼若是不使用BasicConfigurator.configure();而是使用咱們開發經常使用的屬性文件log4j.properties 進行日誌記錄的相關配置,關於配置文件的詳細內容等介紹完Layout和Appender了事後再說明。
  [2]Log4j中的Appender:
  Log4j裏面的Appender相似於JDK 1.4 Logging Framework裏面的Handler組件,其主要目的是管理咱們日誌記錄的結果,描述了這些日誌怎樣進行輸出,下邊列舉了比較經常使用的一些Appender的快照:
  • ConsoleAppender:這種Appender會管理咱們的日誌消息,將日誌事件記錄到控制檯以System.out或者System.err做爲輸出機制,它默認的輸出爲System.out
  • RollingFileAppender:這種Appender從FileAppender繼承過來,大部分屬性和FileAppender是相似的,當日志記錄的文件到達文件容量的最大值的時候,會自動建立一個新的日誌文件
  • FileAppender:將日誌輸出到普通文本文件【這裏有點須要說明,好像從版本1.2開始該Appender將會標記爲禁用,替代的方案爲該類的類:WriterAppender和ConsoleAppender】
  • DailyRollingFileAppender:該Appender從FileAppender繼承而來,可以按照必定的頻度滾動日誌記錄文件
  • WriterAppender:根據用戶的選擇將日誌的信息以數據流的方式發送到任何用戶指定的地方
  • SMTPAppender:在特殊的事件日誌發生的時候,發送一封Email到指定的郵件地址,通常狀況下是針對ERROR級別以及FATAL級別的錯誤進行這種Appender的配置
  • SocketAppender:將發送一個LoggingEvent對象到一個遠程的日誌服務器,通常狀況下是一個SocketNode對象
  • SocketHubAppender:將發送一個LoggingEvent對象的集合到一個遠程的日誌服務器,通常狀況下是一個SocketNodes
  • SyslogAppender:將日誌記錄消息發送到配置好的一個syslog遠程服務器
  • TelnetAppender:該Appender和SocketHubAppender相似,也是向服務器發送日誌信息,可是不是一個SocketNode對象或者SocketNode對象列,通常發送的是Category【1.1版】輸出的結果。
  整個org.apache.log4j包結構裏面,Appender的結構圖以下:[I]標識接口[A]標識抽象類[C]標識具體類
  接口:
  Appender[I]
  類層次結構:
  AppenderSkeleton[A]
  |—AsyncAppender[C]
  |—org.apache.log4j.jdbc.JDBCAppender[C]
  |—org.apache.log4j.net.JMSAppender[C]
  |—org.apache.log4j.lf5.LF5Appender[C]
  |—org.apache.log4j.nt.NTEventAppender[C]
  |—org.apache.log4j.varia.NullAppender[C]
  |—org.apache.log4j.net.SMTPAppender[C]
  |—org.apache.log4j.net.SocketAppender[C]
  |—org.apache.log4j.net.SocketHubAppender[C]
  |—org.apache.log4j.net.SyslogAppender[C]
  |—org.apache.log4j.net.TelnetAppender[C]
  |—WriterAppender[C]
    |—ConsoleAppender[C]
    |—FileAppender[C]
      |—RollingFileAppender[C]
        |—org.apache.log4j.varia.ExternallyRolledFileAppender[C]
      |—DailyRollingFileAppender[C]
  AsyncAppender:該Appender將會使用異步的方式來進行日誌記錄,並且該Appender會將咱們在日誌記錄中記錄下來的相關信息,一旦發現有其餘的Appender與之相關,就會將這些信息發送給相對應的Appender。這種Appender和其餘Appender不同的地方在於它在進行日誌記錄的過程當中會開啓一條新的線程(Thread)來完成日誌記錄。
  【*:AsyncAppender是不能像其餘日誌記錄器同樣經過log4j.properties文件來配置,這個Appender只能使用DOMConfigurator經過編碼的方式來進行配置】
  這裏提供一段使用AsyncAppender的代碼:
package org.susan.java.logging;

import org.apache.log4j.AsyncAppender;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.TTCCLayout;

public class AsyncLogging {
private static Logger logger = Logger.getLogger("org.susan.java.logging.AsyncLogging");
private AsyncAppender asyncAppender = null;
private ConsoleAppender consoleAppender = null;
public AsyncLogging(){
try{
logger.setAdditivity(false);
asyncAppender = new AsyncAppender();
TTCCLayout layout = new TTCCLayout("yyyy-MM-dd");
consoleAppender = new ConsoleAppender(layout,"System.out");
asyncAppender.setName("Async");
asyncAppender.setBufferSize(5);
asyncAppender.setLocationInfo(true);
asyncAppender.setBlocking(false);
asyncAppender.activateOptions();
asyncAppender.addAppender(consoleAppender);
logger.addAppender(asyncAppender);
}catch(Exception ex){
ex.printStackTrace();
}
}
public void doLogging(){
logger.debug("Hello One");
logger.debug("Hello Two");
logger.debug("Hello Three");
}
public static void main(String args[]){
AsyncLogging demo = new AsyncLogging();
demo.doLogging();
}
}
  上邊這段代碼,建立了一個AsyncAppender,而且將她與一個ConsoleAppender鏈接起來,實現日誌記錄的異步操做,運行上邊這段代碼將會獲得如下結果:
2009-09-17 [main] DEBUG org.susan.java.logging.AsyncLogging - Hello One
2009-09-17 [main] DEBUG org.susan.java.logging.AsyncLogging - Hello Two
2009-09-17 [main] DEBUG org.susan.java.logging.AsyncLogging - Hello Three
  須要注意的是下邊的代碼:
  asyncAppender.addAppender(consoleAppender);
  該代碼段的就能夠將咱們所須要的Appender和AsyncAppender鏈接起來進行異步方式的日誌記錄。
  這裏遇到過一個問題,由於我查了不少資料沒有找到相關的文檔,就是這裏AsyncAppender的異步方式的原理。
  我我的以爲:AsyncAppender可使得和它相關的全部鏈接的Appender進行關聯事後異步操做,那麼一旦在AsyncAppender裏面添加了一個Appender事後,該日誌記錄會和AsyncAppender結合在一塊兒實現異步日誌記錄,並且添加的每一個Appender都是使用了和AsyncAppender相關的異步機制而使用新的線程去完整針對不一樣的輸出源進行彼此不影響的併發操做,每一個線程關聯到不一樣的輸出源,也就是說Appender做爲一個異步日誌記錄的最初入口,使得在作日誌記錄裏面的Appender能夠實現異步機制,而Log4j裏面的Appender內部原理自己應該就是異步的,可是內部的原理和外部相互之間Appender之間的結構沒有直接的關聯。按照這樣的理解,每一個AsyncAppender能夠關聯多個不一樣的AsyncAppender,因而我修改了上邊代碼段:
package org.susan.java.logging;

import org.apache.log4j.AsyncAppender;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.TTCCLayout;

public class AsyncLogging {
private static Logger logger = Logger.getLogger("org.susan.java.logging.AsyncLogging");
private AsyncAppender asyncAppender = null;
private ConsoleAppender consoleAppender = null;
//這是新添加的一個Appender2做爲asyncAppender去鏈接的一個新的ConsoleAppender
private ConsoleAppender consoleAppender2 = null;
public AsyncLogging(){
try{
logger.setAdditivity(false);
asyncAppender = new AsyncAppender();
TTCCLayout layout = new TTCCLayout("yyyy-MM-dd");
consoleAppender = new ConsoleAppender(layout,"System.out");
// 爲了在Eclipse平臺裏面測試的時候能夠更加明顯知道誰在進行記錄,因此第二個ConsoleAppender使用了System.err
TTCCLayout layout1 = new TTCCLayout("yyyy-MM-dd");
consoleAppender2 = new ConsoleAppender(layout1,"System.err");

asyncAppender.setName("Async");
asyncAppender.setBufferSize(5);
asyncAppender.setLocationInfo(true);
asyncAppender.setBlocking(false);
asyncAppender.activateOptions();

asyncAppender.addAppender(consoleAppender);
// 這裏建立的異步Appender添加了第二個Appender與之關聯起來
asyncAppender.addAppender(consoleAppender2);
logger.addAppender(asyncAppender);
}catch(Exception ex){
ex.printStackTrace();
}
}
public void doLogging(){
logger.debug("Hello One");
logger.debug("Hello Two");
logger.debug("Hello Three");
}
public static void main(String args[]){
AsyncLogging demo = new AsyncLogging();
demo.doLogging();
}
}
  修改事後將會獲得下邊的輸出:
2009-09-17 [main] DEBUG org.susan.java.logging.AsyncLogging - Hello One
2009-09-17 [main] DEBUG org.susan.java.logging.AsyncLogging - Hello One
2009-09-17 [main] DEBUG org.susan.java.logging.AsyncLogging - Hello Two
2009-09-17 [main] DEBUG org.susan.java.logging.AsyncLogging - Hello Three
2009-09-17 [main] DEBUG org.susan.java.logging.AsyncLogging - Hello Two
2009-09-17 [main] DEBUG org.susan.java.logging.AsyncLogging - Hello Three
  這樣咱們推測的結果就一目瞭然了,上邊紅色部分是consoleAppender2輸出的結果,灰色部分是consoleAppender輸出的結果
  JDBCAppender:1.2版本里面的JDBCAppender未來頗有可能會被替換掉,不只僅如此,它也不會用來記錄相關異常。JDBCAppender主要提供了一個機制直接將日誌寫入到數據庫,每個Appender的調用都會添加到一個ArrayList裏面做爲緩衝,當這個緩衝裏面填充了事件日誌信息的時候,將會被JDBCAppender替代掉,而後根據相關配置生成對應的SQL腳本而且執行。在log4j.properties配置文件裏面能夠配置該Appender的BufferSize(緩衝區大小)、dbURL(數據庫的URL地址)、User(數據庫用戶名)、Password(數據庫密碼)。在操做過程當中,能夠經過使用setSql方法來設置在日誌記錄中須要使用的SQL語句,這個SQL語句將會發送到PatternLayout對象【固然該對象是由用戶定義的】,默認狀況下全部的模式轉換在PatternLayout格式化過程都是會出如今生成的SQL命令裏面的。
  有時候咱們開發過程須要本身定義一個基於數據庫的日誌記錄器,當JDBCAppender做爲咱們使用類的父類的時候,有幾個操做是必需要注意的:
  • 重寫方法getConnection()來獲取咱們須要的鏈接,重寫該方法主要是啓用應用程序的鏈接池
  • 重寫closeConnection(Connectoin con)方法,若是咱們重寫了getConnection方法,那麼應該重寫closeConnection方法用來關閉咱們所須要的鏈接,或者說將不使用的鏈接放入到咱們須要的鏈接池裏面
  • 重寫getLogStatement(LoggingEvent event)方法用來生成動態的語句,默認狀況下咱們直接使用SQL語句的可選值。
  JMSAppender:在使用JMSAppender的過程當中,事件消息是做爲JMS消息定義的類型ObjectMessage在傳輸過程進行序列化和反序列化操做完成的,其使用模式爲JMS Topic方式。
  LF5Appender【不瞭解詳情】:這種Appender將會把日誌記錄到基於日誌控制檯的Swing界面上,該Swing控制檯支持各類類型、多種詳細試圖以及一個基於全文檢索的日誌搜索
  NTEventLogAppender:這個Appender只能使用於Windows操做系統,並且在使用該Appender的時候,須要將Log4j解壓事後裏面的NTEventLogAppender.dll動態連接庫配置到Windows系統的PATH環境變量裏面,不然咱們在開發過程將會收到JVM的錯誤信息:java.lang.UnsatisfiedLinkError
  這裏再提供一段將日誌寫入Windows事件查看器的代碼:
package org.susan.java.logging;

import org.apache.log4j.Logger;

public class NTEventTester {
public static void main(String args[]){
Logger logger = Logger.getLogger("NTlog");
logger.info("This is the test info message");
// 只有這條記錄會寫入進去
logger.fatal("This is the test fatal message");
}
}
  僅僅這段代碼固然不夠,看看這段代碼的配置文件log4j.properties
log4j.logger.NTlog = FATAL,stdout
log4j.appender.stdout=org.apache.log4j.nt.NTEventLogAppender
log4j.appender.stdout.Source=org.susan.java.logging.NTEventTester
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1};%L - %m%n
  上邊的配置後邊會講到,在配置章節講log4j.properties的信息的時候,這裏先不作說明,可是注意log4j.logger.NTlog裏面的NTlog就是在代碼裏面經過Logger.getLogger("NTlog")裏面的參數,並且邊的等級指定了記錄什麼信息,運行這段代碼可能控制檯會有下邊的輸出:
Exception in thread "main" java.lang.UnsatisfiedLinkError: no NTEventLogAppender in java.library.path
  上邊的錯誤就是上邊講到的沒有配置NTEventLogAppender.dll的緣故,因此按照上邊的配置方法須要將dll配置到PATH環境變量裏面,這裏再簡單說明如下:
  在32bit系統裏面,能夠直接把NTEventLogAppender.dll拷貝到C:/Windows/System32,這裏C爲安裝Windows操做系統盤的盤符名稱
  在64bit系統裏面的路徑應該是:C:/Windows/SysWOW64,這一點不要搞錯
  配置好了事後控制檯仍然沒有相關輸出,彆着急,去Windows看看事件查看器就會出現下邊的輸出:

  從截圖能夠看到,該事件的源爲org.susan.java.logging.NTEventTester,也就是咱們本身將這條記錄寫入到事件查看器裏面去的。
  NullAppender:上邊已經說過了org.apache.log4j.varia裏面包含了多個相關組件以及Filter組件,這個Appender能夠將日誌記錄消息發送到任意設備
  SMTPAppender:使用該Appender的時候,能夠配置BufferSize參數來配置有多少個日誌記錄消息會經過郵件發出去。
  這裏再提供一份發送郵件的日誌記錄器的測試代碼:
package org.susan.java.logging;

import org.apache.log4j.Logger;

public class EmailLogging {
public static void main(String args[]){
Logger logger = Logger.getLogger("Emaillog");
logger.info("This is the test info message");
logger.fatal("This is the test fatal message");
System.out.println("Success...");
}
}
  這段代碼自己和上邊NT記錄的自己沒有太大的區別,而真正的區別在於下邊的配置文件項:
log4j.logger.Emaillog = WARN,stdout
log4j.appender.stdout=org.apache.log4j.net.SMTPAppender
log4j.appender.stdout.SMTPHost=smtp.126.com
log4j.appender.stdout.To=silentbalanceyh@126.com
log4j.appender.stdout.Subject=ErrorLog
log4j.appender.stdout.From=silentbalanceyh@126.com
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1};%L - %m%n
  運行這段代碼你就能夠直接將郵件發送出去了,並且是將日誌記錄以郵件的形式發出去。但因爲有些郵件客戶端的問題,可能會遇到相似下邊的錯誤:
log4j:ERROR Error occured while sending e-mail notification.
javax.mail.MessagingException: 503 bad sequence of commands
  因爲本文只着重講日誌記錄,因此這個錯誤這裏不作講解。
  SocketAppender:這種Appender將發送LoggingEvent對象到一個遠程的日誌服務器,通常狀況下是一個SocketNode,而該Appender自己擁有下邊這些屬性,該Appender是客戶端操做的:
  • 若是發送的是SocketNode,並且這種狀況下,會實現日誌記錄的實時同步。換句話說,這些事件日誌會在同一個時間戳進行記錄,並且在這種狀況下,有些日誌會在本地被本地客戶端保存下來。
  • SocketAppender在使用過程當中不須要使用Layout,它會直接把LoggingEvent對象序列化到服務器上
  • 遠程日誌記錄使用TCP協議,所以若是服務器是可達到的,那麼事件日誌最終將直接被送到服務器上
  • 若是遠程服務器出了問題,發送日誌記錄的請求將會暫停。儘管如此,若是當服務器恢復正常的時候,日誌記錄將會繼續發送該請求,當可以繼續鏈接服務器的時候,這種重鏈接會被一個connector線程啓動。
  • 事件日誌將會被本地TCP實現進行緩衝存儲。意思就是若是鏈接到服務器的速度很慢可是比日誌記錄產生日誌的速度快的時候,該日誌記錄客戶端將不會由於網速很慢而受影響;可是若是網速很慢比起日誌記錄客戶端產生日誌的速度都慢的時候,該日誌記錄客戶端只能使用該網速的速錄來進行日誌記錄。
  • SocketAppender若不關聯到任何類型,它將不會被connector線程內的垃圾回收器回收。並且connector線程僅僅會在服務器出問題的狀況下才會存在,爲了不垃圾回收的問題,咱們應該在編程過程當中顯示調用close()方法,
  • 若是JVM主機的SocketAppender在它顯示關閉或者被垃圾回收器回收以前退出,這種狀況下可能使得數據通道里面的數據丟失,這是Windows系統的一個常見問題。因此爲了不數據丟失,最好是在該應用程序退出以前顯示調用SocketAppender的close()方法或者直接調用LogManager.shutdown()方法。
  SocketHubAppender:這個Appender和SocketAppender不一樣的地方是,該Appender是服務端操做的,該Appender一旦運行起來了就開始對外發送相關消息,不管有沒有接收者,其相關特性和SocketAppender差很少,可是因爲該Appender不負責輸出信息,一樣的也是不須要設置Layout的。
  SyslogAppender:該Appender主要是負責向遠程syslog服務器發送事件日誌記錄的
  TelnetAppender:該Appender主要負責向一個只讀端口發送時間日誌,並且在整個過程是能夠基於TCP/IP進行監控的。客戶端能夠經過telnet鏈接到該端口收取日誌記錄。
  WriterAppender:WriterAppender能夠將事件日誌發送到用戶選擇的任何一個地方,輸出類爲基於Writer和OutputStream的相關設備。
  ConsoleAppender:這種Appender會管理咱們的日誌消息,將日誌事件記錄到控制檯以System.out或者System.err做爲輸出機制,它默認的輸出爲System.out
  FileAppender:該Appender能夠將事件日誌寫入到一個文件裏面,它支持java.io.Writer和Console的方式,在1.2的說明裏面,未來可能會被WriterAppender和ConsoleAppender替代。
  RollingFileAppender:這種Appender從FileAppender繼承過來,大部分屬性和FileAppender是相似的,當日志記錄的文件到達文件容量的最大值的時候,會自動建立一個新的日誌文件
  ExternallyRolledFileAppender:該Appender用於監聽某個端口號上邊的「RollOver」消息【設置配置裏面的Port】
  DailyRollingFileAppender:該Appender從FileAppender繼承而來,可以按照必定的頻度滾動日誌記錄文件。
  [3]Log4j中的Layout:
  Log4j中的Layout相似於JDK Logging裏面的格式化輸出,只是Log4j在輸出過程將這些內容規範化了,從Log4j的總體結構上來說,主要有五個子類的Layout
  • HTMLLayout:輸出爲HTML的表格方式,若是沒有指定編碼格式的話,該輸出的編碼格式爲UTF-8UTF-16
  • SimpleLayout:輸出爲最簡單的格式,僅僅打印一個「Level名 - 日誌記錄信息」,例如:「DEBUG - Hello World」
  • DataLayout:該類是一個抽象類,主要是經過可選項來定製輸出,該類有一個子類TTCCLayout
    |—TTCCLayout
    :TTCC的格式主要包含了時間、線程、分類、嵌套內容信息,這四個內容都是能夠經過人爲編程的方式進行關閉和打開的,時間格式依賴DateFormat類
    176 [main] INFO org.apache.log4j.examples.Sort - Populating an array of 2 elements in reverse order.
    225 [main] INFO org.apache.log4j.examples.
    SortAlgo
    - Entered the sort method.
    262 [main] DEBUG org.apache.log4j.examples.
    SortAlgo
    .OUTER i=1 - Outer loop.
    276 [main] DEBUG org.apache.log4j.examples.
    SortAlgo
    .SWAP i=1 j=0 - Swapping intArray[0] = 1 and intArray[1] = 0
    290 [main] DEBUG org.apache.log4j.examples.
    SortAlgo
    .OUTER i=0 - Outer loop.
    304 [main] INFO org.apache.log4j.examples.
    SortAlgo.DUMP - Dump of interger array:

    該Layout的第一個字段是該程序執行的時間,以毫秒爲單位,第二個字段主要輸出的執行的線程,其次是相關日誌記錄的登記,第四個字段是該語句出現的位置以及相關類型。最後一個字段表示嵌套內容相關信息
    【*:不要在不一樣的Appender裏面使用同一個TTCCLayout實例,由於TTCCLayout是非線程安全的,最好的用法是一個Appender使用惟一的一個TTCCLayout實例。】
  這裏先看一段TTCCLayout的實例代碼:
package org.susan.java.logging;

import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.NDC;
import org.apache.log4j.TTCCLayout;
/**
*針對TTCCLayout的一個簡單應用
**/
public class TTCCLayoutDemo {
private static Logger logger = Logger.getLogger("TestTTCC");
private ConsoleAppender appender = null;
private TTCCLayout layout = null;
public TTCCLayoutDemo()
{
logger.setAdditivity(false);
// 初始化定義的TTCCLayout
layout = new TTCCLayout("yyyy-MM-dd");
// 初始化該Layout對應的Appender,這裏使用的ConsoleAppender
appender = new ConsoleAppender(layout,"System.out");
// 將Appender添加到定義的Logger
logger.addAppender(appender);
}

public void computeSquareRoot(double number){
NDC.push(new Double(number).toString());
double sqrt = Math.sqrt(number);
logger.info("The sqrt value: " + sqrt);
NDC.pop();
}

public static void main(String args[]){
TTCCLayoutDemo demo = new TTCCLayoutDemo();
demo.computeSquareRoot(22);
demo.computeSquareRoot(44);
}
}
  不須要log4j.properties文件,這裏會獲得下邊的輸出:
2009-09-17 [main] INFO TestTTCC 22.0 - The sqrt value: 4.69041575982343
2009-09-17 [main] INFO TestTTCC 44.0 - The sqrt value: 6.6332495807108
  【*:這裏須要注意的是輸出和上邊講解遇到的TTCCLayout的輸出不同,由於咱們在初始化構造TTCCLayout的時候使用的不一樣的參數的緣故。】
  • XMLLayout:該Layout會按照XML格式輸,其定義文件爲log4j.dtd,該格式化輸出輸出的不是一個標準的良好格式的XML文檔,通常格式以下:
    <?xml version="1.0"
    ?>
    <!
    DOCTYPE log4j:eventSet SYSTEM "log4j.dtd" [<!ENTITY data SYSTEM "abc">]
    >
    <log4j:eventSet
    vertion="1.2" xmlns:log4j="http://jakarta.apache.org/log4j/"
    >
    &data;

    </log4j:eventSet>

    該格式輸出的編碼通常是
    UTF-8或者UTF-16
  • PatternLayout:這是Layout裏面相對複雜的一個Layout這種狀況下日誌的格式是由模式字符串來進行設置的,主要依賴全局的format方法來格式化一個LoggingEvent而後經過模式轉化將日誌的格式輸出,輸出結果是須要依賴模式轉換的
  [4]模式轉換:
  模式轉換常用在C語言的printf函數裏面,模式轉換就是定義必定的轉化模式,而後將對應日誌輸出的值用轉換模式進行匹配而後轉換爲字符串輸出。在Log4j裏面,全部的轉換模式都是由一個%開始,而後跟着相對的模式符號,並且該模式可使用在log4j.properties屬性文件裏面。
  舉個例子,若是使用了模式「%-5p [%t]: %m%n」模式,看下邊代碼段:
  root.debug("Hello Debug!");
  root.info("Hello Info!");
  上邊這段代碼咱們將會獲得下邊的輸出:
DEBUG [main]: Hello Debug!
INFO [main]: Hello Info!
  那麼這些模式裏面的模式表示什麼意思呢?接下來看一張模式轉換表:
模式參數 用法描述 舉例
%c 列出logger名字空間的名稱 假設當前logger的名空間爲「org.java.susan.logging」
%c——org.java.susan.logging
%20c——若名空間長度小於20的時候,則左邊用空格填充
%-20c——若名空間長度小於20的時候,右邊填充空白
%.40c——若名空間長度超過40就截去多餘的字符
%20.40c——若名空間小於20,左邊用空白填充,若是超過40就截去多餘字符
%-20.40c——若名空間小於20,右邊用空白填充,若是超過40就截去多餘字符
%c{2}——susan.logging
%c{4}——org.java.susan.logging
%C 列舉出logger的類全名(包含包路徑) 假設當前類是org.java.susan.logging.PatternTester
%C——org.java.susan.logging.PatternTester
%C{1}——PatternTester
%d 顯示日誌記錄的時間,{<日期格式>}使用ISO8601定義的日期格式 %d{yyyy/MM/dd HH:mm:ss,SSS}——
2009/9/17 13:25:22,134【最後三位爲毫秒】
%d{ABSOLUTE}——13:25:22,134
%d{DATE}——17 Sep 2009 13:25:22,134
%d{ISO8601}——2009-9-17 13:25:22,134
%F 顯示調用的logger的源文件名 %F——PatternTester.java
%l 輸出日誌事件發生的位置,包括類目錄、發生線程,以及在代碼中的行數 %l——PatternTester.fun(PatternTester.java:45)
%L 輸出日誌事件發生的代碼行 45
%m 顯示輸出消息 %m——該消息輸出的主要目的是爲了進行Debug操做
%M 顯示調用logger的方法名 %M——fun
%n 當前平臺下的換行符 %n——表示換行
%p 顯示該日誌的優先級 %p——INFO
%r 顯示從程序啓動時到記錄該條日誌時已經通過的毫秒 %r——1435
%t 輸出產生該日誌事件的線程名 %t——PatternTester
%x 按NDC順序輸出日誌 假設某線程調用順序是MyDriver調用了org.java.susan.logging.PatternTester
%c %x - %m%n——MyDriver - Call org.java.susan.logging.PatternTester - Log in PatterTester
MyDriver - Return to MyDriver
%X 按MDC輸出日誌。一般用於多個客戶端鏈接同一臺服務器,方便服務器區分是哪一個客戶端訪問留下來的日誌。 %X{5}——(記錄代號爲5的客戶端日誌)
%% 顯示一個百分號 %%——%
  [5]關於NDC和MDC:
  NDC(Nested Diagnostic Context)和MDC(Mapped Diagnostic Context)是log4j中很是有用的兩個類,它們用於存儲應用程序的上下文信息,從而便於在log中使用這些上下文信息。
  <!--[if !supportEmptyParas]--><!--[endif]-->
  NDC採用了一個相似棧機制來push和pop上下文信息,每個線程都獨立地存儲上下文信息。好比說一個servlet就能夠針對每個request建立對應的NDC,存儲客戶端地址等信息。當使用的時候,咱們要儘量確保在進入一個context的時候,把相關的信息使用NDC.push(message);在離開這個context的時候使用NDC.pop()將信息刪除。另外因爲設計上的一些問題,還須要保證在當前thread結束的時候使用NDC.remove()清除內存,不然會產生內存泄漏的問題。存儲了上下文信息以後,咱們就能夠在log的時候將信息輸出。
  使用NDC的重要好處就是,當咱們輸出一些上下文信息的時候,不須要讓logger去尋求這些信息,而只須要在適當的位置進行存儲,而後再配置文件中修改PatternLayout。
  MDC和NDC的用法相似,不一樣的是MDC內部使用了相似map的機制來存儲相關信息,上下文信息也是每一個線程獨立存儲,不一樣的是信息都是以它的key值存儲在「map」中。相對應的方法,MDC.put(key,value);MDC.remove(key);MDC.get(key);在配置PatternLayout的時候使用:%x{key}來輸出對應的value,一樣的MDC也有一個MDCMatchFilter。【*:MDC是線程獨立的,可是一個子線程會自動活得一個父線程MDC的拷貝】
  3)配置相關以及屬性文件log4j.properties
  單獨把配置獨立爲一個小節是由於Log4j大部分是須要進行配置使用的,從上邊郵件發送以及NT事件記錄器的代碼Demo能夠知道其實僅僅須要在Log4j.properties文件裏面配置必定的屬性,開發過程就簡化了不少,上邊兩端代碼基本如出一轍【NT日誌記錄器和Email日誌記錄Demo代碼】,僅僅修改了配置文件。
  [1]BasicConfigurator類:
  BasicConfigurator類主要爲Log4j提供一個基礎的配置環境,經過調用BasicConfigurator.configure方法能夠建立一個簡單的Log4j環境,並且這個方法是經過硬編碼直接將Appender設置爲ConsoleAppender的,該配置的輸出設置爲PatternLayout類,模式爲"%-4r [%t] %-5p %c %x - %m%n",日誌記錄的級別爲Level.DEBUG,代碼說明上邊已經用到過了。
  注意:若是不對Log4j進行任何配置的話,將會收到相似下邊的警告:
log4j:WARN No appenders could be found for logger (org.susan.java.logging.Foo).
log4j:WARN Please initialize the log4j system properly.
  若是咱們經過配置log4j.properties配置文件的方式來進行配置,只須要進行下邊的設置:
# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=DEBUG, A1
# A1 is set to be a ConsoleAppender.
log4j.appender.A1=org.apache.log4j.ConsoleAppender
# A1 uses PatternLayout.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
  [2]log4j.properties文件:(參考連接:http://www.wangchao.net.cn/bbsdetail_60723.html
  根配置:
  根日誌記錄器配置的語法爲:[真正使用的時候,記得去掉開始的#,在properties文件裏面#的含義標識行註釋]
#log4j.rootLogger = [level], appenderName,appenderName,...
  其中Level就是日誌自己的優先級,log4j裏面建議使用的級別爲:ERROR,WARN,INFO,DEBUG
  appenderName就是日誌輸出的地方,某一個Appender的類名
  Appender配置:
  配置日誌輸出目的Appender的語法爲:
#log4j.appender.appenderName = className
#log4j.appender.appenderName.key = value
#log4j.appender.appenderName.key1 = value1
  其中appenderName爲定義一個Appender的名字、className爲一個類全名
  Layout配置:
  佈局配置格式的語法爲:
#log4j.appender.appenderName.layout = className
#log4j.appender.appenderName.layout.key1 = value1
#log4j.appender.appenderName.layout.key2 = value2
  關於Log4j中使用佈局打印的模式以下:
#%p 輸出優先級,DEBUG,INFO,WARN,ERROR,FATAL
#%r 輸出自應用啓動到輸出該log信息耗費的毫秒
#%c 輸出所屬的類,一般就是在所在類的全名
#%t 輸出產生該日誌事件的線程名
#%n 輸出一個回車換行,Windows爲「/r/n」,Unix平臺爲「/n」
#%d 輸出日誌的時間點的日期或者時間,格式爲ISO8601,也可使用%d{yyyy MM dd}格式化輸出
#%l 輸出日誌發生的位置,包括類名、線程以及代碼中的行
  【例:配置一個名爲myConsole的ConsoleAppender,其輸出Layout爲TTCCLayout】
log4j.appender.myConsole = org.apache.log4j.ConsoleAppender
log4j.appender.myConsole.layout = org.apache.log4j.TTCCLayout
  【例:配置一個TTCCLayout的具體屬性】
log4j.appender.myConsole.layout.ThreadPrinting=false
log4j.appender.myConsole.layout.ContextPrinting=false
log4j.appender.myConsole.layout.CategoryPrefixing=false
log4j.appender.myConsole.layout.DateFormat=RELATIVE
  【例:配置一個自定義名稱的Logger】
log4j.logger.org.susan.java.logging.mylogger=DEBUG,myConsole
  這種狀況下,可使用Logger logger = Logger.getLogger("org.susan.java.logging.mylogger");來獲取該日誌記錄,而該日誌記錄的輸出輸出到myConsole這個Appender
  【例:配置一個FileLayout】
log4j.appender.myConsole.layout = org.apache.log4j.PatternLayout
log4j.appender.myConsole.layout.conversionPattern=%p - %m%n
  【例:配置一個SMTP的郵件記錄】
log4j.logger.EmailLogger = WARN,SMTP
log4j.appender.SMTP=org.apache.log4j.net.SMTPAppender
log4j.appender.SMTP.SMTPHost=smtp.126.com
log4j.appender.SMTP.To=silentbalanceyh@126.com
log4j.appender.SMTP.Subject=ErrorLog
log4j.appender.SMTP.From=silentbalanceyh@126.com
log4j.appender.SMTP.layout=org.apache.log4j.SimpleLayout
  【例:NT事件查看器配置】
log4j.logger.NTLogger = FATAL,NTEVENT
log4j.appender.NTEVENT=org.apache.log4j.nt.NTEventLogAppender
log4j.appender.NTEVENT.Source=org.susan.java.logging.NTEventTester
log4j.appender.NTEVENT.layout=org.apache.log4j.PatternLayout
log4j.appender.NTEVENT.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1};%L - %m%n
  【例:Rolling文件配置】
log4j.logger.RollingLogger = FATAL,ROLLING_FILE
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
  【例:Socket相關配置】
log4j.logger.SocketLogger = INFO,SOCKET
log4j.appender.SOCKET=org.apache.log4j.RollingFileAppender
log4j.appender.SOCKET.RemoteHost=localhost
log4j.appender.SOCKET.Port=5001
log4j.appender.SOCKET.LocationInfo=true
log4j.appender.SOCKET.layout=org.apache.log4j.PatternLayout
log4j.appender.
SOCKET.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD]%n%c[CATEGORY]%n%m[MESSAGE]%n%n
  【例:JDBC配置】
log4j.logger.JDBCLogger = INFO,DATABASE
log4j.appender.DATABASE=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.DATABASE.URL=jdbc:mysql://localhost:3306/db_rect
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
  
【例:DailyRolling日誌記錄配置】
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
  ii.Commons Logging Framework:
  1)JCL基本概念[介紹版本爲1.1]:
  Commons Logging Framework又稱爲JCL(Java Common Logging),該日誌記錄框架和Log4j以及Java Logging(JDK 1.4 Logging Framework)存在本質區別。前兩個日誌記錄器自己提供了詳細的實現,都有一個屬於本身的體系結構,而JCL自己沒有。JCL能夠真正稱爲一個日誌框架,由於它提供的是一個日誌接口,並且是一個輕量級的不依賴實現的日誌記錄的抽象接口工具包,它容許任何開發者使用工具在該抽象框架下邊進行日誌記錄的實現層的開發。而它自己只是一個輕量級的抽象框架
  官方下載地址:
  http://apache.freelamp.com/commons/logging/binaries/commons-logging-1.1.1-bin.zip
  由於JCL自己的類比較少,先看看整個抽象框架的包結構:[I]標識接口[A]標識抽象類[C]標識具體類,[E]標識異常
  org.apache.commons.logging
  |—[I]Log
  |—[C]LogFactory
  |—[C]LogSource
  |—[E]LogConfigurationException
  org.apache.commons.logging.impl
  |—[C]AvalonLogger
  |—[C]Jdk13LumberjackLogger
  |—[C]Jdk14Logger
  |—[C]Log4JLogger
  |—[C]LogFactoryImpl
  |—[C]LogKitLogger
  |—[C]NoOpLog
  |—[C]ServletContextCleaner
  |—[C]SimpleLog
  |—[C]WeakHashtable
  • Log接口:
    實現該接口的實例能夠被LogFactory順利建立和初始化,若是某一個日誌記錄想要初始化必須調用一個帶String單參數的構造函數,傳入的語意爲該日誌記錄器的名稱
    Log接口定義了日誌的幾個登記,全部實現該接口的類都須要遵循這幾個等級:
    trace、debug、info、warn、error、fatal
  • LogFactory爲一個抽象類,使用工廠模式,能夠建立一個實現了Log接口的日誌記錄器實例,並且該記錄器實現是集成了JavaAPI和JAXP
    【關鍵:LogFactory的實現是基於
    SAXParserFactory和DocumentBuilderFactory的,因此在使用的時候有可能還會用到Apache Xerces[一個能夠解析XML的庫]
  • LogSource類【Deprecated】
    該類是原版本的建立Log實例的類,如今已經被LogFactory替代了。當LogFactory建立某個Log實例的時候,應用程序能夠經過調用方法
    makeNewLogInstance去實例化某個類或者針對該類的實現類進行配置工做。默認狀況下,若是調用方法getInstance()將會使用下列算法:
    [1]若是Log4J是配置好了的,那麼LogFactory會建立一個
    org.apache.commons.logging.impl.Log4JLogger實例
    [2]若是JDK是1.4或者以上的版本,LogFactory就會建立一個org.apache.commons.logging.impl.Jdk14Logger實例
    [3]如過兩個都沒有知足,就返回一個org.apache.commons.logging.impl.NoOpLog實例
    上邊是默認的配置和默認的建立實例的行爲,可是咱們能夠經過兩種方法修改相關內容
    [1]啓動命令提示行,在進行java編譯的時候設置自定義的org.apache.commons.logging.Log的實現類的名稱
    [2]在運行時調用
    LogSource.setLogImplementation()

  • LogConfigurationException類:
    當使用LogFactory建立實現了Log類接口的實例的時候,若是建立失敗就會拋出該異常
  • AvalonLogger類:
    該類有兩種用途:
    [1]該類的實例能夠經過AvalonLogger(Logger)方法來進行構造,在這種狀況下,該類在Logger外層進行了一層簡單的封裝,這種用法最大的用途在於使用一個屬性設置器的時候。
    [2]方法setDefaultLogger(org.apache.avalon.framework.logger.Logger)的屬性調用來設置Avalon類的關聯

      這裏插入Avalon框架的一段介紹:
    Apache的Avalon是一個包括核心框架、工具、組件和容器的面向組件編程(COP)的完整開發平臺。經過使用關鍵設計模式,如反向控制模式(IoC)和分離考慮模(SoC),Avalon實現了傳統OOP框架的一些優勢: 1.沒有執行鎖 2.組件之間低耦合 3.管理組件生命週期 4.配置管理和易用的API 5.組件元數據框架和工具 6.服務相關的管理獨立的、J2EE或Web環境的嵌入式容器 在COP方面,可重用的組件可以被組合到容器中,以提供應用程序模塊。模塊能夠依次使用來建立你所須要的,從客戶桌面應用程序,到FTP服務器,到Web服務,等等。Avalon提供各類基本組件和缺省的應用程序模塊,幫助你快速的創建你本身的應用程序解決方案。
  • Jdk13LumberjackLogger:實現了接口org.apache.commons.logging.Log,封裝了Lumberjack實現部分,主要用於JDK 1.4以前實現Java的日誌記錄,Lumberjack項目是用來進行Java日誌記錄的老版本的日誌API
  • Jdk14Logger:實現了接口org.apache.commons.logging.Log,用來封裝了上邊咱們講到的JDK 1.4 Logging Framework
  • Log4JLogger:實現接口org.apache.commons.logging.Log,用來封裝了Log4j版本1.2裏面的Logger實例,初始化該實例的配置的時候應該使用一般的初始化方式。
    該類不能用於Log4j 1.3,主要緣由在於:[1]1.2版本的Logger使用的是Priority參數而不是咱們介紹的Level參數;[2]Level類是繼承於Priority的,在1.3版本里面,須要修改相關優先級,可是不是繼承於Priority的,因此不兼容1.3版本,具體緣由是否如此我不太清楚。
  • LogFactoryImpl類:LogFactory的子類,經過必定的算法動態選擇應該使用哪一種日誌記錄器,主要對底層的不一樣的日誌實現類進行簡單的封裝
    [1]
    使用工廠配置屬性org.apache.commons.logging.Log用來標識使用的實現類
    [2]
    使用org.apache.commons.logging.Log系統屬性用來標識使用的實現類
    [3]若是Log4J是配置好了的,那麼LogFactory會建立一個org.apache.commons.logging.impl.Log4JLogger實例
    [4]若是JDK是1.4或者以上的版本,LogFactory就會建立一個org.apache.commons.logging.impl.
    Jdk14Logger
    實例
    [5]如過兩個都沒有知足,就返回一個org.apache.commons.logging.impl.
    SimpleLog實例

    不只僅如此,該類還有一個方法能夠提供反向的關聯,使用Log實現類的方法setLogFactory(),傳入一個參數LogFactory,這種方法調用事後就能夠修改實現類對應的Factory的關聯項,在整個程序運行過程當中,Factory將會記錄下來全部建立過的Logger實例對象,當再次調用getInstance()方法的時候就直接從記錄裏面激活該實例。
    【我仔細思考了如下,這個地方應該使用了Logger的「池化」技術,使用工廠雖然能夠反覆進行實例的構造,可是使用的是Logger的name做爲標識,從設計上考慮,Logger的名字通常狀況下都是某個類的全名,從整個系統級別講,若是OO設計設計得不錯的話,每個類若是須要進行日誌記錄的話最好使用單個日誌記錄器,這樣能夠節省系統開銷。】
  • LogKitLogger類:實現了接口org.apache.commons.logging.Log,該類封裝了avalon-logkit日誌系統,僅僅將LogKit留給用戶本身進行配置。
  • NoOpLog類:該類將會拋出全部的相關異常信息,沒有任何系統屬性配置的支持,通常狀況下不使用該類。
  • SimpleLog類:簡單實現了org.apache.commons.logging.Log接口,用來記錄全部可用的日誌信息,並且把全部定義好的Logger輸出到System.err,下邊的一些系統屬性能夠用來配置該Logger的一些屬性:
    [1]org.apache.commons.logging.simplelog.defaultlog——默認的SimpleLog的等級參數,必須是如下的值的集合(trace,debug,info,warn,error,fatal
    ),若是沒有設置默認爲info
    [2]org.apache.commons.logging.simplelog.
    log.xxxxx——定義某個名稱爲xxxxx實例的日誌器的等級,同上值必須是一個Level的集合
    [3]org.apache.commons.logging.simplelog.
    showlogname
    ——若是爲true在日誌記錄的時候須要輸出name屬性,若是false的話就不輸出Logger的名稱,默認爲false
    [4]org.apache.commons.logging.simplelog.
    showShortLogname——和上邊參數同樣,惟一的區別是該項是類名,而上邊的參數是輸出的類全名,還有不一樣的是默認值爲true
    [5]org.apache.commons.logging.simplelog.
    showdatetime——爲true就輸出時間,若是爲false就不輸出時間,默認是false
    [6]org.apache.commons.logging.simplelog.
    dateTimeFormat——設置時間的格式,該格式應該是java.text.SimpleDateFormat能夠解析的時間合法格式
    這種配置須要檢測類加載器在路徑裏面是否能夠找到simplelog.properties的屬性文件用來配置以上的選項,或者直接在java命令後邊帶上相關的參數
  • ServletContextCleaner類和WeakHashtable類在此不作講解,須要瞭解的能夠去查詢該類的API,並且我沒有用到過這兩個類,因此也沒有去了解這兩個類到底作了些什麼,很差意思。
  2)如何配置Log4j實現
  Log4j是一個經常使用的日誌記錄庫,因此在該抽象框架裏面配置相關實現的時候須要針對一些屬性作一些簡單的操做。JCL自己不提供任何相關的實現,它僅僅是一個抽象框架,在路徑下邊必須提供一個屬性文件:
  commons-logging.properties
  配置的時候須要配置如下屬性:
#log4j.configuration=log4j.properties
  使用該屬性指定咱們須要的Log4j的屬性文件的名稱,若是沒有指定,默認的就是log4j.properties
#log4j.rootCategory=priority[,appender]*
  設置默認的root日誌記錄器的優先級
#log4j.logger.logger.name=priority
  設置名稱爲logger的優先級,通常狀況爲:DEBUG,INFO,WARN,ERROR,FATAL
#log4j.appender.appender.Threshold=priority
  設置不一樣的輸出源:console,files,sockets或者其餘的。
  關於這兩個日誌庫的整合使用,提供一段代碼進行簡單說明:
  log4j.properties內容:
log4j.logger.org.susan.java.logging.commonlog= WARN,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1};%L - %m%n
  commons-logging.properties內容:
log4j.configuration=log4j.properties
  運行代碼:
package org.susan.java.logging;

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

public class LogTest {
public static void main(String args[]){
Log log = LogFactory.getLog("org.susan.java.logging.commonlog");
log.debug("debug");
log.error("error");
log.fatal("fatal");
log.info("info");
log.warn("warn");
}
}
  下邊是輸出結果:
20:26:11,014 ERROR commonlog;10 - error
20:26:11,038 FATAL commonlog;11 - fatal
20:26:11,039 WARN commonlog;13 - warn
  這樣就將抽象框架和Log4j實現集成到一塊兒了。
5.總結
  到這裏,關於異常、日誌、斷言部分的內容就所有講完了,有什麼筆誤的地方記得發Email給我:silentbalanceyh@126.com。日誌部分主要選取了Java裏面比較經常使用的日誌記錄框架,根據各類開源產品的應用能夠知道,Log4j也是推薦使用的日誌記錄框架,此處也針對各類不一樣的日誌記錄器進行了詳細的說明。斷言部分自己內容不太多,只要理解該用法和相關意思了就很容易使用了,最終用於開發的時候注意使用方法以及用的位置合適就能夠了。整體來說,這一部份內容有可能咱們在平時的信息管理類的系統裏面很難遇到,可是真正使用的時候也很不想去查閱官方文檔,這裏總結的不少內容來自官方文檔的參考以及我的作的一些實驗。記得若是有什麼問題發Email給我我會不斷玩善的。沒有想過篇幅過長,原本只有上下兩篇,如今只能打散了造成三篇,但願各位讀者見諒。
相關文章
相關標籤/搜索