Log4J學習【二十七】常用Appender使用例子
阿新 • • 發佈:2019-02-02
看完WriterAppender的程式碼之後,我們就隨便挑選兩個稍微簡單的ConsoleAppender和FilterAppender來看看,我們真正平時在使用的Appender是怎麼實現的。首先是ConsoleAppender:
public void activateOptions() {
if (follow) {
if (target.equals(SYSTEM_ERR)) {
setWriter(createWriter(new SystemErrStream()));
} else {
setWriter(createWriter(new SystemOutStream()));
}
} else {
if (target.equals(SYSTEM_ERR)) {
setWriter(createWriter(System.err));
} else {
setWriter(createWriter(System.out));
}
}
super.activateOptions();
}
在這段程式碼中,我們可以看到另一個我們之前並沒有介紹的屬性follow,這個屬性的作用非常簡單,如果follow為true,那麼就根據我們設定的target屬性(還記得ConsoleAppender的target屬性麼?)來選擇重新建立一個SystemErrStream或者SystemOutStream,如果follow為false,就是使用當前的System.out或System.err來輸出。在這個createWriter方法中,我們就只看一句程式碼就夠了:
retval = new OutputStreamWriter(os, enc);
非常簡單,就是把傳進來的System.out或者System.err,和encoding(還記得WriterAppender上面的encoding屬性麼?)包裝成一個OutputStreamWriter。
另外,這個類也沒有再去實現append方法或者subAppend方法,而直接是使用的WriterAppender去執行的。同時,看完程式碼之後,我們在使用ConsoleAppender設定的那些屬性到底是怎麼起作用的,也就非常明顯了。
再來個例子,看看FileAppender,同理,還是先找acitivateOption再找append或者subAppend:
public void activateOptions() {
if (fileName != null) {
try {
setFile(fileName, fileAppend, bufferedIO, bufferSize);
} catch (java.io.IOException e) {
errorHandler.error("setFile(" + fileName + "," + fileAppend + ") call failed.", e,
ErrorCode.FILE_OPEN_FAILURE);
}
} else {
LogLog.warn("File option not set for appender [" + name + "].");
LogLog.warn("Are you using FileAppender instead of ConsoleAppender?");
}
}
很簡單,首先檢查必須設定檔名,這裡的fileName即是我們設定的file屬性得到的;如果沒有問題,則使用setFile方法建立檔案和Writer,我們來看看setFile的程式碼:
public synchronized void setFile(String fileName, boolean append, boolean bufferedIO,
int bufferSize) throws IOException {
LogLog.debug("setFile called: " + fileName + ", " + append);
reset();
FileOutputStream ostream = null;
try {
ostream = new FileOutputStream(fileName, append);
} catch (FileNotFoundException ex) {
String parentName = new File(fileName).getParent();
if (parentName != null) {
File parentDir = new File(parentName);
if (!parentDir.exists() && parentDir.mkdirs()) {
ostream = new FileOutputStream(fileName, append);
} else {
throw ex;
}
} else {
throw ex;
}
}
Writer fw = createWriter(ostream);
if (bufferedIO) {
fw = new BufferedWriter(fw, bufferSize);
}
}
首先注意呼叫方法傳入的引數值,分別是fileName,append,bufferedIO和bufferSize。怎麼樣,這4個屬性名字都很熟悉吧。首先,根據fileName和append建立一個FileOutputStream;如果檔案不存在則建立這個檔案,再把這個檔案包裝成FileOutputStream;緊接著呼叫WriterAppender的createWriter方法,把FileOutputStream包裝成一個Writer(這個Writer就設定好了encoding了);接著,如果需要快取,則再把Writer用BufferedWriter包裝一次,即可使用。
同樣,這個類也沒有再去實現append方法或者subAppend方法,而直接是使用的WriterAppender去執行的。其實ConsoleAppender和FileAppender都只是對用什麼東西來寫做了規定,其他的比如怎麼寫,就都是WriterAppender來規範的。
程式碼先看到這裡,我們來思考一個問題,我們已經看了FileAppender的實現方式,那麼,我們自己來猜想一下RollingFileAppender的實現呢?那下面我們就來簡單猜想一下,請大家注意一下,可能我們下面的思路不一定就是RollingFileAppender的實現方式,但是我們先去猜,再看程式碼,自己的提升會很大。
首先我們考慮,RollingFileAppender應該不需要去覆寫activateOption方法,因為RollingFileAppender的初始化動作和FileAppender是相同的,都是根據情況建立好日誌檔案而已。但是,在RollingFileAppender中,就必須要去修改subAppend方法了。因為在日誌記錄的過程中,涉及到檔案備份/新開操作和當檔案索引號達到規定最大號碼的時候,刪除備份檔案的動作。這些都只有在subAppend方法中實現。首先,當記錄完成一條日誌後,就需要去判斷當前日誌檔案的大小,如果檔案大小滿足了最大檔案上限,就需要執行檔案處理動作。首先把當前日誌檔案之前的所有日誌檔案備份名+1,即log.log.N變成log.log.N+1,然後再幫當前日誌檔案重新命名為log.log.1,如果當前的備份日誌檔名稱數量已經超過了maxBackupIndex大小,則直接刪除log.log.maxBackupIndex+1這個檔案即可,所以,在RollingFileAppender中,又應該保持一個屬性來記錄當前已經備份了多少檔案號,處理完備份檔案之後,再重新建立log.log,並把FileOutputStream重新定向到這個新的日誌檔案即可。
那麼真正的程式碼是否如我們所設想呢?這個大家就下去自己看看RollingFileAppender的實現,保證能學到別人更好的設計思想和規範的編碼方式。
public void activateOptions() {
if (follow) {
if (target.equals(SYSTEM_ERR)) {
setWriter(createWriter(new SystemErrStream()));
} else {
setWriter(createWriter(new SystemOutStream()));
}
} else {
if (target.equals(SYSTEM_ERR)) {
setWriter(createWriter(System.err));
} else {
setWriter(createWriter(System.out));
}
}
super.activateOptions();
}
在這段程式碼中,我們可以看到另一個我們之前並沒有介紹的屬性follow,這個屬性的作用非常簡單,如果follow為true,那麼就根據我們設定的target屬性(還記得ConsoleAppender的target屬性麼?)來選擇重新建立一個SystemErrStream或者SystemOutStream,如果follow為false,就是使用當前的System.out或System.err來輸出。在這個createWriter方法中,我們就只看一句程式碼就夠了:
retval = new OutputStreamWriter(os, enc);
非常簡單,就是把傳進來的System.out或者System.err,和encoding(還記得WriterAppender上面的encoding屬性麼?)包裝成一個OutputStreamWriter。
另外,這個類也沒有再去實現append方法或者subAppend方法,而直接是使用的WriterAppender去執行的。同時,看完程式碼之後,我們在使用ConsoleAppender設定的那些屬性到底是怎麼起作用的,也就非常明顯了。
再來個例子,看看FileAppender,同理,還是先找acitivateOption再找append或者subAppend:
public void activateOptions() {
if (fileName != null) {
try {
setFile(fileName, fileAppend, bufferedIO, bufferSize);
} catch (java.io.IOException e) {
errorHandler.error("setFile(" + fileName + "," + fileAppend + ") call failed.", e,
ErrorCode.FILE_OPEN_FAILURE);
}
} else {
LogLog.warn("File option not set for appender [" + name + "].");
LogLog.warn("Are you using FileAppender instead of ConsoleAppender?");
}
}
很簡單,首先檢查必須設定檔名,這裡的fileName即是我們設定的file屬性得到的;如果沒有問題,則使用setFile方法建立檔案和Writer,我們來看看setFile的程式碼:
public synchronized void setFile(String fileName, boolean append, boolean bufferedIO,
int bufferSize) throws IOException {
LogLog.debug("setFile called: " + fileName + ", " + append);
reset();
FileOutputStream ostream = null;
try {
ostream = new FileOutputStream(fileName, append);
} catch (FileNotFoundException ex) {
String parentName = new File(fileName).getParent();
if (parentName != null) {
File parentDir = new File(parentName);
if (!parentDir.exists() && parentDir.mkdirs()) {
ostream = new FileOutputStream(fileName, append);
} else {
throw ex;
}
} else {
throw ex;
}
}
Writer fw = createWriter(ostream);
if (bufferedIO) {
fw = new BufferedWriter(fw, bufferSize);
}
}
首先注意呼叫方法傳入的引數值,分別是fileName,append,bufferedIO和bufferSize。怎麼樣,這4個屬性名字都很熟悉吧。首先,根據fileName和append建立一個FileOutputStream;如果檔案不存在則建立這個檔案,再把這個檔案包裝成FileOutputStream;緊接著呼叫WriterAppender的createWriter方法,把FileOutputStream包裝成一個Writer(這個Writer就設定好了encoding了);接著,如果需要快取,則再把Writer用BufferedWriter包裝一次,即可使用。
同樣,這個類也沒有再去實現append方法或者subAppend方法,而直接是使用的WriterAppender去執行的。其實ConsoleAppender和FileAppender都只是對用什麼東西來寫做了規定,其他的比如怎麼寫,就都是WriterAppender來規範的。
程式碼先看到這裡,我們來思考一個問題,我們已經看了FileAppender的實現方式,那麼,我們自己來猜想一下RollingFileAppender的實現呢?那下面我們就來簡單猜想一下,請大家注意一下,可能我們下面的思路不一定就是RollingFileAppender的實現方式,但是我們先去猜,再看程式碼,自己的提升會很大。
首先我們考慮,RollingFileAppender應該不需要去覆寫activateOption方法,因為RollingFileAppender的初始化動作和FileAppender是相同的,都是根據情況建立好日誌檔案而已。但是,在RollingFileAppender中,就必須要去修改subAppend方法了。因為在日誌記錄的過程中,涉及到檔案備份/新開操作和當檔案索引號達到規定最大號碼的時候,刪除備份檔案的動作。這些都只有在subAppend方法中實現。首先,當記錄完成一條日誌後,就需要去判斷當前日誌檔案的大小,如果檔案大小滿足了最大檔案上限,就需要執行檔案處理動作。首先把當前日誌檔案之前的所有日誌檔案備份名+1,即log.log.N變成log.log.N+1,然後再幫當前日誌檔案重新命名為log.log.1,如果當前的備份日誌檔名稱數量已經超過了maxBackupIndex大小,則直接刪除log.log.maxBackupIndex+1這個檔案即可,所以,在RollingFileAppender中,又應該保持一個屬性來記錄當前已經備份了多少檔案號,處理完備份檔案之後,再重新建立log.log,並把FileOutputStream重新定向到這個新的日誌檔案即可。
那麼真正的程式碼是否如我們所設想呢?這個大家就下去自己看看RollingFileAppender的實現,保證能學到別人更好的設計思想和規範的編碼方式。