PluginBench
Skill
Review
Audit score 70

logging

codewithmukesh/dotnet-claude-kit

Health checks, correlation IDs, and observability wiring for .NET 10 services.

What is logging?

Provides the observability glue for .NET 10: health check endpoints (/health/live and /health/ready), correlation ID middleware for request tracing, and log-level strategy. Use this when setting up observability from scratch, wiring health checks, or establishing request tracing across services.

  • Separate liveness and readiness health check endpoints to distinguish restart-worthy failures from traffic-routing decisions
  • Inject correlation IDs into every request and log entry via middleware, enabling request tracing across service boundaries
  • Define log-level strategy (Debug for dev, Information for staging, Warning default for production) to balance signal and cost
  • Provide patterns for propagating correlation IDs on outgoing HttpClient calls
  • Clarify the ownership model: Serilog setup lives in the serilog skill, OpenTelemetry in the opentelemetry skill, and cross-cutting concerns here

How to install logging

npx skills add https://github.com/codewithmukesh/dotnet-claude-kit --skill logging
Claude Code
Cursor
Windsurf
Cline

How to use logging

  1. 1.Add the CorrelationIdMiddleware class to your project and register it early in the request pipeline with app.UseMiddleware<CorrelationIdMiddleware>()
  2. 2.Call builder.Services.AddHealthChecks() and register dependency checks (database, cache, message broker) with the 'ready' tag
  3. 3.Map two health check endpoints: /health/live with Predicate = _ => false (no checks, just liveness), and /health/ready with Predicate = check => check.Tags.Contains("ready")
  4. 4.For outgoing HttpClient calls, use a DelegatingHandler to propagate the X-Correlation-Id header (see httpclient-factory skill)
  5. 5.Set log-level defaults: Debug in development, Information in staging, Warning in production; use MinimumLevel.Override for namespace-specific tuning (see serilog skill)

Use cases

Good for
  • Setting up health check endpoints for Kubernetes or container orchestrators to distinguish liveness probes from readiness probes
  • Adding request correlation IDs to trace a single user complaint through logs across multiple services
  • Establishing log-level defaults that avoid Information-level noise in production while keeping diagnostic detail in development
  • Wiring correlation ID propagation on outgoing HTTP calls via DelegatingHandler
  • Choosing between Debug, Information, Warning, Error, and Fatal log levels for different environments
Who it's for
  • Backend engineers building .NET 10 microservices
  • DevOps and SRE teams configuring health checks for orchestrators
  • Teams adopting structured logging and distributed tracing
  • Developers debugging cross-service request flows

logging FAQ

Why separate /health/live and /health/ready endpoints?

Liveness failure signals the orchestrator to restart the container; readiness failure signals to stop routing traffic. Conflating them causes cascading restarts when a slow dependency (e.g., database) recovers slowly.

How do I propagate correlation IDs to downstream services?

Use a DelegatingHandler in your HttpClient factory to read the correlation ID from the current request context (via HttpContext.Items or LogContext) and attach it as the X-Correlation-Id header on outgoing calls. See the httpclient-factory skill.

Should I log at Information level in production?

No. Information-level request noise at scale is expensive and drowns signals. Use Warning as the production default and override specific namespaces (e.g., business events) to Information via MinimumLevel.Override in the serilog skill.

Where do I configure Serilog sinks and enrichers?

Load the serilog skill, which owns the two-stage AddSerilog() bootstrap. This skill provides the cross-cutting concerns (health checks, correlation IDs, log-level strategy) that sit on top.

How do I add custom health checks?

Call AddCheck<T>() on the IHealthChecksBuilder returned by AddHealthChecks(), tag it with 'ready' if it affects readiness, and map it to the appropriate endpoint.

Full instructions (SKILL.md)

Source of truth, from codewithmukesh/dotnet-claude-kit.


name: logging description: > Observability overview and glue for .NET 10: how the pieces fit together, plus the cross-cutting parts owned here — ASP.NET health check endpoints (/health), correlation IDs, and log-level strategy. For deep Serilog setup load serilog; for traces and metrics load opentelemetry. Load this skill when setting up observability from scratch, wiring health check endpoints or correlation IDs, or when the user says "logging", "observability", "monitoring setup", "liveness", "readiness", or "ILogger".

Logging & Observability

Core Principles

  1. Structured logging with Serilog — Every log entry is a structured event with named properties, not a formatted string. This enables searching, filtering, and alerting. All setup (two-stage bootstrap, AddSerilog(), sinks, enrichers) lives in the serilog skill — that skill's AddSerilog()-over-UseSerilog() guidance is canonical.
  2. OpenTelemetry for distributed tracing — Traces connect requests across services; metrics track system health over time. Full setup lives in the opentelemetry skill.
  3. Health checks for operational readiness — Every service exposes /health endpoints for load balancers and orchestrators. Liveness and readiness are separate questions and separate endpoints.
  4. Correlation IDs for request tracing — Every request gets a unique ID that flows through all log entries and downstream service calls, so one user complaint maps to one filtered log stream.

Patterns

How the Pieces Fit Together

ConcernOwnerSkill
Structured application logsSerilog (AddSerilog())serilog
Request summary loggingUseSerilogRequestLogging()serilog
Traces + metrics + OTLP exportOpenTelemetry SDKopentelemetry
Health endpoints, correlation IDs, log-level strategyThis skilllogging

Wire logging first (you need logs to debug the rest), then health checks, then tracing.

Correlation IDs

// Middleware to set correlation ID
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);
        }
    }
}

// Program.cs — register early so every downstream log carries the ID
app.UseMiddleware<CorrelationIdMiddleware>();

Why middleware: pushing the property once at the pipeline edge attaches it to every log event in the request scope — no per-call-site plumbing. Propagate the same header on outgoing HttpClient calls via a DelegatingHandler (see the httpclient-factory skill).

Health Checks

// Program.cs
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"]);

// Map endpoints
app.MapHealthChecks("/health/live", new HealthCheckOptions
{
    Predicate = _ => false // No dependency checks — just "am I running?"
});

app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
    Predicate = check => check.Tags.Contains("ready")
});

Why two endpoints: liveness failing means "restart me"; readiness failing means "stop sending traffic". Conflating them makes a slow database restart your app in a loop.

Log-Level Strategy

LevelUse forEnvironment default
DebugDiagnostic detail, payload dumps (never PII in prod)Development only
InformationBusiness events: order placed, job completedDev + staging
WarningRecoverable anomalies: retry fired, fallback usedEverywhere — production default
ErrorFailed operations that need attentionEverywhere
Fatal/CriticalApp cannot continueEverywhere

Why Warning as the production default: Information-level request noise at scale costs real money in log storage and drowns the signals. Keep Information for genuine business events via namespace overrides (see the serilog skill's MinimumLevel.Override pattern).

Anti-patterns

Don't Log Sensitive Data

// BAD — logging credentials
logger.LogInformation("User logged in: {Email} with password {Password}", email, password);

// GOOD — log identifiers, never secrets or PII at Information level
logger.LogInformation("User {UserId} logged in", userId);

Don't Skip Health Check Tags

// BAD — all checks run for liveness AND readiness
app.MapHealthChecks("/health");

// GOOD — separate liveness (am I running?) from readiness (can I serve traffic?)
app.MapHealthChecks("/health/live", new() { Predicate = _ => false });
app.MapHealthChecks("/health/ready", new() { Predicate = c => c.Tags.Contains("ready") });

Don't Re-Implement What the Owning Skill Provides

// BAD — hand-rolling Serilog bootstrap here from memory
builder.Host.UseSerilog(...);  // legacy API — the serilog skill forbids this

// GOOD — load the serilog skill and use its two-stage AddSerilog() bootstrap
builder.Services.AddSerilog((services, lc) => lc.ReadFrom.Configuration(builder.Configuration)...);

Decision Guide

ScenarioRecommendation
Application logging setupLoad serilog — AddSerilog() two-stage bootstrap
Distributed tracing / metricsLoad opentelemetry — OTLP exporter
Custom business metricsIMeterFactory + counters/histograms (opentelemetry skill)
Request tracingCorrelation ID middleware (this skill)
Container health/health/live and /health/ready endpoints (this skill)
Log storageSeq (development), Elastic/Grafana/OTLP backend (production)
Log levelsDebug in dev, Information in staging, Warning default in production