Skip to content

ozakboy/OzaLog

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

76 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

OzaLog

nuget github

English | 繁體中文

Disclaimer: OzaLog is not related to NLog (jkowalski's package). This is a separate, independent library.

Migrating from Ozakboy.NLOG v2.x? See Migration Guide. The previous package has been deprecated and renamed to OzaLog.

Changelog: English · 繁體中文

A lean, lightweight .NET local file logging library with the simplest possible static API. No DI, no LoggerFactory, zero NuGet dependencies on net8+. Designed for high-throughput multi-threaded scenarios such as cryptocurrency tick streams.

Why OzaLog

  • Simplest possible APILOG.Info_Log("hello") works without any setup
  • Single purpose — local file logging only, no abstractions
  • Zero dependencies — net8+ targets have no NuGet dependencies
  • HFT-tuned hot path — persistent FileStream pool with LRU eviction, cached timestamp, struct-based queue, drop-oldest backpressure
  • Faster than NLog and Serilog in HFT-style multi-thread scenarios (see Benchmarks below)

What OzaLog is NOT

  • ❌ Not a Microsoft.Extensions.Logging provider — does not integrate with ILogger<T> DI
  • ❌ Not a structured logger — no {Property} placeholder capture
  • ❌ Not a multi-target logger — file output only (no Console / Database / remote sinks)
  • ❌ Not configurable via XML / appsettings.json — programmatic configuration only

If any of the above is a hard requirement, use NLog or Serilog instead.

Supported Frameworks

  • .NET 8.0 / 9.0 / 10.0 (LTS + current)
  • .NET Standard 2.0 / 2.1 (legacy compatibility)

Dropped in OzaLog v3.0: .NET Framework 4.6.2, .NET 6.0, .NET 7.0 (all EOL).

Installation

dotnet add package OzaLog

Or via Package Manager Console:

Install-Package OzaLog

Quick Start

using OzaLog;

LOG.Info_Log("Hello, World!");
LOG.Error_Log("Something went wrong");
LOG.CustomName_Log("BTC", "tick: 67890.12");

That's it — no Configure call required (defaults work). For advanced configuration:

LOG.Configure(o =>
{
    o.KeepDays = -7;                          // keep last 7 days of logs
    o.SetFileSizeInMB(50);                    // split files at 50 MB
    o.EnableAsyncLogging = true;              // default true
    o.EnableConsoleOutput = true;             // also write to console
    o.MaxOpenFileStreams = 100;               // LRU upper bound
    o.DiskFlushIntervalMs = 100;              // periodic flush
    o.EnableGlobalExceptionCapture = false;   // opt-in: auto-log unhandled exceptions
    o.OnDropped = () => Interlocked.Increment(ref _dropCount);
    o.ConfigureAsync(a =>
    {
        a.MaxBatchSize = 1000;
        a.MaxQueueSize = 100_000;
        a.FlushIntervalMs = 100;
    });
});

Log File Layout

{AppRoot}/
└── logs/
    └── 20260509/                 # yyyyMMdd date folder
        └── LogFiles/             # type subfolder (configurable)
            ├── Info_Log.txt
            ├── Error_Log.txt
            ├── BTC_Log.txt       # CustomName logs go here
            └── ETH_Log.txt

Use options.LogPath to change the root, and options.TypeDirectories.*Path to give each level its own folder.

Logging Methods

Every level has the same 5 overloads:

LOG.Info_Log(string message);
LOG.Info_Log(string message, bool writeTxt);
LOG.Info_Log(string message, string[] args, bool writeTxt = true, bool immediateFlush = false);
LOG.Info_Log<T>(T obj, bool writeTxt = true, bool immediateFlush = false) where T : class;
LOG.Info_Log<T>(string message, T obj, bool writeTxt = true, bool immediateFlush = false) where T : class;

Available levels: Trace_Log / Debug_Log / Info_Log / Warn_Log / Error_Log / Fatal_Log.

For custom log buckets:

LOG.CustomName_Log("BTC", "tick: 67890.12");        // → BTC_Log.txt
LOG.CustomName_Log("API", "external call");         // → API_Log.txt

Exception Logging

try { /* code */ }
catch (Exception ex)
{
    LOG.Error_Log(ex);                            // serialized as JSON
    LOG.Error_Log("operation context", ex);       // with custom message
}

Exception details (Type, Message, StackTrace, InnerException, Data dictionary, additional properties) are automatically captured.

Global Exception Capture (opt-in)

LOG.Configure(o => o.EnableGlobalExceptionCapture = true);

Subscribes to AppDomain.UnhandledException and TaskScheduler.UnobservedTaskException and logs them as Fatal with synchronous flush to ensure crash logs land on disk.

Note: This does not cover WPF/WinForms UI thread exceptions or ASP.NET Core middleware exceptions — those need to be hooked separately by the application.

Benchmarks

Measured against ZLogger 2.5.10, ZeroLog 2.6.1, Serilog 4.2.0 + Sinks.File 6.0.0 on .NET 10.0.7 (AMD Ryzen 9 9950X3D, BenchmarkDotNet 0.14):

S1 — Single short message

Method Mean Allocated
OzaLog 65.96 ns 151 B
ZLogger 219.53 ns 278 B
ZeroLog 12.19 ns 0 B
Serilog 168.87 ns 160 B

S3 — HFT 8 thread × 50 products × 2000 logs = 800 K writes

Method Mean Allocated
OzaLog 3,047 μs 3.94 MB
ZLogger 4,996 μs 5.21 KB
ZeroLog 649 μs 3.35 KB
Serilog 11,092 μs 8.27 MB

Verdict: OzaLog is faster than ZLogger and Serilog in both scenarios. ZeroLog wins on raw speed (it uses source generators for true zero-allocation), but OzaLog's static API is simpler.

→ Run benchmarks yourself: dotnet run -c Release --project OzaLog.Benchmarks

License

MIT License

Support

Packages

 
 
 

Contributors