Change Dir

          先知cd——熱愛生活是一切藝術(shù)的開始

          統(tǒng)計(jì)

          留言簿(18)

          積分與排名

          “牛”們的博客

          各個(gè)公司技術(shù)

          我的鏈接

          淘寶技術(shù)

          閱讀排行榜

          評(píng)論排行榜

          logback那些事

          logback:

          logback可以認(rèn)為是log4j的升級(jí)版,依然出自Ceki Gülcü,使用簡(jiǎn)單,只需要在你的classpath里包含slf4j-api.jar、logback-core.jar以及l(fā)ogback-classic.jar即可。

          簡(jiǎn)單代碼示例如下:

          import org.slf4j.Logger;
          import org.slf4j.LoggerFactory;

          import ch.qos.logback.classic.LoggerContext;
          import ch.qos.logback.core.util.StatusPrinter;
          public class LogTest {


              public static void main(String[] args) {

                  Logger logger = LoggerFactory
                          .getLogger(LogTest .class.getName());
                  logger.debug("Hello world.");

          }

          其中的logger對(duì)象和LoggerFactory都來自slf4j項(xiàng)目,slf4j是一個(gè)很好的facade,包裝了接口,就像之前寫的一篇文章中的提到的commons-logging框架。

          同樣也可以認(rèn)為logback+slf4j是log4j+commons-logging的升級(jí)版吧。

          logback的架構(gòu):

          logback包含3個(gè)子工程——classic、core和access。core是其他兩個(gè)的基礎(chǔ),也是logback的核心;classic擴(kuò)展了core,內(nèi)置了slf4j,但也支持各種其他log門面。同log4j一樣,logback的主要構(gòu)成也是Logger、Appender和Layout。Logger是核心控制器及調(diào)用入口,Appender主管配置和寫日志實(shí)際process,Layout控制日志樣式,是Appender的重要配置。值得注意的是,3個(gè)基本核心居然不都在core模塊中,Logger是在classic里的。

          對(duì)于Logger來講,和Log4j一樣,Logger是一個(gè)層次結(jié)構(gòu),每個(gè)logger都有一個(gè)name屬性在LoggerFactory中被一個(gè)map管理著。對(duì)于Logback來說,這個(gè)factory就是classic下的LoggerContext。這里插一段自我理解,在做facade模式的時(shí)候,代碼結(jié)構(gòu)可能會(huì)引入一個(gè)占位性質(zhì)的類,就像slf4j中的StaticLoggerBinder,這個(gè)類在org.slf4j.impl包下,是一個(gè)單例,但是私有構(gòu)造函數(shù)卻拋出了一個(gè)異常,這個(gè)在不熟悉這種寫法時(shí)會(huì)產(chǎn)生困惑。其實(shí)這是很合理的,logback的classic中也有org.slf4j.impl這個(gè)包,其中也有StaticLoggerBinder這個(gè)類,但是內(nèi)容完整了許多。這就完成了slf4j的任務(wù),同時(shí)解除了耦合。我認(rèn)為這種解耦合方式非常好,plugin的感覺。

          再回來說LoggerContext,這個(gè)對(duì)應(yīng)了log4j的LogManager和Hierarchy,用一個(gè)hashtable來維護(hù)logger的cache。代碼真的是簡(jiǎn)潔了很多,再回頭看看log4j中LogManager的getLogger方法,就知道logback的簡(jiǎn)潔了,一個(gè)while遍歷省去了一個(gè)hierarchy。當(dāng)然這里得補(bǔ)充一句,log4j包括logback的整個(gè)日志框架對(duì)于logger對(duì)象,是一個(gè)層次結(jié)構(gòu),這也是為什么log4j中有個(gè)Hierarchy的東西的原因。是一個(gè)層次的話,對(duì)于通過包名來管理日志記錄等級(jí)的管理方式來說,就存在著level的控制,也就是說,你某個(gè)包名的類被設(shè)定了日志級(jí)別是什么,那么對(duì)應(yīng)級(jí)別以下的日志才會(huì)被打印出來。有這樣一個(gè)規(guī)則,logger的日志記錄有效level由hierarchy中離它最近且方向向上(upwards)的一個(gè)logger的級(jí)別決定。如官網(wǎng)上的例子:

          image

          這里有4個(gè)logger,但是只有root被設(shè)定了level是DEBUG,其他幾個(gè)logger由于沒有被設(shè)定,依照規(guī)則,就都是root的level了。

          image

          第二個(gè)例子中,每個(gè)logger都自己設(shè)定了level,那么依據(jù)規(guī)則,離它最近的被使用,當(dāng)然自己離自己最近了。

          image

          第三個(gè)例子里,X.Y沒有設(shè)定level,那么離它最近且upwards的一個(gè)是X,那么X.Y的level就和X的一樣。

          level控制是日志框架的基礎(chǔ),什么樣的日志在什么環(huán)境下被打印出來,這種設(shè)定可以配置才是一個(gè)合理的日志系統(tǒng)。一貫的level控制規(guī)則如下顯示:

          image

          appender這個(gè)東西,和log4j是一樣的,支持一個(gè)logger有多個(gè)appender,在AppenderAttachableImpl里會(huì)維護(hù)一個(gè)CopyOnWriteArrayList來存放一個(gè)logger的appender。每次log的時(shí)候都會(huì)遍歷這個(gè)list里的appender然后調(diào)用對(duì)應(yīng)的doAppend方法。我們?cè)谂渲玫臅r(shí)候每個(gè)logger的配置上有個(gè)additivity屬性,默認(rèn)為true。appender這個(gè)東西同logger一樣有繼承性。additivity屬性就是控制這種繼承的,true代表開啟,false代表關(guān)閉,一般使用都會(huì)設(shè)置false,因?yàn)槿绻莟rue,那么如果appender比較多的話可能日志打的就有點(diǎn)太離譜了。

          Layout和log4j一樣,我沒有細(xì)研究過,但是我認(rèn)為這是控制輸出的一大法寶,下次研究layout的時(shí)候做一個(gè)詳細(xì)的分享,一般大家都使用patternLayout,寫個(gè)表達(dá)式足以。

          在之前的那篇寫log的文章中,提到過一個(gè)優(yōu)化寫法,就是不要直接寫出這樣的代碼:

          logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));

          構(gòu)造string參數(shù)是復(fù)雜且耗時(shí)的。要用if判斷一下。而slf4j提供了比較合適的解決方法:

          Object entry = new SomeObject(); 
          logger.debug("The entry is {}.", entry);

          這樣的代碼就更符合程序設(shè)計(jì)人員的編寫習(xí)慣,而且可讀性我認(rèn)為要遠(yuǎn)遠(yuǎn)高于用+號(hào)連接。但是遺憾的是,這種編碼風(fēng)格作者并沒有持續(xù)貫徹下去,沒有用變長(zhǎng)參數(shù),而是用的object[]終止了參數(shù)的個(gè)數(shù)。也就是說,你對(duì)于多個(gè)變量的log的話,只能這么寫:

          Object[] paramArray = {newVal, below, above};
          logger.debug("Value {} was inserted between {} and {}.", paramArray);

          在執(zhí)行l(wèi)og的時(shí)候,會(huì)有一個(gè)判斷流程,依據(jù)官網(wǎng)上的介紹,我簡(jiǎn)要翻譯一下:

          1,Get the filter chain decision:如果存在,嘗試調(diào)用TurboFilterTurboFilter 會(huì)設(shè)置一個(gè)整個(gè)上下文范圍的閾值或者過濾掉每個(gè)log請(qǐng)求的相關(guān)參數(shù)。如果這個(gè)filter返回的參數(shù)是FilterReply.DENY ,那么log結(jié)束;如果FilterReply.NEUTRAL返回,則進(jìn)入第2步,如果FilterReply.ACCEPT ,直接第3步。

          2, Apply the basic selection rule,如果log請(qǐng)求的閾值和高于配置的閾值,那么放棄處理該log。

          3, Create a LoggingEvent object,logback會(huì)構(gòu)建一個(gè)LoggingEvent對(duì)象,包含了所有的請(qǐng)求參數(shù)。其中有些參數(shù)可能是延遲加載的。

          4, Invoking appenders,logback調(diào)用doAppend方法。

          5, Formatting the output,layout會(huì)把LoggingEvent對(duì)象按固定格式格式化并返回字符串形式,像SocketAppender這樣的方法不會(huì)返回字符串,相似的只會(huì)把它序列化。

          6,  Sending out the LoggingEvent,把最終形式打印到對(duì)應(yīng)的目的地址。

          流程圖見這里http://logback.qos.ch/manual/underTheHood.html

          最后還是通過性能討論結(jié)束這篇短文,性能的東西,我們不去看代碼的話,是無法估計(jì)復(fù)雜度的變化的。那么就官網(wǎng)上給出的3條提示,第一點(diǎn)針對(duì)參數(shù)構(gòu)建,第二點(diǎn)針對(duì)level的定位,這個(gè)在看過代碼后,發(fā)現(xiàn)確實(shí)精簡(jiǎn)了,尤其是那種復(fù)雜的hierarchy結(jié)構(gòu)沒有了,線性的鏈條運(yùn)行起來明顯會(huì)快,算是去除冗余做了優(yōu)化吧。第3條針對(duì)說format和write會(huì)快,尤其是format被大力投入改進(jìn),這個(gè)在看過代碼后,可以做個(gè)比較。

          本文算是一篇半自主半翻譯的文章吧,重在學(xué)習(xí)。

          參考文獻(xiàn):

          http://logback.qos.ch/manual/introduction.html

          posted on 2012-03-31 17:14 changedi 閱讀(2926) 評(píng)論(1)  編輯  收藏 所屬分類: Java技術(shù)

          主站蜘蛛池模板: 磴口县| 安龙县| 揭西县| 古田县| 嘉定区| 呈贡县| 金昌市| 太保市| 兴城市| 永年县| 丘北县| 黄山市| 冕宁县| 喀什市| 万荣县| 深水埗区| 永胜县| 嘉鱼县| 蕉岭县| 成安县| 南皮县| 巴彦淖尔市| 姚安县| 乐清市| 基隆市| 宝清县| 丹江口市| 克什克腾旗| 米林县| 舒兰市| 鄱阳县| 芦溪县| 六枝特区| 当雄县| 石屏县| 建始县| 永丰县| 商都县| 托克逊县| 望谟县| 平江县|