最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時(shí)間:2020年07月31日 09:24:47   作者:等你歸去來  
這篇文章主要介紹了log4j2 自動(dòng)刪除過期日志文件配置及實(shí)現(xiàn)原理解析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

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

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

<?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>
    <!-- 輸出到控制臺(tái) -->
    <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文件中位置存在沖突,如有問題,請(qǐng)注意調(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>
        <!-- 按天遞計(jì)算頻率 -->
        <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>
    <!-- 配置個(gè)例 -->
    <Logger name="com.xx.filter" level="info" />
  </Loggers>
</Configuration>

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

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

<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>

 如果還想多了解一點(diǎn)其運(yùn)行原理,就跟隨本文的腳步吧:

1.自動(dòng)清理大體運(yùn)行流程

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

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

  所以,刪除策略的核心是每一次添加日志時(shí)。代碼驗(yàn)證如下:

// 在每次添加日志時(shí)判定
  // 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();
    // 重點(diǎn):直接檢查是否需要 rollover, 如需要直接進(jìn)行
    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ā)策略判定是否需要進(jìn)行 rolling
    // 如需要, 則調(diào)用 rollover()
    if (triggeringPolicy.isTriggeringEvent(event)) {
      rollover();
    }
  }

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

2. log4j 日志滾動(dòng)

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

 // 1. 判斷是否是 觸發(fā)事件時(shí)機(jī)
  // 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, 是基于時(shí)間判斷的, 此處為每天一次
    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ā)器運(yùn)行 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);
          }
        }
        // 如果配置了異步器, 則使用異步進(jìn)行 rollover
        if (success && descriptor.getAsynchronous() != null) {
          LOGGER.debug("RollingFileManager executing async {}", descriptor.getAsynchronous());
          // CompositeAction, 使用異步線程池運(yùn)行用戶的 action
          asyncExecutor.execute(new AsyncAction(descriptor.getAsynchronous(), this));
          // 在異步運(yùn)行action期間,鎖是不會(huì)被釋放的,以避免線程安全問題
          // 直到異步任務(wù)完成,再主動(dòng)釋放鎖
          releaseRequired = false;
        }
        return true;
      }
      return false;
    } finally {
      if (releaseRequired) {
        semaphore.release();
      }
    }

  }

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

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

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

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

 // 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ù),同時(shí)清理掉大于 max 配置的日志文件
      // 如配置 max=5, 當(dāng)前只有4個(gè)滿足時(shí), 不會(huì)立即清理文件, 但也不會(huì)阻塞后續(xù)流程
      // 只要沒有出現(xiàn)錯(cuò)誤, fileIndex 不會(huì)小于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);
      }
    }
    // 進(jìn)入此區(qū)域即意味著,必然有文件需要滾動(dòng),重新命名了
    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ā)生文件重命名情況,即文件未被重命名未被滾動(dòng)
    // 該種情況應(yīng)該不太會(huì)發(fā)生
    if (currentFileName.equals(renameTo)) {
      LOGGER.warn("Attempt to rename file {} to itself will be ignored", currentFileName);
      return new RolloverDescriptionImpl(currentFileName, false, null, null);
    }
    // 新建一個(gè)重命令的 action, 返回待用
    final FileRenameAction renameAction = new FileRenameAction(new File(currentFileName), new File(renameTo),
          manager.isRenameEmptyFiles());
    // 異步處理器,會(huì)處理用戶配置的異步action,如本文配置的 DeleteAction
    // 它將會(huì)在稍后被提交到異步線程池中運(yùn)行
    final Action asyncAction = merge(compressAction, customActions, stopCustomActionsOnError);
    // 封裝Rollover返回, renameAction 是同步方法, 其他用戶配置的動(dòng)態(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 的方式進(jìn)行清理文件
    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) {
      // 針對(duì)未完成刪除的文件,繼續(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ù),這個(gè)參數(shù)是如何匹配的呢?比如我某個(gè)文件夾下有很歷史文件,是否都會(huì)匹配呢?

 // 文件查找規(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, 一般會(huì)受限于 MM-dd-yyyy-$i.log.gz
    String pattern = manager.getPatternProcessor().getPattern();
    // 此處會(huì)將時(shí)間替換為當(dāng)前, 然后按照此規(guī)則進(jìn)行匹配要處理的文件
    manager.getPatternProcessor().formatFileName(strSubstitutor, buf, NotANumber.NAN);
    return getEligibleFiles(buf.toString(), pattern, isAscending);
  }
  // 細(xì)節(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)精確
        // 只會(huì)刪除當(dāng)天或者在時(shí)間交替的時(shí)候刪除上一天的數(shù)據(jù)咯
        // 如果在這個(gè)時(shí)候進(jìn)行了重啟操作,就再也不會(huì)刪除此文件了
        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();
  }
  // 此處會(huì)將 各種格式的文件名,替換為當(dāng)前時(shí)間或者最后一次滾動(dòng)的文件的時(shí)間。所以匹配的時(shí)候,并不會(huì)匹配超時(shí)當(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 是一個(gè) Runnable 的實(shí)現(xiàn), 被直接提交到線程池運(yùn)行. AsyncAction -> AbstractAction -> Action -> Runnable

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

 // 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, 里面封裝了多個(gè) action
        return action.execute();
      } finally {
        // 任務(wù)執(zhí)行完成,才會(huì)釋放外部的鎖
        // 雖然不是很優(yōu)雅,但是很準(zhǔn)確很安全
        manager.semaphore.release();
      }
    }
    ...
  }

  // CompositeAction 封裝了多個(gè) 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)心的動(dòng)作。

 // CompositeAction 封裝了多個(gè) 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 做真正的刪除動(dòng)作
  // 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() 實(shí)現(xiàn), 添加到 collected 中
    final List<PathWithAttributes> sortedPaths = getSortedPaths();
    trace("Sorted paths:", sortedPaths);

    for (final PathWithAttributes element : sortedPaths) {
      try {
        // 依次調(diào)用 visitFile, 依次判斷是否需要?jiǎng)h除
        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
  }

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

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

5.真正的刪除

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

 // 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);
      // 遍歷所有條件,只要有一個(gè)不符合,即不進(jìn)行刪除。
      // 所以,所以條件是 AND 關(guān)系, 沒有 OR 關(guān)系
      // 如果想配置 OR 關(guān)系,只能配置多個(gè)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;
  }

  刪除策略配置比如:

<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>
      <!-- 按天遞計(jì)算頻率 -->
      <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>

  另外說明,之所以能夠無縫替換,是因?yàn)槔昧瞬煌瑢?shí)現(xiàn)版本的 org/slf4j/impl/StaticLoggerBinder.class, 而外部都使用 slf4j 接口定義實(shí)現(xiàn)的,比如 org.apache.logging.log4j:log4j-slf4j-impl 包的實(shí)現(xiàn)。

總結(jié)

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

相關(guān)文章

  • maven報(bào)錯(cuò):Failed to execute goal on project問題及解決

    maven報(bào)錯(cuò):Failed to execute goal on p

    這篇文章主要介紹了maven報(bào)錯(cuò):Failed to execute goal on project問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • maven子模塊相互依賴打包時(shí)報(bào)錯(cuò)找不到類的解決方案

    maven子模塊相互依賴打包時(shí)報(bào)錯(cuò)找不到類的解決方案

    本文主要介紹了maven子模塊相互依賴打包時(shí)報(bào)錯(cuò)找不到類的解決方案,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • JAVA實(shí)現(xiàn)DOC轉(zhuǎn)PDF的示例代碼

    JAVA實(shí)現(xiàn)DOC轉(zhuǎn)PDF的示例代碼

    Word作為目前主流的文本編輯軟件之一,功能十分強(qiáng)大,但是在傳輸?shù)臅r(shí)候不穩(wěn)定,那么如何從DOC轉(zhuǎn)PDF,本文就來介紹一下,感興趣的可以了解一下
    2021-08-08
  • 如何用Java實(shí)現(xiàn)排列組合算法

    如何用Java實(shí)現(xiàn)排列組合算法

    本文主要介紹了如何用Java實(shí)現(xiàn)排列組合算法,對(duì)算法感興趣的同學(xué),可以參考一下,理解其原理,并且試驗(yàn)一下。
    2021-05-05
  • 淺談SpringBoot @Autowired的兩種注入方式

    淺談SpringBoot @Autowired的兩種注入方式

    本文主要介紹了兩種SpringBoot @Autowired注入方式,具有一定的參考價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-06-06
  • java中自定義Spring Security權(quán)限控制管理示例(實(shí)戰(zhàn)篇)

    java中自定義Spring Security權(quán)限控制管理示例(實(shí)戰(zhàn)篇)

    本篇文章主要介紹了java中自定義Spring Security權(quán)限控制管理示例(實(shí)戰(zhàn)篇) ,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。
    2017-02-02
  • Java語言class類用法及泛化(詳解)

    Java語言class類用法及泛化(詳解)

    這篇文章主要介紹了Java語言class類用法及泛化(詳解),大家都知道Java程序在運(yùn)行過程中,對(duì)所有的對(duì)象今夕類型標(biāo)識(shí),也就是RTTI。這項(xiàng)信息記錄了每個(gè)對(duì)象所屬的類,需要的朋友可以參考下
    2015-07-07
  • java實(shí)現(xiàn)歸并排序算法

    java實(shí)現(xiàn)歸并排序算法

    在學(xué)習(xí)算法的過程中,我們難免會(huì)接觸很多和排序相關(guān)的算法??偠灾瑢?duì)于任何編程人員來說,基本的排序算法是必須要掌握的。那么現(xiàn)在我們將要進(jìn)行基本的歸并排序算法的講解
    2016-01-01
  • Java基礎(chǔ)之關(guān)鍵字final詳解

    Java基礎(chǔ)之關(guān)鍵字final詳解

    這篇文章主要介紹了Java基礎(chǔ)之關(guān)鍵字final詳解,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-05-05
  • java結(jié)合keytool如何實(shí)現(xiàn)非對(duì)稱加密與解密詳解

    java結(jié)合keytool如何實(shí)現(xiàn)非對(duì)稱加密與解密詳解

    這篇文章主要給大家介紹了關(guān)于java結(jié)合keytool如何實(shí)現(xiàn)非對(duì)稱加密與解密的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-08-08

最新評(píng)論

横山县| 遂川县| 资溪县| 奉节县| 常宁市| 临桂县| 昭苏县| 梅州市| 延寿县| 祁门县| 若羌县| 临清市| 凤庆县| 南康市| 灵石县| 苗栗县| 泰宁县| 青海省| 桂阳县| 玛曲县| 南雄市| 万荣县| 卫辉市| 濉溪县| 秀山| 花垣县| 万盛区| 常州市| 荔波县| 伊春市| 平原县| 泽州县| 长寿区| 石楼县| 双辽市| 宣恩县| 夏河县| 囊谦县| 巴南区| 楚雄市| 巴彦县|