歡迎光臨
每天分享高質量文章

惰性日誌

(給ImportNew加星標,提高Java技能)

編譯:ImportNew/唐尤華

itnext.io/lazy-logging-40314cf9bb25

 

在程式碼中新增日誌時要當心:有時儘管並沒有記錄日誌,也會執行日誌陳述句。

 

例如下麵的程式碼:

 

```java
log.debug("I found {} and {}", getone(), gettwo());
```

 

看起來很好,似乎沒有任何問題。日誌輸出字串中包含了兩個除錯引數。

 

所以一切正常,沒有問題?也不盡然。

 

在記錄日誌的程式碼中可以看到下麵兩個輸入:

 

  •  `getone()`
  •  `gettwo()`

 

這兩個方法會”一直“呼叫。這意味著,即使實際可能不會執行 `log` 陳述句,仍然會計算所有 `log` 方法傳入的引數。

 

如果這些呼叫開銷很大,那麼可能會浪費大量 CPU 資源。

 

一種典型的解決方案像下麵這樣:

 

```java
if (log.isDebugEnabled()) {
    log.debug("I found {} and {}", getone(), gettwo());
}
```

 

但這種方法相當醜陋:這不正是 `log.debug` 完成的功能嗎?只在啟用除錯時才記錄日誌。

 

我們需要的是一個單行除錯陳述句,只在啟用日誌記錄時計算輸入的引數。

 

為瞭解決類似的情況,我們將應用 Java 8 Supplier 樣式,只在需要的時候執行計算。

 

不幸的是,大多數日誌框架並不支援 Supplier 樣式。

 

至少是直接支援。

 

實際上,`logger` 並不期望輸入的引數一定是字串,實際引數可以是任意物件。`logger` 會呼叫物件的 `toString` 方法把它轉換為字串。這裡的訣竅是`logger` 只在日誌啟用的情況下呼叫字串。

 

因此,要實現惰性日誌只需要用一個物件來包裝 Supplier,該物件僅在呼叫 toString 時呼叫 Supplier。

 

```java
import java.util.function.Supplier;

public class LazyString {
    private final Supplier> stringSupplier;
    
    public static LazyString lazy(Supplier> stringSupplier) {
        return new LazyString(stringSupplier);
    }

    public LazyString(final Supplier> stringSupplier) {
        this.stringSupplier = stringSupplier;
    }

    @Override
    public String toString() {
        return String.valueOf(stringSupplier.get());
    }
}
```

 

現在可以更新之前的日誌程式碼,結果如下:

 

```java
import static LazyString.lazy;
log.debug("I found {} and {}", lazy(this::getone), lazy(this::gettwo));
```

 

理想情況下,日誌框架能直接支援 Supplier。但在等待的過程中,這也是一種變通的辦法。

 

完整示例程式碼,請檢視我的 Github 倉庫:

贊(0)

分享創造快樂