diff --git a/Directory.Build.props b/Directory.Build.props
index a7bace06..a7563b69 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,5 +1,5 @@
- 4.13.1
+ 4.14.0
diff --git a/src/CheckoutSdk/LogProvider.cs b/src/CheckoutSdk/LogProvider.cs
index 43ae3ca6..47f0082c 100644
--- a/src/CheckoutSdk/LogProvider.cs
+++ b/src/CheckoutSdk/LogProvider.cs
@@ -2,23 +2,42 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System;
+using System.Collections.Concurrent;
namespace Checkout
{
-
public static class LogProvider
{
+ private static readonly object SyncRoot = new object();
private static ILoggerFactory _loggerFactory = new LoggerFactory();
+ private static readonly ConcurrentDictionary Loggers = new ConcurrentDictionary();
public static void SetLogFactory(ILoggerFactory factory)
{
- _loggerFactory?.Dispose();
- _loggerFactory = factory ?? new LoggerFactory();
+ lock (SyncRoot)
+ {
+ _loggerFactory?.Dispose();
+ _loggerFactory = factory ?? new LoggerFactory();
+ Loggers.Clear();
+ }
}
public static ILogger GetLogger(Type loggerType)
{
- return loggerType is null ? NullLogger.Instance : _loggerFactory.CreateLogger(loggerType) ?? NullLogger.Instance;;
+ if (loggerType == null)
+ {
+ return NullLogger.Instance;
+ }
+
+ return Loggers.GetOrAdd(loggerType, type =>
+ {
+ lock (SyncRoot)
+ {
+ var name = type.FullName ?? type.Name;
+ var logger = _loggerFactory.CreateLogger(name);
+ return logger ?? NullLogger.Instance;
+ }
+ });
}
}
}
diff --git a/test/CheckoutSdkTest/CheckoutSdkIntegrationTest.cs b/test/CheckoutSdkTest/CheckoutSdkIntegrationTest.cs
index 288322ee..a416bbfd 100644
--- a/test/CheckoutSdkTest/CheckoutSdkIntegrationTest.cs
+++ b/test/CheckoutSdkTest/CheckoutSdkIntegrationTest.cs
@@ -72,7 +72,7 @@ private class TestingClientFactory : IHttpClientFactory
public HttpClient CreateClient()
{
var httpClient = new HttpClient(new CustomMessageHandler());
- httpClient.Timeout = TimeSpan.FromSeconds(2);
+ httpClient.Timeout = TimeSpan.FromSeconds(60);
return httpClient;
}
}
diff --git a/test/CheckoutSdkTest/LogProviderTest.cs b/test/CheckoutSdkTest/LogProviderTest.cs
index 8f989f11..ab5d2ccc 100644
--- a/test/CheckoutSdkTest/LogProviderTest.cs
+++ b/test/CheckoutSdkTest/LogProviderTest.cs
@@ -6,9 +6,13 @@
namespace Checkout
{
+ [Collection("NonParallel")]
public sealed class LogProviderTests : IDisposable
{
private readonly ILoggerFactory _loggerFactory;
+
+ private static readonly object RandLock = new object();
+ private static readonly Random Random = new Random();
public LogProviderTests()
{
@@ -25,14 +29,20 @@ public void ShouldGetLoggerReturnsValidLogger()
public async Task ShouldCreateASingleLoggerInstanceForMultipleConcurrentRequests()
{
LogProvider.SetLogFactory(_loggerFactory);
- Type[] loggerTypes = new[] { typeof(LogProviderTests), typeof(AnotherTestClass), typeof(NoInitializedType) };
+ Type[] loggerTypes = { typeof(LogProviderTests), typeof(AnotherTestClass), typeof(NoInitializedType) };
+
Task[] createLoggerTasks = Enumerable.Range(1, 50)
.Select(async index =>
{
- int randomDelayMs = new Random().Next(1, 5);
- await Task.Delay(randomDelayMs);
+ int delay;
+ lock (RandLock)
+ {
+ delay = Random.Next(1, 5);
+ }
+ await Task.Delay(delay);
return await Task.FromResult(LogProvider.GetLogger(loggerTypes[index % loggerTypes.Length]));
}).ToArray();
+
ILogger[] loggers = await Task.WhenAll(createLoggerTasks);
Assert.Equal(loggerTypes.Length, loggers.Distinct().Count());
}
@@ -54,6 +64,66 @@ public void ShouldNotThrowExceptionWhenSetLogFactoryWithNullParameter()
{
Assert.Null(Record.Exception(() => LogProvider.SetLogFactory(null)));
}
+
+ [Fact]
+ public void ShouldReplaceLoggerFactoryCorrectly()
+ {
+ var loggerBefore = LogProvider.GetLogger(typeof(LogProviderTests));
+
+ var newFactory = LoggerFactory.Create(builder => builder.AddFilter(_ => false));
+ LogProvider.SetLogFactory(newFactory);
+
+ var loggerAfter = LogProvider.GetLogger(typeof(LogProviderTests));
+
+ Assert.NotSame(loggerBefore, loggerAfter);
+ }
+
+ [Fact]
+ public void ShouldClearLoggersWhenFactoryChanges()
+ {
+ LogProvider.SetLogFactory(_loggerFactory);
+ var logger1 = LogProvider.GetLogger(typeof(LogProviderTests));
+
+ var newFactory = new LoggerFactory();
+ LogProvider.SetLogFactory(newFactory);
+ var logger2 = LogProvider.GetLogger(typeof(LogProviderTests));
+
+ Assert.NotSame(logger1, logger2);
+ }
+
+ [Fact]
+ public void ShouldReturnSameLoggerOnMultipleCalls()
+ {
+ LogProvider.SetLogFactory(_loggerFactory);
+
+ var logger1 = LogProvider.GetLogger(typeof(LogProviderTests));
+ var logger2 = LogProvider.GetLogger(typeof(LogProviderTests));
+
+ Assert.Same(logger1, logger2);
+ }
+
+ [Fact]
+ public async Task ShouldNotThrowWhenCallingSetLogFactoryConcurrently()
+ {
+ var tasks = Enumerable.Range(0, 10).Select(_ => Task.Run(() =>
+ {
+ LogProvider.SetLogFactory(new LoggerFactory());
+ }));
+
+ var exception = await Record.ExceptionAsync(async () => await Task.WhenAll(tasks));
+
+ Assert.Null(exception);
+ }
+
+ [Fact]
+ public void ShouldAllowMultipleNullLoggerFactoryAssignments()
+ {
+ LogProvider.SetLogFactory(null);
+ LogProvider.SetLogFactory(null);
+ var logger = LogProvider.GetLogger(typeof(LogProviderTests));
+
+ Assert.NotNull(logger);
+ }
public void Dispose()
{