Lukdrasil.StepUpLogging 1.9.0

The owner has unlisted this package. This could mean that the package is deprecated, has security vulnerabilities or shouldn't be used anymore.
dotnet add package Lukdrasil.StepUpLogging --version 1.9.0
                    
NuGet\Install-Package Lukdrasil.StepUpLogging -Version 1.9.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Lukdrasil.StepUpLogging" Version="1.9.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Lukdrasil.StepUpLogging" Version="1.9.0" />
                    
Directory.Packages.props
<PackageReference Include="Lukdrasil.StepUpLogging" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Lukdrasil.StepUpLogging --version 1.9.0
                    
#r "nuget: Lukdrasil.StepUpLogging, 1.9.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Lukdrasil.StepUpLogging@1.9.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Lukdrasil.StepUpLogging&version=1.9.0
                    
Install as a Cake Addin
#tool nuget:?package=Lukdrasil.StepUpLogging&version=1.9.0
                    
Install as a Cake Tool

Lukdrasil.StepUpLogging

NuGet License: MIT

Dynamic step-up logging for ASP.NET Core with Serilog - automatically increase log verbosity when errors occur, with minimal performance overhead.

Features

Flexible step-up modes - Auto (production), AlwaysOn (dev), Disabled (minimal)
Automatic step-up on errors - Triggers detailed logging when Error level logs are detected
Pre-error buffering - In-memory per-request log buffering, flushed when errors occur
OpenTelemetry-first - Primary export via OTLP with optional console/file sinks
OpenTelemetry Activities - Built-in distributed tracing with 6+ instrumentation points (default-enabled)
Minimal overhead - 18-29% faster than standard Serilog in baseline tests
Request body capture - Optional capture during step-up with configurable size limits
Sensitive data redaction - Regex-based redaction for query strings and request bodies
OpenTelemetry metrics - Built-in metrics for monitoring step-up triggers and duration
Manual control - Expose endpoints to manually trigger or check step-up status
.NET 10.0 - Built with modern C# 14 features

Quick Start

Installation

dotnet add package Lukdrasil.StepUpLogging

Basic Setup

Option 1: Configuration from appsettings.json (recommended)

using Lukdrasil.StepUpLogging;

var builder = WebApplication.CreateBuilder(args);

// Automatically loads configuration from appsettings.json "SerilogStepUp" section
builder.AddStepUpLogging();

var app = builder.Build();
app.UseStepUpRequestLogging();
app.Run();

Option 2: Programmatic configuration

using Lukdrasil.StepUpLogging;

var builder = WebApplication.CreateBuilder(args);

builder.AddStepUpLogging(opts =>
{
    opts.Mode = StepUpMode.Auto;
    opts.BaseLevel = "Warning";
    opts.StepUpLevel = "Debug";
    opts.EnableConsoleLogging = builder.Environment.IsDevelopment();
});

var app = builder.Build();
app.UseStepUpRequestLogging();
app.Run();

Option 3: Mixed (appsettings.json + programmatic override)

// appsettings.json is loaded first, then overridden by code
builder.AddStepUpLogging(opts =>
{
    // Override only specific settings
    if (builder.Environment.IsProduction())
    {
        opts.Mode = StepUpMode.Auto;
        opts.EnableConsoleLogging = false;
    }
    else
    {
        opts.Mode = StepUpMode.AlwaysOn;
        opts.EnableConsoleLogging = true;
    }
});

Option 4: Aspire ServiceDefaults Integration

When using Aspire ServiceDefaults which already configures Serilog, use the UseStepUpLogging() extension method on LoggerConfiguration:

// In ServiceDefaults/Extensions.cs
public static TBuilder AddServiceDefaults<TBuilder>(this TBuilder builder) 
    where TBuilder : IHostApplicationBuilder
{
    builder.ConfigureOpenTelemetry();
    builder.AddDefaultHealthChecks();
    builder.Services.AddServiceDiscovery();
    
    // Configure Serilog with StepUp logging
    builder.Services.AddSerilog((services, lc) =>
    {
        lc.ReadFrom.Configuration(builder.Configuration)
          .UseStepUpLogging(builder, opts =>
          {
              opts.Mode = builder.Environment.IsDevelopment() 
                  ? StepUpMode.AlwaysOn 
                  : StepUpMode.Auto;
          });
    });
    
    return builder;
}

// In your API Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults(); // Includes StepUp logging

var app = builder.Build();
app.UseStepUpRequestLogging(); // Add request logging middleware
app.Run();

Configuration (appsettings.json)

{
  "SerilogStepUp": {
    "Mode": "Auto",
    "BaseLevel": "Warning",
    "StepUpLevel": "Information",
    "DurationSeconds": 180,

    "AlwaysLogRequestSummary": true,
    "RequestSummaryLevel": "Information",

    "EnableOtlpExporter": true,
    "EnableConsoleLogging": false,
    "CaptureRequestBody": true,
    "MaxBodyCaptureBytes": 16384,
    "ExcludePaths": ["/health", "/metrics"],
    "RedactionRegexes": [
      "password=[^&]*",
      "authorization:.*"
    ],

    "EnablePreErrorBuffering": true,
    "PreErrorBufferSize": 100,
    "PreErrorMaxContexts": 1024,

    "EnrichWithExceptionDetails": true,
    "EnrichWithThreadId": true,
    "EnrichWithProcessId": true,
    "EnrichWithMachineName": true
  }
}

Request Summary behaviour

When "AlwaysLogRequestSummary" is enabled, the middleware emits a single structured summary event at the configured "RequestSummaryLevel" for every completed HTTP request. The summary contains: HTTP method, request path, response status code, elapsed milliseconds and an optional trace/correlation id. Summary events are marked with the "IsRequestSummary" property and are processed by the library's SummarySink so they are exported independently of the StepUp level switch (base Warning) and the normal step-up flow.

To customise where summaries are written, provide a dedicated summary logger in DI when calling AddStepUpLogging, or configure the default sinks; the library enforces a single DI-managed summary logger to avoid unmanaged CreateLogger instances.

OpenTelemetry Configuration

StepUpLogging uses standard OpenTelemetry environment variables for OTLP configuration. OTLP endpoint and protocol are configured exclusively via environment variables and cannot be overridden programmatically:

Environment Variable Purpose Default
OTEL_EXPORTER_OTLP_ENDPOINT OTLP collector endpoint http://localhost:4317
OTEL_EXPORTER_OTLP_PROTOCOL Protocol: grpc or http/protobuf grpc
OTEL_EXPORTER_OTLP_HEADERS Headers (format: key1=value1,key2=value2) (none)
OTEL_RESOURCE_ATTRIBUTES Resource attributes (format: key1=value1,key2=value2) (none)

Example with environment variables:

# Docker or Kubernetes deployment
export OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4317
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer xyz,X-Custom=value"
export OTEL_RESOURCE_ATTRIBUTES="service.name=MyApi,deployment.environment=production"

dotnet MyApp.dll

For other OTLP options (like additional headers or resource attributes), use environment variables or configure via appsettings.json.

Step-Up Modes

Auto (default - Production)

  • Logs at BaseLevel (Warning) during normal operation
  • Automatically steps up to StepUpLevel (Information) when errors occur
  • Returns to BaseLevel after configured duration

AlwaysOn (Development)

  • Always logs at StepUpLevel (Information)
  • Useful for local development to see all detailed logs
  • Step-up triggers are ignored (already at max verbosity)

Disabled (Minimal Logging)

  • Always logs at BaseLevel (Warning)
  • Step-up mechanism is completely disabled
  • Error triggers are ignored
// Development configuration example
{
  "SerilogStepUp": {
    "Mode": "AlwaysOn",
    "StepUpLevel": "Debug",
    "EnableConsoleLogging": true
  }
}

Request Logging

The UseStepUpRequestLogging() middleware enriches each request with detailed context:

Captured Information

  • RequestPath - Normalized request path (trailing slashes removed)
  • QueryString - Query parameters (redacted based on RedactionRegexes)
  • RouteParameters - Route parameter values (e.g., {id}, {role})
  • Headers - HTTP request headers with automatic redaction of sensitive headers
  • RequestBody - POST/PUT/PATCH bodies (when CaptureRequestBody is enabled and logging is stepped-up)

Example Log Output

{
  "Timestamp": "2025-01-08T12:34:56.789Z",
  "Level": "Information",
  "MessageTemplate": "HTTP {Method} {Path} responded {StatusCode} in {Elapsed:0.00}ms",
  "RequestPath": "/api/users/123",
  "RouteParameters": {
    "id": "123"
  },
  "QueryString": "?filter=active&sort=name",
  "Headers": {
    "content-type": "application/json",
    "user-agent": "Mozilla/5.0",
    "authorization": "[REDACTED]",
    "cookie": "[REDACTED]"
  },
  "RequestBody": "{\"name\": \"John Doe\", \"password\": \"[REDACTED]\"}"
}

Sensitive Header Redaction

Built-in redacted headers:

  • Authorization
  • Cookie
  • X-API-Key
  • X-Auth-Token
  • X-Access-Token
  • Authorization-Token
  • Proxy-Authorization
  • WWW-Authenticate
  • Sec-WebSocket-Key

Add custom sensitive headers via configuration:

appsettings.json:

{
  "SerilogStepUp": {
    "AdditionalSensitiveHeaders": [
      "X-Custom-Secret",
      "X-Internal-Token",
      "X-Database-Password"
    ]
  }
}

Programmatically:

builder.AddStepUpLogging(opts =>
{
    opts.AdditionalSensitiveHeaders = new[]
    {
        "X-Custom-Secret",
        "X-Internal-Token"
    };
});

OpenTelemetry Activities & Instrumentation

StepUpLogging automatically instruments your requests with OpenTelemetry Activity objects for distributed tracing. This feature is enabled by default but can be disabled if needed.

Activity Instrumentation Points

The library creates activities at these key points:

Activity Type Description Triggered
LogRequest Server Request processing span Every HTTP request
TriggerStepUp Internal Step-up event triggered When error detected
PerformStepDown Internal Step-down event executed When duration expires
ApplyRedaction Internal Sensitive data redaction Per redaction pattern
CaptureRequestBody Internal Request body capture When step-up active
FlushBufferedEvents Internal Pre-error buffer flush When error occurs

Enable/Disable Activities

Enabled by default - Activities are created automatically when OTEL is registered:

{
  "SerilogStepUp": {
    "EnableActivityInstrumentation": true
  }
}

Disable if needed (opt-out):

builder.AddStepUpLogging(opts =>
{
    opts.EnableActivityInstrumentation = false;  // Disable activity creation
});

Zero Overhead When OTEL Not Registered

When OpenTelemetry is not registered in the service container:

  • Activities are created but not propagated
  • No performance overhead (activities are internal)
  • Enable/disable flag has no effect

Example with Aspire Observability

var builder = WebApplication.CreateBuilder(args);

// ConfigureOpenTelemetry() automatically registers traces
builder.AddServiceDefaults();

builder.AddStepUpLogging(opts =>
{
    opts.EnableActivityInstrumentation = true;  // Default
});

var app = builder.Build();
app.UseStepUpRequestLogging();
app.Run();

// Activities now visible in:
// - Grafana Tempo / Jaeger
// - Application Insights
// - Custom OTEL collectors

Manual Control

Add endpoints to manually trigger step-up or check status:

app.MapPost("/stepup/trigger", (StepUpLoggingController controller) =>
{
    controller.Trigger();
    return Results.Ok(new { message = "Step-up activated" });
});

app.MapGet("/stepup/status", (StepUpLoggingController controller) =>
{
    return Results.Ok(new { active = controller.IsSteppedUp });
});

Pre-Error Buffering

Pre-error buffering automatically captures recent log events in memory per request/activity and flushes them when an error occurs. This provides context around the error without increasing log verbosity in normal operation.

How It Works

  1. Buffering phase: All non-error logs are stored in a ring buffer per OpenTelemetry trace ID (one buffer per request)
  2. Flush trigger: When an Error or Fatal log is emitted, the buffer flushes all captured events from that request to the output
  3. Memory management: Uses LRU eviction to prevent unbounded memory growth (configurable limits on buffer size and active contexts)

Configuration

Enable via appsettings.json:

{
  "SerilogStepUp": {
    "EnablePreErrorBuffering": true,
    "PreErrorBufferSize": 100,
    "PreErrorMaxContexts": 1024
  }
}
Option Default Description
EnablePreErrorBuffering true Enable/disable pre-error buffering
PreErrorBufferSize 100 Max events to retain per request before oldest are dropped
PreErrorMaxContexts 1024 Max concurrent request contexts to track; older ones are evicted

Enable programmatically:

builder.AddStepUpLogging(opts =>
{
    opts.EnablePreErrorBuffering = true;
    opts.PreErrorBufferSize = 200;      // Capture more events per request
    opts.PreErrorMaxContexts = 512;     // Fewer concurrent requests in memory
});

Benefits

  • Diagnostics: See what happened before an error (request headers, SQL queries, business logic) without enabling debug logging for all requests
  • Production-safe: Buffering is per-request; no global state that could consume unbounded memory
  • Configurable: Tune buffer size and context limits based on your memory budget and traffic patterns
  • Automatic: No code changes needed; works transparently in the logging pipeline

Example Scenario

Without buffering:

[Warning] Request started: GET /api/users/123
[Error] User not found (id=123)

With buffering:

[Information] Request started: GET /api/users/123
[Debug] Querying user database: SELECT * FROM Users WHERE Id = @id
[Debug] Query parameters: @id = 123
[Debug] Database response: no rows
[Error] User not found (id=123)

All Debug/Information events are buffered internally; when the error occurs, they are flushed to provide diagnostic context.

Common Scenarios

Production with Auto Step-Up

{
  "SerilogStepUp": {
    "Mode": "Auto",
    "BaseLevel": "Warning",
    "StepUpLevel": "Information",
    "DurationSeconds": 300,
    "EnableOtlpExporter": true,
    "OtlpEndpoint": "http://otel-collector:4317",
    "OtlpResourceAttributes": {
      "service.name": "ProductionAPI",
      "deployment.environment": "production"
    }
  }
}

Local Development (Always Verbose)

{
  "SerilogStepUp": {
    "Mode": "AlwaysOn",
    "StepUpLevel": "Debug",
    "EnableConsoleLogging": true,
    "EnableOtlpExporter": false,
    "CaptureRequestBody": true
  }
}

Environment-Specific Configuration

builder.AddStepUpLogging(opts =>
{
    opts.Mode = builder.Environment.IsDevelopment() 
        ? StepUpMode.AlwaysOn 
        : StepUpMode.Auto;
    
    opts.StepUpLevel = builder.Environment.IsDevelopment() ? "Debug" : "Information";
    opts.EnableConsoleLogging = builder.Environment.IsDevelopment();
    
    // Production: use OTLP, Development: use console
    opts.EnableOtlpExporter = !builder.Environment.IsDevelopment();
    
    if (builder.Environment.IsProduction())
    {
        opts.OtlpEndpoint = Environment.GetEnvironmentVariable("OTEL_ENDPOINT") 
            ?? "http://otel-collector:4317";
        opts.OtlpHeaders["Authorization"] = "Bearer " + 
            Environment.GetEnvironmentVariable("OTEL_TOKEN");
    }
});

With Authentication Headers

# Use environment variables for OTLP authentication
export OTEL_EXPORTER_OTLP_ENDPOINT=http://secure-collector:4317
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer xyz,X-API-Key=secret"
export OTEL_RESOURCE_ATTRIBUTES="service.name=MyAPI,service.version=1.2.3,deployment.environment=production"

dotnet MyApp.dll

Multiple Resource Attributes

# Use environment variable for multiple resource attributes
export OTEL_RESOURCE_ATTRIBUTES="service.name=payment-service,service.version=2.1.0,service.namespace=ecommerce,deployment.environment=production,cloud.provider=azure,cloud.region=westeurope,k8s.cluster.name=prod-cluster,k8s.namespace.name=payment"

dotnet MyApp.dll

Performance

Benchmark results (k6 load test, 50 VUs, 3 minutes):

Metric Standard Serilog StepUpLogging Improvement
Avg Latency 1.19 ms 0.98 ms -18%
P95 Latency 2.12 ms 1.64 ms -23%
Throughput 165.77 req/s 166.21 req/s +0.3%

See full performance test results.

How It Works

  1. Normal operation: Logs at BaseLevel (e.g., Warning)
  2. Error detected: StepUpTriggerSink automatically triggers step-up
  3. Step-up active: Logs at StepUpLevel (e.g., Information) for configured duration
  4. Auto restore: Returns to BaseLevel after duration expires

Export Architecture

Primary: OpenTelemetry OTLP (Production-ready)

  • Logs exported to OTLP collector (default: localhost:4317)
  • Supports both gRPC and HTTP protocols
  • Structured logging with full trace context correlation
  • Resource attributes for service identification

Fallback: Console Logging (Development/Legacy)

  • Enable via EnableConsoleLogging: true in configuration
  • Useful for local development or direct log collection
  • Outputs CompactJSON format

Optional: File Sink (Archival/Compliance)

  • Daily rolling files with 30-day retention
  • Enable via logFilePath parameter in AddStepUpLogging()

Configuration Options

Option Default Environment Variable Description
Step-Up Behavior
Mode Auto - Step-up mode: Auto, AlwaysOn, Disabled
BaseLevel "Warning" - Normal log level
StepUpLevel "Information" - Elevated log level during step-up
DurationSeconds 180 - How long step-up remains active (Auto mode)
Pre-Error Buffering
EnablePreErrorBuffering true - Enable/disable pre-error buffering
PreErrorBufferSize 100 - Max events per request before oldest are dropped
PreErrorMaxContexts 1024 - Max concurrent request contexts; older ones are evicted
OpenTelemetry Instrumentation
EnableActivityInstrumentation true - Enable/disable Activity creation (default-enabled, opt-out)
OpenTelemetry
EnableOtlpExporter true - Export logs to OTLP endpoint
(Endpoint/Protocol) (env only) OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_PROTOCOL OTLP configuration (environment variables only)
Additional Sinks
EnableConsoleLogging false - Enable console output (dev scenarios)
Enrichers
EnrichWithExceptionDetails true - Enrich logs with structured exception details
EnrichWithThreadId false - Include thread ID in log events
EnrichWithProcessId false - Include process ID in log events
EnrichWithMachineName true - Include machine name in log events
EnrichWithEnvironment true - Include environment name (Development/Production)
Request Logging
CaptureRequestBody false - Capture POST/PUT/PATCH bodies during step-up
MaxBodyCaptureBytes 16384 - Max bytes to capture from request body
ExcludePaths ["/health", "/metrics"] - Paths to exclude from logging
RedactionRegexes [] - Regex patterns for redacting sensitive data
AdditionalSensitiveHeaders [] - Custom header names to redact in request logging
Service Identification
ServiceVersion null APP_VERSION Service version for enrichment

OpenTelemetry Activities & Metrics

Activities

When EnableActivityInstrumentation is enabled (default), these activities are created:

  • Request-level: LogRequest (server-side span) tracking entire HTTP request
  • Operation-level: TriggerStepUp, PerformStepDown, FlushBufferedEvents (internal operations)
  • Sub-operation: ApplyRedaction, CaptureRequestBody (child spans of LogRequest)

Activities include W3C trace context tags and semantic conventions:

  • http.scheme - Protocol (http/https)
  • http.host - Host header value
  • security.redaction_applied - Whether redaction was performed

Metrics

Exposed metrics for monitoring:

  • stepup_trigger_total - Total number of step-up triggers
  • stepup_active - Whether step-up is currently active (0 or 1)
  • stepup_duration_seconds - Duration histogram of step-up windows
  • request_body_captured_total - Number of requests with captured body
  • request_redaction_applied_total - Number of requests with redaction applied
  • buffer_events_total - Total events buffered by pre-error buffer
  • buffer_flushed_events_total - Events flushed due to error
  • buffer_flush_total - Number of buffer flush operations
  • buffer_evicted_contexts_total - Contexts evicted due to LRU pressure

License

MIT © Lukdrasil

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated

Added AlwaysLogRequestSummary option and SummarySink to emit per-request structured summaries independently of the step-up level; added EmitRequestSummary API and tests.