Protokollierung in C# und .NET
.NET unterstützt über die ILogger-API leistungsstarke, strukturierte Protokollierung, um das Anwendungsverhalten zu überwachen und Probleme zu diagnostizieren. Protokolle können durch die Konfiguration verschiedener Protokollierungsanbieter in verschiedene Ziele geschrieben werden. Grundlegende Protokollierungsanbieter sind integriert. Zudem stehen viele Drittanbieter zur Verfügung.
Erste Schritte
Dieses erste Beispiel zeigt die Grundlagen, eignet sich jedoch nur für eine triviale Konsolen-App. Diese Beispielkonsolen-App basiert auf den folgenden NuGet-Paketen:
Im nächsten Abschnitt erfahren Sie, wie Sie den Code unter Berücksichtigung von Skalierung, Leistung, Konfiguration und typischen Programmiermustern verbessern können.
using Microsoft.Extensions.Logging;
using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
ILogger logger = factory.CreateLogger("Program");
logger.LogInformation("Hello World! Logging is {Description}.", "fun");
Für das vorherige Beispiel gilt Folgendes:
- Erstellt ein ILoggerFactory. In
ILoggerFactory
wird die gesamte Konfiguration gespeichert, die bestimmt, wohin Protokollmeldungen gesendet werden. In diesem Fall konfigurieren Sie die Konsole Protokollierungsanbieter so, dass Protokollmeldungen auf die Konsole geschrieben werden. - Erstellt ein ILogger-Objekt mit einer Kategorie namens „Program“. Die Kategorie ist ein
string
, das jeder vomILogger
-Objekt protokollierten Meldung zugeordnet ist. Es wird verwendet, um Protokollmeldungen aus derselben Klasse (oder Kategorie) beim Durchsuchen oder Filtern von Protokollen zu gruppieren. - Ruft LogInformation auf, um eine Meldung auf der Ebene
Information
zu protokollieren. Die Protokollierungsebene gibt den Schweregrad des protokollierten Ereignisses an und wird verwendet, um weniger wichtige Protokollmeldungen herauszufiltern. Der Protokolleintrag enthält auch eine Nachrichtenvorlage"Hello World! Logging is {Description}."
und ein Schlüssel-Wert-PaarDescription = fun
. Der Schlüsselname (oder Platzhalter) stammt aus dem Wort innerhalb der geschweiften Klammern in der Vorlage, und der Wert stammt aus dem restlichen Methodenargument.
Diese Projektdatei für dieses Beispiel enthält zwei NuGet-Pakete:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.1" />
</ItemGroup>
</Project>
Tipp
Der gesamte Quellcode des Protokollierungsbeispiels steht im Beispielbrowser zum Download zur Verfügung. Weitere Informationen finden Sie unter Durchsuchen von Codebeispielen: Protokollierung in .NET.
Protokollierung in einer nicht trivialen App
Es gibt einige Änderungen, die Sie am vorherigen Beispiel vornehmen sollten, wenn Sie die Protokollierung in einem weniger trivialen Szenario verwenden:
Wenn Ihre Anwendung DI (Dependency Injection) oder einen Host wie WebApplication oder Generic Host von ASP.NET verwendet, dann sollten Sie dann
ILoggerFactory
- bzw.ILogger
-Objekte aus den jeweiligen DI-Containern verwenden, anstatt sie direkt zu erstellen. Weitere Informationen finden Sie unter Integration in DI und Hosts.Die Protokollierung der Quellengenerierung zur Kompilierungszeit ist in der Regel eine bessere Alternative als
ILogger
-Erweiterungsmethoden wieLogInformation
. Die Protokollierung der Quellengenerierung bietet eine bessere Leistung und eine stärkere Typisierung, und sie verhindert, dassstring
-Konstanten in den Methoden verteilt werden. Der Nachteil ist, dass diese Technik ein wenig mehr Code erfordert.
using Microsoft.Extensions.Logging;
internal partial class Program
{
static void Main(string[] args)
{
using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
ILogger logger = factory.CreateLogger("Program");
LogStartupMessage(logger, "fun");
}
[LoggerMessage(Level = LogLevel.Information, Message = "Hello World! Logging is {Description}.")]
static partial void LogStartupMessage(ILogger logger, string description);
}
- Es wird empfohlen, für die Namen der Protokollkategorien den vollständig qualifizierten Namen der Klasse zu verwenden, die die Protokollmeldung erstellt. Dies erleichtert es, Protokollmeldungen dem Code zuzuordnen, der sie erzeugt hat, und bietet beim Filtern von Protokollen ein gutes Maß an Kontrolle. CreateLogger akzeptiert einen
Type
, um diese Benennung zu vereinfachen.
using Microsoft.Extensions.Logging;
internal class Program
{
static void Main(string[] args)
{
using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
ILogger logger = factory.CreateLogger<Program>();
logger.LogInformation("Hello World! Logging is {Description}.", "fun");
}
}
- Wenn Sie Konsolenprotokolle nicht als einzige Produktionsüberwachungslösung verwenden, fügen Sie die Protokollierungsanbieter hinzu, die Sie verwenden möchten. Sie können beispielsweise OpenTelemetry verwenden, um Protokolle über OTLP (OpenTelemetry-Protokoll) zu senden:
using Microsoft.Extensions.Logging;
using OpenTelemetry.Logs;
using ILoggerFactory factory = LoggerFactory.Create(builder =>
{
builder.AddOpenTelemetry(logging =>
{
logging.AddOtlpExporter();
});
});
ILogger logger = factory.CreateLogger("Program");
logger.LogInformation("Hello World! Logging is {Description}.", "fun");
Integration in Hosts und DI (Dependency Injection)
Wenn Ihre Anwendung DI (Dependency Injection) oder einen Host wie WebApplication oder Generic Host von ASP.NET verwendet, dann sollten Sie dann ILoggerFactory
- bzw. ILogger
-Objekte aus dem DI-Container verwenden, anstatt sie direkt zu erstellen.
Abrufen eines ILogger-Objekts von DI
In diesem Beispiel wird ein ILogger-Objekt in einer gehosteten App mithilfe von ASP.NET Minimal-APIs abgerufen:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<ExampleHandler>();
var app = builder.Build();
var handler = app.Services.GetRequiredService<ExampleHandler>();
app.MapGet("/", handler.HandleRequest);
app.Run();
partial class ExampleHandler(ILogger<ExampleHandler> logger)
{
public string HandleRequest()
{
LogHandleRequest(logger);
return "Hello World";
}
[LoggerMessage(LogLevel.Information, "ExampleHandler.HandleRequest was called")]
public static partial void LogHandleRequest(ILogger logger);
}
Für das vorherige Beispiel gilt Folgendes:
- Erstellte einen Singletondienst namens
ExampleHandler
und ordnete eingehende Webanforderungen zu, um die FunktionExampleHandler.HandleRequest
auszuführen. - In Zeile 8 wird ein primärer Konstruktor für den ExampleHandler definiert, ein Feature, das in C# 12 hinzugefügt wurde. Die Verwendung des älteren C#-Konstruktors würde genauso gut funktionieren, ist aber etwas länger.
- Der Konstruktor definiert einen Parameter vom Typ
ILogger<ExampleHandler>
. ILogger<TCategoryName> ist von ILogger abgeleitet und gibt an, zu welcher Kategorie dasILogger
-Objekt gehört. Der DI-Container sucht einILogger
-Objekt der richtigen Kategorie und gibt es als Konstruktorargument an. Wenn noch keinILogger
-Objekt dieser Kategorie vorhanden ist, erstellt der DI-Container es automatisch ausILoggerFactory
im Dienstanbieter. - Der im Konstruktor empfangene Parameter
logger
wurde für die Protokollierung in der FunktionHandleRequest
verwendet.
Vom Host bereitgestellte ILoggerFactory-Instanz
Host-Generatoren initialisieren die Standardkonfiguration und fügen dann ein konfiguriertes ILoggerFactory
-Objekt dem DI-Container des Hosts hinzu, wenn der Host erstellt wird. Bevor der Host erstellt wird, können Sie die Protokollierungskonfiguration über HostApplicationBuilder.Logging, WebApplicationBuilder.Logging oder ähnliche APIs auf anderen Hosts anpassen. Hosts wenden auch die Protokollierungskonfiguration aus Standardkonfigurationsquellen wie appsettings.json und Umgebungsvariablen an. Weitere Informationen finden Sie unter Konfiguration in .NET.
In diesem Beispiel wird das vorige Beispiel erweitert, um die von WebApplicationBuilder
bereitgestellte ILoggerFactory
-Schnittstelle anzupassen. Hier wird OpenTelemetry als Protokollierungsanbieter hinzugefügt, der die Protokolle über OTLP (OpenTelemetry-Protokoll) überträgt:
var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddOpenTelemetry(logging => logging.AddOtlpExporter());
builder.Services.AddSingleton<ExampleHandler>();
var app = builder.Build();
Erstellen einer ILoggerFactory-Instanz mit DI
Wenn Sie einen DI-Container ohne Host verwenden, verwenden Sie AddLogging, um ILoggerFactory
zu konfigurieren und dem Container hinzuzufügen.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
// Add services to the container including logging
var services = new ServiceCollection();
services.AddLogging(builder => builder.AddConsole());
services.AddSingleton<ExampleService>();
IServiceProvider serviceProvider = services.BuildServiceProvider();
// Get the ExampleService object from the container
ExampleService service = serviceProvider.GetRequiredService<ExampleService>();
// Do some pretend work
service.DoSomeWork(10, 20);
class ExampleService(ILogger<ExampleService> logger)
{
public void DoSomeWork(int x, int y)
{
logger.LogInformation("DoSomeWork was called. x={X}, y={Y}", x, y);
}
}
Für das vorherige Beispiel gilt Folgendes:
- Erstellt einen DI-Dienstcontainer, der eine
ILoggerFactory
-Instanz enthält, die für das Schreiben in die Konsole konfiguriert wurde - Dem Container wurde ein Singleton
ExampleService
hinzugefügt. - Es wurde eine Instanz von
ExampleService
vom DI-Container erstellt, wobei auch automatisch eineILogger<ExampleService>
-Instanz erstellt wurde, die als Konstruktorargument verwendet werden soll. - Aufruf von
ExampleService.DoSomeWork
, mit dem eineILogger<ExampleService>
Meldung auf der Konsole zu protokollieren.
Konfigurieren der Protokollierung
Die Protokollierungskonfiguration wird im Code oder über externe Quellen festgelegt, z. B. Konfigurationsdateien und Umgebungsvariablen. Die Verwendung einer externer Konfiguration ist nach Möglichkeit von Vorteil, da sie geändert werden kann, ohne die Anwendung neu zu erstellen. Einige Aufgaben, z. B. das Festlegen der Protokollierungsanbieter, können jedoch nur über Code konfiguriert werden.
Konfigurieren der Protokollierung ohne Code
Für Apps, die einen Host verwenden, wird die Protokollierungskonfiguration meistens im Abschnitt "Logging"
von appsettings.{Environment}
.json-Dateien angegeben. Für Apps, die keinen Host verwenden, werden stattdessen externe Konfigurationsquellen explizit eingerichtet oder im Code konfiguriert.
Die folgende Datei appsettings.Development.json wird von den .NET-Workerdienstvorlagen generiert:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
Für den oben stehenden JSON-Code gilt:
- Die Protokolliergradkategorien
"Default"
,"Microsoft"
und"Microsoft.Hosting.Lifetime"
werden angegeben. - Der
"Default"
-Wert wird auf alle Kategorien angewendet, die nicht anderweitig angegeben sind, wodurch effektiv alle Standardwerte für alle Kategorien auf"Information"
festgelegt werden. Sie können dieses Verhalten außer Kraft setzen, indem Sie einen Wert für eine Kategorie angeben. - Die Kategorie
"Microsoft"
gilt für alle Kategorien, die mit"Microsoft"
beginnen. - Die Kategorie
"Microsoft"
protokolliert mit dem ProtokolliergradWarning
oder höher. - Die Kategorie
"Microsoft.Hosting.Lifetime"
ist spezifischer als die Kategorie"Microsoft"
, sodass die Kategorie"Microsoft.Hosting.Lifetime"
mit dem Protokolliergrad"Information"
und höher protokolliert. - Ein bestimmter Protokollanbieter wird nicht angegeben, sodass
LogLevel
für alle aktivierten Protokollanbieter mit Ausnahme von Windows EventLog gilt.
Die Logging
-Eigenschaft kann LogLevel und Protokollanbietereigenschaften beinhalten. LogLevel
gibt den Mindestgrad an, der für ausgewählte Kategorien protokolliert werden soll. Im JSON-Code oben werden die Protokolliergrade Information
und Warning
angegeben. LogLevel
gibt den Schweregrad des Protokolls an und liegt zwischen 0 und 6:
Trace
= 0, Debug
= 1, Information
= 2, Warning
= 3, Error
= 4, Critical
= 5 und None
= 6.
Wenn LogLevel
angegeben wird, wird Protokollierung für Meldungen mit dem angegebenen Protokolliergrad oder höher aktiviert. Im JSON-Code oben wird die Kategorie Default
für Information
oder höher protokolliert. Beispielsweise werden Information
-, Warning
-, Error
- und Critical
-Meldungen protokolliert. Wenn kein LogLevel
angegeben wird, wird Protokollierung standardmäßig mit dem Protokolliergrad Information
verwendet. Weitere Informationen finden Sie unter Protokolliergrade.
Eine Anbietereigenschaft kann eine LogLevel
-Eigenschaft angeben. LogLevel
unter einem Anbieter gibt die Protokolliergrade an, die für diesen Anbieter protokolliert werden sollen, und überschreibt die Nicht-Anbieterprotokolleinstellungen. Sehen Sie sich die nachfolgende Datei appsettings.json an:
{
"Logging": {
"LogLevel": {
"Default": "Error",
"Microsoft": "Warning"
},
"Debug": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting": "Trace"
}
},
"EventSource": {
"LogLevel": {
"Default": "Warning"
}
}
}
}
Die Einstellungen in Logging.{ProviderName}.LogLevel
überschreiben die Einstellungen in Logging.LogLevel
. Im JSON-Code oben wird der Standardprotokolliergrad Debug
des Anbieters auf Information
festgelegt:
Logging:Debug:LogLevel:Default:Information
Die Einstellung oben gibt den Protokolliergrad Information
für jede Logging:Debug:
-Kategorie mit Ausnahme von Microsoft.Hosting
an. Wenn eine bestimmte Kategorie aufgelistet wird, überschreibt die jeweilige Kategorie die Standardkategorie. Im JSON-Code oben überschreiben die Logging:Debug:LogLevel
-Kategorien "Microsoft.Hosting"
und "Default"
die Einstellungen in Logging:LogLevel
.
Der Mindestprotokolliergrad kann für Folgendes angegeben werden:
- Bestimmte Anbieter: Beispiel:
Logging:EventSource:LogLevel:Default:Information
- Bestimmte Kategorien: Beispiel:
Logging:LogLevel:Microsoft:Warning
- Alle Anbieter und alle Kategorien:
Logging:LogLevel:Default:Warning
Alle Protokolle unterhalb des Mindestprotokolliergrads werden nicht:
- An den Anbieter übergeben.
- Protokolliert oder angezeigt.
Um alle Protokolle zu unterdrücken, geben Sie LogLevel.None an. LogLevel.None
hat den Wert 6, der höher als LogLevel.Critical
(5) ist.
Wenn ein Anbieter Protokollbereiche unterstützt, gibt IncludeScopes
an, ob sie aktiviert sind. Weitere Informationen finden Sie unter Protokollierbereiche.
Die folgende Datei appsettings.json enthält Einstellungen für alle integrierten Anbieter:
{
"Logging": {
"LogLevel": {
"Default": "Error",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Warning"
},
"Debug": {
"LogLevel": {
"Default": "Information"
}
},
"Console": {
"IncludeScopes": true,
"LogLevel": {
"Microsoft.Extensions.Hosting": "Warning",
"Default": "Information"
}
},
"EventSource": {
"LogLevel": {
"Microsoft": "Information"
}
},
"EventLog": {
"LogLevel": {
"Microsoft": "Information"
}
},
"AzureAppServicesFile": {
"IncludeScopes": true,
"LogLevel": {
"Default": "Warning"
}
},
"AzureAppServicesBlob": {
"IncludeScopes": true,
"LogLevel": {
"Microsoft": "Information"
}
},
"ApplicationInsights": {
"LogLevel": {
"Default": "Information"
}
}
}
}
Im vorgehenden Beispiel:
- Die Kategorien und Protokolliergrade sind keine vorgeschlagenen Werte. Das Beispiel wird bereitgestellt, um alle Standardanbieter zu zeigen.
- Die Einstellungen in
Logging.{ProviderName}.LogLevel
überschreiben die Einstellungen inLogging.LogLevel
. Beispielsweise überschreibt der Protokolliergrad inDebug.LogLevel.Default
den Protokolliergrad inLogLevel.Default
. - Der Alias der einzelnen Anbieter wird verwendet. Jeder Anbieter definiert einen Alias, der in der Konfiguration anstelle des vollqualifizierten Typnamens verwendet werden kann. Die folgenden Anbieteraliase sind integriert:
Console
Debug
EventSource
EventLog
AzureAppServicesFile
AzureAppServicesBlob
ApplicationInsights
Festlegen des Protokolliergrads über die Befehlszeile, Umgebungsvariablen und andere Konfigurationen
Der Protokolliergrad kann von einem beliebigen Konfigurationsanbieter festgelegt werden. Beispielsweise können Sie eine persistente Umgebungsvariable namens Logging:LogLevel:Microsoft
mit dem Wert Information
erstellen.
Erstellen Sie eine persistente Umgebungsvariable anhand des Werts für den Protokolliergrad.
:: Assigns the env var to the value
setx "Logging__LogLevel__Microsoft" "Information" /M
Lesen Sie die Umgebungsvariable in einer neuen Instanz der Eingabeaufforderung.
:: Prints the env var value
echo %Logging__LogLevel__Microsoft%
Die vorherige Umgebungseinstellung wird in der Umgebung persistent gespeichert. Um die Einstellungen bei Verwendung einer App zu testen, die mit den .NET-Workerdienstvorlagen erstellt wurde, verwenden Sie nach dem Zuweisen der Umgebungsvariablen den dotnet run
-Befehl im Projektverzeichnis.
dotnet run
Tipp
Starten Sie nach dem Festlegen einer Umgebungsvariablen Ihre integrierte Entwicklungsumgebung (Integrated Development Environment, IDE) neu, um sicherzustellen, dass neu hinzugefügte Umgebungsvariablen verfügbar sind.
Klicken Sie in Azure App Service auf der Seite Einstellungen > Konfiguration auf Neue Anwendungseinstellung. Anwendungseinstellungen von Azure App Service werden:
- Im Ruhezustand verschlüsselt und über einen verschlüsselten Kanal übermittelt.
- Als Umgebungsvariablen verfügbar gemacht.
Weitere Informationen zum Festlegen von .NET-Konfigurationswerten mithilfe von Umgebungsvariablen finden Sie unter Umgebungsvariablen.
Konfigurieren der Protokollierung mit Code
Verwenden Sie die ILoggingBuilder-API, um die Protokollierung im Code zu konfigurieren. Von verschiedenen Orten aus kann darauf zugegriffen werden:
- Wenn die
ILoggerFactory
-Instanz direkt erstellt wird, konfigurieren Sie sie in LoggerFactory.Create. - Wenn Sie DI ohne Host verwenden, konfigurieren Sie sie in LoggingServiceCollectionExtensions.AddLogging.
- Wenn Sie einen Host verwenden, konfigurieren Sie diese mit HostApplicationBuilder.Logging, WebApplicationBuilder.Logging oder anderen hostspezifischen APIs.
Dieses Beispiel zeigt das Festlegen des Protokollierungsanbieters „Konsole“ und mehrerer Filter.
using Microsoft.Extensions.Logging;
using var loggerFactory = LoggerFactory.Create(static builder =>
{
builder
.AddFilter("Microsoft", LogLevel.Warning)
.AddFilter("System", LogLevel.Warning)
.AddFilter("LoggingConsoleApp.Program", LogLevel.Debug)
.AddConsole();
});
ILogger logger = loggerFactory.CreateLogger<Program>();
logger.LogDebug("Hello {Target}", "Everyone");
Im vorherigen Beispiel wird AddFilter verwendet, um die Protokollebene anzupassen, die für verschiedene Kategorien aktiviert ist. AddConsole wird verwendet, um den Konsolenprotokollierungsanbieter hinzuzufügen. Standardmäßig sind Protokolle mit dem Schweregrad Debug
nicht aktiviert, aber da die Filter in der Konfiguration angepasst wurden, wird die Debugmeldung „Hello Everyone“ auf der Konsole angezeigt.
Anwendung von Filterregeln
Wenn ein ILogger<TCategoryName>-Objekt erstellt wird, wählt das ILoggerFactory-Objekt eine einzige Regel pro Anbieter aus, die auf diese Protokollierung angewendet wird. Alle von einer ILogger
-Instanz geschriebenen Meldungen werden auf Grundlage der ausgewählten Regeln gefiltert. Aus den verfügbaren Regeln wird die für jeden Anbieter und jedes Kategoriepaar spezifischste Regel ausgewählt.
Beim Erstellen von ILogger
für eine vorgegebene Kategorie wird für jeden Anbieter der folgende Algorithmus verwendet:
- Es werden alle Regeln ausgewählt, die dem Anbieter oder dem zugehörigen Alias entsprechen. Wenn keine Übereinstimmung gefunden wird, werden alle Regeln mit einem leeren Anbieter ausgewählt.
- Aus dem Ergebnis des vorherigen Schritts werden die Regeln mit dem längsten übereinstimmenden Kategoriepräfix ausgewählt. Wenn keine Übereinstimmung gefunden wird, werden alle Regeln ohne Angabe einer Kategorie ausgewählt.
- Wenn mehrere Regeln ausgewählt sind, wird die letzte Regel verwendet.
- Wenn keine Regeln ausgewählt sind, geben Sie über LoggingBuilderExtensions.SetMinimumLevel(ILoggingBuilder, LogLevel) den Mindestprotokolliergrad an.
Protokollkategorie
Wenn ein ILogger
-Objekt erstellt wird, wird eine Kategorie angegeben. Diese Kategorie ist in jeder Protokollmeldung enthalten, die von dieser Instanz von ILogger
erstellt wird. Die Kategoriezeichenfolge ist beliebig, aber die Konvention ist, den vollständig qualifizierten Klassennamen zu verwenden. Beispielsweise kann die Kategorie in einer Anwendung mit einem dem folgenden Objekt entsprechend definierten Dienst "Example.DefaultService"
lauten:
namespace Example
{
public class DefaultService : IService
{
private readonly ILogger<DefaultService> _logger;
public DefaultService(ILogger<DefaultService> logger) =>
_logger = logger;
// ...
}
}
Wenn eine weitere Kategorisierung erwünscht ist, besteht die Konvention darin, einen hierarchischen Namen zu verwenden, indem eine Unterkategorie an den voll qualifizierten Klassennamen angehängt und die Kategorie explizit mit LoggerFactory.CreateLogger angegeben wird:
namespace Example
{
public class DefaultService : IService
{
private readonly ILogger _logger;
public DefaultService(ILoggerFactory loggerFactory) =>
_logger = loggerFactory.CreateLogger("Example.DefaultService.CustomCategory");
// ...
}
}
Das Aufrufen von CreateLogger
mit einem festgelegten Namen kann bei Verwendung in mehreren Klassen/Typen nützlich sein, damit die Ereignisse nach Kategorie organisiert werden können.
ILogger<T>
entspricht dem Aufruf von CreateLogger
mit dem vollqualifizierten Typnamen T
.
Protokolliergrad
In der folgenden Tabelle werden die LogLevel-Werte, die Erweiterungsmethode Log{LogLevel}
und die empfohlene Syntax aufgeführt:
LogLevel | Wert | Methode | BESCHREIBUNG |
---|---|---|---|
Ablaufverfolgung | 0 | LogTrace | Enthält die ausführlichsten Meldungen. Diese Meldungen enthalten möglicherweise sensible App-Daten. Sie sind standardmäßig deaktiviert und sollten nicht in einer Produktionsumgebung aktiviert werden. |
Debuggen | 1 | LogDebug | Zum Debuggen und für die Entwicklung. Wegen des großen Volumens in der Produktion mit Vorsicht zu verwenden. |
Information | 2 | LogInformation | Verfolgt den allgemeinen Ablauf der App nach. Kann über einen langfristigen Wert verfügen. |
Warnung | 3 | LogWarning | Für ungewöhnliche oder unerwartete Ereignisse. Schließt in der Regel Fehler oder Bedingungen ein, die nicht bewirken, dass die App fehlschlägt. |
Fehler | 4 | LogError | Für Fehler und Ausnahmen, die nicht behandelt werden können. Diese Meldungen weisen auf einen Fehler im aktuellen Vorgang oder der aktuellen Anforderung und nicht auf einen anwendungsweiten Fehler hin. |
Critical (Kritisch) | 5 | LogCritical | Für Fehler, die sofortige Aufmerksamkeit erfordern. Beispiel: Szenarios mit Datenverlust, Speichermangel. |
Keine | 6 | Gibt an, dass keine Meldungen geschrieben werden sollen. |
In der Tabelle oben wird der LogLevel
vom niedrigsten bis zum höchsten Schweregrad aufgelistet.
Der erste Parameter der Log-Methode (LogLevel) gibt den Schweregrad des Protokolls an. Anstatt Log(LogLevel, ...)
aufzurufen, rufen die meisten Entwickler die Log{LogLevel}-Erweiterungsmethoden auf. Die Log{LogLevel}
-Erweiterungsmethoden rufen die Log
-Methode auf und geben das LogLevel
an. Beispielsweise sind die folgenden beiden Protokollierungsaufrufe funktionell gleichwertig und generieren das gleiche Protokoll:
public void LogDetails()
{
var logMessage = "Details for log.";
_logger.Log(LogLevel.Information, AppLogEvents.Details, logMessage);
_logger.LogInformation(AppLogEvents.Details, logMessage);
}
AppLogEvents.Details
ist die Ereignis-ID, die implizit durch einen konstanten Int32-Wert dargestellt wird. AppLogEvents
ist eine Klasse, die verschiedene benannte Bezeichnerkonstanten verfügbar macht und im Abschnitt Protokollereignis-ID angezeigt wird.
Der folgende Code erstellt Information
- und Warning
-Protokolle:
public async Task<T> GetAsync<T>(string id)
{
_logger.LogInformation(AppLogEvents.Read, "Reading value for {Id}", id);
var result = await _repository.GetAsync(id);
if (result is null)
{
_logger.LogWarning(AppLogEvents.ReadNotFound, "GetAsync({Id}) not found", id);
}
return result;
}
Im vorherigen Code ist der erste Log{LogLevel}
Parameter die AppLogEvents.Read
Protokollereignis-ID. Der zweite Parameter ist eine Meldungsvorlage mit Platzhaltern für Argumentwerte, die von den verbleibenden Methodenparametern bereitgestellt werden. Die Methodenparameter werden im Abschnitt Meldungsvorlage weiter unten in diesem Artikel erläutert.
Konfigurieren Sie den geeigneten Protokolliergrad, und rufen Sie die richtigen Log{LogLevel}
-Methoden auf, um die Menge der Protokollausgabe zu steuern, die in ein bestimmtes Speichermedium geschrieben werden. Zum Beispiel:
- In einer Produktionsumgebung
- Das Protokollieren mit den Protokolliergraden
Trace
oderDebug
verursacht viele detaillierte Protokollmeldungen. Um die Kosten zu kontrollieren und die Datenspeichergrenzen nicht zu überschreiten, protokollieren Sie Meldungen der ProtokolliergradeTrace
undDebug
in einem kostengünstigen Datenspeicher mit hohem Datenvolumen. Erwägen Sie eine Beschränkung vonTrace
undDebug
auf bestimmte Kategorien. - Bei der Protokollierung mit den Protokolliergraden
Warning
bisCritical
sollten nur wenige Protokollmeldungen generiert werden.- Kosten und Speichergrenzwerte stellen in der Regel kein Problem dar.
- Wenige Protokolle ermöglichen eine größere Flexibilität bei der Auswahl von Datenspeichern.
- Das Protokollieren mit den Protokolliergraden
- Bei der Entwicklung:
- Legen Sie
Warning
fest. - Fügen Sie bei der Problembehandlung
Trace
- oderDebug
-Meldungen hinzu. Um die Ausgabe einzuschränken, legen SieTrace
oderDebug
nur für die zu untersuchenden Kategorien fest.
- Legen Sie
Der folgende JSON-Code legt Logging:Console:LogLevel:Microsoft:Information
fest:
{
"Logging": {
"LogLevel": {
"Microsoft": "Warning"
},
"Console": {
"LogLevel": {
"Microsoft": "Information"
}
}
}
}
Protokollereignis-ID
Jedes Protokoll kann einen Ereignisbezeichner angeben. EventId ist eine Struktur mit einer Id
und optionalen schreibgeschützten Name
-Eigenschaften. Im Beispielquellcode wird die AppLogEvents
-Klasse zum Definieren von Ereignis-IDs verwendet:
using Microsoft.Extensions.Logging;
internal static class AppLogEvents
{
internal static EventId Create = new(1000, "Created");
internal static EventId Read = new(1001, "Read");
internal static EventId Update = new(1002, "Updated");
internal static EventId Delete = new(1003, "Deleted");
// These are also valid EventId instances, as there's
// an implicit conversion from int to an EventId
internal const int Details = 3000;
internal const int Error = 3001;
internal static EventId ReadNotFound = 4000;
internal static EventId UpdateNotFound = 4001;
// ...
}
Tipp
Weitere Informationen zum Konvertieren eines int
in eine EventId
finden Sie unter EventId.Implicit(Int32 to EventId)-Operator.
Eine Ereignis-ID ordnet eine Gruppe von Ereignissen zu. Beispielsweise könnten alle Protokolle im Zusammenhang mit dem Lesen von Werten aus einem Repository 1001
lauten.
Der Protokollierungsanbieter kann die Ereignis-ID in einem ID-Feld, in der Protokollierungsmeldung oder gar nicht protokollieren. Der Debuganbieter zeigt keine Ereignis-IDs an. Der Konsolenanbieter zeigt Ereignis-IDs in Klammern hinter der Kategorie an:
info: Example.DefaultService.GetAsync[1001]
Reading value for a1b2c3
warn: Example.DefaultService.GetAsync[4000]
GetAsync(a1b2c3) not found
Einige Protokollierungsanbieter speichern die Ereignis-ID in einem Feld, das das Filtern nach der ID ermöglicht.
Protokollmeldungsvorlage
Jede Protokoll-API verwendet eine Meldungsvorlage. Die Meldungsvorlage kann Platzhalter enthalten, für die Argumente bereitgestellt werden. Verwenden Sie Namen für die Platzhalter, keine Zahlen. Die Reihenfolge der Platzhalter – nicht ihre Namen – bestimmt, welche Parameter verwendet werden, um ihre Werte zur Verfügung zu stellen. Im folgenden Code befinden sich die Parameternamen in der Meldungsvorlage nicht in der richtigen Reihenfolge:
string p1 = "param1";
string p2 = "param2";
_logger.LogInformation("Parameter values: {p2}, {p1}", p1, p2);
Dieser Code oben erstellt eine Protokollierungsmeldung mit den Parameterwerten in der richtigen Reihenfolge:
Parameter values: param1, param2
Hinweis
Gehen Sie bei der Verwendung mehrerer Platzhalter innerhalb einer einzelnen Nachrichtenvorlage sorgfältig vor, da diese ordnungszahlbasiert sind. Die Namen werden nicht verwendet, um die Argumente an den Platzhaltern auszurichten.
Dieser Ansatz ermöglicht es Protokollierungsanbietern, semantische oder strukturierte Protokollierung zu implementieren. Die Argumente selbst (nicht nur die formatierte Meldungsvorlage) werden an das Protokollierungssystem übergeben. Dies ermöglicht es Protokollierungsanbietern, die Parameterwerte als Felder zu speichern. Sehen Sie sich die folgende Protokollierungsmethode an:
_logger.LogInformation("Getting item {Id} at {RunTime}", id, DateTime.Now);
Beispielsweise bei der Protokollierung in Azure Table Storage:
- Jede Azure Table-Entität kann über
ID
- undRunTime
-Eigenschaften verfügen. - Tabellen mit Eigenschaften vereinfachen Abfragen für protokollierte Daten. Beispielsweise kann eine Abfrage alle Protokolle innerhalb eines bestimmten
RunTime
-Bereichs ermitteln, ohne die Zeitangabe aus der Textnachricht analysieren zu müssen.
Formatierung von Protokollnachrichtenvorlagen
Protokollnachrichtenvorlagen unterstützen Platzhalterformatierung. Vorlagen können ein beliebiges gültiges Format für das vorgegebene Typargument angeben. Sehen Sie sich z. B. die folgende Information
-Protokollierungsnachrichtenvorlage an:
_logger.LogInformation("Logged on {PlaceHolderName:MMMM dd, yyyy}", DateTimeOffset.UtcNow);
// Logged on January 06, 2022
Im vorherigen Beispiel ist die DateTimeOffset
-Instanz der Typ, der dem PlaceHolderName
in der Protokollierungsnachrichtenvorlage entspricht. Dieser Name kann beliebig sein, da die Werte ordnungszahlbasiert sind. Das MMMM dd, yyyy
-Format ist für den DateTimeOffset
-Typ gültig.
Weitere Informationen zur DateTime
- und DateTimeOffset
-Formatierung finden Sie unter Benutzerdefinierte Datums- und Uhrzeitformatzeichenfolgen.
Beispiele
Die folgenden Beispiele zeigen, wie eine Nachrichtenvorlage mithilfe der {}
-Platzhaltersyntax formatiert wird. Außerdem wird ein Beispiel für das Escapen der {}
-Platzhaltersyntax zusammen mit seiner Ausgabe gezeigt. Schließlich wird auch die Zeichenfolgeninterpolation mit Vorlagenerstellungsplatzhaltern gezeigt:
logger.LogInformation("Number: {Number}", 1); // Number: 1
logger.LogInformation("{{Number}}: {Number}", 3); // {Number}: 3
logger.LogInformation($"{{{{Number}}}}: {{Number}}", 5); // {Number}: 5
Tipp
- In den meisten Fällen sollten Sie bei der Protokollierung die Formatierung der Protokollnachrichtenvorlage verwenden. Die Verwendung der Zeichenfolgeninterpolation kann zu Leistungsproblemen führen.
- Die Codeanalyseregel CA2254: Vorlage sollte ein statischer Ausdruck sein hilft Ihnen bei der Warnung vor Stellen, an denen Ihre Protokollnachrichten nicht die richtige Formatierung verwenden.
Protokollieren von Ausnahmen
Die Protokollierungsmethoden verfügen über Überladungen, die einen Ausnahmeparameter annehmen:
public void Test(string id)
{
try
{
if (id is "none")
{
throw new Exception("Default Id detected.");
}
}
catch (Exception ex)
{
_logger.LogWarning(
AppLogEvents.Error, ex,
"Failed to process iteration: {Id}", id);
}
}
Ausnahmeprotokollierung ist anbieterspezifisch.
Standardprotokolliergrad
Wenn der Standardprotokolliergrad nicht festgelegt ist, ist der Standardwert des Standardprotokolliergrads Information
.
Sehen Sie sich zum Beispiel die folgende Workerdienst-App an:
- Sie wurde mit den .NET-Workervorlagen erstellt.
- appsettings.json und appsettings.Development.json wurden gelöscht oder umbenannt.
Mit dem oben beschriebenen Setup generiert die Navigation zur Datenschutz- oder Homepage viele Trace
-, Debug
- und Information
-Meldungen mit Microsoft
im Kategorienamen.
Mit dem folgenden Code wird der Standardprotokolliergrad festgelegt, wenn der Standardprotokolliergrad nicht in der Konfiguration festgelegt ist:
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Logging.SetMinimumLevel(LogLevel.Warning);
using IHost host = builder.Build();
await host.RunAsync();
Filterfunktion
Eine Filterfunktion wird für alle Anbieter und Kategorien aufgerufen, denen keine Regeln durch Konfiguration oder Code zugewiesen sind:
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddFilter((provider, category, logLevel) =>
{
return provider.Contains("ConsoleLoggerProvider")
&& (category.Contains("Example") || category.Contains("Microsoft"))
&& logLevel >= LogLevel.Information;
});
using IHost host = builder.Build();
await host.RunAsync();
Der Code oben zeigt Konsolenprotokolle an, wenn die Kategorie Example
oder Microsoft
enthält und der Protokolliergrad Information
oder höher ist.
Protokollbereiche
Ein Bereich gruppiert eine Reihe von logischen Vorgängen. Diese Gruppierung kann verwendet werden, um an jedes Protokoll, das als Teil einer Gruppe erstellt wird, die gleichen Daten anzufügen. So kann beispielsweise jedes Protokoll, das im Rahmen der Verarbeitung einer Transaktion erstellt wird, die Transaktions-ID enthalten.
Ein Bereich:
- Ist ein IDisposable-Typ, der von der BeginScope-Methode zurückgegeben wird.
- Er bleibt bestehen, bis er verworfen wird.
Die folgenden Anbieter unterstützen Bereiche:
Verwenden Sie einen Bereich, indem Sie Protokollierungsaufrufe mit einem using
-Block umschließen, der als Wrapper verwendet wird:
public async Task<T> GetAsync<T>(string id)
{
T result;
var transactionId = Guid.NewGuid().ToString();
using (_logger.BeginScope(new List<KeyValuePair<string, object>>
{
new KeyValuePair<string, object>("TransactionId", transactionId),
}))
{
_logger.LogInformation(
AppLogEvents.Read, "Reading value for {Id}", id);
var result = await _repository.GetAsync(id);
if (result is null)
{
_logger.LogWarning(
AppLogEvents.ReadNotFound, "GetAsync({Id}) not found", id);
}
}
return result;
}
Der folgende JSON-Code aktiviert Bereiche für den Konsolenanbieter:
{
"Logging": {
"Debug": {
"LogLevel": {
"Default": "Information"
}
},
"Console": {
"IncludeScopes": true,
"LogLevel": {
"Microsoft": "Warning",
"Default": "Information"
}
},
"LogLevel": {
"Default": "Debug"
}
}
}
Der folgende Code aktiviert Bereiche für den Konsolenanbieter:
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Logging.ClearProviders();
builder.Logging.AddConsole(options => options.IncludeScopes = true);
using IHost host = builder.Build();
await host.RunAsync();
Erstellen von Protokollen in Main
Der folgende Code führt Protokollierung in Main
aus, indem nach dem Erstellen des Hosts eine ILogger
-Instanz von der Abhängigkeitsinjektion abgerufen wird:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using IHost host = Host.CreateApplicationBuilder(args).Build();
var logger = host.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Host created.");
await host.RunAsync();
Der vorangehende Code basiert auf zwei NuGet-Paketen:
Die Projektdatei sähe in etwa wie folgt aus:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
</ItemGroup>
</Project>
Keine asynchronen Protokollierungsmethoden
Die Protokollierung sollte so schnell erfolgen, dass sich die Leistungskosten von asynchronem Code nicht lohnen. Wenn ein Protokollierungsdatenspeicher langsam ist, schreiben Sie nicht direkt in diesen. Erwägen Sie, die Protokollnachrichten zunächst in einen schnellen Speicher zu schreiben und sie dann später in den langsamen Speicher zu verschieben. Wenn Sie beispielsweise in SQL Server protokollieren, verwenden Sie nicht direkt eine Log
-Methode, da die Log
-Methoden synchron sind. Fügen Sie stattdessen die Protokollmeldungen synchron zu einer Warteschlange im Arbeitsspeicher hinzu, und rufen Sie mithilfe eines Hintergrundworkers die Nachrichten aus der Warteschlange ab, um sie dann asynchron per Pushvorgang an SQL Server zu übertragen.
Ändern von Protokolliergraden in einer aktuell ausgeführten App
Die Protokollierungs-API umfasst kein Szenario zum Ändern der Protokollebene, während eine App ausgeführt wird. Einige Konfigurationsanbieter können jedoch die Konfiguration erneut laden, was sich unmittelbar auf die Protokollierungskonfiguration auswirkt. Beispielsweise lädt der Dateikonfigurationsanbieter die Protokollierungskonfiguration standardmäßig neu. Wenn die Konfiguration im Code geändert wird, während eine App ausgeführt wird, kann die App IConfigurationRoot.Reload aufrufen, um Protokollierungskonfiguration der App zu aktualisieren.
NuGet-Pakete
Die Schnittstellen ILogger<TCategoryName> und ILoggerFactory sowie Implementierungen sind in den meisten .NET-SDKs als impliziter Paketverweis enthalten. Sie sind auch explizit in den folgenden NuGet-Paketen verfügbar, wenn nicht anderweitig implizit auf sie verwiesen wird:
- Die Schnittstellen befinden sich in Microsoft.Extensions.Logging.Abstractions.
- Die Standardimplementierungen befinden sich in Microsoft.Extensions.Logging.
Weitere Informationen zu den .NET SDKs, die implizite Paketverweise enthalten, finden Sie in der Tabelle mit den impliziten Namespaces für .NET SDKs.
Siehe auch
- Protokollierungsanbieter in .NET
- Implementieren eines benutzerdefinierten Protokollierungsanbieters in .NET
- Formatieren von Konsolenprotokollen
- Hochleistungsprotokollierung in .NET
- Protokollierungsleitfaden für .NET-Bibliotheksersteller*innen
- Fehler beim Protokollieren sollten im Repository github.com/dotnet/runtime/ erstellt werden.