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.
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-configurationSKILL.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
// ═══════════════════════════════════════════════════════════════
[LoggeScaffolds 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.
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.
Generates CQRS Queries with Handlers and Response DTOs for read operations. Uses Dapper for optimized read queries, bypassing the domain model for better performance.
Generates Domain Entities following DDD principles with factory methods, private setters, domain events, and proper encapsulation. Supports aggregate roots, child entities, and value objects.
Generates Repository interfaces and implementations following the Repository pattern. Provides data access abstraction for aggregate roots with EF Core implementations.
Generates Entity Framework Core configurations using Fluent API. Maps domain entities to database tables with proper relationships, constraints, and conventions.
Generates RESTful API Controllers with proper routing, versioning, authorization, and MediatR integration. Follows REST conventions and Clean Architecture patterns.
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.