代码之家  ›  专栏  ›  技术社区  ›  Dai

如何使自定义的未处理异常日志记录中间件具有开发人员异常页面,而不具有重复的日志条目?

  •  1
  • Dai  · 技术社区  · 6 年前

    an X/Y problem :

    • 我的ASP.NET核心MVC+Web API应用程序使用其他中间件层,如使用 Microsoft.Extensions.Logging 写入事件(如身份验证失败事件)。
    • 这些事件目前是用最少的额外数据编写的,这使得故障排除变得困难。我想添加HTTP请求头和路径等信息,以帮助识别错误事件的原因。
    • 我的研究使我使用了 ILogger<T>.BeginScope 创建一个“作用域记录器”中间件,该中间件将持续到ASP.NET核心HTTP请求的生命周期。这 using( logger.BeginScope( ... ) ) 调用将存在于中间件中(通过影响DI来工作) ILogger 临时服务)。
    • 我的中间件会 捕获异常本身,仅添加到日志范围,如下所示:

      class LoggingScopeMiddleware {
      
          private readonly ILoggerFactory loggerFactory; // DI
          private readonly RequestDelegate next;
      
          public async Task Invoke( HttpContext context ) {
      
              Dictionary<String,Object> values = CreateDict( context.Request... );
      
              using( this.loggerFactory.CreateLogger().BeginScope( values ) ) {
      
                  this.next( context );
              }
      
          }
      }
      
    • 但是,如果在内部引发异常 next BeginScope . 我原以为这不是命中注定的,但事实确实如此。

    • 我还希望开发者例外页面继续工作
    • LoggingScopeMiddleware.Invoke 方法,则开发人员异常页面不会捕获异常,我只会在浏览器中显示一个空白页面。
    • 如果我在我的 LoggingScopeMiddleware 然后我可以用额外的属性记录它:

      class ExceptionCatchingLoggingScopeMiddleware {
      
          private readonly ILoggerFactory  loggerFactory; // DI
          private readonly RequestDelegate next;
          private readonly Boolean         rethrow = true;
      
          public async Task Invoke( HttpContext context ) {
      
              Dictionary<String,Object> values = CreateDict( context.Request... );
      
              using( this.loggerFactory.CreateLogger().BeginScope( values ) ) {
                  try {
                      this.next( context );
                  } catch( Exception ex ) {
                      LogException( ex );
                      if( this.rethrow ) throw;
                  } 
              }
      
          }
      }
      
      • 但是如果我重新调用它,那么在我控制范围之外的中间件链的其他地方会记录两次异常(并且这个重复的日志条目不包括额外的属性)。如果我使用 when( LogException( ex ) /* == false */ ) C#6.0中的技巧。
      • 如果我吞下异常并且不重新抛出它,那么开发人员异常页面将不会被调用,我将得到空白输出。

    总之:

    • 情景1:

      • 中间件: --> DeveloperExceptionPage --> LoggingScopeMiddleware -->
      • 开发者页面:显示。(好)
      • 未处理的异常:记录时没有作用域属性。(坏)
    • 情景2:

      • 中间件: --> LoggingScopeMiddleware --> DeveloperExceptionPage -->
      • 未处理的异常:记录时没有作用域属性。(坏)
    • 情景3:

      • 中间件: --> ExceptionCatchingLoggingScopeMiddleware --> DeveloperExceptionPage -->
    • 情景4:

      • 中间件: --> DeveloperExceptionPage --> ExceptionCatchingLoggingScopeMiddleware( rethrow: true ) -->
      • 开发者页面:显示。(好)
    • 情景5:

      • 中间件: --> DeveloperExceptionPage --> ExceptionCatchingLoggingScopeMiddleware( rethrow: false ) -->
      • 开发人员页面:未显示。(坏)
    0 回复  |  直到 6 年前