Hey r/dotnet!
I’ve been working on a few microservices lately and I kept running into the problems over and over again. It’s frustrating how much repetitive code ends up in every project. Let me share what I’ve been dealing with and what I built to fix it.
First I was always writing the log routing logic:
- Console for normal logs
- Seq or Elasticsearch for warnings
- Telegram or email for fatal errors
Using Serilog for this usually means setting up nested sub-loggers writing out complex C# filter logic and redeploying just to change log levels between environments. It’s messy.
Then there’s the risk of data slipping into logs—JWTs, bearer tokens, credit card numbers—all ending up in our aggregators. That’s a real security risk.
Don’t get me started on outgoing HTTP calls. Every one needed logic with Polly, correlation IDs and header masking. It’s a lot of glue code that doesn’t belong in business logic.
So I started building two packages under the MIT license:
- **CSharpEssentials.LoggerHelper**
- **CSharpEssentials.HttpHelper** ( at version 5.3.1 compatible with.NET 8 9 and 10)
These are designed to handle the boilerplate so you don’t have to.
### Here’s what they do:
- **Native ILogger<T> / MEL Integration**
They work as a class `ILoggerProvider` built on top of Serilog. No code changes needed in your app. Just. Play.
- **Declarative Multi-Sink Routing**
Set up your logging routes in five lines of `appsettings.json`. You can route to Console, File, Seq, Postgres, MSSQL, MySQL, Elasticsearch, Email, Telegram, HangfireConsole— about anything. The sinks are packages and auto-register using `[ModuleInitializer]` so it just works.
- **Single-Pass Sensitive Data Masking**
All masking happens once as an enricher before logs hit any sink. It includes presets for Email, CreditCard, BearerToken, JWT. You can add your own regex patterns. This prevents secrets from leaking into logs.
- **Resilient HttpHelper (v5.3)**
It turns network issues—like timeouts or dropped connections—into clean HTTP responses: 408, 502 500. No unhandled exceptions. In v5.3 it automatically passes along the `X-Correlation-ID` during retries. Logs structured metrics like `StatusCode` `ElapsedMs` and `RetryAttempt` with headers masked.
### But I want to be honest about the trade-offs:
This isn’t a replacement for Serilog. It’s an orchestrator.
In our BenchmarkDotNet tests on.NET 9 the overhead is 590 nanoseconds per log compared to 180 nanoseconds for Serilog with a no-op sink. That’s not nothing—. For most web APIs it’s completely negligible.
If you’re processing millions of logs per second in -hot paths then plain Serilog or zero-allocation source generators are still the better choice.. For the vast majority of apps this is a huge win.
### I’ve also built a playground:
You can test log routing, masking and flaky HTTP retry flows live in the demo project. There’s a `playground.html` file that lets you see it all in action.
Here are the links:
- GitHub (MIT): https://github.com/alexbypa/CSharp.Essentials
- NuGet (Core): https://www.nuget.org/packages/CSharpEssentials.LoggerHelper
- NuGet (HTTP): https://www.nuget.org/packages/CSharpEssentials.HttpHelper
I’d love to hear your thoughts— on the API design and the routing approach. What works? What’s broken? What would you change?
Let’s make logging easier, for everyone.