Jeff Finn
Jeff Finn

Reputation: 2055

How is NLog binded to ILogger with Ninject & Ninject.Extensions.Logging

Hi I am trying to use NLog with an Web Api 2 application that uses Ninject for dependency injection.

I have struggled to find any concrete info on how to do this so far I have installed the NuGet packages for NLog & NLog.Configuration. So far what I have read suggests that theses packages should just work and take care of the binding for me.

I set up the Nlog.config file and then I installed the Ninject.Extensions.Logging & Ninject.Extensions.NLog4 packages.

I'm trying to use NLog as follows

public class ErrorCheckController : ApiController
{
    private readonly ILogger _logger;

    public ErrorCheckController(ILogger logger)
    {
        _logger = logger;
    }

    public IHttpActionResult Get(string version)
    {
        try
        {
            throw new Exception("Manually thrown exception");
        }
        catch (Exception ex)
        {
            _logger.Debug(ex);
        }

        return Ok();
    }
}

In my NinjectWebCommon I have tried

  1. No Binding
  2. Binding with kernel.Bind<ILogger>().To<Logger>();
  3. Binding with kernel.Bind<ILogger>().ToMethod(lm => LogManager.GetLogger("MyLogger")); where MyLogger is the name of the target configuration

When I do 1 & 2 I get the error

An error occurred when trying to create a controller of type 'ErrorCheckController'. Make sure that the controller has a parameterless public constructor.

The 3rd option gets a Logger but it isn't configured using the NLog.config file

EDIT Here is my config file

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
  autoReload="true"
  throwExceptions="false">

  <targets>
    <target xsi:type="Database" name="Ink3DPowderDbLogger" connectionStringName="MyDbContext">
      <commandText>
        INSERT INTO Log.ApiLog (
    Application,
    Logger,
    Logged,
    Level,
    Environment,
    Request,
    Message,
    BaseDir,
    Message,
    CallSite,
    CallSiteLn,
    Exception,
    EventProperties,
    StackTrace,
    ServerName,
    Port,
    Url,
    Https,
    ServerAddress,
    RemoteAddress,
    ) VALUES (
    @Application,
    @Logger,
    @Logged,
    @Level,
    @Environment,
    @Request,
    @BaseDir,
    @Message,
    @CallSite,
    @CallSiteLine,
    @Exception,
    @EventProperties,
    @StackTrace,
    @ServerName,
    @Port,
    @Url,
    @Https,
    @ServerAddress,
    @RemoteAddress,
    );
  </commandText>

  <parameter name="@application" layout="${appsetting:name=AppName:default=Unknown}" />
  <parameter name="@logger" layout="${logger}" />
  <parameter name="@logged" layout="${date}" />
  <parameter name="@level" layout="${level}" />
  <parameter name="@environment" layout="${environment}" />
  <parameter name="@request" layout="${asp-request}" />
  <parameter name="@basedir" layout="${basedir}" />
  <parameter name="@message" layout="${message}" />
  <parameter name="@callSite" layout="${callsite}" />
  <parameter name="@callSiteLine" layout="${callsite-linenumber}" />
  <parameter name="@exception" layout="${exception:tostring}" />
  <parameter name="@eventproperties" layout="${eventproperties:tostring}" />
  <parameter name="@stacktrace" layout="${stacktrace:tostring}" />
  <parameter name="@serverName" layout="${aspnet-request:serverVariable=SERVER_NAME}" />
  <parameter name="@port" layout="${aspnet-request:serverVariable=SERVER_PORT}" />
  <parameter name="@url" layout="${aspnet-request:serverVariable=HTTP_URL}" />
  <parameter name="@https" layout="${when:inner=1:when='${aspnet-request:serverVariable=HTTPS}' == 'on'}${when:inner=0:when='${aspnet-request:serverVariable=HTTPS}' != 'on'}" />
  <parameter name="@serverAddress" layout="${aspnet-request:serverVariable=LOCAL_ADDR}" />
  <parameter name="@remoteAddress" layout="${aspnet-request:serverVariable=REMOTE_ADDR}:${aspnet-request:serverVariable=REMOTE_PORT}" />

</target>

  </targets>

  <rules>
    <logger name="*" minlevel="Info" writeTo="Ink3DPowderDbLogger" />
  </rules>
</nlog>

EDIT 2 I Have changed my Get Method to

public IHttpActionResult Get(string version)
    {
        IKernel kernel = new StandardKernel();
        //var resolver = new NinjectDependencyResolver(kernel);
        //var logger = (ILogger) resolver.GetService(typeof(ILogger));
        var logger = kernel.Get<ILogger>();

        try
        {
            throw new Exception("Manually thrown exception");
        }
        catch (Exception ex)
        {
            //_logger.Debug(ex);
            logger.Debug(ex);
        }

        return Ok();
    }

This gives me an error

No matching bindings are available, and the type is not self-bindable.

Which sounds like nothing is getting binded to ILogger.

Edit 3

I was looking back over my config file and noticed that I had told NLog to fail silently. I turned this off and now I am getting proper errors. There seems to be an issue with my

I get the errors

Exception: Error when setting property 'Layout' on NLog.Targets.DatabaseParameterInfo

Inner Exception: LayoutRenderer cannot be found: 'appsetting'

I'm not too sure why this can't be found I'm using the same syntax that is in the documentation for appsetting

Upvotes: 1

Views: 2526

Answers (2)

Jeff Finn
Jeff Finn

Reputation: 2055

The problem ended up being that I was trying to Inject

NLog.ILogger

instead of

Ninject.Extensions.Logging.ILogger

It is working now.

Upvotes: 1

Steven
Steven

Reputation: 172646

Make sure that the controller has a parameterless public constructor.

The reason you get this error is because:

  1. you didn't configure your controller explicitly in Ninject
  2. There is an error in your configuration

Because there is no explicit registration for the controller, Ninject will try to create it for you, but since there is problem in the configuration it will return null (which is dictated by the IDependencyResolver contract).

Once you register all your controllers explicitly (you should always register all your root types explicitly in the container), Ninject will throw an expressive exception explaining what the configuration error is. Registering all root types explicitly also allows you to test your DI configuration.

Upvotes: 1

Related Questions