剖析項目多個logback配置(下)


來源:http://www.cnblogs.com/guozp/p/5973038.html 

 

上篇大概描述了logback的加載順序以及加載的源碼,本篇將分析如果在你的Maven或者其他多模塊的項目中,每個模塊都存在logback.xml的情況,項目會加載哪個為准。

這里簡單的測試下,我的service模塊下有個logback.xml,其他的模塊下也有,但是輸出目錄不同,以此來觀察。

service模塊:

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="false" scanPeriod="60 seconds" debug="false">
    <property name="LOG_HOME" value="D:/log" />
    <property name="appName" value="index"></property>

    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <Encoding>UTF-8</Encoding>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </layout>
    </appender>

    <appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <Encoding>UTF-8</Encoding>
        <file>${LOG_HOME}/${appName}.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
            <MaxHistory>10</MaxHistory>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>500MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern>
        </layout>
    </appender>

    <!-- Spring framework logger -->
    <logger name="org.springframework" level="error" additivity="false"></logger>

    <logger name="com.XX.XX" level="info" additivity="false">
        <appender-ref ref="stdout" />
        <appender-ref ref="appLogAppender" />
    </logger>

    <root level="info">
        <appender-ref ref="stdout" />
    </root>
</configuration>

 

其他模塊配置,例如Dao:

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="false" scanPeriod="60 seconds" debug="false">
    <property name="LOG_HOME" value="D:/log" />
    <property name="appName" value="index_dao"></property>

    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <Encoding>UTF-8</Encoding>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </layout>
    </appender>

    <appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <Encoding>UTF-8</Encoding>
        <file>${LOG_HOME}/${appName}.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
            <MaxHistory>10</MaxHistory>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>500MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern>
        </layout>
    </appender>

    <!-- Spring framework logger -->
    <logger name="org.springframework" level="error" additivity="false"></logger>

    <logger name="com.XX.XX" level="info" additivity="false">
        <appender-ref ref="stdout" />
        <appender-ref ref="appLogAppender" />
    </logger>

    <root level="info">
        <appender-ref ref="stdout" />
    </root>
</configuration>

在不同的模塊有不同的用例,但是service模塊依賴dao和common模塊,觀察service模塊會加載哪個文件,dao模塊又會加載哪個文件,在用例中取出配置文件的某個值可以直觀的看到你到底加載的那個配置文件(我本地使用的是log4j的配置文件,但是我項目中沒有log4j的jar,僅僅是適用配置文件中的值輸出測試而已)

service模塊的值:

log4j.rootLogger=INFO,A1,FF

dao模塊的值:

log4j.rootLogger=INFO,A1,FF,dddddddd

common模塊的值:

log4j.rootLogger=INFO,A1,FF,ccccccccc

用例:

public class LogTest {
    private static Logger logger = LoggerFactory.getLogger(LogTest.class);

    public static void main(String[] args) throws Exception {
        logger.info("111111");
        ClassLoader classLoader = LogTest.class.getClassLoader();
        HashSet urlSet = new HashSet();
        //看當前類路徑下存在的配置文件
        Enumeration urlEnum = classLoader.getResources("log4j.properties");
        while(urlEnum.hasMoreElements()) {
            URL url = (URL)urlEnum.nextElement();
            urlSet.add(url);
            System.out.println(url);
        }
        //查找具有給定名稱的資源
        URL url = classLoader.getResource("log4j.properties");
        File file = new File(url.toURI());
        BufferedReader bf = new BufferedReader(new FileReader(file));
        String s = bf.readLine();
        System.out.println(s);
    }

}

輸出結果:

2016-10-20 16:26:08.439 [main] INFO com.jd.index.storm.LogTest - 111111
file:/D:/IdeaCode/gitCode/index_file/index_file_service/target/classes/log4j.properties
file:/D:/IdeaCode/gitCode/index_file/index_file_common/target/classes/log4j.properties
file:/D:/IdeaCode/gitCode/index_file/index_file_dao/target/classes/log4j.properties
log4j.rootLogger=INFO,A1,FF

輸出文件:

 

從結果可以看出我在service模塊的用例加載的是當前模塊下的log4j配置文件,而且日志使用的logback配置也是當前模塊下的,並沒有使用其他模塊中的。

具體原因可以參考以下源碼:

 public void autoConfig() throws JoranException {
        StatusListenerConfigHelper.installIfAsked(this.loggerContext);
        //在這里加載真正的配置文件
        URL url = this.findURLOfDefaultConfigurationFile(true);
        if(url != null) {
            this.configureByResource(url);
        } else {
            BasicConfigurator.configure(this.loggerContext);
        }

    }

public URL findURLOfDefaultConfigurationFile(boolean updateStatus) {
        ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this);
        URL url = this.findConfigFileURLFromSystemProperties(myClassLoader, updateStatus);
        if(url != null) {
            return url;
        } else {
       //通過getResource返回目標文件的URL
            url = this.getResource("logback.groovy", myClassLoader, updateStatus);
            if(url != null) {
                return url;
            } else {
                url = this.getResource("logback-test.xml", myClassLoader, updateStatus);
                return url != null?url:this.getResource("logback.xml", myClassLoader, updateStatus);
            }
        }
    }

private URL getResource(String filename, ClassLoader myClassLoader, boolean updateStatus) {
       //通過調用類加載器加載
        URL url = Loader.getResource(filename, myClassLoader);
        if(updateStatus) {
       //輸出警告
            this.statusOnResourceSearch(filename, myClassLoader, url);
        }

        return url;
    }

  public static URL getResource(String resource, ClassLoader classLoader) {
        try {
       //通過調用類加載器加載    
            return classLoader.getResource(resource);
        } catch (Throwable var3) {
            return null;
        }
    }

 

 

ClassLoader中的方法:
此方法首先搜索資源的父類加載器;如果父類加載器為 null,則搜索的路徑就是虛擬機的內置類加載器的路徑。如果搜索失敗,則此方法將調用 findResource(String) 來查找資源
 public URL getResource(String name) {
        URL url;
        if (parent != null) {
            url = parent.getResource(name);
        } else {
            url = getBootstrapResource(name);
        }
        if (url == null) {
            url = findResource(name);
        }
        return url;
    }

 

注意我用例中使用的另一個方法:

public Enumeration<URL> getResources(String name) 

 

資源的 URL 對象的枚舉。如果找不到資源,則該枚舉將為空。類加載器無權訪問的資源不在此枚舉中。

public Enumeration<URL> getResources(String name) throws IOException {
        Enumeration[] tmp = new Enumeration[2];
        if (parent != null) {
            tmp[0] = parent.getResources(name);
        } else {
            tmp[0] = getBootstrapResources(name);
        }
        tmp[1] = findResources(name);

        return new CompoundEnumeration<>(tmp);
    }

 至此可以看出,logback加載配置文件順序是調用的類加載器原生的方法,所以加載的順序以及要加載哪個配置自然和原生的類加載器一樣。

簡單來說,看你的程序運行在哪個模塊,運行時會加載相應的模塊的日志配置,並不使用其他模塊下的日志配置,雖然同在類路徑下。

本項目包含Storm和其他的模塊,但是Storm輸出日志的時候並不會使用其他模塊下的logback的配置,原因就在這里。

在這里順道嘮叨幾句Storm日志問題,很多人對此還是有還是有些模糊的,例如使用的是那個篇日志文件,文件名稱又是哪里定義的。

storm使用logback作為日志服務插件,配置文件在$STORM_HOME/logback/cluster.xml 。

對於storm,我們關心的主要是worker、nimbus、supervisor等日志(worker-xxxx.log,nimbus.log,supervisor.log),

這些日志使用的都是配置中的A1(即使用的是默認配置):
<appender name="A1" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${storm.home}/logs/${logfile.name}</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
      <fileNamePattern>${storm.home}/logs/${logfile.name}.%i</fileNamePattern>
      <minIndex>1</minIndex>
      <maxIndex>9</maxIndex>
    </rollingPolicy>

    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
        <maxFileSize>1024MB</maxFileSize>
    </triggeringPolicy>

    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n</pattern>
    </encoder>
 </appender>

 <appender name="ACCESS" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${storm.home}/logs/access.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
      <fileNamePattern>${storm.home}/logs/access.log.%i</fileNamePattern>
      <minIndex>1</minIndex>
      <maxIndex>9</maxIndex>
    </rollingPolicy>

    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>100MB</maxFileSize>
    </triggeringPolicy>

    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n</pattern>
    </encoder>
  </appender>

  <appender name="METRICS" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${storm.home}/logs/metrics.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
      <fileNamePattern>metrics.log.%i</fileNamePattern>
      <minIndex>1</minIndex>
      <maxIndex>9</maxIndex>
    </rollingPolicy>

    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>2MB</maxFileSize>
    </triggeringPolicy>

    <encoder>
      <pattern>%d %-8r %m%n</pattern>
    </encoder>
  </appender>

  <root level="INFO">
    <appender-ref ref="A1"/>
  </root>
這里的${storm.home}、${logfile.name}這個是從哪里傳入的呢?可以從bin/storm腳本中看到nimbus、ui、supervisor是在啟動的時候傳入了storm.home和logfile.name。
Storm中worker的日志,使用當前work的端口,並且只有在集群有topology運行的時候才會生成,可以再supervisor.clj的launch-worker方法中生成了logfile.name
閱讀源碼,可以看到在形如:worker-6719.log。
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM