Skip to content

Commit

Permalink
Merge pull request #13 from altso/logger-options
Browse files Browse the repository at this point in the history
Add AutoShowLogDisplayThreshold option to logger
  • Loading branch information
altso authored Nov 15, 2021
2 parents 805dae5 + cf95420 commit f7d296a
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
using System;
using System.Collections.Concurrent;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;

namespace ExcelRna.Extensions.Logging.Tests;
Expand All @@ -8,7 +13,9 @@ public class LogDisplayLoggerProviderTests
public void CreateLogger_creates_LogDisplayLogger()
{
// ARRANGE
var provider = new LogDisplayLoggerProvider();
var options = new LogDisplayLoggerOptions();
var optionsMonitor = Mock.Of<IOptionsMonitor<LogDisplayLoggerOptions>>(monitor => monitor.CurrentValue == options);
var provider = new LogDisplayLoggerProvider(optionsMonitor);

// ACT
var logger = provider.CreateLogger("Test");
Expand All @@ -21,9 +28,74 @@ public void CreateLogger_creates_LogDisplayLogger()
public void Dispose_does_not_throw()
{
// ARRANGE
var provider = new LogDisplayLoggerProvider();
var options = new LogDisplayLoggerOptions();
var optionsMonitor = Mock.Of<IOptionsMonitor<LogDisplayLoggerOptions>>(monitor => monitor.CurrentValue == options);
Mock.Get(optionsMonitor)
.Setup(monitor => monitor.OnChange(It.IsAny<Action<LogDisplayLoggerOptions, string>>()))
.Returns(Mock.Of<IDisposable>());
var provider = new LogDisplayLoggerProvider(optionsMonitor);

// ACT
provider.Dispose();
}

[Fact]
public void Options_changes_are_applied_to_loggers()
{
// ARRANGE
var optionsMonitor = new TestOptionsMonitor<LogDisplayLoggerOptions>();
optionsMonitor.Set(Options.DefaultName, new LogDisplayLoggerOptions { AutoShowLogDisplayThreshold = LogLevel.Error });

var provider = new LogDisplayLoggerProvider(optionsMonitor);
var logger = (LogDisplayLogger)provider.CreateLogger("Test");

// ACT & ASSERT
Assert.Equal(LogLevel.Error, logger.Options.AutoShowLogDisplayThreshold);
optionsMonitor.Set(Options.DefaultName, new LogDisplayLoggerOptions { AutoShowLogDisplayThreshold = LogLevel.Trace });
Assert.Equal(LogLevel.Trace, logger.Options.AutoShowLogDisplayThreshold);
}

private class TestOptionsMonitor<TOptions> : IOptionsMonitor<TOptions>
where TOptions : class, new()
{
private readonly ConcurrentDictionary<string, TOptions> _options = new();
private event Action<TOptions, string> OnChangeEvent;

public TOptions Get(string name)
{
return _options.TryGetValue(name ?? Options.DefaultName, out var options) ? options : throw new Exception(nameof(name));
}

public void Set(string name, TOptions options)
{
_options.TryRemove(name, out _);
_options.TryAdd(name, options);
OnChangeEvent?.Invoke(options, name);
}

public IDisposable OnChange(Action<TOptions, string> listener)
{
var disposable = new ChangeTrackerDisposable(this, listener);
OnChangeEvent += disposable.OnChange;
return disposable;
}

public TOptions CurrentValue => Get(Options.DefaultName);

private class ChangeTrackerDisposable : IDisposable
{
private readonly Action<TOptions, string> _listener;
private readonly TestOptionsMonitor<TOptions> _monitor;

public ChangeTrackerDisposable(TestOptionsMonitor<TOptions> monitor, Action<TOptions, string> listener)
{
_listener = listener;
_monitor = monitor;
}

public void OnChange(TOptions options, string name) => _listener.Invoke(options, name);

public void Dispose() => _monitor.OnChangeEvent -= OnChange;
}
}
}
51 changes: 37 additions & 14 deletions Source/ExcelRna.Extensions.Logging.Tests/LogDisplayLoggerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ public void IsEnabled_returns_correct_value()
public void Log_is_noop_when_not_enabled()
{
// ARRANGE
var logger = new LogDisplayLogger("Test");
var recordLine = new Mock<Action<string, string[]>>(MockBehavior.Strict);
logger.RecordLine = recordLine.Object;
var logger = new LogDisplayLogger("Test")
{
RecordLine = Mock.Of<Action<string, string[]>>(MockBehavior.Strict),
};

// ACT
logger.Log(LogLevel.None, "TestMessage");
Expand All @@ -52,9 +53,10 @@ public void Log_is_noop_when_not_enabled()
public void Log_is_noop_when_message_is_empty()
{
// ARRANGE
var logger = new LogDisplayLogger("Test");
var recordLine = new Mock<Action<string, string[]>>(MockBehavior.Strict);
logger.RecordLine = recordLine.Object;
var logger = new LogDisplayLogger("Test")
{
RecordLine = Mock.Of<Action<string, string[]>>(MockBehavior.Strict),
};

// ACT
logger.Log(LogLevel.Information, "");
Expand All @@ -64,9 +66,10 @@ public void Log_is_noop_when_message_is_empty()
public void Log_throws_when_formatter_is_null()
{
// ARRANGE
var logger = new LogDisplayLogger("Test");
var recordLine = new Mock<Action<string, string[]>>(MockBehavior.Strict);
logger.RecordLine = recordLine.Object;
var logger = new LogDisplayLogger("Test")
{
RecordLine = Mock.Of<Action<string, string[]>>(MockBehavior.Strict),
};

// ACT & ASSERT
Assert.Throws<ArgumentNullException>(() => logger.Log(LogLevel.Information, 0, "State", null, null!));
Expand All @@ -76,14 +79,34 @@ public void Log_throws_when_formatter_is_null()
public void Log_includes_exception()
{
// ARRANGE
var logger = new LogDisplayLogger("Test");
var recordLine = new Mock<Action<string, string[]>>(MockBehavior.Strict);
recordLine.Setup(invoke => invoke(
var logger = new LogDisplayLogger("Test")
{
RecordLine = Mock.Of<Action<string, string[]>>(),
};

// ACT
logger.Log(LogLevel.Information, new Exception("TestException"), "TestMessage");

// ASSERT
Mock.Get(logger.RecordLine).Verify(invoke => invoke(
It.Is<string>(s => s.Contains("TestMessage") && s.Contains("TestException")),
It.Is<string[]>(p => p.Length == 0)));
logger.RecordLine = recordLine.Object;
It.Is<string[]>(p => p.Length == 0)), Times.Once);
}

[Fact]
public void Log_auto_shows_LogDisplay()
{
// ARRANGE
var logger = new LogDisplayLogger("Test")
{
RecordLine = Mock.Of<Action<string, string[]>>(),
Show = Mock.Of<Action>(),
};

// ACT
logger.Log(LogLevel.Error, new Exception("TestException"), "TestMessage");

// ASSERT
Mock.Get(logger.Show).Verify(invoke => invoke(), Times.Once);
}
}
13 changes: 12 additions & 1 deletion Source/ExcelRna.Extensions.Logging/LogDisplayLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,19 @@ internal sealed class LogDisplayLogger : ILogger
/// Initializes a new instance of the <see cref="LogDisplayLogger"/> class.
/// </summary>
/// <param name="name">The name of the logger.</param>
public LogDisplayLogger(string name)
/// <param name="options">The options of the logger.</param>
public LogDisplayLogger(string name, LogDisplayLoggerOptions? options = null)
{
_name = name;
Options = options ?? new LogDisplayLoggerOptions();
}

internal Action<string, string[]> RecordLine { get; set; } = LogDisplay.RecordLine;

internal Action Show { get; set; } = LogDisplay.Show;

internal LogDisplayLoggerOptions Options { get; set; }

/// <inheritdoc />
public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None;

Expand Down Expand Up @@ -56,5 +62,10 @@ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Except
}

RecordLine(message, Array.Empty<string>());

if (logLevel >= Options.AutoShowLogDisplayThreshold)
{
Show();
}
}
}
8 changes: 8 additions & 0 deletions Source/ExcelRna.Extensions.Logging/LogDisplayLoggerOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Microsoft.Extensions.Logging;

namespace ExcelRna.Extensions.Logging;

public class LogDisplayLoggerOptions
{
public LogLevel AutoShowLogDisplayThreshold { get; set; } = LogLevel.Error;
}
25 changes: 24 additions & 1 deletion Source/ExcelRna.Extensions.Logging/LogDisplayLoggerProvider.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace ExcelRna.Extensions.Logging;

Expand All @@ -8,14 +12,33 @@ namespace ExcelRna.Extensions.Logging;
[ProviderAlias("LogDisplay")]
public class LogDisplayLoggerProvider : ILoggerProvider
{
private readonly IOptionsMonitor<LogDisplayLoggerOptions> _options;
private readonly IDisposable _optionsReloadToken;
private readonly ConcurrentDictionary<string, LogDisplayLogger> _loggers = new();

public LogDisplayLoggerProvider(IOptionsMonitor<LogDisplayLoggerOptions> options)
{
_options = options;
_optionsReloadToken = options.OnChange(ReloadOptions);
}

/// <inheritdoc />
public ILogger CreateLogger(string name)
{
return new LogDisplayLogger(name);
return _loggers.GetOrAdd(name, n => new LogDisplayLogger(n, _options.CurrentValue));
}

/// <inheritdoc />
public void Dispose()
{
_optionsReloadToken.Dispose();
}

private void ReloadOptions(LogDisplayLoggerOptions options)
{
foreach (KeyValuePair<string, LogDisplayLogger> logger in _loggers)
{
logger.Value.Options = options;
}
}
}
5 changes: 2 additions & 3 deletions Source/Samples/QuickStart/QuickStartAddIn.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices;
using ExcelDna.Integration;
using ExcelDna.Integration.CustomUI;
using ExcelDna.IntelliSense;
Expand Down Expand Up @@ -86,7 +85,7 @@ public override string GetCustomUI(string ribbonId)
";
}

public void OnButtonPressed(IRibbonControl control) => _logger.LogInformation(new StackOverflowException(), _quickStartService.SayHello(control.Tag));
public void OnButtonPressed(IRibbonControl control) => _logger.LogInformation(_quickStartService.SayHello(control.Tag));

public void OnLogDisplay(IRibbonControl control) => LogDisplay.Show();
}

0 comments on commit f7d296a

Please sign in to comment.