Skip to content

Instantly share code, notes, and snippets.

@ahmedalejo
Last active October 6, 2024 18:15
Show Gist options
  • Save ahmedalejo/13db39cee504127c75456185518e00eb to your computer and use it in GitHub Desktop.
Save ahmedalejo/13db39cee504127c75456185518e00eb to your computer and use it in GitHub Desktop.
C#: Enable unobtrusive HttpClient request-response logging
//Original share here https://github.com/reactiveui/refit/issues/258#issuecomment-243394076 on Aug 30, 2016
//var httpClient = new HttpClient(new HttpLoggingHandler()){ BaseAddress = /**/};
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
namespace Service.Shared.Clients;
public class HttpLoggingHandler : DelegatingHandler
{
public HttpLoggingHandler(HttpMessageHandler innerHandler)
: base(innerHandler)
{ }
async protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
await Task.Delay(1, cancellationToken).ConfigureAwait(false);
var req = request;
var id = Guid.NewGuid().ToString();
var msg = $"[{id} - Request]";
var uri = req.RequestUri ?? throw new ArgumentNullException(nameof(request), "RequestUri can´t be null");
var scheme = req.RequestUri.Scheme.ToUpperInvariant();
WriteLine($"{msg}========Start==========");
WriteLine($"{msg} {req.Method} {uri.PathAndQuery} {scheme}/{req.Version}");
WriteLine($"{msg} Host: {uri.Scheme}://{uri.Host}");
foreach (var header in req.Headers)
WriteLine($"{msg} {header.Key}: {string.Join(", ", header.Value)}");
if (req.Content != null)
{
foreach (var header in req.Content.Headers)
WriteLine($"{msg} {header.Key}: {string.Join(", ", header.Value)}");
if (req.Content is StringContent || IsTextBasedContentType(req.Headers) || IsTextBasedContentType(req.Content.Headers))
{
var result = await req.Content.ReadAsStringAsync(cancellationToken);
WriteLine($"{msg} Content:");
WriteLine($"{msg} {string.Join("", result.Cast<char>().Take(255))}...");
}
}
var start = DateTime.Now;
var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
var end = DateTime.Now;
WriteLine($"{msg} Duration: {end - start}");
WriteLine($"{msg}==========End==========");
msg = $"[{id} - Response]";
WriteLine($"{msg}=========Start=========");
var resp = response;
WriteLine($"{msg} {scheme}/{resp.Version} {(int)resp.StatusCode} {resp.ReasonPhrase}");
foreach (var header in resp.Headers)
WriteLine($"{msg} {header.Key}: {string.Join(", ", header.Value)}");
if (resp.Content != null)
{
foreach (var header in resp.Content.Headers)
WriteLine($"{msg} {header.Key}: {string.Join(", ", header.Value)}");
if (resp.Content is StringContent || IsTextBasedContentType(resp.Headers) || IsTextBasedContentType(resp.Content.Headers))
{
start = DateTime.Now;
var result = await resp.Content.ReadAsStringAsync(cancellationToken);
end = DateTime.Now;
WriteLine($"{msg} Content:");
WriteLine($"{msg} {string.Join("", result.Cast<char>().Take(255))}...");
WriteLine($"{msg} Duration: {end - start}");
}
}
WriteLine($"{msg}==========End==========");
return response;
}
private static readonly string[] types = new[] { "html", "text", "xml", "json", "txt" };
private static void WriteLine(FormattableString message) =>
Debug.WriteLine(message);
private static bool IsTextBasedContentType(HttpHeaders headers)
{
if (!headers.TryGetValues("Content-Type", out var values))
return false;
var header = string.Join(" ", values).ToLowerInvariant();
return types.Any(t => header.Contains(t));
}
}
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Request]=========Start=========
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Request]========Start==========
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Request] GET / HTTPS/1.1
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Request] Host: https://www.google.com
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Request] Duration: 00:00:00.2298277
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Request]==========End==========
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Response]=========Start=========
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Response] HTTPS/1.1 200 OK
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Response] X-XSS-Protection: 1; mode=block
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Response] X-Frame-Options: SAMEORIGIN
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Response] Alternate-Protocol: 443:quic
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Response] Alt-Svc: quic=":443"; ma=2592000; v="35,34,33,32,31,30"
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Response] Vary: Accept-Encoding
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Response] Transfer-Encoding: chunked
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Response] Accept-Ranges: none
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Response] Cache-Control: max-age=0, private
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Response] Date: Tue, 30 Aug 2016 10:02:32 GMT
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Response] P3P: CP="This is not a P3P policy! See https://www.google.com/support/accounts/answer/151657?hl=en for more info."
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Response] Set-Cookie: NID=85=SAxtO23sG2Xuap7whetoj9Hn353S-wRFCEifyNFeFBIRWkd5fL4h2ws3rgyvk_dfIPbX_5SYqELFHWj4nXO62FxWpC3eeurykZEZQtI-EO9KsQDWpju9SaQ3XY9xwUa7; expires=Wed, 01-Mar-2017 10:02:32 GMT; path=/; domain=.google.com.br; HttpOnly
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Response] Server: gws
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Response] Content-Type: text/html; charset=ISO-8859-1
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Response] Expires: -1
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Response] Content:
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Response] <!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="pt"><head><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"><meta content="/images/branding/googleg/1x/googleg_standard_color_128dp.png" itemprop="image"><title>...
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Response] Duration: 00:00:00.0503445
[83544d2e-6f8b-4e6c-9827-a5e58557f99c - Response]==========End==========
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment