Skip to content

Instantly share code, notes, and snippets.

@kiwipiet
Created November 25, 2021 21:56
Show Gist options
  • Save kiwipiet/80f56c80e75c4402779b1d2f404ab631 to your computer and use it in GitHub Desktop.
Save kiwipiet/80f56c80e75c4402779b1d2f404ab631 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public class HttpLoggingHandler : DelegatingHandler
{
private readonly string[] _types = { "html", "text", "xml", "json", "txt", "x-www-form-urlencoded" };
private static readonly RequestResponseLogger _requestResponseLogger = new RequestResponseLogger();
protected override async Task<HttpResponseMessage> SendAsync(
[NotNull] HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (request.RequestUri.PathAndQuery.Contains("swagger"))
{
return await base.SendAsync(
request,
cancellationToken)
.ConfigureAwait(false);
}
var logContents = new List<string>();
logContents.AddContent($"Start Request {request.RequestUri.PathAndQuery}");
if (request.Content != null)
{
if (request.Content is StringContent
|| IsTextBasedContentType(request.Headers)
|| IsTextBasedContentType(request.Content.Headers))
{
var result = await request.Content.ReadAsStringAsync().ConfigureAwait(false);
if (IsJsonContentType(request.Headers)
|| IsJsonContentType(request.Content.Headers))
{
result = JToken.Parse(result).ToString(Formatting.Indented);
}
logContents.AddContent("Request Content:");
logContents.AddContent(result);
}
}
else
{
logContents.AddContent("No Response Content");
}
logContents.AddContent($"End Request {request.RequestUri.PathAndQuery}");
var response = await base.SendAsync(
request,
cancellationToken)
.ConfigureAwait(false);
logContents.AddContent($"Start Response {request.RequestUri.PathAndQuery}");
logContents.AddContent($"StatusCode: {(int)response.StatusCode} ReasonPhrase: {response.ReasonPhrase}");
if (response.Content != null)
{
if (response.Content is StringContent
|| IsTextBasedContentType(response.Headers)
|| IsTextBasedContentType(response.Content.Headers))
{
var result = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (IsJsonContentType(response.Headers)
|| IsJsonContentType(response.Content.Headers))
{
result = JToken.Parse(result).ToString(Formatting.Indented);
}
logContents.AddContent("Response Content:");
logContents.AddContent(result);
}
}
else
{
logContents.AddContent("No Response Content");
}
logContents.AddContent($"End Response {request.RequestUri.PathAndQuery}");
_requestResponseLogger.Enqueue(logContents);
return response;
}
private static bool IsJsonContentType(HttpHeaders headers)
{
if (!headers.TryGetValues(
"Content-Type",
out var values))
{
return false;
}
var header = string.Join(
" ",
values)
.ToLowerInvariant();
return header.Contains("json");
}
private 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));
}
private class RequestResponseLogger
{
private readonly BlockingCollection<IEnumerable<string>> _queue =
new BlockingCollection<IEnumerable<string>>();
private static readonly CancellationTokenSource CancellationTokenSource = new CancellationTokenSource();
private readonly EventLoopScheduler _eventLoopScheduler = new EventLoopScheduler();
public RequestResponseLogger()
{
_queue.GetConsumingEnumerable(CancellationTokenSource.Token)
.ToObservable(_eventLoopScheduler)
.Subscribe(
contents =>
{
File.AppendAllLines(GetFilePath(), contents, Encoding.UTF8);
});
}
public void Enqueue(IEnumerable<string> contents)
{
_queue.Add(contents);
}
private static string GetFilePath()
{
var dirPath = Path.GetDirectoryName(ConfigurationManager.AppSettings["LogFullPath"]);
if (!Directory.Exists(dirPath))
{
Directory.CreateDirectory(dirPath);
}
return Path.Combine(dirPath, $"RequestResponse-{DateTime.Now:yyyy-MM-dd}.log");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment