diff --git a/README.md b/README.md index b1ebd24..5ea2c23 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..2a6c317 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -76,6 +76,7 @@ void ProcessFilterSwitchDeclarations() SubscribeToFilterExpressionChanges(); _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 7eedf70..4510aa9 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs @@ -77,6 +77,11 @@ 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. + /// + 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/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 942fad9..4916f35 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -1547,4 +1547,32 @@ public void TestLogLevelSwitchesCallback(string switchName) var systemThreading = Assert.Contains("System.Threading", switches); Assert.Equal(LogEventLevel.Debug, systemThreading.MinimumLevel); } + + [Fact] + public void TestLogFilterSwitchesCallback() + { + // language=json + var json = """ + { + "Serilog": { + "FilterSwitches": { + "switch1": "Prop = 1", + "$switch2": "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