Lightbringer.SimpleFileLogger 1.1.7

dotnet add package Lightbringer.SimpleFileLogger --version 1.1.7
                    
NuGet\Install-Package Lightbringer.SimpleFileLogger -Version 1.1.7
                    
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="Lightbringer.SimpleFileLogger" Version="1.1.7" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Lightbringer.SimpleFileLogger" Version="1.1.7" />
                    
Directory.Packages.props
<PackageReference Include="Lightbringer.SimpleFileLogger" />
                    
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 Lightbringer.SimpleFileLogger --version 1.1.7
                    
#r "nuget: Lightbringer.SimpleFileLogger, 1.1.7"
                    
#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.
#:package Lightbringer.SimpleFileLogger@1.1.7
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Lightbringer.SimpleFileLogger&version=1.1.7
                    
Install as a Cake Addin
#tool nuget:?package=Lightbringer.SimpleFileLogger&version=1.1.7
                    
Install as a Cake Tool

Simple File Logger

Lightweight library for writing log information to files using the .NET ILogger<T> interface. For more information regarding the usage of ILogger<T> see the Microsoft Documentation

Release Notes

Version 1.1.7

Set initial default log folder to ./Logs if no specific configuration is provided.

Version 1.1.6

Improved resilience against file writing issues. Added error handling to the log message process loop as well as retry mechanisms for file writing operations and a watchdog that monitors if logs are still written and initates a restart in case of persistent file writing issues.

If AppendWithRery(...) fails after 5 attempts, the FileLoggerProvider tries to write the message in a file named LoggingFailures.log instead. The watchdog will initiate a restart of the logging process if there are pending log messages for more than 30 seconds without a successful write operation.

Version 1.1.5

The method names for the extensions to log to individual files have changed from Log[LEVEL](...) to Log[LEVEL]ToFile(...) avoid conflicts with the standard overloads of ILogger. See details below.

Quick Start

The minimum required framework version is .NET 6

The library is available as a NuGet package named Lightbringer.SimpleFileLogger.

After including the package in your project, SimpleFileLogger exposes an extension method to add file logging with only one line of code during startup:

using SimpleFileLogger;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSimpleFileLogging(builder.Configuration);

With only a minimum of configuration you can start logging to ./Logs/Default_yyyy-mm-dd.log immediately:

    "Logging": { 
        "LogLevel": {
            // Define LogLevels here ...
        },
        "FileLoggerOptions": {
            "LogFolder": "./Logs",
            "FileNamesWithoutExtension": {
                "*": "Default"
            }
        }
    }

Detailed Setup

If your file logging options are not located under Logging:FileLoggerOptions in appsettings.json, you can pass the section as a second parameter:

builder.Services.AddSimpleFileLogging(builder.Configuration, "MyFileLoggingConfigSection");

If you prefer to provide the file logging options via code instead of appsettings.json, use the traditional way of configuration:

builder.Services.AddLogging(logBuilder =>
{
    logBuilder.Services.AddSingleton<ILoggerProvider, FileLoggerProvider>();
    logBuilder.Services.Configure<LoggerOptions>(options => ConfigureFileLogging(options));
});

private void ConfigureFileLogging(LoggerOptions options)
{
    options.LogFolder = "./Logs";
    // ...
}

Configuration

Configuration should usually be provided via appsettings.json in section Logging:FileLoggerOptions

    "Logging": { 
        "LogLevel": {
            "Default": "Debug",
            "Microsoft.AspNetCore": "Warning"
        },
        "FileLoggerOptions": {
            "LogFolder": "./Logs",
            "FileNamesWithoutExtension": {
                "Microsoft": "Framework/Microsoft",
                "Microsoft.EntityFrameworkCore": "Framework/EFCore",
                "MyNamespace.MyClass": "MyClass",
                "*": "Default"
            },
            "NumberOfDaysToKeepLogs": 5,
            "EventOptions": [
                { "Id": 99, "SubFolder": "Errors" },
                { "Id": 1, "SubFolder": "Sub", "NameExtension": "AddName" },
                { "Id": 2, "SubFolderFromEventName": true },
                { "Id": 3, "NameExtensionFromEventName": true }
            ]
        }
    }

In the LogLevel section, the minimum level that leads to writing a log message to a file can be specified for namespaces or classes using the full qualified name. Default applies to all classes in namespaces not mentioned in the configuration. This is the .NET logging standard and nothing specific of SimpleFileLogger.

In the section below named FileLoggerOptions the options for file logging are provided. You can specify a root folder with the LogFolder value. If it is not set, the directory of the application assembly is used as root. Then a file name without extension can be specified for each namespace or class that shall be logged in its own file. The path is relative to LogFolder. The entry * is mandatory and specifies the default log file used for any content that falls not in one of the namespace defined above.

The file names provided here are extended by _yyyy-MM-dd.log (as long as no further EventOptions are provided, see below). So a new log file is created every day.

New in v1.1.3

With the property NumberOfDaysToKeepLogs it can now be controlled how long log files will be stored. If this property is defined and has a value > 0, a method that checks for outdated log files will run on startup and then every hour. All *.log files within your defined LogFolder that are older than the number of defined days will be deleted.

To get more control over file names and log directories, certain options can be provided via the EventOptions array. The EventId provided for a call to ILogger.Log(eventId, ...) will be matched against the specified EventOptions objects. The follwing properties are available:

  • Id: int mandatory , must match the EventId.Id property provided for logging
  • SubFolder: string optional , a folder that will be inserted between the default LogFolder and the FileName. Can contain multiple directories
    • e.g. sub/path
  • NameExtension: string optional, an addition that will be applied after the defined FileName and before the date.
    • e.g. _nameExtension --> fileName_nameExtension_22-04-02.log (note that the _ here is part of the definition and will not be inserted automatically)
  • SubFolderFromEventName: boolean optional, if set to true, the sub folder to insert between LogFolder and the FileName will be taken dynamically from EventId.Name when calling ILogger.Log(eventId, ...). If EventId.Name is null or empty, the Id number will be used
  • NameExtensionFromEventName: boolean optional, if set to true, the exentsion applied to the FileName will be taken dynamically from EventId.Name when calling ILogger.Log(eventId, ...). If EventId.Name is null or empty, the Id number will be used. Here, an _ will be inserted automatically between default file name and exension

LogLevels can be changed at any time without restarting the application. Changes in file names only apply after a restart.

That's all regarding the configuration. Now you only need to add a ILogger<T> to the constructor of classes you want to log information from and call the corresponding methods. Log information from existing framework classes will also be written automatically.

Examples

    // Constructor
    public MyClass(/*...*/ ILogger<MyClass> logger)
    {
        this.logger = logger;
        // ...
    }

    private void MyMethod()
    {
        try
        {
            object obj;
            // Do anything awesome
            logger.LogInformation("Did anything awesome to {obj}", obj)
        }
        catch(Exception exc)
        {
            // assuming that an EventOption definiton is set like {Id: 99, SubFolder: "exceptions"}
            // the message goes into e.g. logs/exceptions/MyClass_2022-04-08.log
            logger.LogCritical(99, exc, "Exception!");
        }
    }

    public void ChangeObject(Foo obj)
    {
        obj.Name = "bar";
        var eventId = new EventId(10, obj.Name)
        // ...
        // assuming that an EventOption definiton is set like {Id: 10, NameExtensionFromEventName: true}
        // the message goes into e.g. logs/MyClass_bar_2022-04-08.log
        logger.LogDebug(eventId, "Value set for {val}", obj.Value)
    }

Logging of complex objects

The library provides an extension method ToJson(ILogger? logger = null, LogLevel level = LogLevel.None) for object that serializes the object to JSON. Reference cycles are not serialized. If the serialization could be expensive, you can provide the ILogger instance and the LogLevel for the Log(...) call to prevent unnecessary serialization if logging is currently not enabled for the defined level.

// serialization will always be performed
logger.LogError(exc, "Operation failed! Resource was:\n{resourceJson}", newResource.ToJson());

// serialization will only be performed if Trace logging is currently enabled for the logger instance
logger.LogTrace("Trace logging! largeObject is:\n{objJson}", largeObject.ToJson(logger, LogLevel.Trace));

Be carful with frequent logging of complex objects with a lot of references to other complex objects because the log size could increase rapidly. Advice is to do this only for a limited time when Trace or Debug logging is enabled.

<mark>Note that Microsoft recommends to not us $"{var} text" interpolation but to use placeholders and parameters that are passed to the log method.</mark>

Logging to Individual Log Files

Aside from the file name and path control utilizing the EventId of a log entry, there is also the option to log to completely individual files via extension mehtods of ILogger. This can't be done with ILogger<T> since there is no way to cast it to ILogger respecitvely IFileLogger (at least I could not find a way).

To use IFileLogger directly, you would have to inject an instance of ILoggerProvider to your class and create the ILogger manually:

    public IndexModel(ILoggerProvider loggerProvider)
    {     
        this.generalLogger = loggerProvider.CreateLogger("GeneralLogger");
    }

    private void MyMethodWithLogging()
    {
        // ...
        generalLogger.LogInformationToFile("individualSub/individualLog", $"my {0} text, {1}", arg1, arg2);
        // ...
    }

In version 1.1.5 the method names for the extension to log to individual files have changed from Log[LEVEL](...) to Log[LEVEL]ToFile(...) since it turned out the the default name conflicted with the standard overloads when adding using SimpleFileLogger; to a code file, causing accidentally logging to random file names (content as file name) without intending to do so.

The extension for individual file logging can not take advantage of the FormattedLogValues object used as TState in ILogger.Log<TState> since this is an internal class. Therefore you cannot use named parameters and will have to fallback to standard number based indexed arguments or string interpolation.

More

SimpleFileLogger internally uses a BlockingCollection<LogMessage> in a long running Task that decouples the logging from the working thread and prevents concurrent file access in scenarios where a lot of log messages have to be written in a short period of time.

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 was computed.  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.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.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
1.1.7 144 1/24/2026
1.1.6 226 11/7/2025
1.1.5 881 6/3/2022
1.1.4 549 5/30/2022
1.1.3 560 5/15/2022
1.1.2 561 5/3/2022
1.1.1 560 5/2/2022