1515using System ;
1616using System . Collections . Generic ;
1717using System . Diagnostics ;
18+ using System . Linq ;
1819using System . Threading . Tasks ;
1920using Microsoft . AspNetCore . Http ;
2021using Microsoft . AspNetCore . Http . Features ;
@@ -26,18 +27,19 @@ namespace Serilog.AspNetCore
2627{
2728 class RequestLoggingMiddleware
2829 {
29- // We may, at some future point, wish to allow customization of the template used in completion events.
30- const int MessageTemplatePlaceholderCount = 4 ;
31- static readonly MessageTemplate MessageTemplate =
32- new MessageTemplateParser ( ) . Parse ( "HTTP {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms" ) ;
33-
3430 readonly RequestDelegate _next ;
3531 readonly DiagnosticContext _diagnosticContext ;
32+ readonly MessageTemplate _messageTemplate ;
33+ readonly int _messageTemplatePlaceholderCount ;
3634
37- public RequestLoggingMiddleware ( RequestDelegate next , DiagnosticContext diagnosticContext )
35+ public RequestLoggingMiddleware ( RequestDelegate next , DiagnosticContext diagnosticContext , RequestLoggingOptions options )
3836 {
37+ if ( options == null ) throw new ArgumentNullException ( nameof ( options ) ) ;
3938 _next = next ?? throw new ArgumentNullException ( nameof ( next ) ) ;
4039 _diagnosticContext = diagnosticContext ?? throw new ArgumentNullException ( nameof ( diagnosticContext ) ) ;
40+
41+ _messageTemplate = new MessageTemplateParser ( ) . Parse ( options . MessageTemplate ) ;
42+ _messageTemplatePlaceholderCount = _messageTemplate . Tokens . OfType < PropertyToken > ( ) . Count ( ) ;
4143 }
4244
4345 // ReSharper disable once UnusedMember.Global
@@ -46,19 +48,28 @@ public async Task Invoke(HttpContext httpContext)
4648 if ( httpContext == null ) throw new ArgumentNullException ( nameof ( httpContext ) ) ;
4749
4850 var start = Stopwatch . GetTimestamp ( ) ;
51+
4952 var collector = _diagnosticContext . BeginCollection ( ) ;
5053 try
5154 {
5255 await _next ( httpContext ) ;
5356 var elapsedMs = GetElapsedMilliseconds ( start , Stopwatch . GetTimestamp ( ) ) ;
54- var statusCode = httpContext . Response ? . StatusCode ;
57+ var statusCode = httpContext . Response . StatusCode ;
5558 LogCompletion ( httpContext , collector , statusCode , elapsedMs , null ) ;
5659 }
57- // Never caught, because `LogCompletion()` returns false.
58- catch ( Exception ex ) when ( LogCompletion ( httpContext , collector , 500 , GetElapsedMilliseconds ( start , Stopwatch . GetTimestamp ( ) ) , ex ) ) { }
60+ catch ( Exception ex )
61+ // Never caught, because `LogCompletion()` returns false. This ensures e.g. the developer exception page is still
62+ // shown, although it does also mean we see a duplicate "unhandled exception" event from ASP.NET Core.
63+ when ( LogCompletion ( httpContext , collector , 500 , GetElapsedMilliseconds ( start , Stopwatch . GetTimestamp ( ) ) , ex ) )
64+ {
65+ }
66+ finally
67+ {
68+ collector . Dispose ( ) ;
69+ }
5970 }
6071
61- static bool LogCompletion ( HttpContext httpContext , DiagnosticContextCollector collector , int ? statusCode , double elapsedMs , Exception ex )
72+ bool LogCompletion ( HttpContext httpContext , DiagnosticContextCollector collector , int statusCode , double elapsedMs , Exception ex )
6273 {
6374 var level = statusCode > 499 ? LogEventLevel . Error : LogEventLevel . Information ;
6475
@@ -67,14 +78,14 @@ static bool LogCompletion(HttpContext httpContext, DiagnosticContextCollector co
6778 if ( ! collector . TryComplete ( out var properties ) )
6879 properties = new List < LogEventProperty > ( ) ;
6980
70- properties . Capacity = properties . Count + MessageTemplatePlaceholderCount ;
81+ properties . Capacity = properties . Count + _messageTemplatePlaceholderCount ;
7182
7283 // Last-in (rightly) wins...
7384 properties . Add ( new LogEventProperty ( "RequestMethod" , new ScalarValue ( httpContext . Request . Method ) ) ) ;
7485 properties . Add ( new LogEventProperty ( "RequestPath" , new ScalarValue ( GetPath ( httpContext ) ) ) ) ;
7586 properties . Add ( new LogEventProperty ( "StatusCode" , new ScalarValue ( statusCode ) ) ) ;
7687 properties . Add ( new LogEventProperty ( "Elapsed" , new ScalarValue ( elapsedMs ) ) ) ;
77- var evt = new LogEvent ( DateTimeOffset . Now , level , ex , MessageTemplate , properties ) ;
88+ var evt = new LogEvent ( DateTimeOffset . Now , level , ex , _messageTemplate , properties ) ;
7889 Log . Write ( evt ) ;
7990
8091 return false ;
0 commit comments