1

I built my API to accept instances of an ILogger instead of an ILogger after reading:

Simple Injector: Register ILogger<T> by using ILoggerFactory.CreateLogger<T>()

and this:

Should I take ILogger, ILogger<T>, ILoggerFactory or ILoggerProvider for a library?

And I've been using SimpleInjector to correctly inject the correct typed ILogger automagically. SimpleInjectors .AddLogging method just makes that work. But you can also register the ILogger like this:

container.RegisterConditional(
    typeof(ILogger),
    c => typeof(LogManager<>).MakeGenericType(c.Consumer.ImplementationType),
    Lifestyle.Singleton,
    _ => true);

where LogManager looks something like this:

public class LogManager<T> : ILogger<T> {
    private ILogger _logger;

    public LogManager(ILoggerFactory factory) {
        _logger = factory.CreateLogger<T>();
    }
    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) {
        _logger.Log(logLevel, eventId, state, exception, formatter);
    }
}

And maybe there's a class defined like this that I want to register:

public class MyFactory {
    public ILogger Logger { get; }

    public MyFactory(ILogger logger) {
        Logger = logger;
    }
}

Is there a way to use Autofac to also automagically inject the desired typed ILogger MyFactory> instance into the constructor?

UPDATE:

I tried this this morning:

        containerBuilder.Register((c, p) => {
            var operation = (IResolveOperation)c.GetType().GetProperty("Operation").GetValue(c);
            Type serviceType = (Type)operation.InitiatingRequest.Service.GetType().GetProperty("ServiceType").GetValue(operation.InitiatingRequest.Service);
            var loggerFactory = c.Resolve<ILoggerFactory>();
            var theLogger = (Microsoft.Extensions.Logging.ILogger)Activator.CreateInstance(typeof(LogManager<>).MakeGenericType(serviceType));
            theLogger.GetType().GetMethod("SetLogger").Invoke(theLogger, new object[] { loggerFactory });
            theLogger.BeginScope(new Dictionary<string, object> {
                {"OrderNumber", 12345 },
                {"User", "theUser" }
            });
            return theLogger;
        })
        .As<Microsoft.Extensions.Logging.ILogger>()
        .InstancePerDependency();

I noticed while debugging that I could find the class that was requesting the ILogger and had to use reflection to get at the type. I had to add an empty constructor and method to set the actual logger instance on my LogManager class. The result at the moment is possibly success? I checked some log output and found the following:

{"@t":"2021-10-28T15:27:33.5373547Z","@mt":"I'm a warning","@l":"Warning","SourceContext":"OTH.Shared.SetProblematic","OrderNumber":12345,"User":"theUser"}

Looks like what I was trying to achieve. SetProblematic's constructor requests an ILogger which using some crazy reflection I built an instance of my LogManager class and got that injected instead.

Is what I came up with here necessary or is there a better way to ensure the correct "SourceContext" gets logged along with a common set of properties for the unit of work?

nitewulf50
  • 530
  • 1
  • 4
  • 17
  • Any reason you're not just using the Autofac [.NET Core integration](https://autofac.readthedocs.io/en/latest/integration/netcore.html) so you can register logging the standard `services.AddLogging()` way and have Autofac as the backing container? – Travis Illig Oct 29 '21 at 13:45
  • I'm implementing this in a .NET Framework library so I'm not positive if that makes a difference. I did (previously) read that example for .NET Core and have used that as a template. – nitewulf50 Oct 29 '21 at 18:07

0 Answers0