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

Attempts to resolve ILogger<T> directly from IServiceProvider first #69

113 changes: 110 additions & 3 deletions src/Uno.Core.Tests/LoggerFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,19 @@
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 +51,109 @@ 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);

//ensure 'restore'
ServiceLocator.SetLocatorProvider(() => originalProvider);
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);

//ensure 'restore'
ServiceLocator.SetLocatorProvider(() => originalProvider);
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)));
}
}


}

}
}
52 changes: 40 additions & 12 deletions src/Uno.Core/Logging/LogExtensionPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,40 @@
//
// ******************************************************************
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 +94,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;
}
}
}
}