CSharpEssentials.LoggerHelper 2.0.1

dotnet add package CSharpEssentials.LoggerHelper --version 2.0.1
                    
NuGet\Install-Package CSharpEssentials.LoggerHelper -Version 2.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CSharpEssentials.LoggerHelper" Version="2.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CSharpEssentials.LoggerHelper" Version="2.0.1" />
                    
Directory.Packages.props
<PackageReference Include="CSharpEssentials.LoggerHelper" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CSharpEssentials.LoggerHelper --version 2.0.1
                    
#r "nuget: CSharpEssentials.LoggerHelper, 2.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#addin nuget:?package=CSharpEssentials.LoggerHelper&version=2.0.1
                    
Install CSharpEssentials.LoggerHelper as a Cake Addin
#tool nuget:?package=CSharpEssentials.LoggerHelper&version=2.0.1
                    
Install CSharpEssentials.LoggerHelper as a Cake Tool

CodeQL NuGet Downloads Last Commit

πŸ“¦ 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

  • βœ… Multi-sink logging support:

    • Console
    • File
    • SQL Server
    • PostgreSQL
    • Elasticsearch
    • Email
    • 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 Email configuration section has been renamed.

If you are upgrading from 1.x.x, you MUST update your appsettings.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 in SerilogCondition contains at least one valid log level (e.g., "Error", "Warning"). If the Level array is empty (e.g., "Level": []), that sink will be ignored, and WriteTo** 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 Sample

πŸ’¬ 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 only Error or Fatal 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 the Program.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

πŸ”— GitHub Repository (Demo)


πŸ§ͺ 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 in SerilogCondition 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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2.0.1 18 5/11/2025
2.0.0 21 5/11/2025
1.3.1 26 5/11/2025
1.2.3 33 5/10/2025
1.2.2 29 5/10/2025
1.2.1 74 5/9/2025
1.1.6 122 5/6/2025
1.1.5 142 5/5/2025
1.1.4 126 5/4/2025
1.1.3 130 5/4/2025
1.1.2 129 5/4/2025
1.1.1 122 5/4/2025
1.0.1 93 2/19/2025
1.0.0 89 2/19/2025