Created
November 25, 2021 21:56
-
-
Save kiwipiet/80f56c80e75c4402779b1d2f404ab631 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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