From d22b9fe71329974dd308cce7614194475240791b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Wed, 15 Mar 2023 01:52:15 +0100 Subject: [PATCH 1/2] Add a callback on the reader options to expose the log filter switches Just like it was done for log level switches in #352. --- README.md | 16 ++++++++++ .../Configuration/ConfigurationReader.cs | 3 +- .../ConfigurationReaderOptions.cs | 6 ++++ .../Configuration/ILoggingFilterSwitch.cs | 16 ++++++++++ .../Configuration/LoggingFilterSwitchProxy.cs | 2 +- .../ConfigurationSettingsTests.cs | 31 +++++++++++++++++++ ...erilog.Settings.Configuration.approved.txt | 5 +++ 7 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 src/Serilog.Settings.Configuration/Settings/Configuration/ILoggingFilterSwitch.cs diff --git a/README.md b/README.md index b1ebd24..f2f2630 100644 --- a/README.md +++ b/README.md @@ -275,6 +275,22 @@ Using this package you can also declare `LoggingFilterSwitch`-es in custom secti Level updates to switches are also respected for a dynamic update. +Since version 4.0.0, filter switches are exposed through a callback on the reader options so that a reference can be kept: + +```csharp +var filterSwitches = new Dictionary(); +var options = new ConfigurationReaderOptions +{ + OnFilterSwitchCreated = (switchName, filterSwitch) => filterSwitches[switchName] = filterSwitch +}; + +var logger = new LoggerConfiguration() + .ReadFrom.Configuration(configuration, options) + .CreateLogger(); + +ILoggingFilterSwitch filterSwitch = filterSwitches["$filterSwitch"]; +``` + ### Nested configuration sections Some Serilog packages require a reference to a logger configuration object. The sample program in this project illustrates this with the following entry configuring the _[Serilog.Sinks.Async](https://github.com/serilog/serilog-sinks-async)_ package to wrap the _[Serilog.Sinks.File](https://github.com/serilog/serilog-sinks-file)_ package. The `configure` parameter references the File sink configuration: diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 6840073..67459d2 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -75,7 +75,8 @@ void ProcessFilterSwitchDeclarations() SetFilterSwitch(throwOnError: true); SubscribeToFilterExpressionChanges(); - _resolutionContext.AddFilterSwitch(switchName, filterSwitch); + var referenceName = _resolutionContext.AddFilterSwitch(switchName, filterSwitch); + _resolutionContext.ReaderOptions.OnFilterSwitchCreated?.Invoke(referenceName, filterSwitch); void SubscribeToFilterExpressionChanges() { diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs index 7eedf70..d0696a6 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs @@ -77,6 +77,12 @@ public ConfigurationReaderOptions() : this(dependencyContext: null) /// public Action? OnLevelSwitchCreated { get; init; } + /// + /// Called when a log filter switch is created while reading the Serilog:FilterSwitches section of the configuration. + /// The switch name includes the leading $ character. + /// + public Action? OnFilterSwitchCreated { get; init; } + internal Assembly[]? Assemblies { get; } internal DependencyContext? DependencyContext { get; } internal ConfigurationAssemblySource? ConfigurationAssemblySource { get; } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ILoggingFilterSwitch.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ILoggingFilterSwitch.cs new file mode 100644 index 0000000..321295b --- /dev/null +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ILoggingFilterSwitch.cs @@ -0,0 +1,16 @@ +namespace Serilog.Settings.Configuration; + +/// +/// A log event filter that can be modified at runtime. +/// +/// +/// Under the hood, the logging filter switch is either a Serilog.Expressions.LoggingFilterSwitch or a Serilog.Filters.Expressions.LoggingFilterSwitch instance. +/// +public interface ILoggingFilterSwitch +{ + /// + /// A filter expression against which log events will be tested. + /// Only expressions that evaluate to true are included by the filter. A null expression will accept all events. + /// + public string? Expression { get; set; } +} diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/LoggingFilterSwitchProxy.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/LoggingFilterSwitchProxy.cs index 3b4997d..8da70d0 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/LoggingFilterSwitchProxy.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/LoggingFilterSwitchProxy.cs @@ -1,6 +1,6 @@ namespace Serilog.Settings.Configuration; -class LoggingFilterSwitchProxy +class LoggingFilterSwitchProxy : ILoggingFilterSwitch { readonly Action _setProxy; readonly Func _getProxy; diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 942fad9..23f41e2 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -1547,4 +1547,35 @@ public void TestLogLevelSwitchesCallback(string switchName) var systemThreading = Assert.Contains("System.Threading", switches); Assert.Equal(LogEventLevel.Debug, systemThreading.MinimumLevel); } + + [Theory] + [InlineData("$switch1", "$switch2")] + [InlineData("$switch1", "switch2")] + [InlineData("switch1", "$switch2")] + [InlineData("switch1", "switch2")] + public void TestLogFilterSwitchesCallback(string switch1Name, string switch2Name) + { + var json = $$""" + { + 'Serilog': { + 'FilterSwitches': { + '{{switch1Name}}': 'Prop = 1', + '{{switch2Name}}': 'Prop = 2' + } + } + } + """; + + IDictionary switches = new Dictionary(); + var readerOptions = new ConfigurationReaderOptions { OnFilterSwitchCreated = (name, filterSwitch) => switches[name] = filterSwitch }; + ConfigFromJson(json, options: readerOptions); + + Assert.Equal(2, switches.Count); + + var switch1 = Assert.Contains("$switch1", switches); + Assert.Equal("Prop = 1", switch1.Expression); + + var switch2 = Assert.Contains("$switch2", switches); + Assert.Equal("Prop = 2", switch2.Expression); + } } diff --git a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt index 4c7fc7c..ca43b43 100644 --- a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt +++ b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt @@ -46,7 +46,12 @@ namespace Serilog.Settings.Configuration public bool AllowInternalMethods { get; init; } public bool AllowInternalTypes { get; init; } public System.IFormatProvider? FormatProvider { get; init; } + public System.Action? OnFilterSwitchCreated { get; init; } public System.Action? OnLevelSwitchCreated { get; init; } public string? SectionName { get; init; } } + public interface ILoggingFilterSwitch + { + string? Expression { get; set; } + } } \ No newline at end of file From 7f5c27fcc3f240aae62d8be34deea743278712ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Mon, 8 May 2023 08:34:37 +0200 Subject: [PATCH 2/2] Do not force filter switch names with a leading $ in OnFilterSwitchCreated callback --- README.md | 2 +- .../Configuration/ConfigurationReader.cs | 4 ++-- .../ConfigurationReaderOptions.cs | 1 - .../Configuration/ResolutionContext.cs | 3 +-- .../ConfigurationSettingsTests.cs | 21 ++++++++----------- 5 files changed, 13 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index f2f2630..5ea2c23 100644 --- a/README.md +++ b/README.md @@ -288,7 +288,7 @@ var logger = new LoggerConfiguration() .ReadFrom.Configuration(configuration, options) .CreateLogger(); -ILoggingFilterSwitch filterSwitch = filterSwitches["$filterSwitch"]; +ILoggingFilterSwitch filterSwitch = filterSwitches["filterSwitch"]; ``` ### Nested configuration sections diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 67459d2..2a6c317 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -75,8 +75,8 @@ void ProcessFilterSwitchDeclarations() SetFilterSwitch(throwOnError: true); SubscribeToFilterExpressionChanges(); - var referenceName = _resolutionContext.AddFilterSwitch(switchName, filterSwitch); - _resolutionContext.ReaderOptions.OnFilterSwitchCreated?.Invoke(referenceName, filterSwitch); + _resolutionContext.AddFilterSwitch(switchName, filterSwitch); + _resolutionContext.ReaderOptions.OnFilterSwitchCreated?.Invoke(switchName, filterSwitch); void SubscribeToFilterExpressionChanges() { diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs index d0696a6..4510aa9 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs @@ -79,7 +79,6 @@ public ConfigurationReaderOptions() : this(dependencyContext: null) /// /// Called when a log filter switch is created while reading the Serilog:FilterSwitches section of the configuration. - /// The switch name includes the leading $ character. /// public Action? OnFilterSwitchCreated { get; init; } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs index d002620..61d40d8 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs @@ -62,13 +62,12 @@ public string AddLevelSwitch(string levelSwitchName, LoggingLevelSwitch levelSwi return referenceName; } - public string AddFilterSwitch(string filterSwitchName, LoggingFilterSwitchProxy filterSwitch) + public void AddFilterSwitch(string filterSwitchName, LoggingFilterSwitchProxy filterSwitch) { if (filterSwitchName == null) throw new ArgumentNullException(nameof(filterSwitchName)); if (filterSwitch == null) throw new ArgumentNullException(nameof(filterSwitch)); var referenceName = ToSwitchReference(filterSwitchName); _declaredFilterSwitches[referenceName] = filterSwitch; - return referenceName; } string ToSwitchReference(string switchName) diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 23f41e2..4916f35 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -1548,19 +1548,16 @@ public void TestLogLevelSwitchesCallback(string switchName) Assert.Equal(LogEventLevel.Debug, systemThreading.MinimumLevel); } - [Theory] - [InlineData("$switch1", "$switch2")] - [InlineData("$switch1", "switch2")] - [InlineData("switch1", "$switch2")] - [InlineData("switch1", "switch2")] - public void TestLogFilterSwitchesCallback(string switch1Name, string switch2Name) + [Fact] + public void TestLogFilterSwitchesCallback() { - var json = $$""" + // language=json + var json = """ { - 'Serilog': { - 'FilterSwitches': { - '{{switch1Name}}': 'Prop = 1', - '{{switch2Name}}': 'Prop = 2' + "Serilog": { + "FilterSwitches": { + "switch1": "Prop = 1", + "$switch2": "Prop = 2" } } } @@ -1572,7 +1569,7 @@ public void TestLogFilterSwitchesCallback(string switch1Name, string switch2Name Assert.Equal(2, switches.Count); - var switch1 = Assert.Contains("$switch1", switches); + var switch1 = Assert.Contains("switch1", switches); Assert.Equal("Prop = 1", switch1.Expression); var switch2 = Assert.Contains("$switch2", switches);