Skip to content
Back to skills

Logging

ASecurity

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".

  • 229 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added June 6, 2026
devopsgosqlapidatabase

Works with

  • api

Security analysis

A100/100

Scanned June 6, 2026

npx -y skills add Resgrid/Core --skill logging --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Logging?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Logging
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/resgrid-logging/badge)](https://www.skillsdirectory.com/skills/resgrid-logging)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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

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.
2. **OpenTelemetry for distributed tracing** — Traces connect requests across services. Metrics track system health over time.
3. **Health checks for operational readiness** — Every service exposes `/health` endpoints for load balancers and orchestrators.
4. **Correlation IDs for request tracing** — Every request gets a unique ID that flows through all log entries and downstream service calls.

## Patterns

### Serilog Setup

```csharp
// Program.cs
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");
});

// After building the app
app.UseSerilogRequestLogging(options =>
{
    options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
    {
        diagnosticContext.Set("UserId",
            httpContext.User.FindFirstValue(ClaimTypes.NameIdentifier) ?? "anonymous");
    };
});
```

### Structured Logging (Correct Usage)

```csharp
// GOOD — structured logging with message template
logger.LogInformation("Processing order {OrderId} for customer {CustomerId}",
    orderId, customerId);

// GOOD — include relevant context
logger.LogWarning("Payment failed for order {OrderId}. Attempt {Attempt} of {MaxAttempts}",
    orderId, attempt, maxAttempts);

// GOOD — log exceptions with structured data
logger.LogError(exception, "Failed to process order {OrderId}", orderId);
```

### Correlation IDs

```csharp
// 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
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

```csharp
// 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")
});
```

## Anti-patterns

### Don't Use String Interpolation in Log Messages

```csharp
// BAD — allocates string even if level is disabled, breaks structured logging
logger.LogInformation($"Order {orderId} created for {customerId}");

// GOOD — message template with named parameters
logger.LogInformation("Order {OrderId} created for {CustomerId}", orderId, customerId);
```

### Don't Log Sensitive Data

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

// GOOD — never log secrets, passwords, tokens, or PII
logger.LogInformation("User logged in: {Email}", email);
```

### Don't Skip Health Check Tags

```csharp
// 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") });
```

## 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 |

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.