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?