Disclaimer: OzaLog is not related to NLog (jkowalski's package). This is a separate, independent library.
Migrating from
Ozakboy.NLOGv2.x? See Migration Guide. The previous package has been deprecated and renamed toOzaLog.
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.
- Simplest possible API —
LOG.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)
- ❌ Not a
Microsoft.Extensions.Loggingprovider — does not integrate withILogger<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.
- .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).
dotnet add package OzaLogOr via Package Manager Console:
Install-Package OzaLogusing 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;
});
});{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.
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.txttry { /* 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.
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.
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):
| 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 |
| 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
MIT License
- GitHub Issues: Report Issues
- Pull Requests: Contribute Code