記錄複雜的訊息(高效)
讓我們看一下你可以在許多程式中看到的日誌記錄示例:
public class LoggingComplex {
private static final Logger logger =
Logger.getLogger(LoggingComplex.class.getName());
private int total = 50, orders = 20;
private String username = "Bob";
public void takeOrder() {
// (...) making some stuff
logger.fine(String.format("User %s ordered %d things (%d in total)",
username, orders, total));
// (...) some other stuff
}
// some other methods and calculations
}
上面的例子看起來非常好,但許多程式設計師忘記了 Java VM 是堆疊機器。這意味著在執行方法之前計算所有方法的引數。
這個事實對於 Java 登入至關重要,尤其是用於記錄低階別的內容,如 FINE
,FINER
,FINEST
,預設情況下禁用。讓我們看一下 takeOrder()
方法的 Java 位元組碼。
javap -c LoggingComplex.class
的結果是這樣的:
public void takeOrder();
Code:
0: getstatic #27 // Field logger:Ljava/util/logging/Logger;
3: ldc #45 // String User %s ordered %d things (%d in total)
5: iconst_3
6: anewarray #3 // class java/lang/Object
9: dup
10: iconst_0
11: aload_0
12: getfield #40 // Field username:Ljava/lang/String;
15: aastore
16: dup
17: iconst_1
18: aload_0
19: getfield #36 // Field orders:I
22: invokestatic #47 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
25: aastore
26: dup
27: iconst_2
28: aload_0
29: getfield #34 // Field total:I
32: invokestatic #47 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
35: aastore
36: invokestatic #53 // Method java/lang/String.format:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
39: invokevirtual #59 // Method java/util/logging/Logger.fine:(Ljava/lang/String;)V
42: return
第 39 行執行實際記錄。所有以前的工作(載入變數,建立新物件,在 format
方法中連線字串)如果日誌級別設定為高於 FINE
(預設情況下),則無效。這種日誌記錄效率非常低,消耗了不必要的記憶體和處理器資源。
這就是為什麼你應該詢問是否啟用了你要使用的級別。
正確的方法應該是:
public void takeOrder() {
// making some stuff
if (logger.isLoggable(Level.FINE)) {
// no action taken when there's no need for it
logger.fine(String.format("User %s ordered %d things (%d in total)",
username, orders, total));
}
// some other stuff
}
從 Java 8 開始:
Logger 類還有其他方法,它們將 Supplier<String>
作為引數,可以簡單地由 lambda 提供:
public void takeOrder() {
// making some stuff
logger.fine(() -> String.format("User %s ordered %d things (%d in total)",
username, orders, total));
// some other stuff
}
供應商 get()
方法 - 在這種情況下是 lambda - 僅在啟用相應級別時呼叫,因此不再需要 if
construction。