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>
, kdeTCategoryName
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ž jestatic
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
atributemLoggerMessage
aMessage
šablonou. - Deklaruje
LogProductSaleDetails
, který rozšiřujeILogger
a přijímá aquantity
description
.
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é: