Wskazówki dotyczące rejestrowania dla autorów bibliotek platformy .NET
Jako autor biblioteki uwidacznianie rejestrowania to doskonały sposób, aby zapewnić użytkownikom wgląd w wewnętrzne działanie biblioteki. Te wskazówki ułatwiają uwidacznianie rejestrowania w sposób zgodny z innymi bibliotekami i strukturami .NET. Pomaga również uniknąć typowych wąskich gardeł wydajności, które mogą nie być w inny sposób oczywiste.
Kiedy używać interfejsu ILoggerFactory
Podczas pisania biblioteki, która emituje dzienniki, potrzebny jest ILogger obiekt do rejestrowania dzienników. Aby uzyskać ten obiekt, interfejs API może zaakceptować parametr lub zaakceptować parametr , po którym wywołasz metodę ILogger<TCategoryName>ILoggerFactoryILoggerFactory.CreateLogger. Które podejście powinno być preferowane?
Jeśli potrzebujesz obiektu rejestrowania, który można przekazać do wielu klas, aby wszystkie z nich mogły emitować dzienniki, użyj polecenia
ILoggerFactory
. Zaleca się, aby każda klasa tworzy dzienniki z oddzielną kategorią o takiej samej nazwie jak klasa. W tym celu należy utworzyć unikatoweILogger<TCategoryName>
obiekty dla każdej klasy, która emituje dzienniki. Typowe przykłady obejmują publiczne interfejsy API punktu wejścia dla biblioteki lub publicznych konstruktorów typów, które mogą tworzyć klasy pomocnicze wewnętrznie.Jeśli potrzebujesz obiektu rejestrowania, który jest używany tylko w jednej klasie i nigdy nie jest udostępniany, użyj ,
ILogger<TCategoryName>
gdzieTCategoryName
jest typem, który generuje dzienniki. Typowym przykładem jest konstruktor klasy utworzonej przez iniekcję zależności.
Jeśli projektujesz publiczny interfejs API, który musi pozostawać stabilny w czasie, pamiętaj, że możesz chcieć refaktoryzować wewnętrzną implementację w przyszłości. Nawet jeśli klasa początkowo nie tworzy żadnych wewnętrznych typów pomocników, może to ulec zmianie w miarę rozwoju kodu. Funkcja umożliwia ILoggerFactory
tworzenie nowych ILogger<TCategoryName>
obiektów dla nowych klas bez zmieniania publicznego interfejsu API.
Aby uzyskać więcej informacji, zobacz Jak są stosowane reguły filtrowania.
Preferuj rejestrowanie generowane przez źródło
Interfejs ILogger
API obsługuje dwa podejścia do korzystania z interfejsu API. Możesz wywołać metody, takie jak LoggerExtensions.LogError i LoggerExtensions.LogInformation, lub użyć generatora źródła rejestrowania do zdefiniowania silnie typiowanych metod rejestrowania. W większości sytuacji zaleca się generator źródła, ponieważ oferuje lepszą wydajność i silniejsze wpisywanie. Izoluje również problemy specyficzne dla rejestrowania, takie jak szablony komunikatów, identyfikatory i poziomy dzienników z kodu wywołującego. Metoda niegenerowana przez źródło jest przydatna przede wszystkim w scenariuszach, w których chcesz zrezygnować z tych zalet, aby kod był bardziej zwięzły.
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);
}
Powyższy kod ma następujące działanie:
- Definiuje nazwany
LogMessages
partial class
element , który umożliwiastatic
definiowanie metod rozszerzeń w typieILogger
. - Dekoruje metodę
LogProductSaleDetails
rozszerzenia za pomocą atrybutuLoggerMessage
iMessage
szablonu. LogProductSaleDetails
Deklaruje element , który rozszerzaILogger
element i akceptuje elementquantity
idescription
.
Napiwek
Możesz przejść do kodu wygenerowanego przez źródło podczas debugowania, ponieważ jest on częścią tego samego zestawu co kod, który go wywołuje.
Użyj IsEnabled
polecenia , aby uniknąć kosztownej oceny parametrów
Mogą wystąpić sytuacje, w których ocena parametrów jest kosztowna. Rozwijając poprzedni przykład, wyobraź sobie description
, że parametr jest kosztowny string
do obliczenia. Być może sprzedawany produkt pobiera przyjazny opis produktu i opiera się na zapytaniu bazy danych lub odczytywaniu z pliku. W takich sytuacjach możesz poinstruować generator źródła, aby pominąć IsEnabled
ochronę i ręcznie dodać IsEnabled
strażnika w lokacji wywołania. Dzięki temu użytkownik może określić, gdzie jest wywoływana ochrona, i gwarantuje, że parametry, które mogą być kosztowne do obliczenia, są oceniane tylko wtedy, gdy jest to naprawdę potrzebne. Spójrzmy na poniższy kod:
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);
}
Po wywołaniu LogProductSaleDetails
metody rozszerzenia funkcja straży IsEnabled
jest wywoływana ręcznie, a kosztowna ocena parametrów jest ograniczona do potrzeb. Spójrzmy na poniższy kod:
if (_logger.IsEnabled(LogLevel.Information))
{
// Expensive parameter evaluation
var description = product.GetFriendlyProductDescription();
_logger.LogProductSaleDetails(
quantity,
description);
}
Aby uzyskać więcej informacji, zobacz Generowanie źródła rejestrowania w czasie kompilacji i Rejestrowanie o wysokiej wydajności na platformie .NET.
Unikaj interpolacji ciągów w rejestrowaniu
Typowym błędem jest użycie interpolacji ciągów do tworzenia komunikatów dziennika. Interpolacja ciągów w rejestrowaniu jest problematyczna dla wydajności, ponieważ ciąg jest oceniany nawet wtedy, gdy odpowiedni LogLevel
ciąg nie jest włączony. Zamiast interpolacji ciągów użyj szablonu komunikatu dziennika, formatowania i listy argumentów. Aby uzyskać więcej informacji, zobacz Rejestrowanie na platformie .NET: szablon komunikatu dziennika.
Używanie wartości domyślnych rejestrowania bez operacji
Mogą wystąpić czasy korzystania z biblioteki, która uwidacznia interfejsy API rejestrowania, które oczekują ILogger
elementu lub ILoggerFactory
, którego nie chcesz udostępniać rejestratorowi. W takich przypadkach pakiet NuGet Microsoft.Extensions.Logging.Abstractions udostępnia wartości domyślne rejestrowania bez operacji.
Użytkownicy biblioteki mogą domyślnie rejestrować wartości null, jeśli nie ILoggerFactory
jest podana. Użycie rejestrowania wartości null różni się od definiowania typów jako dopuszczalnych wartości null (ILoggerFactory?
), ponieważ typy są inne niż null. Te typy oparte na wygodach nie rejestrują niczego i w zasadzie nie są ops. Rozważ użycie dowolnego z dostępnych typów abstrakcji, jeśli ma to zastosowanie: