【发布时间】:2021-06-29 18:58:25
【问题描述】:
根据this answer,我们无法解析记录器以记录来自Startup 中ConfigureServices 方法的消息或异常。这对我来说是个问题,因为我在ConfigureServices 中有代码,如果缺少所需的设置,它会抛出异常。有没有其他方法可以记录该异常?
【问题讨论】:
标签: .net asp.net-core
根据this answer,我们无法解析记录器以记录来自Startup 中ConfigureServices 方法的消息或异常。这对我来说是个问题,因为我在ConfigureServices 中有代码,如果缺少所需的设置,它会抛出异常。有没有其他方法可以记录该异常?
【问题讨论】:
标签: .net asp.net-core
ConfigureServices 方法中的异常可以从两个不同的来源引发:
ConfigureServices 方法代码本身。例子:
services.AddScoped<IDependency>(sp =>
{
// Any Exception from here
});
我指出这两种情况是因为我发现它们使用不同的方法解决。
场景 1: 此答案Answer1 中使用的方法相同(使用该启动类,可能会调整在 Configure 方法中处理异常的方式)
场景 2:
这个场景更加困难,因为场景 1 中使用的 try/catch 没有捕捉到工厂方法抛出的异常。但在这些情况下,我们可以访问ServiceProvider,因此我们可以使用类似这样的方式处理每个工厂方法内部的异常。
services.AddScoped<IService>(sp =>
{
var httpContext = sp.GetService<IHttpContextAccessor>().HttpContext;
var logger = sp.GetService<ILogger<IService>>();
try
{
throw new Exception("Text Exception");
}
catch (Exception ex)
{
// log
logger.LogError(ex, ex.Message);
// status error
httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
httpContext.Response.ContentType = "application/json";
await httpContext.Response.WriteAsync(ex.Message);
return null;
}
});
【讨论】:
是的,有一种方法可以记录从ConfigureServices 引发的异常。这只是一个小技巧。也许这不是一个可怕的黑客。由你决定。
ConfigureServices 在Configure 之前运行。我们可以将记录器注入Configure。因此,hack 是抓住我们在ConfigureServices 中抛出的异常,然后记录并从Configure 方法中重新抛出它。
最容易一次看到所有内容,所以这里有一个示例Startup:
public class Startup
{
private Exception _configureServicesException;
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
try
{
// Configure all of your services
}
catch (Exception ex)
{
_configureServicesException = ex;
}
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
{
try
{
if (_configureServicesException != null)
{
ExceptionDispatchInfo.Capture(_configureServicesException).Throw();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}
catch (Exception ex)
{
logger.LogError(
ex,
"Exception thrown when starting application in {Environment} environment",
env.EnvironmentName);
throw;
}
}
}
添加的内容如下:
private Exception _configureServicesException; 是一个字段,用于存储从ConfigureServices 抛出的异常。ConfigureServices 抛出异常,我们会将其存储在该字段中并继续而不重新抛出它。Configure 添加了一个ILogger 参数,以便运行时注入一个记录器。 (稍后会详细介绍。)Configure 时,如果从ConfigureServices 抛出异常,我们将重新抛出它。在此示例中,我们将捕获、记录并重新抛出 Configure 中引发的任何异常,包括我们从 ConfigureServices 中捕获的异常。所有这些都假设我们在此之前已经配置了日志记录。 this Microsoft documentation - "Capture logs within ASP.NET Core startup code" 中详细说明了如何做到这一点。
从该页面复制示例:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.ConfigureLogging((context, builder) =>
{
// Providing an instrumentation key is required if you're using the
// standalone Microsoft.Extensions.Logging.ApplicationInsights package,
// or when you need to capture logs during application startup, for example
// in the Program.cs or Startup.cs itself.
builder.AddApplicationInsights(
context.Configuration["APPINSIGHTS_CONNECTIONSTRING"]);
// Capture all log-level entries from Program
builder.AddFilter<ApplicationInsightsLoggerProvider>(
typeof(Program).FullName, LogLevel.Trace);
// Capture all log-level entries from Startup
builder.AddFilter<ApplicationInsightsLoggerProvider>(
typeof(Startup).FullName, LogLevel.Trace);
});
...关键细节是我们正在配置日志记录,以便运行时可以在我们调用时注入记录器
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
【讨论】:
Build 或主机Run* 上捕获异常并通过程序级别的外部记录器进行记录?这是一种有趣的方法/技巧。