Search code examples
.netdependency-injectionnlog

Use custom async targets with NLog and dependency inject HttpClient


I am using a custom async target that writes logs to Splunk with NLog (5.0.1.) in my .NET 6 application. I want to leverage IHttpClientFactory in ASP.NET Core.

In my startup.cs, under ConfigureServices(), I register my new target and NLog configuration file.

Target.Register<SplunkTarget>("Splunk");
LogManager.LoadConfiguration("NLog.config");

When I try to inject the IHttpClientFactory into my target class's instructor like this:

private readonly IHttpClientFactory _clientFactory;
public SplunkTarget(IHttpClientFactory clientFactory)
{
     _clientFactory = clientFactory;
}

I'm unable to start the application because I get the error:

Exception thrown: 'System.MissingMethodException' in System.Private.CoreLib.dll: 'Cannot dynamically create an instance of type 'MyNamespace.SplunkTarget'. Reason: No parameterless constructor defined.'

How do I use the built-in .NET Core HttpFactory so that I don't have to manually create a Httpclient?


Solution

  • With NLog 5.0 then you can resolve dependencies during initialization:

    public class MyCustomTarget : AsyncTaskTarget
    {
        protected override void InitializeTarget()
        {
            // ResolveService will throw if dependency is not available,
            // and target-initialization will retry when dependency is available
            var clientFactory = ResolveService<IHttpClientFactory>();
        }
    }
    

    If the target must work at startup, then you must register the singleton-interface upfront:

    NLog.LogManager.Setup().SetupExtensions(ext => ext.RegisterSingletonService<IHttpClientFactory>(clientFactory));
    

    But you are also allowed to post-pone the registration until UseNLog() or AddNLog() are called, and the Microsoft ServiceProvider is registered as external dependency-injection-provider (All LogEvents generated until that point will be cached and released on target-initialization success).

    See also: https://github.com/NLog/NLog/wiki/Dependency-injection-with-NLog

    P.S. Before NLog 5.0 then one could override the factory-method like this, and perform the dependency injection:

    var defaultConstructor = ConfigurationItemFactory.Default.CreateInstance;
    
    ConfigurationItemFactory.Default.CreateInstance.CreateInstance = type =>
    {
       if (type == typeof(MyCustomTarget))
           return new MyCustomTarget(myCustomParameter);
       
       return defaultConstructor(type);
    };