Log4jjava |
全面詳解數據庫 |
香山風乘(wjiang1)apache 2012/9/12服務器 |
聲 明app
本文是針對在工做中,學習過程當中遇到的問題和解決方案作的總結,以供有須要的同事和朋友提供參考,在本文中可能有闡述不全面和觀點有誤的地方,敬請讀者諒解和支持。框架
如您對本文持有不一樣的意見和看法請聯繫以下郵件:jsp
對於使用本文中的解決方法形成的損失做者概不負責。工具
Log4j是Apache的一個開放源代碼項目,經過使用Log4j,咱們能夠控制日誌信息輸送的目的地是控制檯、文件、GUI組件、甚至是套接口服務器、NT的事件記錄器、UNIX Syslog守護進程等;咱們也能夠控制每一條日誌的輸出格式;經過定義每一條日誌信息的級別,咱們可以更加細緻地控制日誌的生成過程。這些能夠經過一個配置文件來靈活地進行配置,而不須要修改應用的代碼。
優勢:首先,它能精確地提供運行時的上下文(context)。一旦在程序中加入了Log 代碼,它就能自動的生成並輸出logging信息而不須要人爲的干預。另外,log信息的輸出能夠被保存到一個固定的地方,以備之後研究。除了在開發過程當中發揮它的做用外,一個性能豐富的日誌記錄軟件包能看成一個審計工具(audit tool)使用。
缺點:Logging確實也有它的缺陷。它下降了程序運行的速度。它太冗長,查看時很容易錯過。爲了減小這些負面影響,log4j 被設計得可靠,高效和靈活。由於,記錄日誌不多是一個應用程序的主要焦點,log4j API 儘可能作到容易被理解和使用。
任何logging API 與簡單的System.out.println輸出調試信息方法比較,最主要的優勢在於它可以關閉一些調試信息輸出而不影響其餘人的調試。這種能力的實現是假設這些logging空間,也就是全部的可能發生的日誌說明空間,能夠根據程序開發人員選擇的標準進行分類。這一觀察之前使得咱們選擇了category做爲這個軟件包的中心概念。可是,在log4j 1.2版本之後,Logger類取代了Category類。對於那些熟悉早先版本的log4j的開發人員來講,Logger類只不過是Category類的一個別名。
Logger實際一個類Logger是被命名的實體。Logger的名字大小寫有區別(case-sensitive),而且它們遵照階層式的命名規則: Named Hierarchy
若是一個logger 的名字後面跟着一個點號(dot),它就是點號(dot)後面的那個logger的前輩( ancestor),是這個晚輩(descendant) 的前綴。若是在它本身和這個晚輩之間沒有其它的前輩,它和這個晚輩之間就是父子關係。
|
例如,叫作"com.foo"的logger是叫作 "com.foo.Bar"的logger的父輩 。一樣地,"java"是"java.util" 的父輩,是"java.util.Vector"的前輩。大多數開發人員都熟悉這種命名方法。"com.foo" "com.foo.Bar" "java" "java.util" "java.util.Vector"
根(root)logger 位於logger 階層的最上層。它在兩個方面很特別:
它老是存在的
不能經過使用它的名字直接獲得它
經過這個類的靜態方法Logger.getRootLogger獲得它(指RootLogger)。全部其餘的loggers是經過靜態方法Logger.getLogger來實例化並獲取的。這個方法Logger.getLogger把所想要的logger的名字做爲參數。
log4j實現的原理猜想是經過拋出有一個異常,獲取執行的類名和包名,方法名,行號 例如: StackTraceElement st =new Throwable.getStackTrace();
Log4j由三個重要的組件構成:日誌信息的優先級,日誌信息的輸出目的地,日誌信息的輸出格式。
從上倒下級別依次下降:
OFF(關閉)
FATAL(嚴重錯誤)
ERROR(錯誤)
WARN(警告)
INFO(通常信息)
DEBUG(調試)
TRACE(跟蹤)
ALL(所有)
也能夠經過Level類的子類去定義你本身的優先級別,對於一個給定的logger C,它繼承的級別等於logger階層裏,從C開始往root logger上去的第一個non-null級別。要保證全部的loggers最終都繼承一個優先級別,root logger老是有一個被指派的優先級。
下面是具備各類指派優先級別值的四個表格,以及根據上面的規則所得出的繼承優先級別。
例1:
Logger |
Assigned |
Inherited |
root |
Proot |
Proot |
X |
none |
Proot |
X.Y |
none |
Proot |
X.Y.Z |
none |
Proot |
在上面的示例1中,只有root logger被指派了級別。這個級別的值,Proot,被其它的loggers X, X.Y 和 X.Y.Z繼承了。
例2:
Logger |
Assigned |
Inherited |
root |
Proot |
Proot |
X |
Px |
Px |
X.Y |
Pxy |
Pxy |
X.Y.Z |
Pxyz |
Pxyz |
在上面的示例2中,全部的loggers都有一個指派的級別值。不須要級別繼承。
例3:
Logger |
Assigned |
Inherited |
root |
Proot |
Proot |
X |
Px |
Px |
X.Y |
none |
Px |
X.Y.Z |
Pxyz |
Pxyz |
在示例3中,loggers root, X 和 X.Y.Z 分別被指派級別值Proot, Px 和Pxyz。Logger X.Y 從它的父輩X那裏繼承它的級別值。
例4:
Logger |
Assigned |
Inherited |
root |
Proot |
Proot |
X |
Px |
Px |
X.Y |
none |
Px |
X.Y.Z |
none |
Px |
Example 4 |
在示例4中,loggers root和X 分別被指派級別值Proot和Px。Logger X.Y和X.Y.Z繼承它們最接近的父輩X的被指派的級別值。
org.apache.log4j.ConsoleAppender(控制檯),org.apache.log4j.FileAppender(文件),
org.apache.log4j.DailyRollingFileAppender(天天產生一個日志文件),
org.apache.log4j.RollingFileAppender(文件到達指定大小的時候產生新文件),
org.apache.log4j.WriterAppender(將日誌信息以流格式發送)
org.apache.log4j.lf5.LF5Appender(日誌查看工具)
org.apache.log4j.jdbc.JDBCAppender(日誌數據庫)
更多輸出目地請參考API
1) ConsoleAppender 選項
Threshold=WARN:指定日誌消息的輸出最低層次。
ImmediateFlush=true:默認值是 true,意謂着全部的消息都會被立即輸出。 Target=System.err:默認情況下是:
System.out,指定輸出控制臺
2) FileAppender 選項
Threshold=WARN:指定日誌消息的輸出最低層次。
ImmediateFlush=true:默認值是 true,意謂着全部的消息都會被立即輸出。 File=mylog.txt:指定消息輸出到 mylog.txt 文件。
Append=false:默認值是 true,即將消息增加到指定文件中,false 指將消息覆蓋指定的文件內容。
3) DailyRollingFileAppender 選項
Threshold=WARN:指定日誌消息的輸出最低層次。
ImmediateFlush=true:默認值是 true,意謂着全部的消息都會被立即輸出。File=mylog.txt:指定消息輸出到 mylog.txt 文件。
Append=false:默認值是 true,即將消息增加到指定文件中,false 指將消息覆蓋指定的文件內容。
DatePattern=''.''yyyy-ww:每周滾動一次文件,即每周產生一個新的文件。固然也可以指定按月、周、天、時和分。即對應的格式如下:
''.''yyyy-MM: 每個月
''.''yyyy-ww: 每週
''.''yyyy-MM-dd: 天天
''.''yyyy-MM-dd-a: 每天兩次
''.''yyyy-MM-dd-HH: 每小時
''.''yyyy-MM-dd-HH-mm: 每分鐘
4) RollingFileAppender 選項
Threshold=WARN:指定日誌消息的輸出最低層次。
ImmediateFlush=true:默認值是 true,意謂着全部的消息都會被立即輸出。File=mylog.txt:指定消息輸出到 mylog.txt 文件。
Append=false:默認值是 true,即將消息增加到指定文件中,false 指將消息覆蓋指定的文件內容。
MaxFileSize=100KB: 後綴可以是 KB, MB 或者是 GB. 在日志文件到達該大小時,將會自動滾動,即將原來的內容移到 mylog.log.1 文件。
MaxBackupIndex=2:指定可以產生的滾動文件的最大數。
org.apache.log4j.HTMLLayout(以HTML表格形式佈局),
org.apache.log4j.PatternLayout(能夠靈活地指定佈局模式),
org.apache.log4j.SimpleLayout(包含日誌信息的級別和信息字符串),
org.apache.log4j.TTCCLayout(包含日誌產生的時間、線程、類別等等信息)
1) HTMLLayout 選項
LocationInfo=true:默認值是 false,輸出 java 文件名稱和行號
Title=my app file: 默認值是 Log4J Log Messages.
2) PatternLayout 選項
ConversionPattern=%m%n:指定怎樣格式化指定的消息。
下面是經常使用的格式符號:
%m 輸出代碼中指定的消息
%p 輸出優先級,即DEBUG,INFO,WARN,ERROR,FATAL
%r 輸出自應用啓動到輸出該log信息耗費的毫秒數
%c 輸出所屬的類目,一般就是所在類的全名
%t 輸出產生該日誌事件的線程名
%n 輸出一個回車換行符,Windows平臺爲「\r\n」,Unix平臺爲「\n」
%d 輸出日誌時間點的日期或時間,默認格式爲ISO8601,也能夠在其後指定格式,好比:%d{yyy MMM dd HH:mm:ss,SSS},輸出相似:2002年10月18日22:10:28,921
%l 輸出日誌事件的發生位置,包括類目名、發生的線程,以及在代碼中的行數。
能夠在%與模式字符之間加上修飾符來控制其最小寬度、最大寬度、和文本的對齊方式。如:
l %20c:指定輸出category的名稱,最小的寬度是20,若是category的名稱小於20的話,默認的狀況下右對齊。
l %-20c:指定輸出category的名稱,最小的寬度是20,若是category的名稱小於20的話,」-」號指定左對齊。
l %.30c:指定輸出category的名稱,最大的寬度是30,若是category的名稱大於30的話,就會將左邊多出的字符截掉,但小於30的話也不會有空格。
l %20.30c:若是category的名稱小於20就補空格,而且右對齊,若是其名稱長於30字符,就從左邊交遠銷出的字符截掉。
3) XMLLayout 選項
LocationInfo=true:默認值是 false,輸出 java 文件和行號
Log4j支持兩種配置文件格式,一種是XML格式的文件,一種是properties格式的文件。在沒有配置文件的狀況下在獲取logger以前使用以下方法自動快速地使用缺省 Log4j 環境。
BasicConfigurator.configure ();
咱們常常會遇到修改log4j配置的狀況,在這種狀況下,頻繁重啓應用顯然是不可接受的。幸虧log4j提供了自動從新加載配置文件的能力,在配置文件修改後,便會本身從新加載配置。在1.2及之前的版本中DOMConfigurator和PropertyConfigurator都提供了configureAndWatch方法,對指定的配置文件進行監控,而且能夠設置檢查的間隔時間。
若是想了解log4j的加載過程,能夠設置系統屬性"log4j.debug", "true",便是系統classpath屬性,也能夠代碼實現將屬性加載到系統屬性中,以下:
System.setProperty("log4j.debug", "true");
配置文件位置:在項目的src目錄下,在獲取logger時會自動尋找到,自定義配置文件位置,在獲取logger以前使用以下任意一個方法:
PropertyConfigurator.configure(java.net.URL configURL);
PropertyConfigurator.configure(InputStream inputStream);
PropertyConfigurator.configure(Properties properties);
PropertyConfigurator.configure(String configFilename);
示例:
log4j.rootLogger=error,javass.Console,javass.File
#設置根節點的輸出級別,輸出目的地
log4j.logger.testClient.TestLog4j=ERROR,CONSOLE
#設置子代的輸出級別,輸出目的地
log4j.appender.javass.Console=org.apache.log4j.ConsoleAppender
#目的地的處理類、Consloe 只是一個名稱
log4j.appender.javass.Console.layout=org.apache.log4j.PatternLayout
#輸出格式,選擇不一樣的layout有不一樣的屬性,參照前面的內容
log4j.appender.javass.Console.layout.ConversionPattern=%d{HH:mm:ss,SSS}%5p (%C{1}:%M) - %m%n
#輸出格式的定義
#下面是一樣的定義
log4j.appender.javass.File=org.apache.log4j.DailyRollingFileAppender
log4j.appender.javass.File.file=javass.log
log4j.appender.javass.File.DatePattern=.yyyy-MM-dd
log4j.appender.javass.File.layout=org.apache.log4j.PatternLayout
log4j.appender.javass.File.layout.ConversionPattern=%d{HH:mm:ss,SSS} %5p(%C{1}:%M) - %m%n
代碼示例解釋以下附件
配置文件位置:在項目的src目錄下,在獲取logger時會自動尋找到
自定義配置文件位置,在獲取logger以前使用以下任意一個方法
DOMConfigurator.configure(URL rul);
DOMConfigurator.configure(String filename);
校驗文件:org/apache/log4j/xml/log4j.dtd
在擁有xml和Properties時,會優先查找xml文件
xml declaration and dtd
|
log4j:configuration
|
+-- appender (name, class)
| |
| +-- param (name, value)
| +-- layout (class)
| |
| +-- param (name, value)
+-- logger (name, additivity)
| |
| +-- level (class, value)
| | |
| | +-- param (name, value)
| +-- appender-ref (ref)
+-- root
|
+-- param (name, class)
+-- level
| |
| +-- param (name, value)
+-- appender-ref (ref)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
<appender name="test" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%d{dd HH:mm:ss,SSS\} %-5p] [%t] %c{2\} - %m%n" />
</layout>
</appender>
<appender name="file" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="E:/test.log" />
<param name="DatePattern" value="'.'yyyy-MM-dd'.log'" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%d{MMdd HH:mm:ss SSS\} %-5p] [%t] %c{3\} - %m%n" />
</layout>
</appender>
<logger name="cn.javass" additivity="true"><!—true是添加appender-->
<level value="debug" /><!—設置子代looger的輸出級別-->
<appender-ref ref="file" /><!—設置子代logger的輸出目的地-->
</logger>
<root>
<level value="error"/>
<appender-ref ref="test" />
<appender-ref ref="file" />
</root>
</log4j:configuration>
在log4j.properties裏,控制級別的時候,只能打印出大於指定級別的全部信
息;可是在log4j.xml中能夠經過filter來完成過濾:典型的引用是隻打印出某
種級別的信息。
<appender name="log.file"
class="org.apache.log4j.DailyRollingFileAppender">
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="levelMin" value="info" />
<param name="levelMax" value="info" />
<param name="AcceptOnMatch" value="true" />
</filter>
</appender>
一個常常提出的爭議就是logging的運算開銷。這種關注是有道理的,由於即使是一箇中等大小的應用程序至少也會產生幾千個log輸出。許多工做都花費在測量和改進logging性能上。Log4j聲明它是快速和靈活的:速度第一,靈活性第二。
咱們在看一些成熟框架的源代碼中,常常看到以下代碼:
if (logger.isDebugEnabled()){
logger.debug(「debug:「+name);
}
爲何不是直接logger.debug(「debug:「+name);呢?在配置文件中雖然可使用控制級別爲比debug級別更高的級別,而不輸出debug信息;可是,這裏的字符串鏈接操做仍然會影響運行效率;若是先判斷當前logger的級別,若是級別不合適的話,連這句字符串鏈接均可以不作了。
NDC(Nested Diagnostic Context)和MDC(Mapped Diagnostic Context)是log4j種很是有用的兩個類,它們用於存儲應用程序的上下文信息(context infomation),從而便於在log中使用這些上下文信息。
NDC採用了一個相似棧的機制來push和pop上下文信息,每個線程都獨立地儲存上下文信息。好比說一個servlet就能夠針對每個request建立對應的NDC,儲存客戶端地址等等信息。
當使用的時候,咱們要儘量確保在進入一個context的時候,把相關的信息使用NDC.push(message);在離開這個context的時候使用NDC.pop()將信息刪除。另外因爲設計上的一些問題,還須要保證在當前thread結束的時候使用NDC.remove()清除內存,不然會產生內存泄漏的問題。
存儲了上下文信息以後,咱們就能夠在log的時候將信息輸出。在相應的PatternLayout中使用」%x」來輸出存儲的上下文信息,下面是一個PatternLayout的例子:
%r [%t] %-5p %c{2} %x - %m%n
使用NDC最重要的好處就是,當咱們想輸出一些上下文的信息的時候,不須要讓logger去尋找這些信息,而只須要在適當的位置進行存儲,而後再配置文件中修改PatternLayout。在最新的log4j 1.3版本中增長了一個org.apache.log4j.filters.NDCMatchFilter,用來
根據NDC中存儲的信息接受或拒絕一條log信息。
MDC和NDC很是類似,所不一樣的是MDC內部使用了相似map的機制來存儲信息,上下文信息也是每一個線程獨立地儲存,所不一樣的是信息都是以它們的key值存儲在」map」中。相對應的方法,MDC.put(key, value); MDC.remove(key); MDC.get(key); 在配置PatternLayout的時候使用:%x{key}來輸出對應的value。一樣地,MDC也有一個org.apache.log4j.filters.MDCMatchFilter。這裏須要注意的一點,MDC是線程獨立的,可是一個子線程會自動得到一個父線程MDC的copy。
至於選擇NDC仍是MDC要看須要存儲的上下文信息是堆棧式的仍是key/value形式的。
之前一直使用文本工具查看日誌文件,如今才發現那是多麼浪費時間的一件事情,將日誌輸出目的地設置爲:
log4j.appender.LF5_APPENDER=org.apache.log4j.lf5.LF5Appender
log4j.appender.LF5_APPENDER.MaxNumberOfRecords=2000
將日誌輸出到日誌工具LogFactort5