Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: attempt to resolve ILogger<T> directly from IServiceProvider first #70

Closed
Closed
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: 2 additions & 0 deletions src/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[*]
indent_style = tab
126 changes: 123 additions & 3 deletions src/Uno.Core.Tests/LoggerFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,20 @@
// limitations under the License.
//
// ******************************************************************

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CommonServiceLocator;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Uno.Extensions;
using Uno.Logging;

namespace Uno.Core.Tests
{
[TestClass]
public class MyTestClass
public class LoggerFixtures
{
[TestMethod]
public void TestInfo()
Expand All @@ -48,5 +49,124 @@ public void TestDebug()
{
this.Log().Debug("Test logging");
}


[TestMethod]
public void TestNonGenericLogWithExternalLogger()
{
//setup
var originalProvider = ServiceLocator.IsLocationProviderSet
? ServiceLocator.Current
: default;

var fakeLocator = new FakeServiceLocator();

ServiceLocator.SetLocatorProvider(() => fakeLocator);

Assert.AreEqual(fakeLocator, ServiceLocator.Current);

var message = "Test logging";

typeof(string).Log().Debug(message);

Assert.AreEqual(1, fakeLocator.Outputs.Count);

var actualDebug = fakeLocator.Outputs.Single();
Assert.AreEqual(message, actualDebug.Message);
Assert.AreEqual(LogLevel.Debug, actualDebug.LogLevel);

//teardown
if (originalProvider == null)
{
ServiceLocator.SetLocatorProvider(null);
Assert.IsFalse(ServiceLocator.IsLocationProviderSet);
}
else
{
ServiceLocator.SetLocatorProvider(() => originalProvider);
Assert.IsTrue(ServiceLocator.IsLocationProviderSet);
Assert.AreEqual(originalProvider, ServiceLocator.Current);
}
}

[TestMethod]
public void TestGenericLogWithExternalLogger()
{
//setup
var originalProvider = ServiceLocator.IsLocationProviderSet
? ServiceLocator.Current
: default;

var fakeLocator = new FakeServiceLocator();

ServiceLocator.SetLocatorProvider(() => fakeLocator);

Assert.AreEqual(fakeLocator, ServiceLocator.Current);

var message = "Test logging";

5.Log().Warn(message);

Assert.AreEqual(1, fakeLocator.Outputs.Count);

var actualWarning = fakeLocator.Outputs.Single();
Assert.AreEqual(message, actualWarning.Message);
Assert.AreEqual(LogLevel.Warning, actualWarning.LogLevel);

//teardown
if (originalProvider == null)
{
ServiceLocator.SetLocatorProvider(null);
Assert.IsFalse(ServiceLocator.IsLocationProviderSet);
}
else
{
ServiceLocator.SetLocatorProvider(() => originalProvider);
Assert.IsTrue(ServiceLocator.IsLocationProviderSet);
Assert.AreEqual(originalProvider, ServiceLocator.Current);
}
}

private class FakeServiceLocator : IServiceLocator
{
public IList<(LogLevel LogLevel, EventId EventId, string Message)> Outputs { get; } = new List<(LogLevel, EventId, string)>();

public IEnumerable<object> GetAllInstances(Type serviceType) => throw new NotImplementedException();
public IEnumerable<TService> GetAllInstances<TService>() => throw new NotImplementedException();
public object GetInstance(Type serviceType) => throw new NotImplementedException();
public object GetInstance(Type serviceType, string key) => throw new NotImplementedException();
public TService GetInstance<TService>() => throw new NotImplementedException();
public TService GetInstance<TService>(string key) => throw new NotImplementedException();
public object GetService(Type serviceType)
{
if (serviceType.IsInterface && typeof(ILogger).IsAssignableFrom(serviceType)
&& serviceType.GenericTypeArguments.Length == 1)
{
return Activator
.CreateInstance(typeof(FakeLogger<>)
.MakeGenericType(serviceType.GenericTypeArguments.Single()), args: this);
}
else
{
throw new NotImplementedException();
}
}

class FakeLogger<T> : ILogger<T>
{
readonly FakeServiceLocator locator;
public FakeLogger(FakeServiceLocator locator)
{
this.locator = locator;
}

public IDisposable BeginScope<TState>(TState state) => throw new NotImplementedException();
public bool IsEnabled(LogLevel logLevel) => throw new NotImplementedException();
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
locator.Outputs.Add((logLevel, eventId, formatter(state, exception)));
}
}
}
}
}
11 changes: 8 additions & 3 deletions src/Uno.Core.sln
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27009.1
# Visual Studio Version 16
VisualStudioVersion = 16.0.30011.22
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Uno.Core.Tests", "Uno.Core.Tests\Uno.Core.Tests.csproj", "{B554D7E4-17FC-4909-BA06-80B5B2FB0BD1}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Uno.Core.Tests", "Uno.Core.Tests\Uno.Core.Tests.csproj", "{B554D7E4-17FC-4909-BA06-80B5B2FB0BD1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Uno.Core", "Uno.Core\Uno.Core.csproj", "{ABB4F177-6966-4C24-A465-A5E941A70680}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Uno.Core.Build", "Uno.Core.Build\Uno.Core.Build.csproj", "{E414AC61-1BED-458E-A78C-868514DDA1A2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{84F31EF7-5A72-4F66-A7B5-364CA9F9E22F}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down
51 changes: 39 additions & 12 deletions src/Uno.Core/Logging/LogExtensionPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,40 @@
// limitations under the License.
//
// ******************************************************************

using System;
using System.Linq;
using CommonServiceLocator;
using Microsoft.Extensions.Logging;
using System.Diagnostics;

namespace Uno.Extensions
{
public static class LogExtensionPoint
{
private static ILoggerFactory _loggerFactory;
public static class LogExtensionPoint
{
private static ILoggerFactory _loggerFactory;

private static class Container<T>
{
internal static readonly ILogger Logger = AmbientLoggerFactory.CreateLogger<T>();
}
private static class Container<T>
{
internal static readonly ILogger Logger =
TryGetLoggerFromServiceProvider(typeof(T))
?? AmbientLoggerFactory.CreateLogger<T>();
}

/// <summary>
/// Retreives the <see cref="ILoggerFactory"/> for this the Uno extension point.
/// </summary>
public static ILoggerFactory AmbientLoggerFactory
public static ILoggerFactory AmbientLoggerFactory
=> Transactional.Update(ref _loggerFactory, l => l ?? GetFactory());

/// <summary>
/// Gets a <see cref="ILogger"/> for the specified type.
/// </summary>
/// <param name="forType"></param>
/// <returns></returns>
public static ILogger Log(this Type forType)
=> AmbientLoggerFactory.CreateLogger(forType);
public static ILogger Log(this Type forType)
=>
TryGetLoggerFromServiceProvider(forType)
?? AmbientLoggerFactory.CreateLogger(forType);

/// <summary>
/// Gets a logger instance for the current types
Expand Down Expand Up @@ -88,5 +93,27 @@ private static ILoggerFactory GetFactory()
return new LoggerFactory();
}
}

private static ILogger TryGetLoggerFromServiceProvider(Type categoryTypeName)
{
if (ServiceLocator.IsLocationProviderSet)
{
try
{
var service = ServiceLocator.Current.GetService(typeof(ILogger<>).MakeGenericType(categoryTypeName));

if (service is ILogger logger
&& logger.GetType().GenericTypeArguments.SequenceEqual(new[] { categoryTypeName }))
{
return logger;
}
}
catch
{
}
}

return null;
}
}
}
}