Changelog
Notable changes in each release.
A flexible and feature-rich logging library for .NET applications. This library provides multiple logger implementations (Console and File), automatic caller information capture, custom log levels, and seamless integration with Microsoft.Extensions.Logging.
Install the package from NuGet:
dotnet add package ArturRios.Logging
Or via the Package Manager:
Install-Package ArturRios.Logging
You can also include this repository as a submodule in your project:
git submodule add https://github.com/artur-rios/dotnet-logging.git dotnet-logging
The simplest way to get started is using the StandaloneLogger:
using ArturRios.Logging;
using ArturRios.Logging.Configuration;
// Create console logger configuration
var consoleConfig = new ConsoleLoggerConfiguration
{
// Controls ANSI color usage in console output
UseColors = true
};
// Initialize standalone logger
var logger = new StandaloneLogger(new List<LoggerConfiguration> { consoleConfig });
// Log messages
logger.Info("Application started");
logger.Debug("Debug information");
logger.Error("An error occurred");
Log to files with configurable output:
var fileConfig = new FileLoggerConfiguration
{
ApplicationName = "MyApp", // Required: used in log file names
FolderScheme = LogFolderScheme.ByMonth, // Organize logs by month
FileSplitLevel = LogSplitLevel.Day, // Split logs by day
FilePath = "./logs" // Base path for log files (optional)
};
var logger = new StandaloneLogger(new List<LoggerConfiguration> { fileConfig });
logger.Info("This will be written to a file");
Use multiple loggers simultaneously:
var configurations = new List<LoggerConfiguration>
{
new ConsoleLoggerConfiguration
{
// Controls ANSI color usage in console output
UseColors = true
},
new FileLoggerConfiguration
{
ApplicationName = "MyApp", // Required
FolderScheme = LogFolderScheme.ByMonth,
FileSplitLevel = LogSplitLevel.Day,
FilePath = "./logs"
}
};
var logger = new StandaloneLogger(configurations);
// Every message is written to both the console and the file; there is no minimum level filter
logger.Warn("This warning appears in both console and file");
Integrate with ASP.NET Core or other frameworks using the standard logging abstractions:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using ArturRios.Logging;
using ArturRios.Logging.Adapter;
using ArturRios.Logging.Configuration;
using ArturRios.Logging.Interfaces;
var services = new ServiceCollection();
// The adapter forwards every entry to an IStateLogger resolved from a fresh scope;
// without this registration nothing is written.
services.AddScoped<IStateLogger>(_ => new StateLogger(new List<LoggerConfiguration>
{
new ConsoleLoggerConfiguration { UseColors = true }
}));
services.AddLogging(builder =>
{
builder.AddCustomLogger();
});
var serviceProvider = services.BuildServiceProvider();
var logger = serviceProvider.GetRequiredService<ILogger<MyClass>>();
logger.LogInformation("Integrated with ASP.NET Core");
var config = new ConsoleLoggerConfiguration
{
// Enable or disable ANSI color output in the console
UseColors = true
};
var config = new FileLoggerConfiguration
{
ApplicationName = "MyApp", // Required: used in log file names
FolderScheme = LogFolderScheme.ByMonth, // Organize logs by month
FileSplitLevel = LogSplitLevel.Day, // Split logs by day
FilePath = "./logs" // Base path for log files (optional)
};
The library supports the following log levels in order of severity:
FolderScheme decides the directory a log file is written to, and FileSplitLevel decides its name.
LogFolderScheme | Directory under the base path |
|---|---|
AllInOne | the base path itself |
ByYear | 2026/ |
ByMonth | 2026/08/ |
ByDay | 2026/08/24/ |
ByHour | 2026/08/24/13/ |
ByRequest | one folder per FileLogger instance |
ByRequest names the folder once per logger instance, so resolving the logger from a scoped registration
within a request gives one folder per request; a singleton gives one folder per process. Entries forwarded by
MicrosoftLoggerAdapter resolve the IStateLogger from a fresh scope per entry, so through the adapter a scoped
registration gives one folder per entry.
LogSplitLevel | File name |
|---|---|
Request | MyApp.log |
Year | MyApp_2026.log |
Month | MyApp_2026_08.log |
Day | MyApp_2026_08_24.log |
Hour | MyApp_2026_08_24_13.log |
StateLogger takes the caller’s file and method from a state object instead of compiler attributes — the
shape Microsoft.Extensions.Logging passes along, which is how MicrosoftLoggerAdapter uses it. The keys
CallerFilePath / FilePath and CallerMemberName / MemberName / Method are recognized regardless of case;
anything missing is logged as unknown.
using ArturRios.Logging;
var stateLogger = new StateLogger(configurations);
stateLogger.TraceId = "trace-123";
stateLogger.Info("Operation started", new Dictionary<string, object>
{
["CallerFilePath"] = "OrderService.cs",
["CallerMemberName"] = "PlaceOrder"
});
stateLogger.TraceId = null;
The library automatically captures the calling file and method name without any additional configuration:
logger.Info("User logged in");
// Automatically captures: filename and method name where this call was made
Track related log entries across operations with trace IDs:
var logger = new StandaloneLogger(configurations);
logger.TraceId = "request-uuid-12345";
logger.Info("Processing request"); // Will include trace ID in output
logger.Debug("Step 1 complete"); // Will include trace ID in output
MicrosoftLoggerAdapter stamps a correlation id on everything it forwards, taken from the ambient
Activity:
using var activity = new Activity("ProcessOrder").SetIdFormat(ActivityIdFormat.W3C).Start();
logger.LogInformation("Processing order"); // carries activity.TraceId
Activity is the platform’s correlation primitive, not any one hosting model’s, so this works the same in
a web application, a worker, a console app and a test. Nested activities share a trace id, so every entry
from one logical operation correlates.
To override it — a message id from a queue, a correlation id from an upstream header — assign it:
adapter.TraceId = messageId;
The value is held in an AsyncLocal<string?>, so it applies to the current execution context and
everything that flows from it, and does not leak into concurrent work. It takes precedence over the
activity’s id until it is cleared.
The adapter hands that id to the IStateLogger it resolves for the one entry being forwarded and then puts
the state logger’s own TraceId back, so a singleton IStateLogger can be shared by concurrent work without
one entry’s id landing on another entry’s line or on a later entry that has no id of its own.
Under ArturRios.Util.WebApi, TraceActivityMiddleware starts a W3C activity per request and derives its
own trace id from exactly that activity — so the id logged here is the one the middleware publishes on the
traceparent response header, with no wiring and no ASP.NET Core dependency on this side.
Releases that need changes in consuming code carry an upgrade guide in the changelog:
This project is licensed under the Artur Rios Attribution License. It is free and open source: you may view, use, fork, modify, and extend it, provided you credit the author, Artur Rios, with a link to artur-rios.tech or github.com/artur-rios.
Notable changes in each release.
Building from source, running the tests, branching and releasing.