Skip to content

Instantly share code, notes, and snippets.

@dazinator
Last active January 31, 2018 15:18
Show Gist options
  • Save dazinator/98b2cf4c99e7182b1e11fc4695cadaed to your computer and use it in GitHub Desktop.
Save dazinator/98b2cf4c99e7182b1e11fc4695cadaed to your computer and use it in GitHub Desktop.
Serilog Middleware - with log on dispose
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;
}
}
@JZfi
Copy link

JZfi commented Jan 31, 2018

public class DelegateDisposable : IDisposable
{
    private readonly Action _disposeAction;
    public DelegateDisposable(Action disposeAction)
    {
        _disposeAction = disposeAction;
    }
    public void Dispose()
    {
        _disposeAction();
    }
}

http://www.lazzycoder.com/snippets/delegate-disposable-c-11

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment