Skip to content

Instantly share code, notes, and snippets.

@unseensenpai
Last active January 19, 2024 16:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save unseensenpai/691e8fc2a053e2c93a882d269c251956 to your computer and use it in GitHub Desktop.
Save unseensenpai/691e8fc2a053e2c93a882d269c251956 to your computer and use it in GitHub Desktop.
Request Response Logging Middleware in .Net 8
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
WebApplication app = builder.Build();
app.UseRequestResponseLogging();
app.Run();
using Core.Commons.Logging;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Internal;
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.Extensions.DependencyInjection
{
public class RequestResponseLoggingMiddleware
{
private readonly RequestDelegate _next;
public RequestResponseLoggingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
// LOG REQUEST
Logger.Info(await FormatRequest(context.Request));
var originalBodyStream = context.Response.Body;
using (var responseBody = new MemoryStream())
{
context.Response.Body = responseBody;
// WAIT FOR RESPONSE
await _next(context);
// LOG RESPONSE
Logger.Info(await FormatResponse(context.Response));
if (context.Response.StatusCode != 204)
{
await responseBody.CopyToAsync(originalBodyStream);
}
}
}
/// <summary>
/// Returns the destination path in the Http request, the query if any, and the first 100 characters in the body if any, as a string to log.
/// </summary>
/// <param name="request">Http request instance</param>
/// <returns></returns>
private static async Task<string> FormatRequest(HttpRequest request)
{
try
{
request.EnableBuffering();
var body = request.Body;
var buffer = new byte[Convert.ToInt32(request.ContentLength)];
await request.Body.ReadAsync(buffer, 0, buffer.Length);
var bodyAsText = Encoding.UTF8.GetString(buffer);
body.Seek(0, SeekOrigin.Begin);
request.Body = body;
return $"{request.Scheme}://{request.Host}{request.Path}" +
(!string.IsNullOrEmpty(request.QueryString.ToString()) ? $" - QUERY: {request.QueryString}" : string.Empty) +
(!string.IsNullOrEmpty(bodyAsText) ? (bodyAsText.Length < 100 ? $" - BODY: {bodyAsText}" : $" - BODY: {bodyAsText[..98]}..") : string.Empty);
}
catch (Exception ex)
{
return $"ERROR: {ex.Message} - METHOD: RequestResponseLoggingMiddleware.FormatRequest()";
}
}
/// <summary>
/// Returns the status code from the http response and the first 100 characters in the body if any, as a string to log.
/// </summary>
/// <param name="response">Outgoing side of Http request instance</param>
/// <returns></returns>
private static async Task<string> FormatResponse(HttpResponse response)
{
try
{
response.Body.Seek(0, SeekOrigin.Begin);
string text = await new StreamReader(response.Body).ReadToEndAsync();
response.Body.Seek(0, SeekOrigin.Begin);
return $"Response {response.StatusCode}" +
(!string.IsNullOrEmpty(text) ? (text.Length < 100 ? $" - BODY: {text}" : $" - BODY: {text[..98]}..") : string.Empty);
}
catch (Exception ex)
{
return $"ERROR: {ex.Message} - METHOD: RequestResponseLoggingMiddleware.FormatResponse()";
}
}
}
}
using Microsoft.AspNetCore.Builder;
namespace Microsoft.Extensions.DependencyInjection
{
public static class RequestResponseLoggingMiddlewareExtensions
{
public static IApplicationBuilder UseRequestResponseLogging(this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestResponseLoggingMiddleware>();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment