SolTechnology.Core.Logging 1.1.1

dotnet add package SolTechnology.Core.Logging --version 1.1.1
                    
NuGet\Install-Package SolTechnology.Core.Logging -Version 1.1.1
                    
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="SolTechnology.Core.Logging" Version="1.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SolTechnology.Core.Logging" Version="1.1.1" />
                    
Directory.Packages.props
<PackageReference Include="SolTechnology.Core.Logging" />
                    
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 SolTechnology.Core.Logging --version 1.1.1
                    
#r "nuget: SolTechnology.Core.Logging, 1.1.1"
                    
#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 SolTechnology.Core.Logging@1.1.1
                    
#: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=SolTechnology.Core.Logging&version=1.1.1
                    
Install as a Cake Addin
#tool nuget:?package=SolTechnology.Core.Logging&version=1.1.1
                    
Install as a Cake Tool

SolTechnology.Core.Logging

Production-ready logging primitives for ASP.NET Core: W3C-compliant correlation, request envelope logs with status-aware levels, declarative log-scope enrichment, allocation-free operation lifecycle events, and OpenTelemetry-friendly ActivitySources — all on top of Microsoft.Extensions.Logging. No Serilog or Application Insights dependency required; works seamlessly with both.

NuGet: SolTechnology.Core.Logging · target: net10.0 / Microsoft.AspNetCore.App · zero ambient state, no AsyncLocal traps.


What you get

Feature API
Correlation id (W3C traceparent + X-Correlation-Id) ICorrelationIdService, CorrelationId
Request envelope logs (start / finish, status-aware levels) LoggingMiddleware (auto-wired by UseCoreLogging)
Declarative scope enrichment from header / url / body services.LogDetail(...)
Custom enrichers services.AddLogScopeEnricher<T>(), ILogScopeEnricher
Operation lifecycle events (allocation-free) ILogger.OperationStarted/Succeeded/Failed
OpenTelemetry tracing CoreLoggingActivitySources.OperationsName

Getting started

1. Install

dotnet add package SolTechnology.Core.Logging

2. Register

// Program.cs
builder.Services.AddCoreLogging();          // or AddCoreLogging(opts => { ... })
                                            // or AddCoreLogging(builder.Configuration)

AddCoreLogging is idempotent and registers the ICorrelationIdService and LoggingOptions (validated on application start).

3. Wire the middleware

var app = builder.Build();

app.UseCoreLogging();   // EARLY in the pipeline, before UseRouting / UseEndpoints
app.UseRouting();
app.UseAuthorization();
app.MapControllers();

Place UseCoreLogging early so every request — including ones that fail authentication — gets a correlation id and a request-envelope log entry.

4. (Optional) Configure via appsettings.json

{
  "Logging:Core": {
    "MaxLoggedJsonBodyBytes": 65536,
    "LogClientCorrelationParseErrors": true,
    "SkipPaths": [ "/health", "/alive", "/metrics", "/swagger" ]
  }
}
builder.Services.AddCoreLogging(builder.Configuration);

SkipPaths silences request-envelope logs for liveness / readiness / metrics scrapes — correlation is still propagated, only the noise is gone.


Correlation IDs

CorrelationId aligns with the W3C Trace Context standard and integrates with System.Diagnostics.Activity, so OpenTelemetry, Application Insights and ASP.NET Core 6+ all see the same id.

Inbound resolution order:

  1. The current Activity.TraceId (populated by ASP.NET Core from the traceparent header).
  2. A custom override via X-Correlation-Id (only when no Activity is in scope).
  3. A freshly generated 32-character hex string.

Outbound: both traceparent and X-Correlation-Id are echoed on the response so support can quote a single short id without parsing W3C trace context.

Use it from anywhere — background jobs, message-bus handlers, outbound HttpClient handlers — via DI:

public sealed class AuditWriter(ICorrelationIdService correlation, ILogger<AuditWriter> logger)
{
    public Task WriteAsync(string action, CancellationToken ct)
    {
        var id = correlation.GetOrGenerate();   // creates one for non-HTTP entry points
        logger.LogInformation("Audit [{Action}] correlation [{Correlation}]", action, id);
        return Task.CompletedTask;
    }
}

Per-request scope enrichment

Declarative — LogDetail

Promote a property from header, query/route, or JSON body into the per-request log scope without writing any code:

builder.Services.AddCoreLogging();

// Header → scope["TenantId"]
builder.Services.LogDetail("X-Tenant-Id", asName: "TenantId", source: LogDetailSource.Header);

// Query / route key → scope["UserId"]
builder.Services.LogDetail("userId", source: LogDetailSource.Url);

// JSON body field "name" → scope["CityName"], only on these endpoints
builder.Services.LogDetail(
    "name",
    asName: "CityName",
    source: LogDetailSource.Body,
    endpoints: ["/api/v1/FindLocationOfCity", "/api/FindCityByName"]);

Body parsing is opt-in (driven by LogDetailSource.Body registrations), bounded by LoggingOptions.MaxLoggedJsonBodyBytes (default 64 KB), only runs on application/json, restores the request stream so MVC model binding still works, and silently no-ops on malformed JSON.

Programmatic — ILogScopeEnricher

When LogDetail is not expressive enough (composing values from claims, multiple sources, async lookups, header masking), implement ILogScopeEnricher:

public sealed class UserScopeEnricher : ILogScopeEnricher
{
    public void Enrich(HttpContext context, IDictionary<string, object?> scope)
    {
        if (context.User?.Identity?.IsAuthenticated == true)
        {
            scope["UserId"] = context.User.FindFirst("sub")?.Value;
        }
    }
}

builder.Services.AddLogScopeEnricher<UserScopeEnricher>();

The middleware catches and warns on enricher failures so a faulty enricher cannot take a request down.


Operation lifecycle events

Three canonical events with stable EventIds for dashboard queries:

EventId Method Level Template
2137 OperationStarted Information Operation: [{OperationName}]. Status: [START]
2138 OperationSucceeded Information Operation: [{OperationName}]. Status: [SUCCESS]. Duration: [{DurationMs} ms]
2139 OperationFailed Error Operation: [{OperationName}]. Status: [FAIL]. Duration: [{DurationMs} ms]. Message: [{Message}]
2140 (user message) Information [{Message}]

Backed by [LoggerMessage] source generators — allocation-free, short-circuit when the level is disabled.

var sw = ValueStopwatch.StartNew();
logger.OperationStarted(nameof(ImportInvoices), message: "batch=123");
try
{
    await ImportAsync(ct);
    logger.OperationSucceeded(nameof(ImportInvoices), sw.ElapsedMilliseconds);
}
catch (Exception ex)
{
    logger.OperationFailed(nameof(ImportInvoices), sw.ElapsedMilliseconds, ex);
    throw;
}

Inside a CQRS pipeline these are emitted automatically — see SolTechnology.Core.CQRS and the [LogScope] attribute.


OpenTelemetry

builder.Services.AddOpenTelemetry()
    .WithTracing(t => t
        .AddSource(CoreLoggingActivitySources.OperationsName)   // MediatR operations
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddOtlpExporter());

When no listener is attached, ActivitySource.StartActivity returns null and per-request overhead collapses to one null-conditional access — apps that don't opt in pay nothing.


{
  "Logging": {
    "LogLevel": {
      "Default": "Warning",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information",
      "SolTechnology.Core.Logging.Middleware.LoggingMiddleware": "Information",
      "SolTechnology.Core.CQRS.PipelineBehaviors.LoggingPipelineBehavior": "Information"
    },
    "Console": {
      "FormatterName": "json",
      "FormatterOptions": {
        "IncludeScopes": true,
        "TimestampFormat": "yyyy-MM-ddTHH:mm:ss.fffZ",
        "UseUtcTimestamp": true
      }
    }
  }
}

IncludeScopes: true is required for any sink that should see correlation / enrichment properties (Console JSON formatter, App Insights provider, Loki, Datadog, etc.).


Result in App Insights / structured sink

Timestamp Message CorrelationId CityName
2026-05-06T08:00:10.738Z Started request [POST] [/api/v1/FindLocationOfCity] 8fa1... Warsaw
2026-05-06T08:00:10.745Z Operation: [FindLocationOfCity]. Status: [START] 8fa1... Warsaw
2026-05-06T08:00:12.859Z Operation: [FindLocationOfCity]. Status: [SUCCESS]. Duration: [2114 ms] 8fa1... Warsaw
2026-05-06T08:00:12.860Z Finished request [POST] [/api/v1/FindLocationOfCity] → [200] in [2122 ms] 8fa1... Warsaw
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.
  • net10.0

    • No dependencies.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on SolTechnology.Core.Logging:

Package Downloads
SolTechnology.Core.CQRS

Complete CQRS implementation built on MediatR with Result pattern, automatic FluentValidation, and logging pipeline behaviors. Enforces clean separation between reads and writes with explicit success/failure handling.

SolTechnology.Core.Api

ASP.NET Core API utilities: RFC 7807 ProblemDetails error pipeline, header-based API versioning, MVC filters that auto-convert Result<T> to wire format. One-call AddApiCore + AddApiCoreFilters + UseSwaggerWithVersioning bootstrap. CorrelationId propagation through Core.Logging.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.1 76 5/6/2026
0.5.0 537 12/10/2025
0.2.2 435 7/23/2024
0.2.1 310 10/6/2023
0.2.0 645 3/29/2022
0.1.0 595 3/4/2022