多程序log4j日誌丟失問題分析
阿新 • • 發佈:2019-01-21
二、原因追蹤:
在 log4j 的 DailyRollingFileAppender 類中:
Java程式碼- void rollOver() throws IOException {
- /* Compute filename, but only if datePattern is specified */
- if (datePattern == null) {
- errorHandler.error("Missing DatePattern option in rollOver().");
- return;
- }
-
String datedFilename = fileName+sdf.format(now);
- // It is too early to roll over because we are still within the
- // bounds of the current interval. Rollover will occur once the
- // next interval is reached.
- if (scheduledFilename.equals(datedFilename)) {
- return;
- }
-
// close current file, and rename it to datedFilename
- this.closeFile();
- File target = new File(scheduledFilename);
- if (target.exists()) {
- target.delete();
- }
- File file = new File(fileName);
- boolean result = file.renameTo(target);
- if(result) {
-
LogLog.debug(fileName +" -> "+ scheduledFilename);
- } else {
- LogLog.error("Failed to rename ["+fileName+"] to ["+scheduledFilename+"].");
- }
- try {
- // This will also close the file. This is OK since multiple
- // close operations are safe.
- this.setFile(fileName, false, this.bufferedIO, this.bufferSize);
- }
- catch(IOException e) {
- errorHandler.error("setFile("+fileName+", false) call failed.");
- }
- scheduledFilename = datedFilename;
- }
中間部分程式碼意思是:如果備份檔案不存在,則備份,同時建立新日誌檔案;如果存在,則先刪除掉,再備份;
好,問題在這個時刻就出現了:(假設A程序先進行滾動備份)
1、對於A程序:a. 先將project.log備份(renameTo())為project.log.2009.08.18,然後建立project.log檔案,並將日誌寫在project.log中;
b. 此時A程序持有project.log的檔案控制代碼;而B程序仍然持有project.log.2009.08.18的檔案控制代碼(儘管被重新命名,但控制代碼不變);
2、對於B程序:發現以project.log.2009.08.18為檔名的檔案已經存在,則將其刪除(前一時間段的所有日誌全沒了),並將以project.log為檔名的檔案重新命名為project.log.2009.08.18,然後建立project.log檔案;
3、此時A程序持有project.log.2009.08.18的檔案控制代碼(被B程序重新命名過的),而B程序持有最新建立的project.log;
4、結果導致:前一時間段日誌丟失,A、B程序在不同的檔案裡打日誌;
三、解決方案
1、改變 rollOver() 方法的實現方式:定義 TaskDailyRollingFileAppender 類,該類繼承至 FileAppender ,它與 DailyRollingFileAppender 的主要區別在於以下方法:
Java程式碼- void rollOver() throws IOException {
- /* Compute filename, but only if datePattern is specified */
- if (datePattern == null) {
- errorHandler.error("Missing DatePattern option in rollOver().");
- return;
- }
- String datedFilename = fileName+sdf.format(now);
- // It is too early to roll over because we are still within the
- // bounds of the current interval. Rollover will occur once the
- // next interval is reached.
- if (scheduledFilename.equals(datedFilename)) {
- return;
- }
- // close current file, and rename it to datedFilename
- this.closeFile();
- File target = new File(scheduledFilename);
- if (!target.exists()) {
- File file = new File(fileName);
- boolean result = file.renameTo(target);
- if (result) {
- LogLog.debug(fileName + " -> " + scheduledFilename);
- } else {
- LogLog.error("Failed to rename [" + fileName + "] to [" + scheduledFilename + "].");
- }
- }
- try {
- // This will also close the file. This is OK since multiple
- // close operations are safe.
- this.setFile(fileName, true, this.bufferedIO, this.bufferSize);
- }
- catch(IOException e) {
- errorHandler.error("setFile("+fileName+", false) call failed.");
- }
- scheduledFilename = datedFilename;
- }
2、如果是任務,根據啟動引數taskName 屬性區分日誌檔案:
a. 目前所有後臺任務在啟動腳本里都加上了 -DtaskName 屬性;
b. 定義 TaskDailyRollingFileAppender 類,該類繼承 DailyRollingFileAppender,並覆蓋其 setFile(String file) 方法:
- public void setFile(String file) {
- String taskName = System.getProperty("taskName");
- if (!StringUtil.isEmpty(taskName)) {
- file = file + "." + taskName;
- }
- super.setFile(file);
- }