2011-12-09 70 views
24

幫我在辯論在這裏.. :)即使使用slf4j,你應該保護你的日誌嗎?

這裏http://www.slf4j.org/faq.html#logging_performance的SLF4J網站表示由於參數測井,錄井衛兵是沒有必要的。即而不是寫:

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

你可以逃脫:

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

這真的是好,或者它招致了(雖然更低)創建傳遞給此跟蹤方法的靜態字符串的成本..?

回答

33

我會盡力把我的兩分錢從另一個角度

究竟是什麼參數化日誌記錄的好處?

你只是推遲toString()調用字符串連接,直到真正需要的,這是當你真的要記錄消息。當特定的日誌記錄操作被禁用時,這優化了性能。如果不確定,請檢查source code for SLF4J

參數化日誌記錄是否使守衛在所有情況下都毫無用處?

在哪些情況下會記錄衛士是有用的?

當有其他潛在的昂貴的操作。

例如(在這個特殊的記錄操作被禁用的情況下),如果我們有沒有記錄後衛

logger.debug("User: {}", getUserService().getCurrentUser()); 
  1. 我們將支付成本從obj = getUserService().getCurrentUser()
  2. 我們將節省成本來自"User name: " + obj.toString()

如果我們使用記錄後衛

if (logger.isDebugEnabled()) { 
    logger.debug("User: {}", getUserService().getCurrentUser()); 
} 
  1. 我們將支付logger.isDebugEnabled()
  2. 成本,我們將節省成本從obj = getUserService().getCurrentUser()
  3. 我們將節省的成本從"User name: " + obj.toString()

在後面的c當啓用此特定日誌記錄操作時,我們會以檢查isDebugEnabled()兩次的價格節省兩項成本。

注意:這僅僅是一個例子,不是試圖在這裏辯論好/壞的做法。

+0

謝謝fgelz,這是最明確的解釋,所以我改變了主意。讓你認爲,儘管有所有不同的框架,但在日誌世界中仍然有創新的空間,在推遲昂貴的操作方面..也許與代表 –

+0

@MarkD JDK 8項目Lambda將幫助創新(http:// openjdk .java.net/projects/lambda /) – fglez

+0

你也應該知道調用可變參數方法(如logger.debug(String,Object ..)實際上是在封面logger.debug(String,new Object [所以如果你沒有包裝這些調用,那麼你每次遍歷這個方法時都會創建一個新的Object數組,即使你從來沒有寫過內容,如果你有一個高性能的系統或者經歷內存壓力,那麼隨着時間的推移,這可能會增加;如果使用了很多,logger.isDebugEnabled()調用將被一個體面的JIT所吸引,SLF4J專門爲此提供了一個和兩個參數,但是對於更多的參數流失 – AlBlue

2

請不要使用if語句,因爲我每次看這樣的代碼

if (logger.isDebug()) { 
    logger.debug("Of course it's debug {}", sadFace); 
} 

我哭了。

我希望創建靜態字符串的成本很低,對於99%的用戶來說是微不足道的。

+1

slf4j不使用'{0}'構造。 –

+0

但是,如果您正在使用大量調試日誌記錄,那麼大多數大型應用程序都會執行這些日誌記錄,但這可能會造成很大的開銷。您正在爲垃圾收集器創建更多垃圾,並且一些系統已經在該區域中具有足夠的壓力。如果你沒有任何調用來獲取你的參數,並且你只使用一個常量字符串作爲參數字符串,那很好,直到有人出現並改變它做一些稍微不重要的事情。從「編碼標準」的角度來看,要求if語句更容易。 – Dogs

15

寫作和閱讀所有這些if(logger.isDebugEnabled()) {}可能會花費盡可能多的時間,因爲他們爲您節省。

當然,調用日誌方法不是免費的,但調用isDebugEnabled()的情況也是如此。因此,如果您使用此模式(因爲日誌記錄框架將兩次檢查該級別),您將爲每個活動的日誌語句支付更多費用。

它也混亂了代碼。

在實踐中,我沒有發現性能損失足夠大。

如果日誌是你太慢了,寫一個無阻塞的appender是推動日誌事件到隊列中,而不只是幾張支票,並使用一個後臺線程來處理它們。

背景:標準appender都是同步的,因此在多線程應用程序中登錄可能導致大量小暫停,其中所有線程都等待將日誌消息寫入文件。

+0

感謝這就是我的想法..將等待看到什麼一般conscensus,雖然我給你一個加號:)我喜歡關於appenders的提示,這可能是我們的問題。 –

+0

「調用日誌方法」和「調用isDebugEnabled()」可以**實際上免費**,我認爲有一些JIT優化。我不確定,但如果是爲sl4jf提供實際高性能實現的人員,我會在rutime中爲禁用級別放置一個無操作方法的實現,以便JIT可以刪除該調用。 –

4

有這樣的記錄語句的問題:

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

的是,它會做很多工作來值連接成一個String,而如果調試日誌關閉時則從未使用過。因此,在這種情況下,在執行此行之前檢查調試日誌記錄是否有效。當你只是路過參數:

logger.debug("The entry is {}.", entry); 

那麼它不會不必要地需要建立一個從未使用String,並檢查是沒有必要的;只是將參數傳遞給方法沒有很高的開銷。

請注意,如果您在日誌記錄語句中的參數表達式相對較高,那麼首先檢查日誌記錄級別可能仍然有好處。

+0

您可以通過將它們放入匿名類中的toString()方法來處理昂貴的表達式,以便在實際需要之前不對其進行評估。 –

+0

@ThorbjørnRavnAndersen是的,但這會使代碼非常冗長。 – Jesper

5

由於字符串的創建,不使用警衛。

相反,它通常用於避免潛在的昂貴參數表達式,如entry[i].retrieveExtendedDebugInformation().formatNicely()。爲此,logback確保僅在實際打印日誌消息時才計算參數,而在調用debug()之前,log4j總是評估參數。

這裏唯一的候選人是String.valueOf(entry[i]),這也不是很貴,所以你可以說這個守衛是完全不需要的。

+1

好的 - 這是有道理的,是對事物的另一種觀點。所以在你看來,這是一個判斷是否需要警惕,而守衛只應該用於相對較少(昂貴的)操作?..引入減少這些操作數量的參數。 –

+0

是的,並且取決於多昂貴操作常常只在跟蹤循環內的語句時纔有意義。人們甚至可以爭辯說,如果你的調試語句太昂貴,甚至當你處於更高的日誌級別時,它們會減慢課程速度,那麼你不會使用log.debug,特別是,你可能正在執行while條件){log.debug(complicatedExpression); doSomething();}'而你應該做'log.debug(complicatedExpression);而(條件){log.trace(simpleExpression); doSomething的();}'。但我不是狂熱者,我承認我經常寫警衛。 – wallenborn

+0

「非常昂貴」的代價仍然很高,無法爲擁有大量日誌語句的程序帶來不同。 –

相關問題