Protokollierungsleitfaden für .NET-Bibliotheksersteller*innen

Für Bibliotheksautor*innen ist die Offenlegung der Protokollierung eine großartige Möglichkeit, Benutzer*innen einen Einblick in das Innenleben Ihrer Bibliothek zu geben. Dieser Leitfaden hilft Ihnen, die Protokollierung auf eine Weise verfügbar zu machen, die mit anderen .NET-Bibliotheken und Frameworks konsistent ist. Er hilft Ihnen auch, häufige Leistungsengpässe zu vermeiden, die andernfalls nicht offensichtlich sind.

Wann die ILoggerFactory-Schnittstelle verwendet werden sollte

Beim Schreiben einer Bibliothek, die Protokolle ausgibt, benötigen Sie ein ILogger-Objekt, um die Protokolle aufzuzeichnen. Um dieses Objekt abzurufen, kann Ihre API entweder einen ILogger<TCategoryName>-Parameter akzeptieren oder eine ILoggerFactory akzeptieren, nach der Sie ILoggerFactory.CreateLogger aufrufen. Welcher Ansatz sollte bevorzugt werden?

  • Wenn Sie ein Protokollierungsobjekt benötigen, das an mehrere Klassen übergeben werden kann, damit alle Protokolle ausgeben können, verwenden Sie ILoggerFactory. Es wird empfohlen, dass jede Klasse Protokolle mit einer separaten Kategorie erstellt, die identisch mit der Klasse ist. Dazu benötigen Sie die Factory, um eindeutige ILogger<TCategoryName>-Objekte für jede Klasse zu erstellen, die Protokolle ausgibt. Häufige Beispiele sind öffentliche Einstiegspunkt-APIs für eine Bibliothek oder öffentliche Konstruktoren von Typen, die Hilfsklassen intern erstellen können.

  • Wenn Sie ein Protokollierungsobjekt benötigen, das nur innerhalb einer Klasse verwendet und nie freigegeben wird, verwenden Sie ILogger<TCategoryName>, wobei TCategoryName der Typ ist, der die Protokolle erzeugt. Ein gängiges Beispiel hierfür ist ein Konstruktor für eine Klasse, die durch Abhängigkeitsinjektion erstellt wurde.

Wenn Sie eine öffentliche API entwerfen, die im Laufe der Zeit stabil bleiben muss, denken Sie daran, dass Sie Ihre interne Implementierung in Zukunft möglicherweise umgestalten wollen. Auch wenn eine Klasse anfänglich keine internen Hilfstypen erstellt, kann sich dies ändern, wenn sich der Code weiterentwickelt. Wenn Sie ILoggerFactory verwenden, können Sie neue ILogger<TCategoryName>-Objekte für neue Klassen erstellen, ohne die öffentliche API zu ändern.

Weitere Informationen finden Sie unter Anwendung von Filterregeln.

Bevorzugen der aus der Quelle generierten Protokollierung

Die ILogger-API unterstützt zwei Ansätze zur Verwendung der API. Sie können Methoden wie LoggerExtensions.LogError und LoggerExtensions.LogInformation aufrufen oder den Protokollierungsquellengenerator verwenden, um stark typisierte Protokollierungsmethoden zu definieren. In den meisten Fällen wird der Quellgenerator empfohlen, da er eine überlegene Leistung und eine stärkere Eingabe bietet. Außerdem werden protokollierungsspezifische Bedenken wie Nachrichtenvorlagen, IDs und Protokollebenen vom aufrufenden Code isoliert. Der nicht aus der Quelle generierte Ansatz ist in erster Linie für Szenarien nützlich, in denen Sie bereit sind, diese Vorteile aufzugeben, um den Code präziser zu gestalten.

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

Der vorangehende Code:

  • Definiert eine partial class namens LogMessages, die static ist, sodass sie zum Definieren von Erweiterungsmethoden für den ILogger-Typ verwendet werden kann.
  • Schmückt eine LogProductSaleDetails-Erweiterungsmethode mit dem Attribut LoggerMessage und der Vorlage Message.
  • Deklariert LogProductSaleDetails, die ILogger erweitert und eine quantity sowie eine description akzeptiert.

Tipp

Sie können während des Debuggens in den aus der Quelle generierten Code eintauchen, da er Teil derselben Assembly ist wie der Code, der ihn aufruft.

Verwenden von IsEnabled, um eine teure Parameterauswertung zu vermeiden

Es kann Situationen geben, in denen die Auswertung von Parametern teuer ist. Wenn Sie das vorherige Beispiel erweitern, stellen Sie sich vor, dass der Parameter description eine string ist, die teuer zu berechnen ist. Vielleicht erhält das verkaufte Produkt eine freundliche Produktbeschreibung und basiert auf einer Datenbankabfrage oder dem Lesen aus einer Datei. In diesen Situationen können Sie den Quellgenerator anweisen, den IsEnabled-Wächter zu überspringen und den IsEnabled-Wächter an der Anrufstelle manuell hinzuzufügen. Auf diese Weise kann der Benutzer ermitteln, wo der Wächter aufgerufen wird, und sicherstellen, dass Parameter, die möglicherweise teuer zu berechnen sind, nur ausgewertet werden, wenn sie wirklich benötigt werden. Betrachten Sie folgenden Code:

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

Wenn die LogProductSaleDetails-Erweiterungsmethode aufgerufen wird, wird der IsEnabled-Wächter manuell aufgerufen, und die teure Parameterauswertung ist bei Bedarf beschränkt. Betrachten Sie folgenden Code:

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

    _logger.LogProductSaleDetails(
        quantity,
        description);
}

Weitere Informationen finden Sie unter Protokollierung der Quellgenerierung zur Kompilierzeit und Hochleistungsprotokollierung in .NET.

Vermeiden von Zeichenfolgeninterpolation bei der Protokollierung

Ein häufiger Fehler besteht darin, Zeichenfolgeninterpolation zum Erstellen von Protokollnachrichten zu verwenden. Die Zeichenfolgeninterpolation in der Protokollierung ist für die Leistung problematisch, da die Zeichenfolge ausgewertet wird, auch wenn die entsprechende LogLevel nicht aktiviert ist. Verwenden Sie anstelle der Zeichenfolgeninterpolation die Protokollnachrichtenvorlage, Formatierung und Argumentliste. Weitere Informationen finden Sie unter Protokollierung in .NET: Protokollnachrichtenvorlagen.

Verwenden von no-op-Protokollierungseinstellungen

Bei der Nutzung einer Bibliothek, die Protokollierungs-APIs verfügbar macht, die einen der Typen ILogger oder ILoggerFactory erwarten, kann es vorkommen, dass Sie keine Protokollierung bereitstellen möchten. In diesen Fällen stellt das NuGet-Paket Microsoft.Extensions.Logging.Abstractions Standardeinstellungen für die Nichtausführung der Protokollierung bereit.

Bibliotheksconsumer können standardmäßig auf Null Protokollierung festgelegt werden, wenn keine ILoggerFactory bereitgestellt wird. Die Verwendung der Null-Protokollierung unterscheidet sich von der Definition von Typen als nullable (ILoggerFactory?), da die Typen ungleich NULL sind. Diese komfortbasierten Typen protokollieren nichts und sind im Wesentlichen no-ops. Erwägen Sie ggf. die Verwendung der verfügbaren Abstraktionstypen: