Change Dir

          先知cd——熱愛(ài)生活是一切藝術(shù)的開始

          統(tǒng)計(jì)

          留言簿(18)

          積分與排名

          “牛”們的博客

          各個(gè)公司技術(shù)

          我的鏈接

          淘寶技術(shù)

          閱讀排行榜

          評(píng)論排行榜

          簡(jiǎn)單的log

          在寫j2ee程序時(shí),我們的程序中會(huì)經(jīng)常出現(xiàn)這樣一句代碼:log.error(“something bad happend”);每個(gè)人都知道,這是打印日志用的,那么這個(gè)log對(duì)象如何來(lái)的呢?我們也知道,是

          private Log log = LogFactory.getLog(CLASS.class);
          
          這樣來(lái)的。配置log已經(jīng)不是一件技術(shù)活了,我們總是使用JCL作為wraper,調(diào)用實(shí)際的底層的日志框架,而日志框架無(wú)非就是log4j,logback。這里簡(jiǎn)單的記錄一下log在系統(tǒng)中的配置和使用。 commons-log是非常簡(jiǎn)單的一個(gè)開源項(xiàng)目,apache-commons下代碼算是最少的了。功能最簡(jiǎn)單,包裝一個(gè)統(tǒng)一的log接口,然后調(diào)用實(shí)際的log框架完成日志記錄的工作。 生一個(gè)log對(duì)象,是通過(guò)logFactory完成的。
             1: /**
             2:      * Convenience method to return a named logger, without the application
             3:      * having to care about factories.
             4:      *
             5:      * @param clazz Class from which a log name will be derived
             6:      *
             7:      * @exception LogConfigurationException if a suitable <code>Log</code>
             8:      *  instance cannot be returned
             9:      */
            10:     public static Log getLog(Class clazz)
            11:         throws LogConfigurationException {
            12:  
            13:         return (getFactory().getInstance(clazz));
            14:  
            15:     }

          其中的getFactory方法,首先會(huì)嘗試?yán)卯?dāng)前的classLoader去一個(gè)hashtable里取一個(gè)factory出來(lái),這個(gè)設(shè)計(jì)為了避免資源重復(fù)(按照官方說(shuō)法,getLog是一個(gè)很重的操作),利用了cache的思想,緩存了構(gòu)建過(guò)的factory。這個(gè)緩存的key就是classloader。寫到這里,插一句,這個(gè)設(shè)計(jì)在我們?yōu)橐粋€(gè)已有項(xiàng)目添加資源利用時(shí)是必備的設(shè)計(jì)之一——緩存重復(fù)利用的資源。

             1: ClassLoader contextClassLoader = getContextClassLoaderInternal();
             2:  
             3:        if (contextClassLoader == null) {
             4:            // This is an odd enough situation to report about. This
             5:            // output will be a nuisance on JDK1.1, as the system
             6:            // classloader is null in that environment.
             7:            if (isDiagnosticsEnabled()) {
             8:                logDiagnostic("Context classloader is null.");
             9:            }
            10:        }
            11:  
            12:        // Return any previously registered factory for this class loader
            13:        LogFactory factory = getCachedFactory(contextClassLoader);
            14:        if (factory != null) {
            15:            return factory;
            16:        }

          當(dāng)然第一次加載時(shí)是緩存取不到的,那么logFactory會(huì)嘗試進(jìn)行一次commons-logging.properties文件的讀取,來(lái)配置具體的log方式,沒(méi)有配置的話,接著會(huì)嘗試"META-INF/services/org.apache.commons.logging.LogFactory”來(lái)配置。最后實(shí)在沒(méi)有找到任何信息時(shí),就會(huì)用commons-logging自己的org.apache.commons.logging.impl.LogFactoryImpl來(lái)實(shí)例化了。加載方式比較簡(jiǎn)單,利用初始時(shí)得到的classloader加載org.apache.commons.logging.impl.LogFactoryImpl即可。然后把得到的factory緩存住就OK了。

          顯然的事情是,logFactory這種東西都緩存了,那么log這個(gè)對(duì)象就更要緩存了,LogFactoryImpl里有個(gè)hashtable用來(lái)存放已經(jīng)注冊(cè)了的Log對(duì)象。key的話是調(diào)用時(shí)傳入的className。如果配置了Log4j的話,那么接下來(lái)log的所有行為就寫到log4j配置的日志里了(其實(shí)不用配置,log4j是commons-logging的首選支持)。在沒(méi)有任何配置的情況下,會(huì)支持{log4j,jdk14log,jdk13log和simpleLog}

          以log4j為例說(shuō)一下具體的寫日志:在commons-logging的Log4JLogger拿到后,作為一個(gè)包裝器,其所有的寫日志方法都是調(diào)用的內(nèi)部的一個(gè)logger對(duì)象的,這個(gè)logger來(lái)自Log4j的Logger,logger是通過(guò)LogManager的getLogger方法得到的。

           

          log4j的一個(gè)xml配置解說(shuō):

             1: <?xml version="1.0" encoding="GBK"?>
             2: <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
             3: <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
             4:     <appender name="appender1" class="org.apache.log4j.DailyRollingFileAppender">
             5:         <param name="file" value="/home/admin/yourapp/app.log" />
             6:         <param name="encoding" value="GBK" />
             7:         <param name="append" value="true" />
             8:         <param name="threshold" value="info"/>
             9:         <layout class="org.apache.log4j.PatternLayout">
            10:             <param name="ConversionPattern" value="%d{yy/MM/dd HH:mm:ss:SSS}][%C-%M] %m%n" />
            11:         </layout>
            12:     </appender>
            13:     <logger name="com.yourcom.yourpackage" additivity="false">
            14:        <level value="your_loggingLevel"/>
            15:        <appender-ref ref="appender1"/>
            16:    </logger>
            17:     <root>
            18:        <level value="your_loggingLevel" />
            19:        <appender-ref ref="appender1" />
            20:    </root>

          其中appender定義了日志以什么樣的pattern打印到什么目錄file,日志打印的級(jí)別threshold以及編碼方式encoding。logger配置定義了哪些類里的log代碼會(huì)被打印,這里name指定了對(duì)應(yīng)的包名。root定義了log4j中的hierarchy類中的root logger。最終logger的所有打日志方法都會(huì)調(diào)用下面這個(gè)方法,這個(gè)方法在logger的父類Category中:

             1: /**
             2:      Call the appenders in the hierrachy starting at
             3:      <code>this</code>.  If no appenders could be found, emit a
             4:      warning.
             5: 
             6:      <p>This method calls all the appenders inherited from the
             7:      hierarchy circumventing any evaluation of whether to log or not
             8:      to log the particular log request.
             9: 
            10:      @param event the event to log.  */
            11:   public
            12:   void callAppenders(LoggingEvent event) {
            13:     int writes = 0;
            14:  
            15:     for(Category c = this; c != null; c=c.parent) {
            16:       // Protected against simultaneous call to addAppender, removeAppender,...
            17:       synchronized(c) {
            18:     if(c.aai != null) {
            19:       writes += c.aai.appendLoopOnAppenders(event);
            20:     }
            21:     if(!c.additive) {
            22:       break;
            23:     }
            24:       }
            25:     }
            26:  
            27:     if(writes == 0) {
            28:       repository.emitNoAppenderWarning(this);
            29:     }
            30:   }

          一個(gè)LoggingEvent在打日志的時(shí)候會(huì)被實(shí)例化,并且賦予一些記錄日志相關(guān)的信息。第19行的代碼其實(shí)就是在調(diào)用配置文件中配置過(guò)的appender。具體的打印樣式,會(huì)被Layout類處理。

          最后關(guān)于性能的討論,來(lái)源于官網(wǎng)的解釋,logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i])); 這樣一條語(yǔ)句帶來(lái)了大量的string操作及參數(shù)構(gòu)建。所以文檔建議我們使用這樣的寫法:

             1: if(logger.isDebugEnabled() {
             2:         logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
             3:       }

          理由是這樣避免了log參數(shù)的構(gòu)建。當(dāng)然,用commons-logging也可以的,其中的Log也封裝了所有的Logger原有的方法。另外,layout花費(fèi)了不少時(shí)間,目前的文檔說(shuō),SimpleLayout已經(jīng)優(yōu)化到和System.out.print()一樣快了。

          總結(jié)一下,log4j是使用最普遍的日志應(yīng)用了,依靠日志,我們記錄了信息,同時(shí)可以幫助調(diào)試,是服務(wù)器端開發(fā)的一大利器。了解日志系統(tǒng)的原理也是必需的技能。log系統(tǒng)值得深入的地方還有很多。以后繼續(xù)挖掘。

           

          參考資料:

          http://www.iteye.com/topic/378077 

          http://logging.apache.org/log4j/1.2/manual.html

          posted on 2012-03-28 20:03 changedi 閱讀(2349) 評(píng)論(1)  編輯  收藏

          評(píng)論

          # re: 簡(jiǎn)單的log 2012-03-30 07:50 tb

          很不錯(cuò)的   回復(fù)  更多評(píng)論   


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 武定县| 东平县| 洞口县| 若羌县| 剑河县| 右玉县| 通渭县| 浮梁县| 齐河县| 余庆县| 旺苍县| 花莲县| 乌鲁木齐市| 邹平县| 石屏县| 龙里县| 清丰县| 蒙城县| 奉化市| 邹城市| 彭州市| 长乐市| 房产| 汝南县| 喀喇沁旗| 阿勒泰市| 长沙县| 抚顺县| 宜都市| 崇文区| 上栗县| 宝兴县| 湘西| 潞城市| 内黄县| 新野县| 荔浦县| 康马县| 陇川县| 青神县| 阳江市|