logback系列之三:輸出日誌到文件(滾動)

實際生產中,天天都有大量的日誌生成,單個文件(FileAppender)已經不能知足要求,RollingFileAppender繼承了FileAppender,並提供了更多的功能: 
java

  • 天天生成一個日誌文件安全

  • 將前一天的日誌重命名爲包含日期的格式app

  • 根據須要,刪除過時歷史日誌異步

配置 

logback系列之二:輸出日誌到文件相似,改動的地方: 

1. logback[-test].xml文件: 
async

Java代碼  源碼分析

  1. <appender name="rollingAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">  
        <file>/logs/heuristic.log</file>  
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
            <fileNamePattern>/logs/heuristic-%d{yyyy-MM-dd}.log</fileNamePattern>  
            <maxHistory>30</maxHistory>  
        </rollingPolicy>  
        <encoder><!-- 必須指定,不然不會往文件輸出內容 -->  
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n</pattern>  
        </encoder>  
        <append>false</append>  
        <prudent>false</prudent>  
    </appender>  
      
    <root level="DEBUG">  
        <appender-ref ref="rollingAppender" />  
    </root>


調用測試類的方法,生成heuristic-2013-11-16.log文件。 

源碼分析 

a. 若是指定了file屬性,當天的文件名爲file屬性值(/logs/heuristic.log): 

ch.qos.logback.core.rolling.RollingFileAppender類 
測試

Java代碼  this

  1. public void start() {  
        ...  
        currentlyActiveFile = new File(getFile()); // 獲取日誌文件名  
    }  
      
    public String getFile() {  
        return rollingPolicy.getActiveFileName(); // 從滾動策略獲取  
    }


上面logback[-test].xml的rollingPolicy配置爲 
ch.qos.logback.core.rolling.TimeBasedRollingPolicy類 

看下實現: 
spa

Java代碼  日誌

  1. public String getActiveFileName() {  
      String parentsRawFileProperty = getParentsRawFileProperty();  
      if (parentsRawFileProperty != null) { // logback.xml指定了file屬性  
        return parentsRawFileProperty; // 使用file值  
      } else {  
        return timeBasedFileNamingAndTriggeringPolicy  
            .getCurrentPeriodsFileNameWithoutCompressionSuffix();  
      }  
    }  
      
    public String getParentsRawFileProperty() {  
      return parent.rawFileProperty();  
    }



ch.qos.logback.core.FileAppender類 

Java代碼  

  1. final public String rawFileProperty() {  
      return fileName;  
    }


b必須指定TriggeringPolicy<E>和RollingPolicy,不然不會打印日誌: 

看下 
ch.qos.logback.core.rolling.RollingFileAppender類: 

Java代碼  

  1. public void start() {  
      if (triggeringPolicy == null) {  
        addWarn("No TriggeringPolicy was set for the RollingFileAppender named "  
            + getName());  
        addWarn("For more information, please visit "+CODES_URL+"#rfa_no_tp");  
        return;  
      }  
      
      if (rollingPolicy == null) {  
        addError("No RollingPolicy was set for the RollingFileAppender named "  
            + getName());  
        addError("For more information, please visit "+CODES_URL+"rfa_no_rp");  
        return;  
      }


TimeBasedRollingPolicy類實現了上面的兩個接口,triggeringPolicy和rollingPolicy都指向TimeBasedRollingPolicy的實例對象。 

c. 若是file指定,前一天的文件名改成fileNamePattern的值(/logs/heuristic-2013-11-15.log)。 

經過設置TimeBasedRollingPolicy的maxHistory屬性,能夠控制已產生日誌的數量。若是maxHistory設置爲30,那麼超過30天的log文件會被自動刪除,這是經過RollingFileAppender的rollover()方法來實現的。 

該方法會調用 
ch.qos.logback.core.rolling.TimeBasedRollingPolicy類 
的rollover方法: 

Java代碼  

  1. public void rollover() throws RolloverFailure {  
      // 當此方法調用時,假定前一天日誌文件已經關閉  
      String elapsedPeriodsFileName = timeBasedFileNamingAndTriggeringPolicy  
          .getElapsedPeriodsFileName();  
      
      String elpasedPeriodStem = FileFilterUtil.afterLastSlash(elapsedPeriodsFileName);  
      
      if (compressionMode == CompressionMode.NONE) {  
        if (getParentsRawFileProperty() != null) {  
          renameUtil.rename(getParentsRawFileProperty(), elapsedPeriodsFileName); // file指定,重命名爲fileNamePattern格式。若是目標文件存在,則重命名失敗  
        } // else { nothing to do if CompressionMode == NONE and parentsRawFileProperty == null }  
      } else {  
        if (getParentsRawFileProperty() == null) {  
          future = asyncCompress(elapsedPeriodsFileName, elapsedPeriodsFileName, elpasedPeriodStem); // file未指定,異步壓縮  
        } else {  
          future = renamedRawAndAsyncCompress(elapsedPeriodsFileName, elpasedPeriodStem); // file指定,重命名並異步壓縮  
        }  
      }  
      
      if (archiveRemover != null) {  
        archiveRemover.clean(new Date(timeBasedFileNamingAndTriggeringPolicy.getCurrentTime())); // 刪除歷史日誌  
      }  
    }



默認的archiveRemover爲ch.qos.logback.core.rolling.helper.DefaultArchiveRemover抽象類: 

Java代碼  

public void clean(Date now) {  
  long nowInMillis = now.getTime();  
  int periodsElapsed = computeElapsedPeriodsSinceLastClean(nowInMillis);  
  lastHeartBeat = nowInMillis;  
  if (periodsElapsed > 1) {  
    addInfo("periodsElapsed = " + periodsElapsed);  
  }  
  // 從待刪除日誌的終止日期開始,每次回退一天,刪除過時日誌  
  for (int i = 0; i < periodsElapsed; i++) {  
    cleanByPeriodOffset(now, periodOffsetForDeletionTarget - i);  
  }  
}  
  
public void setMaxHistory(int maxHistory) {  
  // 待刪除日誌的終止日期:回退(maxHistory+1)天  
  this.periodOffsetForDeletionTarget = -maxHistory - 1;  
}
    cleanByPeriodOffset爲抽象方法,這裏交由 
    ch.qos.logback.core.rolling.helper.TimeBasedArchiveRemover子類 實現: 

Java代碼  

  1. protected void cleanByPeriodOffset(Date now, int periodOffset) {  
      Date date2delete = rc.getRelativeDate(now, periodOffset); // 計算須要刪除日誌的日期  
      String filename = fileNamePattern.convert(date2delete);  
      File file2Delete = new File(filename);  
      if (file2Delete.exists() && file2Delete.isFile()) {  
        file2Delete.delete(); // 刪除文件  
        addInfo("deleting " + file2Delete);  
        if (parentClean) {  
          removeFolderIfEmpty(file2Delete.getParentFile());  
        }  
      }  
    }



d. 若是要啓用壓縮,須要將fileNamePattern的後綴名設置爲壓縮格式,如:/logs/heuristic-%d{yyyy-MM-dd}.zip。 

看下ch.qos.logback.core.rolling.RollingPolicyBase類相關的代碼: 

Java代碼  

protected void determineCompressionMode() {  
  if (fileNamePatternStr.endsWith(".gz")) {  
    addInfo("Will use gz compression");  
    compressionMode = CompressionMode.GZ;  
  } else if (fileNamePatternStr.endsWith(".zip")) {  
    addInfo("Will use zip compression");  
    compressionMode = CompressionMode.ZIP;  
  } else { // 若是後綴名不是.gz或.zip,不會啓用壓縮  
    addInfo("No compression will be used");  
    compressionMode = CompressionMode.NONE;  
  }  
}
    e. RollingFileAppender保證append的值爲true,這樣當天先寫入的日誌內容就不會丟失  

    ch.qos.logback.core.rolling.RollingFileAppender類 的start()方法: 

Java代碼  

  1. if (!append) { // append爲false時  
      addWarn("Append mode is mandatory for RollingFileAppender");  
      append = true; // 改爲true  
    }



fprudent模式不支持file設定;若是啓用了壓縮,logback將不會記錄日誌 

ch.qos.logback.core.rolling.RollingFileAppender類 
的start()方法: 

Java代碼  

  1. if (isPrudent()) { // 安全模式,但效率低  
      if (rawFileProperty() != null) {  
        addWarn("Setting \"File\" property to null on account of prudent mode");  
        setFile(null); // 取消file屬性設置  
      }  
      if (rollingPolicy.getCompressionMode() != CompressionMode.NONE) { // 啓用了壓縮  
        addError("Compression is not supported in prudent mode. Aborting");  
        return; // 返回,不會進行後面的記錄日誌操做  
      }  
    }
相關文章
相關標籤/搜索