CSharpEssentials.LoggerHelper is a modular, centralized hub for Serilog sinks that delivers comprehensive observability . Its flexible JSON‑based configuration lets you decide which log levels go to which sink—for example, routing only Error messages to email while sending all logs to ElasticSearch—without touching your code . The package unifies logs, metrics and traces via OpenTelemetry; every log entry carries a trace_id to correlate distributed requests, and an interactive dashboard lets you visualize traces, sink failures and telemetry, and configure alerts . Standard structured fields (e.g., IdTransaction, ApplicationName, MachineName, Action) are included by default, and you can enrich logs with custom properties that propagate across all sinks . Each sink—Console, File, MSSQL, PostgreSQL, ElasticSearch, Email, Telegram, xUnit, Telemetry, Dashboard and AI—is delivered as a separate NuGet package, so you install only what you need; the core loads them dynamically and exposes CurrentError and an in‑memory Errors queue to simplify debugging . The latest AI integration enables natural‑language queries against logs, trace correlation for root‑cause analysis, anomaly detection and automatic incident summaries , while the new xUnit sink captures full traces of failed tests directly in your test output—ideal for debugging flaky tests or disconnected environments
$ dotnet add package CSharpEssentials.LoggerHelperA flexible and modular structured logging library for .NET ( 6.0/8.0 ) applications based on Serilog. Easily configure logging to Console, File, Email, PostgreSQL, ElasticSearch via simple JSON configuration. Includes automatic placeholder validation and multi-sink orchestration.
It allows you to:
Action, IdTransaction, ApplicationName, MachineName{} placeholdersIn common usage scenarios, it is advisable to avoid logging
Informationlevel events to sinks like Telegram, MSSQL, or PostgreSQL. This practice prevents issues such as HTTP 429 (rate limits) on Telegram and reduces risks of deadlocks or insufficient storage in database systems.
✅ Multi-sink logging support:
✅ Structured logs with custom properties
✅ Sync and async logging
✅ Request/response middleware logger
✅ Transaction ID, action, machine name
✅ Custom levels per sink
✅ JSON configuration via appsettings.LoggerHelper.json
dotnet add package CSharpEssentials.LoggerHelperCreate a file named appsettings.LoggerHelper.json in your project root:
{
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Debug",
"System": "Debug"
}
},
"SerilogConfiguration": {
"ApplicationName": "TestApp",
"SerilogCondition": [
{"Sink": "ElasticSearch","Level": []},
{"Sink": "MSSqlServer","Level": []},
{"Sink": "Email","Level": []},
{"Sink": "PostgreSQL","Level": ["Information","Warning","Error","Fatal"]},
{"Sink": "Telegram","Level": []},
{"Sink": "Console","Level": [ "Information" ]},
{"Sink": "File","Level": ["Information","Warning","Error","Fatal"]}
],
"SerilogOption": {
"File": {
"Path": "D:\\Logs\\ServerDemo",
"RollingInterval": "Day",
"RetainedFileCountLimit": 7,
"Shared": true
},
"TelegramOption": {
"chatId": "xxxxx",
"Api_Key": "sssss:ttttttttt"
},
"PostgreSQL": {
"connectionString": "<YOUR CONNECTIONSTRING>",
"tableName": "public",
"schemaName": "dbo",
"needAutoCreateTable": true
},
"ElasticSearch": {
"nodeUris": "http://10.0.1.100:9200",
"indexFormat": "<YOUR INDEX FORMAT>"
},
"Email": {
"From": "<Email Alert>",
"Port": 587,
"Host": "<Host EMail>",
"To": [ "recipient#1", "recipient#2" ],
"CredentialHost": "<UserName SMTP>",
"CredentialPassword": "<Password SMTP>"
},
"MSSqlServer": {
"connectionString": "<YOUR CONNECTIONSTRING>",
"sinkOptionsSection": {
"tableName": "logs",
"schemaName": "dbo",
"autoCreateSqlTable": true,
"batchPostingLimit": 100,
"period": "0.00:00:10"
},
"columnOptionsSection": {
"addStandardColumns": [
"LogEvent"
],
"removeStandardColumns": [
"Properties"
]
}
},
"GeneralConfig": {
"EnableSelfLogging": false
}
}
}
}
}⚠️ Important: The logger will only write to a sink if the
Levelarray inSerilogConditioncontains at least one valid log level (e.g.,"Error","Warning"). If theLevelarray is empty (e.g.,"Level": []), that sink will be ignored, andWriteTo** will not be applied**, even if the sink configuration exists.🧩 PostgreSQL is preconfigured with a default column mapping for logs. The following columns are used automatically:
message,message_template,level,raise_date,exception,properties,props_test,machine_name. No custom mapping is required in the JSON.
🖼️ Example of a Telegram-formatted log message:
💬 Telegram Notice: When using the Telegram sink, log messages are formatted for human readability, and may include emojis or markdown. For this reason, it's strongly recommended to set the
Levelto onlyErrororFatalto avoid exceeding Telegram's rate limits and to prevent excessive message noise.
🛠 Tip: Before publishing to production, test each sink you plan to use. You can enable Serilog self-logging to capture internal errors using:
Serilog.Debugging.SelfLog.Enable(msg => File.AppendAllText(Path.Combine(logPath, "serilog-selflog.txt"), msg));Replace
logPathwith your local or shared log directory. This helps identify misconfigurations or sink loading issues early.
Each sink only receives log levels specified in the SerilogCondition array. If a sink's Level array is empty, that sink will be ignored entirely, and no log will be written to it, even if it's configured elsewhere:
| Sink | Levels |
|---|---|
| Console | Information, Warning, Error, Fatal |
| File | Error, Fatal |
| PostgreSQL | Error, Fatal |
| MSSqlServer | Error, Fatal |
| Telegram | Fatal |
| Elasticsearch | (disabled) |
The LoggerHelper package includes a built-in middleware that logs every incoming HTTP request and outgoing response automatically. It captures:
To enable it, just call:
app.UseMiddleware<RequestResponseLoggingMiddleware>();📌 This middleware uses
LogEventLevel.Informationby default and is automatically compatible with sinks that accept that level.
Program.cs⚠️ IMPORTANT: You must call
AddLoggerConfigurationduring theProgram.cssetup. Without it, LoggerHelper will NOT work. No logs will be captured, and no middleware will be active.
ℹ️ Important: depending on the target framework version, you must configure
LoggerHelperdifferently.
If you are using .NET 6.0, you must call the configuration directly on the builder.
If you are using .NET 8.0, you must call it on the builder.Services.
Here’s how you should do it:
using CSharpEssentials.LoggerHelper;
var builder = WebApplication.CreateBuilder(args);
// Add LoggerHelper configuration
#if NET6_0
builder.AddLoggerConfiguration();
#else
builder.Services.AddLoggerConfiguration(builder);
#endif
builder.Services.AddControllers();
var app = builder.Build();
// Logs every HTTP request and response
app.UseMiddleware<RequestResponseLoggingMiddleware>();
app.MapControllers();
app.Run();| Target Framework | Usage |
|---|---|
| .NET 6.0 | builder.AddLoggerConfiguration(); |
| .NET 8.0 | builder.Services.AddLoggerConfiguration(builder); |
This ensures full compatibility across different .NET versions.
loggerExtension<MyRequest>.TraceSync(
request,
LogEventLevel.Information,
null,
"Operation successful: {OperationName}",
"CreateUser"
);await loggerExtension<MyRequest>.TraceAsync(
request,
LogEventLevel.Error,
exception,
"Error during operation: {OperationName}",
"UpdateUser"
);loggerExtension<IRequest>.TraceSync(
null,
LogEventLevel.Warning,
null,
"System warning: {WarningMessage}",
"Low disk space"
);| Column | Description |
|---|---|
| ApplicationName | Application name |
| message | Message content |
| message_template | Message template |
| level | Log level |
| raise_date | Log timestamp |
| exception | Exception details |
| properties | Serialized properties |
| props_test | Additional serialized data |
| MachineName | Machine name |
| Action | Action name |
| IdTransaction | Unique transaction ID |
| Column | Type | Description |
|---|---|---|
| Message | nvarchar | Message content |
| MessageTemplate | nvarchar | Message template |
| Level | nvarchar | Log level |
| TimeStamp | datetime | Log timestamp |
| Exception | nvarchar | Exception details |
| Properties | nvarchar | Serialized properties |
| LogEvent | nvarchar | Serialized log event |
| IdTransaction | varchar | Unique transaction ID |
| MachineName | varchar | Machine name |
| Action | varchar | Action name |
| Field | Description |
|---|---|
| action | Action name |
| message | Text to log |
| applicationName | Application name |
| level | Log level (Information, Warning, Error, etc.) |
| Field | Value |
|---|---|
| Timestamp | 2025-05-10 17:45:00 |
| Level | Error |
| IdTransaction | 7e7b9f65-ed13-439a-852b-18d9d28dd6ec |
| MachineName | PIXELO30 |
| Action | GetUserDetails |
| ApplicationName | LoggerHelperDemo |
| Message | Error occurred during request |
Try it live with a demo Web API to validate each log level dynamically:
| Method | Endpoint | Query Parameters | Description |
|---|---|---|---|
| GET | /loggerHelper | action, message, level | Sends a structured log with the specified level |
❌ If you get System.IO.IOException like: "file is being used by another process", make sure:
✅ For self-log output (serilog-selflog.txt), ensure that:
FileShare.ReadWrite if needed.Level array in SerilogCondition is not empty.serilog-selflog.txt if enabled — it often reveals silent misconfigurations.Contributions, ideas and issues are welcome! Feel free to open a pull request or discussion on GitHub.
This project is licensed under the MIT License.
Alessandro Chiodo 📧 GitHub · NuGet · LinkedIn 📦 NuGet Package