Introduction à Serilog : Maîtriser la journalisation structurée en .NET

La journalisation structurée a radicalement transformé la manière dont les développeurs diagnostiquent les applications modernes. Au sein de l'écosystème .NET, Serilog s'est imposé comme la bibliothèque de référence. Contrairement aux journaux textuels traditionnels, Serilog traite les entrées comme des données manipulables, facilitant ainsi la recherche et l'analyse sans recourir à des expressions régulières complexes.

Pourquoi choisir Serilog ?

L'implémentation de Serilog répond généralement à trois objectifs critiques :

  • Identifier proactivement les bogues avant qu'ils n'impactent massivement les utilisateurs.
  • Résoudre rapidement les incidents en environnement de production.
  • Extraire des indicateurs de performance et de comportement du système en temps réel.

Configuration initiale

Serilog est disponible via NuGet. Pour commencer, vous avez besoin du package principal et d'un "Sink" (un récepteur) pour diriger vos logs vers une destination, comme la console.

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

Voici un exemple de configuraton basique pour une application console :

using Serilog;

class Program
{
    static void Main()
    {
        // Configuration du pipeline de journalisation
        using var journal = new LoggerConfiguration()
            .WriteTo.Console()
            .CreateLogger();

        journal.Information("Le système de log est opérationnel.");
        journal.Warning("Tentative d'accès suspecte détectée.");
    }
}

Dans ce flux :

  • LoggerConfiguration définit les règles du pipeline.
  • WriteTo.Console() redirige les événements vers la sortie standard.
  • CreateLogger() génère une instance de ILogger.

Pour une utilisation globale, Serilog propose une instance statique pratique :

Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .CreateLogger();

Log.Information("Utilisation de l'instance statique.");
Log.CloseAndFlush(); // Assure que tous les logs sont écrits avant l'arrêt

Le concept d'événement et de niveaux

L'unité fondamentale dans Serilog est l'Événement, et non le message texte. Un événement se compose d'un horodatage, d'un niveau (Debug, Info, Error, etc.), d'un message et de propriétés structurées.

Il est crucial de définir le niveau minimum selon l'environnement :

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug() // Autorise les logs détaillés en développement
    .WriteTo.Console()
    .CreateLogger();

Log.Debug("Calcul du module {ModuleId} en cours...", id);

Note : Pour les exceptions, passez toujours l'objet exception comme premier paramètre : Log.Error(ex, "Échec de la connexion à la base de données");.

Journalisation de données structurées

La force de Serilog réside dans l'utilisation de modèles de messages. En utilisant des jetons nommés, vous capturez des données exploitables.

var zoneId = "FR-01";
var chargeUtile = 450;

Log.Information("Traitement de la zone {Zone} avec une charge de {Poids} kg", zoneId, chargeUtile);

En coulisse, Serilog ne se contente pas de formater une chaîne ; il stocke les propriétés Zone et Poids. Si vous exportez ces données en JSON, vous obtiendrez :

{
  "Timestamp": "2023-10-27T10:00:00",
  "Level": "Information",
  "Message": "Traitement de la zone \"FR-01\" avec une charge de 450 kg",
  "Zone": "FR-01",
  "Poids": 450
}

Pour capturer un objet complexe entier, utilisez l'opérateur de déstructuration @ :

var client = new { Id = 12, Nom = "Entreprise ACME" };
Log.Information("Nouvelle commande pour {@Client}", client);

Persistance au format JSON

Pour stocker des logs structurés localement, le format JSON est idéal. Installez le sink de ficheir et le formateur compact :

dotnet add package Serilog.Sinks.File
dotnet add package Serilog.Formatting.Compact

Configurez ensuite l'écriture :

Log.Logger = new LoggerConfiguration()
    .WriteTo.File(new CompactJsonFormatter(), "logs/app-log.json")
    .CreateLogger();

Enrichissement des données (Enrichment)

L'enrichissement permet d'ajouter automatiquement des informations à chaque événement produit. Cela peut inclure le nom de la machine, l'ID d'un thread ou un identifiant de corrélation.

Log.Logger = new LoggerConfiguration()
    .Enrich.WithProperty("Version", "1.0.5")
    .Enrich.FromLogContext()
    .WriteTo.Console()
    .CreateLogger();

Le LogContext est particulièrement utile pour suivre une transaction à travers plusieurs appels de fonction :

using (LogContext.PushProperty("TransactionId", guid))
{
    Log.Information("Début du traitement...");
    ExecuteLogic(); // Tout log émis ici contiendra le TransactionId
}

Filtrage avancé

Grâce à la structure des données, vous pouvez filtrer vos logs par propriété plutôt que par texte brut. Si vous utilisez un serveur de logs compatible (comme Seq ou Elasticsearch), vous pouvez exécuter des requêtes telles que Poids > 400 pour isoler immédiatement les événements pertinents dans des millions de lignes de logs.

Écosystème et intégrations

Serilog dispose d'un écosystème riche pour s'adapter à divers besoins :

  • Serilog.AspNetCore : Intégration transparente pour capturer les reequêtes HTTP.
  • Serilog.Settings.Configuration : Permet de configurer Serilog via le fichier appsettings.json.
  • Serilog.Exceptions : Ajoute des détails spécifiques aux exceptions levées.
  • Serilog.Sinks.Async : Optimise les performances en écrivant les logs de manière asynchrone.

Étiquettes: Serilog dotnet logging structured-logging CSharp

Publié le 6 juin à 16h07