共用方式為


.NET 程式庫作者的記錄指導

身為程式庫作者,公開記錄是讓取用者深入了解程式庫內部運作的絕佳方式。 本指南可協助您以與其他 .NET 程式庫和架構一致的方式公開記錄。 它還可協助您避免可能不明顯的常見效能瓶頸。

何時使用 ILoggerFactory 介面

撰寫發出記錄的程式庫時,您需要 ILogger 物件來記錄記錄檔。 若要取得該物件,您的 API 可以接受 ILogger<TCategoryName> 參數,或者可以接受 ILoggerFactory,之後您可以呼叫 ILoggerFactory.CreateLogger。 應該優先使用哪一種方法?

  • 當您需要一個可傳遞至多個類別的記錄物件,以便所有類別都能發出記錄時,請使用 ILoggerFactory。 建議每個類別建立具有個別類別的記錄,其名稱與類別相同。 若要這樣做,您需要 Factory 為每個發出記錄的類別建立唯一的 ILogger<TCategoryName> 物件。 常見範例包括程式庫的公用進入點 API,或可能內部建立協助程式類別之型別的公用建構函式。

  • 當您需要只在一個類別內使用且永不共用的記錄物件時,請使用 ILogger<TCategoryName>,其中 TCategoryName 是產生記錄的型別。 常見的範例是相依性插入所建立類別的建構函式。

如果您要設計一個必須隨著時間保持穩定的公用 API,請記住,您可能希望在將來重構內部實作。 即使類別一開始未建立任何內部協助程序型別,這可能會隨著程式碼的發展而變更。 使用 ILoggerFactory 配合為任何新類別建立新的 ILogger<TCategoryName> 物件,而不需要變更公用 API。

如需詳細資訊,請參閱如何套用篩選規則

偏好來源產生的記錄

ILogger API 支援使用 API 的兩種方法。 您可以呼叫 LoggerExtensions.LogErrorLoggerExtensions.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);
}

上述 程式碼:

  • 定義名為 LogMessagespartial class,其類別是 static,因此可用來定義 ILogger 型別的擴充方法。
  • 使用 LoggerMessage 屬性和 Message 範本裝飾LogProductSaleDetails 擴充方法。
  • 宣告 LogProductSaleDetails,其會擴充 ILogger 並接受 quantitydescription

提示

您可以在偵錯期間逐步執行來源產生的程式碼,因為其與呼叫它的程式碼為相同組件的一部分。

使用 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 中高效能記錄

避免記錄中的字串內插補點

常見的錯誤是使用字串內插補點來建置記錄訊息。 記錄中的字串內插補點會對效能產生問題,因為即使未啟用對應的 LogLevel 字串,也會評估字串。 使用記錄訊息範本、格式設定和引數清單,而不是字串內插補點。 如需詳細資訊,請參閱 .NET 中的記錄:記錄訊息範本

使用無作業記錄預設值

有時候,取用會公開預期 ILogger 或 的 ILoggerFactory記錄 API 的連結庫時,您不想要提供記錄器。 在這些情況下, Microsoft.Extensions.Logging.Abstractions NuGet 套件會提供無作業記錄預設值。

如果沒有ILoggerFactory提供連結庫取用者,連結庫取用者可以預設為 Null 記錄Null 記錄的使用與將型別定義為可為 Null (ILoggerFactory?)不同,因為型別為非 Null。 這些以效能為基礎的型別不會記錄任何內容,而且基本上是無作業記錄。 如果適用,請考慮使用任何可用的抽象類型: