實際生產中,天天都有大量的日誌生成,單個文件(FileAppender)已經不能知足要求,RollingFileAppender繼承了FileAppender,並提供了更多的功能:
java
天天生成一個日誌文件安全
將前一天的日誌重命名爲包含日期的格式app
根據須要,刪除過時歷史日誌異步
配置
和logback系列之二:輸出日誌到文件相似,改動的地方:
1. logback[-test].xml文件:
async
Java代碼 源碼分析
<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
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代碼 日誌
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代碼
final public String rawFileProperty() { return fileName; }
b. 必須指定TriggeringPolicy<E>和RollingPolicy,不然不會打印日誌:
看下
ch.qos.logback.core.rolling.RollingFileAppender類:
Java代碼
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代碼
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; }
Java代碼
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; } }
Java代碼
if (!append) { // append爲false時 addWarn("Append mode is mandatory for RollingFileAppender"); append = true; // 改爲true }
f. prudent模式不支持file設定;若是啓用了壓縮,logback將不會記錄日誌:
ch.qos.logback.core.rolling.RollingFileAppender類
的start()方法:
Java代碼
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; // 返回,不會進行後面的記錄日誌操做 } }