Implementing Microsoft.Extensions.Logging.ILogger with NLog
Thu, 25 Feb 2021 00:00 UTC by garethbrown
I’ve designed this ILogger
implementation to solve an issue I encountered today where the code I was working was wasn’t logging errors when the error occurred in a background thread. It was failing silently because unless you configure NLog specifically to throw exceptions, it won’t.
The principle issue in my case was that the usual ILogger<T>
instance instantiated in the class constructor wasn’t available because the controller object on the main thread had disposed before the thread had completed. Fair enough. I then tried creating an instance of ILogger<T>
via a service locator within the thread. And that failed silently too (I’m not sure of what was going on internally here but I’m wondering if it’s to do with the Logger being typed to the class which is disposed, would need to check the framework source code here).
So I opted to create a class that implements Microsoft.Extensions.Logging.ILogger
, and register it as a singleton that can be used everywhere and will not be disposed with the container object.
So we loose the capabilities of ILogger<T>
which would allow the logger to record information about the type, but to me this is a simplification and has practical benefits when working with async code.
The code
Package references that may be required depending on the .NET core framework version you are targeting.
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.1.5" />
<PackageReference Include="NLog" Version="4.5.10" />
<PackageReference Include="NLog.Web.AspNetCore" Version="4.7.0" />
The NLogLogger class it’s self
using Microsoft.Extensions.Logging;
using NLog;
using System;
namespace ANamespace
{
public class NLogLogger : Microsoft.Extensions.Logging.ILogger
{
private NLog.ILogger NLogLoggerInstance { get; }
private class DisposableStub : IDisposable
{
public void Dispose()
{
// Do nothing
}
}
public NLogLogger()
{
NLogLoggerInstance = LogManager.LoadConfiguration("NLog.config").GetCurrentClassLogger();
}
public IDisposable BeginScope<TState>(TState state)
{
// Not implementing this feature
return new DisposableStub();
}
public bool IsEnabled(Microsoft.Extensions.Logging.LogLevel logLevel)
{
if (logLevel == Microsoft.Extensions.Logging.LogLevel.Trace)
{
return NLogLoggerInstance.IsTraceEnabled;
}
else if (logLevel == Microsoft.Extensions.Logging.LogLevel.Debug)
{
return NLogLoggerInstance.IsDebugEnabled;
}
else if (logLevel == Microsoft.Extensions.Logging.LogLevel.Information)
{
return NLogLoggerInstance.IsInfoEnabled;
}
else if (logLevel == Microsoft.Extensions.Logging.LogLevel.Warning)
{
return NLogLoggerInstance.IsWarnEnabled;
}
else if (logLevel == Microsoft.Extensions.Logging.LogLevel.Error)
{
return NLogLoggerInstance.IsErrorEnabled;
}
else if (logLevel == Microsoft.Extensions.Logging.LogLevel.Critical)
{
return NLogLoggerInstance.IsFatalEnabled;
}
else if (logLevel == Microsoft.Extensions.Logging.LogLevel.None)
{
return false;
}
else
{
throw new InvalidOperationException($"Unhandled type of {nameof(Microsoft.Extensions.Logging.LogLevel)}");
}
}
public void Log<TState>(Microsoft.Extensions.Logging.LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (logLevel == Microsoft.Extensions.Logging.LogLevel.Trace)
{
NLogLoggerInstance.Trace(formatter(state, exception));
}
else if (logLevel == Microsoft.Extensions.Logging.LogLevel.Debug)
{
NLogLoggerInstance.Debug(formatter(state, exception));
}
else if (logLevel == Microsoft.Extensions.Logging.LogLevel.Information)
{
NLogLoggerInstance.Info(exception, formatter(state, exception));
}
else if (logLevel == Microsoft.Extensions.Logging.LogLevel.Warning)
{
NLogLoggerInstance.Warn(exception, formatter(state, exception));
}
else if (logLevel == Microsoft.Extensions.Logging.LogLevel.Error)
{
NLogLoggerInstance.Error(exception, formatter(state, exception));
}
else if (logLevel == Microsoft.Extensions.Logging.LogLevel.Critical)
{
NLogLoggerInstance.Fatal(exception, formatter(state, exception));
}
else if (logLevel == Microsoft.Extensions.Logging.LogLevel.None)
{
NLogLoggerInstance.Info(formatter(state, exception));
}
}
}
}
Registering as a service for dependency injection in your ASP.NET core app:
services.AddSingleton<Microsoft.Extensions.Logging.ILogger>(new ANamespace.NLogLogger());
-
Web Analytics
-
.NET
-
API Versioning and Basic UI Authentication with OpenAPI (Swagger / Swashbuckle) in .NET Core 6
-
Converting Enum Types By Value in C#
-
Implementing Microsoft.Extensions.Logging.ILogger with NLog
-
ASP.NET File Uploader with SignalR Progress Bar and Extended Input Stream Processing
-
How to inject Google Adsense In-Article script into your HTML (ASP.NET Core Razor)
-
Robust Error Handling in ASP.NET Core
-
A Utility Class for Finding Database Deadlocks in .NET Applications
-
Sanitizing HTML in .NET Core
-
Uploading Directly to S3 from Client Using Pre-Signed URLs (JavaScript, .NET)
-
Including Automated Swagger Documentation for API Dependencies
-
API Versioning and Basic UI Authentication with OpenAPI (Swagger / Swashbuckle) in .NET Core 6
-
Principles
-
JavaScript & TypeScript
-
AI
-
Software Architecture
-
General
-
Docker