Sdílet prostřednictvím


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 parametr ILogger<TCategoryName>, nebo přijmout ILoggerFactory a poté zavolat ILoggerFactory.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 se samostatnou kategorií, která je pojmenovaná stejně jako třída. K tomu potřebujete, aby továrna vytvořila jedinečné ILogger<TCategoryName> objekty pro každou třídu, která logy generuje. 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 pomocí vkládání 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. Použití ILoggerFactory umožňuje vytváření nových objektů ILogger<TCategoryName> pro jakékoli nové třídy, aniž by se měnilo veřejné 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 a LoggerExtensions.LogInformation, nebo můžete použít generátor logovacího zdroje k definování silně typovaných metod protokolování. Ve většině situací se doporučuje generátor zdroje, protože nabízí vynikající výkon a silnější typování. 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 partial class pojmenovanou LogMessages, což je static, aby ji bylo možné použít k definování rozšiřujících metod na typu ILogger.
  • Ozdobí rozšiřující metodu LogProductSaleDetails atributem LoggerMessage a Message šablonou.
  • Deklaruje LogProductSaleDetails, které rozšiřuje ILogger a přijímá quantity a description.

Návod

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ů.

Mohou nastat situace, kdy je vyhodnocení parametrů nákladné. Představte si rozšíření předchozího příkladu: parametr description je string nákladný na výpočet. Možná produkt, který se prodává, obdrží přívětivý popis a spoléhá na dotaz do databáze nebo čtení ze souboru. V těchto situacích můžete dát pokyn generátoru zdroje, aby ochranu IsEnabled přeskočil a ručně přidal IsEnabled ochranu na místo volání. To uživateli umožňuje určit, kde se ochrana volá, a zajišťuje, aby parametry, které by mohly být nákladné na výpočet, byly vyhodnocovány pouze tehdy, když jsou skutečně potřeba. Vezměte v úvahu 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. Vezměte v úvahu 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 rámci protokolování je problematická z hlediska výkonu, protože řetězec se vyhodnotí, i když odpovídající LogLevel 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žijte výchozí nastavení protokolování no-op

Může se stát, že používáte knihovnu, která zveřejňuje rozhraní API protokolování a očekává buď ILogger nebo ILoggerFactory, ale vy nechcete poskytnout logger. V těchto případech balíček NuGet Microsoft.Extensions.Logging.Abstractions poskytuje výchozí nastavení protokolování no-op.

Uživatelé knihovny mohou použít protokolování s hodnotou null jako výchozí, pokud není ILoggerFactory k dispozici. Použití nulového protokolování se liší od definování typů jako nulových (ILoggerFactory?), protože typy nejsou nulové. 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é: