Pokyny k protokolování pro autory knihoven .NET

Jako autor knihovny představuje zveřejnění protokolování skvělý způsob, jak uživatelům poskytnout přehled o vnitřních pracovních činnostech vaší knihovny. Tyto pokyny vám pomůžou odhalit protokolování způsobem, který je konzistentní s ostatními knihovnami a architekturami .NET. Pomáhá také vyhnout se běžným kritickým bodům výkonu, které nemusí být jinak zřejmé.

Kdy použít ILoggerFactory rozhraní

Při zápisu knihovny, která generuje protokoly, potřebujete ILogger objekt k zaznamenání protokolů. Pokud chcete tento objekt získat, vaše rozhraní API může buď přijmout ILogger<TCategoryName> parametr, nebo může přijmout volání ILoggerFactoryILoggerFactory.CreateLogger. Který přístup by měl být upřednostňovaný?

  • Pokud potřebujete objekt protokolování, který lze předat do více tříd, aby všechny z nich mohly generovat protokoly, použijte ILoggerFactory. Doporučuje se, aby každá třída vytvářela protokoly s samostatnou kategorií s názvem stejná jako třída. K tomu potřebujete, aby továrna vytvořila jedinečné ILogger<TCategoryName> objekty pro každou třídu, která generuje protokoly. Mezi běžné příklady patří rozhraní API veřejného vstupního bodu pro knihovnu nebo veřejné konstruktory typů, které můžou interně vytvářet pomocné třídy.

  • Pokud potřebujete objekt protokolování, který se používá pouze v jedné třídě a nikdy nesdílí, použijte ILogger<TCategoryName>, kde TCategoryName je typ, který vytváří protokoly. Běžným příkladem je konstruktor pro třídu vytvořenou injektáž závislostí.

Pokud navrhujete veřejné rozhraní API, které musí v průběhu času zůstat stabilní, mějte na paměti, že v budoucnu budete chtít refaktorovat interní implementaci. I když třída zpočátku nevytvoří žádné interní pomocné typy, může se to při vývoji kódu změnit. Umožňuje ILoggerFactory vytvářet nové ILogger<TCategoryName> objekty pro všechny nové třídy beze změny veřejného rozhraní API.

Další informace naleznete v tématu Jak se používají pravidla filtrování.

Preferujte protokolování generované zdrojem

Rozhraní ILogger API podporuje dva přístupy k používání rozhraní API. Můžete buď volat metody, jako LoggerExtensions.LogError je a LoggerExtensions.LogInformation, nebo můžete použít generátor zdroje protokolování k definování metod protokolování silného typu. Ve většině situací se doporučuje generátor zdroje, protože nabízí vynikající výkon a silnější psaní. Izoluje také aspekty specifické pro protokolování, jako jsou šablony zpráv, ID a úrovně protokolů, od volajícího kódu. Negenerovaný přístup je primárně užitečný pro scénáře, ve kterých jste ochotni tyto výhody vzdát, aby byl kód výstižnější.

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);
}

Předchozí kód:

  • Definuje pojmenovanou , partial class což je static tak, aby bylo možné použít k definování rozšiřujících metod typuILogger.LogMessages
  • Ozdobí rozšiřující metodu LogProductSaleDetails atributem LoggerMessage a Message šablonou.
  • Deklaruje LogProductSaleDetails, který rozšiřuje ILogger a přijímá a quantitydescription.

Tip

Během ladění můžete vstupovat do zdrojového kódu, protože je součástí stejného sestavení jako kód, který ho volá.

Slouží IsEnabled k zabránění nákladnému vyhodnocení parametrů.

V situacích, kdy je vyhodnocování parametrů nákladné. Rozšířením předchozího příkladu description si představte, že parametr je nákladný string pro výpočty. Možná, že produkt, který se prodává, získá popis přívětivého produktu a spoléhá na databázový dotaz nebo čte ze souboru. V těchto situacích můžete dát generátoru zdroje pokyn, aby stráž přeskočí IsEnabled a ručně přidal IsEnabled stráž na místo volání. To uživateli umožňuje určit, kde se stráž volá, a zajišťuje, aby parametry, které by mohly být nákladné pro výpočty, se vyhodnocují pouze v případě, že je skutečně potřeba. Uvažujte následující kód:

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 Při zavolání IsEnabled metody rozšíření je ochrana vyvolána ručně a nákladné vyhodnocení parametrů je omezené na to, kdy je potřeba. Uvažujte následující kód:

if (_logger.IsEnabled(LogLevel.Information))
{
    // Expensive parameter evaluation
    var description = product.GetFriendlyProductDescription();

    _logger.LogProductSaleDetails(
        quantity,
        description);
}

Další informace najdete v tématu Generování zdroje protokolování v době kompilace a protokolování s vysokým výkonem v .NET.

Vyhněte se interpolaci řetězců při protokolování

Běžnou chybou je použití interpolace řetězců k vytváření zpráv protokolu. Interpolace řetězců v protokolování je problematická pro výkon, protože se řetězec vyhodnotí i v případě, že odpovídající LogLevel hodnota není povolená. Místo interpolace řetězců použijte šablonu zprávy protokolu, formátování a seznam argumentů. Další informace naleznete v tématu Protokolování v .NET: Šablona zprávy protokolu.

Použití výchozích hodnot protokolování no-op

Někdy může docházet k tomu, že používáte knihovnu, která zveřejňuje rozhraní API protokolování, která očekávají, ILogger nebo ILoggerFactory, že nechcete poskytnout protokolovací nástroj. V těchto případech balíček NuGet Microsoft.Extensions.Logging.Abstractions poskytuje výchozí hodnoty protokolování no-op.

Uživatelé knihovny můžou v případě, že nejsou k dispozici, ILoggerFactory výchozí protokolování s hodnotou null. Použití protokolování null se liší od definování typů jako null (ILoggerFactory?), protože typy nejsou null. Tyto typy založené na pohodlí nic nezapíše a jsou v podstatě no-ops. Zvažte použití některého z dostupných typů abstrakce, pokud je to možné: