| name | logging |
| description | Observability for .NET 10 applications. Covers Serilog structured logging, OpenTelemetry traces and metrics, health checks, and correlation IDs. Load this skill when setting up logging, tracing, metrics, or health monitoring, or when the user mentions "Serilog", "logging", "structured log", "OpenTelemetry", "traces", "metrics", "health check", "correlation ID", "observability", "telemetry", "log enrichment", or "ILogger".
|
Logging & Observability
Core Principles
- Structured logging with Serilog — Every log entry is a structured event with named properties, not a formatted string. This enables searching, filtering, and alerting.
- OpenTelemetry for distributed tracing — Traces connect requests across services. Metrics track system health over time.
- Health checks for operational readiness — Every service exposes
/health endpoints for load balancers and orchestrators.
- Correlation IDs for request tracing — Every request gets a unique ID that flows through all log entries and downstream service calls.
Patterns
Serilog Setup
builder.Host.UseSerilog((context, loggerConfig) =>
{
loggerConfig
.ReadFrom.Configuration(context.Configuration)
.Enrich.FromLogContext()
.Enrich.WithMachineName()
.Enrich.WithProperty("Application", "MyApp.Api")
.WriteTo.Console(outputTemplate:
"[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}")
.WriteTo.Seq(context.Configuration["Seq:Url"] ?? "http://localhost:5341");
});
app.UseSerilogRequestLogging(options =>
{
options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
{
diagnosticContext.Set("UserId",
httpContext.User.FindFirstValue(ClaimTypes.NameIdentifier) ?? "anonymous");
};
});
Structured Logging (Correct Usage)
logger.LogInformation("Processing order {OrderId} for customer {CustomerId}",
orderId, customerId);
logger.LogWarning("Payment failed for order {OrderId}. Attempt {Attempt} of {MaxAttempts}",
orderId, attempt, maxAttempts);
logger.LogError(exception, "Failed to process order {OrderId}", orderId);
Correlation IDs
public class CorrelationIdMiddleware(RequestDelegate next)
{
private const string CorrelationIdHeader = "X-Correlation-Id";
public async Task InvokeAsync(HttpContext context)
{
var correlationId = context.Request.Headers[CorrelationIdHeader].FirstOrDefault()
?? Guid.NewGuid().ToString();
context.Items["CorrelationId"] = correlationId;
context.Response.Headers[CorrelationIdHeader] = correlationId;
using (LogContext.PushProperty("CorrelationId", correlationId))
{
await next(context);
}
}
}
app.UseMiddleware<CorrelationIdMiddleware>();
OpenTelemetry Integration
For full OpenTelemetry setup (metrics, tracing, OTLP export), see the opentelemetry skill.
The logging skill focuses on structured logging with Serilog. OpenTelemetry handles the export pipeline.
Health Checks
builder.Services.AddHealthChecks()
.AddNpgSql(builder.Configuration.GetConnectionString("Default")!,
name: "database", tags: ["ready"])
.AddRedis(builder.Configuration.GetConnectionString("Redis")!,
name: "redis", tags: ["ready"])
.AddRabbitMQ(builder.Configuration.GetConnectionString("RabbitMq")!,
name: "rabbitmq", tags: ["ready"]);
app.MapHealthChecks("/health/live", new HealthCheckOptions
{
Predicate = _ => false
});
app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("ready")
});
Anti-patterns
Don't Use String Interpolation in Log Messages
logger.LogInformation($"Order {orderId} created for {customerId}");
logger.LogInformation("Order {OrderId} created for {CustomerId}", orderId, customerId);
Don't Log Sensitive Data
logger.LogInformation("User logged in: {Email} with password {Password}", email, password);
logger.LogInformation("User logged in: {Email}", email);
Don't Skip Health Check Tags
app.MapHealthChecks("/health");
app.MapHealthChecks("/health/live", new() { Predicate = _ => false });
app.MapHealthChecks("/health/ready", new() { Predicate = c => c.Tags.Contains("ready") });
Decision Guide
| Scenario | Recommendation |
|---|
| Application logging | Serilog with structured logging |
| Distributed tracing | OpenTelemetry with OTLP exporter |
| Custom business metrics | IMeterFactory + counters/histograms |
| Request tracing | Correlation ID middleware |
| Container health | /health/live and /health/ready endpoints |
| Log storage | Seq (development), Elastic/Grafana (production) |
| Log levels | Debug in dev, Information in staging, Warning in production |