上善若水
          In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation. To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra
          posts - 146,comments - 147,trackbacks - 0

          JDK1.4開始提供Logging實現,據說當初JDK打算采用Log4J的,后來因為某些原因談判沒談攏,然后就自己開發了一套,不知道是為了報復而故意不沿用Log4J的命名方式和抽象方式,還是開發這個模塊的人水平不夠,或沒用心,亦或是我用Commons LoggingLog4J習慣了,看JDKLogging實現怎么看怎么不爽~~~吐個槽額~~~~

          JDK Logging將日志打印抽象成以下幾個類之間的交互:

          1.    Level,定義日志的級別,類似Log4J中的Level類。

          JDK Logging采用了完全不同于Log4J中對級別的抽象。在JDK Logging中,默認定義了以下幾個級別:SEVERE(對應Log4J中的ERRORFATAL)、WARNING(對應Log4J中的WARN)、INFO(對應Log4J中的INFO)、CONFIG(對應Log4J中的DEBUG)、FINE(對應Log4J中的TRACE)、FINER(對應Log4J中的TRACE)、FINEST(對應Log4J中的TRACE)。另外,類似Log4JJDK Logging也定義了兩個特殊的級別:ALLOFF,分別對應打印所有級別的日志和關閉日志打印。

          Level中包含三個字段:namevalueresourceBundleName。其中name指定級別名稱,value指定該級別對應的一個int值,其值從SEVERE開始依次遞減,resourceBundleName定義本地化后的級別名稱,默認是sun.util.logging.resources.logging,即我們在日志中看到警告、信息等級別字段就是通過調用LevelgetLocalizedName()方法,讀取resourceBundleName對應的resource值來獲得的,這也是Log4J中沒有聽過的。

          Level還定義了一個parse()方法,它可以支持解析name字符串、代表級別的int值(以字符串的形式)、以及對應的Localized名稱,如果所有的都不滿足需求,則拋出IllegalArgumentException

          最后,Level還實現了readResolve()方法,從而確保反序列化后的Level只是Level類中定義的幾個實例(出了自定義的Level實例)。

          2.    LogRecord,封裝了打印一條一直所包含的所有數據,類似Log4J中的LoggingEvent

          它包含了以下信息:

          a.    日志級別(level

          b.    全局標識號(sequenceNumber,即沒創建一個LogRecordsequenceNumber都會自增1

          c.    打印這條日志語句所在的類的名稱(sourceClassName),可以通過調用inferCaller()方法解析出來。解析實現則是通過實例化一個Throwable實例,通過解析該實例的Call Stack即可得出打印這條日志所在的類的名稱和方法名稱,這里貌似不支持行號、文件名等信息。并且通過設置needToInferCaller字段(不可序列化)來判斷sourceClassNamesourceMethodName是否已經取得,從而不用每次調用的時候都去解析而提升性能。

          d.    打印這條日志語句所在調用方法的名稱(sourceMethodName),它也是通過調用inferCaller()方法解析出來。

          e.    打印消息(message

          f.     線程號(threadID),對LogRecord本身而言,從0開始對每個線程自增1

          g.    創建LogRecord的時間(millis

          h.    打印日志中的異常(thrown

          i.      日志名稱(loggerName

          j.     資源名稱(resourceBundleName

          k.    參數列表(parameters數組),在Formatter中通過MessageFormat使用這些參數列表,并在序列化是手動調用其toString()方法序列化,而不是采用默認的序列化方式。

          l.      ResourceBundle實例,獲取本地消息,不可序列化。

          3.    Formatter,根據配置格式化一條日志記錄,類似Log4J中的Layout

          JDK Logging支持兩種類型的FormatterSimpleFormatterXMLFormatter,默認采用SimpleFormatter,它先打印日期和時間、LoggerNamesource ClassName、方法名稱,然后換行,在打印日志級別、本地化后的消息,然后換行,打印異常信息。而XMLFormatter實現getHead()getTail()方法,并且將每條記錄寫成一條<record></record>記錄。吐槽一下,它的實現是在是太不專業了~~

          4.    Handler,實現將日志寫入指定目的地,如ConsoleHandlerFileHandlerSocketHandler即對應將日志寫入控制臺、文件、Socket端口。它類似Log4J中的Appender

          Handler包含對FormatterLevelFilter的引用,其中FormatterLogRecord格式化成String字符串;Level定義當前Handler支持的日志級別;而Filter則提供一個用戶自定義的過濾一些日志的擴展點。

          public interface Filter {

                  public boolean isLoggable(LogRecord record);

          }

          用戶可以定義自己的Filter類以實現用戶自定義的過濾邏輯。Handler還定義了encoding屬性,以配置底層日志的輸出格式。

          Handler中最終要的方法是publish(),它實現了真正打印日志消息的邏輯。

          public abstract void close() throws SecurityException;

           

          默認JDK Logging實現了StreamHandler,而StreamHandler有三個子類:ConsoleHandlerFileHandlerSocketHandlerStreamHandler支持的配置有:

          java.util.logging.StreamHandler.level 設置當前Handler支持的級別,默認為FINE

          java.util.logging.StreamHandler.filter 設置當前HandlerFilter,默認為null

          java.util.logging.StreamHandler.formatter 設置當前HandlerFormatter類,默認為SimpleFormatter

          java.util.logging.StreamHandler.encoding 設置當前Handler的編碼方式,默認為null

           

          ConsoleHandler只是將OutputStream設置為System.err,其他實現和StreamHandler類似。

           

          SocketHandlerOutputStream綁定到對應的端口號中,其他也和StreamHandler類似。另外它還增加了兩個配置:java.util.logging.SocketHandler.portjava.util.logging.SocketHandler.host分別對應端口號和主機。

           

          FileHandler支持指定文件名模板(java.util.logging.FileHandler.pattern),文件最大支持大小(java.util.logging.FileHandler.limit,字節為單位,0為沒有限制),循環日志文件數(java.util.logging.FileHandler.count)、對已存在的日志文件是否往后添加(java.util.logging.FileHandler.append)。

          FileHandler支持的文件模板參數有:

          /     目錄分隔符

          %t   系統臨時目錄

          %h 系統當前用戶目錄

          %g 生成的以區別循環日志文件名

          %u 一個唯一的數字以處理沖突問題

          %% 一個%

          5.    LogManager類,讀取配置文件和管理Logger實例,類似Log4JLogRepository

          LogManager類初始化時,用戶可以通過指定java.util.logging.manager系統屬性以自定義LogManager,默認使用LogManager類本身。初始化完成后創建RootLogger,并將新創建的RootLogger實例加入到LogManager中。LogManager中將所有創建的Logger緩存在loggers字段中(Hashtablename作為keyWeakReference<Logger>作為value)。RootLoggername為空,因而所有對RootLogger的配置都從”.”開始。

           

          Logger的配置支持一下幾種方式:

          <name>.level=INFO|CONFIG….

          handers=<handlerName1>,<handlerName2>…. (”,”分隔或以空格、TAB等字符分隔,全局Handler)

          config=<configClassName1>,<configClassName2>….(自定義類,實現在其構造函數中實現自定義配置)

          <name>.userParentHandlers=true|false|1|0

          <name>.handlers=<handlerName1>,<handlerName2>…(”,”分隔或以空格、TAB等字符分隔)

          <handlerName>.level=INFO|CONFIG….

          以及各自Handler本身支持的配置,具體各自的Handler

           

          用戶可以通過以下方式自定義配置文件:

          a.    設置系統屬性java.util.logging.config.class,由自定義類的構造函數實現自定義配置,如調用LogManagerLogger中的一些靜態方法。

          b.    設置系統屬性java.util.logging.config.file,自定義配置文件路徑。讀取該文件中的內容作為配置信息。

          c.    默認使用${java.home}/lib/logging.properties文件作為配置文件(JDK已經提供了一些默認配置,一般是${JRE_HOME}/lib/logging.properties文件)

           

          類似Log4JLogManager也將Logger構建成樹狀結構,并且對那些暫時沒有真正Logger實例的節點,使用LogNode,同樣構建成樹,這個實現也類似Log4JProvisionNode

          6.    Logger類,用戶打印log接口,類似Log4J中的Logger

          Logger包含nameHandlersresourceBundleNameuseParentHandlersfilteranonymouslevelObjectparentkids等字段。其中其他字段都比較容易理解,anonymous比較難理解,按注釋,它是用來表達當前Logger是一個匿名Logger,即不會被加入到LogManager中,因而也不需要安全檢查,匿名Logger一般在Applet中使用,對Applet不了解,因而也無法做更詳細的解釋。在構建樹時,該Logger同時保持了父節點和子節點的所有引用,對JDK這個Logging的實現一直無力吐槽。

          Logger提供getLogger()接口,這個也是一般用戶直接打交道的接口,傳入name,返回緩存的Logger實例或新創建一個Logger實例。

          對匿名LoggerLogger提供getAnonymousLogger()接口。log(LogRecord)方法是對打印日志的真正實現:

          public void log(LogRecord record) {

              if (record.getLevel().intValue() < levelValue || levelValue == offValue) {

                  return;

              }

              synchronized (this) {

                  if (filter != null && !filter.isLoggable(record)) {

                      return;

                  }

              }

              Logger logger = this;

              while (logger != null) {

                  Handler targets[] = logger.getHandlers();

                  if (targets != null) {

                      for (int i = 0; i < targets.length; i++) {

                          targets[i].publish(record);

                      }

                  }

                  if (!logger.getUseParentHandlers()) {

                      break;

                  }

                  logger = logger.getParent();

              }

          }

          其他log方法只是對該方法中LogRecord中不同字段參數的組合。其他logp()系類方法只是提供給用戶自定log所在的方法名和類名;而enteringexistingthrown等幾個方法只是幾個傻逼的命名而已。

           

          最后給張JDK Logging的類圖吧:


          posted on 2012-11-08 00:49 DLevin 閱讀(4223) 評論(0)  編輯  收藏 所屬分類: Logging
          主站蜘蛛池模板: 交口县| 都安| 新建县| 博客| 隆化县| 宁乡县| 长兴县| 石渠县| 四会市| 昆山市| 赤城县| 周口市| 田阳县| 马关县| 保康县| 和平县| 贺州市| 广饶县| 平远县| 南京市| 巴林右旗| 榆树市| 阿拉善盟| 阳城县| 仁布县| 息烽县| 潢川县| 聊城市| 开鲁县| 邹城市| 海兴县| 潞西市| 定南县| 巴青县| 容城县| 繁峙县| 宁陵县| 钟祥市| 许昌市| 洛隆县| 白玉县|