[英]Lazy evaluation for logging in Java 8
當您的值比計算成本高時,您在日志框架中看到的常見模式是
if (log.isDebugEnabled()) {
String value = expensiveComputation();
log.debug("value: {}", value);
}
由於 Java 添加了 8 個 lambda,所以這樣做會很好:
log.debug("value: {}", (Supplier<String>) this::expensiveComputation);
這幾乎可以工作,因為日志記錄框架將對參數執行toString()
。 問題是Supplier
上的toString()
是Object
中的實現。
有沒有辦法向Logger
方法提供惰性評估的內容? 它幾乎只是一個具有調用get()
的默認toString()
的Supplier
。
要傳遞將以惰性方式執行String
計算的參數,您必須傳遞Supplier
而不是String
。
您調用的方法應具有以下簽名:
void debug(Supplier<?> msgSupplier, Throwable t)
您可以在自己的實用程序類中引入此實用程序方法。
但是您不需要這樣做,因為最近的日志記錄框架(如Log4j2)提供了開箱即用的功能。
例如, org.apache.logging.log4j.Logger提供了重載方法來記錄接受Supplier
。
例如 :
void debug(MessageSupplier msgSupplier, Throwable t)
記錄消息(僅在日志記錄級別為DEBUG級別時構造),包括作為參數傳遞的Throwable t的堆棧跟蹤。 MessageSupplier可能會也可能不會使用MessageFactory來構造Message。
Parameters
:
msgSupplier
- 一個函數,在調用時會生成所需的日志消息。
t
- 記錄的異常,包括其堆棧跟蹤。
從Log4j2文檔:
Java 8 lambda支持延遲日志記錄
在2.4版中,Logger接口添加了對lambda表達式的支持。 這允許客戶端代碼懶惰地記錄消息,而無需顯式檢查是否啟用了請求的日志級別。 例如,以前你會寫:
if (logger.isTraceEnabled()) { logger.trace("Some long-running operation returned {}", expensiveOperation()); }
使用Java 8,您可以使用lambda表達式實現相同的效果。 您不再需要顯式檢查日志級別:
logger.trace("Some long-running operation returned {}", () -> expensiveOperation());
一個小幫手對象將允許您做你想要的:
public class MessageSupplier {
private Supplier<?> supplier;
public MessageSupplier(Supplier<?> supplier) {
this.supplier = supplier;
}
@Override
public String toString() {
return supplier.get().toString();
}
public static MessageSupplier msg(Supplier<?> supplier) {
return new MessageSupplier(supplier);
}
}
然后,使用靜態導入的msg
:
log.debug("foo: {}", msg(this::expensiveComputation));
這幾乎起作用,因為日志框架將對參數執行
toString()
。
這種說法不正確。 如果你進入debug
/ info
/ whatever方法,你會發現這個實現:
public void log(Level level, Supplier<String> msgSupplier) {
if (!isLoggable(level)) {
return;
}
LogRecord lr = new LogRecord(level, msgSupplier.get());
doLog(lr);
}
如果未達到該level
,則甚至不使用Supplier
。
有趣的是,你甚至不能使用這樣的東西
interface LazyString { String toString(); }
作為功能界面
到目前為止我找到的唯一方法是通過匿名課程。
Object o = new Object() { @Override public String toString() { return myExpensiveComputation(); } }; System.out.printf("%s", o);
對於java.util.logging
和Java 8+,您還可以使用這種懶惰且方便的表示法:
LOGGER.fine(() -> "Message1: " + longComputation1() + ". Message2: " + longComputation2());
longComputation1()
和longComputation2()
將被稱為lazy - 即僅在需要時。
因為你透露你使用的 SLF4J 仍然不支持它的核心,如果你在 Kotlin 中編碼(你可以使用 JDK 8),這可能會派上用場:
fun Logger.info( provider: () -> String ) { if (this.isInfoEnabled) this.info(provider.invoke())
fun Logger.debug( provider: () -> String ) { if (this.isDebugEnabled) this.debug(provider.invoke())
fun Logger.trace( provider: () -> String ) { if (this.isTraceEnabled) this.trace(provider.invoke())
用法:
log.trace { "State dump: " + expensiveLongSerialisation(state) }
資料來源:我的要點:) https://gist.github.com/OndraZizka/a7381b8cd86f734bc3b6bf9e528a01ad
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.