寫了幾年的Java,最弱的部份是Log,現在看來還是差不多。最近在改寫Framework時才對Log4J的用法稍有體會,不然也是渾渾噩噩看別人怎麼配置,自己也跟著配置。

  2006年曾設計Logger Proxy委託天津同事開發,其目的有二:一是預設按class full name產生log檔,二是避開像JBoss本身的log4j.properties影響。這樣做效益有好有壞,壞就壞在Ap Server會記一份,自己又用Proxy記了一份,而且不只如此,移到不同的AP Server或Web Server,因WAR或EAR的目錄結構不同,連帶的Proxy跟著改寫,無法通用。

  後來在Google找有無可依不同的class name為檔名產出不同的log檔,是有接近的solution,但不完全滿足其需求。也間接了解了log4j.properties真正的配置方式:

log4j.rootLogger = INFO, stdout
log4j.category.com.opensymphony=DEBUG
log4j.category.org.apache.struts2=INFO, rolling

  原來Log4J是由Logger、Appender和Layout決定。後兩者設定方式還算清楚,而Logger其實可以模擬做到依不同class或package產出不同的log file。上例的log4j.rootLogger、log4j.category.package.class就是一種Logger,rootLogger是super Logger可以記錄所有class的Log,而log4j.category.後面是接package甚至是class full name。

  等於(=)後面以逗號分隔,第一個參數是Level,第二個以後的參數均是Appender,以log4j.category.com.opensymphony為例,雖然rootLogger的Level是INFO,但com.opensymphony這package內所有的class和子class均是DEBUG Level,除非還有更detail的package另外設Level,則以最接近的package為優先。第三例則表示還可參照rolling這個appender。

  但為何不能滿足在執行期動態決定產出的log file name,看FileAppender如下:

log4j.appender.rolling=org.apache.log4j.RollingFileAppender
log4j.appender.rolling.File=c:/logs/test
log4j.appender.rolling.MaxFileSize=100KB
log4j.appender.rolling.MaxBackupIndex=1
log4j.appender.rolling.layout=org.apache.log4j.PatternLayout
log4j.appender.rolling.layout.ConversionPattern=%d{ABSOLUTE} %-5p [%F:%L] : %m%n

  上例的粗體字就事先把file給產出,若是目錄就會報錯,爾後在Google找到解法是去繼承RollingFileAppender。

(log4j.properties設定)

log4j.appender.dynarolling=com.xyz.util.DynaRollingAppender
log4j.appender.dynarolling.File=C:/logs/test
log4j.appender.dynarolling.MaxFileSize=100KB
log4j.appender.dynarolling.MaxBackupIndex=1
log4j.appender.dynarolling.layout=org.apache.log4j.PatternLayout
log4j.appender.dynarolling.layout.ConversionPattern=%d{ABSOLUTE} %-5p [%F:%L] : %m%n

(source code部份)

public class DynaRollingAppender extends RollingFileAppender {
   
    private static String oldFileName = null;
    private static String oldPath = null;
    public void generateLogFile(LocationInfo info) {       
        if (oldPath == null) {
            oldPath = this.fileName.substring(0, this.fileName.lastIndexOf("test"));
        }
        String fileName1 = oldPath;
        fileName1 += info.getClassName() + ".log";
        this.logger1.info(fileName1 + "; " + this.fileName);
        oldFileName = fileName1;
        this.setFile(fileName1);   
       this.activateOptions();

    }


    @Override
    protected void subAppend(LoggingEvent event) {
        String className =  event.getLocationInformation().getClassName();
        if (oldFileName != className) {
            this.generateLogFile(event.getLocationInformation());
        }
        super.subAppend(event);
    }

}

  上例中,事先overide subAppend,換掉setFile內容,但是initial時在換掉之前就做了一次setFile,所以File參數必須是檔案,目的是取得其目錄,然後再換成class full name + ".log"格式做setFile,再activateOptions才會生效,而最後一行super.subAppend則將內容寫入新的log file。

  從程式就看到LoggingEvent含有LocationInfo類別,正是log4j使用reflection取得call它的class name, file name和line number等資訊。但這樣做有沒有效能的issue未知,之前第一次去吉隆坡出差,就曾因Logger Proxy不穩掛過。

arrow
arrow
    全站熱搜

    Jemmy 發表在 痞客邦 留言(0) 人氣()