Skip to content

Instantly share code, notes, and snippets.

@johnkors
Last active November 9, 2020 15:55
Show Gist options
  • Save johnkors/edd89546994ccf5b1a860b0161e06ddb to your computer and use it in GitHub Desktop.
Save johnkors/edd89546994ccf5b1a860b0161e06ddb to your computer and use it in GitHub Desktop.
Alter HttpClientFactory log statements
using System;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Http;
using Microsoft.Extensions.Http.Logging;
using Microsoft.Extensions.Logging;
// ReSharper disable once CheckNamespace
namespace Microsoft.Extensions.DependencyInjection
{
internal static class ServiceCollectionExtensions
{
public static IServiceCollection AddReducedHttpClientFactoryLogging(this IServiceCollection services)
{
services.Replace(ServiceDescriptor.Singleton<IHttpMessageHandlerBuilderFilter, ReducedLoggingHttpMessageHandlerBuilderFilter>());
return services;
}
}
internal class ReducedLoggingHttpMessageHandlerBuilderFilter : IHttpMessageHandlerBuilderFilter
{
private readonly ILoggerFactory _loggerFactory;
public ReducedLoggingHttpMessageHandlerBuilderFilter(ILoggerFactory loggerFactory)
{
_loggerFactory = loggerFactory;
}
public Action<HttpMessageHandlerBuilder> Configure(Action<HttpMessageHandlerBuilder> next)
{
return builder =>
{
next(builder);
var loggerName = !string.IsNullOrEmpty(builder.Name) ? builder.Name : "Default";
var innerLogger = _loggerFactory.CreateLogger($"System.Net.Http.HttpClient.{loggerName}.ClientHandler");
var toRemove = builder.AdditionalHandlers.Where(h => (h is LoggingHttpMessageHandler) || h is LoggingScopeHttpMessageHandler).Select(h => h).ToList();
foreach (var delegatingHandler in toRemove)
{
builder.AdditionalHandlers.Remove(delegatingHandler);
}
builder.AdditionalHandlers.Add(new MinimalLoggingHandler(innerLogger));
};
}
}
public class MinimalLoggingHandler : DelegatingHandler
{
private readonly ILogger _logger;
public MinimalLoggingHandler(ILogger logger)
{
_logger = logger;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
var stopwatch = ValueStopwatch.StartNew();
var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
_logger.LogInformation(new EventId(101, "RequestEnd"), $"{request.RequestUri} - {response.StatusCode} in {stopwatch.GetElapsedTime().TotalMilliseconds}ms");
return response;
}
internal struct ValueStopwatch
{
private static readonly double TimestampToTicks = TimeSpan.TicksPerSecond / (double)Stopwatch.Frequency;
private long _startTimestamp;
public bool IsActive => _startTimestamp != 0;
private ValueStopwatch(long startTimestamp)
{
_startTimestamp = startTimestamp;
}
public static ValueStopwatch StartNew() => new ValueStopwatch(Stopwatch.GetTimestamp());
public TimeSpan GetElapsedTime()
{
if (!IsActive)
{
throw new InvalidOperationException("An uninitialized, or 'default', ValueStopwatch cannot be used to get elapsed time.");
}
long end = Stopwatch.GetTimestamp();
long timestampDelta = end - _startTimestamp;
long ticks = (long)(TimestampToTicks * timestampDelta);
return new TimeSpan(ticks);
}
}
}
}
@johnkors
Copy link
Author

johnkors commented Nov 9, 2020

Makes the following changes to the log output:

- "Sending HTTP request {HttpMethod} {Uri}");
- "Received HTTP response headers after {ElapsedMilliseconds}ms - {StatusCode}"

to

- $"{HttpMethod} {Uri} - {StatusCode} in {ElapsedMilliseconds}ms"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment