Log4j2 中各種配置的含義,你都明白?

1.日誌框架

日誌接口(slf4j)
slf4j是對所有日誌框架制定的一種規範、標準、接口,並不是一個框架的具體的實現,因爲接口並不能獨立使用,需要和具體的日誌框架實現配合使用(如log4j、logback)

日誌實現(log4j、logback、log4j2)

  • log4j是apache實現的一個開源日誌組件

  • logback同樣是由log4j的作者設計完成的,擁有更好的特性,用來取代log4j的一個日誌框架,是slf4j的原生實現

  • log4j2是log4j 1.x和logback的改進版,據說採用了一些新技術(無鎖異步、等等),使得日誌的吞吐量、性能比log4j 1.x提高10倍,並解決了一些死鎖的bug,而且配置更加簡單靈活。

2.爲什麼需要日誌接口,直接使用具體的實現不就行了嗎?

接口用於定製規範,可以有多個實現,使用時是面向接口的(導入的包都是slf4j的包而不是具體某個日誌框架中的包),即直接和接口交互,不直接使用實現,所以可以任意的更換實現而不用更改代碼中的日誌相關代碼。

比如:slf4j定義了一套日誌接口,項目中使用的日誌框架是logback,開發中調用的所有接口都是slf4j的,不直接使用logback,調用是 自己的工程調用slf4j的接口,slf4j的接口去調用logback的實現,可以看到整個過程應用程序並沒有直接使用logback,當項目需要更換更加優秀的日誌框架時(如log4j2)只需要引入Log4j2的jar和Log4j2對應的配置文件即可,完全不用更改Java代碼中的日誌相關的代碼logger.info(“xxx”),也不用修改日誌相關的類的導入的包(import org.slf4j.Logger; import org.slf4j.LoggerFactory;)

使用日誌接口便於更換爲其他日誌框架

log4j、logback、log4j2都是一種日誌具體實現框架,所以既可以單獨使用也可以結合slf4j一起搭配使用。

本文使用Log4j2作爲slf4j的具體實現,引入的包如下:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.25</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.11.0</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.11.0</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.11.0</version>
</dependency>

3.log4j2日誌級別
從大到小依次是: off, fatal, error, warn, info, debug, trace, all

由於我們使用的是slf4j接口包,該接口包中只提供了未標有刪除線的日誌級別的輸出。

4.log4j2配置文件的優先級

  • Log4j will inspect the log4j.configurationFile system property and, if set, will attempt to load the configuration using the ConfigurationFactory that matches the file extension.

  • If no system property is set the properties ConfigurationFactory will look for log4j2-test.properties in the classpath.

  • If no such file is found the YAML ConfigurationFactory will look for log4j2-test.yaml or log4j2-test.yml in the classpath.

  • If no such file is found the JSON ConfigurationFactory will look for log4j2-test.json or log4j2-test.jsn in the classpath.

  • If no such file is found the XML ConfigurationFactory will look for log4j2-test.xml in the classpath.

  • If a test file cannot be located the properties ConfigurationFactory will look for log4j2.properties on the classpath.

  • If a properties file cannot be located the YAML ConfigurationFactory will look for log4j2.yaml or log4j2.yml on the classpath.

  • If a YAML file cannot be located the JSON ConfigurationFactory will look for log4j2.json or log4j2.jsn on the classpath.

  • If a JSON file cannot be located the XML ConfigurationFactory will try to locate log4j2.xml on the classpath.

  • If no configuration file could be located the DefaultConfiguration will be used. This will cause logging output to go to the console.

5.對於log4j2配置文件的理解
配置文件結構:

Appdenders部分

Appender

 Filter
 Layout
 Policies
 Strategy
 Appender

Loggers部分

  • Logger
  • RootLogger

6.對於Appender的理解
簡單說Appender就是一個管道,定義了日誌內容的去向(保存位置)。

配置一個或者多個Filter,Filter的過濾機制和Servlet的Filter有些差別,下文會進行說明。

  • 配置Layout來控制日誌信息的輸出格式。

  • 配置Policies以控制日誌何時(When)進行滾動。

  • 配置Strategy以控制日誌如何(How)進行滾動。

7.對於Logger的理解
簡單說Logger就是一個路由器,指定類、包中的日誌信息流向哪個管道,以及控制他們的流量(日誌級別)

8.log4j2配置文件框架
配置文件格式

<?xml version="1.0" encoding="UTF-8"?>

<Configuration>

    <Appenders>

        <Appender>
            <Filters>
                <LevelRangeFilter minLevel="..." maxLevel="..." onMatch="..." onMismatch="..."/>
            </Filters>

            <PatternLayout pattern="..." charset="..."/>

            <Policies>
                <CronTriggeringPolicy schedule="..."/>
                <SizeBasedTriggeringPolicy size="..."/>
                <TimeBasedTriggeringPolicy />
            </Policies>
        </Appender>

        <Appender>
            // ...
        </Appender>

    </Appenders>

    <Loggers>

        <Logger>
            <AppenderRef ref="...">
        </Logger>

        <Root>
            <AppenderRef ref="...">
        </Root>

    </Loggers>

</Configuration>

9.Appender標籤的實現類
其實這些標籤都是類名或者類名去掉後綴。

Appender的常用的實現類有:

  • ConsoleAppender(Console)

  • FileAppender(File)、RandomAccessFileAppender(RandomAccessFile)

  • RollingFileAppender(RollingFile)、RollingRandomAccessFileAppender(RollingRandomAccessFile)

打開這些實現類的源碼,你一定會恍然大明白,括號中的是實現類在log4j2.xml配置文件中的標籤名。

10.ConsoleAppender(Console)

該實現類會把日誌輸出到控制檯中。

它有兩種輸出方式:

  • SYSTEM_OUT(System.out)

  • SYSTEM_ERR(System.err)

如果不配置,默認使用SYSTEM_OUT進行輸出。括號中是調用的方法。

簡單示例:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="baseConf" status="warn" monitorInterval="30">

    <Appenders>

        <Console name="Console" target="SYSTEM_OUT">
            <!-- 格式化日誌 -->
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/>
        </Console>

    </Appenders>

    <Loggers>

        <!-- level默認爲error -->
        <Root level="info">
            <!-- 這裏引用了Appenders標籤中的name值 -->
            <AppenderRef ref="RollingFile"/>
        </Root>

    </Loggers>

</Configuration>

其它屬性可以參見官方文檔:

http://logging.apache.org/log4j/2.x/manual/appenders.html#ConsoleAppender

10-1.FileAppender(File)、RandomAccessFileAppender(RandomAccessFile)
相同點:寫入日誌信息到文件

不同點:使用的I/O實現類不同,前者使用FileOutputStream,後者使用RandomAccessFile。

官方文檔說是在bufferedIO=true(默認是true)的情況下後者比前者性能提升20% ~ 200%,不明覺厲,就用後者吧。

簡單示例:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="baseConf" status="warn" monitorInterval="30">

    <Appenders>

        <RandomAccessFile name="File" fileName="logs/app.log" immediateFlush="false">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/>
        </RandomAccessFile>

    </Appenders>

    <Loggers>

        <Root level="info">
            <AppenderRef ref="File"/>
        </Root>

    </Loggers>

</Configuration>

常用屬性:

  • fileName:來指定文件位置,文件或目錄不存在則會自動創建。

  • immediateFlush:是否每次寫入都要立刻刷新到硬盤中。默認true,如果使用默認值可能會影響性能。

其它屬性可以參見官方文檔:

http://logging.apache.org/log4j/2.x/manual/appenders.html#RandomAccessFileAppender

10-2.RollingFileAppender(RollingFile)、RollingRandomAccessFileAppender(RollingRandomAccessFile)

這一對之間的區別與上一對之間的區別是一樣的。

上一對的實現類不能進行日誌滾動,所謂日誌滾動就是當達到設定的條件後,日誌文件進行切分。

比如:工程師想讓系統中的日誌按日進行切分,並且按月歸檔。

這時候這一對的作用就體現出來了。

簡單示例:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="baseConf" status="warn" monitorInterval="30">

    <Appenders>

        <RollingRandomAccessFile name="File" fileName="logs/app.log"
                                 filePattern="logs/$${date:hh-mm}/%d{hh-mm-ss}.app.%i.log" >
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/>

            <Policies>
                <!-- 每 5s 翻滾一次 -->
                <CronTriggeringPolicy schedule="0/5 * * * * ?" />
                <SizeBasedTriggeringPolicy size="10 MB"/>
            </Policies>

            <DefaultRolloverStrategy max="10" />

        </RollingRandomAccessFile>

    </Appenders>

    <Loggers>

        <Root level="info">
            <AppenderRef ref="File"/>
        </Root>

    </Loggers>

</Configuration>

1.filePattern:指定了日誌滾動之後的文件命名格式,至於其中的{date:hh-mm}表達式下文介紹。

2.DefaultRolloverStrategy:指定了如何(How)進行翻滾,並且指定了最大翻滾次數(影響%i參數值),超過次數之後會按照相應的規則刪除舊日誌。

3.Policies: 這裏就是規定了何時進行滾動(When),可以有多個Policy。

  • CronTriggeringPolicy設置了每 5s 進行一次翻滾

  • SizeBasedTriggeringPolicy設置了的話,如果當前文件超過了10MB,但是文件的名字還沒有進行翻滾(建立新文件),那麼就會用%i的方式進行翻滾。

10-3.翻滾示例
app.log

第一次翻滾:app.log app.1.log // app.log -> app.1.log
第二次翻滾:app.log app.1.log app.2.lop // app.log -> app.2.log
第三次翻滾:app.log app.1.log app.2.lop app.3.lop // app.log -> app.3.log
第四次翻滾:app.log app.1.log app.2.lop app.3.lop app.4.lop // app.log -> app.4.log
一直到設定的翻滾次數10之後,會把舊的日誌內容覆蓋。

app.2.lop -> app.1.lop
app.3.lop -> app.2.lop
...
app.10.lop -> app.9.lop
app.log -> app.10.lop

一直這樣循環下去,直到創建新文件。
11.Filters
Filters決定日誌事件能否被輸出。過濾條件有三個值:ACCEPT(接受),DENY(拒絕),NEUTRAL(中立)。

11-1.常用的Filter實現類有

  • LevelRangeFilter

  • TimeFilter

  • ThresholdFilter

11-2.上文中提到log4j2中的Filter與Servlet中的有差別。那麼有什麼差別呢?

簡單說就是log4j2中的過濾器ACCEPT和DENY之後,後續的過濾器就不會執行了,只有在NEUTRAL的時候纔會執行後續的過濾器。

11-3.簡單示例

測試代碼:

public class LogMain {

    private static Logger logger = LoggerFactory.getLogger(LogMain.class);

    public static void main(String[] args) throws Exception {

        logger.trace("trace Msg.");
        logger.debug("debug Msg.");
        logger.info("info Msg.");
        logger.warn("warn Msg.");
        logger.error("error Msg.");

    }

}

配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="baseConf" status="warn" monitorInterval="30">

    <Appenders>

        <Console name="Console">

            <!--
                設置 onMismatch="NEUTRAL" 可以讓日誌經過後續的過濾器
                最後一個過濾器建議設置 onMismatch="DENY", 不然日誌就輸出了。
            -->
            <Filters>

                <!-- 從大到小:error, warn, info, debug, trace -->
                <LevelRangeFilter minLevel="error" maxLevel="info" onMatch="ACCEPT" onMismatch="NEUTRAL" />

                <!-- 只允許在每天的 8點~8點半 之間輸出日誌 -->
                <TimeFilter start="08:00:00" end="08:30:00" onMatch="ACCEPT" onMismatch="DENY" />
            </Filters>

            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/>
        </Console>

    </Appenders>

    <Loggers>

        <Root level="info">
            <AppenderRef ref="Console"/>
        </Root>

    </Loggers>

</Configuration>

輸出結果:

17:51:53.546 [main] INFO  me.master.snail.log.LogMain - info Msg.
17:51:53.548 [main] WARN  me.master.snail.log.LogMain - warn Msg.
17:51:53.548 [main] ERROR me.master.snail.log.LogMain - error Msg.

如果當前時間不是 8點~8點半 之間,那麼沒有日誌會輸出。

這裏的info Msg.、warn Msg.和error Msg.爲什麼會輸出呢?

是因爲LevelRangeFilter對它們進行了ACCEPT,而剩下的trace Msg.和debug Msg.則會經過下一個過濾器,然後依次類推。
12.PatternLayout
這是常用的日誌格式化類,其它日誌格式化類很少用。

關於其它日誌類,可以打開PatternLayout類,找到其父類AbstractStringLayout, 看父類的實現類有哪些。

簡單示例:

<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/>

授人以魚不如授人以漁。關於pattern的格式點擊

http://logging.apache.org/log4j/2.x/manual/layouts.html#PatternLayout

具體的其它屬性可以看源碼也可以參考官方文檔。

13.Policy & Strategy
上文也說了,Policy是用來控制日誌文件何時(When)進行滾動的;Strategy是用來控制日誌文件如何(How)進行滾動的。

如果配置的是RollingFile或RollingRandomAccessFile,則必須配置一個Policy。

如果想按月歸檔,按日切分日誌,然後

13-1.Policy常用的實現類:

  • SizeBasedTriggeringPolicy

  • CronTriggeringPolicy

  • TimeBasedTriggeringPolicy

13-1-1.SizeBasedTriggeringPolicy

根據日誌文件的大小進行滾動。

<SizeBasedTriggeringPolicy size="10MB"/>

單位有:KB,MB,GB

13-1-2.CronTriggeringPolicy
使用Cron表達式進行日誌滾動,很靈活。

<CronTriggeringPolicy schedule="0/5 * * * * ?" />

13-1-3.TimeBasedTriggeringPolicy
這個滾動策略依賴於filePattern中配置的最具體的時間單位,根據最具體的時間單位進行滾動。

這種方式比較簡潔。CronTriggeringPolicy策略更強大。

簡單示例:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="baseConf" status="warn" monitorInterval="30">

    <Appenders>

        <RollingRandomAccessFile name="File" fileName="logs/app.log"
                                 filePattern="logs/$${date:hh-mm}/%d{hh-mm-ss}.app.%i.log" >
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/>

            <Policies>
                <!-- 每 5s 翻滾一次 -->
                <!--<CronTriggeringPolicy schedule="0/5 * * * * ?" />-->

                <!--
                    filePattern中最具體的時間單位是 秒。
                    這裏用 TimeBasedTriggeringPolicy 替換 CronTriggeringPolicy

                    注意:modulate屬性是指從啓動時間開始算5秒,還是從0秒開始算5秒,運行一下就明白了。
                    modulate: true(默認值) // 會從啓動時間開始算 5秒
                    modulate: false // 從 0秒開始算
                -->
                <TimeBasedTriggeringPolicy interval="5" modulate="true"/>

                <SizeBasedTriggeringPolicy size="10 MB"/>
            </Policies>

            <DefaultRolloverStrategy max="10" />

        </RollingRandomAccessFile>

    </Appenders>

    <Loggers>

        <Root level="info">
            <AppenderRef ref="File"/>
        </Root>

    </Loggers>

</Configuration>

13-2.Strategy常用的實現類

  • DefaultRolloverStrategy

  • DirectWriteRolloverStrategy

這兩個Strategy都是控制如何進行日誌滾動的,至於他們的區別我還是不太明白,大佬解釋一下吧。

平時大部分用DefaultRolloverStrategy就可以了。

14.Logger

Logger部分就比較簡單了,分爲兩個Logger:

  • Root(必須配置)

  • Logger

簡單示例:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="baseConf" status="warn" monitorInterval="30">

    <Appenders>

        <Console name="Console">
            <PatternLayout>
                <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
            </PatternLayout>
        </Console>

    </Appenders>

    <Loggers>

        <Root level="trace">
            <AppenderRef ref="Console"/>
            <Filters>
                <LevelRangeFilter minLevel="error" maxLevel="info" onMatch="ACCEPT" onMismatch="DENY" />
            </Filters>
        </Root>

    </Loggers>

</Configuration>

注意:Logger中也可以加過濾器的喲~

14-1.比較重要的問題: 日誌重複打印
如果Root中的日誌包含了Logger中的日誌信息,並且AppenderRef是一樣的配置,則日誌會打印兩次。

注意:有兩個條件

  • Root中的日誌包含了Logger中的日誌信息

  • 且AppenderRef是一樣的配置

這時候我們需要使用一個Logger的屬性來解決,那就是additivity,其默認值爲true,需要配置爲false。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="baseConf" status="warn" monitorInterval="30">

    <Appenders>

        <Console name="Console">
            <PatternLayout>
                <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
            </PatternLayout>
        </Console>

    </Appenders>

    <Loggers>

        <Logger name="me.master.snail.log.LogMain" level="info" additivity="false">
            <AppenderRef ref="Console"/>
        </Logger>

        <Root level="trace">
            <AppenderRef ref="Console"/>
            <Filters>
                <LevelRangeFilter minLevel="error" maxLevel="info" onMatch="ACCEPT" onMismatch="DENY" />
            </Filters>
        </Root>

    </Loggers>

</Configuration>

15.Lookups
這個組件類似於JSTL的EL表達式,或者類似於Spring的SpEL表達式。

具體的語法很簡單,這裏就不粘貼複製了,查看官方文檔:

http://logging.apache.org/log4j/2.x/manual/lookups.html

相信你用半個小時就學會了。
16.示例
爲了大家快速開發(方便懶惰的同學),寫一些示例。

16-1.輸出到控制檯

<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="baseConf" status="warn" monitorInterval="30">

    <Appenders>

        <Console name="Console" target="SYSTEM_OUT">
            <!-- 格式化日誌 -->
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/>
        </Console>

    </Appenders>

    <Loggers>

        <!-- level默認爲error -->
        <Root level="info">
            <!-- 這裏引用了Appenders標籤中的name值 -->
            <AppenderRef ref="RollingFile"/>
        </Root>

    </Loggers>

</Configuration>

16-2.輸出到單個文件

<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="baseConf" status="warn" monitorInterval="30">

    <Appenders>

        <RandomAccessFile name="File" fileName="logs/app.log" immediateFlush="false">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/>
        </RandomAccessFile>

    </Appenders>

    <Loggers>

        <Root level="info">
            <AppenderRef ref="File"/>
        </Root>

    </Loggers>

</Configuration>

16-3.按月歸檔日誌,按日進行切分,限制單文件大小爲 500MB, 一天最多生成20個文件,也就是(20 * 500)MB大小的日誌

<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="baseConf" status="warn" monitorInterval="30">

    <Appenders>

        <RollingRandomAccessFile name="File" fileName="logs/app.log"
                                 filePattern="logs/$${date:yyyy-MM}/%d{yyyy-MM-dd}.app.%i.log" >
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/>

            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="false"/>
                <SizeBasedTriggeringPolicy size="500MB"/>
            </Policies>

            <DefaultRolloverStrategy max="20" />
        </RollingRandomAccessFile>

    </Appenders>

    <Loggers>

        <Root level="info">
            <AppenderRef ref="File"/>
        </Root>

    </Loggers>

</Configuration>

16-4.限制Spring框架日誌的輸出級別

<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="baseConf" status="warn" monitorInterval="30">

    <Appenders>

        <RollingRandomAccessFile name="File" fileName="logs/app.log"
                                 filePattern="logs/$${date:yyyy-MM}/%d{yyyy-MM-dd}.app.%i.log" >
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/>

            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="false"/>
                <SizeBasedTriggeringPolicy size="500MB"/>
            </Policies>

            <DefaultRolloverStrategy max="20" />
        </RollingRandomAccessFile>

    </Appenders>

    <Loggers>

        <!--
            限制Spring框架日誌的輸出級別,其它框架類似配置
            或者使用 AppenderRef 標籤,將其輸出到指定文件中,記得加上 additivity="false"
        -->
        <logger name="org.springframework" level="INFO"/>

        <Root level="info">
            <AppenderRef ref="File"/>
        </Root>

    </Loggers>

</Configuration>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章