简体   繁体   中英

How can I resolve the correct logger type using AutoFac?

I'm updating a legacy project that uses AutoFac and I want to use NLog with Simple Logging Facade (SLF)

I have used this in the past with Ninject and it's really easy to setup, I just have to do something like:

kernel.Bind<ILogger>().ToMethod(x => LoggerFactory.GetLogger(x.Request.Target.Member.ReflectedType));

The output would be something like:

NLogNinjectSlf.Services.MyService 2013-12-30 15:21:10.5782 DEBUG Log from injected Logger

Piece of cake

But now I have to use AutoFac and I don't know how to get the Target type that needs the logger

For example if I have the following interface/class:

public interface IMyService
{
    void DoSomething();
}

public class MyService : IMyService
{
    private readonly ILogger _logger;

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

    public void DoSomething()
    {
        _logger.Debug("Log from injected Logger");
    }
}

I want to be able to get the type of MyService class to use it as the name of my logger

In AutoFac this is what I have tried so far:

var containerBuilder = new ContainerBuilder();

containerBuilder.RegisterType<MyService>().As<IMyService>();

containerBuilder.Register(x =>
{
    // TODO: Get the correct type
    return LoggerFactory.GetLogger(x.GetType());
}).As<ILogger>();

BTW: I'm using NLog behind the SLF4Net not really needed to solve the main issue though...

Thanks nemesv that helped me a lot

This is the code that I ended up using

BTW. You can remove the code that inject properties if you wish, and then just use DI in all your classes to inject the ILogger that would increase the performance

public class LoggingModule : Module
{
    protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry,
        IComponentRegistration registration)
    {
        registration.Preparing += OnComponentPreparing;

        registration.Activated += (sender, e) => InjectLoggerProperties(e.Instance);
    }

    private static void InjectLoggerProperties(object instance)
    {
        var instanceType = instance.GetType();

        // Get all the injectable properties to set.
        // If you wanted to ensure the properties were only UNSET properties,
        // here's where you'd do it.
        var properties = instanceType
          .GetProperties(BindingFlags.Public | BindingFlags.Instance)
          .Where(p => p.PropertyType == typeof(ILogger) && p.CanWrite && p.GetIndexParameters().Length == 0);

        // Set the properties located.
        foreach (var propToSet in properties)
        {
            propToSet.SetValue(instance, LoggerFactory.GetLogger(instanceType), null);
        }
    }

    private void OnComponentPreparing(object sender, PreparingEventArgs e)
    {
        var t = e.Component.Activator.LimitType;

        e.Parameters = e.Parameters.Union(
            new[]
            {
                new ResolvedParameter((p, i) => p.ParameterType == typeof (ILogger),
                    (p, i) => LoggerFactory.GetLogger(t))
            });
    }
}

Then register the module:

containerBuilder.RegisterModule<LoggingModule>();

Seems using Autofac 4.3.0 you can simply use OnComponentPreparing() callback without using special properties injection techniques. Both ctor() and properties' values will be injected:

public class LoggingModule : Module
{
 protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry,
        IComponentRegistration registration)
    {
        registration.Preparing += Registration_Preparing;
    }

private static void Registration_Preparing(object sender, PreparingEventArgs e)
    {
        var t = e.Component.Activator.LimitType;

        e.Parameters = e.Parameters.Union(
            new[]
            {
                new ResolvedParameter(
                    (p, i) => p.ParameterType == typeof (ILog),
                    (p, i) => LogFactory.CreateWithType(t))
            });
    }
}

Also I found some nice trick which might be used for properties injection only when you need to control logger lifetime.

public class LoggingModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        const string propertyNameKey = "Autofac.AutowiringPropertyInjector.InstanceType";

        builder.RegisterType<NLogLogger>()
            .As<ILog>()
            .OnPreparing(x =>
            {
                var firstParam = x.Parameters?
                    .OfType<NamedParameter>()
                    .FirstOrDefault(p => p.Name == propertyNameKey);

                if (null == firstParam)
                {
                    return;
                }

                var valueType = firstParam.Value;
                x.Parameters = x.Parameters.Union(
                    new[]
                    {
                        new ResolvedParameter(
                            (p, i) => p.Name == "type",
                            (p, i) => valueType)
                    });
            })
            .InstancePerDependency();

where NLogLogger itself is

public class NLogLogger : ILog
{
    private readonly Logger _log;

    public NLogLogger()
    {
        _log = LogManager.GetCurrentClassLogger();
    }

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM