激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - log4j2 自動刪除過期日志文件的配置及實現(xiàn)原理

log4j2 自動刪除過期日志文件的配置及實現(xiàn)原理

2020-07-31 14:54等你歸去來 Java教程

這篇文章主要介紹了log4j2 自動刪除過期日志文件配置及實現(xiàn)原理解析,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

  日志文件自動刪除功能必不可少,當(dāng)然你可以讓運維去做這事,只是這不地道。而日志組件是一個必備組件,讓其多做一件刪除的工作,無可厚非。本文就來探討下 log4j 的日志文件自動刪除實現(xiàn)吧。

0.自動刪除配置參考樣例: (log4j2.xml)

?
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?xml version="1.0" encoding="UTF-8" ?>
<Configuration status="warn" monitorInterval="30" strict="true"
        schema="Log4J-V2.2.xsd">
  <Properties>
    <Property name="log_level">info</Property>
  </Properties>
  <Appenders>
    <!-- 輸出到控制臺 -->
    <Console name="Console" target="SYSTEM_OUT">
      <ThresholdFilter level="${log_level}" onMatch="ACCEPT" onMismatch="DENY" />
      <PatternLayout pattern="%d{yyyy-MM-dd'T'HH:mm:ss.SSS} [%t] %p - %c - %m%n" />
    </Console>
    <!-- 與properties文件中位置存在沖突,如有問題,請注意調(diào)整 -->
    <RollingFile name="logFile" fileName="logs/app/test.log"
           filePattern="logs/app/history/test-%d{MM-dd-yyyy}-%i.log.gz">
      <ThresholdFilter level="${log_level}" onMatch="ACCEPT" onMismatch="DENY" />
      <PatternLayout pattern="%d{yyyy-MM-dd'T'HH:mm:ss.SSS} [%p] [%c:%L] -- %m%n" />
      <Policies>
        <!-- 按天遞計算頻率 -->
        <TimeBasedTriggeringPolicy interval="1" />
        <SizeBasedTriggeringPolicy size="500 MB" />
        <OnStartupTriggeringPolicy />
      </Policies>
      <!-- 刪除策略配置 -->
      <DefaultRolloverStrategy max="5">
        <Delete basePath="logs/app/history" maxDepth="1">
          <IfFileName glob="*.log.gz"/>
          <IfLastModified age="7d"/>
        </Delete>
        <Delete basePath="logs/app/history" maxDepth="1">
          <IfFileName glob="*.docx"/>
        </Delete>
        <Delete basePath="logs/app/history" maxDepth="1">
          <IfFileName glob="*.vsdx"/>
        </Delete>
      </DefaultRolloverStrategy>
    </RollingFile>
    <Async name="Async" bufferSize="2000" blocking="false">
      <AppenderRef ref="logFile"/>
    </Async>
  </Appenders>
 
  <Loggers>
    <Root level="${log_level}">
      <AppenderRef ref="Console" />
      <AppenderRef ref="Async" />
    </Root>
    <!-- 配置個例 -->
    <Logger name="com.xx.filter" level="info" />
  </Loggers>
</Configuration>

  如果僅想停留在使用層面,如上log4j2.xml配置文件足矣!

  不過,至少得注意一點,以上配置需要基于log4j2, 而如果你是 log4j1.x,則需要做下無縫升級:主要就是換下jar包版本,換個橋接包之類的,比如下參考配置:

?
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
      </dependency>
      <!-- 橋接:告訴commons logging使用Log4j2 -->
      <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>jcl-over-slf4j</artifactId>
        <version>1.7.26</version>
      </dependency>
 
      <!-- 此處老版本,需注釋掉 -->
      <!--<dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
      </dependency>-->
 
      <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-compress</artifactId>
        <version>1.10</version>
      </dependency>
      <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.8.2</version>
      </dependency>
      <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <version>2.8.2</version>
      </dependency>
      <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.8.2</version>
      </dependency>
      <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-web</artifactId>
        <version>2.8.2</version>
      </dependency>

 如果還想多了解一點其運行原理,就跟隨本文的腳步吧:

1.自動清理大體運行流程

  自動刪除工作的運行原理大體流程如下。(大抵都是如此)

    1. 加載log4j2.xml配置文件;
    2. 讀取appenders,并添加到log4j上下文中;
    3. 加載 policy, 加載 rollover 配置;
    4. 寫入日志時判斷是否滿足rollover配置, 默認(rèn)是一天運行一次, 可自行添加各種運行測試, 比如大小、啟動時;

  所以,刪除策略的核心是每一次添加日志時。代碼驗證如下:

?
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
26
27
28
29
30
31
32
33
34
35
36
37
// 在每次添加日志時判定
  // org.apache.logging.log4j.core.appender.RollingRandomAccessFileAppender#append
  /**
   * Write the log entry rolling over the file when required.
   *
   * @param event The LogEvent.
   */
  @Override
  public void append(final LogEvent event) {
    final RollingRandomAccessFileManager manager = getManager();
    // 重點:直接檢查是否需要 rollover, 如需要直接進行
    manager.checkRollover(event);
 
    // Leverage the nice batching behaviour of async Loggers/Appenders:
    // we can signal the file manager that it needs to flush the buffer
    // to disk at the end of a batch.
    // From a user's point of view, this means that all log events are
    // _always_ available in the log file, without incurring the overhead
    // of immediateFlush=true.
    manager.setEndOfBatch(event.isEndOfBatch()); // FIXME manager's EndOfBatch threadlocal can be deleted
 
    // LOG4J2-1292 utilize gc-free Layout.encode() method: taken care of in superclass
    super.append(event);
  }
 
  // org.apache.logging.log4j.core.appender.rolling.RollingFileManager#checkRollover
  /**
   * Determines if a rollover should occur.
   * @param event The LogEvent.
   */
  public synchronized void checkRollover(final LogEvent event) {
    // 由各觸發(fā)策略判定是否需要進行 rolling
    // 如需要, 則調(diào)用 rollover()
    if (triggeringPolicy.isTriggeringEvent(event)) {
      rollover();
    }
  }

  所以,何時進行刪除?答案是在適當(dāng)?shù)臅r機,這個時機可以是任意時候。

2. log4j 日志滾動

  日志滾動,可以是重命名,也可以是刪除文件。但總體判斷是否可觸發(fā)滾動的前提是一致的。我們這里主要關(guān)注文件刪除。我們以時間作為依據(jù)看下判斷過程。

?
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// 1. 判斷是否是 觸發(fā)事件時機
 // org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy#isTriggeringEvent
 /**
  * Determines whether a rollover should occur.
  * @param event  A reference to the currently event.
  * @return true if a rollover should occur.
  */
 @Override
 public boolean isTriggeringEvent(final LogEvent event) {
   if (manager.getFileSize() == 0) {
     return false;
   }
   final long nowMillis = event.getTimeMillis();
   // TimeBasedTriggeringPolicy, 是基于時間判斷的, 此處為每天一次
   if (nowMillis >= nextRolloverMillis) {
     nextRolloverMillis = manager.getPatternProcessor().getNextTime(nowMillis, interval, modulate);
     return true;
   }
   return false;
 }
 // org.apache.logging.log4j.core.appender.rolling.RollingFileManager#rollover()
 public synchronized void rollover() {
   if (!hasOutputStream()) {
     return;
   }
   // strategy 是xml配置的策略
   if (rollover(rolloverStrategy)) {
     try {
       size = 0;
       initialTime = System.currentTimeMillis();
       createFileAfterRollover();
     } catch (final IOException e) {
       logError("Failed to create file after rollover", e);
     }
   }
 }
 // RollingFileManager 統(tǒng)一管理觸發(fā)器
 // org.apache.logging.log4j.core.appender.rolling.RollingFileManager#rollover
 private boolean rollover(final RolloverStrategy strategy) {
 
   boolean releaseRequired = false;
   try {
     // Block until the asynchronous operation is completed.
     // 上鎖保證線程安全
     semaphore.acquire();
     releaseRequired = true;
   } catch (final InterruptedException e) {
     logError("Thread interrupted while attempting to check rollover", e);
     return false;
   }
 
   boolean success = true;
 
   try {
     // 由各觸發(fā)器運行 rollover 邏輯
     final RolloverDescription descriptor = strategy.rollover(this);
     if (descriptor != null) {
       writeFooter();
       closeOutputStream();
       if (descriptor.getSynchronous() != null) {
         LOGGER.debug("RollingFileManager executing synchronous {}", descriptor.getSynchronous());
         try {
           // 先使用同步方法,改名,然后再使用異步方法操作更多
           success = descriptor.getSynchronous().execute();
         } catch (final Exception ex) {
           success = false;
           logError("Caught error in synchronous task", ex);
         }
       }
       // 如果配置了異步器, 則使用異步進行 rollover
       if (success && descriptor.getAsynchronous() != null) {
         LOGGER.debug("RollingFileManager executing async {}", descriptor.getAsynchronous());
         // CompositeAction, 使用異步線程池運行用戶的 action
         asyncExecutor.execute(new AsyncAction(descriptor.getAsynchronous(), this));
         // 在異步運行action期間,鎖是不會被釋放的,以避免線程安全問題
         // 直到異步任務(wù)完成,再主動釋放鎖
         releaseRequired = false;
       }
       return true;
     }
     return false;
   } finally {
     if (releaseRequired) {
       semaphore.release();
     }
   }
 
 }

  此處滾動有兩個處理點,1. 每個滾動策略可以自行處理業(yè)務(wù); 2. RollingFileManager 統(tǒng)一管理觸發(fā)同步和異步的滾動action;

3. DefaultRolloverStrategy 默認(rèn)滾動策略驅(qū)動

  DefaultRolloverStrategy 作為一個默認(rèn)的滾動策略實現(xiàn),可以配置多個 Action, 然后處理刪除操作。

  刪除有兩種方式: 1. 當(dāng)次滾動的文件數(shù)過多,會立即進行刪除; 2. 配置單獨的 DeleteAction, 根據(jù)配置的具體策略進行刪除。(但該Action只會被返回給外部調(diào)用,自身則不會執(zhí)行)

?
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy#rollover
 /**
  * Performs the rollover.
  *
  * @param manager The RollingFileManager name for current active log file.
  * @return A RolloverDescription.
  * @throws SecurityException if an error occurs.
  */
 @Override
 public RolloverDescription rollover(final RollingFileManager manager) throws SecurityException {
   int fileIndex;
   // 默認(rèn) minIndex=1
   if (minIndex == Integer.MIN_VALUE) {
     final SortedMap<Integer, Path> eligibleFiles = getEligibleFiles(manager);
     fileIndex = eligibleFiles.size() > 0 ? eligibleFiles.lastKey() + 1 : 1;
   } else {
     if (maxIndex < 0) {
       return null;
     }
     final long startNanos = System.nanoTime();
     // 刪除case1: 獲取符合條件的文件數(shù),同時清理掉大于 max 配置的日志文件
     // 如配置 max=5, 當(dāng)前只有4個滿足時, 不會立即清理文件, 但也不會阻塞后續(xù)流程
     // 只要沒有出現(xiàn)錯誤, fileIndex 不會小于0
     fileIndex = purge(minIndex, maxIndex, manager);
     if (fileIndex < 0) {
       return null;
     }
     if (LOGGER.isTraceEnabled()) {
       final double durationMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos);
       LOGGER.trace("DefaultRolloverStrategy.purge() took {} milliseconds", durationMillis);
     }
   }
   // 進入此區(qū)域即意味著,必然有文件需要滾動,重新命名了
   final StringBuilder buf = new StringBuilder(255);
   manager.getPatternProcessor().formatFileName(strSubstitutor, buf, fileIndex);
   final String currentFileName = manager.getFileName();
 
   String renameTo = buf.toString();
   final String compressedName = renameTo;
   Action compressAction = null;
 
   FileExtension fileExtension = manager.getFileExtension();
   if (fileExtension != null) {
     renameTo = renameTo.substring(0, renameTo.length() - fileExtension.length());
     compressAction = fileExtension.createCompressAction(renameTo, compressedName,
         true, compressionLevel);
   }
   // 未發(fā)生文件重命名情況,即文件未被重命名未被滾動
   // 該種情況應(yīng)該不太會發(fā)生
   if (currentFileName.equals(renameTo)) {
     LOGGER.warn("Attempt to rename file {} to itself will be ignored", currentFileName);
     return new RolloverDescriptionImpl(currentFileName, false, null, null);
   }
   // 新建一個重命令的 action, 返回待用
   final FileRenameAction renameAction = new FileRenameAction(new File(currentFileName), new File(renameTo),
         manager.isRenameEmptyFiles());
   // 異步處理器,會處理用戶配置的異步action,如本文配置的 DeleteAction
   // 它將會在稍后被提交到異步線程池中運行
   final Action asyncAction = merge(compressAction, customActions, stopCustomActionsOnError);
   // 封裝Rollover返回, renameAction 是同步方法, 其他用戶配置的動態(tài)action 則是異步方法
   // 刪除case2: 封裝異步返回action
   return new RolloverDescriptionImpl(currentFileName, false, renameAction, asyncAction);
 }
 private int purge(final int lowIndex, final int highIndex, final RollingFileManager manager) {
   // 默認(rèn)使用 accending 的方式進行清理文件
   return useMax ? purgeAscending(lowIndex, highIndex, manager) : purgeDescending(lowIndex, highIndex, manager);
 }
 // org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy#purgeAscending
 /**
  * Purges and renames old log files in preparation for rollover. The oldest file will have the smallest index, the
  * newest the highest.
  *
  * @param lowIndex low index. Log file associated with low index will be deleted if needed.
  * @param highIndex high index.
  * @param manager The RollingFileManager
  * @return true if purge was successful and rollover should be attempted.
  */
 private int purgeAscending(final int lowIndex, final int highIndex, final RollingFileManager manager) {
   final SortedMap<Integer, Path> eligibleFiles = getEligibleFiles(manager);
   final int maxFiles = highIndex - lowIndex + 1;
 
   boolean renameFiles = false;
   // 依次迭代 eligibleFiles, 刪除
   while (eligibleFiles.size() >= maxFiles) {
     try {
       LOGGER.debug("Eligible files: {}", eligibleFiles);
       Integer key = eligibleFiles.firstKey();
       LOGGER.debug("Deleting {}", eligibleFiles.get(key).toFile().getAbsolutePath());
       // 調(diào)用nio的接口刪除文件
       Files.delete(eligibleFiles.get(key));
       eligibleFiles.remove(key);
       renameFiles = true;
     } catch (IOException ioe) {
       LOGGER.error("Unable to delete {}, {}", eligibleFiles.firstKey(), ioe.getMessage(), ioe);
       break;
     }
   }
   final StringBuilder buf = new StringBuilder();
   if (renameFiles) {
     // 針對未完成刪除的文件,繼續(xù)處理
     // 比如使用 匹配的方式匹配文件, 則不能被正常刪除
     // 還有些未超過maxFiles的文件
     for (Map.Entry<Integer, Path> entry : eligibleFiles.entrySet()) {
       buf.setLength(0);
       // LOG4J2-531: directory scan & rollover must use same format
       manager.getPatternProcessor().formatFileName(strSubstitutor, buf, entry.getKey() - 1);
       String currentName = entry.getValue().toFile().getName();
       String renameTo = buf.toString();
       int suffixLength = suffixLength(renameTo);
       if (suffixLength > 0 && suffixLength(currentName) == 0) {
         renameTo = renameTo.substring(0, renameTo.length() - suffixLength);
       }
       Action action = new FileRenameAction(entry.getValue().toFile(), new File(renameTo), true);
       try {
         LOGGER.debug("DefaultRolloverStrategy.purgeAscending executing {}", action);
         if (!action.execute()) {
           return -1;
         }
       } catch (final Exception ex) {
         LOGGER.warn("Exception during purge in RollingFileAppender", ex);
         return -1;
       }
     }
   }
   // 此處返回的 findIndex 一定是 >=0 的
   return eligibleFiles.size() > 0 ?
       (eligibleFiles.lastKey() < highIndex ? eligibleFiles.lastKey() + 1 : highIndex) : lowIndex;
 }

4. 符合過濾條件的文件查找

  當(dāng)配置了 max 參數(shù),這個參數(shù)是如何匹配的呢?比如我某個文件夾下有很歷史文件,是否都會匹配呢?

?
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// 文件查找規(guī)則
 // org.apache.logging.log4j.core.appender.rolling.AbstractRolloverStrategy#getEligibleFiles
 protected SortedMap<Integer, Path> getEligibleFiles(final RollingFileManager manager) {
   return getEligibleFiles(manager, true);
 }
 protected SortedMap<Integer, Path> getEligibleFiles(final RollingFileManager manager,
                           final boolean isAscending) {
   final StringBuilder buf = new StringBuilder();
   // 此處的pattern 即是在appender上配置的 filePattern, 一般會受限于 MM-dd-yyyy-$i.log.gz
   String pattern = manager.getPatternProcessor().getPattern();
   // 此處會將時間替換為當(dāng)前, 然后按照此規(guī)則進行匹配要處理的文件
   manager.getPatternProcessor().formatFileName(strSubstitutor, buf, NotANumber.NAN);
   return getEligibleFiles(buf.toString(), pattern, isAscending);
 }
 // 細節(jié)匹配要處理的文件
 protected SortedMap<Integer, Path> getEligibleFiles(String path, String logfilePattern, boolean isAscending) {
   TreeMap<Integer, Path> eligibleFiles = new TreeMap<>();
   File file = new File(path);
   File parent = file.getParentFile();
   if (parent == null) {
     parent = new File(".");
   } else {
     parent.mkdirs();
   }
   if (!logfilePattern.contains("%i")) {
     return eligibleFiles;
   }
   Path dir = parent.toPath();
   String fileName = file.getName();
   int suffixLength = suffixLength(fileName);
   if (suffixLength > 0) {
     fileName = fileName.substring(0, fileName.length() - suffixLength) + ".*";
   }
   String filePattern = fileName.replace(NotANumber.VALUE, "(\\d+)");
   Pattern pattern = Pattern.compile(filePattern);
 
   try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
     for (Path entry: stream) {
       // 該匹配相當(dāng)精確
       // 只會刪除當(dāng)天或者在時間交替的時候刪除上一天的數(shù)據(jù)咯
       // 如果在這個時候進行了重啟操作,就再也不會刪除此文件了
       Matcher matcher = pattern.matcher(entry.toFile().getName());
       if (matcher.matches()) {
         Integer index = Integer.parseInt(matcher.group(1));
         eligibleFiles.put(index, entry);
       }
     }
   } catch (IOException ioe) {
     throw new LoggingException("Error reading folder " + dir + " " + ioe.getMessage(), ioe);
   }
   return isAscending? eligibleFiles : eligibleFiles.descendingMap();
 }
 // 此處會將 各種格式的文件名,替換為當(dāng)前時間或者最后一次滾動的文件的時間。所以匹配的時候,并不會匹配超時當(dāng)前認(rèn)知范圍的文件
 /**
  * Formats file name.
  * @param subst The StrSubstitutor.
  * @param buf string buffer to which formatted file name is appended, may not be null.
  * @param obj object to be evaluated in formatting, may not be null.
  */
 public final void formatFileName(final StrSubstitutor subst, final StringBuilder buf, final boolean useCurrentTime,
                  final Object obj) {
   // LOG4J2-628: we deliberately use System time, not the log4j.Clock time
   // for creating the file name of rolled-over files.
   final long time = useCurrentTime && currentFileTime != 0 ? currentFileTime :
       prevFileTime != 0 ? prevFileTime : System.currentTimeMillis();
   formatFileName(buf, new Date(time), obj);
   final LogEvent event = new Log4jLogEvent.Builder().setTimeMillis(time).build();
   final String fileName = subst.replace(event, buf);
   buf.setLength(0);
   buf.append(fileName);
 }

  AsyncAction 是一個 Runnable 的實現(xiàn), 被直接提交到線程池運行. AsyncAction -> AbstractAction -> Action -> Runnable

  它是一個統(tǒng)一管理異步Action的包裝,主要是管理鎖和異常類操作。

?
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// org.apache.logging.log4j.core.appender.rolling.RollingFileManager.AsyncAction
 /**
  * Performs actions asynchronously.
  */
 private static class AsyncAction extends AbstractAction {
 
   private final Action action;
   private final RollingFileManager manager;
 
   /**
    * Constructor.
    * @param act The action to perform.
    * @param manager The manager.
    */
   public AsyncAction(final Action act, final RollingFileManager manager) {
     this.action = act;
     this.manager = manager;
   }
 
   /**
    * Executes an action.
    *
    * @return true if action was successful. A return value of false will cause
    *     the rollover to be aborted if possible.
    * @throws java.io.IOException if IO error, a thrown exception will cause the rollover
    *               to be aborted if possible.
    */
   @Override
   public boolean execute() throws IOException {
     try {
       // 門面調(diào)用 action.execute(), 一般是調(diào)用 CompositeAction, 里面封裝了多個 action
       return action.execute();
     } finally {
       // 任務(wù)執(zhí)行完成,才會釋放外部的鎖
       // 雖然不是很優(yōu)雅,但是很準(zhǔn)確很安全
       manager.semaphore.release();
     }
   }
   ...
 }
 
 // CompositeAction 封裝了多個 action 處理
 // org.apache.logging.log4j.core.appender.rolling.action.CompositeAction#run
 /**
  * Execute sequence of actions.
  *
  * @return true if all actions were successful.
  * @throws IOException on IO error.
  */
 @Override
 public boolean execute() throws IOException {
   if (stopOnError) {
     // 依次調(diào)用action
     for (final Action action : actions) {
       if (!action.execute()) {
         return false;
       }
     }
 
     return true;
   }
   boolean status = true;
   IOException exception = null;
 
   for (final Action action : actions) {
     try {
       status &= action.execute();
     } catch (final IOException ex) {
       status = false;
 
       if (exception == null) {
         exception = ex;
       }
     }
   }
 
   if (exception != null) {
     throw exception;
   }
 
   return status;
 }

  DeleteAction是我們真正關(guān)心的動作。

?
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// CompositeAction 封裝了多個 action 處理
 // org.apache.logging.log4j.core.appender.rolling.action.CompositeAction#run
 /**
  * Execute sequence of actions.
  *
  * @return true if all actions were successful.
  * @throws IOException on IO error.
  */
 @Override
 public boolean execute() throws IOException {
   if (stopOnError) {
     // 依次調(diào)用action
     for (final Action action : actions) {
       if (!action.execute()) {
         return false;
       }
     }
 
     return true;
   }
   boolean status = true;
   IOException exception = null;
 
   for (final Action action : actions) {
     try {
       status &= action.execute();
     } catch (final IOException ex) {
       status = false;
 
       if (exception == null) {
         exception = ex;
       }
     }
   }
 
   if (exception != null) {
     throw exception;
   }
 
   return status;
 }
 
 // DeleteAction 做真正的刪除動作
 // org.apache.logging.log4j.core.appender.rolling.action.DeleteAction#execute()
 @Override
 public boolean execute() throws IOException {
   // 如果沒有script配置,則直接委托父類處理
   return scriptCondition != null ? executeScript() : super.execute();
 }
 org.apache.logging.log4j.core.appender.rolling.action.AbstractPathAction#execute()
 @Override
 public boolean execute() throws IOException {
   // 根據(jù)指定的basePath, 和過濾條件,選擇相關(guān)文件
   // 調(diào)用 DeleteAction 的 createFileVisitor(), 返回 DeletingVisitor
   return execute(createFileVisitor(getBasePath(), pathConditions));
 }
 // org.apache.logging.log4j.core.appender.rolling.action.DeleteAction#execute(java.nio.file.FileVisitor<java.nio.file.Path>)
 @Override
 public boolean execute(final FileVisitor<Path> visitor) throws IOException {
   // 根據(jù)maxDepth設(shè)置,遍歷所有可能的文件路徑
   // 使用 Files.walkFileTree() 實現(xiàn), 添加到 collected 中
   final List<PathWithAttributes> sortedPaths = getSortedPaths();
   trace("Sorted paths:", sortedPaths);
 
   for (final PathWithAttributes element : sortedPaths) {
     try {
       // 依次調(diào)用 visitFile, 依次判斷是否需要刪除
       visitor.visitFile(element.getPath(), element.getAttributes());
     } catch (final IOException ioex) {
       LOGGER.error("Error in post-rollover Delete when visiting {}", element.getPath(), ioex);
       visitor.visitFileFailed(element.getPath(), ioex);
     }
   }
   // TODO return (visitor.success || ignoreProcessingFailure)
   return true; // do not abort rollover even if processing failed
 }

  最終,即和想像的一樣:找到要查找的文件夾,遍歷各文件,用多個條件判斷是否滿足。刪除符合條件的文件。

  只是這其中注意的點:如何刪除文件的線程安全性;如何保證刪除工作不影響業(yè)務(wù)線程;很常見的鎖和多線程的應(yīng)用。

5.真正的刪除

  真正的刪除動作就是在DeleteAction中配置的,但上面可以看它是調(diào)用visitor的visitFile方法,所以有必要看看是如何真正處理刪除的。(實際上前面在purge時已經(jīng)做過一次刪除操作了,所以別被兩個點迷惑了,建議盡量只依賴于Delete配置,可以將外部max設(shè)置很大以避免兩處生效)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// org.apache.logging.log4j.core.appender.rolling.action.DeletingVisitor#visitFile
 @Override
 public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
   for (final PathCondition pathFilter : pathConditions) {
     final Path relative = basePath.relativize(file);
     // 遍歷所有條件,只要有一個不符合,即不進行刪除。
     // 所以,所以條件是 AND 關(guān)系, 沒有 OR 關(guān)系
     // 如果想配置 OR 關(guān)系,只能配置多個DELETE
     if (!pathFilter.accept(basePath, relative, attrs)) {
       LOGGER.trace("Not deleting base={}, relative={}", basePath, relative);
       return FileVisitResult.CONTINUE;
     }
   }
   // 直接刪除文件
   if (isTestMode()) {
     LOGGER.info("Deleting {} (TEST MODE: file not actually deleted)", file);
   } else {
     delete(file);
   }
   return FileVisitResult.CONTINUE;
 }

  刪除策略配置比如:

?
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
26
<RollingFile name="logFile" fileName="logs/app/test.log"
         filePattern="logs/app/history/test-%d{MM-dd-yyyy}-%i.log.gz">
    <ThresholdFilter level="${log_level}" onMatch="ACCEPT" onMismatch="DENY" />
    <PatternLayout pattern="%d{yyyy-MM-dd'T'HH:mm:ss.SSS} [%p] [%c:%L] -- %m%n" />
    <Policies>
      <!-- 按天遞計算頻率 -->
      <TimeBasedTriggeringPolicy interval="1" />
      <SizeBasedTriggeringPolicy size="500 MB" />
      <OnStartupTriggeringPolicy />
    </Policies>
    <!-- 刪除策略配置 -->
    <DefaultRolloverStrategy max="5000">
      <Delete basePath="logs/app/history" maxDepth="1">
        <!-- 配置且關(guān)系 -->
        <IfFileName glob="*.log.gz"/>
        <IfLastModified age="7d"/>
      </Delete>
      <!-- 配置或關(guān)系 -->
      <Delete basePath="logs/app/history" maxDepth="1">
        <IfFileName glob="*.docx"/>
      </Delete>
      <Delete basePath="logs/app/history" maxDepth="1">
        <IfFileName glob="*.vsdx"/>
      </Delete>
    </DefaultRolloverStrategy>
  </RollingFile>

  另外說明,之所以能夠無縫替換,是因為利用了不同實現(xiàn)版本的 org/slf4j/impl/StaticLoggerBinder.class, 而外部都使用 slf4j 接口定義實現(xiàn)的,比如 org.apache.logging.log4j:log4j-slf4j-impl 包的實現(xiàn)。

總結(jié)

到此這篇關(guān)于log4j2 自動刪除過期日志文件的配置及實現(xiàn)原理解析的文章就介紹到這了,更多相關(guān)log4j2自動刪除過期日志文件內(nèi)容請搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!

原文鏈接:https://www.cnblogs.com/yougewe/p/13407812.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产青草视频在线观看 | av电影网站在线 | 一级成人欧美一区在线观看 | 国产精品一区二区视频 | 一区二区三区小视频 | 国产做爰全免费的视频黑人 | 把娇妻调教成暴露狂 | 亚洲天堂在线电影 | 国产九色在线播放九色 | 久久免费看片 | 黄色免费小视频网站 | 久久99精品久久久久久国产越南 | 日本欧美一区二区三区在线播 | 日韩激情一区 | 国产精品欧美日韩一区二区 | 亚洲一区二区在线免费 | freexxx69性欧美hd | 一区免费| 国产品久久 | 超碰97最新| 91伊人久久| 欧美三级欧美成人高清www | 亚洲一区免费观看 | 斗罗破苍穹在线观看免费完整观看 | 久久视频在线免费观看 | 成年免费看 | 一及毛片视频 | av老司机久久 | 天天看成人免费毛片视频 | 午夜视频在线看 | 成人激情久久 | av电影免费看 | 日韩精品中文字幕一区 | 日本免费一区二区三区四区 | 欧美色视频免费 | 黄色大片网 | 亚洲视频成人在线 | 精品久久中文网址 | 思思久而久而蕉人 | 欧美无极品 | 黄污在线看 |