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ć unikatowe ILogger<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>gdzie TCategoryName 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 LogMessagespartial class element , który umożliwia static definiowanie metod rozszerzeń w typieILogger.
  • Dekoruje metodę LogProductSaleDetails rozszerzenia za pomocą atrybutu LoggerMessage i Message szablonu.
  • LogProductSaleDetailsDeklaruje element , który rozszerza ILogger element i akceptuje element quantity i description.

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: