Skip to main content
ClaudeWave
Skill64 repo starsupdated 22d ago

dotnet-logging-configuration

Configures structured logging with ILogger<T> and ILoggerFactory following Microsoft best practices. Includes Serilog setup, log enrichment, and logging source generators for high-performance logging.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/ronnythedev/dotnet-clean-architecture-skills /tmp/dotnet-logging-configuration && cp -r /tmp/dotnet-logging-configuration/skills/23-dotnet-logging-configuration ~/.claude/skills/dotnet-logging-configuration
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# Logging Configuration for .NET

## Overview

This skill configures structured logging following Microsoft best practices:

- **ILogger<T>** - Inject into services for category-based logging
- **ILoggerFactory** - Create loggers dynamically when needed
- **Structured Logging** - Preserve log properties for analysis
- **Source Generators** - High-performance compile-time logging
- **Serilog Integration** - Enhanced sinks and enrichers

## Quick Reference

| Interface | Lifetime | Use Case |
|-----------|----------|----------|
| `ILogger<T>` | Singleton | Standard service logging |
| `ILoggerFactory` | Singleton | Create loggers dynamically |
| `ILogger` | - | Non-generic (avoid in DI) |

---

## Logging Structure

```
/Infrastructure/Logging/
├── LoggerConfiguration.cs
├── LogEvents.cs           # LoggerMessage source generators
├── LogEnrichers/
│   ├── UserContextEnricher.cs
│   └── CorrelationIdEnricher.cs
└── Sinks/
    └── CustomSink.cs
```

---

## Template: Service with ILogger<T>

```csharp
// src/{name}.application/Services/OrderService.cs
using Microsoft.Extensions.Logging;

namespace {name}.application.services;

/// <summary>
/// Services should inject ILogger<T> for category-based logging.
/// The category is automatically set to the full type name.
/// </summary>
public sealed class OrderService : IOrderService
{
    private readonly ILogger<OrderService> _logger;
    private readonly IOrderRepository _orderRepository;

    public OrderService(
        ILogger<OrderService> logger,
        IOrderRepository orderRepository)
    {
        _logger = logger;
        _orderRepository = orderRepository;
    }

    public async Task<Result<Order>> ProcessOrderAsync(
        Guid orderId,
        CancellationToken cancellationToken)
    {
        // ═══════════════════════════════════════════════════════════════
        // STRUCTURED LOGGING - Use placeholders, not string interpolation
        // ═══════════════════════════════════════════════════════════════
        _logger.LogInformation(
            "Processing order {OrderId}",
            orderId);

        try
        {
            var order = await _orderRepository.GetByIdAsync(orderId, cancellationToken);

            if (order is null)
            {
                _logger.LogWarning(
                    "Order {OrderId} not found",
                    orderId);

                return Result.Failure<Order>(OrderErrors.NotFound(orderId));
            }

            // Log with multiple properties
            _logger.LogInformation(
                "Order {OrderId} retrieved. Status: {Status}, Total: {Total}",
                orderId,
                order.Status,
                order.Total);

            return Result.Success(order);
        }
        catch (Exception ex)
        {
            // Always log exceptions with the exception parameter first
            _logger.LogError(
                ex,
                "Error processing order {OrderId}",
                orderId);

            throw;
        }
    }
}
```

---

## Template: ILoggerFactory Usage

```csharp
// src/{name}.infrastructure/Services/DynamicLoggerService.cs
using Microsoft.Extensions.Logging;

namespace {name}.infrastructure.services;

/// <summary>
/// Use ILoggerFactory when you need to create loggers dynamically.
/// Common use cases:
/// - Factory classes that create multiple types
/// - Plugin systems where type isn't known at compile time
/// - Base classes that want child-specific categories
/// </summary>
public sealed class PluginManager
{
    private readonly ILoggerFactory _loggerFactory;

    public PluginManager(ILoggerFactory loggerFactory)
    {
        _loggerFactory = loggerFactory;
    }

    public IPlugin LoadPlugin(string pluginName, Type pluginType)
    {
        // Create a logger with a dynamic category
        var logger = _loggerFactory.CreateLogger(pluginType);

        logger.LogInformation(
            "Loading plugin {PluginName} of type {PluginType}",
            pluginName,
            pluginType.Name);

        // Or with a string category
        var customLogger = _loggerFactory.CreateLogger($"Plugins.{pluginName}");

        customLogger.LogDebug(
            "Plugin {PluginName} initialized",
            pluginName);

        return CreatePlugin(pluginType, customLogger);
    }
}
```

---

## Template: High-Performance Logging with Source Generators

```csharp
// src/{name}.application/Logging/LogEvents.cs
using Microsoft.Extensions.Logging;

namespace {name}.application.logging;

/// <summary>
/// LoggerMessage source generators provide the best logging performance.
/// Benefits:
/// - Zero allocation for disabled log levels
/// - Compile-time validation of message templates
/// - Strongly typed parameters
/// </summary>
public static partial class LogEvents
{
    // ═══════════════════════════════════════════════════════════════
    // INFORMATION LEVEL
    // ═══════════════════════════════════════════════════════════════

    [LoggerMessage(
        EventId = 1000,
        Level = LogLevel.Information,
        Message = "Processing request {RequestName} with ID {RequestId}")]
    public static partial void LogRequestProcessing(
        this ILogger logger,
        string requestName,
        Guid requestId);

    [LoggerMessage(
        EventId = 1001,
        Level = LogLevel.Information,
        Message = "Request {RequestName} completed in {ElapsedMs}ms")]
    public static partial void LogRequestCompleted(
        this ILogger logger,
        string requestName,
        long elapsedMs);

    [LoggerMessage(
        EventId = 1002,
        Level = LogLevel.Information,
        Message = "User {UserId} authenticated successfully")]
    public static partial void LogUserAuthenticated(
        this ILogger logger,
        Guid userId);

    // ═══════════════════════════════════════════════════════════════
    // WARNING LEVEL
    // ═══════════════════════════════════════════════════════════════

    [Logge
dotnet-clean-architectureSkill

Scaffolds a complete .NET solution following Clean Architecture principles with proper layer separation (API, Application, Domain, Infrastructure). Creates project structure, dependency injection setup, and cross-cutting concerns configuration.

dotnet-cqrs-command-generatorSkill

Generates CQRS Commands with Handlers, Validators, and Request DTOs following Clean Architecture patterns. Commands represent actions that modify state and return Result types for proper error handling.

dotnet-cqrs-query-generatorSkill

Generates CQRS Queries with Handlers and Response DTOs for read operations. Uses Dapper for optimized read queries, bypassing the domain model for better performance.

dotnet-domain-entity-generatorSkill

Generates Domain Entities following DDD principles with factory methods, private setters, domain events, and proper encapsulation. Supports aggregate roots, child entities, and value objects.

dotnet-repository-patternSkill

Generates Repository interfaces and implementations following the Repository pattern. Provides data access abstraction for aggregate roots with EF Core implementations.

dotnet-ef-core-configurationSkill

Generates Entity Framework Core configurations using Fluent API. Maps domain entities to database tables with proper relationships, constraints, and conventions.

dotnet-legacy-api-controllersSkill

Generates RESTful API Controllers with proper routing, versioning, authorization, and MediatR integration. Follows REST conventions and Clean Architecture patterns.

dotnet-minimal-api-endpointsSkill

Generates Minimal API endpoints following Microsoft's recommended approach. Creates fast, testable HTTP APIs with minimal code using MapGet/MapPost/MapPut/MapDelete. Preferred over controller-based APIs for new projects.