7

Note: Updated with suggested improvements, closer but still not quite there!

Similar to this question - Passing in the type of the declaring class for NLog using Autofac - I am trying to inject NLog instances into my repository classes.

Interface:

public interface ILogger<T>
    where T: class
{
    ...
}

Implementation:

public class NLogger<T> : ILogger<T>
    where T: class
{
    private readonly Logger _logger;

    public NLogger()
    {
        _logger = LogManager.GetLogger(typeof(T).FullName);
    }

    public void Debug(string message)
    {
        _logger.Debug(message);
    }

    ...
}

Registered with Autofac as:

builder.RegisterGeneric(typeof (NLogger<>)).As(typeof (ILogger<>));

Using breakpoints, I see that Autofac is creating a bunch of ILogger/NLogger's with the correct types for all of my various repositories, but the resulting logs show up with callsite being "NLog.LoggerImpl.Write".

Thanks for any help!


Working solution with generics:

public class NLogger<T> : ILogger<T>
    where T: class
{
    private readonly Logger _logger;

    public NLogger()
    {
        _logger = LogManager.GetLogger(typeof(T).FullName);
    }

    public void Debug(string message)
    {
        _logger.Log(typeof(T), new LogEventInfo(LogLevel.Debug, _logger.Name, message));
    }
Community
  • 1
  • 1
Jeremy
  • 388
  • 1
  • 4
  • 12
  • Highly related question: http://stackoverflow.com/questions/7412156/how-to-retain-callsite-information-when-wrapping-nlog – nemesv Feb 20 '14 at 21:19
  • Read that, but I'm not trying to pass the class name in as a constructor, trying to do this with generics. Similar to the 3 year old answer from bentayloruk on http://stackoverflow.com/questions/6623431 – Jeremy Feb 20 '14 at 21:35
  • 1
    You still need to do what is described in the linked question: so you need to change your Debug method to : `public void Debug(string message) { _logger.Log(typeof(NLogger), new LogEventInfo(LogLevel.Debug, _logger.Name,message)); }` to make the `${callsite}` work correctly. – nemesv Feb 20 '14 at 21:48
  • Sadly even with this, ${callsite} is showing up as NLog.LoggerImpl.Write – Jeremy Feb 21 '14 at 16:41

2 Answers2

6

Logger wrappers need to call .Log() and pass additional info for callsite to work. For example:

_logger.Log(typeof (NLogger<T>), new LogEventInfo(LogLevel.Debug, _logger.Name, null, format, args));

EDIT: Since you're still having trouble, here's how I do it, and I know it's behaving correctly:

public interface ILog
{
    [StringFormatMethodAttribute("format")]
    void Debug(string format, params object[] args);
    [StringFormatMethodAttribute("format")]
    void Info(string format, params object[] args);
    [StringFormatMethodAttribute("format")]
    void Warn(string format, params object[] args);

    [StringFormatMethodAttribute("format")] 
    void Error(string format, params object[] args);
    void Error(Exception ex);

    [StringFormatMethodAttribute("format")]
    void Error(Exception ex, string format, params object[] args);

    [StringFormatMethodAttribute("format")]
    void Fatal(Exception ex, string format, params object[] args);
}

public class NLogLogger : ILog
{
    private readonly Logger _log;

    public NLogLogger(Type type)
    {
        _log = LogManager.GetLogger(type.FullName);
    }

    public void Debug(string format, params object[] args)
    {
        Log(LogLevel.Debug, format, args);
    }

    public void Info(string format, params object[] args)
    {
        Log(LogLevel.Info, format, args);
    }

    public void Warn(string format, params object[] args)
    {
        Log(LogLevel.Warn, format, args);
    }

    public void Error(string format, params object[] args)
    {
        Log(LogLevel.Error, format, args);
    }

    public void Error(Exception ex)
    {
        Log(LogLevel.Error, null, null, ex);
    }

    public void Error(Exception ex, string format, params object[] args)
    {
        Log(LogLevel.Error, format, args, ex);
    }

    public void Fatal(Exception ex, string format, params object[] args)
    {
        Log(LogLevel.Fatal, format, args, ex);
    }

    private void Log(LogLevel level, string format, object[] args)
    {
        _log.Log(typeof (NLogLogger), new LogEventInfo(level, _log.Name, null, format, args));
    }

    private void Log(LogLevel level, string format, object[] args, Exception ex)
    {
        _log.Log(typeof (NLogLogger), new LogEventInfo(level, _log.Name, null, format, args, ex));
    }
}

public class LoggingModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder
            .Register((c, p) => new NLogLogger(p.TypedAs<Type>()))
            .AsImplementedInterfaces();
    }

    protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
    {
        registration.Preparing +=
            (sender, args) =>
            {
                var forType = args.Component.Activator.LimitType;

                var logParameter = new ResolvedParameter(
                    (p, c) => p.ParameterType == typeof(ILog),
                    (p, c) => c.Resolve<ILog>(TypedParameter.From(forType)));

                args.Parameters = args.Parameters.Union(new[] { logParameter });
            };
    }
}
Jim Bolla
  • 8,265
  • 36
  • 54
  • 1
    Close but needs to be typeof(NLogger) to work, thanks Jim and nemesv this made my day! – Jeremy Feb 21 '14 at 15:51
  • Well crap, even with this, callsite is still coming through as "NLog.LoggerImpl.Write" – Jeremy Feb 21 '14 at 16:37
  • I don't know why that is. I edited my answer to show how I use it in my project, complete with Autofac module. I avoid the generic altogether. – Jim Bolla Feb 21 '14 at 16:43
  • Ah-hah, got it --- had to use "_logger.Log(typeof(T), ..." instead of "_logger.Log(typeof(NLogger), ..." in my case. Thanks for all the help and more detailed example! – Jeremy Feb 21 '14 at 16:56
  • @JimBolla, I'm trying to implement your autofac solution to the letter and I'm stuck on the "p.TypedAs()" call, which keeps throwing "Sequence is empty" exceptions. Looking at the autofac source, I see that ParameterExtensions.TypedAs() ultimately constrains where T : ConstantParameter (TypedParameter, NamedParameter), while ResolvedParameter only inherits from Parameter. How are you getting this logic to work when ResolvedParameter doesn't seem to work with TypedAs()? – Jason Castellano Oct 19 '17 at 18:20
  • Found the problem I was having and leaving this comment up in case others may find it useful. Please refer to the following (https://stackoverflow.com/questions/19778170/explicit-resolving-of-ilog-in-autofac-when-using-with-log-injection-module) for resolution. – Jason Castellano Oct 20 '17 at 14:36
0

For those using ILogger from Microsoft.Extensions.Logging namespace together with NLog.Extensions.Logging Nuget package.

public class NLoggerModule : Module
{
    private readonly NLogLoggerProvider _provider;

    public NLoggerModule()
    {
        _provider = new NLogLoggerProvider();
    }

    protected override void Load(ContainerBuilder builder)
    {
        builder.Register(CreateLogger).AsImplementedInterfaces();
    }

    private ILogger CreateLogger(IComponentContext c, IEnumerable<Parameter> p)
    {
        var logger = _provider.CreateLogger(p.TypedAs<Type>().FullName);
        return logger;
    }

    protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
    {
        registration.Preparing += Registration_Preparing;
    }

    private void Registration_Preparing(object sender, PreparingEventArgs args)
    {
        var forType = args.Component.Activator.LimitType;

        var logParameter = new ResolvedParameter(
            (p, c) => p.ParameterType == typeof(ILogger),
            (p, c) => c.Resolve<ILogger>(TypedParameter.From(forType)));

        args.Parameters = args.Parameters.Union(new[] { logParameter });
    }
}
Mikhail Orlov
  • 2,789
  • 2
  • 28
  • 38