File: Logging\LoggingHttpMessageHandlerBuilderFilter.cs
Web Access
Project: src\src\libraries\Microsoft.Extensions.Http\src\Microsoft.Extensions.Http.csproj (Microsoft.Extensions.Http)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Threading;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Http.Logging;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
 
namespace Microsoft.Extensions.Http
{
    // Internal so we can change the requirements without breaking changes.
    internal sealed class LoggingHttpMessageHandlerBuilderFilter : IHttpMessageHandlerBuilderFilter
    {
        // we want to prevent a circular depencency between ILoggerFactory and IHttpMessageHandlerBuilderFilter, in case
        // any of ILoggerProvider instances use IHttpClientFactory to send logs to an external server
        private ILoggerFactory? _loggerFactory;
        private ILoggerFactory LoggerFactory => _loggerFactory ??= _serviceProvider.GetRequiredService<ILoggerFactory>();
        private readonly IServiceProvider _serviceProvider;
        private readonly IOptionsMonitor<HttpClientFactoryOptions> _optionsMonitor;
 
        public LoggingHttpMessageHandlerBuilderFilter(IServiceProvider serviceProvider, IOptionsMonitor<HttpClientFactoryOptions> optionsMonitor)
        {
            ThrowHelper.ThrowIfNull(serviceProvider);
            ThrowHelper.ThrowIfNull(optionsMonitor);
 
            _serviceProvider = serviceProvider;
            _optionsMonitor = optionsMonitor;
        }
 
        public Action<HttpMessageHandlerBuilder> Configure(Action<HttpMessageHandlerBuilder> next)
        {
            ThrowHelper.ThrowIfNull(next);
 
            return (builder) =>
            {
                // Run other configuration first, we want to decorate.
                next(builder);
 
                HttpClientFactoryOptions options = _optionsMonitor.Get(builder.Name);
                if (options.SuppressDefaultLogging)
                {
                    return;
                }
 
                string loggerName = !string.IsNullOrEmpty(builder.Name) ? builder.Name : "Default";
 
                // We want all of our logging message to show up as-if they are coming from HttpClient,
                // but also to include the name of the client for more fine-grained control.
                ILogger outerLogger = LoggerFactory.CreateLogger($"System.Net.Http.HttpClient.{loggerName}.LogicalHandler");
                ILogger innerLogger = LoggerFactory.CreateLogger($"System.Net.Http.HttpClient.{loggerName}.ClientHandler");
 
                // The 'scope' handler goes first so it can surround everything.
                builder.AdditionalHandlers.Insert(0, new LoggingScopeHttpMessageHandler(outerLogger, options));
 
                // We want this handler to be last so we can log details about the request after
                // service discovery and security happen.
                builder.AdditionalHandlers.Add(new LoggingHttpMessageHandler(innerLogger, options));
            };
        }
    }
}