我正在使用一个利用Simple Injector作为依赖注入器的项目。另一方面,该项目使用Microsoft.Extensions.Logging来记录某些类中发生的事件。
我的技术问题很容易解释。
我想在我的DI中注册ILogger独立于正在调用的类T,但是我需要从我的ILoggerFactory.CreateLogger<T>()
方法执行它,因为这使用{{{}获取记录器配置1}}。
我需要使用类似的东西来实例化我的记录器:
Microsoft.Extensions.Configuration
我可以通过以下方式实现注射:
private Microsoft.Extensions.Logging.ILogger CreateLogger<T>()
{
var factory = this.ResolveService<ILoggerFactory>();
var logger = factory.CreateLogger<T>();
return logger;
}
这使我们可以解决类似的问题:
Container.Register(typeof(ILogger<>), typeof(Logger<>));
但正如我所说,这样做不会通过从public class SomeApiController : ApiController
{
public SomeApiController(ILogger<SomeApiController> logger)
{
//logger is well instantiated, but doesn't got the configuration
logger.LogInformation("test log.");
}
}
类获得的配置,所以这没用。
有没有办法使用我的
Microsoft.Extensions.Logging.ILoggerFactory
注册ILogger<T>
?
答案 0 :(得分:18)
<强> TLDR 强>;使用以下注册:
container.RegisterInstance<ILoggerFactory>(loggerFactory);
container.RegisterSingleton(typeof(ILogger<>), typeof(Logger<>));
CreateLogger<T>()
方法是ILoggerFactory
上的扩展方法,并按以下方式实施(LoggerFactoryExtensions):
public static ILogger<T> CreateLogger<T>(this ILoggerFactory factory)
{
return new Logger<T>(factory);
}
CreateLogger<T>
仅在为工厂供应时创建新的Logger<T>
。换句话说,调用CreateLogger<T>
与手动创建Logger<T>
或让Simple Injector执行此操作具有相同的效果。
因此,给定的注册应解决您的问题:
container.RegisterInstance<ILoggerFactory>(loggerFactory);
container.RegisterSingleton(typeof(ILogger<>), typeof(Logger<>));
但是,不要让应用程序组件依赖ILogger<T>
依赖项,而是让它们依赖于ILogger
。这使您的代码更简单,更容易测试,并且更不容易出错。您可以让Simple Injector确保仍然注入正确的Logger<T>
:
container.RegisterConditional(
typeof(ILogger),
c => typeof(Logger<>).MakeGenericType(c.Consumer.ImplementationType),
Lifestyle.Singleton,
_ => true);
这可确保每个应用程序组件都有自己的Logger<T>
实例,其中T
是注入记录器的组件的类型。以下面的课程为例,取决于ILogger
:
public class ComponentA : IService
{
public ComponentA(ILogger logger) { ... }
}
上述注册将确保ComponentA
注入了Logger<ComponentA>
,即使它只取决于ILogger
而不是ILogger<T>
。
如果您对更加健全的解决方案感兴趣,可以在这里停止阅读,或继续阅读。
您可以选择定义特定于应用程序的记录器抽象,而不是让应用程序组件依赖于框架定义的ILogger
抽象,而不是Dependency Inversion Principle(DIP)规定的。
DIP声明抽象应该由应用程序本身定义 - 这意味着您定义自己的logger abstraction,并在此基础上构建适配器,就像描述here一样。您可以简单地从描述的MicrosoftLoggingAdapter
派生您的通用适配器,如下所示:
public sealed class MicrosoftLoggingAdapter<T> : MicrosoftLoggingAdapter
{
public MicrosoftLoggingAdapter(ILoggerFactory factory)
: base(factory.CreateLogger<T>()) { }
}
使用此通用适配器,您可以按如下方式配置Simple Injector:
container.RegisterInstance<ILoggerFactory>(factory);
container.RegisterConditional(
typeof(MyApplication.Abstractions.ILogger),
c => typeof(MicrosoftLoggingAdapter<>).MakeGenericType(c.Consumer.ImplementationType),
Lifestyle.Singleton,
_ => true);
答案 1 :(得分:9)
根据史蒂文的解决方案,我发布了答案以帮助其他人:
private void RegisterServices()
{
Container.Register(ConfigureLogger, Lifestyle.Singleton);
Container.Register(typeof(ILogger<>), typeof(LoggingAdapter<>));
}
private ILoggerFactory ConfigureLogger()
{
LoggerFactory factory = new LoggerFactory();
var config = new ConfigurationBuilder()
.AddJsonFile("logging.json")
.Build();
//serilog provider configuration
var log = new LoggerConfiguration()
//.ReadFrom.Configuration(config)
.WriteTo
.RollingFile(ConfigSettings.LogsPath)
.CreateLogger();
factory.AddSerilog(log);
return factory;
}
public class LoggingAdapter<T> : ILogger<T>
{
private readonly Microsoft.Extensions.Logging.ILogger adaptee;
public LoggingAdapter(ILoggerFactory factory)
{
adaptee = factory.CreateLogger<T>();
}
public IDisposable BeginScope<TState>(TState state)
{
return adaptee.BeginScope(state);
}
public bool IsEnabled(LogLevel logLevel)
{
return adaptee.IsEnabled(logLevel);
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
adaptee.Log(logLevel, eventId, state, exception, formatter);
}
}
如您所见,我的解决方案是使用Serilog作为登录Microsoft.Extensions.Logging
的提供程序。
希望它有所帮助!