CSharpEssentials.LoggerHelper
5.2.2.5
dotnet add package CSharpEssentials.LoggerHelper --version 5.2.2.5
NuGet\Install-Package CSharpEssentials.LoggerHelper -Version 5.2.2.5
<PackageReference Include="CSharpEssentials.LoggerHelper" Version="5.2.2.5" />
<PackageVersion Include="CSharpEssentials.LoggerHelper" Version="5.2.2.5" />
<PackageReference Include="CSharpEssentials.LoggerHelper" />
paket add CSharpEssentials.LoggerHelper --version 5.2.2.5
#r "nuget: CSharpEssentials.LoggerHelper, 5.2.2.5"
#:package CSharpEssentials.LoggerHelper@5.2.2.5
#addin nuget:?package=CSharpEssentials.LoggerHelper&version=5.2.2.5
#tool nuget:?package=CSharpEssentials.LoggerHelper&version=5.2.2.5
CSharpEssentials — LoggerHelper
Stop writing Serilog boilerplate. Route any log level to any sink — in one JSON file.
LoggerHelper is a modular logging infrastructure for .NET. Install the core + only the sink packages you need, drop in a JSON config, and your entire app's ILogger<T> routes to Console, File, Email, Telegram, SQL Server, PostgreSQL, Elasticsearch, Seq, and Hangfire Console — each receiving only the log levels you configure.
Zero code changes required if you already use ILogger<T>. LoggerHelper registers as a standard ILoggerProvider.
dotnet add package CSharpEssentials.LoggerHelper
dotnet add package CSharpEssentials.LoggerHelper.Sink.Console
dotnet add package CSharpEssentials.LoggerHelper.Sink.File
Table of Contents
- The Boilerplate Problem
- Packages
- Quick Start
- Run the Demo in 60 Seconds
- Feature Highlights
- AI Integration — MCP Server
- Sink Overview & JSON Examples
- Comparison
- Architecture
- Coming Soon
- Documentation & Links
🔥 The Boilerplate Problem
Setting up Serilog with multiple sinks, per-level routing, and enrichment means repeating the same infrastructure code in every project. Here's what it typically looks like:
❌ Before — Vanilla Serilog (30+ lines, repeated per project)
// Program.cs — Vanilla Serilog setup
Log.Logger = new LoggerConfiguration()
.Enrich.WithProperty("ApplicationName", "MyApp")
.Enrich.WithProperty("MachineName", Environment.MachineName)
.Enrich.FromLogContext()
.WriteTo.Conditional(
e => e.Level is LogEventLevel.Information or LogEventLevel.Warning,
cfg => cfg.Console())
.WriteTo.Conditional(
e => e.Level >= LogEventLevel.Information,
cfg => cfg.File("Logs/log-.txt", rollingInterval: RollingInterval.Day,
retainedFileCountLimit: 7, shared: true,
formatter: new JsonFormatter()))
.WriteTo.Conditional(
e => e.Level >= LogEventLevel.Error,
cfg => cfg.Email(new EmailConnectionInfo {
FromEmail = "alerts@myapp.com",
ToEmail = "team@myapp.com",
MailServer = "smtp.myapp.com",
Port = 587,
EnableSsl = true,
EmailSubject = "[MyApp] Error"
}))
.CreateLogger();
builder.Host.UseSerilog();
// Repeat for every project. Adjust. Break on typos. Re-test from scratch.
✅ After — LoggerHelper (5 lines of C# + declarative JSON)
// Program.cs — that's it
builder.Services.AddLoggerHelper(builder.Configuration);
app.UseLoggerHelper();
// appsettings.LoggerHelper.json
{
"LoggerHelper": {
"ApplicationName": "MyApp",
"Routes": [
{ "Sink": "Console", "Levels": ["Information", "Warning"] },
{ "Sink": "File", "Levels": ["Information", "Warning", "Error", "Fatal"] },
{ "Sink": "Email", "Levels": ["Error", "Fatal"] }
],
"Sinks": {
"File": { "Path": "Logs", "RollingInterval": "Day" },
"Email": { "From": "alerts@myapp.com", "To": "team@myapp.com",
"Host": "smtp.myapp.com", "Port": 587 }
}
}
}
Every ILogger<T> in your app now routes through LoggerHelper. No other changes needed.
📦 Packages
| Package | Description | Version |
|---|---|---|
CSharpEssentials.LoggerHelper |
Core routing engine, ILogger<T> bridge, JSON/fluent config |
|
...Sink.Console |
Colored console output, per-level themes — guide → | |
...Sink.File |
Rolling JSON files, per-property subdirectories, configurable retention — guide → | |
...Sink.Email |
SMTP alerts, HTML templates, throttling — guide → | |
...Sink.Telegram |
Bot notifications, MarkdownV2, throttling — guide → | |
...Sink.Elasticsearch |
Elasticsearch/OpenSearch indexing, Kibana-ready — guide → | |
...Sink.MSSqlServer |
SQL Server structured logs, auto table creation — guide → | |
...Sink.Postgresql |
PostgreSQL, JSONB columns, custom schema — guide → | |
...Sink.Seq |
Seq centralized log server — guide → | |
...Sink.HangfireConsole |
Structured logs in Hangfire Dashboard with color output — guide → | |
...Sink.Dashboard |
Dashboard LoggerHelper — guide → | |
CSharpEssentials.LoggerHelper.MCP |
MCP server: AI assistants can query sink health, errors & config | |
CSharpEssentials.HttpHelper |
HttpClient + Polly resilience, rate limiting, auto logging |
🚀 Quick Start
Option A — JSON config (recommended)
1. Install packages
dotnet add package CSharpEssentials.LoggerHelper
dotnet add package CSharpEssentials.LoggerHelper.Sink.Console
dotnet add package CSharpEssentials.LoggerHelper.Sink.File
2. Wire up in Program.cs
builder.Services.AddLoggerHelper(builder.Configuration);
app.UseLoggerHelper();
3. Create appsettings.LoggerHelper.json in your project root
{
"LoggerHelper": {
"ApplicationName": "MyApp",
"Routes": [
{ "Sink": "Console", "Levels": ["Debug", "Information", "Warning"] },
{ "Sink": "File", "Levels": ["Information", "Warning", "Error", "Fatal"] }
],
"Sinks": {
"File": { "Path": "Logs", "RollingInterval": "Day", "RetainedFileCountLimit": 7 }
},
"General": { "EnableRequestResponseLogging": true }
}
}
Done. Every ILogger<T> in your app now routes through LoggerHelper.
Option B — Fluent API
builder.Services.AddLoggerHelper(b => b
.WithApplicationName("MyApp")
.AddRoute("Console", LogEventLevel.Information, LogEventLevel.Warning)
.AddRoute("File", LogEventLevel.Information, LogEventLevel.Warning, LogEventLevel.Error, LogEventLevel.Fatal)
.AddRoute("Email", LogEventLevel.Error, LogEventLevel.Fatal)
.ConfigureFile(f => { f.Path = "Logs"; f.RollingInterval = "Day"; })
.ConfigureEmail(e => { e.To = "ops@example.com"; e.Host = "smtp.example.com"; })
.EnableRequestResponseLogging()
);
Option C — JSON + Fluent merge
// JSON defines shared config across environments.
// Fluent adds development-only extras without touching JSON.
builder.Services.AddLoggerHelper(builder.Configuration, b => b
.AddRoute("Console", LogEventLevel.Debug)
);
⚡ Run the Demo in 60 Seconds
Clone the repo and start the interactive demo app — no database required, runs on Console + File only:
git clone https://github.com/alexbypa/CSharp.Essentials.git
cd CSharp.Essentials/src/CSharpEssentials.LoggerHelper.Demo
dotnet run
Open http://localhost:5000/swagger — the Swagger UI lists all available demo scenarios. Each endpoint produces structured logs visible immediately in the terminal and in the Logs/ folder.
In Development mode (
dotnet runalways uses Development), the project automatically loadsappsettings.LoggerHelper.debug.json, which configures only Console + File — no SQL Server or PostgreSQL required.
To activate all four sinks (Console, File, MSSqlServer, PostgreSQL) setASPNETCORE_ENVIRONMENT=Production.
✨ Feature Highlights
Per-Level Sink Routing — Declarative
Send different log levels to different destinations without writing conditional predicates:
"Routes": [
{ "Sink": "Console", "Levels": ["Debug", "Information", "Warning"] },
{ "Sink": "File", "Levels": ["Information", "Warning", "Error", "Fatal"] },
{ "Sink": "Telegram", "Levels": ["Error", "Fatal"] },
{ "Sink": "Email", "Levels": ["Fatal"] },
{ "Sink": "Elasticsearch", "Levels": ["Information", "Warning", "Error", "Fatal"] }
]
Native ILogger<T> — Zero Code Changes
If your app already uses ILogger<T>, you change nothing. LoggerHelper registers as a standard ILoggerProvider:
public class OrderService(ILogger<OrderService> logger) {
public void Process(int orderId) {
logger.LogInformation("Processing order {OrderId}", orderId); // → Console + File
logger.LogError("Payment failed for {OrderId}", orderId); // → File + Email
}
}
Named parameters like {OrderId} are preserved as structured Serilog properties — not flattened into strings.
BeginScope — Context That Travels
using (_logger.BeginScope(new Dictionary<string, object?> {
["OrderId"] = orderId,
["UserId"] = userId
}))
{
_logger.LogInformation("Validation started"); // OrderId + UserId attached
await ValidateStock(); // inner logs also carry them
_logger.LogInformation("Order confirmed"); // OrderId + UserId attached
}
Automatic Enrichment
Every log event automatically carries:
| Property | Source |
|---|---|
ApplicationName |
Config / WithApplicationName() |
MachineName |
Environment.MachineName |
SourceContext |
Class name from ILogger<T> |
TraceId / SpanId |
System.Diagnostics.Activity (OpenTelemetry) |
Internal Diagnostics
If a sink fails (wrong connection string, unreachable SMTP), your app keeps running. Errors are captured silently and inspectable at runtime:
app.MapGet("/health/logging", (ILogErrorStore errors) =>
errors.Count == 0
? Results.Ok("All sinks healthy")
: Results.Problem(string.Join("\n", errors.GetAll().Select(e => $"{e.SinkName}: {e.ErrorMessage}")))
);
Request/Response Logging Middleware
"General": { "EnableRequestResponseLogging": true }
app.UseLoggerHelper();
One setting, one line — full HTTP request/response logging with correlation IDs and timing.
Dynamic File Routing (Multi-Tenant)
Route logs to subdirectories based on any log property:
"Sinks": {
"File": { "Path": "Logs", "RollingInterval": "Day", "FileNameProperty": "TenantId" }
}
Logs with TenantId = "acme" → Logs/acme/log-20250101.txt.
Logs without the property → Logs/log-20250101.txt.
Sensitive Data Masking — One JSON Block Protects Every Sink
Stop writing Regex.Replace calls before every logger.LogInformation. Declare what's sensitive
once, and LoggerHelper redacts it everywhere — Console, File, SQL, Elasticsearch, Seq, Telegram —
before any sink ever sees it:
"SensitiveDataMasking": {
"Enabled": true,
"MaskText": "***MASKED***",
"Presets": [ "Email", "CreditCard", "JwtToken", "BearerToken", "ConnectionStringSecret" ],
"SensitiveProperties": [ "Password", "ApiKey" ],
"Rules": [
{ "Name": "OrderId", "Pattern": "ORD-\\d+" }
]
}
logger.LogInformation("Login for {Email} with {Password}", "alice@example.com", "Sup3rSecret!");
// → every sink receives: Login for ***MASKED*** with ***MASKED***
- Built-in presets for the secrets that leak most often: emails, credit card numbers, JWTs,
Bearer ...tokens, andPassword=.../Pwd=...in connection strings. SensitivePropertiesredacts named structured fields outright (e.g.Password,ApiKey), regardless of content.- Custom regex rules with an optional
secretcapture group mask only part of a match —Bearer ***MASKED***keeps the scheme visible while hiding the token. - Zero overhead when disabled (the default) — the enricher isn't added to the pipeline at all.
Serilog has no first-class equivalent: redaction usually means a hand-rolled IDestructuringPolicy
or a third-party enricher wired up per project. Here it's one JSON block, applied globally.
🤖 AI Integration — MCP Server
dotnet add package CSharpEssentials.LoggerHelper.MCP
Give your AI assistant live visibility into your running app's logging state. Two lines of setup expose a Model Context Protocol (MCP) server that Claude, Cursor, GitHub Copilot, and any MCP-compatible client can query.
// Program.cs — add after AddLoggerHelper()
builder.Services.AddLoggerHelperMcp();
// ...
app.MapLoggerHelperMcp("/mcp"); // Streamable HTTP — Claude Code, Cursor, Copilot
app.MapLoggerHelperMcpSse(); // HTTP+SSE — Claude Desktop, MCP Inspector
AI can now ask your app:
- "Are all sinks healthy?" →
loggerhelper_get_health(OK / WARNING / CRITICAL) - "Show me the last 10 logging errors" →
loggerhelper_get_errors - "What levels does the Email sink receive?" →
loggerhelper_get_sinks - "Is PII masking enabled?" →
loggerhelper_get_config
Predefined prompt — run a full diagnostic with one command:
curl -X POST http://localhost:5000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"prompts/get","params":{"name":"diagnose-logging","arguments":{"focus":"all"}}}'
The diagnose-logging prompt instructs the AI to call all four tools and return a structured report
with Overall Status, Failed Sinks, Configuration Issues, and Recommended Actions.
Why this matters: Every other .NET logging library requires a separate dashboard (Seq, Kibana, Grafana) before an AI assistant can see log state. LoggerHelper MCP ships that built in — zero extra infrastructure, zero extra dependencies, one NuGet package.
📋 Sink Overview
Each sink is a separate NuGet package — install only what you need. Click guide → for the full configuration reference, sample output, and troubleshooting for each sink.
| Sink | What it does | Install | Full guide |
|---|---|---|---|
| Console | Colored terminal output, per-level themes | dotnet add package CSharpEssentials.LoggerHelper.Sink.Console |
guide → |
| File | Rolling JSON files, per-property subdirectories, configurable retention | dotnet add package CSharpEssentials.LoggerHelper.Sink.File |
guide → |
| SMTP alerts, HTML templates, throttling | dotnet add package CSharpEssentials.LoggerHelper.Sink.Email |
guide → | |
| Telegram | Bot notifications, MarkdownV2, throttling | dotnet add package CSharpEssentials.LoggerHelper.Sink.Telegram |
guide → |
| Elasticsearch | Elasticsearch / OpenSearch indexing, auto template, Kibana-ready | dotnet add package CSharpEssentials.LoggerHelper.Sink.Elasticsearch |
guide → |
| SQL Server | Structured log table, auto creation, custom columns | dotnet add package CSharpEssentials.LoggerHelper.Sink.MSSqlServer |
guide → |
| PostgreSQL | JSONB properties column, custom schema | dotnet add package CSharpEssentials.LoggerHelper.Sink.Postgresql |
guide → |
| Seq | Centralized log server with search, alerts, and dashboards | dotnet add package CSharpEssentials.LoggerHelper.Sink.Seq |
guide → |
| Hangfire Console | Structured logs inside the Hangfire Dashboard | dotnet add package CSharpEssentials.LoggerHelper.Sink.HangfireConsole |
guide → |
Every sink uses the same routing pattern — just add the sink name to Routes:
"Routes": [
{ "Sink": "Console", "Levels": ["Debug", "Information", "Warning"] },
{ "Sink": "File", "Levels": ["Information", "Warning", "Error", "Fatal"] },
{ "Sink": "Elasticsearch", "Levels": ["Warning", "Error", "Fatal"] }
]
For per-sink configuration options (connection strings, paths, retention, custom columns), see the guide → linked in the table above.
📊 Comparison
| Feature | Serilog alone | NLog | LoggerHelper v5 |
|---|---|---|---|
| Per-level sink routing (declarative) | Manual per sink | Via targets | JSON / fluent — built-in |
ILogger<T> compatible |
Via bridge pkg | Native | Native — zero code change |
| Install only needed sinks | ❌ | ❌ | ✅ modular NuGet |
| Named params preserved (structured) | ✅ | ✅ | ✅ |
BeginScope structured |
✅ | ✅ | ✅ propagates to Serilog |
| OpenTelemetry trace correlation | Manual | Manual | ✅ built-in, auto |
| Internal error diagnostics | ❌ | ❌ | ✅ injectable ILogErrorStore |
| Fluent OR JSON OR merged | ❌ | ❌ | ✅ all three |
| Request/Response middleware | Serilog.AspNetCore | Manual | ✅ 1 line |
| Email/Telegram alerts | 3rd-party | NLog.MailKit | ✅ built-in + throttling |
| Dynamic file routing by property | ❌ | ❌ | ✅ multi-tenant ready |
| Sink plugin system (custom sinks) | Manual wiring | Manual | ✅ [ModuleInitializer] auto-reg |
| Initial setup complexity | 15–30 lines | XML + code | ✅ 5 lines |
| Sensitive data masking (PII/secrets) | Manual IDestructuringPolicy |
3rd-party | ✅ JSON-driven, all sinks at once |
🏗️ Architecture
LoggerHelper uses a zero-dependency plugin architecture. The core package has no knowledge of any specific sink — they self-register at startup via [ModuleInitializer].
Your App
└── CSharpEssentials.LoggerHelper (core)
├── Bridges ILogger<T> → Serilog (zero allocations on hot path)
├── Routes events by level via pre-computed HashSet<LogEventLevel>
├── Exposes ILogErrorStore for sink failure diagnostics
└── Discovers sink plugins automatically at startup
├── Sink.Console (auto-registers via [ModuleInitializer])
├── Sink.File (auto-registers)
├── Sink.Email (auto-registers)
└── ... any ISinkPlugin
Performance Focus
Every version ships a targeted performance audit. Key hot-path optimizations to date:
| Version | Component | Optimization | Impact |
|---|---|---|---|
| v5.0.6 | Middleware ReadBodySafe |
ArrayPool<char> replaces new char[64K] |
−256 KB LOH per request |
| v5.0.5 | SinkRouting.Matches() |
HashSet<LogEventLevel> replaces string O(n) scan |
Zero alloc per log event |
| v5.0.5 | Telegram Emit() |
Fire-and-forget Task.Run vs blocking GetResult() |
No pipeline stall |
| v5.0.5 | Email template | Cached at ctor vs File.ReadAllText per emit |
No disk I/O on hot path |
Building a Custom Sink
[LoggerHelperSink]
public sealed class MyTargetSinkPlugin : ISinkPlugin {
public bool CanHandle(string sinkName) =>
sinkName.Equals("MyTarget", StringComparison.OrdinalIgnoreCase);
public void Configure(LoggerConfiguration loggerConfig, SinkRouting routing, LoggerHelperOptions options) {
var opts = options.GetSinkConfig<MyTargetOptions>("MyTarget")
?? options.BindSinkSection<MyTargetOptions>("MyTarget");
loggerConfig.WriteTo.Conditional(
evt => routing.Matches(evt.Level),
wt => wt.MySink(opts?.ConnectionString)
);
}
}
public static class PluginInitializer {
[ModuleInitializer]
public static void Init() => SinkPluginRegistry.Register(new MyTargetSinkPlugin());
}
Reference CSharpEssentials.LoggerHelper as a NuGet package. The sink auto-registers with no changes to the core.
📚 Documentation & Links
- Documentation Site — full reference, guides, and playground
- Interactive Playground
- Changelog
- Benchmark Results
- Migration Guide v2/v4 → v5
- NuGet — LoggerHelper
- NuGet — HttpHelper
- GitHub Issues
License
MIT — Alessandro Chiodo
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. 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. |
-
net10.0
- Serilog (>= 4.2.0)
- Serilog.Settings.Configuration (>= 9.0.0)
-
net8.0
- Microsoft.Extensions.Configuration (>= 10.0.9)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.9)
- Microsoft.Extensions.Configuration.Json (>= 9.0.1)
- Microsoft.Extensions.Logging (>= 9.0.1)
- Serilog (>= 4.2.0)
- Serilog.Settings.Configuration (>= 9.0.0)
-
net9.0
- Microsoft.Extensions.Configuration (>= 10.0.9)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.9)
- Serilog (>= 4.2.0)
- Serilog.Settings.Configuration (>= 9.0.0)
NuGet packages (12)
Showing the top 5 NuGet packages that depend on CSharpEssentials.LoggerHelper:
| Package | Downloads |
|---|---|
|
CSharpEssentials.HttpHelper
Make every HTTP call resilient in one line. Built-in retry, rate limiting, and automatic logging — so your app keeps working when APIs don't. Built on IHttpClientFactory — register once with AddHttpClients(), inject anywhere. |
|
|
CSharpEssentials.LoggerHelper.Sink.Console
See your logs in color, instantly. Add Console to your LoggerHelper routes and get beautiful, level-filtered colored output — zero configuration beyond a single JSON entry. Auto-registered via [ModuleInitializer]: no code changes, just add the NuGet. Part of the CSharpEssentials.LoggerHelper ecosystem with 9 pluggable sinks and now a built-in MCP server for AI assistant integration. |
|
|
CSharpEssentials.LoggerHelper.Sink.MSSqlServer
Query your logs with SQL. Auto-creates the table, batches the writes, and lets you JOIN logs with your business data — because sometimes grep isn't enough. AutoCreateSqlTable means zero migrations to get started. |
|
|
CSharpEssentials.LoggerHelper.Sink.Postgresql
Logs meet PostgreSQL. JSONB fields you can query, auto-created tables, and the full power of SQL — perfect for teams already running Postgres. NeedAutoCreateTable means zero migrations to get started. |
|
|
CSharpEssentials.LoggerHelper.Sink.Elasticsearch
Search millions of logs in milliseconds. Ship structured logs straight to Elasticsearch and build Kibana dashboards that actually tell you what's happening. Works with self-hosted Elasticsearch, OpenSearch, and Elastic Cloud — point IndexFormat at your ILM policy and go. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 5.2.2.5 | 0 | 7/5/2026 |
| 5.2.2.4 | 0 | 7/5/2026 |
| 5.2.2.3 | 0 | 7/5/2026 |
| 5.2.2.2 | 0 | 7/5/2026 |
| 5.2.2.1 | 40 | 7/4/2026 |
| 5.2.2 | 72 | 7/3/2026 |
| 5.2.0 | 307 | 6/29/2026 |
| 5.1.1 | 318 | 6/19/2026 |
| 5.1.0 | 308 | 6/16/2026 |
| 5.0.8 | 364 | 6/13/2026 |
| 5.0.7 | 342 | 6/11/2026 |
| 5.0.6 | 323 | 6/10/2026 |
| 5.0.5 | 360 | 6/6/2026 |
| 5.0.4 | 339 | 6/5/2026 |
| 5.0.3 | 342 | 6/2/2026 |
| 5.0.2 | 411 | 6/1/2026 |
| 5.0.1 | 394 | 5/31/2026 |
| 5.0.0 | 364 | 5/31/2026 |
| 4.1.0 | 177 | 3/27/2026 |
| 4.0.13 | 179 | 2/28/2026 |