using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net.Http;
using System.Threading;
var expired = new ExpiredHandlerTrackingEntry(active);
_expiredHandlers.Enqueue(expired);
- Log.HandlerExpired(_logger.Value, active.Name, active.Lifetime);
+ Log.HandlerExpired(_logger, active.Name, active.Lifetime);
StartCleanupTimer();
}
try
{
int initialCount = _expiredHandlers.Count;
- Log.CleanupCycleStart(_logger.Value, initialCount);
+ Log.CleanupCycleStart(_logger, initialCount);
var stopwatch = ValueStopwatch.StartNew();
}
catch (Exception ex)
{
- Log.CleanupItemFailed(_logger.Value, entry.Name, ex);
+ Log.CleanupItemFailed(_logger, entry.Name, ex);
}
}
else
}
}
- Log.CleanupCycleEnd(_logger.Value, stopwatch.GetElapsedTime(), disposedCount, _expiredHandlers.Count);
+ Log.CleanupCycleEnd(_logger, stopwatch.GetElapsedTime(), disposedCount, _expiredHandlers.Count);
}
finally
{
"HttpMessageHandler expired after {HandlerLifetime}ms for client '{ClientName}'");
- public static void CleanupCycleStart(ILogger logger, int initialCount)
+ public static void CleanupCycleStart(Lazy<ILogger> loggerLazy, int initialCount)
{
- _cleanupCycleStart(logger, initialCount, null);
+ if (TryGetLogger(loggerLazy, out ILogger? logger))
+ {
+ _cleanupCycleStart(logger, initialCount, null);
+ }
+ }
+
+ public static void CleanupCycleEnd(Lazy<ILogger> loggerLazy, TimeSpan duration, int disposedCount, int finalCount)
+ {
+ if (TryGetLogger(loggerLazy, out ILogger? logger))
+ {
+ _cleanupCycleEnd(logger, duration.TotalMilliseconds, disposedCount, finalCount, null);
+ }
}
- public static void CleanupCycleEnd(ILogger logger, TimeSpan duration, int disposedCount, int finalCount)
+ public static void CleanupItemFailed(Lazy<ILogger> loggerLazy, string clientName, Exception exception)
{
- _cleanupCycleEnd(logger, duration.TotalMilliseconds, disposedCount, finalCount, null);
+ if (TryGetLogger(loggerLazy, out ILogger? logger))
+ {
+ _cleanupItemFailed(logger, clientName, exception);
+ }
}
- public static void CleanupItemFailed(ILogger logger, string clientName, Exception exception)
+ public static void HandlerExpired(Lazy<ILogger> loggerLazy, string clientName, TimeSpan lifetime)
{
- _cleanupItemFailed(logger, clientName, exception);
+ if (TryGetLogger(loggerLazy, out ILogger? logger))
+ {
+ _handlerExpired(logger, lifetime.TotalMilliseconds, clientName, null);
+ }
}
- public static void HandlerExpired(ILogger logger, string clientName, TimeSpan lifetime)
+ private static bool TryGetLogger(Lazy<ILogger> loggerLazy, [NotNullWhen(true)] out ILogger? logger)
{
- _handlerExpired(logger, lifetime.TotalMilliseconds, clientName, null);
+ logger = null;
+ try
+ {
+ logger = loggerLazy.Value;
+ }
+ catch { } // not throwing in logs
+
+ return logger is not null;
}
}
}
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Threading;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
+using Microsoft.Extensions.Logging.Testing;
using Microsoft.Extensions.Options;
using Moq;
using Moq.Protected;
return cleanupEntry;
}
+ [Fact]
+ public void ServiceProviderDispose_DebugLoggingDoesNotThrow()
+ {
+ var sink = new TestSink();
+
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddLogging();
+ serviceCollection.AddSingleton<ILoggerFactory>(new TestLoggerFactory(sink, enabled: true));
+ serviceCollection.AddHttpClient("test");
+
+ var serviceProvider = serviceCollection.BuildServiceProvider();
+
+ var httpClientFactory = (DefaultHttpClientFactory)serviceProvider.GetRequiredService<IHttpClientFactory>();
+ _ = httpClientFactory.CreateClient("test");
+
+ serviceProvider.Dispose();
+
+ httpClientFactory.StartCleanupTimer(); // we need to create a timer instance before triggering cleanup; normally it happens after the first expiry
+ httpClientFactory.CleanupTimer_Tick(); // trigger cleanup to (try to) write debug logs
+ // but no log is added, because ILogger couldn't be created
+ Assert.Equal(0, sink.Writes.Count(w => w.LoggerName == typeof(DefaultHttpClientFactory).FullName));
+ }
+
private class TestHttpClientFactory : DefaultHttpClientFactory
{
public TestHttpClientFactory(