Hochleistungsprotokollierung mit LoggerMessage in ASP.NET CoreHigh-performance logging with LoggerMessage in ASP.NET Core

LoggerMessage-Features erstellen Delegate, die zwischengespeichert werden können und die im Vergleich zu Methoden zur Protokollierungserweiterung (wie z. B. LogInformation und LogDebug) weniger Objektzuweisungen benötigen und den Rechenaufwand reduzieren.LoggerMessage features create cacheable delegates that require fewer object allocations and reduced computational overhead compared to logger extension methods, such as LogInformation and LogDebug. Verwenden Sie das LoggerMessage-Muster für Hochleistungsprotokollierungen.For high-performance logging scenarios, use the LoggerMessage pattern.

LoggerMessage hat im Vergleich zu Protokollierungserweiterungsmethoden die folgenden Vorzüge:LoggerMessage provides the following performance advantages over Logger extension methods:

  • Protokollierungserweiterungsmethoden erfordern das Konvertieren von Werttypen wie int in object (sogenanntes Boxing).Logger extension methods require "boxing" (converting) value types, such as int, into object. Das LoggerMessage-Muster verhindert das Konvertieren mithilfe von statischen Action-Feldern und mithilfe von Erweiterungsmethoden mit stark typisierten Parametern.The LoggerMessage pattern avoids boxing by using static Action fields and extension methods with strongly-typed parameters.
  • Protokollierungserweiterungsmethoden müssen die Meldungsvorlagen (sogenannte Formatzeichenfolgen) jedes Mal analysieren, wenn eine Protokollmeldung geschrieben wird.Logger extension methods must parse the message template (named format string) every time a log message is written. LoggerMessage erfordert das Analysieren einer Vorlage nur einmal beim Festlegen der Meldung.LoggerMessage only requires parsing a template once when the message is defined.

Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)View or download sample code (how to download)

Die Beispiel-App veranschaulicht LoggerMessage-Features mit einem einfachen System zur Zitatnachverfolgung.The sample app demonstrates LoggerMessage features with a basic quote tracking system. Die App fügt Zitate hinzu und löscht diese mithilfe einer speicherinternen Datenbank.The app adds and deletes quotes using an in-memory database. Während dieser Vorgänge werden Protokollmeldungen mit dem LoggerMessage-Muster erstellt.As these operations occur, log messages are generated using the LoggerMessage pattern.

LoggerMessage.DefineLoggerMessage.Define

Define(LogLevel, EventId, String) erstellt einen Action-Delegaten zum Protokollieren von Meldungen.Define(LogLevel, EventId, String) creates an Action delegate for logging a message. Define überlädt die Zulassung und übergibt bis zu sechs Typparameter an eine benannte Formatzeichenfolge (Vorlage).Define overloads permit passing up to six type parameters to a named format string (template).

Die für die Methode Define bereitgestellte Zeichenfolge ist eine Vorlage und keine interpolierte Zeichenfolge.The string provided to the Define method is a template and not an interpolated string. Platzhalter werden in der Reihenfolge der angegebenen Typen ersetzt.Placeholders are filled in the order that the types are specified. Die Platzhalternamen in den Vorlagen sollten eindeutig und in allen Vorlagen einheitlich sein.Placeholder names in the template should be descriptive and consistent across templates. Sie fungieren als Eigenschaftennamen in strukturierten Protokolldaten.They serve as property names within structured log data. Es wird empfohlen, für Platzhalternamen die Pascal-Schreibweise zu verwenden.We recommend Pascal casing for placeholder names. Platzhalter in einer derartigen Schreibweise sind z.B. {Count} und {FirstName}.For example, {Count}, {FirstName}.

Jede Protokollmeldung ist ein Action-Objekt, das in einem von LoggerMessage.Define erstellten statischen Feld enthalten ist.Each log message is an Action held in a static field created by LoggerMessage.Define. Beispiel: Die Beispiel-App erstellt ein Feld, das eine Protokollmeldung für eine GET-Anforderung für die Indexseite beschreibt (Internal/LoggerExtensions.cs):For example, the sample app creates a field to describe a log message for a GET request for the Index page (Internal/LoggerExtensions.cs):

private static readonly Action<ILogger, Exception> _indexPageRequested;

Geben Sie für das Action-Objekt Folgendes an:For the Action, specify:

  • die ProtokollierungsebeneThe log level.
  • Einen eindeutigen Ereignis-Bezeichner (EventId) mit dem Namen der statischen ErweiterungsmethodeA unique event identifier (EventId) with the name of the static extension method.
  • die Meldungsvorlage (eine benannte Formatzeichenfolge)The message template (named format string).

Eine Anforderung der Indexseite der Beispiel-App legt Folgendes fest:A request for the Index page of the sample app sets the:

  • Die Protokollierungsebene ist Information.Log level to Information.
  • Die Ereignis-ID ist 1 mit dem Namen der IndexPageRequested-Methode.Event id to 1 with the name of the IndexPageRequested method.
  • Die Meldungsvorlage (eine benannte Formatzeichenfolge) ist eine Zeichenfolge.Message template (named format string) to a string.
_indexPageRequested = LoggerMessage.Define(
    LogLevel.Information, 
    new EventId(1, nameof(IndexPageRequested)), 
    "GET request for Index page");

Strukturierte Protokollspeicher verwenden möglicherweise den Ereignisnamen wenn dieser mit der Ereignis-ID bereitgestellt wird. Dadurch wird die Protokollierung ergänzt.Structured logging stores may use the event name when it's supplied with the event id to enrich logging. Serilog verwendet z.B. den Ereignisnamen.For example, Serilog uses the event name.

Das Action-Objekt wird mit einer stark typisierten Erweiterungsmethode aufgerufen.The Action is invoked through a strongly-typed extension method. Die IndexPageRequested-Methode protokolliert eine Meldung für eine GET-Anforderung der Indexseite in der Beispiel-App:The IndexPageRequested method logs a message for an Index page GET request in the sample app:

public static void IndexPageRequested(this ILogger logger)
{
    _indexPageRequested(logger, null);
}

IndexPageRequested wird in der Protokollierung in der OnGetAsync-Methode in Pages/Index.cshtml.cs aufgerufen:IndexPageRequested is called on the logger in the OnGetAsync method in Pages/Index.cshtml.cs:

public async Task OnGetAsync()
{
    _logger.IndexPageRequested();

    Quotes = await _db.Quotes.AsNoTracking().ToListAsync();
}

Sehen Sie sich folgende Konsolenausgabe der App an:Inspect the app's console output:

info: LoggerMessageSample.Pages.IndexModel[1]
      => RequestId:0HL90M6E7PHK4:00000001 RequestPath:/ => /Index
      GET request for Index page

Definieren Sie bis zu sechs Typen, wenn Sie das statische Feld erstellen, um Parameter an eine Protokollmeldung zu übergeben.To pass parameters to a log message, define up to six types when creating the static field. Die Beispiel-App protokolliert eine Zeichenfolge beim Hinzufügen eines Zitats durch die Definition eines string-Typs für das Action-Feld:The sample app logs a string when adding a quote by defining a string type for the Action field:

private static readonly Action<ILogger, string, Exception> _quoteAdded;

Die Protokollmeldungsvorlage des Delegaten erhält ihre Platzhalterwerte von den bereitgestellten Typen.The delegate's log message template receives its placeholder values from the types provided. Die Beispiel-App definiert einen Delegaten zu Hinzufügen eines Zitats, wo der Zitatparameter ein string-Objekt ist:The sample app defines a delegate for adding a quote where the quote parameter is a string:

_quoteAdded = LoggerMessage.Define<string>(
    LogLevel.Information, 
    new EventId(2, nameof(QuoteAdded)), 
    "Quote added (Quote = '{Quote}')");

QuoteAdded, die statische Erweiterungsmethode zum Hinzufügen von Zitaten, erhält den Wert des Zitatarguments und übergibt diesen an den Action-Delegaten:The static extension method for adding a quote, QuoteAdded, receives the quote argument value and passes it to the Action delegate:

public static void QuoteAdded(this ILogger logger, string quote)
{
    _quoteAdded(logger, quote, null);
}

Im Seitenmodell der Indexseite (Pages/Index.cshtml.cs) wird QuoteAdded aufgerufen, um die Meldung zu protokollieren:In the Index page's page model (Pages/Index.cshtml.cs), QuoteAdded is called to log the message:

public async Task<IActionResult> OnPostAddQuoteAsync()
{
    _db.Quotes.Add(Quote);
    await _db.SaveChangesAsync();

    _logger.QuoteAdded(Quote.Text);

    return RedirectToPage();
}

Sehen Sie sich folgende Konsolenausgabe der App an:Inspect the app's console output:

info: LoggerMessageSample.Pages.IndexModel[2]
      => RequestId:0HL90M6E7PHK5:0000000A RequestPath:/ => /Index
      Quote added (Quote = 'You can avoid reality, but you cannot avoid the 
          consequences of avoiding reality. - Ayn Rand')

Die Beispiel-App implementiert ein try-catch-Muster für das Löschen von Zitaten.The sample app implements a try-catch pattern for quote deletion. Es wird eine Meldung protokolliert, die angibt, dass der Löschvorgang erfolgreich war.An informational message is logged for a successful delete operation. Es wird eine Fehlermeldung protokolliert, die angibt, dass bei einem Löschvorgang eine Ausnahme ausgelöst wurde.An error message is logged for a delete operation when an exception is thrown. Die Protokollmeldung für den fehlgeschlagenen Löschvorgang enthält die Ausnahmenprotokollnachverfolgung (Internal/LoggerExtensions.cs):The log message for the unsuccessful delete operation includes the exception stack trace (Internal/LoggerExtensions.cs):

private static readonly Action<ILogger, string, int, Exception> _quoteDeleted;
private static readonly Action<ILogger, int, Exception> _quoteDeleteFailed;
_quoteDeleted = LoggerMessage.Define<string, int>(
    LogLevel.Information, 
    new EventId(4, nameof(QuoteDeleted)), 
    "Quote deleted (Quote = '{Quote}' Id = {Id})");

_quoteDeleteFailed = LoggerMessage.Define<int>(
    LogLevel.Error, 
    new EventId(5, nameof(QuoteDeleteFailed)), 
    "Quote delete failed (Id = {Id})");

Beachten Sie, wie die Ausnahme an den Delegaten in QuoteDeleteFailed übergeben wird:Note how the exception is passed to the delegate in QuoteDeleteFailed:

public static void QuoteDeleted(this ILogger logger, string quote, int id)
{
    _quoteDeleted(logger, quote, id, null);
}

public static void QuoteDeleteFailed(this ILogger logger, int id, Exception ex)
{
    _quoteDeleteFailed(logger, id, ex);
}

Im Seitenmodell für die Indexseite ruft ein erfolgreicher Zitatlöschvorgang die QuoteDeleted-Methode in der Protokollierung auf:In the page model for the Index page, a successful quote deletion calls the QuoteDeleted method on the logger. Wenn kein zu löschendes Zitat gefunden wird, wir eine ArgumentNullException ausgelöst.When a quote isn't found for deletion, an ArgumentNullException is thrown. Die Ausnahme wird von der try-catch-Anweisung aufgefangen und durch den Aufruf der QuoteDeleteFailed-Methode in der Protokollierung im catch-Block protokolliert (Pages/Index.cshtml.cs):The exception is trapped by the try-catch statement and logged by calling the QuoteDeleteFailed method on the logger in the catch block (Pages/Index.cshtml.cs):

public async Task<IActionResult> OnPostDeleteQuoteAsync(int id)
{
    try
    {
        var quote = await _db.Quotes.FindAsync(id);
        _db.Quotes.Remove(quote);
        await _db.SaveChangesAsync();

        _logger.QuoteDeleted(quote.Text, id);
    }
    catch (NullReferenceException ex)
    {
        _logger.QuoteDeleteFailed(id, ex);
    }

    return RedirectToPage();
}

Wenn ein Zitat erfolgreich gelöscht wurde, sehen Sie sich die Konsolenausgabe der App an:When a quote is successfully deleted, inspect the app's console output:

info: LoggerMessageSample.Pages.IndexModel[4]
      => RequestId:0HL90M6E7PHK5:00000016 RequestPath:/ => /Index
      Quote deleted (Quote = 'You can avoid reality, but you cannot avoid the 
          consequences of avoiding reality. - Ayn Rand' Id = 1)

Wenn der Zitatlöschvorgang fehlschlägt, sehen Sie sich die Konsolenausgabe der App an.When quote deletion fails, inspect the app's console output. Beachten Sie, dass die Ausnahme in der Protokollmeldung beinhaltet ist:Note that the exception is included in the log message:

LoggerMessageSample.Pages.IndexModel: Error: Quote delete failed (Id = 999)

System.NullReferenceException: Object reference not set to an instance of an object.
   at lambda_method(Closure , ValueBuffer )
   at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
   at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryShapedQueryCompilingExpressionVisitor.AsyncQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
   at LoggerMessageSample.Pages.IndexModel.OnPostDeleteQuoteAsync(Int32 id) in c:\Users\guard\Documents\GitHub\Docs\aspnetcore\fundamentals\logging\loggermessage\samples\3.x\LoggerMessageSample\Pages\Index.cshtml.cs:line 77

LoggerMessage.DefineScopeLoggerMessage.DefineScope

DefineScope(String) erstellt einen Func<TResult>-Delegaten zum Definieren eines Protokollbereichs.DefineScope(String) creates a Func<TResult> delegate for defining a log scope. DefineScope überlädt die Zulassung und übergibt bis zu drei Typparameter an eine benannte Formatzeichenfolge (Vorlage).DefineScope overloads permit passing up to three type parameters to a named format string (template).

Wie bei der Methode Define ist die Zeichenfolge,die für die Methode DefineScope bereitgestellt wurde, eine Vorlage und keine interpolierte Zeichenfolge.As is the case with the Define method, the string provided to the DefineScope method is a template and not an interpolated string. Platzhalter werden in der Reihenfolge der angegebenen Typen ersetzt.Placeholders are filled in the order that the types are specified. Die Platzhalternamen in den Vorlagen sollten eindeutig und in allen Vorlagen einheitlich sein.Placeholder names in the template should be descriptive and consistent across templates. Sie fungieren als Eigenschaftennamen in strukturierten Protokolldaten.They serve as property names within structured log data. Es wird empfohlen, für Platzhalternamen die Pascal-Schreibweise zu verwenden.We recommend Pascal casing for placeholder names. Platzhalter in einer derartigen Schreibweise sind z.B. {Count} und {FirstName}.For example, {Count}, {FirstName}.

Definieren Sie einen Protokollbereich, um mehrere Protokollmeldungen mit der DefineScope-Methode anzuwenden.Define a log scope to apply to a series of log messages using the DefineScope method.

Die Beispiel-App verfügt über die Schaltfläche Clear all (Alles löschen) zum Löschen aller Zitate in der Datenbank.The sample app has a Clear All button for deleting all of the quotes in the database. Die Zitate werden nacheinander entfernt und gelöscht.The quotes are deleted by removing them one at a time. Bei jedem Zitatlöschvorgang wird die QuoteDeleted-Methode in der Protokollierung aufgerufen.Each time a quote is deleted, the QuoteDeleted method is called on the logger. Diesen Protokollmeldung wird ein Protokollbereich hinzugefügt.A log scope is added to these log messages.

Aktivieren Sie IncludeScopes in der Datei appsettings.json im Abschnitt „Konsolenprotokollierung“:Enable IncludeScopes in the console logger section of appsettings.json:

{
  "Logging": {
    "Console": {
      "IncludeScopes": true
    },
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Fügen Sie zum Erstellen eines Protokollbereichs ein Feld hinzu, dass einen Func<TResult>-Delegaten für den Bereich enthält.To create a log scope, add a field to hold a Func<TResult> delegate for the scope. Die Beispiel-App erstellt ein Feld mit dem Namen _allQuotesDeletedScope (Internal/LoggerExtensions.cs):The sample app creates a field called _allQuotesDeletedScope (Internal/LoggerExtensions.cs):

private static Func<ILogger, int, IDisposable> _allQuotesDeletedScope;

Verwenden Sie DefineScope zum Erstellen eines Delegaten.Use DefineScope to create the delegate. Sie können bis zu drei Typen als Vorlagenargumente festlegen, die verwendet werden können, wenn der Delegat aufgerufen wird.Up to three types can be specified for use as template arguments when the delegate is invoked. Die Beispiel-App verwendet eine Meldungsvorlage, die die Anzahl der gelöschten Zitate enthält (ein int-Typ):The sample app uses a message template that includes the number of deleted quotes (an int type):

_allQuotesDeletedScope = 
    LoggerMessage.DefineScope<int>("All quotes deleted (Count = {Count})");

Stellen Sie eine statische Erweiterungsmethode für die Protokollmeldung bereit.Provide a static extension method for the log message. Beziehen Sie Typparameter für benannte Eigenschaften mit ein, die in der Meldungsvorlage vorhanden sind.Include any type parameters for named properties that appear in the message template. Die Beispiel-App erstellt ein count der zu löschenden Zitate und gibt _allQuotesDeletedScope zurück:The sample app takes in a count of quotes to delete and returns _allQuotesDeletedScope:

public static IDisposable AllQuotesDeletedScope(
    this ILogger logger, int count)
{
    return _allQuotesDeletedScope(logger, count);
}

Der Bereich umschließt die Protokollerweiterungsaufrufe in einem using-Block:The scope wraps the logging extension calls in a using block:

public async Task<IActionResult> OnPostDeleteAllQuotesAsync()
{
    var quoteCount = await _db.Quotes.CountAsync();

    using (_logger.AllQuotesDeletedScope(quoteCount))
    {
        foreach (Quote quote in _db.Quotes)
        {
            _db.Quotes.Remove(quote);

            _logger.QuoteDeleted(quote.Text, quote.Id);
        }
        await _db.SaveChangesAsync();
    }

    return RedirectToPage();
}

Sehen Sie sich die Protokollmeldungen in der Konsolenausgabe der App an.Inspect the log messages in the app's console output. Das folgende Ergebnis zeigt drei gelöschte Zitate sowie die Protokollbereichsmeldung:The following result shows three quotes deleted with the log scope message included:

info: LoggerMessageSample.Pages.IndexModel[4]
      => RequestId:0HL90M6E7PHK5:0000002E RequestPath:/ => /Index => 
          All quotes deleted (Count = 3)
      Quote deleted (Quote = 'Quote 1' Id = 2)
info: LoggerMessageSample.Pages.IndexModel[4]
      => RequestId:0HL90M6E7PHK5:0000002E RequestPath:/ => /Index => 
          All quotes deleted (Count = 3)
      Quote deleted (Quote = 'Quote 2' Id = 3)
info: LoggerMessageSample.Pages.IndexModel[4]
      => RequestId:0HL90M6E7PHK5:0000002E RequestPath:/ => /Index => 
          All quotes deleted (Count = 3)
      Quote deleted (Quote = 'Quote 3' Id = 4)

LoggerMessage-Features erstellen Delegate, die zwischengespeichert werden können und die im Vergleich zu Methoden zur Protokollierungserweiterung (wie z. B. LogInformation und LogDebug) weniger Objektzuweisungen benötigen und den Rechenaufwand reduzieren.LoggerMessage features create cacheable delegates that require fewer object allocations and reduced computational overhead compared to logger extension methods, such as LogInformation and LogDebug. Verwenden Sie das LoggerMessage-Muster für Hochleistungsprotokollierungen.For high-performance logging scenarios, use the LoggerMessage pattern.

LoggerMessage hat im Vergleich zu Protokollierungserweiterungsmethoden die folgenden Vorzüge:LoggerMessage provides the following performance advantages over Logger extension methods:

  • Protokollierungserweiterungsmethoden erfordern das Konvertieren von Werttypen wie int in object (sogenanntes Boxing).Logger extension methods require "boxing" (converting) value types, such as int, into object. Das LoggerMessage-Muster verhindert das Konvertieren mithilfe von statischen Action-Feldern und mithilfe von Erweiterungsmethoden mit stark typisierten Parametern.The LoggerMessage pattern avoids boxing by using static Action fields and extension methods with strongly-typed parameters.
  • Protokollierungserweiterungsmethoden müssen die Meldungsvorlagen (sogenannte Formatzeichenfolgen) jedes Mal analysieren, wenn eine Protokollmeldung geschrieben wird.Logger extension methods must parse the message template (named format string) every time a log message is written. LoggerMessage erfordert das Analysieren einer Vorlage nur einmal beim Festlegen der Meldung.LoggerMessage only requires parsing a template once when the message is defined.

Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)View or download sample code (how to download)

Die Beispiel-App veranschaulicht LoggerMessage-Features mit einem einfachen System zur Zitatnachverfolgung.The sample app demonstrates LoggerMessage features with a basic quote tracking system. Die App fügt Zitate hinzu und löscht diese mithilfe einer speicherinternen Datenbank.The app adds and deletes quotes using an in-memory database. Während dieser Vorgänge werden Protokollmeldungen mit dem LoggerMessage-Muster erstellt.As these operations occur, log messages are generated using the LoggerMessage pattern.

LoggerMessage.DefineLoggerMessage.Define

Define(LogLevel, EventId, String) erstellt einen Action-Delegaten zum Protokollieren von Meldungen.Define(LogLevel, EventId, String) creates an Action delegate for logging a message. Define überlädt die Zulassung und übergibt bis zu sechs Typparameter an eine benannte Formatzeichenfolge (Vorlage).Define overloads permit passing up to six type parameters to a named format string (template).

Die für die Methode Define bereitgestellte Zeichenfolge ist eine Vorlage und keine interpolierte Zeichenfolge.The string provided to the Define method is a template and not an interpolated string. Platzhalter werden in der Reihenfolge der angegebenen Typen ersetzt.Placeholders are filled in the order that the types are specified. Die Platzhalternamen in den Vorlagen sollten eindeutig und in allen Vorlagen einheitlich sein.Placeholder names in the template should be descriptive and consistent across templates. Sie fungieren als Eigenschaftennamen in strukturierten Protokolldaten.They serve as property names within structured log data. Es wird empfohlen, für Platzhalternamen die Pascal-Schreibweise zu verwenden.We recommend Pascal casing for placeholder names. Platzhalter in einer derartigen Schreibweise sind z.B. {Count} und {FirstName}.For example, {Count}, {FirstName}.

Jede Protokollmeldung ist ein Action-Objekt, das in einem von LoggerMessage.Define erstellten statischen Feld enthalten ist.Each log message is an Action held in a static field created by LoggerMessage.Define. Beispiel: Die Beispiel-App erstellt ein Feld, das eine Protokollmeldung für eine GET-Anforderung für die Indexseite beschreibt (Internal/LoggerExtensions.cs):For example, the sample app creates a field to describe a log message for a GET request for the Index page (Internal/LoggerExtensions.cs):

private static readonly Action<ILogger, Exception> _indexPageRequested;

Geben Sie für das Action-Objekt Folgendes an:For the Action, specify:

  • die ProtokollierungsebeneThe log level.
  • Einen eindeutigen Ereignis-Bezeichner (EventId) mit dem Namen der statischen ErweiterungsmethodeA unique event identifier (EventId) with the name of the static extension method.
  • die Meldungsvorlage (eine benannte Formatzeichenfolge)The message template (named format string).

Eine Anforderung der Indexseite der Beispiel-App legt Folgendes fest:A request for the Index page of the sample app sets the:

  • Die Protokollierungsebene ist Information.Log level to Information.
  • Die Ereignis-ID ist 1 mit dem Namen der IndexPageRequested-Methode.Event id to 1 with the name of the IndexPageRequested method.
  • Die Meldungsvorlage (eine benannte Formatzeichenfolge) ist eine Zeichenfolge.Message template (named format string) to a string.
_indexPageRequested = LoggerMessage.Define(
    LogLevel.Information, 
    new EventId(1, nameof(IndexPageRequested)), 
    "GET request for Index page");

Strukturierte Protokollspeicher verwenden möglicherweise den Ereignisnamen wenn dieser mit der Ereignis-ID bereitgestellt wird. Dadurch wird die Protokollierung ergänzt.Structured logging stores may use the event name when it's supplied with the event id to enrich logging. Serilog verwendet z.B. den Ereignisnamen.For example, Serilog uses the event name.

Das Action-Objekt wird mit einer stark typisierten Erweiterungsmethode aufgerufen.The Action is invoked through a strongly-typed extension method. Die IndexPageRequested-Methode protokolliert eine Meldung für eine GET-Anforderung der Indexseite in der Beispiel-App:The IndexPageRequested method logs a message for an Index page GET request in the sample app:

public static void IndexPageRequested(this ILogger logger)
{
    _indexPageRequested(logger, null);
}

IndexPageRequested wird in der Protokollierung in der OnGetAsync-Methode in Pages/Index.cshtml.cs aufgerufen:IndexPageRequested is called on the logger in the OnGetAsync method in Pages/Index.cshtml.cs:

public async Task OnGetAsync()
{
    _logger.IndexPageRequested();

    Quotes = await _db.Quotes.AsNoTracking().ToListAsync();
}

Sehen Sie sich folgende Konsolenausgabe der App an:Inspect the app's console output:

info: LoggerMessageSample.Pages.IndexModel[1]
      => RequestId:0HL90M6E7PHK4:00000001 RequestPath:/ => /Index
      GET request for Index page

Definieren Sie bis zu sechs Typen, wenn Sie das statische Feld erstellen, um Parameter an eine Protokollmeldung zu übergeben.To pass parameters to a log message, define up to six types when creating the static field. Die Beispiel-App protokolliert eine Zeichenfolge beim Hinzufügen eines Zitats durch die Definition eines string-Typs für das Action-Feld:The sample app logs a string when adding a quote by defining a string type for the Action field:

private static readonly Action<ILogger, string, Exception> _quoteAdded;

Die Protokollmeldungsvorlage des Delegaten erhält ihre Platzhalterwerte von den bereitgestellten Typen.The delegate's log message template receives its placeholder values from the types provided. Die Beispiel-App definiert einen Delegaten zu Hinzufügen eines Zitats, wo der Zitatparameter ein string-Objekt ist:The sample app defines a delegate for adding a quote where the quote parameter is a string:

_quoteAdded = LoggerMessage.Define<string>(
    LogLevel.Information, 
    new EventId(2, nameof(QuoteAdded)), 
    "Quote added (Quote = '{Quote}')");

QuoteAdded, die statische Erweiterungsmethode zum Hinzufügen von Zitaten, erhält den Wert des Zitatarguments und übergibt diesen an den Action-Delegaten:The static extension method for adding a quote, QuoteAdded, receives the quote argument value and passes it to the Action delegate:

public static void QuoteAdded(this ILogger logger, string quote)
{
    _quoteAdded(logger, quote, null);
}

Im Seitenmodell der Indexseite (Pages/Index.cshtml.cs) wird QuoteAdded aufgerufen, um die Meldung zu protokollieren:In the Index page's page model (Pages/Index.cshtml.cs), QuoteAdded is called to log the message:

public async Task<IActionResult> OnPostAddQuoteAsync()
{
    _db.Quotes.Add(Quote);
    await _db.SaveChangesAsync();

    _logger.QuoteAdded(Quote.Text);

    return RedirectToPage();
}

Sehen Sie sich folgende Konsolenausgabe der App an:Inspect the app's console output:

info: LoggerMessageSample.Pages.IndexModel[2]
      => RequestId:0HL90M6E7PHK5:0000000A RequestPath:/ => /Index
      Quote added (Quote = 'You can avoid reality, but you cannot avoid the 
          consequences of avoiding reality. - Ayn Rand')

Die Beispiel-App implementiert ein try-catch-Muster für das Löschen von Zitaten.The sample app implements a try-catch pattern for quote deletion. Es wird eine Meldung protokolliert, die angibt, dass der Löschvorgang erfolgreich war.An informational message is logged for a successful delete operation. Es wird eine Fehlermeldung protokolliert, die angibt, dass bei einem Löschvorgang eine Ausnahme ausgelöst wurde.An error message is logged for a delete operation when an exception is thrown. Die Protokollmeldung für den fehlgeschlagenen Löschvorgang enthält die Ausnahmenprotokollnachverfolgung (Internal/LoggerExtensions.cs):The log message for the unsuccessful delete operation includes the exception stack trace (Internal/LoggerExtensions.cs):

private static readonly Action<ILogger, string, int, Exception> _quoteDeleted;
private static readonly Action<ILogger, int, Exception> _quoteDeleteFailed;
_quoteDeleted = LoggerMessage.Define<string, int>(
    LogLevel.Information, 
    new EventId(4, nameof(QuoteDeleted)), 
    "Quote deleted (Quote = '{Quote}' Id = {Id})");

_quoteDeleteFailed = LoggerMessage.Define<int>(
    LogLevel.Error, 
    new EventId(5, nameof(QuoteDeleteFailed)), 
    "Quote delete failed (Id = {Id})");

Beachten Sie, wie die Ausnahme an den Delegaten in QuoteDeleteFailed übergeben wird:Note how the exception is passed to the delegate in QuoteDeleteFailed:

public static void QuoteDeleted(this ILogger logger, string quote, int id)
{
    _quoteDeleted(logger, quote, id, null);
}

public static void QuoteDeleteFailed(this ILogger logger, int id, Exception ex)
{
    _quoteDeleteFailed(logger, id, ex);
}

Im Seitenmodell für die Indexseite ruft ein erfolgreicher Zitatlöschvorgang die QuoteDeleted-Methode in der Protokollierung auf:In the page model for the Index page, a successful quote deletion calls the QuoteDeleted method on the logger. Wenn kein zu löschendes Zitat gefunden wird, wir eine ArgumentNullException ausgelöst.When a quote isn't found for deletion, an ArgumentNullException is thrown. Die Ausnahme wird von der try-catch-Anweisung aufgefangen und durch den Aufruf der QuoteDeleteFailed-Methode in der Protokollierung im catch-Block protokolliert (Pages/Index.cshtml.cs):The exception is trapped by the try-catch statement and logged by calling the QuoteDeleteFailed method on the logger in the catch block (Pages/Index.cshtml.cs):

public async Task<IActionResult> OnPostDeleteQuoteAsync(int id)
{
    var quote = await _db.Quotes.FindAsync(id);

    // DO NOT use this approach in production code!
    // You should check quote to see if it's null before removing 
    // it and saving changes to the database. A try-catch is used 
    // here for demonstration purposes of LoggerMessage features.
    try
    {
        _db.Quotes.Remove(quote);
        await _db.SaveChangesAsync();

        _logger.QuoteDeleted(quote.Text, id);
    }
    catch (ArgumentNullException ex)
    {
        _logger.QuoteDeleteFailed(id, ex);
    }

    return RedirectToPage();
}

Wenn ein Zitat erfolgreich gelöscht wurde, sehen Sie sich die Konsolenausgabe der App an:When a quote is successfully deleted, inspect the app's console output:

info: LoggerMessageSample.Pages.IndexModel[4]
      => RequestId:0HL90M6E7PHK5:00000016 RequestPath:/ => /Index
      Quote deleted (Quote = 'You can avoid reality, but you cannot avoid the 
          consequences of avoiding reality. - Ayn Rand' Id = 1)

Wenn der Zitatlöschvorgang fehlschlägt, sehen Sie sich die Konsolenausgabe der App an.When quote deletion fails, inspect the app's console output. Beachten Sie, dass die Ausnahme in der Protokollmeldung beinhaltet ist:Note that the exception is included in the log message:

fail: LoggerMessageSample.Pages.IndexModel[5]
      => RequestId:0HL90M6E7PHK5:00000010 RequestPath:/ => /Index
      Quote delete failed (Id = 999)
System.ArgumentNullException: Value cannot be null.
Parameter name: entity
   at Microsoft.EntityFrameworkCore.Utilities.Check.NotNull[T]
       (T value, String parameterName)
   at Microsoft.EntityFrameworkCore.DbContext.Remove[TEntity](TEntity entity)
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.Remove(TEntity entity)
   at LoggerMessageSample.Pages.IndexModel.<OnPostDeleteQuoteAsync>d__14.MoveNext() 
      in <PATH>\sample\Pages\Index.cshtml.cs:line 87

LoggerMessage.DefineScopeLoggerMessage.DefineScope

DefineScope(String) erstellt einen Func<TResult>-Delegaten zum Definieren eines Protokollbereichs.DefineScope(String) creates a Func<TResult> delegate for defining a log scope. DefineScope überlädt die Zulassung und übergibt bis zu drei Typparameter an eine benannte Formatzeichenfolge (Vorlage).DefineScope overloads permit passing up to three type parameters to a named format string (template).

Wie bei der Methode Define ist die Zeichenfolge,die für die Methode DefineScope bereitgestellt wurde, eine Vorlage und keine interpolierte Zeichenfolge.As is the case with the Define method, the string provided to the DefineScope method is a template and not an interpolated string. Platzhalter werden in der Reihenfolge der angegebenen Typen ersetzt.Placeholders are filled in the order that the types are specified. Die Platzhalternamen in den Vorlagen sollten eindeutig und in allen Vorlagen einheitlich sein.Placeholder names in the template should be descriptive and consistent across templates. Sie fungieren als Eigenschaftennamen in strukturierten Protokolldaten.They serve as property names within structured log data. Es wird empfohlen, für Platzhalternamen die Pascal-Schreibweise zu verwenden.We recommend Pascal casing for placeholder names. Platzhalter in einer derartigen Schreibweise sind z.B. {Count} und {FirstName}.For example, {Count}, {FirstName}.

Definieren Sie einen Protokollbereich, um mehrere Protokollmeldungen mit der DefineScope-Methode anzuwenden.Define a log scope to apply to a series of log messages using the DefineScope method.

Die Beispiel-App verfügt über die Schaltfläche Clear all (Alles löschen) zum Löschen aller Zitate in der Datenbank.The sample app has a Clear All button for deleting all of the quotes in the database. Die Zitate werden nacheinander entfernt und gelöscht.The quotes are deleted by removing them one at a time. Bei jedem Zitatlöschvorgang wird die QuoteDeleted-Methode in der Protokollierung aufgerufen.Each time a quote is deleted, the QuoteDeleted method is called on the logger. Diesen Protokollmeldung wird ein Protokollbereich hinzugefügt.A log scope is added to these log messages.

Aktivieren Sie IncludeScopes in der Datei appsettings.json im Abschnitt „Konsolenprotokollierung“:Enable IncludeScopes in the console logger section of appsettings.json:

{
  "Logging": {
    "Console": {
      "IncludeScopes": true
    },
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Fügen Sie zum Erstellen eines Protokollbereichs ein Feld hinzu, dass einen Func<TResult>-Delegaten für den Bereich enthält.To create a log scope, add a field to hold a Func<TResult> delegate for the scope. Die Beispiel-App erstellt ein Feld mit dem Namen _allQuotesDeletedScope (Internal/LoggerExtensions.cs):The sample app creates a field called _allQuotesDeletedScope (Internal/LoggerExtensions.cs):

private static Func<ILogger, int, IDisposable> _allQuotesDeletedScope;

Verwenden Sie DefineScope zum Erstellen eines Delegaten.Use DefineScope to create the delegate. Sie können bis zu drei Typen als Vorlagenargumente festlegen, die verwendet werden können, wenn der Delegat aufgerufen wird.Up to three types can be specified for use as template arguments when the delegate is invoked. Die Beispiel-App verwendet eine Meldungsvorlage, die die Anzahl der gelöschten Zitate enthält (ein int-Typ):The sample app uses a message template that includes the number of deleted quotes (an int type):

_allQuotesDeletedScope = 
    LoggerMessage.DefineScope<int>("All quotes deleted (Count = {Count})");

Stellen Sie eine statische Erweiterungsmethode für die Protokollmeldung bereit.Provide a static extension method for the log message. Beziehen Sie Typparameter für benannte Eigenschaften mit ein, die in der Meldungsvorlage vorhanden sind.Include any type parameters for named properties that appear in the message template. Die Beispiel-App erstellt ein count der zu löschenden Zitate und gibt _allQuotesDeletedScope zurück:The sample app takes in a count of quotes to delete and returns _allQuotesDeletedScope:

public static IDisposable AllQuotesDeletedScope(
    this ILogger logger, int count)
{
    return _allQuotesDeletedScope(logger, count);
}

Der Bereich umschließt die Protokollerweiterungsaufrufe in einem using-Block:The scope wraps the logging extension calls in a using block:

public async Task<IActionResult> OnPostDeleteAllQuotesAsync()
{
    var quoteCount = await _db.Quotes.CountAsync();

    using (_logger.AllQuotesDeletedScope(quoteCount))
    {
        foreach (Quote quote in _db.Quotes)
        {
            _db.Quotes.Remove(quote);

            _logger.QuoteDeleted(quote.Text, quote.Id);
        }
        await _db.SaveChangesAsync();
    }

    return RedirectToPage();
}

Sehen Sie sich die Protokollmeldungen in der Konsolenausgabe der App an.Inspect the log messages in the app's console output. Das folgende Ergebnis zeigt drei gelöschte Zitate sowie die Protokollbereichsmeldung:The following result shows three quotes deleted with the log scope message included:

info: LoggerMessageSample.Pages.IndexModel[4]
      => RequestId:0HL90M6E7PHK5:0000002E RequestPath:/ => /Index => 
          All quotes deleted (Count = 3)
      Quote deleted (Quote = 'Quote 1' Id = 2)
info: LoggerMessageSample.Pages.IndexModel[4]
      => RequestId:0HL90M6E7PHK5:0000002E RequestPath:/ => /Index => 
          All quotes deleted (Count = 3)
      Quote deleted (Quote = 'Quote 2' Id = 3)
info: LoggerMessageSample.Pages.IndexModel[4]
      => RequestId:0HL90M6E7PHK5:0000002E RequestPath:/ => /Index => 
          All quotes deleted (Count = 3)
      Quote deleted (Quote = 'Quote 3' Id = 4)

Zusätzliche RessourcenAdditional resources