Enigma.Logging.NLog
1.0.0
dotnet add package Enigma.Logging.NLog --version 1.0.0
NuGet\Install-Package Enigma.Logging.NLog -Version 1.0.0
<PackageReference Include="Enigma.Logging.NLog" Version="1.0.0" />
<PackageVersion Include="Enigma.Logging.NLog" Version="1.0.0" />
<PackageReference Include="Enigma.Logging.NLog" />
paket add Enigma.Logging.NLog --version 1.0.0
#r "nuget: Enigma.Logging.NLog, 1.0.0"
#:package Enigma.Logging.NLog@1.0.0
#addin nuget:?package=Enigma.Logging.NLog&version=1.0.0
#tool nuget:?package=Enigma.Logging.NLog&version=1.0.0
Enigma.Logging.NLog
Enigma.Logging.NLog builds NLog-backed
Microsoft.Extensions.Logging
loggers through a small fluent builder. You add one or more NLog targets, then build a disposable
handle that owns the underlying LogFactory and hands you a ready-to-use ILogger.
Built on NLog 6. The handle implements the shared
Enigma.Logging.Abstractions contract
(ILoggerHandle / IFlushableLoggerHandle), so consuming code can stay provider-neutral.
What's new in 1.0 — first release of the Enigma.Logging umbrella: NLog-backed MEL loggers via a fluent target builder + DI helper. See the release notes.
Install
dotnet add package Enigma.Logging.NLog
Quick start
Add targets, then build a logger. The Build* methods return a LoggerHandle — dispose it at
application shutdown so buffered targets are flushed (see Flush & dispose).
using Enigma.Logging.NLog;
using var handle = new LoggerProviderBuilder()
.AddColoredConsoleTarget("console",
config => config.Layout = LoggerProviderBuilder.DefaultLayout)
.AddFileTarget("file",
config =>
{
config.Layout = LoggerProviderBuilder.DefaultLayout;
config.FileName = "Logs/app.log";
})
.BuildLogger("myLogger");
ILogger logger = handle.Logger;
logger.LogInformation("Hello from Enigma.Logging.NLog");
LoggerProviderBuilder.DefaultLayout is a ready-made layout including timestamp, level, message,
thread id, call site, GC memory, and exception details.
Getting loggers from the handle
LoggerHandle gives you three ways to obtain loggers, all routed through the same owned
NLogLoggerProvider:
// The logger built with the name passed to BuildLogger(...)
ILogger logger = handle.Logger;
// A categorized logger by type — category is typeof(T).FullName
ILogger<MyService> typed = handle.CreateLogger<MyService>();
// The underlying ILoggerFactory, e.g. for a category name
ILogger byName = handle.Factory.CreateLogger("Custom.Category");
Factory, CreateLogger<T>, and Flush throw ObjectDisposedException once the handle has been
disposed. Logger keeps returning its cached reference and does not throw.
Available target extensions
Each extension takes a target name, a config delegate for the NLog target, and the optional
minLevel / maxLevel / loggerNamePattern parameters described below:
| Method | NLog target |
|---|---|
AddConsoleTarget |
ConsoleTarget |
AddColoredConsoleTarget |
ColoredConsoleTarget |
AddFileTarget |
FileTarget |
AddDatabaseTarget |
DatabaseTarget |
AddDebugSystemTarget |
DebugSystemTarget |
For any other NLog target type, use the generic methods directly:
builder.AddTarget(myTarget, minLevel, maxLevel, loggerNamePattern);
builder.AddTargetWithConfiguration(myTarget, config, minLevel, maxLevel, loggerNamePattern);
Database target example
DatabaseTarget lives in the NLog.Database package on NLog 6; this package already references it,
so no extra install is needed.
using var handle = new LoggerProviderBuilder()
.AddDatabaseTarget("db",
config =>
{
// Table structure for SQLite:
// CREATE TABLE "LOGS" (
// "Id" INTEGER,
// "Timestamp" TEXT,
// "Level" TEXT,
// "Message" TEXT,
// "ThreadId" INTEGER,
// "Callsite" TEXT,
// "GCTotalMemory" INTEGER,
// "Exception" TEXT,
// PRIMARY KEY("Id" AUTOINCREMENT)
// );
config.KeepConnection = false;
config.DBProvider = "System.Data.SQLite.SQLiteConnection, System.Data.SQLite";
config.ConnectionString = "Data Source=C:\\Temp\\logs.db;Version=3;";
config.CommandText = "INSERT INTO LOGS (Timestamp, Level, Message, ThreadId, Callsite, GCTotalMemory, Exception) VALUES (@Timestamp, @Level, @Message, @ThreadId, @Callsite, @GCTotalMemory, @Exception)";
config.Parameters.Add(new DatabaseParameterInfo { Name = "@Timestamp", Layout = "${longdate}" });
config.Parameters.Add(new DatabaseParameterInfo { Name = "@Level", Layout = "${level:uppercase=true}" });
config.Parameters.Add(new DatabaseParameterInfo { Name = "@Message", Layout = "${message}" });
config.Parameters.Add(new DatabaseParameterInfo { Name = "@ThreadId", Layout = "${threadid}" });
config.Parameters.Add(new DatabaseParameterInfo { Name = "@Callsite", Layout = "${callsite:className=false:fileName=true:includeSourcePath=false:methodName=true}" });
config.Parameters.Add(new DatabaseParameterInfo { Name = "@GCTotalMemory", Layout = "${gc:property=TotalMemory}" });
config.Parameters.Add(new DatabaseParameterInfo { Name = "@Exception", Layout = "${exception:format=ToString}" });
})
.BuildLogger("myLogger");
Building from an NLog config file
To build from an existing NLog XML configuration instead of adding targets in code, use
BuildLoggerFromConfigFile. It loads the file into the handle's own factory (targets added in code
are ignored on this path):
using var handle = new LoggerProviderBuilder()
.BuildLoggerFromConfigFile("myLogger", "nlog.config");
handle.Logger.LogInformation("Configured from file");
Log level semantics
minLevel (default Trace) and maxLevel (default Critical) bound the
Microsoft.Extensions.Logging.LogLevel range a target receives — they map to an NLog level range on
the target's rule. So minLevel: LogLevel.Warning, maxLevel: LogLevel.Error sends a target only
Warning and Error entries. The mapping is:
LogLevel |
NLog level |
|---|---|
Trace |
Trace |
Debug |
Debug |
Information |
Info |
Warning |
Warn |
Error |
Error |
Critical |
Fatal |
None |
Off |
Passing LogLevel.None maps to NLog's Off; using it as a bound effectively disables the target's
rule.
Logger name scoping
By default a target's rule matches every logger name ("*"). Pass loggerNamePattern to scope a
target to specific logger names using NLog wildcard
syntax:
new LoggerProviderBuilder()
// Only loggers whose name starts with "MyApp.Payments" write to this target
.AddFileTarget("payments", config => config.FileName = "Logs/payments.log",
loggerNamePattern: "MyApp.Payments.*")
.BuildLogger("MyApp.Payments.Processor");
Flush & dispose
LoggerHandle owns the underlying NLog LogFactory. Buffered targets (async/file/database) may hold
entries in memory, so you must release the handle to avoid losing logs:
Dispose()— flushes buffered entries, then shuts the factory down and releases its targets. Idempotent (safe to call more than once). Prefer ausingstatement, or dispose it explicitly at application shutdown.Flush()— pushes buffered entries to their targets without tearing the logger down, for when you want a checkpoint but keep logging afterwards. This is theIFlushableLoggerHandlemember.
using var handle = new LoggerProviderBuilder()
.AddFileTarget("file", config => config.FileName = "Logs/app.log")
.BuildLogger("myLogger");
handle.Logger.LogInformation("buffered entry");
handle.Flush(); // optional: force it out now
// handle disposed here → final flush + shutdown
Single-use builder
A builder is single-use: add every target first, then call one Build* method exactly once. After
a logger is built the builder is sealed — any further AddTarget / AddTargetWithConfiguration call,
or a second Build* call, throws InvalidOperationException. Calling BuildLogger with no targets
added also throws InvalidOperationException. To build another logger, create a new
LoggerProviderBuilder.
All public methods throw ArgumentNullException for null arguments (name, target, config,
configFilePath, loggerNamePattern).
Thread safety
The built ILogger and the NLog LogFactory behind the handle are thread-safe for logging — share
the handle's loggers freely across threads. The LoggerProviderBuilder itself is not designed for
concurrent use: it is a single-use, add-then-build object, so configure and build it on one thread
before sharing the resulting loggers.
Using with Microsoft.Extensions.DependencyInjection
AddNLogLoggerBuilder(name, configure) builds the logger and wires it into the container in one call:
using Enigma.Logging.NLog;
services.AddNLogLoggerBuilder("MyApp", builder => builder
.AddColoredConsoleTarget("console",
config => config.Layout = LoggerProviderBuilder.DefaultLayout)
.AddFileTarget("file",
config =>
{
config.Layout = LoggerProviderBuilder.DefaultLayout;
config.FileName = "Logs/app.log";
}));
What you get:
ILogger<T>injection routes through NLog — the built provider is registered in theMicrosoft.Extensions.Loggingpipeline, so ordinary constructor injection just works:public sealed class OrderService(ILogger<OrderService> logger) { /* … */ }The handle is resolvable as
ILoggerHandleandIFlushableLoggerHandle(the same singleton instance), for direct access toLogger,Factory,CreateLogger<T>(), andFlush().The container owns the handle's lifetime — disposing the service provider (application shutdown) disposes the handle, which flushes buffered targets and shuts NLog down. No manual
usingneeded.
Prefer the standalone builder when the logger must outlive, or exist without, a DI container.
Supported frameworks
netstandard2.0 · net8.0 · net10.0
On netstandard2.0 and net8.0 the effective Microsoft.Extensions.Logging floor is 8.0.0; on
net10.0 it is 10.0.0 — both resolved transitively from NLog.Extensions.Logging.
Related packages
Enigma.Logging.Abstractions— the shared handle contract this package implements (readme).Enigma.Logging.Serilog— the same builder → handle shape on Serilog (readme).- Enigma.Logging on GitHub — the umbrella readme.
License
Licensed under the MIT License.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. 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 is compatible. 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 is compatible. 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. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Enigma.Logging.Abstractions (>= 1.0.0)
- NLog (>= 6.1.4)
- NLog.Database (>= 6.0.3)
- NLog.Extensions.Logging (>= 6.1.4)
-
net10.0
- Enigma.Logging.Abstractions (>= 1.0.0)
- NLog (>= 6.1.4)
- NLog.Database (>= 6.0.3)
- NLog.Extensions.Logging (>= 6.1.4)
-
net8.0
- Enigma.Logging.Abstractions (>= 1.0.0)
- NLog (>= 6.1.4)
- NLog.Database (>= 6.0.3)
- NLog.Extensions.Logging (>= 6.1.4)
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.0.0 | 76 | 7/31/2026 |
Enigma.Logging.NLog 1.0.0 — initial release. Build NLog-backed Microsoft.Extensions.Logging loggers with a fluent target builder and a disposable, flushable LoggerHandle (console/colored-console/file/database/debug targets, level-range + logger-name-pattern gating, IServiceCollection DI helper). Supersedes the retired NLogLoggerBuilder package (new identity, no upgrade path). See RELEASENOTES.md for the full details.