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

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

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

    隨筆-7  評論-23  文章-0  trackbacks-0
    最近需要用到log4j動態定制Logger的場景,然后加上以前對于這個日志工具拿來就用而不知其原理的原因,所以決定花點時間看下它的源碼,如果你還對log4j如何使用感到困惑,那么請首先簡要瀏覽下它的官網http://logging.apache.org/log4j/

    Log4j總體來說是一個可定制,支持同時多種形式輸出日志,并且高度結構化的日志庫??啥ㄖ?,也就是既可以通過log4j.properties或者log4j.xml定義日志輸出的級別(Level),形式(Appender)以及文本格式(Layout),也可以通過Logger類或者LogManager類取得Logger實例,并且設置日志輸出級別(Level),形式(Appender)以及文本格式(Layout),可以說是相當的簡便與靈活。下面一段代碼簡單地說明了后者的實現。
    Logger MY_LOG = Logger.getLogger("MY_LOG");

    //文件形式的輸出方式實例化
    DailyRollingFileAppender appender = new DailyRollingFileAppender();
    appender.setName(name);
    appender.setAppend(
    true);
    appender.setEncoding(
    "GBK");
    //文本的輸出格式采用PatternLayout
    appender.setLayout(new PatternLayout(pattern));
    appender.setFile(
    new File(getLogPath(), fileName).getAbsolutePath());
    appender.activateOptions();

    //將appender加入到MY_LOG的appender集合
    MY_LOG.addAppender(appender);
    //設定日志輸出級別為INFO
    MY_LOG.setLevel(Level.INFO);

    Log4j初始化的代碼是在LogManager的靜態塊里面,這個靜態塊無論如何都會實例化一個日志級別為DEBUGRootLogger,并且初始化一個以這個RootLogger為根節點的級聯結構,然后檢查有沒有用戶指定重寫這個日志系統的初始化工作,如果沒有,那么先去找log4j.xml,如果沒有找到,那么再去找log4j.properties(也就是log4j.xml的優先級高于log4j.properties), 只有找到這兩個配置文件的其中一個,再初始化文件里面內容,主要是一些配置文件中指定的Logger初始化,以及各個LoggerAppender列表設定,各個Appender的具體實例,Layout等。其實,沒找到任何log4j配置文件也沒什么關系,因為已經有RootLogger,日志系統骨架已經完成了,所以也可以通過如前面的代碼添加Logger

    //初始化以RootLogger為根的級聯結構,rootLogger默認DEBUG級別
    Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG));
    //有沒指定log4j.configuration(內部使用,不知出于什么目的,未探究)
    if(configurationOptionStr == null{
        
    //加載classpath下log4j.xml
         url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);
       
    if(url == null{
            
    //如果log4j.xml沒有,加載log4j.properties
            url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);
        }

    }
     else {
        
    try {
        url 
    = new URL(configurationOptionStr);
        }
     catch (MalformedURLException ex) {
        url 
    = Loader.getResource(configurationOptionStr); 
        }

    }
    if(url != null{
        LogLog.debug(
    "Using URL ["+url+"] for automatic log4j configuration."); 
        
    //開始真正解析配置文件     
        OptionConverter.selectAndConfigure(url, configuratorClassName,LogManager.getLoggerRepository());
    }
     else {
        LogLog.debug(
    "Could not find resource:["+configurationOptionStr+"].");
    }

    簡要介紹完Log4J的初始化后,我們有必要來看下從Logger myLogger =LogManager. getLogger(“MY_ LOG”), myLogger.debug(“some message”)結束之后,Log4j到底為我們做了些什么。


     
    上面這幅序列圖主要描述的過程就是從LoggerManager取得一個Logger的過程,其中最重要的操作在Hierarchy類中,這個類說白了就是存儲Logger的倉庫,其內部使用一個HashMap ht來存儲LoggerProvisionNode。

    這里需要解釋下ProvisionNode,這個類是一個Vector的實現,之前我們談到過初始化的一開始,以RootLogger為根節點的級聯結構(就是Hierarchy實例),那么這個ProvisionNode想當于這個級聯結構中的樹節點,比如我在定義了一個名字為a.b.cLogger,那么總共會生成”a”,”a.b”兩個ProvisionNode,以及一個名字為”a.b.c”Logger。Hierarchy并沒有一個鏈表來維護他們之間的順序,ProvisionNode會通過其本身就是向量的特性將屬于它的Logger進行有序的排列,而Logger本身則通過parent屬性記住他們的日志屬性可以從哪里繼承。

    這樣做的好處有兩點,第一點就是只要名字相同,從LogManager中取出來的Logger就是同一個實例,第二點好處就是級聯結構,可以定義出差異化的Logger,特別是其以a.b.c.d類似包結構的拆分日志節點,使得包級別的日志差異化輸出更加的容易,并且這種特性提供了日志節點屬性繼承的功能。

    舉個例子,我們一般取得一個Logger實例是這樣的, Logger log=LogManager.getLogger(Class class), Log4j處理方式為getLogger(class.getName()),這也就是說,這個Logger的名字是帶包名的完全限定名字,所以如果我們通過名為a.b.c.aclass,a.b.c.d.bclass這么兩個類取得Logger實例,然后定義名為a.b.c和a.b.c.d 2個Logger,那么總共會生成如下的節點 
               
                ProvisionNode: a,a.b
                        Logger: a.b.c(加入a,a.b 2個ProvisionNode中并且parent為RootLogger)
                                 a.b.c.d(加入a,a.b 2個ProvisionNode中,并且parent為a.b.c)
                                 a.b.c.aclass  (加入到a,a.b 2ProvisionNode中,并且parent為a.b.c), 
                                 a.b.c.d.bclass(加入到a,a.b 2ProvisionNode中,并且parent為a.b.c.d)

    其中ProvisionNode可能升級為Logger,當同名的Logger加入時,該ProvisionNode中所有以該ProvisionNode為父節點的子節點修改parent指向新的Logger.

    final private void updateChildren(ProvisionNode pn, Logger logger) {
        
    final int last = pn.size();
       
        
    for(int i = 0; i < last; i++{
             Logger l 
    = (Logger) pn.elementAt(i);
              
    //有可能子節點的父節點指向更低一級的節點,比如孫節點。這個應該不難理解。
             
     if(!l.parent.name.startsWith(logger.name)) {
                 logger.parent 
    = l.parent;
                 l.parent 
    = logger;
            }

         }

    }

    這樣Logger a.b.c除了自身的日志輸出設置之外,還享受rootLogger的輸出(輸出級別,Appenders),Logger a.b.c.d同時享受rootLogger和a.b.c的設置,a.b.c.d.bclass享受rootLogger,a.b.c,a.b.c.d的日志輸出設定,也就是可以分別定義一批Logger的輸出級別和輸出形式以及其他屬性,當然也有控制開關控制這種繼承。下圖說明了一些問題。



    取得Logger之后,隨后就是需要按指定形式輸出日志內容,首先需要在LoggerRepository中判定當前Level是否可以做日志輸出,包括與全局的thresholdInt進行判定(相當于總開關,這個級別通不過直接返回,不做任何事情),通過后,再與其自身Logger日志級別比較,如果沒有設定,查其父節點的日志級別,仍然沒有設定,那么查父節點的父節點,直到查到有日志級別設定或者最終到RootLogger(其默認為Debug級別),比較通過后,就可以調用callDependers進行日志輸出了。

    日志級別為 OFF>FATAL>ERROR>WARN>INFO>DEBUG>ALL,只有輸出級別大于等于Logger自身級別才能進行輸出,比如 logger.debug,那么只有該Logger的級別(也可能是其先輩節點的日志級別)DEBUG或者ALL才能允許被輸出。全局的thresholdInt如果不設定是保持在ALL級別。

    一般Logger會通過AppenderAttachableImpl的實例來維護多個Appender,并且可以共享父節點的Appender List(包括RootLogger里面定義的appenders,所以我們一般在log4j.xml或者log4j.properties里面定義一個某個包下的Logger,然后掛有幾個Appender,而程序中通過完全限定的類名(這個類屬于前面指定的包)取得Logger,那么當這些Logger輸出日志的時候,其本身并沒有任何Appender,但是卻通過先輩節點定義的Appenders得以輸出。這里需要注意的是,如果在直系節點間定義相同的appender,似乎會多次重復輸出,因為其會遍歷自身以及所有先輩節點的Appender list并且逐一進行doAppend調用,而不會去排重,并且addAppender的時候也沒有遍歷先輩節點排重。


    Appender持有一個Filter鏈表,doAppend的時候首先走一遍過濾器,結果有3種,Filter.DENY(直接拒絕返回),Filter.ACCEPT(接收輸出請求,并且不再走之后的Filter),Filter.NEUTRAL(繼續執行下一個Filter),順利通過Filter鏈之后,進入真正的輸出日志過程,這邊以WriteAppender為例,首先將message通過持有的Layout進行格式化(format),然后調用輸出流輸出日志到目標文件(FileAppender)或者屏幕(ConsoleAppender)。

    至此,整個日志輸出過程結束。

    下面兩幅圖分別是Log4j的整體類圖,不是非常完整,但是大概能夠了解到整個結構。



    總結下,
    log4J出來已經很多年了,以前只是使用下,并沒有去探究里面機制,但其某些機制還是相當不錯的。文中可能出現一些錯誤,請各位能夠指出。

    posted on 2010-10-22 10:40 BucketLI 閱讀(4304) 評論(1)  編輯  收藏

    評論:
    # re: Log4j代碼隨讀 2012-05-15 11:28 | mabusyao
    最后一張圖,Appender并沒有在Hierarchy中被使用到。倒是AppenderAttachableImpl和Appender之間應當有關聯關系的。

    問樓主個問題,Hierarchy里面的RendererMap是干什么用的?  回復  更多評論
      

    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    主站蜘蛛池模板: 麻豆精品不卡国产免费看| 噼里啪啦电影在线观看免费高清 | 免费a级毛片高清视频不卡| 亚洲欧洲日韩国产一区二区三区| 国产成人涩涩涩视频在线观看免费| 一个人看的www视频免费在线观看| 亚洲国产精品久久久久| 免费鲁丝片一级在线观看| 羞羞视频在线观看免费| 亚洲福利电影一区二区?| 国产乱弄免费视频| 中文字幕在线观看免费视频| 亚洲av色香蕉一区二区三区蜜桃| 亚洲国产精品无码久久久不卡| 久久久www成人免费毛片| 两个人日本免费完整版在线观看1| 亚洲人成电影网站久久| 狠狠色伊人亚洲综合成人| 天天摸天天碰成人免费视频| 永久免费AV无码网站国产| 亚洲乱色伦图片区小说| 久久久亚洲欧洲日产国码二区| 亚洲第一区在线观看| 午夜福利不卡片在线播放免费| 中文字幕av无码不卡免费| 亚洲综合色婷婷在线观看| 亚洲国产精品久久久久婷婷老年| 四虎国产精品免费视| 国产va免费精品观看精品| 日本免费一区二区久久人人澡 | 69视频免费在线观看| 人成免费在线视频| 亚洲午夜精品久久久久久app | 在线观看亚洲精品专区| 亚洲中文字幕久久精品无码2021| 亚洲精品亚洲人成人网| www亚洲精品少妇裸乳一区二区| 成人女人A级毛片免费软件| 99久热只有精品视频免费观看17| 一个人看的www免费高清| 国产精品久久久久久亚洲小说|