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
                    
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="Enigma.Logging.NLog" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Enigma.Logging.NLog" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Enigma.Logging.NLog" />
                    
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 Enigma.Logging.NLog --version 1.0.0
                    
#r "nuget: Enigma.Logging.NLog, 1.0.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.
#:package Enigma.Logging.NLog@1.0.0
                    
#: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=Enigma.Logging.NLog&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Enigma.Logging.NLog&version=1.0.0
                    
Install as a Cake Tool

Enigma.Logging.NLog

NuGet License: MIT

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 LoggerHandledispose 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 a using statement, 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 the IFlushableLoggerHandle member.
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 the Microsoft.Extensions.Logging pipeline, so ordinary constructor injection just works:

    public sealed class OrderService(ILogger<OrderService> logger) { /* … */ }
    
  • The handle is resolvable as ILoggerHandle and IFlushableLoggerHandle (the same singleton instance), for direct access to Logger, Factory, CreateLogger<T>(), and Flush().

  • 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 using needed.

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.

License

Licensed under the MIT License.

Product 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. 
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.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.