Skip to content

NLog GetCurrentClassLogger and Microsoft ILogger

Rolf Kristensen edited this page Mar 11, 2023 · 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 contructors. 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 contructor-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("ExtraLog"). But for Microsoft ILogger then one must request ILoggerFactory as input:

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

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

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

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.