Skip to content

NLog GetCurrentClassLogger and Microsoft ILogger

Rolf Kristensen edited this page Sep 26, 2024 · 29 revisions

Microsoft ILogger Injection

Microsoft ILogger should be injected as input-parameter for the class-constructor. Instead of having NLog Logger as static class variable:

public class MyClass
{
     private readonly ILogger Logger;

     public MyClass(ILogger<MyClass> logger)
     {
         Logger = logger;
     }
}

This introduces some extra noise for all class constructors, which have to be resolved using dependency injection. But it also makes it possible to have multiple Host-applications within the same process. And also gives better isolation when running unit-tests in parallel.

NLog GetCurrentClassLogger finds its Logger-Name using reflection tricks, but the generic Microsoft ILogger<MyClass> extracts the Logger-Name from the generic-type. The Microsoft Dependency Injection has a small memory allocation overhead, as it will always allocate a new class-object for every ILogger<MyClass>-parameter. And you must specify the generic ILogger<T> as constructor-parameter, or else the dependency injection will fail with System.InvalidOperationException: Unable to resolve service for type 'Microsoft.Extensions.Logging.ILogger'.

How to get ILogger with dynamic Logger name ?

If the class wants to generate output to multiple logger-objects, then NLog provides LogManager.GetLogger("BonusLogger"). But for Microsoft ILogger then one must request ILoggerFactory as input:

public class MyClass
{
     private readonly ILogger Logger;
     private readonly ILogger BonusLogger;

     public MyClass(ILogger<MyClass> logger, ILoggerFactory loggerFactory)
     {
         Logger = logger;
         BonusLogger = loggerFactory.CreateLogger("BonusLogger");
     }
}

How to create class instance without ILogger ?

.NET Core 2 introduced NullLogger and NullLoggerFactory so one can do this:

public class MyClass
{
     private readonly ILogger Logger;

     public MyClass(ILogger<MyClass> logger = null)
     {
         Logger = logger ?? NullLogger.Instance;
     }
}

This can be useful in unit-testing scenarios, or for utility-classes where logging output is not always necessary.

How to create local ILogger without dependency injection ?

.NET Core 3 introduced LoggerFactory.Create that can be useful for application startup logging, before the dependency injection system is fully initialized

var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
var logger = loggerFactory.CreateLogger("StartupLogger");
logger.LogInformation("Starting...");

This can also be used for unit-testing scenarios, as each test-scenario can have their own local LoggerFactory.

Maybe consider using Lazy<LoggerFactory> for creating a singleton, and then use it as custom fallback value (instead of NullLoggerFactory).

If just wanting NLog and optimal performance then instead of using LoggerFactory.Create, then use new NLogLoggerFactory.

How to capture properties with ILogger ?

Microsoft ILogger has support for structured logging with support of message-templates:

_logger.LogDebug("Logon from {userid}", request.UserId);

It is also possible to Capture custom LogEvent Properties

Comparison

NLog Pros

Pros of using NLog directly

  • Best performance by remove layer of indirection and allowing deferred formatting.
  • More options with the logger API, e.g. Logger.WithProperty(..)
  • Works in all platforms
  • No Dependency Injection needed which saves complexity.

Pros Microsoft.Extensions.Logging

Pros of using NLog via Microsoft.Extensions.Logging:

  • Fully integrated with ASP.NET Core, e.g. Microsoft also writes to the logger API and that will be also captured (and possible filtered) by NLog
  • Writing to the logging abstraction will make your code log-library independent.
  • Works well with .NET Core Dependency injection
  • Load NLog configuration from appsettings.json, instead of using XML file NLog.config.