Skip to content

Logging and observability

CaeriusNet uses source-generated [LoggerMessage] logging for zero-allocation structured output. Every database operation emits timing, procedure name, and result metadata — enabling diagnostics, performance monitoring, and alerting without custom instrumentation.

For OpenTelemetry tracing and metrics, see Aspire Integration — Tracing & Telemetry.

Overview

FeatureImplementation
Zero-allocation[LoggerMessage] source generators — no string interpolation at runtime
Structured parametersNamed placeholders ({ProcedureName}, {Duration}, {RowCount})
Execution timingStopwatch.GetElapsedTime for high-resolution measurement
Event-ID conventionCategorized by subsystem (see table below)
Provider-agnosticWorks with any ILogger implementation

Configuration

Setting the logger

CaeriusNet uses a static LoggerProvider to obtain its logger instance. Configure it once during application startup:

csharp
using CaeriusNet.Logging;

var builder = WebApplication.CreateBuilder(args);

// ... DI registration ...

var app = builder.Build();
LoggerProvider.SetLogger(app.Services.GetRequiredService<ILoggerFactory>());

Integration with DI

When using CaeriusNetBuilder, the logger is wired automatically as long as ILoggerFactory is registered in the DI container — LoggerProvider.SetLogger is called for you during Build().

csharp
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddLogging(logging =>
{
    logging.AddConsole();
    logging.SetMinimumLevel(LogLevel.Information);
});

CaeriusNetBuilder
    .Create(builder.Services)
    .WithSqlServer(connectionString)
    .Build();

Filtering by category

Use ILoggerFactory configuration to filter CaeriusNet logs by subsystem:

csharp
builder.Services.AddLogging(logging =>
{
    logging.AddFilter("CaeriusNet",          LogLevel.Information);
    logging.AddFilter("CaeriusNet.Commands", LogLevel.Debug);    // verbose for command execution
    logging.AddFilter("CaeriusNet.Cache",    LogLevel.Warning);  // suppress per-call cache noise
});

Event-ID categories

CaeriusNet organizes event IDs by subsystem. Use these ranges to filter, route, or alert on specific categories:

RangeCategoryDescription
1000–1999In-Memory cacheHit, miss, set, eviction
2000–2999Frozen cacheHit, miss, freeze operations
3000–3999Redis cacheGet, set, connection events
4000–4999Database / connectionConnection open, close, pool events
5000–5999Command executionStart, complete, duration, row count

Event reference

Event IDLevelMessage template
1001DebugIn-memory cache hit for key '{CacheKey}'
1002DebugIn-memory cache miss for key '{CacheKey}'
1003InformationIn-memory cache set for key '{CacheKey}' with expiration {Expiration}
2001DebugFrozen cache hit for key '{CacheKey}'
2002DebugFrozen cache miss for key '{CacheKey}'
2003InformationFrozen cache set for key '{CacheKey}'
3001DebugRedis cache hit for key '{CacheKey}'
3002DebugRedis cache miss for key '{CacheKey}'
3003InformationRedis cache set for key '{CacheKey}' with expiration {Expiration}
3004WarningRedis connection failed: {ErrorMessage}
4001DebugOpening SQL connection
4002DebugSQL connection opened in {Duration}
4003DebugSQL connection closed
5001DebugExecuting stored procedure '{ProcedureName}'
5002InformationStored procedure '{ProcedureName}' completed in {Duration} ({RowCount} rows)
5003WarningStored procedure '{ProcedureName}' exceeded threshold: {Duration}
5004ErrorStored procedure '{ProcedureName}' failed: {ErrorMessage}

Structured parameters

CaeriusNet log messages use semantic (structured) placeholders. Structured logging sinks preserve them as queryable key/value pairs:

PlaceholderTypeDescription
{ProcedureName}stringFully qualified stored procedure name (schema.name)
{Duration}TimeSpanElapsed execution time
{RowCount}intNumber of rows returned or affected
{CacheKey}stringCache key used for lookup or store
{Expiration}TimeSpanCache entry TTL
{ErrorMessage}stringException message on failure
{IsolationLevel}stringTransaction isolation level

Why this matters

Structured parameters unlock powerful queries — "all executions of sp_GetUsers slower than 500 ms", "cache misses per key in the last hour", "failure rate by procedure name" — without parsing log text.

Integration examples

Serilog

csharp
using Serilog;

var builder = WebApplication.CreateBuilder(args);

builder.Host.UseSerilog((context, config) =>
{
    config
        .ReadFrom.Configuration(context.Configuration)
        .WriteTo.Console(outputTemplate:
            "[{Timestamp:HH:mm:ss} {Level:u3}] [{EventId}] {Message:lj}{NewLine}{Exception}")
        .WriteTo.Seq("http://localhost:5341");
});

CaeriusNetBuilder
    .Create(builder.Services)
    .WithSqlServer(connectionString)
    .Build();

Filter CaeriusNet events in appsettings.json:

json
{
  "Serilog": {
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "CaeriusNet.Cache":    "Warning",
        "CaeriusNet.Commands": "Debug"
      }
    }
  }
}

OpenTelemetry

Export CaeriusNet logs to an OTLP-compatible backend:

csharp
builder.Services.AddOpenTelemetry()
    .WithLogging(logging => logging.AddOtlpExporter());

builder.Services.AddLogging(logging =>
{
    logging.AddOpenTelemetry(options =>
    {
        options.IncludeFormattedMessage = true;
        options.IncludeScopes = true;
    });
});

Application Insights

csharp
builder.Services.AddApplicationInsightsTelemetry();
builder.Services.AddLogging(logging =>
{
    logging.AddApplicationInsights();
    logging.AddFilter<ApplicationInsightsLoggerProvider>(
        "CaeriusNet", LogLevel.Information);
});

Performance considerations

AspectGuidance
Log level filteringSet CaeriusNet.Cache to Warning in production to suppress per-call cache noise
Hot pathsDebug-level callsites are compiled out when the level is disabled (the source-generator emits an IsEnabled check)
Structured sinksPrefer Seq, Elasticsearch, or OTLP over flat-file sinks for queryability
SamplingFor very high RPS, configure sampling in your telemetry pipeline

Avoid excessive logging in hot paths

Debug-level cache events fire on every call. In production, ensure your minimum level is Information (or higher) for CaeriusNet.Cache to avoid log volume drowning your sinks.

Troubleshooting

SymptomCauseFix
No CaeriusNet logs appearLogger not configuredEnsure ILoggerFactory is registered before CaeriusNetBuilder.Build()
Missing structured parametersFlat-text sink in useSwitch to a structured sink (Seq, OTLP, JSON console)
High log volumeDebug level enabled in productionRaise the minimum level for CaeriusNet.Cache to Warning
Missing timing dataEvent ID 5002 filtered outRe-enable CaeriusNet.Commands at Information

Next: Aspire Integration — connect CaeriusNet to the Aspire dashboard via OpenTelemetry.

Released under the MIT License.