CodeGym /Kurse /C# SELF /Strukturiertes Logging und

Strukturiertes Logging und Serilog

C# SELF
Level 64 , Lektion 1
Verfügbar

1. Einführung

Wenn normales Textlogging dein Tagebuch mit Notizen "Ich habe heute gegessen" ist, dann verwandelt strukturiertes Logging jeden Eintrag in eine Karte mit Feldern: {Datum: ..., Ereignis: "Gegessen", Kalorien: 500, Gericht: "Gebratenes Fleisch"}. Das bedeutet, dass du später nicht nur das Tagebuch lesen, sondern auch ein Diagramm der Kalorien pro Monat erstellen, Einträge nach Gericht filtern und herausfinden kannst, wann du zu spät gegessen hast.

Warum reicht reiner Text nicht?

Mit einfachem Text ist alles erst mal simpel... bis zu einem bestimmten Punkt. Versuch mal, Statistiken über Fehler in Logs, Umsatzmengen im Onlineshop oder die Aktionenkette eines einzelnen Nutzers anhand seiner ID (zum Beispiel 42) zu sammeln, wenn alle Daten bloß lange Textblöcke sind. Strukturiertes Logging erlaubt es, Analyse und sogar KI an Logs anzuhängen. Damit kann man Anomalien suchen, Dashboards bauen und automatisch auf Probleme reagieren.

Vorteile

  • Erlaubt, nicht nur Nachrichten zu loggen, sondern auch damit verbundene Daten (Felder/Eigenschaften).
  • Logs lassen sich automatisch analysieren: zählen, filtern, Berichte erstellen.
  • Es werden standardisierte Formate wie JSON verwendet, die Maschinen leicht parsen können.

Serilog: was ist das und warum verwenden?

Serilog (offizielle Seite: serilog.net, Dokumentation: github.com/serilog/serilog/wiki) ist eine beliebte Bibliothek für strukturiertes Logging in .NET. Sie integriert sich gut in Microsoft.Extensions.Logging, unterstützt die Ausgabe in dutzende Systeme (Datei, Konsole, Seq, ElasticSearch, Grafana, Azure usw.), hat geringen Performance-Overhead und ist einfach zu konfigurieren.

Wodurch unterscheidet sich Serilog vom "einfachen Logging"?

  • Struktur: Logs sind Objekte mit Feldern, nach denen man filtern kann (z. B. alle Fehler eines Nutzers mit ID 42).
  • Formate: kann nicht nur Text, sondern auch JSON, XML schreiben, was für weitere Verarbeitung praktisch ist.
  • Flexibilität: viele fertige Sink-Pakete, um Logs überallhin zu schicken.

Aufbau eines Log-Eintrags mit Serilog

Schauen wir uns einen strukturierten Log-Eintrag an, bevor wir Code schreiben.

{
  "Timestamp": "2024-06-22T10:23:45.123Z",
  "Level": "Information",
  "MessageTemplate": "Benutzer {UserId} hat sich angemeldet",
  "Properties": {
    "UserId": 42,
    "IpAddress": "127.0.0.1"
  }
}

Selbst eine einfache Analyse versteht hier: es geht um den Benutzer Nr.42 und seine IP-Adresse.

2. Installation und Grundkonfiguration von Serilog im C#-Projekt

Schritt 1. NuGet-Pakete installieren

In Rider/Visual Studio über den NuGet Package Manager installieren:

  • Serilog
  • Serilog.Sinks.Console (Ausgabe in die Konsole)
  • Serilog.Extensions.Logging (für die Integration mit Microsoft.Extensions.Logging)

Per Kommandozeile:

dotnet add package Serilog
dotnet add package Serilog.Sinks.Console

Schritt 2. Minimale Konfiguration

Fügen wir die Konfiguration in Program.cs hinzu und schreiben den ersten Log.

using System;
using Serilog;

namespace MySuperApp
{
    class Program
    {
        static void Main(string[] args)
        {
            // 1. Basis-Konfiguration von Serilog: Ausgabe in die Konsole
            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .WriteTo.Console()
                .CreateLogger();

            // 2. Beispiel für strukturiertes Logging
            int userId = 42;
            string ip = "127.0.0.1";
            Log.Information("Benutzer {UserId} hat sich mit IP {IpAddress} angemeldet", userId, ip);

            Log.CloseAndFlush();
        }
    }
}

In der Konsole sieht man ungefähr so etwas:

[10:30:16 INF] Benutzer 42 hat sich mit IP 127.0.0.1 angemeldet

Danach lässt sich die Ausgabe leicht in eine JSON-Datei, an Seq oder an ein anderes System schicken.

3. Formatierung der Logs: Message Template

In Serilog wird statt String-Konkatenation die Template-Syntax verwendet:

Log.Information("Operation {Operation} an der Datei {FileName}", "löschung", "test.txt");

Das ist nicht nur hübsch — es ist strukturiertes Logging: die Felder Operation und FileName landen im Log und sind für Filterung und Aggregation nutzbar.

Worin unterscheidet sich das von string.Format?

string.Format("Operation {0} an der Datei {1}", operation, fileName) fügt einfach die Platzhalter {0}, {1} zusammen. Serilog hingegen erzeugt separate Felder, mit denen man später analytisch arbeiten kann.

Flexible Konfiguration: Levels, Filter, verschiedene "Sinks"

Serilog kann Logs gleichzeitig an mehrere Ziele schreiben.

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug()
    .WriteTo.Console()
    .WriteTo.File("log.txt", rollingInterval: RollingInterval.Day)
    .CreateLogger();

Jetzt werden Logs sowohl in die Konsole als auch in eine Datei mit täglicher Rotation geschrieben.

4. Beispiel

Angenommen, wir schreiben eine Konsolen-"Notiz"-App, die es erlaubt, Nutzer-Einträge zu erstellen. Fügen wir strukturiertes Logging für die Aktionen hinzu.

using System;
using Serilog;

namespace NotesApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Information()
                .WriteTo.Console()
                .WriteTo.File("notes-log.json", rollingInterval: RollingInterval.Day, 
                              formatter: new Serilog.Formatting.Json.JsonFormatter())
                .CreateLogger();

            Console.WriteLine("Geben Sie den Benutzernamen ein:");
            string userName = Console.ReadLine();

            Log.Information("Benutzer {UserName} hat die Anwendung NotesApp gestartet", userName);

            while (true)
            {
                Console.WriteLine("Geben Sie den Text der Notiz ein (oder schreiben Sie 'beenden'):");
                string note = Console.ReadLine();

                if (note == "beenden")
                {
                    Log.Information("Benutzer {UserName} hat die Arbeit beendet", userName);
                    break;
                }

                Log.Information("Benutzer {UserName} hat eine Notiz erstellt: {NoteText}", userName, note);
            }

            Log.CloseAndFlush();
        }
    }
}

Kommentar:
Das Logging zeichnet auf, wer das Programm startet, was eingegeben wird und wann die Arbeit beendet wird. In der Datei notes-log.json ist jeder Eintrag ein JSON-Objekt, das sich leicht analysieren lässt.

5. Nützliche Feinheiten

Best Practices für strukturiertes Logging

  • Missbrauche Levels Debug/Trace nicht im Production — benutze Information und Warning sinnvoll.
  • Verwende benannte Parameter in Templates statt String-Konkatenation.
  • Logge niemals sensible Daten (Passwörter, Tokens, Keys).
  • Logge wichtige Business-Ereignisse, nicht nur Fehler und Exceptions.
  • Konfiguriere Rotation und Aufräumen der Logs, damit der Speicherplatz nicht ausgeht.

Visualisierung und Analyse: Seq, Kibana, Application Insights

Serilog unterstützt viele Sinks — Endpunkte, an die Logs geschickt werden.

Sink Kurzbeschreibung Wo verwendet
Console Direkt in die Konsole Entwicklung, Tests
File In eine lokale oder Netzwerkdatei Kleine Projekte, dev
Seq Weboberfläche mit Filterung und Dashboards Enterprise, Analytics
ElasticSearch Leistungsfähiges Speicher- und Analyse-System Große Unternehmen
Azure Application Insights Cloud-Monitoring und Telemetrie Azure-starke Services

Seq (datalust.co/seq) ist eine sehr beliebte Lösung für Development und interne Nutzung: bequeme Filterung, Feldsuche und schnelle Bereitstellung.

Tabellen und Visualisierung

Unten eine kurze Tabelle, was man strukturiert loggen kann:

Was geloggt wird Wie es in Serilog aussieht Beispielwert
Benutzer-ID
{UserId}
123
Aktion
{Action}
"Löschen"
Fehler
Log.Error(ex, "Fehler in {Module}")
"Registrierungsmodul"
Dauer der Operation {Elapsed:0.000} sek 1.234
Dateiname
{FileName}
"report.pdf"

Coole Tricks und zusätzliche Möglichkeiten von Serilog

  • Enrichers: fügen jedem Log Eigenschaften hinzu (z. B. .Enrich.WithMachineName()).
  • Korrelation: füge RequestId hinzu, um Ereignisketten zu korrelieren.
  • Konfiguration über appsettings.json: praktisch für Production.
{
  "Serilog": {
    "MinimumLevel": "Debug",
    "WriteTo": [
      { "Name": "Console" },
      { "Name": "File", "Args": { "path": "log.txt" } }
    ]
  }
}

Advanced sinks: man kann Logs an Slack, Telegram oder per E-Mail schicken (aber vorsichtig, damit man nicht für jeden Fehler tausend Mails bekommt).

6. Praxis: Integration mit Microsoft.Extensions.Logging

In .NET verwendet man oft das Standard-Interface ILogger, um nicht an eine konkrete Bibliothek gebunden zu sein. Serilog kann als Provider angebunden werden.

Schritt 1. Paket installieren

dotnet add package Serilog.Extensions.Logging

Schritt 2. Konfigurieren

using Microsoft.Extensions.Logging;
using Serilog;

// ...

// Serilog wie gewohnt konfigurieren:
Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .CreateLogger();

// Jetzt Microsoft.Extensions.Logging nutzen
var loggerFactory = LoggerFactory.Create(builder =>
{
    builder.AddSerilog();
});

ILogger<Program> logger = loggerFactory.CreateLogger<Program>();
logger.LogInformation("Testnachricht: {TestValue}", 123);

// Nicht vergessen zu schließen:
Log.CloseAndFlush();

Kommentar:
Jetzt ist dein gesamter Code, der ILogger<T> benutzt, unabhängig vom Provider — bei Bedarf kann man auf NLog oder Log4Net wechseln.

7. Typische Fehler bei der Arbeit mit Serilog

Log-Überfrachtung: wenn man alles loggt, wird nützliche Information schwer auffindbar.

Exceptions als Text loggen: benutze die Overload mit Exception — so landet die Fehlerstruktur im Log.

try
{
    // some code
}
catch (Exception ex)
{
    Log.Error(ex, "Beim Ausführen der Anfrage ist ein Fehler aufgetreten");
}

Konfigurations-Missbrauch: verwandle die Konfiguration nicht in ein Durcheinander — füge nur die nötigen Sinks und Levels hinzu.

Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION