Dependency Injection wth NLog
Asked Answered
C

3

23

I've got a .NET Core web app that I'm trying to add logging to via NLog. In previous projects, I've just used something like the following at the top of every class:

private static Logger logger = LogManager.GetCurrentClassLogger();

I'm trying to follow more best practices with this project where possible.

My question is, how can I inject a logger which has the fully qualified name of the class that it's being injected into?

In my startup.cs file, so far I've got the following:

services.AddScoped<BLL.Logging.NLogLogger>();

At the moment, the NLogLogger class creates an instance of an NLog Logger via GetCurrentClassLogger(), which when used will only ever report the name as "BLL.Logging.NLogLogger" rather than the actual class that the logger is being injected into.

To simplify the question and make it a bit more generic: How can you pass in the name of the class that .NET Core is going to inject a class into?

I've thought about something like the below, but not sure how to achieve it:

services.AddScoped<BLL.Logging.NLogLogger>(serviceProvider => new BLL.Logging.NLogLogger("class name here"));
Coccus answered 8/1, 2017 at 15:20 Comment(1)
You can't. There is no way to do this using the built-in ASP.NET Core container. You will have to use a 'real' DI container for this, like Autofac or Simple Injector.Fireboard
H
19

Using DI specify ILogger<T> type instead of Logger, where T is the class type that uses the logger, so NLog will know about the class. For example:

public class TodoController : Controller
{
    private readonly ILogger _logger;

    public TodoController(ILogger<TodoController> logger)
    {
        _logger = logger;
    }
}

References:

Halliburton answered 8/1, 2017 at 16:27 Comment(5)
Ah great, that's exactly what I needed, it's much cleaner than the solution I came up with before I saw your answer. One thing that bothers me is that the logger.LogError() method forces you to specify an eventID as a paremeter - generally I wouldn't do this and instead use the ${activityid} layout renderer, so for now, I'm just going to set the value to 0 for example - any thoughts/suggestions regarding the eventID?Coccus
@Steviebob u can use this version of method github.com/aspnet/Logging/blob/master/src/… . Moreover, LogError is an extension method, and you are free to define you own version, if you want to use another method signature.Halliburton
@oleksa ILogger<T> is defined in Microsoft.Extensions.Logging, and doesn't relate to Nlog. Check your usings and nuget packages. Look into Introduction to Logging in ASP.NET Core if you are new with logging staff in asp.net core. And feel free to create a new SO question if neededHalliburton
above link is broken. New one: Logging/src/Microsoft.Extensions.Logging.Abstractions/LoggerExtensions.csHalliburton
above link is broken (as well). New one: dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerExtensions.csButterandeggs
C
9

I think I've figured out an acceptable solution, although not exactly the way I asked the question.

First of all, I create a "LoggerFactory", which has a single method called "GetLogger" (which creates the concrete NLog Logger and accepts a class name as a parameter), and I inject this factory instead of the logger directly.

LoggerFactory:

public class LoggerFactory : ILoggerFactory
{
    public ILogger GetLogger(string fullyQualifiedClassName)
    {
        return new NLogLogger(fullyQualifiedClassName);
    }
}

NLogLogger:

public class NLogLogger : ILogger, INLogLogger
{
    NLog.Logger mylogger;
    public NLogLogger(string fullyQualifiedClassName)
    {
        mylogger = NLog.LogManager.GetLogger(fullyQualifiedClassName);
    }
}

Startup.cs:

services.AddScoped<BLL.Logging.ILoggerFactory>();

The in my controller class, I've got:

    private BLL.Logging.ILogger logger;//my NLogLogger inherits this interface

    public HomeController(BLL.Logging.ILoggerFactory loggerFactory)
    {
        logger = loggerFactory.GetLogger(this.GetType().FullName);
    }

So what I've effectively now done, is rather than injecting the actual Logger itself (Which @Steven indicated would not be possible the way I was trying to do it), I've instead injected the utility to create an instance of a logger which wraps NLog.

I've still got the responsibility to create the logger within the class, but at least I've decoupled from the underlying logging framework (NLog).

Coccus answered 8/1, 2017 at 16:23 Comment(2)
This is seriously not what you want! Looks like you have now a reference of NLog in your controller, and you are breaking up the Microsoft Logging interface.Tegular
@SteveRakebrandt The OP is asking for DI of NLog loggers. So as far as answering that question goes this is more of a solution than the accepted answer. And imho there is no upside of sticking with the MS Logging interface. NLog has good features that you want to take advatage of if you opt for that framework and don't want them to be obmitted by having your loggers stuffed in a generic MS interface.Denude
D
0

If you don't want to have a dependency on NLog in your controller (so you depend on Microsoft abstractions instead) :

In your command handler / controller

using Microsoft.Extensions.Logging;

class MyHandler(ILoggerFactory loggerFactory)
{
    public void Handler()
    {
        var logger = loggerFactory.CreateLogger("myCustomLogger");
        logger?.LogDebug("My log");
    }
}

nlog.config

<rules>
  <logger name="myCustomLogger" minlevel="Trace" writeTo="myCustomFile" final="true" /> <!-- final so that myCustomLogger only logs in this rule -->
  <logger name="*" minlevel="Trace" writeTo="logfile" />
</rules>


<targets>
  <target xsi:type="File" name="logfile" fileName="C:\MyApp\logs\log.log"
          layout="${longdate}|${level}|${message} |${all-event-properties} ${exception:format=tostring}" />
  
  <target xsi:type="File" name="myCustomFile" fileName="C:\MyApp\logs\lastHttpCall.log"
          layout="${message}" replaceFileContentsOnEachWrite="true" />
</targets>
Deodorize answered 29/3, 2024 at 5:5 Comment(1)
The standard approach is using Microsoft LoggerFactory (Call AddLogging when empty ServiceCollection), and then add NLog as Logging Provider using AddNLog / UseNLog. See also: github.com/NLog/NLog/wiki/…Romulus

© 2022 - 2025 — McMap. All rights reserved.