Last active
January 31, 2018 15:18
-
-
Save dazinator/98b2cf4c99e7182b1e11fc4695cadaed to your computer and use it in GitHub Desktop.
Serilog Middleware - with log on dispose
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class SerilogMiddleware | |
{ | |
const string MessageTemplate = | |
"HTTP {RequestMethod} {RequestPath} {InterimStatusCode} status code {StatusCode} in {Elapsed:0.0000} ms"; | |
private readonly ILogger Log; // global::Serilog.Log.ForContext<SerilogMiddleware>(); | |
readonly RequestDelegate _next; | |
public SerilogMiddleware(RequestDelegate next, ILogger logger) | |
{ | |
Log = logger; | |
if (next == null) throw new ArgumentNullException(nameof(next)); | |
_next = next; | |
} | |
public async Task Invoke(HttpContext httpContext) | |
{ | |
if (httpContext == null) throw new ArgumentNullException(nameof(httpContext)); | |
var sw = Stopwatch.StartNew(); | |
try | |
{ | |
await _next(httpContext); | |
sw.Stop(); | |
LogOnDispose(httpContext, sw.Elapsed.TotalMilliseconds, null); | |
} | |
// Never caught, because `LogException()` returns false. | |
catch (Exception ex) when (LogException(httpContext, sw, ex)) { } | |
} | |
private void LogOnDispose(HttpContext httpContext, double elapsedMilliseconds, Exception ex) | |
{ | |
var interimStatusCode = httpContext.Response?.StatusCode; | |
var interimLevel = interimStatusCode > 499 ? LogEventLevel.Error : LogEventLevel.Information; | |
ILogger log = null; | |
bool errorContext = interimLevel == LogEventLevel.Error || ex != null; | |
if (errorContext) | |
{ | |
log = LogForErrorContext(httpContext); | |
} | |
else | |
{ | |
log = Log; | |
} | |
var loggingDisposable = new DelegateDisposable(() => | |
{ | |
var statusCode = httpContext.Response?.StatusCode; | |
var finalLevel = statusCode > 499 ? LogEventLevel.Error : LogEventLevel.Information; | |
if (!errorContext && finalLevel == LogEventLevel.Error) | |
{ | |
log = LogForErrorContext(httpContext); | |
log.Error(ex, MessageTemplate, httpContext.Request.Method, httpContext.Request.Path, interimStatusCode, statusCode, elapsedMilliseconds); | |
} | |
else | |
{ | |
log.Write(finalLevel, MessageTemplate, httpContext.Request.Method, httpContext.Request.Path, interimStatusCode, statusCode, elapsedMilliseconds); | |
} | |
}); | |
httpContext.Response.RegisterForDispose(loggingDisposable); | |
} | |
private bool LogException(HttpContext httpContext, Stopwatch sw, Exception ex) | |
{ | |
sw.Stop(); | |
// var log = LogForErrorContext(httpContext); | |
LogOnDispose(httpContext, sw.Elapsed.TotalMilliseconds, ex); | |
return false; | |
} | |
private ILogger LogForErrorContext(HttpContext httpContext) | |
{ | |
var request = httpContext.Request; | |
var result = Log | |
.ForContext("RequestHeaders", request.Headers.ToDictionary(h => h.Key, h => h.Value.ToString()), destructureObjects: true) | |
.ForContext("RequestHost", request.Host) | |
.ForContext("RequestProtocol", request.Protocol); | |
if (request.HasFormContentType) | |
result = result.ForContext("RequestForm", request.Form.ToDictionary(v => v.Key, v => v.Value.ToString())); | |
return result; | |
} | |
} |
public class DelegateDisposable : IDisposable
{
private readonly Action _disposeAction;
public DelegateDisposable(Action disposeAction)
{
_disposeAction = disposeAction;
}
public void Dispose()
{
_disposeAction();
}
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Where do you have DelegateDisposable defined? I want to use your code but the DelegateDisposable is not defined and I am not smart enough to finish the code. Please help. Thanks.