Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<Project>
<PropertyGroup>
<Version>4.13.1</Version>
<Version>4.14.0</Version>
</PropertyGroup>
</Project>
27 changes: 23 additions & 4 deletions src/CheckoutSdk/LogProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Type, ILogger> Loggers = new ConcurrentDictionary<Type, ILogger>();

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;
}
});
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion test/CheckoutSdkTest/CheckoutSdkIntegrationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down
76 changes: 73 additions & 3 deletions test/CheckoutSdkTest/LogProviderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand All @@ -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<ILogger>[] 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());
}
Expand All @@ -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()
{
Expand Down
Loading