diff --git a/CHANGES.md b/CHANGES.md index 6b7a83c..fcf5d51 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,12 +2,14 @@ 3.2.0 (pre-release) +* #162 - LoggingFilterSwitch support * #202 - added support to AuditTo.Logger * #203 - added support for custom types in arrays and custom collections * #218 - fixed an issue with `dotnet restore` with `rid` specified if referenced from `netstandard` project * #219 - reduced search graph for configuration dlls to avoid native assets * #221 - added support for conditional/leveled enrichers from Serilog 2.9+ * #222 - updated Microsoft.Extensions.DependencyModel +* #231 - make '$' sign optional for minimum level / filter switch declarations * #237 - DependencyContextAssemblyFinder fix: check `serilog` at the start of the name for any dependent package * #239 - handle NotSupportedException for .net 5.0 single file applications diff --git a/Directory.Build.props b/Directory.Build.props index 7a4f2d1..03b68ca 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,8 @@ latest + True + diff --git a/README.md b/README.md index b01e548..c120063 100644 --- a/README.md +++ b/README.md @@ -140,12 +140,12 @@ Any changes for `Default`, `Microsoft`, `System` sources will be applied at runt (Note: only existing sources are respected for a dynamic update. Inserting new records in `Override` section is **not** supported.) -You can also declare `LoggingLevelSwitch`-es in custom section and reference them for Sink parameters: +You can also declare `LoggingLevelSwitch`-es in custom section and reference them for sink parameters: ```json { "Serilog": { - "LevelSwitches": { "$controlSwitch": "Verbose" }, + "LevelSwitches": { "controlSwitch": "Verbose" }, "WriteTo": [ { "Name": "Seq", @@ -205,7 +205,7 @@ This section defines a static list of key-value pairs that will enrich log event ### Filter section -This section defines filters that will be applied to log events. It is especially usefull in combination with _[Serilog.Filters.Expression](https://github.com/serilog/serilog-filters-expressions)_ package so you can write expression in text form: +This section defines filters that will be applied to log events. It is especially usefull in combination with _[Serilog.Filters.Expression](https://github.com/serilog/serilog-expressions)_ (or legacy _[Serilog.Filters.Expression](https://github.com/serilog/serilog-filters-expressions)_) package so you can write expression in text form: ```json "Filter": [{ @@ -216,6 +216,25 @@ This section defines filters that will be applied to log events. It is especiall }] ``` +Using this package you can also declare `LoggingFilterSwitch`-es in custom section and reference them for filter parameters: + +```json +{ + "Serilog": { + "FilterSwitches": { "filterSwitch": "Application = 'Sample'" }, + "Filter": [ + { + "Name": "ControlledBy", + "Args": { + "switch": "$filterSwitch" + } + } + ] +} +``` + +Level updates to switches are also respected for a dynamic update. + ### 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/sample/Sample/Program.cs b/sample/Sample/Program.cs index 5fb611e..726df35 100644 --- a/sample/Sample/Program.cs +++ b/sample/Sample/Program.cs @@ -10,7 +10,6 @@ using Serilog; using Serilog.Core; using Serilog.Events; -using System.Collections.Generic; using Serilog.Debugging; namespace Sample @@ -19,6 +18,8 @@ public class Program { public static void Main(string[] args) { + SelfLog.Enable(Console.Error); + Thread.CurrentThread.Name = "Main thread"; var configuration = new ConfigurationBuilder() diff --git a/sample/Sample/appsettings.json b/sample/Sample/appsettings.json index 3a31dbf..0d89e71 100644 --- a/sample/Sample/appsettings.json +++ b/sample/Sample/appsettings.json @@ -1,7 +1,7 @@ { "Serilog": { "Using": [ "Serilog.Sinks.Console" ], - "LevelSwitches": { "$controlSwitch": "Verbose" }, + "LevelSwitches": { "controlSwitch": "Verbose" }, "FilterSwitches": { "$filterSwitch": "Application = 'Sample'" }, "MinimumLevel": { "Default": "Debug", diff --git a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj index 02ef562..e54b5a5 100644 --- a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj +++ b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj @@ -31,7 +31,7 @@ - + diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 1c2d619..720d56d 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -3,9 +3,10 @@ using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; + using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Primitives; -using System.Text.RegularExpressions; using Serilog.Configuration; using Serilog.Core; @@ -17,7 +18,7 @@ namespace Serilog.Settings.Configuration { class ConfigurationReader : IConfigurationReader { - const string LevelSwitchNameRegex = @"^\$[A-Za-z]+[A-Za-z0-9]*$"; + const string LevelSwitchNameRegex = @"^\${0,1}[A-Za-z]+[A-Za-z0-9]*$"; readonly IConfigurationSection _section; readonly IReadOnlyCollection _configurationAssemblies; @@ -68,7 +69,7 @@ void ProcessFilterSwitchDeclarations() // switchName must be something like $switch to avoid ambiguities if (!IsValidSwitchName(switchName)) { - throw new FormatException($"\"{switchName}\" is not a valid name for a Filter Switch declaration. Filter switch must be declared with a '$' sign, like \"FilterSwitches\" : {{\"$switchName\" : \"{{FilterExpression}}\"}}"); + throw new FormatException($"\"{switchName}\" is not a valid name for a Filter Switch declaration. The first character of the name must be a letter or '$' sign, like \"FilterSwitches\" : {{\"$switchName\" : \"{{FilterExpression}}\"}}"); } SetFilterSwitch(throwOnError: true); @@ -118,7 +119,7 @@ void ProcessLevelSwitchDeclarations() // switchName must be something like $switch to avoid ambiguities if (!IsValidSwitchName(switchName)) { - throw new FormatException($"\"{switchName}\" is not a valid name for a Level Switch declaration. Level switch must be declared with a '$' sign, like \"LevelSwitches\" : {{\"$switchName\" : \"InitialLevel\"}}"); + throw new FormatException($"\"{switchName}\" is not a valid name for a Level Switch declaration. The first character of the name must be a letter or '$' sign, like \"LevelSwitches\" : {{\"$switchName\" : \"InitialLevel\"}}"); } LoggingLevelSwitch newSwitch; diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs index 91ce8dc..2963a64 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs @@ -67,14 +67,19 @@ public void AddLevelSwitch(string levelSwitchName, LoggingLevelSwitch levelSwitc { if (levelSwitchName == null) throw new ArgumentNullException(nameof(levelSwitchName)); if (levelSwitch == null) throw new ArgumentNullException(nameof(levelSwitch)); - _declaredLevelSwitches[levelSwitchName] = levelSwitch; + _declaredLevelSwitches[ToSwitchReference(levelSwitchName)] = levelSwitch; } public void AddFilterSwitch(string filterSwitchName, LoggingFilterSwitchProxy filterSwitch) { if (filterSwitchName == null) throw new ArgumentNullException(nameof(filterSwitchName)); if (filterSwitch == null) throw new ArgumentNullException(nameof(filterSwitch)); - _declaredFilterSwitches[filterSwitchName] = filterSwitch; + _declaredFilterSwitches[ToSwitchReference(filterSwitchName)] = filterSwitch; + } + + string ToSwitchReference(string switchName) + { + return switchName.StartsWith("$") ? switchName : $"${switchName}"; } } } diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index ed2e62b..472b0f9 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -305,8 +305,10 @@ public void SinksAreConfiguredWithStaticMember() [Theory] [InlineData("$switchName", true)] [InlineData("$SwitchName", true)] + [InlineData("SwitchName", true)] [InlineData("$switch1", true)] [InlineData("$sw1tch0", true)] + [InlineData("sw1tch0", true)] [InlineData("$SWITCHNAME", true)] [InlineData("$$switchname", false)] [InlineData("$switchname$", false)] @@ -326,32 +328,34 @@ public void LoggingLevelSwitchNameValidityScenarios(string switchName, bool expe public void LoggingLevelSwitchWithInvalidNameThrowsFormatException() { var json = @"{ - ""Serilog"": { - ""LevelSwitches"": {""switchNameNotStartingWithDollar"" : ""Warning"" } + ""Serilog"": { + ""LevelSwitches"": {""1InvalidSwitchName"" : ""Warning"" } } }"; var ex = Assert.Throws(() => ConfigFromJson(json)); - Assert.Contains("\"switchNameNotStartingWithDollar\"", ex.Message); + Assert.Contains("\"1InvalidSwitchName\"", ex.Message); Assert.Contains("'$' sign", ex.Message); Assert.Contains("\"LevelSwitches\" : {\"$switchName\" :", ex.Message); } - [Fact] - public void LoggingFilterSwitchIsConfigured() + [Theory] + [InlineData("$mySwitch")] + [InlineData("mySwitch")] + public void LoggingFilterSwitchIsConfigured(string switchName) { - var json = @"{ - 'Serilog': { - 'FilterSwitches': { '$mySwitch': 'Prop = 42' }, - 'Filter:BySwitch': { + var json = $@"{{ + 'Serilog': {{ + 'FilterSwitches': {{ '{switchName}': 'Prop = 42' }}, + 'Filter:BySwitch': {{ 'Name': 'ControlledBy', - 'Args': { + 'Args': {{ 'switch': '$mySwitch' - } - } - } - }"; + }} + }} + }} + }}"; LogEvent evt = null; var log = ConfigFromJson(json) @@ -365,17 +369,19 @@ public void LoggingFilterSwitchIsConfigured() Assert.NotNull(evt); } - [Fact] - public void LoggingLevelSwitchIsConfigured() + [Theory] + [InlineData("$switch1")] + [InlineData("switch1")] + public void LoggingLevelSwitchIsConfigured(string switchName) { - var json = @"{ - ""Serilog"": { - ""LevelSwitches"": {""$switch1"" : ""Warning"" }, - ""MinimumLevel"" : { - ""ControlledBy"" : ""$switch1"" - } - } - }"; + var json = $@"{{ + 'Serilog': {{ + 'LevelSwitches': {{ '{switchName}' : 'Warning' }}, + 'MinimumLevel' : {{ + 'ControlledBy' : '$switch1' + }} + }} + }}"; LogEvent evt = null; var log = ConfigFromJson(json)