摘要:在中對一個實例命名非常的簡單,在每一個類中可以有一個靜態的實例對象,可以用類的完全限定名作為實例的名字。但不管怎樣,用類的完全限定名作為實例的名字是一個非常好的方式。
本文是對log4j官網Introduction部分的翻譯,原文鏈接地址:http://logging.apache.org/log...。
Introduction:幾乎每個大型應用都包含自己的日志API。1996年,為了整個項目的一致性,E.U.?SEMPER項目團隊決定開發自己的日志API。經過無數次的改進,這款日志API成為了Java領域非常流行的日志package,這就是log4j。
在代碼中加日志進行調試是一種低級的方式。但因為調試工具并不總是可以使用,所以打日志有時候是唯一的調試方法。例如多線程應用和分布式應用。
有經驗表明在軟件開發周期中,日志組件占有重要的地位。加入日志有許多好處。可以通過日志精確了解應用運行的狀態。加入代碼中的日志,無需人們手工干預就可自動生成輸出結果。日志的輸出結果可以保存在永久存儲介質上,方便今后對其進行查看。此外,在軟件開發周期中,充足豐富的日志也可以當做審計材料使用。
日志也有缺點,它會使應用程序變慢。如果日志過多,還會導致屏幕閃動。為了緩和這些缺點,log4j被設計成可靠的、快速的和可擴展的。由于日志很少是一個應用中關注的重點,所以log4j的API盡可能設計的簡單易懂。
log4j主要有三大組件——loggers、appenders和layouts。這三大組建共同協作,使開發者可以根據不同的日志級別和日志類型輸出信息,并且可以指定信息輸出的格式和信息輸出的目的地。
Logger hierarchy相比于使用簡單的System.out.println語句,日志API最大的優勢就是它可以禁止某一類型的日志輸出,同時又不影響其它類型的日志輸出。要實現這種能力,需要開發人員根據某種條件,將日志劃分為不同的類型。老版本的log4j將Category類作為核心就是由于上面這個原因。但log4j到1.2版本時,已經使用Logger類代替了原來的Category類。對于那些熟悉老版本log4j的人,可以簡單把Logger類當做Category類的一個別名。
Loggers是被命名的實體,Logger的名稱是大小寫敏感的,并且遵循層次命名規則。
舉個例子,命名為“com.foo”的logger是命名為“com.foo.Bar”的logger的父親。命名為“java”的logger是命名為“java.util”的logger的父親,是命名為“java.util.Vector”的祖先。這種命名規則應該被許多研發人員所熟悉。
root logger位于整個logger繼承體系的最頂端,相比于普通logger它有兩個特別之處:
root logger總是存在。
root logger不能通過名稱獲取。
可以通過調用Logger類的靜態方法getRootLogger獲取root logger對象。其它普通logger的實例可以通過Logger類的另一個靜態方法getLogger獲取。getLogger方法接受一個參數作為logger的名字。
Logger類中的其它一些基本方法如下所示:
package org.apache.log4j; public class Logger { // Creation & retrieval methods: public static Logger getRootLogger(); public static Logger getLogger(String name); // printing methods: public void trace(Object message); public void debug(Object message); public void info(Object message); public void warn(Object message); public void error(Object message); public void fatal(Object message); // generic printing method: public void log(Level l, Object message); }
Loggers可以被分配日志級別,可以分配的級別如下:
TRACE
DEBUG
INFO
WARN
ERROR和
FATAL
這些級別被定義在org.apache.log4j.Level類中。雖然你也可以通繼承Level類定義你自己的專有級別,但是我們不鼓勵你這樣做。
如果一個logger沒有指定任何level,那么這個logger會從它的父親那里繼承level。
為了保證所有的logger可以最終被指定一個level,root logger總是被分配一個level。
下面四個表是上面規則的例子:
Example 1
在第一個例子中,只有root logger被分配了一個level值Proot,Proot會被其它所有的logger——x、x.y、x.y.z繼承。
Example 2
在第二個例子中,所有的logger都被分配了一個level值,就不需要繼承level了。
Example 3
在第三個例子中,root、x和x.y.z三個logger分別被分配了Proot、Px和Pxyz三個level值,x.y這個logger從它的父親那里繼承level值。
Example 4
在第四個例子中,root和x兩個logger分別被分配了Proot和Px這兩個level值。x.y和x.y.z兩個logger則從離自己最近的祖先x繼承level值。
可以調用logger實例的printing方法輸入日志。printing方法包括debug、info、warn、error、fatal和log。
按照定義,printing方法決定了日志請求的等級。例如,c是一個logger實例,c.info("..")語句請求輸出INFO級別的日志。
只有日志請求級別大于等于日志級別的時候,日志請求才會被準許輸出信息。否則,日志請求會被禁止。這條規則是log4j的核心。日志的level是有序的。對于標準的日志級別:DEBUG
// get a logger instance named "com.foo" Logger logger = Logger.getLogger("com.foo"); // Now set its level. Normally you do not need to set the // level of a logger programmatically. This is usually done // in configuration files. logger.setLevel(Level.INFO); Logger barlogger = Logger.getLogger("com.foo.Bar"); // This request is enabled, because WARN >= INFO. logger.warn("Low fuel level."); // This request is disabled, because DEBUG < INFO. logger.debug("Starting search for nearest gas station."); // The logger instance barlogger, named "com.foo.Bar", // will inherit its level from the logger named // "com.foo" Thus, the following request is enabled // because INFO >= INFO. barlogger.info("Located nearest gas station."); // This request is disabled, because DEBUG < INFO. barlogger.debug("Exiting gas station search");
用相同的名字參數調用getLogger方法總是會返回同一個logger對象的引用,例如在下面兩行代碼中:
Logger x = Logger.getLogger("wombat"); Logger y = Logger.getLogger("wombat");
x和y引用的是同一個logger對象。
這樣在配置好一個logger實例之后,可以很方便的在代碼的其它地方獲取到這個logger實例,而無需傳遞logger實例的引用。
與生物學中的父子關系不同,log4j中的父親不一定總是早于它的孩子出生。log4j中的logger實例可以以任意的順序被構造或配置。一個parent logger即使在它的后代之后被實例化,它們也依然可以建立起父子關系。
log4j的配置通常會在應用初始化時被完成。最常用的方式是通過讀取配置文件完成配置。稍后會討論這個過程。
在log4j中對一個logger實例命名非常的簡單,在每一個類中可以有一個靜態的logger實例對象,可以用類的完全限定名作為logger實例的名字。這對于定義一個logger非常有用,由于在輸出日志的時候可以帶有logger實例的名字,所以這種用類的完全限定名作為logger實例的名字可以很容易看出日志發生的位置。當然這只是一種通用做法,log4j對此并沒有限制,開發人員可以隨意指定logger實例的名字。但不管怎樣,用類的完全限定名作為logger實例的名字是一個非常好的方式。
禁止和允許日志輸出的能力只是全部功能的一部分。log4j允許將日志輸出到多個目的地。在log4j的術語中,日志輸出目的地被稱為appender。目前,存在的appender包括命令行、文件、GUI組件、遠程socket服務器、JMS、NT事件日志和遠程UNIX Syslog后臺進程。并且可以支持異步的方式記錄日志。
一個logger實例可以同時掛載多個appender。
addAppender方法向logger實例添加一個appender。對于每一個被允許的日志輸出請求,logger實例不僅將該請求轉發到自己所有的appender上,而且還將日志輸出請求轉發到它祖先上的所有appender上。例如,如果一個命令行appender被添加到root logger上,那么所有被允許的日志請求至少會輸出到命令行中。如果在這個基礎上再向C logger中添加一個文件appender,那么對于C和它的后代,被允許的日志會同時輸出到命令行和文件。通過將additivity flag設置為false,可以覆蓋這種默認行為。
Appender Additivity:對C logger的日志輸出請求會轉發到C自己和它祖先們的全部appender。這種行為用術語“appender additivity”表示。如果 P是C的祖先,P將additivity flag設置為false。那么C的日志會輸出到它自己的appender和C到P之間(包括P)每個logger的appender,而不會輸出到P以上祖先的appender。對于每個logger,它的additivity flag默認是設置為true的。
下面的表格是這樣的一個例子:
通常,研發不僅希望指定日志輸出的目的地,而且希望能夠指定日志輸出的格式。可以在appender上關聯一個layout用于指定日志輸出格式。layout會按照用戶的意愿輸出一定格式的日志信息。
PatternLayout可以讓用戶像使用C語言中的printf那樣使用格式化表達式定制日志輸出的格式。
例如,使用PatternLayout的表達式"%r [%t] %-5p %c - %m%n”可以包含下面日志信息:
176 [main] INFO org.foo.Bar - Located nearest gas station.
第一個字段是程序啟動到現在經過的毫秒數,第二個字段是輸出日志的線程,第三個字段是日志的級別,第四個字段是logger的名字。’-’后面的文本是日志輸出信息。%n是換行。
log4j會按照用戶指定的具體條件輸出日志內容。例如,如果你頻繁的需要輸出Orange類對象的日志,那么一可以注冊一個OrangeRenderer,每當輸出orange的日志時,它都會被調用。
Object rendering follows the class hierarchy. For example, assuming oranges are fruits, if you register a?FruitRenderer, all fruits including oranges will be rendered by the?FruitRenderer, unless of course you registered an orange specific?OrangeRenderer.
Object renderers have to implement the?ObjectRenderer?interface.
在現有應用中加入日志需要大量的工作。調研表明,大約有接近4%的代碼跟日志有關。因此,即使不是那么大的應用也會有成千上萬行的日志代碼。Given their number, it becomes imperative to manage these log statements without the need to modify them manually.
og4j環境是完全可以通過寫程序進行配置的。然而,使用配置文件對log4j進行配置會更加的靈活。目前,配置文件可以采用XML和properties兩種格式的文件。
import com.foo.Bar; // Import log4j classes. import org.apache.log4j.Logger; import org.apache.log4j.BasicConfigurator; public class MyApp { // Define a static logger variable so that it references the // Logger instance named "MyApp". static Logger logger = Logger.getLogger(MyApp.class); public static void main(String[] args) { // Set up a simple configuration that logs on the console. BasicConfigurator.configure(); logger.info("Entering application."); Bar bar = new Bar(); bar.doIt(); logger.info("Exiting application."); } }
MyApp在導入了log4j相關的類,然后用MyApp的全限定類名定義了一個靜態的logger實例變量。
MyApp中使用的Bar類:
package com.foo; import org.apache.log4j.Logger; public class Bar { static Logger logger = Logger.getLogger(Bar.class); public void doIt() { logger.debug("Did it again!"); } }
調用BasicConfigurator.configure方法創建了一個非常簡單的log4j配置。它以硬編碼的方式向root logger中添加了一個ConsoleAppender,日志輸出會使用PatternLayout的模板"%-4r [%t] %-5p %c %x - %m%n”進行格式化。注意默認情況下,root logger被分配的日志級別是Level.DEBUG。
上面程序輸入的日志為:
0 [main] INFO MyApp - Entering application. 36 [main] DEBUG com.foo.Bar - Did it again! 51 [main] INFO MyApp - Exiting application.
下面的圖形是MyApp在調用完BasicConfigurator.configure方法之后的對象圖:
前面的這種方式只能一直輸出同一種配置模式的日志信息,可以很容易的在MyApp啟動時修改日志配置信息,使其輸出不同配置模式的日志。
import com.foo.Bar; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; public class MyApp { static Logger logger = Logger.getLogger(MyApp.class.getName()); public static void main(String[] args) { // BasicConfigurator replaced with PropertyConfigurator. PropertyConfigurator.configure(args[0]); logger.info("Entering application."); Bar bar = new Bar(); bar.doIt(); logger.info("Exiting application."); } }
這個版本的MyApp使用PropertyConfigurator解析文件并對日志進行配置。
下面這個配置文件產生的配置結果與之前使用BasicConfigurator產生的結果完全相同。
# 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
如果我們不再對com.foo包中任何組件輸出的日志感興趣,下面的配置文件可以實現這一點:
log4j.rootLogger=DEBUG, A1 log4j.appender.A1=org.apache.log4j.ConsoleAppender log4j.appender.A1.layout=org.apache.log4j.PatternLayout # Print the date in ISO 8601 format log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p %c - %m%n # Print only messages of level WARN or above in the package com.foo. log4j.logger.com.foo=WARN
現在MyApp的日志輸出如下:
2000-09-07 14:07:41,508 [main] INFO MyApp - Entering application. 2000-09-07 14:07:41,529 [main] INFO MyApp - Exiting application.
由于logger com.foo.Bar沒有被指定任何的日志級別,所以它會從com.foo上繼承,在配置文件中指定com.foo的日志級別是WARN,而代碼Bar.doIt中的log語句日志請求輸出的是DEBUG級別,要比WARN級別低,所以doIt方法中的日志請求不會被響應。
下面是另一個配置文件,它使用了多個appenders:
log4j.rootLogger=debug, stdout, R log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout # Pattern to output the caller"s file name and line number. log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n log4j.appender.R=org.apache.log4j.RollingFileAppender log4j.appender.R.File=example.log log4j.appender.R.MaxFileSize=100KB # Keep one backup file log4j.appender.R.MaxBackupIndex=1 log4j.appender.R.layout=org.apache.log4j.PatternLayout log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
使用這個配置文件會向命令行輸出如下日志信息:
INFO [main] (MyApp2.java:12) - Entering application. DEBUG [main] (Bar.java:8) - Doing it again! INFO [main] (MyApp2.java:15) - Exiting application.
此外,root logger被分配的第二個appender,是將日志信息直接輸出到example.log文件中的。當example.log文件到達100KB時,會發生roll-over。這時老版本的example.log會自動移動到example.log.1。
注意到要改變日志行為無需重新編譯代碼。我們可以改變配置文件讓其日志輸出到UNIX Syslog daemon、將所有的com.foo輸出的日志都重定向到NT Event logger、或者將日志事件轉發到遠程的log4j服務器。
log4j庫沒有對它的運行環境做過任何的假設。也就是說,log4j沒有任何默認的appender。然而在一些環境下,日志類的靜態初始化器會自動嘗試配置log4j。Java從語言層面保證在類加載的時候,靜態初始化器會被調用一次且僅被調用一次。但是要額外留意不同的classloader可能會對同一個類加載多個副本。
The default initialization is very useful in environments where the exact entry point to the application depends on the runtime environment. For example, the same application can be used as a stand-alone application, as an applet, or as a servlet under the control of a web-server.
The exact default initialization algorithm is defined as follows:
設置log4j.defaultInitOverride系統屬性為非false值會導致log4j略過默認的初始化過程
設置log4j.configuration系統屬性的字符串值。通過設置log4j.configuration系統屬性的值指定默認初始化文件是最常用的方式。如果系統屬性log4j.configuration沒有被明確定義,則為它分配默認值為log4j.properties。log4j.configuration的值記為resource變量。
嘗試將resource變量轉換為url。
如果resource變量無法轉化為url,比如在轉化url時發生MalformedURLException異常,那么調用org.apache.log4j.helpers.Loader.getResource(resource, Logger.class)在classpath中尋找resource并返回一個url。
如果找不到url,終止默認初始化,否則使用url對log4j進行配置。
如果url不是以“.xml”為后綴,那么將會使用PropertyConfigurator解析url并對log4j進行配置。如果url的后綴是“.xml”,那么將會使用DOMConfigurator完成上述工作。你可以隨意指定一個定制的配置器(configurator)。log4j.configuratorClass系統屬性的值就是你定制配置器的全限定類名。你自己定制的配置器必須實現Configurator這個接口。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67570.html
摘要:本文要來分享給大家程序員最常用的日志框架組件。沒有基礎的同學也不要著急,這套教程覆蓋了目前所有的日志框架,只要你學,就一定用得到,先收藏,以備不時之需。 作為一名Java程序員,我們開發了很多Java應用程序,包括桌面應用、WEB應用以及移動應用。然而日志系統是一個成熟Java應用所必不可少的。在開發和調試階段,日志可以幫...
摘要:項目介紹在之前的整合項目之后,新增日志簡單集成,之前的代碼不予展示與介紹,想了解的請參考整合項目項目代碼獲取項目結構代碼控制層,,主要包含登錄及幾個頁面跳轉會跳到我們自定義的中登錄用戶名或密碼錯誤業務處理層,包含一個包,以接口類型存在 spring-springmvc-mybatis-shiro項目介紹 在之前的mybatis整合項目之后,新增日志、簡單集成shiro,之前的代碼不予展...
摘要:作為一個實用主義者,我喜歡在理解基本原理后快速的搭建系統,當系統運行起的時候有那種愉悅和興奮。,著手搭建,我用的是進行的。要使用日志系統,就需要進行相關配置,這個不用我多說了叁。 作為一個實用主義者,我喜歡在理解基本原理后快速的搭建系統,當系統運行起的時候有那種愉悅和興奮。最近在完善公司框架,從最基本的日志系統開始。 java日志系統比較流行的是log4j,slf4j和logbac...
摘要:但是考慮到各不相同,所以出現了等日志框架。日志框架只是統一的,其底層的具體的日志記錄工作還是由等承擔。如何選擇和搭配日志系統目前來說,新應用使用是首選,一些老系統中很可能使用的是等。所以若日志沖突時,使用的三方庫只需要相應的實現庫即可。 日志系統的發展 我們日常接觸到的日志系統有很多種,log4j,JUL(jdk自帶),logback等,我們可以直接根據對象的日志API進行使用。但是考...
摘要:今天在配置日志的時候,發現日志重復打印的問題。把配置文件修改成如下日志控制臺日志級別日志級別日志級別日志通過以上配置模板即可解決各級別日志重復打印的問題。 今天在配置Log4j日志的時候,發現日志重復打印的問題。網上查了很多資料,發現介紹Log4j配置的文章數量不少,但提到這個問題的文章卻寥寥,解決了自己的問題以后,趕緊記錄一下。 原文地址:http://www.jianshu.com...
閱讀 2139·2021-10-14 09:43
閱讀 2201·2019-08-30 15:55
閱讀 730·2019-08-30 14:23
閱讀 2025·2019-08-30 13:21
閱讀 1240·2019-08-30 12:50
閱讀 2204·2019-08-29 18:46
閱讀 2285·2019-08-29 17:28
閱讀 2367·2019-08-29 17:21