iamsingularity
2/9/2017 - 5:27 PM

Owin Middleware Logging

Owin Middleware Logging

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>();

            // ....
        }
    }
}	
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();
        }
    }
}