精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Java日志緩存機制的實現

開發 后端
Java 日志機制在很多文章中都有介紹,為了便于后面文章部分的理解,在這里再簡單介紹一下本文用到的一些關鍵字。

概述

日志技術為產品的質量和服務提供了重要的支撐。JDK 在 1.4 版本以后加入了日志機制,為 Java 開發人員提供了便利。但這種日志機制是基于靜態日志級別的,也就是在程序運行前就需設定下來要打印的日志級別,這樣就會帶來一些不便。

在 JDK 提供的日志功能中,日志級別被細化為 9 級,用以區分不同日志的用途,用來記錄一個錯誤,或者記錄正常運行的信息,又或是記錄詳細的調試信息。由于日志級別是靜態的,如果日志級別設定過高,低級 別的日志難以打印出來,從而導致在錯誤發生時候,難以去追蹤錯誤的發生原因,目前常常采用的方式是在錯誤發生的時候,不得不先調整日志級別到相對低的程 度,然后再去觸發錯誤,使得問題根源得到顯現。但是這種發生問題需要改動產品配置,然后重新觸發問題進行調試的方式使得產品用戶體驗變差,而且有些問題會 因為偶發性,環境很復雜等原因很難重新觸發。

相反,如果起初就把日志級別調整到比較低,那么日志中間會有大量無用信息,而且當產品比較復雜的時候,會導致產生的日志文件很大,刷新很快,無法及時的記錄有效的信息,甚至成為性能瓶頸,從而降低了日志功能對產品的幫助。

本文借助 Java Logging 中的 MemoryHandler 類將所有級別日志緩存起來,在適當時刻輸出,來解決這個問題。主要圍繞 MemoryHandler 的定義和logging.properties 文件的處理而展開。

實例依附的場景如下,設想用戶需要在產品發生嚴重錯誤時,查看先前發生的包含 Exception 的錯誤信息,以此作為診斷問題緣由的依據。使用 Java 緩沖機制作出的一個解決方案是,將所有產品運行過程中產生的包含Exception 的日志條目保存在一個可設定大小的循環緩沖隊列中,當嚴重錯誤(SEVERE)發生時,將緩沖隊列中的日志輸出到指定平臺,供用戶查閱。

Java 日志機制的介紹

Java 日志機制在很多文章中都有介紹,為了便于后面文章部分的理解,在這里再簡單介紹一下本文用到的一些關鍵字。

Level:JDK 中定義了 Off、Severe、Warning、Info、Config、Fine、Finer、Finest、All 九個日志級別,定義 Off 為日志***等級,All 為***等級。每條日志必須對應一個級別。級別的定義主要用來對日志的嚴重程度進行分類,同時可以用于控制日志是否輸出。

LogRecord:每一條日志會被記錄為一條 LogRecord, 其中存儲了類名、方法名、線程 ID、打印的消息等等一些信息。

Logger:日志結構的基本單元。Logger 是以樹形結構存儲在內存中的,根節點為 root。com.test(如果存在)一定是 com.test.demo(如果存在)的父節點,即前綴匹配的已存在的 logger 一定是這個 logger 的父節點。這種父子關系的定義,可以為用戶提供更為自由的控制粒度。因為子節點中如果沒有定義處理規則,如級別 handler、formatter 等,那么默認就會使用父節點中的這些處理規則。

Handler:用來處理 LogRecord,默認 Handler 是可以連接成一個鏈狀,依次對 LogRecord 進行處理。

Filter:日志過濾器。在 JDK 中,沒有實現。

Formatter:它主要用于定義一個 LogRecord 的輸出格式。

圖 1. Java 日志處理流程

圖 1 展示了一個 LogRecord 的處理流程。一條日志進入處理流程首先是 Logger,其中定義了可通過的 Level,如果 LogRecord 的 Level 高于Logger 的等級,則進入 Filter(如果有)過濾。如果沒有定義 Level,則使用父 Logger 的 Level。Handler 中過程類似,其中 Handler 也定義了可通過 Level,然后進行 Filter 過濾,通過如果后面還有其他 Handler,則直接交由后面的 Handler 進行處理,否則會直接綁定到 formatter 上面輸出到指定位置。

在實現日志緩存之前,先對 Filter 和 Formatter 兩個輔助類進行介紹。

Filter

Filter 是一個接口,主要是對 LogRecord 進行過濾,控制是否對 LogRecord 進行進一步處理,其可以綁定在 Logger 下或 Handler 下。

只要在 boolean isLoggable(LogRecord)方法中加上過濾邏輯就可以實現對 logrecord 進行控制,如果只想對發生了 Exception 的那些 log 記錄進行記錄,那么可以通過清單 1 來實現,當然首先需要將該 Filter 通過調用 setFilter(Filter)方法或者配置文件方式綁定到對應的 Logger 或 Handler。

清單 1. 一個 Filter 實例的實現

  1. Override 
  2.  public boolean isLoggable(LogRecord record){ 
  3.  if(record.getThrown()!=null){ 
  4.         return true
  5.  }else
  6.          return false;  
  7.  } 
  8.  } 

Formatter

Formatter 主要是對 Handler 在輸出 log 記錄的格式進行控制,比如輸出日期的格式,輸出為 HTML 還是 XML 格式,文本參數替換等。Formatter 可以綁定到 Handler 上,Handler 會自動調用 Formatter 的 String format(LogRecord r) 方法對日志記錄進行格式化,該方法具有默認的實現,如果想實現自定義格式可以繼承 Formater 類并重寫該方法,默認情況下例如清單 2 在經過 Formatter 格式化后,會將 {0} 和 {1} 替換成對應的參數。

清單 2. 記錄一條 log

  1. logger.log(Level.WARNING,"this log is for test1: {0} and test2:{1}"
  2.     new Object[]{newTest1(), 
  3.     new Test2()}); 

MemoryHandler

MemoryHandler 是 Java Logging 中兩大類 Handler 之一,另一類是 StreamHandler,二者直接繼承于 Handler,代表了兩種不同的設計思路。Java Logging Handler 是一個抽象類,需要根據使用場景創建具體 Handler,實現各自的 publish、flush 以及 close 等方法。

MemoryHandler 使用了典型的“注冊 – 通知”的觀察者模式。MemoryHandler 先注冊到對自己感興趣的 Logger 中(logger.addHandler(handler)),在這些 Logger 調用發布日志的 API:log()、logp()、logrb() 等,遍歷這些 Logger 下綁定的所有 Handlers 時,通知觸發自身 publish(LogRecord)方法的調用,將日志寫入 buffer,當轉儲到下一個日志發布平臺的條件成立,轉儲日志并清空 buffer。

這里的 buffer 是 MemoryHandler 自身維護一個可自定義大小的循環緩沖隊列,來保存所有運行時觸發的 Exception 日志條目。同時在構造函數中要求指定一個 Target Handler,用于承接輸出;在滿足特定 flush buffer 的條件下,如日志條目等級高于 MemoryHandler 設定的 push level 等級(實例中定義為 SEVERE)等,將日志移交至下一步輸出平臺。從而形成如下日志轉儲輸出鏈:

圖 2. Log 轉儲鏈

在實例中,通過對 MemoryHandler 配置項 .push 的 Level 進行判斷,決定是否將日志推向下一個 Handler,通常在 publish() 方法內實現。代碼清單如下:

#p#

清單 3

  1. // 只紀錄有異常并且高于 pushLevel 的 logRecord 
  2.     final Level level = record.getLevel();        
  3.     final Throwable thrown = record.getThrown(); 
  4.     If(level >= pushLevel){ 
  5.        push(); 
  6.     } 

MemoryHandler.push 方法的觸發條件

Push 方法會導致 MemoryHandler 轉儲日志到下一 handler,清空 buffer。觸發條件可以是但不局限于以下幾種,實例中使用的是默認的***種:

  • 日志條目的 Level 大于或等于當前 MemoryHandler 中默認定義或用戶配置的 pushLevel;
  • 外部程序調用 MemoryHandler 的 push 方法;
  • MemoryHandler 子類可以重載 log 方法或自定義觸發方法,在方法中逐一掃描日志條目,滿足自定義規則則觸發轉儲日志和清空 buffer 的操作。MemoryHanadler 的可配置屬性

表 1.MemoryHandler 可配置屬性

屬性名 描述 缺省值
繼承屬性 MemoryHandler.level MemoryHandler 接受的輸入到 buffer 的日志等級 Level.INFO
MemoryHandler.filter 在輸入到 buffer 之前,可在 filter 中自定義除日志等級外的其他過濾條件 (Undefined)
MemoryHandler.formatter 指定輸入至 buffer 的日志格式 (Undefined)
MemoryHandler.encoding 指定輸入至 buffer 的日志編碼,在 MemoryHandler 中應用甚少 (Undefined)
私有屬性 MemoryHandler.size 以日志條目為單位定義循環 buffer 的大小 1,000
MemoryHandler.push 定義將 buffer 中的日志條目發送至下一個 Handler 的*** Level(包含) Level.SEVERE
MemoryHandler.target 在構造函數中指定下一步承接日志的 Handler (Undefined)

使用方式:

以上是記錄產品 Exception 錯誤日志,以及如何轉儲的 MemoryHandler 處理的內部細節;接下來給出 MemoryHandler 的一些使用方式。

1. 直接使用 java.util.logging 中的 MemoryHandler

清單4

  1. // 在 buffer 中維護 5 條日志信息 
  2. // 僅記錄 Level 大于等于 Warning 的日志條目并 
  3. // 刷新 buffer 中的日志條目到 fileHandler 中處理 
  4.          int bufferSize = 5
  5.          f = new FileHandler("testMemoryHandler.log"); 
  6.          m = new MemoryHandler(f, bufferSize, Level.WARNING); 
  7.          … 
  8.          myLogger = Logger.getLogger("com.ibm.test"); 
  9.          myLogger.addHandler(m); 
  10.          myLogger.log(Level.WARNING, “this is a WARNING log”); 

. 自定義

1)反射

思考自定義 MyHandler 繼承自 MemoryHandler 的場景,由于無法直接使用作為父類私有屬性的 size、buffer 及 buffer 中的 cursor,如果在 MyHandler 中有獲取和改變這些屬性的需求,一個途徑是使用反射。清單 5 展示了使用反射讀取用戶配置并設置私有屬性。

清單5

  1. int m_size; 
  2.   String sizeString = manager.getProperty(loggerName + ".size"); 
  3.   if (null != sizeString) { 
  4.          try { 
  5.           m_size = Integer.parseInt(sizeString); 
  6.           if (m_size <= 0) { 
  7.              m_size = BUFFER_SIZE; // default 1000 
  8.           } 
  9.  // 通過 java 反射機制獲取私有屬性 
  10.           Field f; 
  11.           f = getClass().getSuperclass().getDeclaredField("size"); 
  12.           f.setAccessible(true); 
  13.           f.setInt(this, m_size); 
  14.           f = getClass().getSuperclass().getDeclaredField("buffer"); 
  15.           f.setAccessible(true); 
  16.           f.set(thisnew LogRecord[m_size]); 
  17.          } catch (Exception e) { 
  18.          } 
  19.   } 

2)重寫

直接使用反射方便快捷,適用于對父類私有屬性無頻繁訪問的場景。思考這樣一種場景,默認環形隊列無法滿足我們存儲需求,此時不妨令自定義的 MyMemoryHandler 直接繼承 Handler,直接對存儲結構進行操作,可以通過清單 6 實現。

清單 6

  1. public class MyMemoryHandler extends Handler{ 
  2.   // 默認存儲 LogRecord 的緩沖區容量 
  3.   private static final int DEFAULT_SIZE = 1000
  4.   // 設置緩沖區大小 
  5.   private int size = DEFAULT_SIZE; 
  6.   // 設置緩沖區 
  7.   private LogRecord[] buffer; 
  8.   // 參考 java.util.logging.MemoryHandler 實現其它部分 
  9.   ... 
  10.  } 

使用 MemoryHandler 時需關注的幾個問題

了解了使用 MemoryHandler 實現的 Java 日志緩沖機制的內部細節和外部應用之后,來著眼于兩處具體實現過程中遇到的問題:Logger/Handler/LogRecord Level 的傳遞影響,以及如何在開發 MemoryHandler 過程中處理錯誤日志。

1. Level 的傳遞影響

Java.util.logging 中有三種類型的 Level,分別是 Logger 的 Level,Handler 的 Level 和 LogRecord 的 Level. 前兩者可以通過配置文件設置。之后將日志的 Level 分別與 Logger 和 Handler 的 Level 進行比較,過濾無須記錄的日志。在使用 Java Log 時需關注 Level 之間相互影響的問題,尤其在遍歷 Logger 綁定了多個 Handlers 時。如圖 3 所示:

圖 3. Java Log 中 Level 的傳遞影響

Java.util.logging.Logger 提供的 setUseParentHandlers 方法,也可能會影響到最終輸出終端的日志顯示。這個方法允許用戶將自身的日志條目打印一份到 Parent Logger 的輸出終端中。缺省會打印到 Parent Logger 終端。此時,如果 Parent Logger Level 相關的設置與自身 Logger 不同,則打印到 Parent Logger 和自身中的日志條目也會有所不同。如圖 4 所示:

圖 4. 子類日志需打印到父類輸出終端

2. 開發 log 接口過程中處理錯誤日志

在開發 log 相關接口中調用自身接口打印 log,可能會陷入無限循環。Java.util.logging 中考慮到這類問題,提供了一個 ErrorManager 接口,供 Handler 在記錄日志期間報告任何錯誤,而非直接拋出異常或調用自身的 log 相關接口記錄錯誤或異常。Handler 需實現 setErrorManager() 方法,該方法為此應用程序構造 java.util.logging.ErrorManager 對象,并在錯誤發生時,通過 reportError 方法調用 ErrorManager 的 error 方法,缺省將錯誤輸出到標準錯誤流,或依據 Handler 中自定義的實現處理錯誤流。關閉錯誤流時,使用 Logger.removeHandler 移除此 Handler 實例。

兩種經典使用場景,一種是自定義 MyErrorManager,實現父類相關接口,在記錄日志的程序中調用 MyHandler.setErrorManager(new MyEroorManager()); 另一種是在 Handler 中自定義 ErrorManager 相關方法,示例如清單 7:

#p#

清單 7

  1. public class MyHandler extends Handler{ 
  2. // 在構造方法中實現 setErrorManager 方法 
  3. public MyHandler(){ 
  4.    ...... 
  5.     setErrorManager (new ErrorManager() { 
  6.         public void  error (String msg, Exception ex, int code) { 
  7.             System.err.println("Error reported by MyHandler " 
  8.                              + msg + ex.getMessage()); 
  9.         } 
  10.     }); 
  11. public void publish(LogRecord record){ 
  12.     if (!isLoggable(record)) return
  13.     try { 
  14.         // 一些可能會拋出異常的操作 
  15.     } catch(Exception e) { 
  16.         reportError ("Error occurs in publish ", e, ErrorManager.WRITE_FAILURE); 
  17.     } 
  18. ...... 

logging.properties

logging.properties 文件是 Java 日志的配置文件,每一行以“key=value”的形式描述,可以配置日志的全局信息和特定日志配置信息,清單 8 是我們為測試代碼配置的 logging.properties。

清單 8. logging.properties 文件示例

  1. #Level 等級 OFF > SEVERE > WARNING > INFO > CONFIG > FINE > FINER > FINEST > ALL 
  2. # 為 FileHandler 指定日志級別 
  3. java.util.logging.FileHandler.level=WARNING 
  4. # 為 FileHandler 指定 formatter 
  5. java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter 
  6. # 為自定義的 TestMemoryHandler 指定日志級別 
  7. com.ibm.test.MemoryHandler.level=INFO 
  8. # 設置 TestMemoryHandler 最多記錄日志條數 
  9. com.ibm.test.TestMemoryHandler.size=1000 
  10. # 設置 TestMemoryHandler 的自定義域 useParentLevel 
  11. com.ibm.test.TestMemoryHandler.useParentLevel=WARNING 
  12. # 設置特定 log 的 handler 為 TestMemoryHandler 
  13. com.ibm.test.handlers=com.ibm.test.TestMemoryHandler 
  14. # 指定全局的 Handler 為 FileHandler 
  15. handlers=java.util.logging.FileHandler 

從 清單 8 中可以看出 logging.properties 文件主要是用來給 logger 指定等級(level),配置 handler 和 formatter 信息。

如何監聽 logging.properties

如果一個系統對安全性要求比較高,例如系統需要對更改 logging.properties 文件進行日志記錄,記錄何時何人更改了哪些記錄,那么應該怎么做呢?

這里可以利用 JDK 提供的 PropertyChangeListener 來監聽 logging.properties 文件屬性的改變。

例如創建一個 LogPropertyListener 類,其實現了 java.benas.PropertyChangeListener 接口,PropertyChangeListener 接口中只包含一個 propertyChange(PropertyChangeEvent)方法,該方法的實現如清 9 所示。

清單 9. propertyChange 方法的實現

  1. @Override 
  2. public void propertyChange(PropertyChangeEvent event) { 
  3.    if (event.getSource() instanceof LogManager){ 
  4.        LogManager manager=(LogManager)event.getSource(); 
  5.        update(manager); 
  6.        execute(); 
  7.        reset(); 
  8.    } 

propertyChange(PropertyChangeEvent)方法中首先調用 update(LogManager)方法來找出 logging.properties 文件中更改的,增加的以及刪除的項,這部分代碼如清單 10 所示;然后調用 execute() 方法來執行具體邏輯,參見 清單 11;***調用 reset() 方法對相關屬性保存以及清空,如 清單 12 所示。

清單 10. 監聽改變的條目

  1. public void update(LogManager manager){ 
  2.  Properties logProps = null ; 
  3.   // 使用 Java 反射機制獲取私有屬性 
  4.    try { 
  5.      Field f = manager.getClass().getDeclaredField("props"); 
  6.      f.setAccessible(true ); 
  7.      logProps=(Properties)f.get(manager); 
  8.     }catch (Exception e){ 
  9.        logger.log(Level.SEVERE,"Get private field error.", e); 
  10.         return ; 
  11.    } 
  12.    Set<String> logPropsName=logProps.stringPropertyNames(); 
  13.     for (String logPropName:logPropsName){ 
  14.         String newVal=logProps.getProperty(logPropName).trim(); 
  15.        // 記錄當前的屬性 
  16.        newProps.put(logPropName, newVal);   
  17.        // 如果給屬性上次已經記錄過 
  18.        if (oldProps.containsKey(logPropName)){ 
  19.             String oldVal = oldProps.get(logPropName); 
  20.             if (newVal== null ?oldVal== null :newVal.equals(oldVal)){ 
  21.            // 屬性值沒有改變,不做任何操作 
  22.         }else { 
  23.             changedProps.put(logPropName, newVal); 
  24.        } 
  25.        oldProps.remove(logPropName); 
  26.    }else {// 如果上次沒有記錄過該屬性,則其應為新加的屬性,記錄之 
  27.         changedProps.put(logPropName, newVal);               
  28.        } 
  29.     } 

代碼中 oldProps、newProps 以及 changedProps 都是 HashMap類型,oldProps 存儲修改前 logging.properties 文件內容,newProps 存儲修改后 logging.properties 內容,changedProps 主要用來存儲增加的或者是修改的部分。

方法首先通過 Java 的反射機制獲得 LogManager 中的私有屬性 props(存儲了 logging.properties 文件中的屬性信息),然后通過與 oldProps 比較可以得到增加的以及修改的屬性信息,*** oldProps 中剩下的就是刪除的信息了。

#p#

清單 11. 具體處理邏輯方法

  1. private void execute(){ 
  2.  // 處理刪除的屬性 
  3.  for (String prop:oldProps.keySet()){ 
  4.    // 這里可以加入其它處理步驟 
  5.    logger.info("'"+prop+"="+oldProps.get(prop)+"'has been removed");           
  6.  } 
  7.  // 處理改變或者新加的屬性 
  8.  for (String prop:changedProps.keySet()){ 
  9.      // 這里可以加入其它處理步驟 
  10.      logger.info("'"+prop+"="+oldProps.get(prop)+"'has been changed or added"); 
  11.  } 

該方法是主要的處理邏輯,對修改或者刪除的屬性進行相應的處理,比如記錄屬性更改日志等。這里也可以獲取當前系統的登錄者,和當前時間,這樣便可以詳細記錄何人何時更改過哪個日志條目。

清單 12. 重置所有數據結構

  1. private void reset(){ 
  2. oldProps = newProps; 
  3. newProps= new HashMap< String,String>(); 
  4. changedProps.clear(); 

eset() 方法主要是用來重置各個屬性,以便下一次使用。

當然如果只寫一個 PropertyChangeListener 還不能發揮應有的功能,還需要將這個 PropertyChangeListener 實例注冊到 LogManager 中,可以通過清單 13 實現。

清單 13. 注冊 PropertyChangeListener

  1. // 為'logging.properties'文件注冊監聽器 
  2. LogPropertyListener listener= new LogPropertyListener(); 
  3. LogManager.getLogManager().addPropertyChangeListener(listener); 

如何實現自定義標簽

在 清單 8中有一些自定義的條目,比如 com.ibm.test.TestMemoryHandler。

useParentLever=WARNING”,表示如果日志等級超過 useParentLever 所定義的等級 WARNING 時,該條日志在 TestMemoryHandler 處理后需要傳遞到對應 Log 的父 Log 的 Handler 進行處理(例如將發生了 WARNING 及以上等級的日志上下文緩存信息打印到文件中),否則不傳遞到父 Log 的 Handler 進行處理,這種情況下如果不做任何處理,Java 原有的 Log 機制是不支持這種定義的。那么如何使得 Java Log 支持這種自定義標簽呢?這里可以使用 PropertyListener 對自定義標簽進行處理來使得 Java Log 支持這種自定義標簽,例如對“useParentLever”進行處理可以通過清單 14 實現。

清單 14

  1. private void execute(){ 
  2.        // 處理刪除的屬性 
  3.         for (String prop:oldProps.keySet()){ 
  4.             if (prop.endsWith(".useParentLevel")){ 
  5.                String logName=prop.substring(0, prop.lastIndexOf(".")); 
  6.                Logger log=Logger.getLogger(logName); 
  7.                 for (Handler handler:log.getHandlers()){ 
  8.                     if (handler  instanceof TestMemoryHandler){ 
  9.                        ((TestMemoryHandler)handler) 
  10.                            .setUseParentLevel(oldProps.get(prop)); 
  11.                         break ; 
  12.                    } 
  13.                } 
  14.            } 
  15.        } 
  16.        // 處理改變或者新加的屬性 
  17.         for (String prop:changedProps.keySet()){ 
  18.             if (prop.endsWith(".useParentLevel")){ 
  19.                // 在這里添加邏輯處理步驟 
  20.            } 
  21.        } 

在清單 14 處理之后,就可以在自定義的 TestMemoryHandler 中進行判斷了,對 log 的等級與其域 useParentLevel 進行比較,決定是否傳遞到父 Log 的 Handler 進行處理。在自定義 TestMemoryHandler 中保存對應的 Log 信息可以很容易的實現將信息傳遞到父 Log 的 Handler,而保存對應 Log 信息又可以通過 PropertyListener 來實現,例如清單 15 更改了 清單 13中相應代碼實現這一功能。

清單 15

  1. if (handler  instanceof TestMemoryHandler){ 
  2.     ((TestMemoryHandler)handler).setUseParentLevel(oldProps.get(prop)); 
  3.     ((TestMemoryHandler)handler).addLogger(log); 
  4.       break ; 

具體如何處理自定義標簽的值那就看程序的需要了,通過這種方法就可以很容易在 logging.properties 添加自定義的標簽了。

自定義讀取配置文件

如果 logging.properties 文件更改了,需要通過調用 readConfiguration(InputStream)方法使更改生效,但是從 JDK 的源碼中可以看到 readConfiguration(InputStream)方法會重置整個 Log 系統,也就是說會把所有的 log 的等級恢復為默認值,將所有 log 的 handler 置為 null 等,這樣所有存儲的信息就會丟失。

比如,TestMemoryHandler 緩存了 1000 條 logRecord,現在用戶更改了 logging.properties 文件,并且調用了 readConfiguration(InputStream) 方法來使之生效,那么由于 JDK 本身的 Log 機制,更改后對應 log 的 TestMemoryHandler 就是新創建的,那么原來存儲的 1000 條 logRecord 的 TestMemoryHandler 實例就會丟失。

那么這個問題應該如何解決呢?這里給出三種思路:

1). 由于每個 Handler 都有一個 close() 方法(任何繼承于 Handler 的類都需要實現該方法),Java Log 機制在將 handler 置為 null 之前會調用對應 handler 的 close() 方法,那么就可以在 handler(例如 TestMemoryHandler)的 close() 方法中保存下相應的信息。

2). 研究 readConfiguration(InputStream)方法,寫一個替代的方法,然后每次調用替代的方法。

3). 繼承 LogManager 類,覆蓋 readConfiguration(InputStream)方法。

這里***種方法是保存原有的信息,然后進行恢復,但是這種方法不是很實用和高效;第二和第三種方法其實是一樣的,都是寫一個替代的方法,例如可以在 替代的方法中對 Handler 為 TestMemoryHandler 的不置為 null,然后在讀取 logging.properties 文件時發現為 TestMemoryHandler 屬性時,找到對應 TestMemoryHandler 的實例,并更改相應的屬性值(這個在清單 14 中有所體現),其他不屬于 TestMemoryHandler 屬性值的可以按照 JDK 原有的處理邏輯進行處理,比如設置 log 的 level 等。

另一方面,由于 JDK1.6 及之前版本不支持文件修改監聽功能,每次修改了 logging.properties 文件后需要顯式調用 readConfiguration(InputStream)才能使得修改生效,但是自 JDK1.7 開始已經支持對文件修改監聽功能了,主要是在 java.nio.file.* 包中提供了相關的 API,這里不再詳述。

那么在 JDK1.7 之前,可以使用 apache 的 commons-io 庫中的 FileMonitor 類,在此也不再詳述。

總結

通過對 MemoryHandler 和 logging.properties 進行定義,可以通過 Java 日志實現自定義日志緩存,從而提高 Java 日志的可用性,為產品質量提供更強有力的支持。

原文鏈接:http://blog.jobbole.com/44668/

責任編輯:陳四芳 來源: 伯樂在線
相關推薦

2011-12-15 09:33:19

Java

2014-11-04 10:34:27

JavaCache

2025-02-05 12:22:21

2019-11-14 14:30:10

Java類反射代碼

2009-11-23 17:56:44

PHP緩存機制

2009-06-18 14:51:12

Hibernate緩存Hibernate

2015-09-28 15:59:00

Java動態代理機制

2011-03-16 09:26:41

ReadWriteLoJava

2009-06-17 15:43:03

Hibernate緩存

2023-02-24 16:46:25

Glide緩存機制

2018-07-12 15:30:03

HTTP緩存機制

2024-06-28 08:31:54

2019-05-27 14:40:43

Java同步機制多線程編程

2009-11-09 17:55:13

WCF緩存

2016-03-09 09:54:47

Python開發緩存機制

2010-10-13 16:44:10

MySQL查詢緩存機制

2025-01-02 14:50:34

MyBatis開發緩存

2022-09-23 08:02:42

Kafka消息緩存

2010-09-14 09:30:04

Java多態

2018-08-07 10:44:50

緩存技術瀏覽器
點贊
收藏

51CTO技術棧公眾號

亚洲免费视频一区二区三区| 久草免费在线视频观看| 日韩精品一级毛片在线播放| 中文字幕一区不卡| 国产精品一区免费观看| 精品久久久久久久久久久久久久久久| 欧美一区三区| 欧美一级免费大片| 99精品视频在线看| 欧美一区二区三区| caoporen国产精品视频| 国产日韩欧美视频在线| 亚洲精品午夜久久久久久久| 日韩精品永久网址| 亚洲电影av在线| 激情五月俺来也| 91九色国产在线播放| 中文字幕乱码久久午夜不卡| 国产精品国产三级欧美二区 | 在线黄色免费看| 僵尸再翻生在线观看免费国语| 国产精品久久久一本精品| 国内外成人免费视频| 亚洲一级在线播放| 久久久久免费| 久久频这里精品99香蕉| 97精品在线播放| 国产成人三级| 亚洲精品成人av| 久久久国产精品久久久| julia一区二区三区中文字幕| 午夜精品福利在线| 黄网站色视频免费观看| 在线播放毛片| 国产香蕉久久精品综合网| 黄色小网站91| 亚洲免费不卡视频| 国内精品伊人久久久久av一坑| 国产黑人绿帽在线第一区| 日韩毛片在线视频| 国产精品激情| 欧美巨乳在线观看| 欧美偷拍第一页| 欧美好骚综合网| 中文国产亚洲喷潮| 妺妺窝人体色WWW精品| 美国一区二区| 亚洲精品久久久久久下一站 | 国产精品乱码久久久久| 在线手机中文字幕| 精品毛片三在线观看| 大陆av在线播放| av在线理伦电影| 亚洲国产日产av| 国产美女在线一区| av影视在线看| 精品国产1区2区| 男人日女人bb视频| 性欧美videohd高精| 日本乱码高清不卡字幕| 玩弄japan白嫩少妇hd| 成人日韩在线观看| 欧美日韩免费在线视频| 亚洲精品免费一区亚洲精品免费精品一区 | 黄在线观看网站| 亚洲天堂资源| 欧美在线观看视频一区二区| 国产喷水theporn| 亚洲精品自拍| 日韩欧美中文字幕制服| 第一页在线视频| 加勒比色综合久久久久久久久| 亚洲国产欧美自拍| av男人的天堂av| 欧美一二区在线观看| 久久久精品视频在线观看| 精品国产乱码久久久久久鸭王1| 欧美一区91| 久久久噜噜噜久久中文字免| 日韩精品1区2区| 久久中文精品| 成人激情黄色网| 亚洲国产999| 久久蜜桃av一区精品变态类天堂| 涩涩日韩在线| 超碰porn在线| 欧美午夜精品在线| 亚洲欧洲日本精品| 一区二区免费| 亚洲欧美日韩在线一区| 女同久久另类69精品国产| 欧美日韩免费观看一区=区三区| 欧美肥婆姓交大片| 久久久蜜桃一区二区| 精品一区二区日韩| 国产亚洲自拍偷拍| 91社区在线| 亚洲国产精品久久久男人的天堂| 成人一级片网站| 亚洲精品自拍| 亚洲免费人成在线视频观看| 美女网站视频色| 日韩一级免费| 国产中文字幕日韩| 天天干,夜夜爽| 国产精品国产三级国产三级人妇 | 青青草国产精品亚洲专区无| 亚洲综合视频1区| 日韩三级电影网| 亚洲视频网在线直播| 精品人妻一区二区三区四区在线| 日韩黄色在线| 亚洲免费人成在线视频观看| 福利所第一导航| 久久精品人人| 国产精品国产精品国产专区蜜臀ah | 最新中文字幕亚洲| 国产精品xxxx喷水欧美| 狠狠色综合日日| 人禽交欧美网站免费| 岛国毛片av在线| 91麻豆精品国产91久久久使用方法 | 欧美大秀在线观看| 精品肉丝脚一区二区三区| 男人的j进女人的j一区| 快播亚洲色图| 97超碰在线免费| 日韩女优av电影在线观看| 日韩一区二区三区四区视频| 亚洲欧美日韩国产一区| 99视频在线免费观看| 欧美成人hd| 欧美视频精品在线| 婷婷色一区二区三区| 国产欧美69| 国产伦精品一区二区三区视频免费 | 久久久久久亚洲精品| 在线观看毛片av| 国产亚洲成aⅴ人片在线观看| 亚洲熟妇无码一区二区三区| 超碰在线人人干| 日韩极品一区| 国产激情久久久久| 黄色大片在线看| 色呦呦日韩精品| 中文字幕免费看| 亚洲视频播放| 欧美精品成人一区二区在线观看 | 亚洲精品一区二区妖精| 国产精品丝袜一区二区三区| 国产精品无码2021在线观看| 日本精品一区二区三区高清 | 成人精品电影| 国产精品电影观看| eeuss影院在线播放| 在线视频一区二区三| 男人舔女人下部高潮全视频| 日韩在线一区二区| 午夜精品视频在线观看一区二区| 欧美aaa大片视频一二区| 中文字幕九色91在线| 亚洲熟妇无码久久精品| 亚洲欧洲在线观看av| 亚洲精品永久视频| 欧美日韩ab| 精品蜜桃一区二区三区| videos性欧美另类高清| 国产亚洲欧美日韩精品| 一区二区小视频| 亚洲欧美中日韩| 亚洲乱妇老熟女爽到高潮的片 | 青青青在线视频免费观看| 成人看的羞羞网站| 91免费欧美精品| 欧美日韩经典丝袜| 亚洲精品一区久久久久久| 无码人妻精品一区二区三区蜜桃91| 国产日韩欧美高清在线| 91高清国产视频| 国产综合精品| 欧美福利精品| a一区二区三区亚洲| 欧美激情一二区| 黄色网址在线播放| 91精品国产91热久久久做人人| 91亚洲精品久久久| 蜜桃视频网站在线观看| 久久99精品国产99久久6尤物| 乱h高h女3p含苞待放| 国产盗摄一区二区三区| 大j8黑人w巨大888a片| 亚洲精品推荐| 久cao在线| 精品福利视频一区二区三区| 欧美三级韩国三级日本三斤在线观看| 99久精品国产| 激情五月俺来也| 欧美日韩中文字幕在线视频| 十八禁视频网站在线观看| 欧美电影免费播放| 国产一区二区三区奇米久涩 | 污片在线观看一区二区| 被灌满精子的波多野结衣| 校园春色另类视频| 国产在线98福利播放视频| 黄色在线免费观看网站| 色妞在线综合亚洲欧美| 污污的视频网站在线观看| 欧美日韩一级二级三级| 天天操天天摸天天干| 人妻少妇精品无码专区久久| 亚洲精品菠萝久久久久久久| 久久黄色小视频| 日韩av一区二区在线影视| 一区中文字幕在线观看| 亚洲人挤奶视频| 国产高清不卡av| 欧美国产视频| 国产第一区电影| 无码小电影在线观看网站免费 | 日韩电影在线免费看| 日韩精品久久久| 久久精品美女视频| 国产成人av在线影院| 美女网站视频黄色| 免费看的黄色欧美网站| 女人被男人躁得好爽免费视频| 欧美特黄一级大片| 亚洲激情自拍视频| 中文字幕第4页| 91女神在线视频| 中文字幕免费在线播放| 国产成人在线视频播放| 在线一区二区不卡| 精品伊人久久久久7777人| 国产一二三四在线视频| 久久性色av| 精品一卡二卡三卡| 亚洲永久免费| 国产精品视频一区二区三区四区五区| 亚洲性图久久| 丝袜人妻一区二区三区| 激情欧美日韩| 久久这里只有精品23| 在线成人亚洲| 国产av天堂无码一区二区三区| 国产精品vip| 欧美日韩视频免费| 红桃视频国产精品| 欧美日韩福利在线| 欧美日本中文| 黄色大片在线免费看| 一区二区三区福利| 国产黄页在线观看| 国产精品嫩草99av在线| 国产午夜福利视频在线观看| 香蕉久久a毛片| 成人小视频在线看| 美女视频一区二区三区| 亚洲怡红院在线| 国产一区二区三区av电影| 日本亚洲一区二区三区| 成人蜜臀av电影| wwwwxxxx国产| 欧美激情在线看| 日韩精品123区| 亚洲综合激情网| 久草国产精品视频| 在线中文字幕一区二区| 亚洲一二区视频| 日韩免费性生活视频播放| 韩国av免费在线| 亚洲欧美国产精品| 永久免费av在线| 九九热r在线视频精品| freexxx性亚洲精品| 日韩男女性生活视频| 日韩一级特黄| 国产精品久久久久av福利动漫| 日本精品影院| 亚洲精品欧美精品| 欧美三级网页| www.亚洲天堂网| 无码精品在线观看| 日韩精品日韩在线观看| 91在线播放网站| 久久久伊人日本| 国产一区二区精品调教| 成人中文字幕+乱码+中文字幕| 国产精品17p| 亚洲免费视频一区| 伊人精品成人久久综合软件| www黄色在线| 国产成人精品一区二| 中文字幕人妻一区二区三区在线视频| 欧美国产精品v| 精品在线视频免费观看| 欧美三级日韩三级| 四虎在线视频免费观看| 日韩中文字在线| 蜜桃视频在线观看免费视频| 成人网在线免费观看| 在线日韩网站| 国产精品视频网站在线观看| 天堂久久一区二区三区| 风韵丰满熟妇啪啪区老熟熟女| 国产日韩精品视频一区| 国产乡下妇女做爰| 欧美丰满少妇xxxbbb| 嫩草在线播放| 久久久久久国产精品三级玉女聊斋| 写真福利精品福利在线观看| 国产精品我不卡| 久久久久蜜桃| 手机在线看福利| 2023国产精品自拍| 久久久久无码国产精品不卡| 欧美日韩国产乱码电影| 精品资源在线看| 国产91精品久久久| 国产96在线亚洲| 黑人巨茎大战欧美白妇| 久久草av在线| 蜜臀久久99精品久久久久久| 精品久久久久久久久中文字幕 | 亚洲第一区第二区| 免费av在线网址| 国产精品女人久久久久久| 校园春色另类视频| 国产免费黄色小视频| 成人一级黄色片| 久久一级黄色片| 日韩一级视频免费观看在线| 黄色的网站在线观看| 国产精品视频在线观看| 成人网18免费网站| 亚洲欧美另类动漫| 国产女主播在线一区二区| 成年人免费高清视频| 日韩美女av在线| 亚洲妇女成熟| 欧美资源一区| 久久中文在线| 美国黄色特级片| 欧美视频第二页| 日本在线天堂| 91精品啪aⅴ在线观看国产| 香蕉久久网站| 亚洲美女又黄又爽在线观看| av在线播放免费| 国产精品视频xxx| 91亚洲自偷观看高清| 日韩欧美国产片| 亚洲色图在线播放| av高清一区二区| 欧美激情高清视频| 久久人人爽人人爽人人片av不| 免费 成 人 黄 色| 久久丝袜美腿综合| 中文字幕二区三区| 俺去啦;欧美日韩| 精品一区视频| 免费看欧美黑人毛片| 99re66热这里只有精品3直播 | 国产黄色大片免费看| 欧美日韩国产高清一区二区三区| 一区二区三区黄色片| 亚洲区中文字幕| 精品欧美一区二区三区在线观看| 日韩欧美视频一区二区三区四区 | 日韩中文字幕免费在线| 国产日韩欧美精品电影三级在线| 91精品国产乱码久久| 欧美成人免费播放| 看全色黄大色大片免费久久久| 精品人妻一区二区三区四区在线| 欧美激情在线免费观看| 国产色片在线观看| 性欧美视频videos6一9| 精品国产91久久久久久浪潮蜜月| jizz欧美性11| 亚洲一区二区三区四区中文字幕| 三级在线电影| 国产伊人精品在线| 日韩一级在线| 亚洲色图欧美色| 欧美成人a∨高清免费观看| 免费观看一级欧美片| 亚洲一级视频在线观看| 久久伊人精品天天| 国内精品偷拍| 成人性生交免费看| 亚洲午夜电影在线| 啊v视频在线| 超碰97国产在线| 日本欧美在线观看| 久久精品人妻一区二区三区| 在线观看免费高清视频97| 亚洲天堂av资源在线观看| 欧美日韩一区二区在线免费观看 |