RunLog 3.3.0

dotnet add package RunLog --version 3.3.0
                    
NuGet\Install-Package RunLog -Version 3.3.0
                    
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="RunLog" Version="3.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RunLog" Version="3.3.0" />
                    
Directory.Packages.props
<PackageReference Include="RunLog" />
                    
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 RunLog --version 3.3.0
                    
#r "nuget: RunLog, 3.3.0"
                    
#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=RunLog&version=3.3.0
                    
Install RunLog as a Cake Addin
#tool nuget:?package=RunLog&version=3.3.0
                    
Install RunLog as a Cake Tool

RunLog

NuGet

RunLog is a lightweight, customizable logging framework inspired by Serilog. It provides a fluent API for configuring loggers with various sinks, enrichers, and minimum log levels. The framework supports both contextual logging and structured log templates.

Features

  • Fluent configuration API
  • Multiple log levels (Verbose, Debug, Information, Warning, Error, Fatal)
  • Multiple built-in sink types (Console, File)
  • File sinks are buffered by default for better performance
  • Customizable minimum log levels per sink
  • File rolling capabilities (by time interval or size)
  • Structured logging with property value capturing
  • Thread-safe operation
  • Exception logging
  • Custom formatters support
  • Enrichers for adding custom properties
  • Named logger support for component-specific logging

Installation

Install the RunLog package from NuGet:

Install-Package RunLog
# or
dotnet add package RunLog

Package URL: NuGet Gallery

Quick Start

Important: You must configure the logger by setting Log.Logger before using any logging methods. If you attempt to use logging methods before setting Log.Logger, an InvalidOperationException will be thrown.

Here's a simple example to get you started:

using RunLog;

// Configure the logger (this step is required)
Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug()
    .WriteTo.Console()
    .WriteTo.File("logs/app.log", rollingInterval: RollingInterval.Day)
    .Enrich("ApplicationName", "MyApp")
    .Enrich("Version", "1.0.0")
    .CreateLogger();

// Log some messages
Log.Information("Application {AppName} starting up", "MyApp");
Log.Debug("Processing item {ItemId}", 42);

try
{
    // Some operation that might throw
    throw new System.InvalidOperationException("Something went wrong");
}
catch (Exception ex)
{
    Log.Error(ex, "Error processing request");
}

// Optional: You can manually flush logs at any point if needed
// Log.CloseAndFlush();

When you set Log.Logger, RunLog automatically registers shutdown handlers to ensure logs are properly flushed when your application exits, so you typically don't need to call Log.CloseAndFlush() explicitly.

Configuration

RunLog uses a fluent configuration API to set up logging:

Setting Minimum Level

// Set minimum level for all sinks
var config = new LoggerConfiguration()
    .MinimumLevel.Information();  // Only Information and above will be logged

// Other available levels:
// .MinimumLevel.Verbose()
// .MinimumLevel.Debug()
// .MinimumLevel.Warning()
// .MinimumLevel.Error()
// .MinimumLevel.Fatal()

Console Sink

var config = new LoggerConfiguration()
    .WriteTo.Console();  // Uses the global minimum level

// Or specify a minimum level for this sink only
var config = new LoggerConfiguration()
    .WriteTo.Console(LogLevel.Warning);  // Only Warning and above will go to console

File Sink

File sinks are buffered by default for improved performance.

// Basic file sink (buffered by default)
var config = new LoggerConfiguration()
    .WriteTo.File("logs/app.log");

// File sink with custom settings
var config = new LoggerConfiguration()
    .WriteTo.File(
        path: "logs/app.log",
        restrictedToMinimumLevel: LogLevel.Information,
        rollingInterval: RollingInterval.Day,
        fileSizeLimitBytes: 10 * 1024 * 1024,  // 10 MB
        retainedFileCountLimit: 31);  // Keep last 31 files

// File sink with custom buffering settings
var config = new LoggerConfiguration()
    .WriteTo.File(
        path: "logs/app.log",
        restrictedToMinimumLevel: LogLevel.Debug,
        enableBuffering: true,  // Enabled by default
        bufferSize: 500,        // Custom buffer size
        flushInterval: TimeSpan.FromSeconds(3));  // Custom flush interval

// Disable buffering for immediate writes (may impact performance)
var config = new LoggerConfiguration()
    .WriteTo.File(
        path: "logs/app.log",
        enableBuffering: false);  // Disable buffering for immediate writes

Multiple Sinks

var config = new LoggerConfiguration()
    .MinimumLevel.Debug()  // Global minimum
    .WriteTo.Console(LogLevel.Information)  // Console shows Info+
    .WriteTo.File("logs/app.log", LogLevel.Debug)  // File includes Debug+
    .WriteTo.File("logs/errors.log", LogLevel.Error);  // Only Error+

Enriching Log Events

Add custom properties to all log events:

var config = new LoggerConfiguration()
    .Enrich("ApplicationName", "MyApp")
    .Enrich("Environment", "Production")
    .Enrich("Version", "1.0.0");

Logging Examples

Basic Logging

Log.Verbose("This is a verbose message");
Log.Debug("This is a debug message");
Log.Information("This is an information message");
Log.Warning("This is a warning message");
Log.Error("This is an error message");
Log.Fatal("This is a fatal error message");

Structured Logging

// Log with named properties
Log.Information("User {UserId} logged in from {IpAddress}", 123, "192.168.1.1");

// Multiple properties
Log.Information(
    "Order {OrderId} placed for {Amount} items by customer {CustomerId}",
    "ORD-12345",
    42,
    "CUST-789");

Logging Exceptions

try
{
    // Code that might throw
    throw new InvalidOperationException("Something went wrong");
}
catch (Exception ex)
{
    // Log with the exception
    Log.Error(ex, "Failed to process transaction {TransactionId}", "TXN-123");
}

Formatted Values

// Formatting numbers
Log.Information("Amount: {Amount:C}", 123.45);  // Currency
Log.Information("Percentage: {Percent:P}", 0.75);  // Percentage
Log.Information("Hex value: {Value:X}", 255);  // Hex

// Formatting dates
Log.Information("Date: {Date:yyyy-MM-dd}", DateTime.Now);
Log.Information("Time: {Time:HH:mm:ss}", DateTime.Now);

Advanced Usage

Using Named Loggers

RunLog 3.3.0 adds support for named loggers, which allows you to register and retrieve specific logger instances:

// Create a specialized logger for a component
var paymentLogger = new LoggerConfiguration()
    .MinimumLevel.Debug()
    .WriteTo.File("logs/payments.log")
    .Enrich("Component", "PaymentProcessor")
    .CreateLogger();

// Register the logger with a name
Log.RegisterLogger("Payments", paymentLogger);

// Later, in payment processing code:
var logger = Log.GetLogger("Payments");
logger.Information("Processing payment for {OrderId}", "ORD-123");

// Check if a specific logger exists
if (Log.LoggerExists("Payments"))
{
    // Use the logger
}

Creating a Custom Instance

If you need a separate logger instance with different configuration:

// Create a specific logger for a component
var componentLogger = new LoggerConfiguration()
    .MinimumLevel.Debug()
    .WriteTo.File("logs/component.log")
    .Enrich("Component", "PaymentProcessor")
    .CreateLogger();

// Use the component-specific logger
componentLogger.Information("Payment processed for {OrderId}", "ORD-123");

Changing Logger at Runtime

You can replace the global logger at runtime:

// Initial configuration
Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Information()
    .WriteTo.Console()
    .CreateLogger();

// Later, change to a different configuration
Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug()  // More verbose
    .WriteTo.Console()
    .WriteTo.File("logs/app.log")  // Add file logging
    .CreateLogger();

Manual Flushing

While RunLog automatically registers shutdown handlers to flush logs when the application exits, you can manually flush logs at any time if needed:

// Force immediate flush of any buffered logs
Log.CloseAndFlush();

File Rolling Options

RunLog supports various file rolling strategies:

// Roll by time interval
var config = new LoggerConfiguration()
    .WriteTo.File("logs/app.log", rollingInterval: RollingInterval.Day);  // _yyyyMMdd suffix

// Available intervals:
// RollingInterval.Infinite - no rolling
// RollingInterval.Year - _yyyy suffix
// RollingInterval.Month - _yyyyMM suffix
// RollingInterval.Week - _yyyyWww suffix (ISO week)
// RollingInterval.Day - _yyyyMMdd suffix
// RollingInterval.Hour - _yyyyMMddHH suffix
// RollingInterval.Minute - _yyyyMMddHHmm suffix

// Roll by file size (creates numerical sequence)
var config = new LoggerConfiguration()
    .WriteTo.File(
        path: "logs/app.log",
        fileSizeLimitBytes: 1024 * 1024);  // Roll at 1 MB

Combining Size and Time-Based Rolling

var config = new LoggerConfiguration()
    .WriteTo.File(
        path: "logs/app.log",
        rollingInterval: RollingInterval.Day,
        fileSizeLimitBytes: 10 * 1024 * 1024,  // 10 MB
        retainedFileCountLimit: 7);  // Keep 7 files

Performance Considerations

File sinks are now buffered by default for better performance, but you can still adjust the buffering settings:

// High-performance configuration with custom buffer settings
var config = new LoggerConfiguration()
    .MinimumLevel.Information()  // Filter out Debug and Verbose
    .WriteTo.File(
        path: "logs/app.log",
        bufferSize: 1000,  // Buffer up to 1000 log events
        flushInterval: TimeSpan.FromSeconds(2));  // Flush every 2 seconds

For scenarios requiring immediate writes regardless of performance impact:

// Disable buffering for immediate writes
var config = new LoggerConfiguration()
    .WriteTo.File(
        path: "logs/app.log",
        enableBuffering: false);  // Immediate writes

Error Handling

RunLog includes built-in error handling with retry mechanisms and fallback logging:

// The File sink will automatically:
// - Retry file writes up to 3 times with increasing delay
// - Fall back to temporary directory logging if the target location is inaccessible
// - Handle transient file access issues gracefully

Thread Safety

All logging operations in RunLog are thread-safe by default. In version 3.3.0, thread safety has been enhanced with comprehensive synchronization locks:

// These operations can be safely called from multiple threads
Log.Information("Processing request from thread {ThreadId}", Thread.CurrentThread.ManagedThreadId);

Full Example

Here's a comprehensive example showcasing multiple features:

using System;
using System.Threading;
using RunLog;

namespace LoggingDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // Configure the logging system
            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .Enrich("Application", "LoggingDemo")
                .Enrich("Environment", "Development")
                .Enrich("Version", "1.0.0")
                .WriteTo.Console(LogLevel.Information)
                .WriteTo.File(
                    path: "logs/app.log", 
                    restrictedToMinimumLevel: LogLevel.Information,
                    rollingInterval: RollingInterval.Day,
                    fileSizeLimitBytes: 10 * 1024 * 1024,
                    retainedFileCountLimit: 7)
                .WriteTo.File(
                    path: "logs/debug.log",
                    restrictedToMinimumLevel: LogLevel.Debug,
                    rollingInterval: RollingInterval.Day,
                    bufferSize: 100,
                    flushInterval: TimeSpan.FromSeconds(5))
                .CreateLogger();
            
            // Create and register a component-specific logger
            var orderLogger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .WriteTo.File("logs/orders.log")
                .Enrich("Component", "OrderProcessor")
                .CreateLogger();
                
            Log.RegisterLogger("Orders", orderLogger);
            
            try
            {
                Log.Information("Application starting up");
                
                // Simulating application activity
                for (int i = 0; i < 5; i++)
                {
                    Log.Debug("Processing item {ItemIndex}", i);
                    
                    // Use the component-specific logger
                    if (i % 2 == 0)
                    {
                        Log.GetLogger("Orders").Information("Creating order for item {ItemIndex}", i);
                        Log.Information("Successfully processed item {ItemIndex}", i);
                    }
                    else
                    {
                        Log.Warning("Item {ItemIndex} had issues during processing", i);
                    }
                    
                    Thread.Sleep(100);  // Simulate work
                }
                
                // Simulate an error
                try
                {
                    throw new InvalidOperationException("Simulated error for demonstration");
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "Error occurred during processing of {Operation}", "DemoOperation");
                }
                
                Log.Information("Application shutting down normally");
            }
            catch (Exception ex)
            {
                Log.Fatal(ex, "Unexpected error caused application to terminate");
                throw;
            }
            // No need to call Log.CloseAndFlush() as it's automatically handled on application exit
        }
    }
}

Dependencies

RunLog 3.3.0 has no external dependencies. All necessary functionality for date/time calculations, file operations, and thread synchronization is implemented directly within the package.

Changes in 3.3.0

  • Added named logger functionality (RegisterLogger, GetLogger, LoggerExists)
  • Enhanced thread safety with comprehensive synchronization locks
  • Added public Sinks property to Logger class for inspection
  • Improved buffering support with background thread processing
  • Better error handling in FileSink with retry logic for transient IO issues
  • Enhanced message rendering with improved format specifier handling
  • Added fallback error handling for file write operations
  • Improved shutdown handling with enhanced resource disposal
  • Added comprehensive XML documentation comments

Changes in 3.2.0

  • Removed dependency on AaTurpin.Utilities package
  • Implemented internal versions of date/time utilities and file path handling
  • Maintained compatibility with all core logging features

License

MIT

Product Compatible and additional computed target framework versions.
.NET Framework net472 is compatible.  net48 was computed.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETFramework 4.7.2

    • No dependencies.

NuGet packages (4)

Showing the top 4 NuGet packages that depend on RunLog:

Package Downloads
AaTurpin.SleepManager

AaTurpin.SleepManager is a .NET NuGet package that provides a simple interface to control the Windows system's sleep behavior. It allows your applications to temporarily prevent the system from entering sleep mode or turning off the display, which is particularly useful for long-running operations, presentations, or media playback scenarios.

AaTurpin.NetworkFileManager

A robust, high-performance file management library for .NET Framework 4.7.2 applications, specifically designed for reliable file operations across network locations and local storage.

AaTurpin.ConfigManager

A comprehensive .NET library for managing application configuration with support for type-safe settings, directory entries, and network drive mappings. ConfigManager simplifies reading and writing app.config values, handling directories with exclusion paths, and maintaining drive mappings with integrated logging via RunLog.

AaTurpin.NamedPipes

A lightweight, robust .NET Framework library for inter-process communication using Windows named pipes. Simplifies creation of named pipe servers and clients with features including automatic reconnection, thread-safe operations, comprehensive logging integration, and proper resource management. Ideal for building reliable inter-process communication in desktop applications and Windows services.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
3.3.0 145 5/7/2025
3.2.0 150 5/2/2025
3.1.0 140 4/30/2025
3.0.0 140 4/30/2025
2.0.0 83 4/26/2025
1.0.0 142 4/25/2025

RunLog 3.3.0 Release Notes
     New Features:
     * Added named logger functionality (RegisterLogger, GetLogger, LoggerExists)
     * Enhanced thread safety with synchronization locks in Logger class
     * Added public Sinks property to Logger class for inspection
     * Improved buffering support with optional background thread processing
     Improvements:

     Better error handling in FileSink with retry logic for transient IO issues
     Enhanced message rendering with improved format specifier handling
     Added fallback error handling for file write operations
     Improved shutdown handling with enhanced resource disposal
     Added comprehensive XML documentation comments

     Bug Fixes:

     Fixed issues with initialization of collection properties
     Enhanced error handling in CloseAndFlush operations
     Resolved thread safety issues in logging operations

     Compatible:

     Maintains compatibility with .NET Framework 4.7.2
     No breaking changes to core API
     Backward compatible with applications using RunLog 3.2.0