Skip to content

Commit

Permalink
MicrosoftILoggerTarget - With ILoggerFactory as constructor parameter (
Browse files Browse the repository at this point in the history
  • Loading branch information
snakefoot committed Jan 24, 2021
1 parent 603889d commit f4bafa5
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 31 deletions.
99 changes: 69 additions & 30 deletions src/NLog.Extensions.Logging/Targets/MicrosoftILoggerTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace NLog.Extensions.Logging
[Target("MicrosoftILogger")]
public class MicrosoftILoggerTarget : TargetWithContext
{
private readonly Microsoft.Extensions.Logging.ILoggerFactory _loggerFactory;
private readonly Microsoft.Extensions.Logging.ILogger _logger;

/// <summary>
Expand All @@ -29,22 +30,35 @@ public class MicrosoftILoggerTarget : TargetWithContext
/// <summary>
/// Initializes a new instance of the <see cref="MicrosoftILoggerTarget" /> class.
/// </summary>
/// <param name="logger">Microsoft ILogger instance</param>
/// <param name="logger">Microsoft ILogger singleton instance</param>
public MicrosoftILoggerTarget(Microsoft.Extensions.Logging.ILogger logger)
{
_logger = logger;
Layout = "${message}";
OptimizeBufferReuse = true;
}

/// <summary>
/// Initializes a new instance of the <see cref="MicrosoftILoggerTarget" /> class.
/// </summary>
/// <param name="loggerFactory">Microsoft ILoggerFactory instance</param>
public MicrosoftILoggerTarget(Microsoft.Extensions.Logging.ILoggerFactory loggerFactory)
{
_loggerFactory = loggerFactory;
Layout = "${message}";
OptimizeBufferReuse = true;
}

/// <summary>
/// Converts NLog-LogEvent into Microsoft Extension Logging LogState
/// </summary>
/// <param name="logEvent"></param>
protected override void Write(LogEventInfo logEvent)
{
var ilogger = _logger ?? (string.IsNullOrEmpty(logEvent.LoggerName) ? _loggerFactory.CreateLogger("NLog") : _loggerFactory.CreateLogger(logEvent.LoggerName));

var logLevel = ConvertToLogLevel(logEvent.Level);
if (!_logger.IsEnabled(logLevel))
if (!ilogger.IsEnabled(logLevel))
return;

var eventId = default(EventId);
Expand All @@ -70,10 +84,10 @@ protected override void Write(LogEventInfo logEvent)
contextProperties = null;
}

_logger.Log(ConvertToLogLevel(logEvent.Level), eventId, new LogState(logEvent, layoutMessage, contextProperties), logEvent.Exception, LogStateFormatter);
ilogger.Log(ConvertToLogLevel(logEvent.Level), eventId, new LogState(logEvent, layoutMessage, contextProperties), logEvent.Exception, (s,ex) => LogStateFormatter(s));
}

private struct LogState : IReadOnlyList<KeyValuePair<string, object>>
private struct LogState : IReadOnlyList<KeyValuePair<string, object>>, IEquatable<LogState>
{
private readonly LogEventInfo _logEvent;
public readonly string LayoutMessage;
Expand All @@ -85,11 +99,11 @@ private struct LogState : IReadOnlyList<KeyValuePair<string, object>>
{
get
{
if (_logEvent.HasProperties && TryGetPropertyFromIndex(_logEvent.Properties, CreateLogEventProperty, ref index, out var property))
if (_logEvent.HasProperties && TryGetLogEventProperty(_logEvent.Properties, ref index, out var property))
{
return property;
}
if (_contextProperties != null && TryGetPropertyFromIndex(_contextProperties, p => p, ref index, out var contextProperty))
if (_contextProperties != null && TryGetContextProperty(_contextProperties, ref index, out var contextProperty))
{
return contextProperty;
}
Expand All @@ -112,7 +126,7 @@ public LogState(LogEventInfo logEvent, string layoutMessage, IDictionary<string,
IEnumerable<KeyValuePair<string, object>> allProperties = _contextProperties?.Concat(originalMessage) ?? originalMessage;
if (_logEvent.HasProperties)
{
allProperties = _logEvent.Properties.Select(CreateLogEventProperty).Concat(allProperties);
allProperties = _logEvent.Properties.Select(p => CreateLogEventProperty(p)).Concat(allProperties);
}
return allProperties.GetEnumerator();
}
Expand All @@ -122,40 +136,65 @@ IEnumerator IEnumerable.GetEnumerator()
return GetEnumerator();
}

private static bool TryGetPropertyFromIndex<TKey, TValue>(ICollection<KeyValuePair<TKey, TValue>> properties, Func<KeyValuePair<TKey, TValue>, KeyValuePair<string, object>> converter, ref int index, out KeyValuePair<string, object> property)
private KeyValuePair<string, object> CreateOriginalFormatProperty()
{
if (index < properties.Count)
{
foreach (var prop in properties)
{
if (index-- == 0)
{
property = converter(prop);
return true;
}
}
}
else
{
index -= properties.Count;
}
return new KeyValuePair<string, object>(NLogLogger.OriginalFormatPropertyName, _logEvent.Message);
}

property = default;
return false;
public bool Equals(LogState other)
{
return ReferenceEquals(_logEvent, other._logEvent);
}

private static KeyValuePair<string, object> CreateLogEventProperty(KeyValuePair<object, object> prop)
public override bool Equals(object obj)
{
return new KeyValuePair<string, object>(prop.Key.ToString(), prop.Value);
return obj is LogState other && Equals(other);
}

private KeyValuePair<string, object> CreateOriginalFormatProperty()
public override int GetHashCode()
{
return new KeyValuePair<string, object>(NLogLogger.OriginalFormatPropertyName, _logEvent.Message);
return _logEvent.GetHashCode();
}
}

private static string LogStateFormatter(LogState logState, Exception _)
private static bool TryGetContextProperty(IDictionary<string, object> contextProperties, ref int index, out KeyValuePair<string, object> contextProperty)
{
return TryGetPropertyFromIndex(contextProperties, p => p, ref index, out contextProperty);
}

private static bool TryGetLogEventProperty(IDictionary<object, object> logEventProperties, ref int index, out KeyValuePair<string, object> logEventProperty)
{
return TryGetPropertyFromIndex(logEventProperties, p => CreateLogEventProperty(p), ref index, out logEventProperty);
}

private static bool TryGetPropertyFromIndex<TKey, TValue>(ICollection<KeyValuePair<TKey, TValue>> properties, Func<KeyValuePair<TKey, TValue>, KeyValuePair<string, object>> converter, ref int index, out KeyValuePair<string, object> property) where TKey : class
{
if (index < properties.Count)
{
foreach (var prop in properties)
{
if (index-- == 0)
{
property = converter(prop);
return true;
}
}
}
else
{
index -= properties.Count;
}

property = default;
return false;
}

private static KeyValuePair<string, object> CreateLogEventProperty(KeyValuePair<object, object> prop)
{
return new KeyValuePair<string, object>(prop.Key.ToString(), prop.Value);
}

private static string LogStateFormatter(LogState logState)
{
return logState.LayoutMessage;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,22 @@ public void SimpleILoggerMessageTest()
Assert.Equal("Hello World", mock.LastLogProperties[0].Value);
}

[Fact]
public void SimpleILoggerFactoryMessageTest()
{
// Arrange
var (logger, mock) = CreateLoggerFactoryMock(out _);

// Act
logger.Info("Hello World");

// Assert
Assert.Single(mock.Loggers);
Assert.Equal("Hello World", mock.Loggers.First().Value.LastLogMessage);
Assert.Single(mock.Loggers.First().Value.LastLogProperties);
Assert.Equal("Hello World", mock.Loggers.First().Value.LastLogProperties[0].Value);
}

[Fact]
public void FilterILoggerMessageTest()
{
Expand Down Expand Up @@ -165,6 +181,7 @@ public void TestOnLogLevel(string levelText, string expectedILoggerLogLevelText)
Assert.Equal("message1", mock.LastLogMessage);
Assert.Equal(expectedLogLevel, mock.LastLogLevel);
}

[Fact]
public void LogWitException()
{
Expand Down Expand Up @@ -192,23 +209,66 @@ private static (Logger, LoggerMock) CreateLoggerMock(out MicrosoftILoggerTarget
{
var logFactory = new LogFactory();
var logConfig = new Config.LoggingConfiguration();
var loggerMock = new LoggerMock();
var loggerMock = new LoggerMock("NLog");
target = new MicrosoftILoggerTarget(loggerMock) { Layout = "${message}" };
logConfig.AddRuleForAllLevels(target);
logFactory.Configuration = logConfig;
var logger = logFactory.GetCurrentClassLogger();
return (logger, loggerMock);
}

private static (Logger, LoggerFactoryMock) CreateLoggerFactoryMock(out MicrosoftILoggerTarget target)
{
var logFactory = new LogFactory();
var logConfig = new Config.LoggingConfiguration();
var loggerFactoryMock = new LoggerFactoryMock();
target = new MicrosoftILoggerTarget(loggerFactoryMock) { Layout = "${message}" };
logConfig.AddRuleForAllLevels(target);
logFactory.Configuration = logConfig;
var logger = logFactory.GetCurrentClassLogger();
return (logger, loggerFactoryMock);
}

class LoggerFactoryMock : Microsoft.Extensions.Logging.ILoggerFactory
{
public readonly Dictionary<string, LoggerMock> Loggers = new Dictionary<string, LoggerMock>();

public void AddProvider(ILoggerProvider provider)
{
// Nothing to do
}

public Microsoft.Extensions.Logging.ILogger CreateLogger(string categoryName)
{
if (!Loggers.TryGetValue(categoryName, out var logger))
{
logger = new LoggerMock(categoryName);
Loggers[categoryName] = logger;
}
return logger;
}

public void Dispose()
{
// Nothing to do
}
}

class LoggerMock : Microsoft.Extensions.Logging.ILogger
{
public readonly string CategoryName;
public Microsoft.Extensions.Logging.LogLevel LastLogLevel;
public string LastLogMessage;
public Exception LastLogException;
public IList<KeyValuePair<string, object>> LastLogProperties;
public EventId LastLogEventId;
public bool EnableAllLevels;

public LoggerMock(string categoryName)
{
CategoryName = categoryName;
}

public IDisposable BeginScope<TState>(TState state)
{
return null;
Expand Down Expand Up @@ -247,6 +307,11 @@ public void Log<TState>(Microsoft.Extensions.Logging.LogLevel logLevel, EventId
throw new ArgumentException($"Property Value mismatch {LastLogProperties[i].Value} <-> {property.Value}");
}
}

public override string ToString()
{
return CategoryName;
}
}
}
}

0 comments on commit f4bafa5

Please sign in to comment.