using System.Net.Http.Headers; namespace ZymonicServices; public class HttpLoggingHandler : DelegatingHandler { IZymonicLogger _logger; public HttpLoggingHandler(IZymonicLogger logger, HttpMessageHandler? innerHandler = null) : base(innerHandler ?? new HttpClientHandler() { UseCookies = false }) { _logger = logger; } async protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var req = request; var id = Guid.NewGuid().ToString(); _logger.LogDebug("[{MsgGuid}] {Method} {PathAndQuery} {Scheme}/{Version}", id, req.Method, req.RequestUri is not null ? req.RequestUri.PathAndQuery : "" , req.RequestUri is not null ? req.RequestUri.Scheme : "", req.Version); _logger.LogDebug("[{MsgGUid}] Host: {Scheme}://{Host}", id, req.RequestUri is not null ? req.RequestUri.Scheme : "", req.RequestUri is not null ? req.RequestUri.Host : ""); foreach (var header in req.Headers) _logger.LogDebug("[{MsgGuid}] {Header}: {HeaderValue}", id, header.Key, string.Join(", ", header.Value)); if (req.Content != null) { foreach (var header in req.Content.Headers) _logger.LogDebug("[{MsgGuid}] {Header}: {HeaderValue}", id, header.Key, string.Join(", ", header.Value)); if (req.Content is StringContent || this.IsTextBasedContentType(req.Headers) || this.IsTextBasedContentType(req.Content.Headers)) { var result = await req.Content.ReadAsStringAsync(); _logger.LogDebug("[{MsgGuid}] {RequestContent}", id, string.Join("", result.Cast().Take(50000))); } } var start = DateTime.Now; try { //using var listener = new TestEventListener((m) => { _logger.LogDebug($"[{MsgGuid}] - {m}"); }, TestEventListener.NetworkingEvents); var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false); var end = DateTime.Now; _logger.LogDebug("[{MsgGuid}] Duration: {RequestDuration}", id, end - start); var resp = response; _logger.LogDebug("[{MsgGuid}] {Scheme}/{SchemeVersion} {StatusCode} {Reason}", id, req.RequestUri is not null ? req.RequestUri.Scheme.ToUpper() : "", resp.Version, resp.StatusCode, resp.ReasonPhrase is not null ? resp.ReasonPhrase : ""); foreach (var header in resp.Headers) _logger.LogDebug("[{MsgGuid}] {Header}: {HeaderValue}", id, header.Key, string.Join(", ", header.Value)); if (resp.Content != null) { foreach (var header in resp.Content.Headers) _logger.LogDebug("[{MsgGuid}] {Header}: {HeaderValue}", id, header.Key, string.Join(", ", header.Value)); if (resp.Content is StringContent || this.IsTextBasedContentType(resp.Headers) || this.IsTextBasedContentType(resp.Content.Headers)) { start = DateTime.Now; var result = await resp.Content.ReadAsStringAsync(); end = DateTime.Now; _logger.LogDebug("[{MsgGuid}] {ResponseContent}", id, string.Join("", result.Cast().Take(50000))); _logger.LogDebug("[{MsgGuid}] Duration: {ReadDuration}", id, end - start); } } return response; } catch (Exception e) { var end = DateTime.Now; _logger.LogDebug("[{MsgGuid}] Duration: {Duration}", id, end - start); _logger.LogError("[{MsgGuid}] Exception occurred: {Exception}", id, e); throw; } } readonly string[] types = new[] { "html", "text", "xml", "json", "txt", "x-www-form-urlencoded" }; bool IsTextBasedContentType(HttpHeaders headers) { IEnumerable? values; if (!headers.TryGetValues("Content-Type", out values)) return false; var header = string.Join(" ", values).ToLowerInvariant(); return types.Any(t => header.Contains(t)); } }