Serilog configuration with two-stage bootstrap, structured logging, enrichers, Seq sink, and request logging middleware. Trigger: Serilog, structured logging, logging, Seq, enricher, log.
From dotnet-ai-kitnpx claudepluginhub faysilalshareef/dotnet-ai-kit --plugin dotnet-ai-kitThis skill uses the workspace's default tool permissions.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Executes pre-written implementation plans: critically reviews, follows bite-sized steps exactly, runs verifications, tracks progress with checkpoints, uses git worktrees, stops on blockers.
{PropertyName} — never string interpolation// Program.cs
using Serilog;
// Stage 1: Bootstrap logger for startup errors
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateBootstrapLogger();
try
{
var builder = WebApplication.CreateBuilder(args);
// Stage 2: Full logger with configuration
builder.Host.UseSerilog((context, services, configuration) =>
configuration
.ReadFrom.Configuration(context.Configuration)
.ReadFrom.Services(services)
.Enrich.FromLogContext()
.Enrich.WithMachineName()
.Enrich.WithProperty("Application", "{Domain}Service")
.WriteTo.Console(outputTemplate:
"[{Timestamp:HH:mm:ss} {Level:u3}] " +
"{Message:lj} {Properties:j}" +
"{NewLine}{Exception}")
.WriteTo.Seq(
context.Configuration["Seq:Url"]
?? "http://localhost:5341"));
// Configure services...
var app = builder.Build();
// Request logging middleware
app.UseSerilogRequestLogging(options =>
{
options.EnrichDiagnosticContext = (diagnosticContext,
httpContext) =>
{
diagnosticContext.Set("UserId",
httpContext.User.FindFirstValue(
ClaimTypes.NameIdentifier) ?? "anonymous");
diagnosticContext.Set("ClientIp",
httpContext.Connection.RemoteIpAddress?.ToString());
};
});
app.Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Application terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore.Database.Command": "Warning",
"Microsoft.EntityFrameworkCore.Infrastructure": "Warning",
"System": "Warning"
}
}
},
"Seq": {
"Url": "http://localhost:5341"
}
}
// GOOD: Structured properties — queryable in Seq
logger.LogInformation(
"Order {OrderId} created for customer {CustomerId} " +
"with total {OrderTotal:C}",
orderId, customerId, total);
// BAD: String interpolation — loses structure
logger.LogInformation(
$"Order {orderId} created for customer {customerId}");
// GOOD: Object destructuring with @
logger.LogInformation(
"Processing order {@OrderDetails}",
new { orderId, customerId, ItemCount = items.Count });
// GOOD: Exception as first parameter
logger.LogError(ex,
"Failed to process order {OrderId}", orderId);
// BAD: Exception in message template
logger.LogError(
"Failed to process order {OrderId}: {Error}",
orderId, ex.Message); // loses stack trace
// Push properties for a scope — all logs within include them
using (LogContext.PushProperty("OrderId", orderId))
using (LogContext.PushProperty("CorrelationId",
Activity.Current?.TraceId.ToString()))
{
logger.LogInformation("Starting order processing");
await ProcessItemsAsync(order, ct);
logger.LogInformation("Order processing complete");
// Both logs include OrderId and CorrelationId
}
public sealed class CorrelationIdEnricher(
IHttpContextAccessor httpContextAccessor) : ILogEventEnricher
{
public void Enrich(LogEvent logEvent,
ILogEventPropertyFactory propertyFactory)
{
var correlationId =
httpContextAccessor.HttpContext?
.Request.Headers["X-Correlation-Id"]
.FirstOrDefault()
?? Activity.Current?.TraceId.ToString()
?? Guid.NewGuid().ToString();
logEvent.AddPropertyIfAbsent(
propertyFactory.CreateProperty(
"CorrelationId", correlationId));
}
}
// Registration
builder.Host.UseSerilog((context, services, configuration) =>
configuration
.Enrich.With(
services.GetRequiredService<CorrelationIdEnricher>())
// ... rest of config
);
Verbose — Detailed debugging (not in production)
Debug — Internal state useful for debugging
Information — Normal operation events (request handled, order created)
Warning — Unexpected but handled (retry, degraded, slow query)
Error — Failure that needs attention (exception, failed operation)
Fatal — Application cannot continue (startup failure, data corruption)
// Check if level is enabled before expensive operations
if (logger.IsEnabled(LogLevel.Debug))
{
var serialized = JsonSerializer.Serialize(complexObject);
logger.LogDebug("Request payload: {Payload}", serialized);
}
<PackageReference Include="Serilog.AspNetCore" />
<PackageReference Include="Serilog.Sinks.Seq" />
<PackageReference Include="Serilog.Sinks.Console" />
<PackageReference Include="Serilog.Enrichers.Environment" />
<PackageReference Include="Serilog.Enrichers.Thread" />
$"Order {id}")try/catch around application startupLog.CloseAndFlush() in finally blockSerilog.AspNetCore package in .csprojUseSerilog in Program.csLog.Logger = new LoggerConfiguration() initializationWriteTo.Seq or WriteTo.Console configurationappsettings.json for "Serilog" sectionSerilog.AspNetCore, Serilog.Sinks.SeqProgram.csILogger string interpolation with structured templates| Scenario | Sink |
|---|---|
| Development | Console |
| Centralized logging | Seq, Elasticsearch, Datadog |
| Cloud native | Console (stdout) + aggregator |
| File-based | Rolling file (not recommended for containers) |