Skip to content

[HttpClientFactory] Remove dependency on ILoggerFactory#89531

Merged
CarnaViire merged 4 commits into
dotnet:mainfrom
CarnaViire:hcf-remove-iloggerfactory
Aug 9, 2023
Merged

[HttpClientFactory] Remove dependency on ILoggerFactory#89531
CarnaViire merged 4 commits into
dotnet:mainfrom
CarnaViire:hcf-remove-iloggerfactory

Conversation

@CarnaViire

Copy link
Copy Markdown
Member
  1. Internal diagnostics logging in DefaultHttpClientFactory now uses a new EventSource ("Private.InternalDiagnostics.Microsoft.Extensions.Http") instead of ILogger

  2. LoggingHttpMessageHandlerBuilderFilter only resolves ILoggerFactory if default logging is on

cc @CodeBlanch

Fixes #89032

@ghost

ghost commented Jul 26, 2023

Copy link
Copy Markdown

Tagging subscribers to this area: @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

Issue Details
  1. Internal diagnostics logging in DefaultHttpClientFactory now uses a new EventSource ("Private.InternalDiagnostics.Microsoft.Extensions.Http") instead of ILogger

  2. LoggingHttpMessageHandlerBuilderFilter only resolves ILoggerFactory if default logging is on

cc @CodeBlanch

Fixes #89032

Author: CarnaViire
Assignees: -
Labels:

area-Extensions-HttpClientFactory

Milestone: -

@CarnaViire

Copy link
Copy Markdown
Member Author

Test failures are unrelated.

@ManickaP ManickaP left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just one question.

Comment thread src/libraries/Microsoft.Extensions.Http/src/Logging/NetEventSource.cs Outdated
@CodeBlanch

Copy link
Copy Markdown
Contributor

@CarnaViire

Would you mind verifying that this test passes with the change? This will verify #89032. It seems like it should, but just want to make sure.

        [Fact]
        public void LoggerFactoryWithHttpClientFactoryTest()
        {
            var services = new ServiceCollection();

            services.AddHttpClient();

            services.AddLogging(builder => builder.Services.AddSingleton<ILoggerProvider, TestLoggerProvider>());

            using var serviceProvider = services.BuildServiceProvider();

            var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
        }

        private sealed class TestLoggerProvider : ILoggerProvider
        {
            private readonly HttpClient _httpClient;

            public TestLoggerProvider(IHttpClientFactory httpClientFactory)
            {
                _httpClient = httpClientFactory.CreateClient("TestLoggerProvider");
            }

            public ILogger CreateLogger(string categoryName)
            {
                return NullLogger.Instance;
            }

            public void Dispose()
            {
                _httpClient.Dispose();
            }
        }

@CarnaViire

CarnaViire commented Jul 27, 2023

Copy link
Copy Markdown
Member Author

@CodeBlanch you need to remove the default HttpClient logging (that uses ILoggerFactory, and I cannot change that without breaking the end users) -- so in your suggestion I've changed services.AddHttpClient(); to

services.AddHttpClient("TestLoggerProvider").RemoveAllLoggers();

Default logging can also be removed for all HttpClients by

services.ConfigureHttpClientDefaults(b => b.RemoveAllLoggers());

(it is still possible to add the default logging back per-name with AddDefaultLogger())

@JamesNK

JamesNK commented Jul 28, 2023

Copy link
Copy Markdown
Member

Wait, this is a breaking change for anyone who is using these logs. Also, EventSource is not as easy to use or as powerful as ILogger.

Could this be fixed by not resolving ILoggerFactory in the DefaultHttpClientFactory constructor? DefaultHttpClientFactory has IServiceProvider. It could be used to lazily get an ILogger instance the first time it is used. e.g.

private ILogger Logger
{
    get { return _logger ??= _serviceProvider.GetRequiredService<ILoggerFactory>().CreateLogger<DefaultHttpClientFactory>(); }
}

@JamesNK JamesNK left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ghost ghost added the needs-author-action An issue or pull request that requires more info or actions from the author. label Jul 28, 2023
@CarnaViire

CarnaViire commented Jul 28, 2023

Copy link
Copy Markdown
Member Author

Wait, this is a breaking change for anyone who is using these logs.

You mean debugging DefaultHttpClientFactory logs that say that the cleanup cycle has started? This really looked like an internal diagnostics stuff, not a thing anyone could depend on?

Because the "default" per-client logs stay the same as they were (using ILogger).

I've thought on an ability to bring the debugging back to ILogger via an app-context switch, if it's reeeally needed.... And the user setting this would guarantee that they haven't registered any logging provider that internally uses HttpClientFactory.

Could this be fixed by not resolving ILoggerFactory in the DefaultHttpClientFactory constructor?

I don't think there's any other way to fix the bug. Even if the factory would be created by making the resolution to be lazy, the whole thing would still enter an endless cycle trying to log that it logs that it logs etc

@ghost ghost removed the needs-author-action An issue or pull request that requires more info or actions from the author. label Jul 28, 2023
@JamesNK

JamesNK commented Jul 28, 2023

Copy link
Copy Markdown
Member

I don't think there's any other way to fix the bug. Even if the factory would be created by making the resolution to be lazy, the whole thing would still enter an endless cycle trying to log that it logs that it logs etc

Are you sure?

Logging isn't async. _logger.LogInformation("Hello world") won't pause until the log message is sent with HttpClient. What I guess OTel folks do is put the log message into a queue and send it via HttpClient in a background thread. That's how most complex log providers do async IO.

Some more log messages will get written when, for example, HttpClientFactory's CleanupCycleStart message is logged, but that shouldn't put the ILoggerProvider that uses the client factory into a stack overflow because HttpClientFactory logged something.

Btw I don't have an objection to requiring people to remove default HTTP client logging. That's just a configuration change required by someone who wants to do this.

Alternatively, you could suggest to them that they disable that logging with Microsoft.Extensions.Logging filters. They could do the same thing to filter out logging from HttpClientFactory if it is problematic.

@CarnaViire

Copy link
Copy Markdown
Member Author

@JamesNK @ManickaP PTAL

@ManickaP ManickaP left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume the added tests simulate the reported problem and with the lazy initialization it's solved. If so, still LGTM.

_loggerFactory = loggerFactory;
_optionsMonitor = optionsMonitor;

_loggerFactory = new Lazy<ILoggerFactory>(serviceProvider.GetRequiredService<ILoggerFactory>, LazyThreadSafetyMode.PublicationOnly);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment explaining why.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why PublicationOnly in this lazy and ExecutionAndPublication in DefaultHttpClientFactory lazy?

@CarnaViire CarnaViire Aug 2, 2023

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we don't need to guard this one at all, given that DI should already guard creation of a singleton.

DefaultHttpClientFactory lazy is creating ILogger, which is most possibly a new instance every time CreateLogger is invoked, so I want to avoid unnecessary allocations.

@CarnaViire

Copy link
Copy Markdown
Member Author

@JamesNK PTAL

@CarnaViire CarnaViire requested a review from JamesNK August 4, 2023 12:04
@CarnaViire CarnaViire merged commit b355725 into dotnet:main Aug 9, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[HttpClientFactory] ILoggerFactory dependency issue

5 participants