作為圖書館作者,公開日誌是讓消費者深入了解圖書館內部運作的絕佳方式。 這份指引幫助你以與其他 .NET 函式庫和框架一致的方式,公開日誌。 這也有助於你避免常見的效能瓶頸。
何時使用此介面ILoggerFactory
撰寫會產生日誌的函式庫時,你需要一個 ILogger 物件來記錄日誌。 要取得該物件,你的 API 可以接受參數 ILogger<TCategoryName> ,或接受參數 ILoggerFactory ,然後你呼叫 ILoggerFactory.CreateLogger。 應該優先採用哪一種方法?
- 當你需要一個可以傳遞給多個類別、讓所有類別都能發出日誌的日誌物件時,請使用
ILoggerFactory。 建議每個類別建立一個獨立類別的日誌,該類別名稱與該類別相同。 為此,你需要工廠為每個會發出日誌的類別建立獨特的ILogger<TCategoryName>物件。 常見例子包括庫的公開入口點 API,或是可能在內部建立輔助類別的公開建構子。 - 當你需要一個只在單一類別中使用且從未共享的日誌物件時,可以使用
ILogger<TCategoryName>,其中TCategoryName是產生日誌的型別。 一個常見的例子是依賴注入所建立類別的建構子。
如果你設計的公開 API 必須隨時間穩定,請記得未來你可能會想要重構內部實作。 即使類別一開始沒有建立任何內部輔助型別,隨著程式碼演進,這也可能改變。 使用 ILoggerFactory 這個功能可以讓你在不改變公開 API 的情況下,為任何新類別建立新 ILogger<TCategoryName> 物件。
欲了解更多資訊,請參閱 「如何套用過濾規則」。
偏好由源代碼產生的日誌
API ILogger 支援兩種使用 API 的方法。 你可以呼叫LoggerExtensions.LogError和LoggerExtensions.LogInformation等方法,或者使用日誌源代碼生成器來定義強型別的日誌方法。 在大多數情況下,建議使用原始碼產生器,因為它提供更優異的效能和更強的型別。 它也會將日誌特定的問題,如訊息範本、ID 和日誌層級,從呼叫程式碼中分離出來。 非原始碼生成的方法主要適用於願意放棄這些優勢以讓程式碼更簡潔的情境。
using Microsoft.Extensions.Logging;
namespace Logging.LibraryAuthors;
internal static partial class LogMessages
{
[LoggerMessage(
Message = "Sold {Quantity} of {Description}",
Level = LogLevel.Information)]
internal static partial void LogProductSaleDetails(
this ILogger logger,
int quantity,
string description);
}
上述 程式碼:
- 定義了一個名為
LogMessages的partial class,static,以便可以在ILogger型別上定義擴展方法。 - 用
LoggerMessage屬性和Message範本裝飾LogProductSaleDetails擴充方法。 - 宣告
LogProductSaleDetails,擴展ILogger,並接受quantity和description。
小提示
你可以在除錯時直接進入原始碼產生的程式碼,因為它和呼叫它的程式碼是同一個組合語言的一部分。
使用 IsEnabled 來避免昂貴的參數評估
有些情況下,評估參數可能會很昂貴。 在前一個例子基礎上,假設 description 參數是計算成本高昂的 string。 也許所售產品會獲得友善的產品描述,並依賴資料庫查詢或從檔案中讀取。 在這種情況下,你可以指示源產生器跳過 IsEnabled 呼叫器,手動在呼叫地點新增 IsEnabled 呼叫器。 這讓使用者能判斷守衛被呼叫的位置,並確保只有在真正需要時才評估可能計算昂貴的參數。 請考慮下列程式碼:
using Microsoft.Extensions.Logging;
namespace Logging.LibraryAuthors;
internal static partial class LogMessages
{
[LoggerMessage(
Message = "Sold {Quantity} of {Description}",
Level = LogLevel.Information,
SkipEnabledCheck = true)]
internal static partial void LogProductSaleDetails(
this ILogger logger,
int quantity,
string description);
}
當 LogProductSaleDetails 擴充方法被呼叫時,IsEnabled 守護會被手動啟動,且高成本的參數評估僅在必要時進行。 請考慮下列程式碼:
if (_logger.IsEnabled(LogLevel.Information))
{
// Expensive parameter evaluation
var description = product.GetFriendlyProductDescription();
_logger.LogProductSaleDetails(
quantity,
description);
}
欲了解更多資訊,請參閱 編譯時日誌來源生成 與 .NET 中的高效能日誌。
避免在 log 記錄中使用字串插值
一個常見錯誤是使用 字串插值 來建立日誌訊息。 日誌中的字串插值對效能來說是有問題的,因為即使對應 LogLevel 的字串未啟用,字串仍會被評估。 與其使用字串插值,不如使用日誌訊息範本、格式和參數清單。 更多資訊請參閱 登入 .NET:日誌訊息範本。
使用 no-op 日誌預設值
當你使用一個暴露日誌 API 的函式庫,而這些 API 預期使用ILogger或ILoggerFactory,有時你可能不想提供記錄器。 在這些情況下, Microsoft.Extensions.Logging.Abstractions NuGet 套件提供 no-op 日誌預設值。
如果沒有提供,圖書館使用者可預設為ILoggerFactory。 空 記錄 的使用與定義可ILoggerFactory?空()型別不同,因為型別是非空的。 這些為方便而設計的類型不會記錄任何內容,基本上不進行任何操作。 在適用時,請考慮使用任一種可用的抽象類型: