【问题标题】:How do I log an exception from ConfigureServices in Startup in an ASP.NET Core 3.0 application如何在 ASP.NET Core 3.0 应用程序的启动中记录来自 ConfigureServices 的异常
【发布时间】:2021-06-29 18:58:25
【问题描述】:

根据this answer,我们无法解析记录器以记录来自StartupConfigureServices 方法的消息或异常。这对我来说是个问题,因为我在ConfigureServices 中有代码,如果缺少所需的设置,它会抛出异常。有没有其他方法可以记录该异常?

【问题讨论】:

    标签: .net asp.net-core


    【解决方案1】:

    ConfigureServices 方法中的异常可以从两个不同的来源引发:

    1. 来自ConfigureServices 方法代码本身。
    2. 从工厂方法传递到 DI 容器。

    例子:

    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;
            }                
        });
    

    【讨论】:

      【解决方案2】:

      是的,有一种方法可以记录从ConfigureServices 引发的异常。这只是一个小技巧。也许这不是一个可怕的黑客。由你决定。

      ConfigureServicesConfigure 之前运行。我们可以将记录器注入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* 上捕获异常并通过程序级别的外部记录器进行记录?这是一种有趣的方法/技巧。
      • 我得看看你指的是什么。也许您可以提供不同的答案。我对我的游戏并不兴奋,这就是为什么我称它为 hack。但它很容易阅读和理解。在配置日志记录之前,我需要在应用程序中准确记录一个点。
      猜你喜欢
      • 2019-11-04
      • 1970-01-01
      • 2019-05-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多