Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save iamsingularity/266adb31acd68c9fad297d16ace75316 to your computer and use it in GitHub Desktop.
Save iamsingularity/266adb31acd68c9fad297d16ace75316 to your computer and use it in GitHub Desktop.
Owin Middleware Logging
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Owin;
using NLog; // in this example, we use NLog
namespace net.github.gist.coenm
{
public class LoggingMiddleware : OwinMiddleware
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public LoggingMiddleware(OwinMiddleware next) : base(next)
{ }
public override async Task Invoke(IOwinContext context)
{
LogRequestResponseHelper.LogDebugRequest(Logger, context.Request);
var responseBody = "";
if(Logger.IsTraceEnabled) //use trace for logging the response
{
using (var captureResponseBody = new CaptureResponseBody(context))
{
await Next.Invoke(context);
responseBody = await captureResponseBody.GetBody();
}
}
else
await Next.Invoke(context);
LogRequestResponseHelper.LogDebugResponse(Logger, context.Response);
if (Logger.IsTraceEnabled
&& !string.IsNullOrEmpty(context.Response.ContentType) && context.Response.ContentType.ToLower().StartsWith("application/json"))
LogRequestResponseHelper.LogTraceResponse(Logger, responseBody);
}
private class CaptureResponseBody : IDisposable
{
// Response body is a write-only network stream by default for Katana hosts.
// You will need to replace context.Response.Body with a MemoryStream,
// read the stream, log the content and then copy the memory stream content
// back into the original network stream
private readonly Stream stream;
private readonly MemoryStream buffer;
public CaptureResponseBody(IOwinContext context)
{
stream = context.Response.Body;
buffer = new MemoryStream();
context.Response.Body = buffer;
}
public async Task<string> GetBody()
{
buffer.Seek(0, SeekOrigin.Begin);
var reader = new StreamReader(buffer);
return await reader.ReadToEndAsync();
}
public async void Dispose()
{
await GetBody();
// You need to do this so that the response we buffered
// is flushed out to the client application.
buffer.Seek(0, SeekOrigin.Begin);
await buffer.CopyToAsync(stream);
}
}
}
}
using System;
using Microsoft.Owin;
using NLog; // Using NLog in this example
namespace com.github.gist.coenm
{
public static class LogRequestResponseHelper
{
public static void LogDebugResponse(Logger logger, IOwinResponse response)
{
if (!logger.IsDebugEnabled)
return;
MappedDiagnosticsContext.Clear();
MappedDiagnosticsContext.Set("response.StatusCode", response.StatusCode.ToString());
logger.Debug(String.Format("Response statuscode:'{0}'.", response.StatusCode));
MappedDiagnosticsContext.Clear();
}
public static void LogTraceResponse(Logger logger, string body)
{
if (!logger.IsTraceEnabled)
return;
logger.Trace("Response body: {0}", body);
}
public static void LogDebugRequest(Logger logger, IOwinRequest request)
{
if (!logger.IsDebugEnabled)
return;
MappedDiagnosticsContext.Clear();
MappedDiagnosticsContext.Set("request.MediaType", request.MediaType);
MappedDiagnosticsContext.Set("request.Host", request.Host.ToString());
MappedDiagnosticsContext.Set("request.ContentType", request.ContentType);
MappedDiagnosticsContext.Set("request.Scheme", request.Scheme);
MappedDiagnosticsContext.Set("request.Method", request.Method);
MappedDiagnosticsContext.Set("request.Path", request.Path.ToString());
MappedDiagnosticsContext.Set("request.QueryString", request.QueryString.ToString());
MappedDiagnosticsContext.Set("request.Accept", request.Accept);
var logMsg = string.Format("Request scheme:'{0}'; method:'{1}'; path:'{2}'; query:'{3}'; accept:'{4}'",
request.Scheme,
request.Method,
request.Path,
request.QueryString,
request.Accept);
logger.Debug(logMsg);
MappedDiagnosticsContext.Clear();
}
}
}
using System.Linq;
using System.Net.Http.Formatting;
using System.Web.Http;
using Owin;
namespace com.github.gist.coen
{
public class Startup
{
private readonly Container container = new Container();
public void Configuration(IAppBuilder appBuilder)
{
// ....
appBuilder.Use<LoggingMiddleware>();
// ....
}
}
}
Copy link

ghost commented Sep 26, 2017

Hi, great work. Helped a lot to resolve my problem. But one question:
LoggingMiddleware.cs Line 64, why do we need this inside Dispose method?

Copy link

ghost commented Sep 26, 2017

One more note: When used in a download scenario, reader.ReadToEndAsync (LoggingMiddleware line 59) reads the whole file downloaded into memory. This can fail with OutOfMemoryException if the file to download is big of size, e. g. 500 MB.

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