Kapela.Logs.SqlServerProvider 10.2.0

Prefix Reserved
dotnet add package Kapela.Logs.SqlServerProvider --version 10.2.0
                    
NuGet\Install-Package Kapela.Logs.SqlServerProvider -Version 10.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Kapela.Logs.SqlServerProvider" Version="10.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Kapela.Logs.SqlServerProvider" Version="10.2.0" />
                    
Directory.Packages.props
<PackageReference Include="Kapela.Logs.SqlServerProvider" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Kapela.Logs.SqlServerProvider --version 10.2.0
                    
#r "nuget: Kapela.Logs.SqlServerProvider, 10.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Kapela.Logs.SqlServerProvider@10.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Kapela.Logs.SqlServerProvider&version=10.2.0
                    
Install as a Cake Addin
#tool nuget:?package=Kapela.Logs.SqlServerProvider&version=10.2.0
                    
Install as a Cake Tool

🗄️ Kapela.Logs.SqlServerProvider

Enregistrez vos logs directement dans Microsoft SQL Server avec création automatique de table et support des colonnes personnalisées.


🧭 Table des matières


🎯 Présentation

Kapela.Logs.SqlServerProvider persiste les logs dans une base de données Microsoft SQL Server. Il offre :

  • ✅ Création automatique de la table de logs au démarrage
  • ✅ Migration automatique du schéma — tout ajout ou retrait de propriété [SqlLog] est propagé au prochain démarrage, sans script SQL à écrire
  • ✅ Mapping automatique des propriétés vers des colonnes SQL via l'attribut [SqlLog]
  • ✅ Rétention configurable avec suppression automatique des anciens enregistrements
  • ✅ Schéma configurable (par défaut dbo)
  • ✅ Écriture bufferisée en arrière-plan pour des performances optimales

📦 Dépend de Kapela.Logs, installé automatiquement par NuGet — pas besoin de l'ajouter séparément.


🆕 Nouveautés

Cette version introduit la lecture typée des logs via ILogReader, branchée automatiquement lors de l'enregistrement du provider.

Ajouts

  • Trois variantes d'ILogReader selon le typage des propriétés étendues (ILogReader, ILogReader<TLogProps>, ILogReader<TLogProps, TScopeProps>).
  • DTO LogEntry et ScopeEntry (et leurs variantes génériques) — retournés par le reader, colonnes standards + propriétés étendues typées + scope parent.
  • Requêtes fluentes LogQuery / ScopeQuery — Take(n), OrderAscending() / OrderDescending(), ForScope(Guid), WithoutScope().
  • Pagination SQL Server native via TOP (n) ; rattachement des scopes aux logs en une seule requête groupée (pas de N+1).

Enregistrement automatique

  • Chaque surcharge UseSqlServerProvider enregistre désormais la variante d'ILogReader correspondante dans l'injection de dépendances.

Note

  • Renommage du nom de ScopeTableName par défaut : "LogsOperation" → "LogsOperations".

📖 Détail d'utilisation dans la section Lecture des logs plus bas.


📥 Installation

.NET CLI

dotnet add package Kapela.Logs.SqlServerProvider

Package Manager Console

Install-Package Kapela.Logs.SqlServerProvider

🚀 Démarrage rapide

using Kapela.Logs;
using Kapela.Logs.SqlServerProvider;

builder.UseKapelaLogs(logs =>
{
    logs.UseSqlServerProvider(
        "Server=myServer;Database=myDB;Trusted_Connection=True;",
        new SqlServerLoggerOptions
        {
            TableName = "AppLogs",
            AutoCreateTable = true,
            NbDayRetention = 90
        }
    );
});

⚙️ Options de configuration

Propriété Type Description Défaut
ConnectionString string Lecture seule — définie via le paramètre de UseSqlServerProvider() —
SqlSchema string Schéma de la table "dbo"
TableName string Nom de la table de logs "Logs"
AutoCreateTable bool Crée le schéma (SqlSchema) puis la table s'ils n'existent pas false
AutoMigrate bool Compare le schéma de la table avec le modèle et applique les différences (colonnes ajoutées ou supprimées). Nécessite AutoCreateTable = true. false
AutoMigrateAllowDrop bool Autorise la suppression des colonnes obsolètes. Si false, les colonnes absentes du modèle sont rendues nullable plutôt que supprimées. Nécessite AutoMigrate = true. false
EnableScopes bool Active la gestion des scopes. Crée une table LogsOperations et ajoute une colonne OperationId dans Logs. false
ScopeTableName string Nom de la table des opérations (scopes). "LogsOperations"
NbDayRetention uint Jours de conservation (0 = illimité) 0
Timeout uint Délai d'expiration des commandes SQL en secondes (0 = pas de limite) 0
GlobalLogLevel LogLevel? Niveau minimal pour ce provider Information

🏗️ Création automatique de table

C'est le mode recommandé : avec AutoCreateTable = true (combiné ou non à AutoMigrate = true), le provider crée au premier démarrage le schéma SqlSchema (s'il n'existe pas) puis la table, et aligne ensuite ses colonnes sur votre modèle .NET à chaque démarrage suivant. Aucune DDL à rédiger, aucune migration à synchroniser à la main.

ℹ️ Le schéma n'est jamais supprimé. Si SqlSchema est vide, aucune qualification par schéma n'est appliquée.

CREATE TABLE [dbo].[AppLogs] (
    [Id]        [int] IDENTITY(1,1) NOT NULL,
    [Date]      [datetime] NOT NULL,
    [Level]     [nvarchar](20) NOT NULL,
    [LevelId]   [int] NOT NULL,
    [Source]    [nvarchar](100) NOT NULL,
    [Message]   [nvarchar](MAX) NOT NULL,
    [Exception] [nvarchar](MAX) NULL
)

📌 Besoin de garder la main sur le schéma (migrations EF Core, Flyway, Liquibase, scripts DBA) ? Voir Gestion manuelle du schéma juste en dessous.


🛠️ Gestion manuelle du schéma

Les options d'automatisation (AutoCreateTable, AutoMigrate, AutoMigrateAllowDrop) sont désactivées par défaut. Sans action explicite, le provider ne crée, n'altère ni ne supprime aucune table ni colonne — il se contente d'insérer les logs dans les tables existantes.

Ce mode permet d'intégrer le provider dans un projet dont le schéma est déjà piloté par un outil de migration externe. Il est fonctionnel mais moins confortable : c'est à vous de garantir que la base reste alignée sur le modèle .NET.

Dans ce mode, vous êtes responsable de :

  • créer la table de logs avec les colonnes standards (Id, Date, Level, LevelId, Source, Message, Exception) et les colonnes issues de votre ILogProperties ;
  • si les scopes sont activés, créer la table LogsOperations (PK Id GUID, colonnes Name, StartDate, EndDate nullable, plus les colonnes issues de votre ILogScopeProperties) et ajouter une colonne OperationId nullable dans la table de logs ;
  • maintenir la cohérence du schéma lors des évolutions de votre modèle — tout nouveau [SqlLog] ou toute colonne retirée doit être reflété en base.

💡 Recommandation — pour la grande majorité des déploiements, laisser le provider gérer la création et la migration supprime une classe entière de bugs (oubli d'une colonne après ajout d'un [SqlLog], désalignement entre prod et dev). Privilégiez la gestion manuelle seulement si votre environnement l'impose (politique DBA, pipeline de migration déjà en place).


🏷️ Colonnes personnalisées avec [SqlLog]

L'attribut [SqlLog] mappe des propriétés .NET vers des colonnes SQL. Il s'applique aussi bien aux logs (classes implémentant ILogProperties) qu'aux scopes (classes implémentant ILogScopeProperties — voir section Scopes plus bas).

Enrichissez vos logs avec des données métier en implémentant ILogProperties :

using Kapela.Logs;

public class AppLogContext : ILogProperties
{
    [SqlLog(Name = "UserId", MaxLength = 50)]
    public string? UserId { get; set; }

    [SqlLog(MaxLength = 200)]
    public string? RequestPath { get; set; }

    [SqlLog]
    public int? StatusCode { get; set; }

    [SqlLog(Name = "CorrelationId", MaxLength = 36)]
    public string? TraceId { get; set; }
}

Paramètres de [SqlLog]

Paramètre Type Description
Name string Nom de la colonne en base (défaut : nom de la propriété)
MaxLength uint Taille maximale pour les colonnes nvarchar

Enregistrement avec le contexte personnalisé

builder.UseKapelaLogs(logs =>
{
    logs.UseSqlServerProvider<AppLogContext>(
        connectionString,
        new SqlServerLoggerOptions { AutoCreateTable = true }
    );
});

🔗 Scopes

Les scopes permettent de regrouper plusieurs logs sous une opération logique commune. Chaque log émis dans le scope est lié à l'opération via une colonne OperationId dans la table de logs ; l'opération elle-même est tracée dans une table dédiée (LogsOperations).

Activation

builder.UseKapelaLogs(logs =>
{
    logs.UseSqlServerProvider(
        connectionString,
        new SqlServerLoggerOptions
        {
            AutoCreateTable = true,
            AutoMigrate = true,   // requis si la table Logs existe déjà
            EnableScopes = true
        }
    );
});

⚠️ Si la table Logs a été créée sans EnableScopes, la colonne OperationId est absente. AutoMigrate = true est nécessaire pour qu'elle soit ajoutée automatiquement au démarrage. Sans cela, l'émission d'un log dans un scope provoquera une erreur SQL.

Propriétés étendues sur l'opération (ILogScopeProperties)

Pour ajouter des colonnes personnalisées à la table LogsOperations, créez une classe implémentant ILogScopeProperties :

public class MyScopeProps : ILogScopeProperties
{
    [SqlLog(Name = "CommandeId")]
    public Guid? CommandeId { get; set; }

    [SqlLog(MaxLength = 100)]
    public string? Client { get; set; }
}

Enregistrez les deux types au niveau du provider :

logs.UseSqlServerProvider<AppLogContext, MyScopeProps>(connectionString, options);

Utilisation

var scope = new LogScope("TraiterCommande", new MyScopeProps
{
    CommandeId = commandeId,
    Client = "dupont"
});

using (logger.BeginScope(scope))
{
    logger.LogInformation("Début du traitement");
    logger.LogWarning("Stock faible");
}
// À la sortie du using : EndDate est renseignée dans LogsOperations

Tables générées

LogsOperations

Colonne Description
Id (GUID) Clé primaire
Name Nom de l'opération
StartDate Date de début
EndDate (nullable) Date de fin, renseignée à la fermeture du scope
(colonnes étendues) Issues de ILogScopeProperties

Logs — colonne ajoutée :

Colonne Description
OperationId (GUID, nullable) Référence vers LogsOperations

📖 Lecture des logs

Chaque surcharge de UseSqlServerProvider() enregistre aussi un ILogReader adapté dans l'injection de dépendances. Résolvez-le par injection pour interroger la base sans réécrire de SQL.

Variantes enregistrées

Surcharge utilisée Lecteur enregistré
UseSqlServerProvider(cs, options) ILogReader
UseSqlServerProvider<TLogProps>(cs, options) ILogReader<TLogProps>
UseSqlServerProvider<TLogProps, TScopeProps>(cs, options) ILogReader<TLogProps, TScopeProps>

Requête fluente — LogQuery

Méthode Rôle Défaut
Take(uint) Nombre maximum de logs 100
OrderAscending() / OrderDescending() Tri par Date Descendant
ForScope(Guid) Restreint aux logs d'un scope donné (requiert EnableScopes = true) —
WithoutScope() Restreint aux logs orphelins non rattachés à un scope (requiert EnableScopes = true) —

Requête fluente — ScopeQuery

Méthode Rôle Défaut
Take(uint) Nombre maximum de scopes 100
OrderAscending() / OrderDescending() Tri par StartDate Descendant
ForScope(Guid) Restreint à un scope précis (0 ou 1 résultat) —

💡 GetScopesAsync() retourne [] si EnableScopes = false. Même comportement pour GetLogsAsync(q => q.ForScope(...)) et GetLogsAsync(q => q.WithoutScope()) quand les scopes sont désactivés.

Exemple

public class LogsPage(ILogReader<AppLogContext, MyScopeProps> reader)
{
    public async Task<IReadOnlyList<LogEntry<AppLogContext, MyScopeProps>>> LoadAsync(CancellationToken ct)
    {
        return await reader.GetLogsAsync(q => q.Take(50).OrderDescending(), ct);
    }
}

Chaque LogEntry<TLogProps, TScopeProps> expose :

  • les colonnes standards (Id, Date, LogLevel, Source, Message, Exception),
  • les propriétés étendues fortement typées via ExtendedProperties (instance de TLogProps),
  • le scope parent typé via Scope (instance de ScopeEntry<TScopeProps>?), s'il existe.

⚙️ Pagination : SQL Server utilise TOP (n) ; les scopes sont rattachés aux logs en une seule requête groupée (pas de N+1).


⚙️ Configuration appsettings.json

{
  "Logging": {
    "SqlServer": {
      "SqlSchema": "dbo",
      "TableName": "AppLogs",
      "AutoCreateTable": true,
      "NbDayRetention": 90,
      "Timeout": 30,
      "GlobalLogLevel": "Information",
      "LogLevel": {
        "Default": "Information",
        "Microsoft": "Warning"
      }
    }
  },
  "ConnectionStrings": {
    "Logs": "<votre chaîne de connexion>"
  }
}

🔐 ConnectionString : passez-la toujours via builder.Configuration.GetConnectionString("Logs") (section ConnectionStrings d'appsettings.json) plutôt que dans Logging:SqlServer. Ne stockez jamais une chaîne de connexion en clair — si vous utilisez Kapela.Security.Encryption, préférez EncryptionHelper.GetConnectionString(builder.Configuration, "Logs") qui gère automatiquement le déchiffrement.


💡 Exemple complet

// Propriétés étendues
public class AppLogContext : ILogProperties
{
    [SqlLog(MaxLength = 50)]
    public string? UserId { get; set; }

    [SqlLog(MaxLength = 20)]
    public string? AppEnvironment { get; set; }
}

// Program.cs
builder.UseKapelaLogs(logs =>
{
    logs.UseSqlServerProvider<AppLogContext>(
        builder.Configuration.GetConnectionString("Logs")!,         // sans chiffrement
        // EncryptionHelper.GetConnectionString(builder.Configuration, "Logs"), // avec Kapela.Security.Encryption
        new SqlServerLoggerOptions
        {
            SqlSchema = "logs",
            TableName = "ApplicationLogs",
            AutoCreateTable = true,
            NbDayRetention = 365,
            GlobalLogLevel = LogLevel.Information,
            LogLevel =
            {
                ["Microsoft.AspNetCore"] = LogLevel.Warning
            }
        }
    );
});

// MyService.cs
public class MyService(ILogger<MyService> logger)
{
    public void ProcessRequest(string userId, string path)
    {
        logger.LogInformation("Requête reçue", new AppLogContext
        {
            UserId = userId,
            AppEnvironment = "Production"
        });

        logger.LogError("Erreur lors du traitement", new AppLogContext
        {
            UserId = userId,
            AppEnvironment = "Production"
        });
    }
}

📄 Licence MIT — © Kapela 2024-2026

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
10.2.0 139 7/13/2026
10.1.0 150 4/23/2026
10.0.3 110 4/19/2026
10.0.2 115 4/9/2026
10.0.1 112 2/11/2026