我创建了一个记录器,它应该记录应用程序运行开始时的任何内容。我想让它在Startup
和ConfigureServices
之间保持不变。我将其存储在类似于configuration
的属性中代码
public Startup(IConfiguration configuration)
{
Configuration = configuration;
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder.SetMinimumLevel(LogLevel.Error);
builder.AddEventLog(s =>
{
s.LogName = "MyLogName";
s.SourceName = "MySource";
});
// add some other persisting logger
});
StartupLogger = loggerFactory.CreateLogger<Startup>();
}
public IConfiguration Configuration { get; }
public ILogger StartupLogger { get; } // Logger exists here when breakpoint is in ConfigureServices
public void ConfigureServices(IServiceCollection services)
{
StartupLogger.LogError("Failed to do something"); // <-- throws exception
}
是我得到的错误。看起来内部日志记录器在进程
中被处理掉了消息:"写入记录器时发生错误。无法访问已处置的对象。对象名称:'EventLogInternal'.
完整的堆栈
'StartupLogger.LogError("Failed to do something")'抛出类型为'System '的异常。AggregateException '
数据:{System.Collections。ListDictionaryInternal}
HResult: -2146233088
helpink: null
InnerException: {"无法访问已处置的对象。rnObject name: 'EventLogInternal'."}
InnerExceptions: Count = 1
消息:"写入记录器时发生错误。无法访问已处置的对象。rnObject name: 'EventLogInternal'.)">
来源:" microsoft . extensions . loging& quot;
StackTrace: "在Microsoft.Extensions.Logging.Logger。rn at microsoft . extension . logging . logger . log [TState](LogLevel LogLevel, EventId EventId, TState状态,Exception Exception, Func ' 3 formatter)rn at microsoft . extension . logging . logger ' 1. microsoft . extension . logging . log . log [TState](LogLevel LogLevel, EventId EventId, TState状态,Exception Exception, Func ' 3 formatter)rn at microsoft . extension . logging . loggerextensions log (ILogger logger, LogLevel LogLevel, EventId EventId, EventId EventId, Exception Exception, Exception Exception,字符串消息,Object[] args)rn at Microsoft.Extensions.Logging.LoggerExtensions。LogError(ILogger logger, String message, Object[] args)">
TargetSite:{无效ThrowLoggingError(System.Collections.Generic.List ' 1[System.Exception])}
我肯定用错方法了。谢谢你的指导。
将using声明using var loggerFactory
更改为普通声明-这会导致在作用域结束时处置loggerFactory
(在本例中为Startup
构造函数):
public Startup(IConfiguration configuration)
{
Configuration = configuration;
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.SetMinimumLevel(LogLevel.Error);
builder.AddEventLog(s =>
{
s.LogName = "MyLogName";
s.SourceName = "MySource";
});
// add some other persisting logger
});
StartupLogger = loggerFactory.CreateLogger<Startup>();
}
还请注意,Startup
Configure
方法支持依赖注入,如果您可以将初始化逻辑移到那里-您可以将ILogger<Startup>
注入该方法:
public Startup(IConfiguration configuration)
{
// ...
public void Configure(IApplicationBuilder app, , IWebHostEnvironment env, ILogger<Startup> logger)
{
logger.LogError("Failed to do something");
}
}
你真的需要在ConfigureServices中记录吗?还是需要记录正在更新的选项?
对于任何遵循选项模式的东西(以及所有内置服务),你可以在lambda中配置选项,注入其他服务;
services.AddMvc();
services.AddOptions<MvcOptions>()
.Configure<ILogger<Startup>>((options, logger) => {
options.something = X;
logger.LogInformation($"... {X} ...");
});
注意services.AddMvc(options => {...});
扩展方法在内部也做了类似的事情。
或者通过专用服务执行配置;
services.AddTransient<IConfigureOptions<MvcOptions>, ConfigureMvcOptions>();
public class ConfigureMvcOptions : IConfigureOptions<MvcOptions>{
private readonly ILogger<ConfigureMvcOptions> logger;
public ConfigureMvcOptions(ILogger<ConfigureMvcOptions> logger){
...
}
public void Configure(MvcOptions config)
{
options.something = X;
logger.LogInformation($"... {X} ...");
}
}
或者是一种服务;
services.AddSingleton<ConfigureEverything>();
services.AddSingleton<IConfigureOptions<MvcOptions>>(p => p.GetService<ConfigureEverything>());
services.AddSingleton<IConfigureOptions<SomethingElse>>(p => p.GetService<ConfigureEverything>());
// etc
public class ConfigureMvcOptions :
IConfigureOptions<MvcOptions>,
IConfigureOptions<SomethingElse>,
// etc
显然,这是不灵活的,只是创建一个启动日志。在上述所有情况下,配置方法只在第一次使用时调用,一旦通用主机启动。