Die Protokollierung ist ein wesentlicher und wertvoller Bestandteil der Softwareentwicklung. Es wird zu einem „Muss“ in jeder Bibliothek oder Anwendung. Die Protokollierung kann bei jedem Schritt der Software-Entwicklung helfen, Probleme und Fragen zu finden, vor allem aber in Fällen, in denen die übliche Debugging-Methode nicht möglich ist.
Wenn Sie zum Beispiel Ihre Anwendung in Produktion geben und es keine Möglichkeit gibt, zu sehen, wie Ihr Code funktioniert, können Sie einfach die protokollierten Informationen lesen und herausfinden, ob alles richtig läuft. Schließlich kann die Protokollierung auch in der Spätphase – nach der Produktfreigabe – hilfreich sein. Wenn nach einer langen Zeit stabiler Arbeit etwas schief geht, können Sie einfach die Protokollinformationen überprüfen, und in den meisten Fällen reicht das aus, um zu verstehen, was schief gelaufen ist.
Dieser Artikel wurde für .NET-Entwickler geschrieben, die Logging in ihren Projekten implementieren wollen und nicht mit den verschiedenen Logging-Frameworks von Drittanbietern und deren Funktionen vertraut sind, die bereitgestellt werden können. Er beschreibt die wichtigsten Schritte der Logging-Implementierung, von der Installation und Konfiguration des Frameworks bis zum Schreiben der ersten Anwendungsprotokolle. In diesem Artikel erfahren Sie mehr über die strukturierte Datenprotokollierung in verschiedenen Frameworks sowie die Vor- und Nachteile der einzelnen Frameworks. Die Beispiele in diesem Artikel wurden mit dem .NET Core Framework geschrieben.
Was ist strukturierte Protokollierung?
Um alle Vorteile der Protokollierung zu nutzen, sollten Sie eine Protokollierungsfunktion auf die richtige Weise implementieren. Je mehr nützliche und notwendige Informationen Sie durch die Protokollierung bereitstellen können, desto leichter werden Sie Antworten auf die Frage „was ist schief gelaufen“ erhalten. Das bedeutet nicht, dass Sie alles protokollieren sollten, Sie müssen nur herausfinden, in welchen Fällen eine Protokollierung sinnvoll ist. Aber nur Ausnahmemeldungen zu protokollieren kann nutzlos sein. Wenn Sie beispielsweise in den Protokollen eine Meldung wie „Object null reference“ erhalten, ohne dass der Kontext, der Parametername oder sogar der Funktionsname, bei dem die Ausnahme ausgelöst wurde, angegeben wird. Sie sollten genügend Informationen bereitstellen, um die Fehlersuche zu erleichtern. Um dies auf einfache Art und Weise zu erreichen, können Sie spezielle .Net Logging Frameworks verwenden, die viele Funktionen wie strukturiertes Logging bieten können.
Einfaches Logging bedeutet, dass alle Datensätze als Strings gespeichert werden. Aber viele Probleme lassen sich nicht eindeutig in ein paar Worten übertragen. In diesem Fall sollten wir die strukturierte Protokollierung verwenden – die Speicherung ganzer Objekte in Protokollen. Zum Beispiel der Anforderungskörper, das Benutzermodell, die Transaktionsabfrage, usw. Dies kann helfen, einen Fehler zu reproduzieren und herauszufinden, was schief gelaufen ist.
Außerdem bietet die strukturierte Protokollierung einige Sortier- und Suchfunktionen in den Protokolldateien. So können Sie beispielsweise bei fehlgeschlagenen Anfragen das Benutzermodell angeben und dann in den Protokollen einfach nach ClientId suchen. Mit Hilfe von Logging-Frameworks können Sie jedes beliebige Objekt in Ihren Protokollen einfach übergeben.
Wenn wir zum Beispiel einfaches Logging verwenden, sehen unsere Logs wie folgt aus:
2019-02-04T12:23:34Z INFO Completed 200 OK in 79ms (Views: 78.8ms | ActiveRecord: 0.0ms)
Es ist nicht wirklich nützlich, wenn ein Fehler auftritt, daher können wir dieses Protokoll mit Hilfe der strukturierten Protokollierung verbessern. In diesem Fall können wir etwas erhalten wie:
{
"dt": "2020-04-07T12:23:34Z",
"level": "info",
"message": "Completed 200 OK in 79ms (Views: 78.8ms | ActiveRecord: 0.0ms)",
"context": {
"host": "34.225.155.83",
"user_id": 5,
"path": "/welcome",
"method": "GET"
},
"http_response_sent": {
"status": 200,
"duration_ms": 79.0,
"view_ms": 78.8,
"active_record_ms": 0.0
}
}
Sieht besser aus und hat einige Vorteile:
- Gut strukturierte Daten im offiziellen Kodierungsformat (JSON).
- Keine speziellen Parsing-Regeln.
- Einfaches Arbeiten mit den Daten (Suche, Filter, Sortierung, menschenlesbare Ansicht usw.).
Bevor wir jedoch mit strukturierten Protokollen beginnen, müssen wir die Protokollierung auf allgemeine Weise implementieren.
Eingebaute Logging-API
Um mit der Protokollierungs-API arbeiten zu können, benötigen Sie einen Anbieter, der Protokolle anzeigt oder speichert. Sie können den Console-Anbieter wählen, um Protokolle in Ihrer Anwendungskonsole anzuzeigen, oder den Azure Application Insights-Anbieter, um sie in Azure zu speichern. Übrigens können Sie mehrere Anbieter verwenden, um Protokolle an verschiedenen Orten zu speichern. Wenn Sie eine Anwendung mit Generic Host verwenden, sollten Sie einfach die Methode AddConsole (oder einen anderen Anbieternamen) aufrufen. Versuchen wir es mit der Standard-Web-API-Projektvorlage.
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging(configureLogging => {
configureLogging.ClearProviders();
configureLogging.AddConsole();
})
.ConfigureWebHostDefaults(webBuilder => {
webBuilder.UseStartup();
});
Die Methode ClearProviders wird verwendet, um bereits hinzugefügte (Standard-)Anbieter zu löschen. Sie können also die Standardprovider durch beliebige Provider ersetzen.
Nach dem Hinzufügen von Protokollierungsanbietern können Sie Protokolle erstellen. Dies kann mit Hilfe des ILogger<>-Objekts geschehen, das durch Dependency Injection erhalten werden kann. Dann sollten Sie einen Logger mit einer bestimmten Kategorie (String) erstellen, dies kann ein Controller oder ein Klassenname sein:
public class WeatherForecastController : ControllerBase {
private readonly ILogger _logger;
public WeatherForecastController(ILogger logger) {
_logger = logger;
}
Dann sind Sie bereit, Protokolle zu schreiben. Dies kann mit den Methoden LogInformation, LogError, LogWarning, etc. geschehen. Zum Beispiel:
public IEnumerable Get() {
_logger.LogInformation("Method 'Get' called at {0}", DateTime.UtcNow);
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast {
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
}
Wenn Sie dann Console als Provider hinzugefügt haben, können Sie nach jedem Aufruf dieser Methode in der Ausgabekonsole etwas sehen wie:
Method `Get` called 04/07/2020 09:58:06
Zum Schreiben von Protokollen können Sie eine der folgenden Protokollebenen verwenden, die von .NET Core bereitgestellt werden:
- Trace = 0. Wird in der Regel nur für die Entwicklung verwendet und ist standardmäßig deaktiviert, um zu verhindern, dass sensible Daten in die Produktion gelangen.
- Debug = 1.Alle Debug-Informationen (z. B. Parameterwert bei einem Schritt der Codeausführung) können zur Fehlerbehebung in der Produktion aktiviert werden.
- Information = 2. Allgemeine Informationen, die dazu dienen, einige Meldungen über den aktuellen Schritt oder Zustand des Systems zu liefern.
- Warnung = 3. Wird verwendet, um unerwartete Ereignisse anzuzeigen, die die Ausführung der Anwendung nicht zum Absturz bringen oder blockieren.
- Fehler = 4. Wird für Fehler und Ausnahmen verwendet, die nicht behandelt wurden und einen Fehler im aktuellen Vorgang anzeigen können.
- Kritisch = 5. Wird verwendet, um Informationen über Fehler zu liefern, die sofortige Aufmerksamkeit erfordern. Zum Beispiel: wenig Festplattenspeicher usw.
.NET-Protokollierungs-Frameworks
Die Verwendung eines Frameworks eines Drittanbieters ist der Verwendung integrierter Anbieter sehr ähnlich. Sie müssen lediglich ein NuGet-Paket zu Ihrem Projekt hinzufügen und dann eine ILoggerFactory-Erweiterungsmethode aufrufen, die Ihr Protokollierungs-Framework bereitstellt. Diese Frameworks können Ihnen mehr Fähigkeiten und Funktionen bieten, um Ihren Protokollierungsprozess zu verbessern, eine semantische Protokollierung durchzuführen und die Ansicht der erstellten Protokolle zu verbessern.
In diesem Artikel werden wir einen Blick auf drei verschiedene Logging-Frameworks für .NET werfen – NLog, Serilog und Log4Net.
NLog-Protokollierungs-Framework
NLog ist eine kostenlose Logging-Plattform für .NET-Plattformen. NLog unterstützt das Ändern der Logging-Konfiguration während des Betriebs, strukturiertes Logging und kann problemlos in mehrere Ziele schreiben. Die wichtigsten Vorteile der Verwendung von NLog sind: einfache Verwendung, Erweiterung, Konfiguration und hohe Leistung.
Um das NLog-Framework zu verwenden, müssen Sie die neueste Version von NLog und NLog.Web.AspNetCore-Paketen über den NuGet-Paketmanager installieren.
Dann müssen Sie die Datei nlog.config im Stammverzeichnis des Projekts erstellen. Diese Datei beschreibt die Ziele, in die Protokolle geschrieben werden sollen, sowie einige zusätzliche Regeln, um die Protokollierung zu starten.
<!--?xml version="1.0" encoding="utf-8" ?-->
<!-- enable asp.net core layout renderers -->
<!-- the targets to write to -->
<!-- write logs to file -->
<!-- another file log, only own logs. Uses some ASP.NET core renderers -->
<!-- rules to map from logger name to target -->
<!--All logs, including from Microsoft-->
<!--Skip non-critical Microsoft logs and so log only own logs-->
<!-- BlackHole without writeTo -->
Der nächste Schritt besteht darin, das Kopieren des Ordners bin für nlog.config zu aktivieren. Dies kann auf folgende Weise geschehen:
Öffnen Sie in einem Projektmappen-Explorer die Eigenschaften der Datei nlog.config und setzen Sie den Parameter „Copy to Output Directory“ auf „Copy if newer“.
Dann müssen Sie die Datei Program.cs aktualisieren, um NLog zu starten.
public class Program {
public static void Main(string[] args) {
var logger = NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
try {
logger.Debug("init main");
CreateHostBuilder(args).Build().Run();
}
catch (Exception exception) {
//NLog: catch setup errors
logger.Error(exception, "Stopped program because of exception");
throw;
}
finally {
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
NLog.LogManager.Shutdown();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging(configureLogging => {
configureLogging.ClearProviders();
configureLogging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
})
.ConfigureWebHostDefaults(webBuilder => {
webBuilder.UseStartup();
})
.UseNLog(); // NLog: Setup NLog for Dependency injection
}
Der nächste Schritt ist die Konfiguration der Datei appsettings.json. Sie müssen „Default“ entfernen oder korrekte Werte eingeben. Andernfalls wird jeder Aufruf von SetMinimumLevel überschrieben. Sie können die Protokollierung auf diese Weise konfigurieren:
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Trace",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
Falls Sie verschiedene Umgebungen haben und verschiedene Konfigurationsdateien verwenden, denken Sie daran, diese Parameter in jeder von ihnen anzugeben.
Wenn Sie alle oben genannten Schritte durchgeführt haben, können Sie nun versuchen, Protokolle zu schreiben. Zum Beispiel können Sie Protokolle wie folgt in Ihren Controller schreiben:
public class WeatherForecastController : ControllerBase {
private readonly ILogger _logger;
public WeatherForecastController(ILogger logger) {
_logger = logger;
_logger.LogDebug(1, "NLog injected into WeatherForecastController");
}
private static readonly string[] Summaries = new[] {
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
[HttpGet]
public IEnumerable Get() {
_logger.LogInformation("Method 'Get' called at {0}", DateTime.UtcNow);
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast {
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
}
Wie Sie sehen können, ist die Verwendung von NLog der Standardprotokollierung von .NET Core sehr ähnlich. Jetzt können Sie Ihre Protokolle an der Stelle überprüfen, die Sie in der Datei nlog.config konfiguriert haben. Es wird wie folgt aussehen:
2020-04-07 14:31:58.4400||DEBUG|LoggingSample.Program|init main
2020-04-07 14:31:59.1861||INFO|Microsoft.Hosting.Lifetime|Application started. Press Ctrl+C to shut down.
2020-04-07 14:31:59.1861||INFO|Microsoft.Hosting.Lifetime|Hosting environment: Development
2020-04-07 14:31:59.1979||INFO|Microsoft.Hosting.Lifetime|Content root path: C:UsersAdminsourcereposLoggingSampleLoggingSample
2020-04-07 14:31:59.2970||INFO|LoggingSample.Controllers.WeatherForecastController|Method 'Get' called at 07-Apr-20 11:31:59
Um eine strukturierte Protokollierung in NLog zu verwenden, können Sie die Formatierung einfach durch ein vorangestelltes @ steuern. Zum Beispiel:
var order = new Order {
OrderId = 2,
Status = OrderStatus.Processing
};
logger.Info("Order updated: {@value1}", order);
Ergebnis der Ausgabe:
Order updated: {"OrderId":2, "Status":"Processing"}
Wenn wir diese Anweisung ohne @-Symbol verwenden, z. B.:
logger.Info("Order updated: {value1}", order);
Wie Sie also sehen, ist die Verwendung von strukturierten Protokollen im NLog-Framework wirklich einfach. Ein einziges Symbol bietet die Möglichkeit, Ihren Protokollen mehr Kontext zu geben, was bei der Fehlerbehandlung hilfreich sein kann.
Zusammenfassend lässt sich sagen, dass das NLog-Framework der Standardprotokollierung sehr ähnlich ist und wirklich einfach zu konfigurieren und zu verwenden ist. Übrigens, es gibt nur eine Sache, die Sie wissen sollten, bevor Sie dieses Framework benutzen – Sie werden keinen Hinweis bekommen, wenn etwas mit NLog schief gelaufen ist. Wenn Sie zum Beispiel Ihre Konfigurationsdatei vergessen haben, wird NLog nicht funktionieren. Sie werden darüber keine Benachrichtigung erhalten. Dies wurde gemacht, um zu verhindern, dass eine Anwendung wegen der Protokollierung heruntergefahren wird. Aber es kann eine böse Überraschung sein, wenn etwas schief geht und Sie herausfinden, dass die Protokolle aufgrund einer falschen Konfigurationsdatei leer sind.
Serilog-Protokollierungs-Framework
Serilog ist eine weitere Bibliothek, die die Protokollierung auf der Konsole, in Dateien oder anderswo ermöglicht. Sie hat eine saubere API und ist einfach einzurichten. Es wurde mit Blick auf leistungsstarke, strukturierte Ereignisdaten entwickelt.
Um mit Serilog zu arbeiten, müssen Sie zwei Pakete mit NuGet installieren – Serilog und Serilog.Sinks.Console. Um einen Logger zu erstellen, müssen Sie nur den folgenden Code schreiben:
var logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateLogger();
Danach können Sie den Logger auf die gleiche Weise wie den eingebauten Logger verwenden:
log.Information("Hello, Serilog!");
Außerdem können Sie den Anbieter mit der Erweiterungsmethode WriteTo in einen beliebigen anderen ändern. Um zum Beispiel eine Dateiausgabe zu verwenden, können Sie den folgenden Code verwenden:
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.File("logs\myapp.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
Log.Information(„Hallo, Welt!“);
Serilog unterstützt strukturiertes Logging. Wenn Sie ein Objekt protokollieren müssen, können Sie einfach die nächste Anweisung verwenden:
var position = new { Latitude = 25, Longitude = 134 };
var elapsedMs = 34;
log.Information("Processed {@Position} in {Elapsed:000} ms.", position, elapsedMs);
Dieser ‘@’-Operator weist Serilog an, das Objekt zu serialisieren und mit der Methode ToString zu konvertieren.
Eine weitere interessante Funktion von Serilog ist Enricher. Dabei handelt es sich um einen Teil des Codes, der bei jeder Protokollanforderung ausgeführt wird und zusätzliche Informationen zur Anforderung liefert. Mit Hilfe von Enricher können Sie mehr Informationen bereitstellen, ohne dass Sie bei jeder Anfrage etwas Besonderes tun müssen. Sie können einfach zusätzliche Eigenschaften an Ihre Anfrage anhängen. Dadurch werden die Stack Traces verständlicher.
Wenn Sie die Protokolle überprüfen, werden Sie etwas Ähnliches erhalten:
[15:46:03 INF] Hello, world!
[15:46:03 INF] Processed { Latitude: 25, Longitude: 134 } in 034 ms.
[15:46:03 DBG] Dividing 10 by 0
[15:46:03 ERR] Something went wrong
Wie Sie sehen können, stellt die strukturierte Protokollierung das gesamte Objektmodell im JSON-Format in Protokollen zur Verfügung. Es ermöglicht also alle Funktionen der strukturierten Protokollierung ohne Probleme und ist wirklich einfach in Ihre Anwendung zu implementieren.
Zusammenfassend lässt sich sagen, dass Serilog ein relativ einfach zu bedienendes Werkzeug ist. Die Unterstützung für die strukturierte Protokollierung ist ziemlich gut, und die Protokolle können an eine große Anzahl von Zielen gesendet werden.
Apache log4net Logging-Framework
Log4net ist eine Bibliothek, die die Möglichkeit bietet, Logger-Ausgaben an beliebige Ausgabeziele zu senden. Log4net bietet die Möglichkeit, die Protokollierung zur Laufzeit zu aktivieren oder zu deaktivieren, ohne den Anwendungscode zu ändern. Die Hauptvorteile sind Geschwindigkeit und Flexibilität.
Um die Arbeit mit lo4net zu beginnen, müssen Sie es über NuGet installieren. Dann müssen wir eine Konfigurationsdatei erstellen. Sie hat das XML-Format und sollte etwa so aussehen:
<!-- Pattern to output the caller's file name and line number -->
Danach können wir anfangen, mit log4net zu arbeiten. Strukturierte Logs in log4net können ohne spezielle Syntax erstellt werden, indem einfach ein Objekt als Parameter übergeben wird. Der folgende Code zeigt ein Beispiel für die Verwendung von log4net zur Erstellung einfacher und strukturierter Protokolle:
class Program {
private static readonly ILog log = LogManager.GetLogger(typeof(Program));
static void Main(string[] args) {
var logRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
XmlConfigurator.Configure(logRepository, new FileInfo("log4net.config"));
log.Debug("Starting up");
log.Debug(new { color = "red", num = 1 });
log.Debug("Shutting down");
Console.ReadLine();
}
}
Wenn wir diese Anwendung ausführen und dann die Ausgabe überprüfen, können wir sehen:
2018-08-18 18:57:37,278 DEBUG 1 LoggingDemo.Log4Net.Program - Starting up
2018-08-18 18:57:37,298 DEBUG 1 LoggingDemo.Log4Net.Program - { color = red, int1 = 1 }
2018-08-18 18:57:37,298 DEBUG 1 LoggingDemo.Log4Net.Program - Shutting down
Wenn ich log4net zusammenfasse, muss ich auf ein paar wichtige Dinge achten. Es ist überhaupt nicht gut dokumentiert, und es kann schwierig sein, damit zu arbeiten. Die Standardkonfigurationsdatei ist nicht die richtige Wahl. Sie sollten es selbst konfigurieren. Wenn Sie also einen schnellen Start brauchen und eine gute Dokumentation wünschen, sollten Sie vielleicht etwas anderes ausprobieren.
Fazit
Vergleichen wir einige grundlegende Schritte zur Implementierung einer strukturierten Protokollierung und die Funktionen, die bereitgestellt werden können.
log4net | NLog | Serilog | |
Dokumentation | Hat eine allgemeine Dokumentation und Beispiele | Gut dokumentiert und mit vielen Beispielen | Gut dokumentiert und mit vielen Beispielen und Tutorials |
.NET-Kompatibilität | .NET Framework 2.0 or higher .NET Core 1.0 or higher |
.NET Frameworks 3.5 – 4.8 .NET Core 1.0 or higher |
.NET Framework 4.5 or higher .NET Core 1.0 or higher |
Übergabe des gesamten Objekts an Logs | Einfaches Übergeben eines Objekts als Parameter | Markieren eines Objekts mit @ | Markieren eines Objekts mit @ |
Konfiguration | Die Konfigurationsdatei muss angepasst werden, um eine bessere Ansicht der Protokolle zu erhalten | Keine spezielle Konfiguration für die strukturierte Protokollierung erforderlich | Keine spezielle Konfiguration für die strukturierte Protokollierung erforderlich |
Komplexität der Implementierung | Ähnlich wie die Standard-API für die Protokollierung, jedoch muss die Konfigurationsdatei angepasst werden | Ähnlich wie die standardmäßige Protokollierungs-API, einfach zu implementieren, aber keine Hinweise, wenn die Implementierung nicht erfolgreich war und nicht funktioniert | Ähnlich wie die Standard-API für die Protokollierung, einfach zu implementieren |
Wie Sie sehen können, sind alle drei .NET-Protokollierungs-Frameworks ähnlich, und es ist kein großes Problem, zu einem von ihnen zu wechseln, da sie ähnliche Anweisungen haben und leicht installiert und verwendet werden können. Außerdem können wir jedes dieser Frameworks verwenden, um eine strukturierte Protokollierung in Ihrer Anwendung zu implementieren. Alle von ihnen bieten ähnliche Funktionen.
Aber wenn wir einige Entscheidungen treffen und nur eines auswählen müssen, würde ich empfehlen, Serilog auszuprobieren, weil es eine moderne API, eine einfache Einrichtung und Wartung sowie eine einfache Unterstützung für strukturiertes Logging von Haus aus bietet. Im Übrigen ist es sehr gut dokumentiert und alles in allem einfach zu bedienen. Da sie sich jedoch alle ziemlich ähnlich sind, können Sie jede von ihnen ohne Schwierigkeiten ausprobieren und Ihre eigene Entscheidung treffen.