CSharpEssentials.LoggerHelper
2.0.1
dotnet add package CSharpEssentials.LoggerHelper --version 2.0.1
NuGet\Install-Package CSharpEssentials.LoggerHelper -Version 2.0.1
<PackageReference Include="CSharpEssentials.LoggerHelper" Version="2.0.1" />
<PackageVersion Include="CSharpEssentials.LoggerHelper" Version="2.0.1" />
<PackageReference Include="CSharpEssentials.LoggerHelper" />
paket add CSharpEssentials.LoggerHelper --version 2.0.1
#r "nuget: CSharpEssentials.LoggerHelper, 2.0.1"
#addin nuget:?package=CSharpEssentials.LoggerHelper&version=2.0.1
#tool nuget:?package=CSharpEssentials.LoggerHelper&version=2.0.1
π¦ CSharpEssentials.LoggerHelper
Introduction
A 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:
- Send structured logs with guaranteed fields like
Action
,IdTransaction
,ApplicationName
,MachineName
- Dynamically enable multi-sink support (Console, File, HTML Email, PostgreSQL, ElasticSearch)
- Automatically validate message
{}
placeholders - Centralize configuration through LoggerBuilder, just by editing the appsettings.json file
In common usage scenarios, it is advisable to avoid logging
Information
level 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.
π Table of Contents
- β¨ Features
- π Installation
- βοΈ Configuration
- π Log Levels
- π§ͺ ASP.NET Core Setup
- π§βπ» Usage Examples
- 𧬠Database Schema
- π Demo API
- π Contributing
- π License
- π€ Author
β¨ Features
β Multi-sink logging support:
- Console
- File
- SQL Server
- PostgreSQL
- Elasticsearch
- Telegram
β 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
β οΈ Version 2.0.0 - Breaking Change
Starting from version 2.0.0, the
If you are upgrading from
1.x.x
, you MUST update yourappsettings.LoggerHelper.json
.
Old (before 2.0.0):
"Email": {
"From": "...",
"Host": "...",
"Port": 587,
"To": ["..."],
"CredentialHost": "...",
"CredentialPassword": "..."
}
New (since 2.0.0):
"Email": {
"From": "...",
"Host": "...",
"Port": 587,
"To": "...",
"username": "...",
"password": "...",
"EnableSsl": true
}
π¨ Why Email Handling Changed
Starting from version 2.0.0, LoggerHelper no longer uses the standard Serilog.Sinks.Email for sending emails.
Reason:
The official Serilog Email Sink does not support custom body formatting (HTML templates, structured logs, color coding, etc).
It only supports plain text messages generated via RenderMessage()
, without the ability to control the message content.
π See discussion: GitHub Issue - serilog/serilog-sinks-email
What changed:
- LoggerHelper now uses a custom internal SMTP sink:
LoggerHelperEmailSink
. - This allows sending fully customized HTML-formatted emails.
- Supports dynamic coloring based on log level (Information, Warning, Error).
- Supports secure SMTP with SSL/TLS.
β
No third-party dependencies added.
β
Full control over email appearance and content.
π Installation
dotnet add package CSharpEssentials.LoggerHelper
βοΈ Configuration
Create 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" ],
"username": "<UserName SMTP>",
"password": "<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
Level
array inSerilogCondition
contains at least one valid log level (e.g.,"Error"
,"Warning"
). If theLevel
array 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.
π Log Levels
πΌοΈ 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
Level
to onlyError
orFatal
to 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
logPath
with 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) |
π§ͺ ASP.NET Core Setup
Request/Response Logging Middleware
The LoggerHelper package includes a built-in middleware that logs every incoming HTTP request and outgoing response automatically. It captures:
- HTTP method, path, status code
- Request body (if available)
- Response body (if possible)
- Duration in milliseconds
To enable it, just call:
app.UseMiddleware<RequestResponseLoggingMiddleware>();
π This middleware uses
LogEventLevel.Information
by default and is automatically compatible with sinks that accept that level.
π₯ Register the logger in Program.cs
π Important Setup
β οΈ IMPORTANT: You must call
AddLoggerConfiguration
during theProgram.cs
setup. 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
LoggerHelper
differently.
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();
π§ Explanation
Target Framework | Usage |
---|---|
.NET 6.0 | builder.AddLoggerConfiguration(); |
.NET 8.0 | builder.Services.AddLoggerConfiguration(builder); |
This ensures full compatibility across different .NET versions.
π§βπ» Usage Examples
πΉ With request object
loggerExtension<MyRequest>.TraceSync(
request,
LogEventLevel.Information,
null,
"Operation successful: {OperationName}",
"CreateUser"
);
πΉ Async logging
await loggerExtension<MyRequest>.TraceAsync(
request,
LogEventLevel.Error,
exception,
"Error during operation: {OperationName}",
"UpdateUser"
);
πΉ Without request object
loggerExtension<IRequest>.TraceSync(
null,
LogEventLevel.Warning,
null,
"System warning: {WarningMessage}",
"Low disk space"
);
Database Schema
PostgreSQL
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 |
SQL Server
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 |
Swagger Example
Field | Description |
---|---|
action | Action name |
message | Text to log |
applicationName | Application name |
level | Log level (Information, Warning, Error, etc.) |
HTML Email Screenshot
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 |
Demo API
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 |
π§ͺ Troubleshooting
File access denied?
β If you get
System.IO.IOException
like: "file is being used by another process", make sure:- No other process (e.g. text editor, logging library) is locking the file.
- The file is not open in append-only exclusive mode.
β For self-log output (
serilog-selflog.txt
), ensure that:- The target folder exists.
- The executing process has write permission to it.
- Use
FileShare.ReadWrite
if needed.
Sink not writing logs?
- β
Make sure the
Level
array inSerilogCondition
is not empty. - β
Check
serilog-selflog.txt
if enabled β it often reveals silent misconfigurations.
π Contributing
Contributions, ideas and issues are welcome! Feel free to open a pull request or discussion on GitHub.
π License
This project is licensed under the MIT License.
π€ Author
Alessandro Chiodo π§ GitHub Β· NuGet Β· LinkedIn π¦ NuGet Package
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. 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 was computed. 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. |
-
net6.0
- Microsoft.Extensions.Configuration (>= 9.0.4)
- Microsoft.Extensions.Configuration.Binder (>= 9.0.1)
- Microsoft.Extensions.Configuration.Json (>= 9.0.1)
- Serilog (>= 4.2.0)
- Serilog.Settings.Configuration (>= 9.0.0)
- Serilog.Sinks.Console (>= 6.0.0)
- Serilog.Sinks.Elasticsearch (>= 10.0.0)
- Serilog.Sinks.Email (>= 4.1.0)
- Serilog.Sinks.MSSqlServer (>= 8.1.0)
- Serilog.Sinks.Postgresql.Alternative (>= 3.5.0)
- Serilog.Sinks.Telegram (>= 0.2.1)
- System.Diagnostics.DiagnosticSource (>= 9.0.4)
-
net8.0
- Microsoft.Extensions.Configuration (>= 9.0.4)
- Microsoft.Extensions.Configuration.Binder (>= 9.0.1)
- Microsoft.Extensions.Configuration.Json (>= 9.0.1)
- Serilog (>= 4.2.0)
- Serilog.Settings.Configuration (>= 9.0.0)
- Serilog.Sinks.Console (>= 6.0.0)
- Serilog.Sinks.Elasticsearch (>= 10.0.0)
- Serilog.Sinks.Email (>= 4.1.0)
- Serilog.Sinks.MSSqlServer (>= 8.1.0)
- Serilog.Sinks.Postgresql.Alternative (>= 3.5.0)
- Serilog.Sinks.Telegram (>= 0.2.1)
- System.Diagnostics.DiagnosticSource (>= 9.0.4)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.