<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    上善若水
    In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation. To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra
    posts - 146,comments - 147,trackbacks - 0

    自從七月份去走川藏后,已經好幾個月沒有更新博客了。其實八月底從拉薩回來后一直在Spring的代碼,也想寫幾篇關于Spring源碼的解讀,可惜Spring實在是太復雜了,花了我一個多月的時間,框架大體流程是有頭緒了,但是具體實現和各個模塊的具體細節還都不是很清楚,遲遲不敢動筆。其實原本我不想回到Logging這一塊,我知道光看完Log4J的代碼還不夠,也感覺Log在系統中其實占據了蠻重要的位置(雖然很多人都沒有意識到),不過一般Log框架使用簡單,遇到問題也比較少,即使看完源碼對實際工作也幫助不大。可惜我前段時間答應要在公司講一個關于Logging相關的Session了,木有辦法,只能重拾Logging的內容。也趁這個機會把JavaLogging相關框架做一個全面的了解,包括Commons LoggingSLF4JJDK LoggingLogBack。首先從Commons LoggingSLF4J的比較開始。

    先來隨便扯點吧,貌似所有這些流行的Logging框架都和Log4J多少有點關系(不太確定Commons Logging有多大關系,不過至少也都是Apache下的項目吧)。JDK Logging據說當初是想用Log4J的,但是當時兩家好像談判談崩了,然后JDK自己實現了一個,貌似結構和Log4J差不多,只是實現的比較爛,基本上也只能在做測試的時候用,而SLF4JLogBack都是出自Log4J的創始人Ceki Gülcü之手。這家伙也算是閑的蛋疼,光整Logging這些框架貌似就花了不少時間吧。

    言歸正傳,在Logging系統中,目前框架都是基于相同的設計,即從一個LogFactory中取得一個命名的LogLogger)實例,然后使用這個LogLogger)實例打印debuginfowarnerror等不同級別的日志。作為兩個門面日志系統,Commons LoggingSLF4J也同樣采用這樣的設計。所謂門面日志系統,是指它們本身并不實現具體的日志打印邏輯,它們只是作為一個代理系統,接收應用程序的日志打印請求,然后根據當前環境和配置,選取一個具體的日志實現系統,將真正的打印邏輯交給具體的日志實現系統,從而實現應用程序日志系統的“可插拔”,即可以通過配置或更換jar包來方便的更換底層日志實現系統,而不需要改變任何代碼。個人感覺SLF4J的實現更加靈活,并且它還提供了MakerMDC的接口。這個將在接下的小節中具體介紹。

    Commons Logging的設計比較簡單,它定義了一個Log接口,所有它支持的日志系統都有相應的Log實現類,如Log4JLoggerJdk14LoggerJdk13LumberjackLoggerSimpleLogNoOpLogAvalonLoggerLogKitLogger等類,在LogFactory中定義了一定的規則,從而根據當前的環境和配置取得特定的Log子類實例。

    Commons Logging中默認實現的LogFactoryLogFactoryImpl類)查找具體Log實現類的邏輯如下:

    1.    查找在commons-logging.properties文件中是否定存在以org.apache.commons.logging.Logorg.apache.commons.logging.log(舊版本,不建議使用)key定義的Log實現類,如果是,則使用該類。

    2.    否則,查找在系統屬性中(-D方式啟動參數)是否存在以org.apache.commons.logging.Logorg.apache.commons.logging.log(舊版本,不建議使用)key定義的Log實現類,如果是,則使用該類。

    3.    否則,如果在classpath中存在Log4Jjar包,則使用Log4JLogger類。

    4.    否則,如果當前使用的JDK版本或等于1.4,則使用Jdk14Logger類。

    5.    否則,如果存在Lumberjack版本的Logging系統,則使用Jdk13LumberjackLogger類。

    6.    否則,如果可以正常初始化Commons Logging自身實現的SimpleLog實例,則使用該類

    7.    最后,以上步驟都失敗,則拋出LogConfigurationException

    其實,Commons Logging還支持用戶自定義的LogFactory實現類。對LogFactory類的查找邏輯為:

    1.    查看系統屬性中是否存在以org.apache.commons.logging.LogFactorykeyLogFactory實現類,若有,則使用該類實例化一個LogFactory實例。

    2.    否則,嘗試使用service provider的方式查找LogFactory實現類,即查看classpathjar包中是否存在META-INF/services/org.apache.commons.logging.LogFactory文件,如果存在,則使用該文件內定義的LogFactory類實例化一個LogFactory實例。

    3.    否則,查找commons-logging.properties文件是否存在,并且其中存在以org.apache.commons.logging.LogFactorykeyLogFactory實現類,若有,則使用該類實例化一個LogFactory實例。

    4.    否則,使用默認的LogFactoryImpl實現類實例化一個LogFactory實例。

    Commons Logging的類設計圖如下:


    在使用Commons Logging時,經常在服務器部署中會遇到ClassLoader的問題,這也是經常被很多人所詬病的地方,特別是在和Log4J一起使用的時候。常見的如,由于Common Logging使用非常廣泛,因而很多Web容器(WebSphere)在內也會使用它作為日志處理系統而將其jar包引入到容器本身中,此時LogFactory是使用Web容器本身的ClassLoader裝載的,即使Log4J中使用了ContextClassLoader來查找配置文件,此時的Thread依然在容器中,因而它使用的ClassLoader還是容器本身的ClassLoader實例,此時需要把Log4J的配置文件放到共享目錄下,該配置文件才能被正常識別(以我的理解,容器在啟動的時候,它根本無法獲得Web應用程序中的jar包,所以也需要將Log4Jjar包放到共享目錄中才可以,不過我木有用過WebSphere,也沒法測試,所以只能猜測~)。在WebSphere還可以通過設置類的加載順序為PARENT_LAST的方法來解決。而在Jboss中則只能將自己的配置加到其conf下的Log4J配置文件中,因為Jboss默認導入Log4J包。具體可以參考我轉載的一篇文章,Log4j/common log和各種服務器集成的問題(木有經驗,只能用別人的文章了。。。還很可惜的木有機會測試。。。):http://www.tkk7.com/DLevin/archive/2012/11/02/390639.html,另外還找到一篇更加詳細的描述Commons Logging中存在的ClassLoader問題的文章:http://articles.qos.ch/classloader.html

     

    Commons Logging的具體實現:

    在使用Commons Logging時,一般是通過LogFactory獲取Log實例,然后調用Log接口中相應的方法。因而Commons Logging的實現可以分成以下幾個步驟:

    1.    LogFactory類初始化

    a.    緩存加載LogFactoryClassLoaderthisClassLoader字段),出于性能考慮。因為getClassLoader()方法可能會使用AccessController(雖然目前并沒有使用),因而緩存起來以提升性能。

    b.    初始化診斷流。讀取系統屬性org.apache.commons.logging.diagnostics.dest,若該屬性的值為STDOUTSTDERR、文件名。則初始化診斷流字段(diagnosticStream),并初始化診斷消息的前綴(diagnosticPrefix),其格式為:”[LogFactory from <ClassLoaderName@HashCode>] “, 該前綴用于處理在同一個應用程序中可能會有多個ClassLoader加載LogFactory實例的問題。

    c.    如果配置了診斷流,則打印當前環境信息:java.ext.dirjava.class.pathClassLoader以及ClassLoader層級關系信息。

    d.    初始化factories實例(Hashtable),用于緩存LogFactorycontext-classloader –-> LogFactory instance)。如果系統屬性org.apache.commons.logging.LogFactory.HashtableImpl存在,則使用該屬性定義的Class作為factories Hashtable的實現類,否則,使用Common Logging實現的WeakHashtable。若初始化沒有成功,則使用Hashtable類本身。使用WeakHashtable是為了處理在webapp中,當webapp被卸載是引起的內存泄露問題,即當webapp被卸載時,其ClassLoader的引用還存在,該ClassLoader不會被回收而引起內存泄露。因而當不支持WeakHashtable時,需要卸載webapp時,調用LogFactory.relase()方法。

    e.    最后,如果需要打印診斷信息,則打印“BOOTSTRAP COMPLETED”信息

    2.    查找LogFactory類實現,并實例化。

    當調用LogFactory.getLog()方法時,它首先會創建LogFactory實例(getFactory()),然后創建相應的Log實例。getFactory()方法不支持線程同步,因而多個線程可能會創建多個相同的LogFactory實例,由于創建多個LogFactory實例對系統并沒有影響,因而可以不用實現同步機制。

    a.    獲取context-classloader實例。

    b.    factories Hashtable(緩存)中獲取LogFactory實例。

    c.    讀取commons-logging.properties配置文件(如果存在的話,如果存在多個,則可以定義priority屬性值,取所有commons-logging.properties文件中priority數值最大的文件),如果設置use_tccl屬性為false,則在類的加載過程中使用初始化cachethisClassLoader字段,而不用context ClassLoader

    d.    查找系統屬性中是否存在org.apache.commons.logging.LogFactory值,若有,則使用該值作為LogFactory的實現類,并實例化該LogFactory實例。

    e.    使用service provider方法查找LogFactory的實現類,并實例化。對應Service ID是:META-INF/services/org.apache.commons.logging.LogFactory

    f.     查找commons-logging.properties文件中是否定義了LogFactory的實現類:org.apache.commons.logging.LogFactory,是則用該類實例化一個出LogFactory

    g.    否則,使用默認的LogFactory實現:LogFactoryImpl類。

    h.    緩存新創建的LogFactory實例,并將commons-logging.properties配置文件中所有的鍵值對加到LogFactory的屬性集合中。

    3.    通過LogFactory實例查找Log實例(LogFactoryImpl實現)

    使用LogFactory實例調用getInstance()方法取得Log實例。

    a.    如果緩存(instances字段,Hashtable)存在,則使用緩存中的值。

    b.    查找用戶自定義的Log實例,即從先從commons-logging.properties配置文件中配置的org.apache.commons.logging.Logorg.apache.commons.logging.log,舊版本)類,若不存在,查找系統屬性中配置的org.apache.commons.logging.Logorg.apache.commons.logging.log,舊版本)類。如果找到,實例化Log實例

    c.    遍歷classesToDiscover數組,嘗試創建該數組中定義的Log實例,并緩存Log類的Constructor實例,在下次創建Log實例是就不需要重新計算。在創建Log實例時,如果use_tccl屬性設為false,則使用當前ClassLoader(加載當前LogFactory類的ClassLoader),否則盡量使用Context ClassLoader,一般來說Context ClassLoader和當前ClassLoader相同或者是當前ClassLoader的下層ClassLoader,然而在很多自定義ClassLoader系統中并沒有設置正確的Context ClassLoader導致當前ClassLoader成了Context ClassLoader的下層,LogFactoryImpl默認處理這種情況,即使用當前ClassLoader。用戶可以通過設置org.apache.commons.logging.Log.allowFlawedContext配置作為這個特性的開關。

    d.    如果Log類定義setLogFactory()方法,則調用該方法,將當前LogFactory實例傳入。

    e.    將新創建的Log實例存入緩存中。

    4.    調用Log實例中相應的方法

    Log接口比較簡單,并且Log4JJDK相應的實現類也都直接代理給各自框架,因而實現比較簡單,不在詳述。關于SimpleLog,可以類似的參考http://www.tkk7.com/DLevin/archive/2012/06/12/380647.html 不過還是有必要對Jdk14Logger的實現吐槽一下,每次調用log方法時都會創建新的Throwable實例,然后去計算ClassNameMethod,這會引起嚴重的性能問題,因為創建一個Throwable實例,意味著需要停止當前的運行,dump出一個調用棧快照。它為什么不像Jdk13LumberjackLogger的實現一樣,把這兩個字段緩存起來呢??

     

    posted on 2012-11-04 01:20 DLevin 閱讀(4507) 評論(0)  編輯  收藏 所屬分類: Logging
    主站蜘蛛池模板: 国产成人精品日本亚洲专一区| 国产精品无码一区二区三区免费 | 亚洲精品无码aⅴ中文字幕蜜桃| 亚洲色精品aⅴ一区区三区| 亚洲第一成年男人的天堂| 亚洲国产精品乱码一区二区| 久久久久亚洲AV成人网人人网站| 亚洲av无码一区二区乱子伦as| 激情内射亚洲一区二区三区爱妻| caoporm超免费公开视频| 国产精品亚洲综合网站| 69成人免费视频| 亚洲黄色三级视频| 和日本免费不卡在线v| 亚洲91av视频| 久久天天躁狠狠躁夜夜免费观看| 亚洲色欲色欲www在线丝| 一本大道一卡二大卡三卡免费| 国产精品1024在线永久免费 | 精品一卡2卡三卡4卡免费视频| 一个人看的www免费视频在线观看| 免费吃奶摸下激烈视频| 精品国产日韩亚洲一区91| 两个人的视频高清在线观看免费| 亚洲美女一区二区三区| 免费h片在线观看网址最新| 亚洲jjzzjjzz在线观看| 影音先锋在线免费观看| 亚洲VA中文字幕无码一二三区| 亚洲精品欧美综合四区 | 看全免费的一级毛片| 亚洲综合精品网站在线观看| 亚洲国产午夜精品理论片在线播放| 亚洲国产综合久久天堂| 中文字幕无线码中文字幕免费| 成人免费无码大片A毛片抽搐色欲| 亚洲爆乳少妇无码激情| 好看的亚洲黄色经典| 无码av免费一区二区三区试看| 亚洲色中文字幕在线播放| 中文字幕第一页亚洲|