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

时间:2019-01-26 03:32:40

标签: logging asp.net-core

这里是an X/Y problem

  • 我的ASP.NET Core MVC + Web API应用程序使用了其他中间件层,例如IdentityServer4,它们使用Microsoft.Extensions.Logging来编写事件(例如身份验证失败事件)。
  • 这些事件当前仅使用最少的额外数据进行编写,因此很难进行故障排除。我想添加诸如HTTP请求标头和路径之类的信息,以帮助确定不良事件的原因。
  • 我的研究促使我使用ILogger<T>.BeginScope创建了一个“作用域记录器”中间件,该中间件在ASP.NET Core 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;
                } 
            }
    
        }
    }
    
    • 但是,如果我重新抛出该异常,则该异常会在我的控件之外的中间件链中的其他位置记录两次(并且此重复的日志条目不包括额外的属性)。如果我在C#6.0中使用when( LogException( ex ) /* == false */ )技巧,也会发生这种情况。
    • 如果我吞下该异常并且不重新抛出该异常,那么将不会调用开发人员异常页面,并且我得到空白的输出。

总结:

  • 场景1:

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

    • 中间件:--> LoggingScopeMiddleware --> DeveloperExceptionPage -->
    • 开发人员页面:已显示。 (好)
    • 未处理的异常:记录时没有作用域属性。 (不好)
  • 场景3:

    • 中间件:--> ExceptionCatchingLoggingScopeMiddleware --> DeveloperExceptionPage -->
    • 开发人员页面:已显示。 (好)
    • 未处理的异常:记录时没有作用域属性。 (不好)
  • 方案4:

    • 中间件:--> DeveloperExceptionPage --> ExceptionCatchingLoggingScopeMiddleware( rethrow: true ) -->
    • 开发人员页面:已显示。 (好)
    • 未处理的异常:记录两次,一次具有范围属性(良好),另一次没有范围属性(不良)
  • 场景5:

    • 中间件:--> DeveloperExceptionPage --> ExceptionCatchingLoggingScopeMiddleware( rethrow: false ) -->
    • 开发人员页面:不显示。 (不好)
    • 未处理的异常:使用范围属性(良好)记录一次

0 个答案:

没有答案