一、什麼是RollingFile

RollingFileAppender是Log4j2中的一種能夠實現日誌檔案滾動更新(rollover)的Appender。

rollover的意思是當滿足一定條件(如檔案達到了指定的大小,達到了指定的時間)後,就重新命名原日誌檔案進行歸檔,並生成新的日誌檔案用於log寫入。如果還設定了一定時間內允許歸檔的日誌檔案的最大數量,將對過舊的日誌檔案進行刪除操作。

RollingFile實現日誌檔案滾動更新,依賴於TriggeringPolicy和RolloverStrategy。

其中,TriggeringPolicy為觸發策略,其決定了何時觸發日誌檔案的rollover,即When。

RolloverStrategy為滾動更新策略,其決定了當觸發了日誌檔案的rollover時,如何進行檔案的rollover,即How。

Log4j2提供了預設的rollover策略DefaultRolloverStrategy。

下面通過一個log4j2.xml檔案配置簡單瞭解RollingFile的配置。

1.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">
<Appenders>
<RollingFile name="RollingFile" fileName="logs/app.log"
filePattern="logs/app-%d{yyyy-MM-dd HH}.log">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy interval="1"/>
<SizeBasedTriggeringPolicy size="250MB"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<Root level="error">
<AppenderRef ref="RollingFile"/>
</Root>
</Loggers>
</Configuration>

上述配置檔案中配置了一個RollingFile,日誌寫入logs/app.log檔案中,每經過1小時或者當檔案大小到達250M時,按照app-2017-08-01 12.log的格式對app.log進行重新命名並歸檔,並生成新的檔案用於寫入log。

其中,fileName指定日誌檔案的位置和檔名稱(如果檔案或檔案所在的目錄不存在,會建立檔案。)

filePattern指定觸發rollover時,檔案的重新命名規則。filePattern中可以指定類似於SimpleDateFormat中的date/time pattern,如yyyy-MM-dd HH,或者%i指定一個整數計數器。

TimeBasedTriggeringPolicy指定了基於時間的觸發策略。
SizeBasedTriggeringPolicy指定了基於檔案大小的觸發策略。

二、TriggeringPolicy

RollingFile的觸發rollover的策略有CronTriggeringPolicy(Cron表示式觸發)、OnStartupTriggeringPolicy(JVM啟動時觸發)、SizeBasedTriggeringPolicy(基於檔案大小)、TimeBasedTriggeringPolicy(基於時間)、CompositeTriggeringPolicy(多個觸發策略的混合,如同時基於檔案大小和時間)。

其中,SizeBasedTriggeringPolicy(基於日誌檔案大小)、TimeBasedTriggeringPolicy(基於時間)或同時基於檔案大小和時間的混合觸發策略最常用。

SizeBasedTriggeringPolicy

SizeBasedTriggeringPolicy規定了當日志文件達到了指定的size時,觸發rollover操作。size引數可以用KB、MB、GB等做字尾來指定具體的位元組數,如20MB。

<SizeBasedTriggeringPolicy size="250MB"/>

TimeBasedTriggeringPolicy

TimeBasedTriggeringPolicy規定了當日志文件名中的date/time pattern不再符合filePattern中的date/time pattern時,觸發rollover操作。

比如,filePattern指定檔案重新命名規則為app-%d{yyyy-MM-dd HH}.log,檔名為app-2017-08-25 11.log,當時間達到2017年8月25日中午12點(2017-08-25 12),將觸發rollover操作。

引數名
型別
描述
interval integer

此引數需要與filePattern結合使用,規定了觸發rollover的頻率,預設值為1。假設interval為4,若filePattern的date/time pattern的最小時間粒度為小時(如yyyy-MM-dd HH),則每4小時觸發一次rollover;若filePattern的date/time pattern的最小時間粒度為分鐘(如yyyy-MM-dd HH-mm),則每4分鐘觸發一次rollover。

modulate boolean

指明是否對interval進行調節,預設為false。若modulate為true,會以0為開始對interval進行偏移計算。例如,最小時間粒度為小時,當前為3:00,interval為4,則以後觸發rollover的時間依次為4:00,8:00,12:00,16:00,...。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
<Appenders>
<RollingFile name="RollingFile" fileName="logs/app.log"
filePattern="logs/app-%d{yyyy-MM-dd HH}-%i.log">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="250 MB"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<Root level="error">
<AppenderRef ref="RollingFile"/>
</Root>
</Loggers>
</Configuration>

上述配置檔案中,filePattern中yyyy-MM-dd HH最小時間粒度為小時,TimeBasedTriggeringPolicy中interval使用預設值1,將每1小時觸發一次rollover。

若將filePattern改為filePattern=“logs/app-%d{yyyy-MM-dd HH-mm}-%i.log”,yyyy-MM-dd HH-mm最小時間粒度為分鐘,將每1分鐘觸發一次rollover。

CompositeTriggeringPolicy

將多個TriggeringPolicy放到Policies中表示使用複合策略

<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="250 MB"/>
</Policies>

如上,同時使用了TimeBasedTriggeringPolicy、SizeBasedTriggeringPolicy,有一個條件滿足,就會觸發rollover。

三、DefaultRolloverStrategy

DefaultRolloverStrategy指定了當觸發rollover時的預設策略。

DefaultRolloverStrategy是Log4j2提供的預設的rollover策略,即使在log4j2.xml中沒有顯式指明,也相當於為RollingFile配置下添加了如下語句。DefaultRolloverStrategy預設的max為7。

<DefaultRolloverStrategy max="7"/>

 max引數指定了計數器的最大值。一旦計數器達到了最大值,過舊的檔案將被刪除。

注意:不要認為max引數是需要保留的日誌檔案的最大數目。

max引數是與filePattern中的計數器%i配合起作用的,其具體作用方式與filePattern的配置密切相關。

1.如果filePattern中僅含有date/time pattern,每次rollover時,將用當前的日期和時間替換檔案中的日期格式對檔案進行重新命名。max引數將不起作用。

如,filePattern="logs/app-%d{yyyy-MM-dd}.log"

2.如果filePattern中僅含有整數計數器(即%i),每次rollover時,檔案重新命名時的計數器將每次加1(初始值為1),若達到max的值,將刪除舊的檔案。

如,filePattern="logs/app-%i.log"

3.如果filePattern中既含有date/time pattern,又含有%i,每次rollover時,計數器將每次加1,若達到max的值,將刪除舊的檔案,直到data/time pattern不再符合,被替換為當前的日期和時間,計數器再從1開始。

如,filePattern="logs/app-%d{yyyy-MM-dd HH-mm}-%i.log"

假設fileName為logs/app.log,SizeBasedTriggeringPolicy的size為10KB,DefaultRolloverStrategy的max為3。

根據filePattern配置的不同分為以下幾種情況:

情況1:filePattern中僅含有date/time pattern

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="trace" name="MyApp" packages="">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
<RollingFile name="RollingFile" fileName="logs/app.log"
filePattern="logs/app-%d{yyyy-MM-dd}.log">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="10KB"/>
</Policies>
<DefaultRolloverStrategy max="3"/>
</RollingFile>
</Appenders>
<Loggers>
<Root level="trace">
<AppenderRef ref="Console"/>
<AppenderRef ref="RollingFile"/>
</Root>
</Loggers>
</Configuration>
filePattern="logs/app-%d{yyyy-MM-dd}.log",指定當發生rollover時,將按照app-%d{yyyy-MM-dd}.log的格式對檔案進行重新命名。

每次觸發rollover時,將按照如下方式對檔案進行rollover。

第X次rollover
當前用於寫入log的檔案
歸檔的檔案
描述
0 app.log - 所有的log都寫進app.log中。
1 app.log app-2017-08-17.log

當app.log的size達到10KB,觸發第1次rollover,app.log被重新命名為app-2017-08-17.log。新的app.log被創建出來,用於寫入log。

2 app.log

app-2017-08-17.log

當app.log的size達到10KB,觸發第2次rollover,原來的app-2017-08-17.log將刪除。app.log被重新命名為app-2017-08-17.log。新的app.log檔案被創建出來,用於寫入log。

情況2:filePattern中僅含有整數計數器(%i)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="trace" name="MyApp" packages="">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
<RollingFile name="RollingFile" fileName="logs/app.log"
filePattern="logs/app-%i.log">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="10KB"/>
</Policies>
<DefaultRolloverStrategy max="3"/>
</RollingFile>
</Appenders>
<Loggers>
<Root level="trace">
<AppenderRef ref="Console"/>
<AppenderRef ref="RollingFile"/>
</Root>
</Loggers>
</Configuration>
filePattern="logs/app-%i.log",其餘配置同上。

每次觸發rollover時,將按照如下方式對檔案進行rollover。

第X次rollover
當前用於寫入log的檔案
歸檔的檔案
描述
0 app.log - 所有的log都寫進app.log中。
1 app.log app-1.log

當app.log的size達到10KB,觸發第1次rollover,app.log被重新命名為app-1.log。新的app.log被創建出來,用於寫入log。

2 app.log

app-1.log

app-2.log

當app.log的size達到10KB,觸發第2次rollover,app.log被重新命名為app-2.log。新的app.log被創建出來,用於寫入log。

3 app.log

app-1.log

app-2.log

app-3.log

當app.log的size達到10KB,觸發第3次rollover,app.log被重新命名為app-3.log。新的app.log被創建出來,用於寫入log。

4 app.log

app-1.log

app-2.log

app-3.log

當app.log的size達到10KB,觸發第4次rollover,app-1.log被刪除(即最初的、最舊的app.log)。app-2.log被重新命名為app-1.log,app-3.log被重新命名為app-2.log,app.log被重新命名為app-3.log。新的app.log被創建出來,用於寫入log。

情況3:如果filePattern中既含有date/time pattern,又含有%i計數器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="trace" name="MyApp" packages="">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
<RollingFile name="RollingFile" fileName="logs/app.log"
filePattern="logs/app-%d{yyyy-MM-dd HH-mm}-%i.log">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="10KB"/>
</Policies>
<DefaultRolloverStrategy max="3"/>
</RollingFile>
</Appenders>
<Loggers>
<Root level="trace">
<AppenderRef ref="Console"/>
<AppenderRef ref="RollingFile"/>
</Root>
</Loggers>
</Configuration>
filePattern="logs/app-%d{yyyy-MM-dd HH-mm}-%i.log",同時指定了TimeBasedTriggeringPolicy和SizeBasedTriggeringPolicy的觸發策略,每1分鐘或者檔案大小達到10KB,將觸發rollover。

每次觸發rollover時,將按照如下方式對檔案進行rollover。

第X次rollover
當前用於寫入log的檔案
歸檔的檔案
描述
0 app.log - 所有的log都寫進app.log中。
1 app.log app-2017-08-17 20-52-1.log

當app.log的size達到10KB,觸發第1次rollover,app.log被重新命名為app-2017-08-17 20-52-1.log。新的app.log被創建出來,用於寫入log。

2 app.log

app-2017-08-17 20-52-1.log

app-2017-08-17 20-52-2.log

當app.log的size達到10KB,觸發第2次rollover,app.log被重新命名為app-2017-08-17 20-52-2.log。新的app.log被創建出來,用於寫入log。

3 app.log

app-2017-08-17 20-52-1.log

app-2017-08-17 20-52-2.log

app-2017-08-17 20-52-3.log

當app.log的size達到10KB,觸發第3次rollover,app.log被重新命名為app-2017-08-17 20-52-3.log.log。新的app.log被創建出來,用於寫入log。

4 app.log

app-2017-08-17 20-52-1.log

app-2017-08-17 20-52-2.log

app-2017-08-17 20-52-3.log

當app.log的size達到10KB,觸發第4次rollover,因計數器的值到達max值,app-2017-08-17 20-52-1.log被刪除(即最初的、最舊的app.log)。app-2017-08-17 20-52-2.log被重新命名為app-2017-08-17 20-52-1.log,app-2017-08-17 20-52-3.log被重新命名為app-2017-08-17 20-52-2.log,app.log被重新命名為app-2017-08-17 20-52-3.log。新的app.log被創建出來,用於寫入log。

5 app.log

app-2017-08-17 20-52-1.log

app-2017-08-17 20-52-2.log

app-2017-08-17 20-52-3.log

當前時間變為app-2017-08-17 20-53,觸發第5次rollover,app-2017-08-17 20-52-1.log被刪除。app-2017-08-17 20-52-2.log被重新命名為app-2017-08-17 20-52-1.log,app-2017-08-17 20-52-3.log被重新命名為app-2017-08-17 20-52-2.log,app.log被重新命名為app-2017-08-17 20-52-3.log。新的app.log被創建出來,用於寫入log。
6 app.log

app-2017-08-17 20-52-1.log

app-2017-08-17 20-52-2.log

app-2017-08-17 20-52-3.log

app-2017-08-17 20-53-1.log

當app.log的size達到10KB,觸發第6次rollover,app.log被重新命名為app-2017-08-17 20-53-1.log。新的app.log被創建出來,用於寫入log。

總結:

1.max引數是與filePattern中的計數器%i配合起作用的,若filePattern為filePattern="logs/app-%d{yyyy-MM-dd}.log">,由於沒有設定%i計數器,max引數將不起作用。

2.max引數不是需要保留的檔案的最大個數。如情況3,日誌檔案date/time pattern不再符合filePattern時,計算器將被重置為1,日誌總個數超過了max的指定值。

可認為max引數規定了一定時間範圍內歸檔檔案的最大個數。

四、DeleteAction

DefaultRolloverStrategy制定了預設的rollover策略,通過max引數可控制一定時間範圍內歸檔的日誌檔案的最大個數。

Log4j 2.5 引入了DeleteAction,使使用者可以自己控制刪除哪些檔案,而不僅僅是通過DefaultRolloverStrategy的預設策略。

注意:通過DeleteAction可以刪除任何檔案,而不僅僅像DefaultRolloverStrategy那樣,刪除最舊的檔案,所以使用的時候需要謹慎!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
<Properties>
<Property name="baseDir">logs</Property>
</Properties>
<Appenders>
<RollingFile name="RollingFile" fileName="${baseDir}/app.log"
filePattern="${baseDir}/app-%d{yyyy-MM-dd}.log.gz">
<PatternLayout pattern="%d %p %c{1.} [%t] %m%n" />
<CronTriggeringPolicy schedule="0 0 0 * * ?"/>
<DefaultRolloverStrategy>
<Delete basePath="${baseDir}" maxDepth="2">
<IfFileName glob="*/app-*.log.gz" />
<IfLastModified age="60d" />
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
</Appenders>
<Loggers>
<Root level="error">
<AppenderRef ref="RollingFile"/>
</Root>
</Loggers>
</Configuration>

上述配置檔案中,Delete部分便是配置DeleteAction的刪除策略,指定了當觸發rollover時,刪除baseDir資料夾或其子檔案下面的檔名符合app-*.log.gz且距離最後的修改日期超過60天的檔案。

其中,basePath指定了掃描開始路徑,為baseDir資料夾。maxDepth指定了目錄掃描深度,為2表示掃描baseDir資料夾及其子資料夾。

IfFileName指定了檔名需滿足的條件,IfLastModified指定了檔案修改時間需要滿足的條件。

DeleteAction常用引數如下:

引數名
型別
描述
basePath String

必填。目錄掃描開始路徑。

maxDepth int

掃描的最大目錄深度。0表示basePath指定的檔案自身。Integer.MAX_VALUE表示掃描所有的目錄層。預設值為1,表示僅掃描basePath下的檔案。

testMode boolean

如果為true,實際的檔案不會被刪除,刪除檔案的資訊會列印到log4j2的INFO級別的log中。可使用此引數測試配置是否符合預測。預設為false。

pathConditions PathCondition[]

刪除檔案的過濾條件,滿足指定條件的檔案將會被刪除,可以指定一個或多個。

如果指定多個pathCondition,需要同時滿足。Conditions可以巢狀,當巢狀配置時,只有當滿足了外部的contion時,才能對內部的condition進行判斷。如果Conditions不是巢狀的,會可能以任意順序進行判斷。

Conditions也可以通過使用IfAll,IfAny,IfNot等類似於AND,OR,NOT的condition,實現複雜的condition。

  • IfFileName-判斷檔案的檔名是否滿足正則表示式或glob表示式
  • IfLastModified-判斷檔案的修改時間是否早於指定的duration
  • IfAccumulatedFileCount-判斷在遍歷檔案樹的時候,檔案個數是否超過了指定值
  • IfAccumulatedFileSize-判斷在遍歷檔案樹的時候,檔案的總大小是否超過了指定值
  • IfAll-判斷巢狀的condition是否都滿足
  • IfAny-判斷巢狀的condition是否有一個滿足
  • IfNot-判斷巢狀的condition是否不滿足

五、程式測試demo

public class HelloWorld {
public static void main(String[] args) {
Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
try{
//通過列印i,日誌檔案中數字越小代表越老
for(int i = 0; i < 
0.001291036605835