Skip to content

Instantly share code, notes, and snippets.

@coenm
Created January 9, 2015 08:03
Show Gist options
  • Save coenm/c997a6f645d9a31acd98 to your computer and use it in GitHub Desktop.
Save coenm/c997a6f645d9a31acd98 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 12, 2017

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

@dandrejvv
Copy link

dandrejvv commented Oct 10, 2018

I noticed that the async Dispose is not a good idea. If too much requests come through eventually you get IO Exceptions. A "safer" way is to only have a method that will do what Dispose does that is also async void but you rather use try / finally block instead of a using block in the Task Invoke(IOwinContext context) method.
You also don't need that GetBody call inside the Dispose method. It just a MemoryStream which is already populated with data from other Middlewares so calling it will make 0 difference.

@propellingbits
Copy link

@dandrejvv - Please never use async void

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