NLog.Web: Could not create instance of custom target (when using dependency injection)

I have an ASP.NET Core 2.0 web application. In the Main method of the app, when I build WebHostBuilder I use .UseNLog() method to enable NLog (after .UseStartup but I’ve tested it also whe .UseNLog() was before .UseStartup). I have the following custom target:

    [Target("RemoteNLogTarget")]
    public class RemoteNLogTarget : TargetWithLayout
    {
        protected ILogStorage LogStorage;

        public RemoteNLogTarget(ILogStorage logStorage)
        {
            LogStorage = logStorage;
        }

        protected override void Write(LogEventInfo logEvent)
        {
            LogStorage.Store(Layout.Render(logEvent));
        }
    }

ILogStorage is the following:

public interface ILogStorage
    {
        void Store(string logMessage);
    }

I have implementation of ILogStorage it registered in services (as scoped) in my Startup.

In Startup.Configure method I have the following code, which should enable me to use custom targets with dependency injection as per: https://stackoverflow.com/a/42101946/1955346

 var builtInProvider = ConfigurationItemFactory.Default.CreateInstance;
            ConfigurationItemFactory.Default.CreateInstance = type =>
            {
                return serviceProvider.GetService(type) ?? builtInProvider(type);
            };
            Target.Register<RemoteNLogTarget>("RemoteNLogTarget");

Finally, my nlog.config:

<extensions>
   <add assembly="MyAssembly"/>
</extensions>
<targets>
      <target xsi:type="RemoteNLogTarget" name="RemoteNLogTarget"
            layout="${pad:fixedLength=True:padding=-5:${uppercase:${level}}} | ${longdate} | ${processtime} | ${logger} | ${message} | ${exception:format=toString,Data}"
            />
</targets>

Regardless of having <extensions> declared or not, regardless of having Target.Register<> called or not, I always end up with the exception :

Cannot access the constructor of type: RemoteNLogTarget. Is the required permission granted?

from: https://github.com/NLog/NLog/blob/fd6e91a2daed54570a8e89cc2575e0e11158343f/src/NLog/Internal/FactoryHelper.cs#L57

It seems to me like setting CreateInstance does not work there. Fun fact is that when I initialize my application, my custom DI method is actually called (and obviously returns proper instance of my RemoteNLogTarget), but the exception is thrown anyway.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 30 (18 by maintainers)

Most upvoted comments

I have made these modifications to my app and it has corrected the internal nlog errors for me. Thanks for the good write up!

@fuzzzerd The combination of dynamic logging configuration and dependency injection can give a catch22. You want to have logging up and running early, but this will fail if dependent on custom objects that requires a dependency injection provider is fully loaded.

The “ugly” work-around to this catch22 is to have two constructors for your custom-objects:

  • Default Constructor that initializes an object in disabled state (Ignored by dependency injection provider)
  • Specialized Constructor that initializes an object in working state.

Alternative override the CreateInstance-method as first thing (Before creating first Logger-object or loading NLog config). To call the specialized constructor with parameters that signals that it should be in disabled state.

Then one can “just” perform a reload of the NLog config, after the the dependency injection provider has been fully loaded and performed final override of CreateInstance. This will make NLog recreate all configuration items once again, and now with a working dependency injection provider that calls the specialized constructor with correct parameters.

NLog.LogManager.Configuration = NLog.LogManager.Configuration.Reload();

Also created a wiki-page: https://github.com/NLog/NLog/wiki/Dependency-injection-with-NLog

@304NotModified still having errors logged. I’m not 100% confident its a bug in NLog, as it could be that I’m not doing something correctly.

My problem appears to be related to the fact that I have a custom Target defined in my nlog.config and when I call .UseNLog() in program.cs main method it loads that configuration file and the target is not registered yet.

If I move my call of

NLog.Targets.Target.Register("NLogCustomTarget", typeof(NLogCustomTarget)); // register our custom target

ahead of the .UseNLog() call I get the same error OP has here, that the constructor is not accessible, presumably because I am injecting dependencies into the target constructor so it knows where/how to write log entries.

If there is a better/different way to register custom targets with dependency injection I’d love to learn more about it.

Like I said, what I have does work in the sense my target is called and can write log data, but it causes an internal nlog error as well as asp.net core sdtout errors on startup.