From f724410169c166daf4936f23258f9651b5c046c4 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 15 Mar 2018 09:26:21 +1000 Subject: [PATCH 01/61] Dev (major) version bump, updating dependency versions --- .../Serilog.Settings.Configuration.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj index 8d6e342..fb72dd5 100644 --- a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj +++ b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj @@ -2,7 +2,7 @@ Microsoft.Extensions.Configuration (appsettings.json) support for Serilog. - 2.6.0 + 3.0.0 Serilog Contributors net451;netstandard1.6 true From 5f35b907c920db830936ff0816f4c590f3abcec8 Mon Sep 17 00:00:00 2001 From: Matthew Erbs Date: Thu, 5 Apr 2018 09:16:16 +1000 Subject: [PATCH 02/61] Travis build files and update to NetCore 2. --- .travis.yml | 11 +++++++++++ build.sh | 13 +++++++++++++ .../Serilog.Settings.Configuration.Tests.csproj | 2 +- test/TestDummies/TestDummies.csproj | 2 +- 4 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 .travis.yml create mode 100644 build.sh diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..d70d01d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +language: csharp + +matrix: + include: + - os: linux + dist: trusty + sudo: required + dotnet: 2.1.4 + group: edge +script: + - ./build.sh \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..b389e68 --- /dev/null +++ b/build.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e +dotnet --info +dotnet restore + +for path in src/**/*.csproj; do + dotnet build -f netstandard1.6 -c Release ${path} +done + +for path in test/*.Tests/*.csproj; do + dotnet test -f netcoreapp2.0 -c Release ${path} +done \ No newline at end of file diff --git a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj index a7ff7c7..e817514 100644 --- a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj +++ b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj @@ -1,7 +1,7 @@  - net452;netcoreapp1.0 + net452;netcoreapp2.0 Serilog.Settings.Configuration.Tests ../../assets/Serilog.snk true diff --git a/test/TestDummies/TestDummies.csproj b/test/TestDummies/TestDummies.csproj index d1e5399..b0d89c0 100644 --- a/test/TestDummies/TestDummies.csproj +++ b/test/TestDummies/TestDummies.csproj @@ -1,7 +1,7 @@  - net452;netstandard1.3 + net452;netstandard2.0 TestDummies ../../assets/Serilog.snk true From aabcbd3b0bb279a69e44ad06077a4dad12aa5fe9 Mon Sep 17 00:00:00 2001 From: Matthew Erbs Date: Thu, 5 Apr 2018 09:48:10 +1000 Subject: [PATCH 03/61] Added Sample to build on Travis. --- .gitignore | 3 +++ build.sh | 5 ++++- sample/Sample/Sample.csproj | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 0ced0ba..0bb311c 100644 --- a/.gitignore +++ b/.gitignore @@ -196,3 +196,6 @@ FakesAssemblies/ *.opt project.lock.json + +#Test files +*.txt \ No newline at end of file diff --git a/build.sh b/build.sh index b389e68..fe5c82e 100644 --- a/build.sh +++ b/build.sh @@ -10,4 +10,7 @@ done for path in test/*.Tests/*.csproj; do dotnet test -f netcoreapp2.0 -c Release ${path} -done \ No newline at end of file +done + +cd sample/Sample/ +dotnet run -f netcoreapp2.0 -c Release \ No newline at end of file diff --git a/sample/Sample/Sample.csproj b/sample/Sample/Sample.csproj index a7ec218..2bc43fc 100644 --- a/sample/Sample/Sample.csproj +++ b/sample/Sample/Sample.csproj @@ -1,7 +1,7 @@  - net46;netcoreapp1.0 + net46;netcoreapp2.0 Exe From e0ac44f88a595a2e768ad8652d5f5c83836228b5 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Fri, 16 Mar 2018 06:56:02 +1000 Subject: [PATCH 04/61] Fixes #91 - wrong parameter names on surrogate WriteTo.Logger() configuration method --- sample/Sample/Sample.csproj | 6 ++++++ sample/Sample/appsettings.json | 4 ++-- .../Settings/Configuration/ConfigurationReader.cs | 10 +++++++--- .../Configuration/ConfigurationValueSyntax.cs | 14 ++++++++++++++ 4 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationValueSyntax.cs diff --git a/sample/Sample/Sample.csproj b/sample/Sample/Sample.csproj index 2bc43fc..69e0e28 100644 --- a/sample/Sample/Sample.csproj +++ b/sample/Sample/Sample.csproj @@ -23,4 +23,10 @@ + + + PreserveNewest + + + diff --git a/sample/Sample/appsettings.json b/sample/Sample/appsettings.json index 8ac80fe..db91572 100644 --- a/sample/Sample/appsettings.json +++ b/sample/Sample/appsettings.json @@ -17,7 +17,7 @@ "Name": "Console", "Args": { "outputTemplate": "[{Timestamp:HH:mm:ss} {SourceContext} [{Level}] {Message}{NewLine}{Exception}", - "theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console" + "theme": "Serilog.Sinks.SystemConsole.Themes.SystemConsoleTheme::Grayscale, Serilog.Sinks.Console" } } ] @@ -58,4 +58,4 @@ } ] } -} \ No newline at end of file +} diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 1a58212..8b50e08 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -329,7 +329,7 @@ internal static IList FindSinkConfigurationMethods(IReadOnlyCollecti { var found = FindConfigurationMethods(configurationAssemblies, typeof(LoggerSinkConfiguration)); if (configurationAssemblies.Contains(typeof(LoggerSinkConfiguration).GetTypeInfo().Assembly)) - found.Add(GetSurrogateConfigurationMethod, LoggingLevelSwitch>((c, a, s) => Logger(c, a, s))); + found.Add(GetSurrogateConfigurationMethod, LoggingLevelSwitch>((c, a, s) => Logger(c, a, LevelAlias.Minimum, s))); return found; } @@ -380,8 +380,12 @@ internal static LoggerConfiguration FromLogContext(LoggerEnrichmentConfiguration => loggerEnrichmentConfiguration.FromLogContext(); // Unlike the other configuration methods, Logger is an instance method rather than an extension. - internal static LoggerConfiguration Logger(LoggerSinkConfiguration loggerSinkConfiguration, Action configureLogger, LoggingLevelSwitch restrictedToMinimumLevel = null) - => loggerSinkConfiguration.Logger(configureLogger, levelSwitch: restrictedToMinimumLevel); + internal static LoggerConfiguration Logger( + LoggerSinkConfiguration loggerSinkConfiguration, + Action configureLogger, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch levelSwitch = null) + => loggerSinkConfiguration.Logger(configureLogger, restrictedToMinimumLevel, levelSwitch); internal static MethodInfo GetSurrogateConfigurationMethod(Expression> method) => (method.Body as MethodCallExpression)?.Method; diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationValueSyntax.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationValueSyntax.cs new file mode 100644 index 0000000..0569531 --- /dev/null +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationValueSyntax.cs @@ -0,0 +1,14 @@ +using System.Text.RegularExpressions; + +namespace Serilog.Settings.Configuration +{ + static class ConfigurationValueSyntax + { + const string LevelSwitchNameRegex = @"^\$[A-Za-z]+[A-Za-z0-9]*$"; + + public static bool IsValidSwitchName(string input) + { + return Regex.IsMatch(input, LevelSwitchNameRegex); + } + } +} \ No newline at end of file From a2018842b70280a0336bc9c3be2bc26c68217b3d Mon Sep 17 00:00:00 2001 From: Matthew Erbs Date: Thu, 5 Apr 2018 12:23:26 +1000 Subject: [PATCH 05/61] Added tests from #91 comments --- .../ConfigurationSettingsTests.cs | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 5a627a7..bdb5c6a 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -390,5 +390,73 @@ public void LoggingLevelSwitchCanBeUsedForMinimumLevelOverrides() Assert.False(evt is null, "LoggingLevelSwitch level was changed to Information for logger System.*. It should now log Information events for SourceContext System.Bar."); // ReSharper restore HeuristicUnreachableCode } + + + [Trait("Bugfix", "#91")] + [Fact] + public void WriteToLoggerWithRestrictedToMinimumLevelIsSupported() + { + var json = @"{ + ""Serilog"": { + ""Using"": [""TestDummies""], + ""WriteTo"": [{ + ""Name"": ""Logger"", + ""Args"": { + ""configureLogger"" : { + ""WriteTo"": [{ + ""Name"": ""DummyRollingFile"", + ""Args"": {""pathFormat"" : ""C:\\""} + }]}, + ""restrictedToMinimumLevel"": ""Warning"" + } + }] + } + }"; + + var log = ConfigFromJson(json) + .CreateLogger(); + + DummyRollingFileSink.Emitted.Clear(); + + log.Write(Some.InformationEvent()); + log.Write(Some.WarningEvent()); + + Assert.Equal(1, DummyRollingFileSink.Emitted.Count); + } + + [Trait("Bugfix", "#91")] + [Fact] + public void WriteToSubLoggerWithLevelSwitchIsSupported() + { + var json = @"{ + ""Serilog"": { + ""Using"": [""TestDummies""], + ""LevelSwitches"": {""$switch1"" : ""Warning"" }, + ""MinimumLevel"" : { + ""ControlledBy"" : ""$switch1"" + }, + ""WriteTo"": [{ + ""Name"": ""Logger"", + ""Args"": { + ""configureLogger"" : { + ""WriteTo"": [{ + ""Name"": ""DummyRollingFile"", + ""Args"": {""pathFormat"" : ""C:\\""} + }]} + } + }] + } + }"; + + var log = ConfigFromJson(json) + .CreateLogger(); + + DummyRollingFileSink.Emitted.Clear(); + + log.Write(Some.InformationEvent()); + log.Write(Some.WarningEvent()); + + Assert.Equal(1, DummyRollingFileSink.Emitted.Count); + } } } From 30fe4587e80c526faefa09c82026a1dd05e48fa5 Mon Sep 17 00:00:00 2001 From: Matthew Erbs Date: Thu, 5 Apr 2018 12:42:27 +1000 Subject: [PATCH 06/61] Allowed exit on Sample app for build. --- build.sh | 3 ++- sample/Sample/Program.cs | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/build.sh b/build.sh index fe5c82e..27b2e5e 100644 --- a/build.sh +++ b/build.sh @@ -13,4 +13,5 @@ for path in test/*.Tests/*.csproj; do done cd sample/Sample/ -dotnet run -f netcoreapp2.0 -c Release \ No newline at end of file +dotnet build -f netcoreapp2.0 -c Release +dotnet bin/Release/netcoreapp2.0/Sample.dll --run-once diff --git a/sample/Sample/Program.cs b/sample/Sample/Program.cs index 95e6989..be63efb 100644 --- a/sample/Sample/Program.cs +++ b/sample/Sample/Program.cs @@ -3,7 +3,7 @@ using Microsoft.Extensions.Configuration; using Serilog; using System.IO; - +using System.Linq; using Serilog.Core; using Serilog.Events; @@ -22,6 +22,8 @@ public static void Main(string[] args) .ReadFrom.Configuration(configuration) .CreateLogger(); + logger.Information("Args: {a}", args); + do { logger.ForContext().Information("Hello, world!"); @@ -32,7 +34,7 @@ public static void Main(string[] args) Console.WriteLine(); } - while (Console.ReadKey().KeyChar != 'q'); + while (!args.Contains("--run-once") && (Console.ReadKey().KeyChar != 'q')); } } From b1668abc37b2bbbf977403a9ecc68bb9feee66cf Mon Sep 17 00:00:00 2001 From: Matthew Erbs Date: Thu, 5 Apr 2018 12:49:57 +1000 Subject: [PATCH 07/61] Remove .Net Core 1 install scripts. --- appveyor.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index f17ab19..467d351 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,12 +2,6 @@ version: '{build}' skip_tags: true image: Visual Studio 2017 configuration: Release -install: - - ps: mkdir -Force ".\build\" | Out-Null - - ps: Invoke-WebRequest "https://raw.githubusercontent.com/dotnet/cli/rel/1.0.0/scripts/obtain/dotnet-install.ps1" -OutFile ".\build\installcli.ps1" - - ps: $env:DOTNET_INSTALL_DIR = "$pwd\.dotnetcli" - - ps: '& .\build\installcli.ps1 -InstallDir "$env:DOTNET_INSTALL_DIR" -NoPath -Version 1.0.1' - - ps: $env:Path = "$env:DOTNET_INSTALL_DIR;$env:Path" build_script: - ps: ./Build.ps1 test: off From 0097364daf8097b4126e1423932cd6c4223f299f Mon Sep 17 00:00:00 2001 From: Matthew Erbs Date: Thu, 5 Apr 2018 12:57:03 +1000 Subject: [PATCH 08/61] Set execute permissions on build. --- build.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 build.sh diff --git a/build.sh b/build.sh old mode 100644 new mode 100755 From 6c9d9dcbdbfcc0f4d4cbc65b40fcf1c7c5de3a4e Mon Sep 17 00:00:00 2001 From: Adam Chester Date: Thu, 5 Apr 2018 15:17:09 +1000 Subject: [PATCH 09/61] README: Use `Console` instead of `LiterateConsole` because [serilog-sinks-literate is being retired](https://github.com/serilog/serilog-sinks-literate#this-package-is-being-retired). --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index de71252..18b5638 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,10 @@ Configuration is read from the `Serilog` section. ```json { "Serilog": { - "Using": ["Serilog.Sinks.Literate"], + "Using": ["Serilog.Sinks.Console"], "MinimumLevel": "Debug", "WriteTo": [ - { "Name": "LiterateConsole" }, + { "Name": "Console" }, { "Name": "File", "Args": { "path": "%TEMP%\\Logs\\serilog-configuration-sample.txt" } } ], "Enrich": ["FromLogContext", "WithMachineName", "WithThreadId"], @@ -21,7 +21,7 @@ Configuration is read from the `Serilog` section. } ``` -This example relies on the _Serilog.Sinks.Literate_, _Serilog.Sinks.File_, _Serilog.Enrichers.Environment_, _Serilog.Settings.Configuration_ and _Serilog.Enrichers.Thread_ packages also being installed. +This example relies on the _Serilog.Sinks.Console_, _Serilog.Sinks.File_, _Serilog.Enrichers.Environment_, _Serilog.Settings.Configuration_ and _Serilog.Enrichers.Thread_ packages also being installed. After installing this package, use `ReadFrom.Configuration()` and pass an `IConfiguration` object. @@ -46,7 +46,7 @@ public class Program The `WriteTo` and `Enrich` sections support the same syntax, for example the following is valid if no arguments are needed by the sinks: ```json -"WriteTo": ["LiterateConsole", "DiagnosticTrace"] +"WriteTo": ["Console", "DiagnosticTrace"] ``` Or alternatively, the long-form (`"Name":` ...) syntax from the first example can be used when arguments need to be supplied. From 4bf535cfd4a5f50b30ea0f3286a17b318a760be8 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Tue, 10 Apr 2018 05:53:59 -0400 Subject: [PATCH 10/61] project files updated --- sample/Sample/Sample.csproj | 9 +++++++- .../Serilog.Settings.Configuration.csproj | 16 ++++++++------ ...erilog.Settings.Configuration.Tests.csproj | 21 +++++++++++++++++-- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/sample/Sample/Sample.csproj b/sample/Sample/Sample.csproj index 69e0e28..1b69583 100644 --- a/sample/Sample/Sample.csproj +++ b/sample/Sample/Sample.csproj @@ -13,8 +13,15 @@ - + + + + + + + + diff --git a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj index fb72dd5..a674efd 100644 --- a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj +++ b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj @@ -4,7 +4,7 @@ Microsoft.Extensions.Configuration (appsettings.json) support for Serilog. 3.0.0 Serilog Contributors - net451;netstandard1.6 + netstandard2.0;net451 true true Serilog.Settings.Configuration @@ -16,18 +16,22 @@ https://serilog.net/images/serilog-configuration-nuget.png https://github.com/serilog/serilog-settings-configuration https://www.apache.org/licenses/LICENSE-2.0 - - - true Serilog - - + + + + + + + + + $(DefineConstants);APPDOMAIN diff --git a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj index e817514..deb329a 100644 --- a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj +++ b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj @@ -1,5 +1,16 @@  + + net452;netcoreapp2.0 Serilog.Settings.Configuration.Tests @@ -13,12 +24,18 @@ + + + + + + + + - - From 8ccd28ce183eaaee0c6efde5471ca0787f848c99 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Tue, 10 Apr 2018 09:38:18 -0400 Subject: [PATCH 11/61] update to netstandard2.0 --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 27b2e5e..19ab21c 100755 --- a/build.sh +++ b/build.sh @@ -5,7 +5,7 @@ dotnet --info dotnet restore for path in src/**/*.csproj; do - dotnet build -f netstandard1.6 -c Release ${path} + dotnet build -f netstandard2.0 -c Release ${path} done for path in test/*.Tests/*.csproj; do From 9489ad458e8fc783a64b10a3ba030afebf9fbbea Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Wed, 11 Apr 2018 07:43:51 -0400 Subject: [PATCH 12/61] removed APPDOMAIN constant --- .../Serilog.Settings.Configuration.csproj | 4 ---- .../Settings/Configuration/ConfigurationReader.cs | 2 -- 2 files changed, 6 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj index a674efd..1e24c68 100644 --- a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj +++ b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj @@ -32,8 +32,4 @@ - - $(DefineConstants);APPDOMAIN - - diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 8b50e08..3514237 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -286,12 +286,10 @@ where filter(assemblyName.Name) } else { -#if APPDOMAIN query = from outputAssemblyPath in System.IO.Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll") let assemblyFileName = System.IO.Path.GetFileNameWithoutExtension(outputAssemblyPath) where filter(assemblyFileName) select AssemblyName.GetAssemblyName(outputAssemblyPath); -#endif } return query.ToArray(); From 7e148416dfa62cd765dc450fde3adb081ba89b3d Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Tue, 17 Apr 2018 07:29:41 -0400 Subject: [PATCH 13/61] minor formatting cleanup; rollback of direct parm value during method execution --- ...figurationLoggerConfigurationExtensions.cs | 5 ++- .../Configuration/ConfigurationReader.cs | 37 ++++++++++++------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs b/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs index b4e9d34..399ba07 100644 --- a/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs +++ b/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs @@ -26,7 +26,10 @@ namespace Serilog /// public static class ConfigurationLoggerConfigurationExtensions { - const string DefaultSectionName = "Serilog"; + /// + /// Configuration section name required by this package. + /// + public const string DefaultSectionName = "Serilog"; /// /// Reads logger settings from the provided configuration object using the default section name. diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 3514237..c74459b 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -24,6 +24,8 @@ class ConfigurationReader : IConfigurationReader readonly DependencyContext _dependencyContext; readonly IReadOnlyCollection _configurationAssemblies; + #region Constructors + public ConfigurationReader(IConfigurationSection configuration, DependencyContext dependencyContext) { _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); @@ -38,6 +40,11 @@ public ConfigurationReader(IConfigurationSection configuration, DependencyContex _configurationAssemblies = configurationAssemblies ?? throw new ArgumentNullException(nameof(configurationAssemblies)); } + #endregion + + #region Configure and related Apply methods + + // Called by LoggerConfiguration in Serilog Core public void Configure(LoggerConfiguration loggerConfiguration) { var declaredLevelSwitches = ProcessLevelSwitchDeclarations(); @@ -80,8 +87,7 @@ IReadOnlyDictionary ProcessLevelSwitchDeclarations() return namedSwitches; } - void ApplyMinimumLevel(LoggerConfiguration loggerConfiguration, - IReadOnlyDictionary declaredLevelSwitches) + void ApplyMinimumLevel(LoggerConfiguration loggerConfiguration, IReadOnlyDictionary declaredLevelSwitches) { var minimumLevelDirective = _configuration.GetSection("MinimumLevel"); @@ -134,10 +140,7 @@ void ApplyMinimumLevel(IConfigurationSection directive, Action declaredLevelSwitches) + void ApplyFilters(LoggerConfiguration loggerConfiguration, IReadOnlyDictionary declaredLevelSwitches) { var filterDirective = _configuration.GetSection("Filter"); if (filterDirective != null) @@ -147,8 +150,7 @@ void ApplyFilters(LoggerConfiguration loggerConfiguration, } } - void ApplySinks(LoggerConfiguration loggerConfiguration, - IReadOnlyDictionary declaredLevelSwitches) + void ApplySinks(LoggerConfiguration loggerConfiguration, IReadOnlyDictionary declaredLevelSwitches) { var writeToDirective = _configuration.GetSection("WriteTo"); if (writeToDirective != null) @@ -158,8 +160,7 @@ void ApplySinks(LoggerConfiguration loggerConfiguration, } } - void ApplyAuditSinks(LoggerConfiguration loggerConfiguration, - IReadOnlyDictionary declaredLevelSwitches) + void ApplyAuditSinks(LoggerConfiguration loggerConfiguration, IReadOnlyDictionary declaredLevelSwitches) { var auditToDirective = _configuration.GetSection("AuditTo"); if (auditToDirective != null) @@ -169,15 +170,13 @@ void ApplyAuditSinks(LoggerConfiguration loggerConfiguration, } } - void IConfigurationReader.ApplySinks(LoggerSinkConfiguration loggerSinkConfiguration, - IReadOnlyDictionary declaredLevelSwitches) + void IConfigurationReader.ApplySinks(LoggerSinkConfiguration loggerSinkConfiguration, IReadOnlyDictionary declaredLevelSwitches) { var methodCalls = GetMethodCalls(_configuration); CallConfigurationMethods(methodCalls, FindSinkConfigurationMethods(_configurationAssemblies), loggerSinkConfiguration, declaredLevelSwitches); } - void ApplyEnrichment(LoggerConfiguration loggerConfiguration, - IReadOnlyDictionary declaredLevelSwitches) + void ApplyEnrichment(LoggerConfiguration loggerConfiguration, IReadOnlyDictionary declaredLevelSwitches) { var enrichDirective = _configuration.GetSection("Enrich"); if (enrichDirective != null) @@ -196,6 +195,10 @@ void ApplyEnrichment(LoggerConfiguration loggerConfiguration, } } + #endregion + + #region Internal implementation + internal ILookup> GetMethodCalls(IConfigurationSection directive) { var children = directive.GetChildren().ToList(); @@ -369,6 +372,10 @@ internal static IList FindConfigurationMethods(IReadOnlyCollection loggerFilterConfiguration.With(filter); @@ -399,5 +406,7 @@ internal static LogEventLevel ParseLogEventLevel(string value) throw new InvalidOperationException($"The value {value} is not a valid Serilog level."); return parsedLevel; } + + #endregion } } From 90b87bad90572fab614154508a4de76fe1f24471 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Tue, 17 Apr 2018 09:15:32 -0400 Subject: [PATCH 14/61] changed null-ref checks to Any() tests on GetSection results --- .../Configuration/ConfigurationReader.cs | 52 +++++++++---------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index c74459b..227ad83 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -59,31 +59,27 @@ IReadOnlyDictionary ProcessLevelSwitchDeclarations() { var levelSwitchesDirective = _configuration.GetSection("LevelSwitches"); var namedSwitches = new Dictionary(); - if (levelSwitchesDirective != null) + foreach (var levelSwitchDeclaration in levelSwitchesDirective.GetChildren()) { - foreach (var levelSwitchDeclaration in levelSwitchesDirective.GetChildren()) + var switchName = levelSwitchDeclaration.Key; + var switchInitialLevel = levelSwitchDeclaration.Value; + // switchName must be something like $switch to avoid ambiguities + if (!IsValidSwitchName(switchName)) { - var switchName = levelSwitchDeclaration.Key; - var switchInitialLevel = levelSwitchDeclaration.Value; - // 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\"}}"); - } - LoggingLevelSwitch newSwitch; - if (string.IsNullOrEmpty(switchInitialLevel)) - { - newSwitch = new LoggingLevelSwitch(); - } - else - { - var initialLevel = ParseLogEventLevel(switchInitialLevel); - newSwitch = new LoggingLevelSwitch(initialLevel); - } - namedSwitches.Add(switchName, newSwitch); + 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\"}}"); + } + LoggingLevelSwitch newSwitch; + if (string.IsNullOrEmpty(switchInitialLevel)) + { + newSwitch = new LoggingLevelSwitch(); } + else + { + var initialLevel = ParseLogEventLevel(switchInitialLevel); + newSwitch = new LoggingLevelSwitch(initialLevel); + } + namedSwitches.Add(switchName, newSwitch); } - return namedSwitches; } @@ -98,7 +94,7 @@ void ApplyMinimumLevel(LoggerConfiguration loggerConfiguration, IReadOnlyDiction } var minLevelControlledByDirective = minimumLevelDirective.GetSection("ControlledBy"); - if (minLevelControlledByDirective?.Value != null) + if (minLevelControlledByDirective.Value != null) { var globalMinimumLevelSwitch = declaredLevelSwitches.LookUpSwitchByName(minLevelControlledByDirective.Value); // not calling ApplyMinimumLevel local function because here we have a reference to a LogLevelSwitch already @@ -143,7 +139,7 @@ void ApplyMinimumLevel(IConfigurationSection directive, Action declaredLevelSwitches) { var filterDirective = _configuration.GetSection("Filter"); - if (filterDirective != null) + if (filterDirective.GetChildren().Any()) { var methodCalls = GetMethodCalls(filterDirective); CallConfigurationMethods(methodCalls, FindFilterConfigurationMethods(_configurationAssemblies), loggerConfiguration.Filter, declaredLevelSwitches); @@ -153,7 +149,7 @@ void ApplyFilters(LoggerConfiguration loggerConfiguration, IReadOnlyDictionary declaredLevelSwitches) { var writeToDirective = _configuration.GetSection("WriteTo"); - if (writeToDirective != null) + if (writeToDirective.GetChildren().Any()) { var methodCalls = GetMethodCalls(writeToDirective); CallConfigurationMethods(methodCalls, FindSinkConfigurationMethods(_configurationAssemblies), loggerConfiguration.WriteTo, declaredLevelSwitches); @@ -163,7 +159,7 @@ void ApplySinks(LoggerConfiguration loggerConfiguration, IReadOnlyDictionary declaredLevelSwitches) { var auditToDirective = _configuration.GetSection("AuditTo"); - if (auditToDirective != null) + if (auditToDirective.GetChildren().Any()) { var methodCalls = GetMethodCalls(auditToDirective); CallConfigurationMethods(methodCalls, FindAuditSinkConfigurationMethods(_configurationAssemblies), loggerConfiguration.AuditTo, declaredLevelSwitches); @@ -179,14 +175,14 @@ void IConfigurationReader.ApplySinks(LoggerSinkConfiguration loggerSinkConfigura void ApplyEnrichment(LoggerConfiguration loggerConfiguration, IReadOnlyDictionary declaredLevelSwitches) { var enrichDirective = _configuration.GetSection("Enrich"); - if (enrichDirective != null) + if (enrichDirective.GetChildren().Any()) { var methodCalls = GetMethodCalls(enrichDirective); CallConfigurationMethods(methodCalls, FindEventEnricherConfigurationMethods(_configurationAssemblies), loggerConfiguration.Enrich, declaredLevelSwitches); } var propertiesDirective = _configuration.GetSection("Properties"); - if (propertiesDirective != null) + if (propertiesDirective.GetChildren().Any()) { foreach (var enrichProperyDirective in propertiesDirective.GetChildren()) { @@ -250,7 +246,7 @@ IReadOnlyCollection LoadConfigurationAssemblies() var assemblies = new Dictionary(); var usingSection = _configuration.GetSection("Using"); - if (usingSection != null) + if (usingSection.GetChildren().Any()) { foreach (var simpleName in usingSection.GetChildren().Select(c => c.Value)) { From 82375c0d4d17fefda4f1d3f7a9ca2666581e0407 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Wed, 18 Apr 2018 08:34:39 -0400 Subject: [PATCH 15/61] confing binding and config section hinting --- README.md | 34 +++++++++++++++++++ sample/Sample/appsettings.json | 4 +-- .../Serilog.Settings.Configuration.csproj | 4 +++ .../Configuration/BoundArgumentValue.cs | 22 ++++++++++++ .../Configuration/ConfigurationReader.cs | 12 +++++-- 5 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 src/Serilog.Settings.Configuration/Settings/Configuration/BoundArgumentValue.cs diff --git a/README.md b/README.md index 18b5638..b64a451 100644 --- a/README.md +++ b/README.md @@ -91,3 +91,37 @@ For example, to set the minimum log level using the _Windows_ command prompt: set Serilog:MinimumLevel=Debug dotnet run ``` + +### Nested configuration sections + +Some Serilog packages require a reference to a logger configuration object. These must be "hinted" by appending a `>` character to the end of the argument name. The sample program in this project illustrates this with the following entry configuring the _Serilog.Sinks.Async_ package to wrap the _Serilog.Sinks.File_ package. + +Note the `configure>` parameter name with the trailing hint character: + +```json +"WriteTo:Async": { + "Name": "Async", + "Args": { + "configure>": [ + { + "Name": "File", + "Args": { + "path": "%TEMP%\\Logs\\serilog-configuration-sample.txt", + "outputTemplate": "{Timestamp:o} [{Level:u3}] ({Application}/{MachineName}/{ThreadId}) {Message}{NewLine}{Exception}" + } + } + ] + } +}, +``` + +### IConfiguration parameter + +If a Serilog package requires additional external configuration information (for example, access to a `ConnectionStrings` section, which would be outside of the `Serilog` section), the sink should include an `IConfiguration` parameter in the configuration extension method. This package will automatically populate that parameter. It should not be declared in the argument list in the configuration source. + +### Complex parameter value binding + +When the configuration specifies a discrete value for a parameter (such as a string literal), the package will attempt to convert that value to the target method's declared CLR type of the parameter. Additional explicit handling is provided for parsing strings to `Uri` and `TimeSpan` objects and `enum` elements. + +If the parameter value is not a discrete value, the package will use the configuration binding system provided by _Microsoft.Extensions.Options.ConfigurationExtensions_ to attempt to populate the parameter. Almost anything that can be bound by `IConfiguration.Get` should work with this package. An example of this is the optional `List` parameter used to configure the .NET Standard version of the _Serilog.Sinks.MSSqlServer_ package. + diff --git a/sample/Sample/appsettings.json b/sample/Sample/appsettings.json index db91572..c0311e2 100644 --- a/sample/Sample/appsettings.json +++ b/sample/Sample/appsettings.json @@ -11,7 +11,7 @@ "WriteTo:Sublogger": { "Name": "Logger", "Args": { - "configureLogger": { + "configureLogger>": { "WriteTo": [ { "Name": "Console", @@ -28,7 +28,7 @@ "WriteTo:Async": { "Name": "Async", "Args": { - "configure": [ + "configure>": [ { "Name": "File", "Args": { diff --git a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj index 1e24c68..4024707 100644 --- a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj +++ b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj @@ -17,6 +17,8 @@ https://github.com/serilog/serilog-settings-configuration https://www.apache.org/licenses/LICENSE-2.0 Serilog + true + 3.0.0.002 @@ -26,10 +28,12 @@ + + diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/BoundArgumentValue.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/BoundArgumentValue.cs new file mode 100644 index 0000000..ea8c7dc --- /dev/null +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/BoundArgumentValue.cs @@ -0,0 +1,22 @@ +using Microsoft.Extensions.Configuration; +using Serilog.Core; +using System; +using System.Collections.Generic; + +namespace Serilog.Settings.Configuration +{ + class BoundArgumentValue : IConfigurationArgumentValue + { + readonly IConfigurationSection section; + + public BoundArgumentValue(IConfigurationSection section) + { + this.section = section; + } + + public object ConvertTo(Type toType, IReadOnlyDictionary declaredLevelSwitches) + { + return section.Get(toType); + } + } +} diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 227ad83..5f6158e 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -19,6 +19,7 @@ namespace Serilog.Settings.Configuration class ConfigurationReader : IConfigurationReader { const string LevelSwitchNameRegex = @"^\$[A-Za-z]+[A-Za-z0-9]*$"; + const string ConfigSectionHintChar = ">"; readonly IConfigurationSection _configuration; readonly DependencyContext _dependencyContext; @@ -209,7 +210,7 @@ internal ILookup> GetMet let name = GetSectionName(child) let callArgs = (from argument in child.GetSection("Args").GetChildren() select new { - Name = argument.Key, + Name = argument.Key.Replace(ConfigSectionHintChar, string.Empty), Value = GetArgumentValue(argument) }).ToDictionary(p => p.Name, p => p.Value) select new { Name = name, Args = callArgs })) .ToLookup(p => p.Name, p => p.Args); @@ -225,7 +226,14 @@ IConfigurationArgumentValue GetArgumentValue(IConfigurationSection argumentSecti } else { - argumentValue = new ConfigurationSectionArgumentValue(new ConfigurationReader(argumentSection, _configurationAssemblies, _dependencyContext)); + if(argumentSection.Key.EndsWith(ConfigSectionHintChar)) + { + argumentValue = new ConfigurationSectionArgumentValue(new ConfigurationReader(argumentSection, _configurationAssemblies, _dependencyContext)); + } + else + { + argumentValue = new BoundArgumentValue(argumentSection); + } } return argumentValue; From 80b2ec98c56394b9f6901a33c7db64af62f4c652 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Wed, 18 Apr 2018 13:19:39 -0400 Subject: [PATCH 16/61] populate value for an IConfiguration parameter on target method --- ...figurationLoggerConfigurationExtensions.cs | 22 ++++++---- .../Serilog.Settings.Configuration.csproj | 6 ++- .../Configuration/ConfigurationReader.cs | 43 +++++++++++++------ 3 files changed, 47 insertions(+), 24 deletions(-) diff --git a/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs b/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs index 399ba07..9f007fa 100644 --- a/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs +++ b/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs @@ -32,10 +32,12 @@ public static class ConfigurationLoggerConfigurationExtensions public const string DefaultSectionName = "Serilog"; /// - /// Reads logger settings from the provided configuration object using the default section name. + /// Reads logger settings from the provided configuration object using the default section name. Generally this + /// is preferable over the other method that takes a configuration section. Only this version will populate + /// IConfiguration parameters on target methods. /// /// Logger setting configuration. - /// A configuration object with a Serilog section. + /// A configuration object which contains a Serilog section. /// The dependency context from which sink/enricher packages can be located. If not supplied, the platform /// default will be used. /// An object allowing configuration to continue. @@ -45,28 +47,32 @@ public static LoggerConfiguration Configuration( DependencyContext dependencyContext = null) { if (configuration == null) throw new ArgumentNullException(nameof(configuration)); - return settingConfiguration.ConfigurationSection(configuration.GetSection(DefaultSectionName), dependencyContext); + return settingConfiguration.Settings( + new ConfigurationReader( + configuration, + dependencyContext ?? (Assembly.GetEntryAssembly() != null ? DependencyContext.Default : null))); } /// - /// Reads logger settings from the provided configuration section. + /// Reads logger settings from the provided configuration section. Generally it is preferable to use the other + /// extension method that takes the full configuration object. /// /// Logger setting configuration. - /// The Serilog configuration section + /// The Serilog configuration section /// The dependency context from which sink/enricher packages can be located. If not supplied, the platform /// default will be used. /// An object allowing configuration to continue. public static LoggerConfiguration ConfigurationSection( this LoggerSettingsConfiguration settingConfiguration, - IConfigurationSection configuration, + IConfigurationSection configSection, DependencyContext dependencyContext = null) { if (settingConfiguration == null) throw new ArgumentNullException(nameof(settingConfiguration)); - if (configuration == null) throw new ArgumentNullException(nameof(configuration)); + if (configSection == null) throw new ArgumentNullException(nameof(configSection)); return settingConfiguration.Settings( new ConfigurationReader( - configuration, + configSection, dependencyContext ?? (Assembly.GetEntryAssembly() != null ? DependencyContext.Default : null))); } } diff --git a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj index 4024707..99d3b7e 100644 --- a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj +++ b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj @@ -17,8 +17,10 @@ https://github.com/serilog/serilog-settings-configuration https://www.apache.org/licenses/LICENSE-2.0 Serilog - true - 3.0.0.002 + false + 3.0.0 + 3.0.0.0 + 3.0.0 diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 5f6158e..a37e454 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -21,22 +21,35 @@ class ConfigurationReader : IConfigurationReader const string LevelSwitchNameRegex = @"^\$[A-Za-z]+[A-Za-z0-9]*$"; const string ConfigSectionHintChar = ">"; - readonly IConfigurationSection _configuration; + static IConfiguration _configuration; + + readonly IConfigurationSection _section; readonly DependencyContext _dependencyContext; readonly IReadOnlyCollection _configurationAssemblies; #region Constructors - public ConfigurationReader(IConfigurationSection configuration, DependencyContext dependencyContext) + public ConfigurationReader(IConfiguration configuration, DependencyContext dependencyContext) { _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + _section = configuration.GetSection(ConfigurationLoggerConfigurationExtensions.DefaultSectionName); _dependencyContext = dependencyContext; _configurationAssemblies = LoadConfigurationAssemblies(); } - ConfigurationReader(IConfigurationSection configuration, IReadOnlyCollection configurationAssemblies, DependencyContext dependencyContext) + // Generally the initial call should use IConfiguration rather than IConfigurationSection, otherwise + // IConfiguration parameters in the target methods will not be populated. + ConfigurationReader(IConfigurationSection configSection, DependencyContext dependencyContext) { - _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + _section = configSection ?? throw new ArgumentNullException(nameof(configSection)); + _dependencyContext = dependencyContext; + _configurationAssemblies = LoadConfigurationAssemblies(); + } + + // Used internally for processing nested configuration sections -- see GetMethodCalls below. + ConfigurationReader(IConfigurationSection configSection, IReadOnlyCollection configurationAssemblies, DependencyContext dependencyContext) + { + _section = configSection ?? throw new ArgumentNullException(nameof(configSection)); _dependencyContext = dependencyContext; _configurationAssemblies = configurationAssemblies ?? throw new ArgumentNullException(nameof(configurationAssemblies)); } @@ -45,7 +58,6 @@ public ConfigurationReader(IConfigurationSection configuration, DependencyContex #region Configure and related Apply methods - // Called by LoggerConfiguration in Serilog Core public void Configure(LoggerConfiguration loggerConfiguration) { var declaredLevelSwitches = ProcessLevelSwitchDeclarations(); @@ -58,7 +70,7 @@ public void Configure(LoggerConfiguration loggerConfiguration) IReadOnlyDictionary ProcessLevelSwitchDeclarations() { - var levelSwitchesDirective = _configuration.GetSection("LevelSwitches"); + var levelSwitchesDirective = _section.GetSection("LevelSwitches"); var namedSwitches = new Dictionary(); foreach (var levelSwitchDeclaration in levelSwitchesDirective.GetChildren()) { @@ -86,7 +98,7 @@ IReadOnlyDictionary ProcessLevelSwitchDeclarations() void ApplyMinimumLevel(LoggerConfiguration loggerConfiguration, IReadOnlyDictionary declaredLevelSwitches) { - var minimumLevelDirective = _configuration.GetSection("MinimumLevel"); + var minimumLevelDirective = _section.GetSection("MinimumLevel"); var defaultMinLevelDirective = minimumLevelDirective.Value != null ? minimumLevelDirective : minimumLevelDirective.GetSection("Default"); if (defaultMinLevelDirective.Value != null) @@ -139,7 +151,7 @@ void ApplyMinimumLevel(IConfigurationSection directive, Action declaredLevelSwitches) { - var filterDirective = _configuration.GetSection("Filter"); + var filterDirective = _section.GetSection("Filter"); if (filterDirective.GetChildren().Any()) { var methodCalls = GetMethodCalls(filterDirective); @@ -149,7 +161,7 @@ void ApplyFilters(LoggerConfiguration loggerConfiguration, IReadOnlyDictionary declaredLevelSwitches) { - var writeToDirective = _configuration.GetSection("WriteTo"); + var writeToDirective = _section.GetSection("WriteTo"); if (writeToDirective.GetChildren().Any()) { var methodCalls = GetMethodCalls(writeToDirective); @@ -159,7 +171,7 @@ void ApplySinks(LoggerConfiguration loggerConfiguration, IReadOnlyDictionary declaredLevelSwitches) { - var auditToDirective = _configuration.GetSection("AuditTo"); + var auditToDirective = _section.GetSection("AuditTo"); if (auditToDirective.GetChildren().Any()) { var methodCalls = GetMethodCalls(auditToDirective); @@ -169,20 +181,20 @@ void ApplyAuditSinks(LoggerConfiguration loggerConfiguration, IReadOnlyDictionar void IConfigurationReader.ApplySinks(LoggerSinkConfiguration loggerSinkConfiguration, IReadOnlyDictionary declaredLevelSwitches) { - var methodCalls = GetMethodCalls(_configuration); + var methodCalls = GetMethodCalls(_section); CallConfigurationMethods(methodCalls, FindSinkConfigurationMethods(_configurationAssemblies), loggerSinkConfiguration, declaredLevelSwitches); } void ApplyEnrichment(LoggerConfiguration loggerConfiguration, IReadOnlyDictionary declaredLevelSwitches) { - var enrichDirective = _configuration.GetSection("Enrich"); + var enrichDirective = _section.GetSection("Enrich"); if (enrichDirective.GetChildren().Any()) { var methodCalls = GetMethodCalls(enrichDirective); CallConfigurationMethods(methodCalls, FindEventEnricherConfigurationMethods(_configurationAssemblies), loggerConfiguration.Enrich, declaredLevelSwitches); } - var propertiesDirective = _configuration.GetSection("Properties"); + var propertiesDirective = _section.GetSection("Properties"); if (propertiesDirective.GetChildren().Any()) { foreach (var enrichProperyDirective in propertiesDirective.GetChildren()) @@ -253,7 +265,7 @@ IReadOnlyCollection LoadConfigurationAssemblies() { var assemblies = new Dictionary(); - var usingSection = _configuration.GetSection("Using"); + var usingSection = _section.GetSection("Using"); if (usingSection.GetChildren().Any()) { foreach (var simpleName in usingSection.GetChildren().Select(c => c.Value)) @@ -314,6 +326,9 @@ static void CallConfigurationMethods(ILookup s.Key == p.Name) select directive.Key == null ? p.DefaultValue : directive.Value.ConvertTo(p.ParameterType, declaredLevelSwitches)).ToList(); + var parm = methodInfo.GetParameters().FirstOrDefault(i => i.ParameterType == typeof(IConfiguration)); + if(parm != null) call[parm.Position - 1] = _configuration; + call.Insert(0, receiver); methodInfo.Invoke(null, call.ToArray()); From 1239fda1f791cd02b8dc21c1a073892d4598eaf9 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Thu, 19 Apr 2018 07:50:12 -0400 Subject: [PATCH 17/61] recognize IConfigurationSection parameter types --- .../Serilog.Settings.Configuration.csproj | 4 ++-- .../Settings/Configuration/ConfigurationReader.cs | 2 +- .../{BoundArgumentValue.cs => ObjectArgumentValue.cs} | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) rename src/Serilog.Settings.Configuration/Settings/Configuration/{BoundArgumentValue.cs => ObjectArgumentValue.cs} (69%) diff --git a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj index 99d3b7e..def4c05 100644 --- a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj +++ b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj @@ -17,8 +17,8 @@ https://github.com/serilog/serilog-settings-configuration https://www.apache.org/licenses/LICENSE-2.0 Serilog - false - 3.0.0 + true + 3.0.0.7 3.0.0.0 3.0.0 diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index a37e454..da3ff16 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -244,7 +244,7 @@ IConfigurationArgumentValue GetArgumentValue(IConfigurationSection argumentSecti } else { - argumentValue = new BoundArgumentValue(argumentSection); + argumentValue = new ObjectArgumentValue(argumentSection); } } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/BoundArgumentValue.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs similarity index 69% rename from src/Serilog.Settings.Configuration/Settings/Configuration/BoundArgumentValue.cs rename to src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs index ea8c7dc..e347e5d 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/BoundArgumentValue.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs @@ -5,17 +5,18 @@ namespace Serilog.Settings.Configuration { - class BoundArgumentValue : IConfigurationArgumentValue + class ObjectArgumentValue : IConfigurationArgumentValue { readonly IConfigurationSection section; - public BoundArgumentValue(IConfigurationSection section) + public ObjectArgumentValue(IConfigurationSection section) { this.section = section; } public object ConvertTo(Type toType, IReadOnlyDictionary declaredLevelSwitches) { + if(toType == typeof(IConfigurationSection)) return section; return section.Get(toType); } } From a81d1c60991435ef097605a406405e45c69a0210 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Thu, 19 Apr 2018 07:55:27 -0400 Subject: [PATCH 18/61] docs for IConfigurationSection --- README.md | 4 ++++ .../Serilog.Settings.Configuration.csproj | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b64a451..e3f1ba3 100644 --- a/README.md +++ b/README.md @@ -125,3 +125,7 @@ When the configuration specifies a discrete value for a parameter (such as a str If the parameter value is not a discrete value, the package will use the configuration binding system provided by _Microsoft.Extensions.Options.ConfigurationExtensions_ to attempt to populate the parameter. Almost anything that can be bound by `IConfiguration.Get` should work with this package. An example of this is the optional `List` parameter used to configure the .NET Standard version of the _Serilog.Sinks.MSSqlServer_ package. +### IConfigurationSection parameters + +Certain Serilog packages may require configuration information that can't be easily represented by discrete values or direct binding-friendly representations. An example might be lists of values to remove from a collection of default values. In this case the method can accept an entire `IConfigurationSection` as a call parameter and this package will recognize that and populate the parameter. In this way, Serilog packages can support arbitrarily complex configuration scenarios. + diff --git a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj index def4c05..c216387 100644 --- a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj +++ b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj @@ -17,9 +17,9 @@ https://github.com/serilog/serilog-settings-configuration https://www.apache.org/licenses/LICENSE-2.0 Serilog - true - 3.0.0.7 - 3.0.0.0 + false + 3.0.0 + 3.0.0 3.0.0 From 60cf69921b1c82ea10db7aa17a1fb446351abea6 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Thu, 19 Apr 2018 13:43:40 -0400 Subject: [PATCH 19/61] renamed const to NestedConfigHintChar --- .../Settings/Configuration/ConfigurationReader.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index da3ff16..9a1321f 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -19,7 +19,7 @@ namespace Serilog.Settings.Configuration class ConfigurationReader : IConfigurationReader { const string LevelSwitchNameRegex = @"^\$[A-Za-z]+[A-Za-z0-9]*$"; - const string ConfigSectionHintChar = ">"; + const string NestedConfigHintChar = ">"; static IConfiguration _configuration; @@ -222,7 +222,7 @@ internal ILookup> GetMet let name = GetSectionName(child) let callArgs = (from argument in child.GetSection("Args").GetChildren() select new { - Name = argument.Key.Replace(ConfigSectionHintChar, string.Empty), + Name = argument.Key.Replace(NestedConfigHintChar, string.Empty), Value = GetArgumentValue(argument) }).ToDictionary(p => p.Name, p => p.Value) select new { Name = name, Args = callArgs })) .ToLookup(p => p.Name, p => p.Args); @@ -238,7 +238,7 @@ IConfigurationArgumentValue GetArgumentValue(IConfigurationSection argumentSecti } else { - if(argumentSection.Key.EndsWith(ConfigSectionHintChar)) + if(argumentSection.Key.EndsWith(NestedConfigHintChar)) { argumentValue = new ConfigurationSectionArgumentValue(new ConfigurationReader(argumentSection, _configurationAssemblies, _dependencyContext)); } From da883fa86087e44dd456c251e82f67969cfc90c5 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Thu, 19 Apr 2018 14:09:13 -0400 Subject: [PATCH 20/61] fixed unit tests with config subsections --- .../ConfigurationSettingsTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index bdb5c6a..af48ca3 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -402,7 +402,7 @@ public void WriteToLoggerWithRestrictedToMinimumLevelIsSupported() ""WriteTo"": [{ ""Name"": ""Logger"", ""Args"": { - ""configureLogger"" : { + ""configureLogger>"" : { ""WriteTo"": [{ ""Name"": ""DummyRollingFile"", ""Args"": {""pathFormat"" : ""C:\\""} @@ -438,7 +438,7 @@ public void WriteToSubLoggerWithLevelSwitchIsSupported() ""WriteTo"": [{ ""Name"": ""Logger"", ""Args"": { - ""configureLogger"" : { + ""configureLogger>"" : { ""WriteTo"": [{ ""Name"": ""DummyRollingFile"", ""Args"": {""pathFormat"" : ""C:\\""} From 400b1de5068ea0e026077771fc107d7d12ca2d49 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Fri, 20 Apr 2018 07:36:51 -0400 Subject: [PATCH 21/61] moved ConfigurationSectionArgumentValue functionality into ObjectArgumentValue, removed hint hack --- README.md | 6 +-- sample/Sample/appsettings.json | 4 +- .../Configuration/ConfigurationReader.cs | 29 ++----------- .../ConfigurationSectionArgumentValue.cs | 41 ------------------- .../Configuration/ObjectArgumentValue.cs | 36 +++++++++++++++- .../ConfigurationSettingsTests.cs | 4 +- 6 files changed, 44 insertions(+), 76 deletions(-) delete mode 100644 src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationSectionArgumentValue.cs diff --git a/README.md b/README.md index e3f1ba3..21e7e34 100644 --- a/README.md +++ b/README.md @@ -94,15 +94,13 @@ dotnet run ### Nested configuration sections -Some Serilog packages require a reference to a logger configuration object. These must be "hinted" by appending a `>` character to the end of the argument name. The sample program in this project illustrates this with the following entry configuring the _Serilog.Sinks.Async_ package to wrap the _Serilog.Sinks.File_ package. - -Note the `configure>` parameter name with the trailing hint character: +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_ package to wrap the _Serilog.Sinks.File_ package. The `configure` parameter references the File sink configuration: ```json "WriteTo:Async": { "Name": "Async", "Args": { - "configure>": [ + "configure": [ { "Name": "File", "Args": { diff --git a/sample/Sample/appsettings.json b/sample/Sample/appsettings.json index c0311e2..db91572 100644 --- a/sample/Sample/appsettings.json +++ b/sample/Sample/appsettings.json @@ -11,7 +11,7 @@ "WriteTo:Sublogger": { "Name": "Logger", "Args": { - "configureLogger>": { + "configureLogger": { "WriteTo": [ { "Name": "Console", @@ -28,7 +28,7 @@ "WriteTo:Async": { "Name": "Async", "Args": { - "configure>": [ + "configure": [ { "Name": "File", "Args": { diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 9a1321f..a5eb331 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -19,7 +19,6 @@ namespace Serilog.Settings.Configuration class ConfigurationReader : IConfigurationReader { const string LevelSwitchNameRegex = @"^\$[A-Za-z]+[A-Za-z0-9]*$"; - const string NestedConfigHintChar = ">"; static IConfiguration _configuration; @@ -27,8 +26,6 @@ class ConfigurationReader : IConfigurationReader readonly DependencyContext _dependencyContext; readonly IReadOnlyCollection _configurationAssemblies; - #region Constructors - public ConfigurationReader(IConfiguration configuration, DependencyContext dependencyContext) { _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); @@ -47,17 +44,13 @@ public ConfigurationReader(IConfiguration configuration, DependencyContext depen } // Used internally for processing nested configuration sections -- see GetMethodCalls below. - ConfigurationReader(IConfigurationSection configSection, IReadOnlyCollection configurationAssemblies, DependencyContext dependencyContext) + internal ConfigurationReader(IConfigurationSection configSection, IReadOnlyCollection configurationAssemblies, DependencyContext dependencyContext) { _section = configSection ?? throw new ArgumentNullException(nameof(configSection)); _dependencyContext = dependencyContext; _configurationAssemblies = configurationAssemblies ?? throw new ArgumentNullException(nameof(configurationAssemblies)); } - #endregion - - #region Configure and related Apply methods - public void Configure(LoggerConfiguration loggerConfiguration) { var declaredLevelSwitches = ProcessLevelSwitchDeclarations(); @@ -204,10 +197,6 @@ void ApplyEnrichment(LoggerConfiguration loggerConfiguration, IReadOnlyDictionar } } - #endregion - - #region Internal implementation - internal ILookup> GetMethodCalls(IConfigurationSection directive) { var children = directive.GetChildren().ToList(); @@ -222,7 +211,7 @@ internal ILookup> GetMet let name = GetSectionName(child) let callArgs = (from argument in child.GetSection("Args").GetChildren() select new { - Name = argument.Key.Replace(NestedConfigHintChar, string.Empty), + Name = argument.Key, Value = GetArgumentValue(argument) }).ToDictionary(p => p.Name, p => p.Value) select new { Name = name, Args = callArgs })) .ToLookup(p => p.Name, p => p.Args); @@ -238,14 +227,7 @@ IConfigurationArgumentValue GetArgumentValue(IConfigurationSection argumentSecti } else { - if(argumentSection.Key.EndsWith(NestedConfigHintChar)) - { - argumentValue = new ConfigurationSectionArgumentValue(new ConfigurationReader(argumentSection, _configurationAssemblies, _dependencyContext)); - } - else - { - argumentValue = new ObjectArgumentValue(argumentSection); - } + argumentValue = new ObjectArgumentValue(argumentSection, _configurationAssemblies, _dependencyContext); } return argumentValue; @@ -391,10 +373,6 @@ internal static IList FindConfigurationMethods(IReadOnlyCollection loggerFilterConfiguration.With(filter); @@ -426,6 +404,5 @@ internal static LogEventLevel ParseLogEventLevel(string value) return parsedLevel; } - #endregion } } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationSectionArgumentValue.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationSectionArgumentValue.cs deleted file mode 100644 index b7f6825..0000000 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationSectionArgumentValue.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Serilog.Configuration; -using System; -using System.Collections.Generic; -using System.Reflection; -using Serilog.Core; - -namespace Serilog.Settings.Configuration -{ - class ConfigurationSectionArgumentValue : IConfigurationArgumentValue - { - readonly IConfigurationReader _configReader; - - public ConfigurationSectionArgumentValue(IConfigurationReader configReader) - { - _configReader = configReader ?? throw new ArgumentNullException(nameof(configReader)); - } - - public object ConvertTo(Type toType, IReadOnlyDictionary declaredLevelSwitches) - { - var typeInfo = toType.GetTypeInfo(); - if (!typeInfo.IsGenericType || - typeInfo.GetGenericTypeDefinition() is Type genericType && genericType != typeof(Action<>)) - { - throw new InvalidOperationException("Argument value should be of type Action<>."); - } - - var configurationType = typeInfo.GenericTypeArguments[0]; - if (configurationType == typeof(LoggerSinkConfiguration)) - { - return new Action(loggerSinkConfig => _configReader.ApplySinks(loggerSinkConfig, declaredLevelSwitches)); - } - - if (configurationType == typeof(LoggerConfiguration)) - { - return new Action(_configReader.Configure); - } - - throw new ArgumentException($"Handling {configurationType} is not implemented."); - } - } -} diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs index e347e5d..8242476 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs @@ -1,22 +1,56 @@ using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyModel; +using Serilog.Configuration; using Serilog.Core; using System; using System.Collections.Generic; +using System.Reflection; namespace Serilog.Settings.Configuration { class ObjectArgumentValue : IConfigurationArgumentValue { readonly IConfigurationSection section; + readonly IReadOnlyCollection configurationAssemblies; + readonly DependencyContext dependencyContext; - public ObjectArgumentValue(IConfigurationSection section) + public ObjectArgumentValue(IConfigurationSection section, IReadOnlyCollection configurationAssemblies, DependencyContext dependencyContext) { this.section = section; + + // used by nested logger configurations to feed a new pass by ConfigurationReader + this.configurationAssemblies = configurationAssemblies; + this.dependencyContext = dependencyContext; } public object ConvertTo(Type toType, IReadOnlyDictionary declaredLevelSwitches) { + // return the entire section for internal processing if(toType == typeof(IConfigurationSection)) return section; + + // process a nested configuration to populate an Action<> logger/sink config parameter? + var typeInfo = toType.GetTypeInfo(); + if(typeInfo.IsGenericType && + typeInfo.GetGenericTypeDefinition() is Type genericType && genericType == typeof(Action<>)) + { + var configType = typeInfo.GenericTypeArguments[0]; + if(configType != typeof(LoggerConfiguration) && configType != typeof(LoggerSinkConfiguration)) + throw new ArgumentException($"Configuration for Action<{configType}> is not implemented."); + + IConfigurationReader configReader = new ConfigurationReader(section, configurationAssemblies, dependencyContext); + + if(configType == typeof(LoggerConfiguration)) + { + return new Action(configReader.Configure); + } + + if(configType == typeof(LoggerSinkConfiguration)) + { + return new Action(loggerSinkConfig => configReader.ApplySinks(loggerSinkConfig, declaredLevelSwitches)); + } + } + + // MS Config binding return section.Get(toType); } } diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index af48ca3..bdb5c6a 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -402,7 +402,7 @@ public void WriteToLoggerWithRestrictedToMinimumLevelIsSupported() ""WriteTo"": [{ ""Name"": ""Logger"", ""Args"": { - ""configureLogger>"" : { + ""configureLogger"" : { ""WriteTo"": [{ ""Name"": ""DummyRollingFile"", ""Args"": {""pathFormat"" : ""C:\\""} @@ -438,7 +438,7 @@ public void WriteToSubLoggerWithLevelSwitchIsSupported() ""WriteTo"": [{ ""Name"": ""Logger"", ""Args"": { - ""configureLogger>"" : { + ""configureLogger"" : { ""WriteTo"": [{ ""Name"": ""DummyRollingFile"", ""Args"": {""pathFormat"" : ""C:\\""} From f9754e46ca9c74a23fca32d1cac0f244a4cb1034 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Fri, 20 Apr 2018 08:05:59 -0400 Subject: [PATCH 22/61] unit test for IConfig, IConfigSection, and config object-binding --- .../ConfigurationSettingsTests.cs | 52 ++++++++++++++++++- ...erilog.Settings.Configuration.Tests.csproj | 2 + .../DummyLoggerConfigurationExtensions.cs | 21 ++++++++ test/TestDummies/TestDummies.csproj | 5 ++ 4 files changed, 79 insertions(+), 1 deletion(-) diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index bdb5c6a..615302a 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -199,7 +199,6 @@ public void SinksAreConfiguredWithStaticMember() Assert.Equal(ConsoleThemes.Theme1, DummyConsoleSink.Theme); } - [Theory] [InlineData("$switchName", true)] [InlineData("$SwitchName", true)] @@ -391,6 +390,57 @@ public void LoggingLevelSwitchCanBeUsedForMinimumLevelOverrides() // ReSharper restore HeuristicUnreachableCode } + [Fact] + public void SinkWithIConfigurationArguments() + { + var json = @"{ + ""Serilog"": { + ""Using"": [""TestDummies""], + ""WriteTo"": [{ + ""Name"": ""DummyRollingFile"", + ""Args"": {""pathFormat"" : ""C:\\"", + ""configurationSection"" : { ""foo"" : ""bar"" } } + }] + } + }"; + + // IConfiguration and IConfigurationSection arguments do not have + // default values so they will throw if they are not populated + + var log = ConfigFromJson(json) + .CreateLogger(); + + DummyRollingFileSink.Emitted.Clear(); + + log.Write(Some.InformationEvent()); + + Assert.Equal(1, DummyRollingFileSink.Emitted.Count); + } + + [Fact] + public void SinkWithConfigurationBindingArgument() + { + var json = @"{ + ""Serilog"": { + ""Using"": [""TestDummies""], + ""WriteTo"": [{ + ""Name"": ""DummyRollingFile"", + ""Args"": {""pathFormat"" : ""C:\\"", + ""objectBinding"" : [ { ""foo"" : ""bar"" }, { ""abc"" : ""xyz"" } ] } + }] + } + }"; + + var log = ConfigFromJson(json) + .CreateLogger(); + + DummyRollingFileSink.Emitted.Clear(); + + log.Write(Some.InformationEvent()); + + Assert.Equal(1, DummyRollingFileSink.Emitted.Count); + } + [Trait("Bugfix", "#91")] [Fact] diff --git a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj index deb329a..3a88c1b 100644 --- a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj +++ b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj @@ -26,10 +26,12 @@ + + diff --git a/test/TestDummies/DummyLoggerConfigurationExtensions.cs b/test/TestDummies/DummyLoggerConfigurationExtensions.cs index b01c422..d8ac626 100644 --- a/test/TestDummies/DummyLoggerConfigurationExtensions.cs +++ b/test/TestDummies/DummyLoggerConfigurationExtensions.cs @@ -6,6 +6,8 @@ using Serilog.Core; using TestDummies.Console; using TestDummies.Console.Themes; +using Microsoft.Extensions.Configuration; +using System.Collections.Generic; namespace TestDummies { @@ -35,6 +37,25 @@ public static LoggerConfiguration DummyRollingFile( return loggerSinkConfiguration.Sink(new DummyRollingFileSink(), restrictedToMinimumLevel); } + public static LoggerConfiguration DummyRollingFile( + this LoggerSinkConfiguration loggerSinkConfiguration, + IConfiguration appConfiguration, + IConfigurationSection configurationSection, + string pathFormat, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) + { + return loggerSinkConfiguration.Sink(new DummyRollingFileSink(), restrictedToMinimumLevel); + } + + public static LoggerConfiguration DummyRollingFile( + this LoggerSinkConfiguration loggerSinkConfiguration, + List objectBinding, + string pathFormat, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) + { + return loggerSinkConfiguration.Sink(new DummyRollingFileSink(), restrictedToMinimumLevel); + } + public static LoggerConfiguration DummyRollingFile( this LoggerAuditSinkConfiguration loggerSinkConfiguration, string pathFormat, diff --git a/test/TestDummies/TestDummies.csproj b/test/TestDummies/TestDummies.csproj index b0d89c0..5c212f5 100644 --- a/test/TestDummies/TestDummies.csproj +++ b/test/TestDummies/TestDummies.csproj @@ -16,9 +16,14 @@ + + + + + From 0b3b661b136b6b669ea062878bfa596a66046d03 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Fri, 20 Apr 2018 11:33:57 -0400 Subject: [PATCH 23/61] unit tests for string/int array arguments --- .../ConfigurationSettingsTests.cs | 48 +++++++++++++++++++ .../DummyLoggerConfigurationExtensions.cs | 18 +++++++ 2 files changed, 66 insertions(+) diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 615302a..40435f3 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -441,6 +441,54 @@ public void SinkWithConfigurationBindingArgument() Assert.Equal(1, DummyRollingFileSink.Emitted.Count); } + [Fact] + public void SinkWithStringArrayArgument() + { + var json = @"{ + ""Serilog"": { + ""Using"": [""TestDummies""], + ""WriteTo"": [{ + ""Name"": ""DummyRollingFile"", + ""Args"": {""pathFormat"" : ""C:\\"", + ""stringArrayBinding"" : [ ""foo"", ""bar"", ""baz"" ] } + }] + } + }"; + + var log = ConfigFromJson(json) + .CreateLogger(); + + DummyRollingFileSink.Emitted.Clear(); + + log.Write(Some.InformationEvent()); + + Assert.Equal(1, DummyRollingFileSink.Emitted.Count); + } + + [Fact] + public void SinkWithIntArrayArgument() + { + var json = @"{ + ""Serilog"": { + ""Using"": [""TestDummies""], + ""WriteTo"": [{ + ""Name"": ""DummyRollingFile"", + ""Args"": {""pathFormat"" : ""C:\\"", + ""intArrayBinding"" : [ 1,2,3,4,5 ] } + }] + } + }"; + + var log = ConfigFromJson(json) + .CreateLogger(); + + DummyRollingFileSink.Emitted.Clear(); + + log.Write(Some.InformationEvent()); + + Assert.Equal(1, DummyRollingFileSink.Emitted.Count); + } + [Trait("Bugfix", "#91")] [Fact] diff --git a/test/TestDummies/DummyLoggerConfigurationExtensions.cs b/test/TestDummies/DummyLoggerConfigurationExtensions.cs index d8ac626..5f1e039 100644 --- a/test/TestDummies/DummyLoggerConfigurationExtensions.cs +++ b/test/TestDummies/DummyLoggerConfigurationExtensions.cs @@ -56,6 +56,24 @@ public static LoggerConfiguration DummyRollingFile( return loggerSinkConfiguration.Sink(new DummyRollingFileSink(), restrictedToMinimumLevel); } + public static LoggerConfiguration DummyRollingFile( + this LoggerSinkConfiguration loggerSinkConfiguration, + string[] stringArrayBinding, + string pathFormat, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) + { + return loggerSinkConfiguration.Sink(new DummyRollingFileSink(), restrictedToMinimumLevel); + } + + public static LoggerConfiguration DummyRollingFile( + this LoggerSinkConfiguration loggerSinkConfiguration, + int[] intArrayBinding, + string pathFormat, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) + { + return loggerSinkConfiguration.Sink(new DummyRollingFileSink(), restrictedToMinimumLevel); + } + public static LoggerConfiguration DummyRollingFile( this LoggerAuditSinkConfiguration loggerSinkConfiguration, string pathFormat, From 01166250270d03c96536f84bcde620b81f69c4f5 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Sun, 22 Apr 2018 08:25:20 +1000 Subject: [PATCH 24/61] Try removing pinned properties, etc, to match general Serilog project setup --- .../Serilog.Settings.Configuration.csproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj index c216387..7de6782 100644 --- a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj +++ b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj @@ -17,10 +17,6 @@ https://github.com/serilog/serilog-settings-configuration https://www.apache.org/licenses/LICENSE-2.0 Serilog - false - 3.0.0 - 3.0.0 - 3.0.0 From 7b18eefa1477342af586e7965463cc6567158771 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Sat, 28 Apr 2018 09:40:24 -0400 Subject: [PATCH 25/61] added exception --- .../Settings/Configuration/ConfigurationReader.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index a5eb331..761c2d8 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -221,6 +221,10 @@ internal ILookup> GetMet IConfigurationArgumentValue GetArgumentValue(IConfigurationSection argumentSection) { IConfigurationArgumentValue argumentValue; + + if(argumentSection.Value != null && argumentSection.GetChildren().Any()) + throw new InvalidOperationException($"Combined configuration sources must result in a discrete value (string, int, etc.) or complex value (section, list, etc.), not both. Argument: {argumentSection.Path}"); + if (argumentSection.Value != null) { argumentValue = new StringArgumentValue(() => argumentSection.Value, argumentSection.GetReloadToken); From 99cb4af7daa949888c64ed1d6a5f35fc9516ee6f Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Sat, 28 Apr 2018 10:25:17 -0400 Subject: [PATCH 26/61] unit test for multiple conflicting argument values --- .../ConfigurationSettingsTests.cs | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 40435f3..59e8a4c 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -12,9 +12,11 @@ namespace Serilog.Settings.Configuration.Tests { public class ConfigurationSettingsTests { - static LoggerConfiguration ConfigFromJson(string jsonString) + static LoggerConfiguration ConfigFromJson(string jsonString, string secondJsonSource = null) { - var config = new ConfigurationBuilder().AddJsonString(jsonString).Build(); + var builder = new ConfigurationBuilder().AddJsonString(jsonString); + if(secondJsonSource != null) builder.AddJsonString(secondJsonSource); + var config = builder.Build(); return new LoggerConfiguration() .ReadFrom.Configuration(config); } @@ -556,5 +558,42 @@ public void WriteToSubLoggerWithLevelSwitchIsSupported() Assert.Equal(1, DummyRollingFileSink.Emitted.Count); } + + [Trait("Bugfix", "#103")] + [Fact] + public void MultipleArgumentValuesThrowsInvalidOperationException() + { + var jsonDiscreteValue = @"{ + ""Serilog"": { + ""Using"": [""TestDummies""], + ""WriteTo"": [{ + ""Name"": ""DummyRollingFile"", + ""Args"": {""pathFormat"" : ""C:\\""} + }] + } + }"; + + var jsonComplexValue = @"{ + ""Serilog"": { + ""Using"": [""TestDummies""], + ""WriteTo"": [{ + ""Name"": ""DummyRollingFile"", + ""Args"": {""pathFormat"" : { ""foo"" : ""bar"" } } + }] + } + }"; + + // These will combine into a ConfigurationSection object that has both + // Value == "C:\" and GetChildren() == List. No configuration + // extension matching this exists (in theory an "object" argument could + // accept either value). ConfigurationReader should throw as soon as + // the multiple values are recognized; it will never attempt to locate + // a matching argument. + + var ex = Assert.Throws(() => ConfigFromJson(jsonDiscreteValue, jsonComplexValue)); + + Assert.Contains("Combined configuration sources", ex.Message); + Assert.Contains("pathFormat", ex.Message); + } } } From 36df0a6395f4e853283ae0b15f9dfdf334180de3 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Sat, 28 Apr 2018 12:05:41 -0400 Subject: [PATCH 27/61] code formatting --- .../Configuration/ConfigurationReader.cs | 2 +- .../ConfigurationSettingsTests.cs | 20 ++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 761c2d8..417e0cd 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -222,7 +222,7 @@ IConfigurationArgumentValue GetArgumentValue(IConfigurationSection argumentSecti { IConfigurationArgumentValue argumentValue; - if(argumentSection.Value != null && argumentSection.GetChildren().Any()) + if (argumentSection.Value != null && argumentSection.GetChildren().Any()) throw new InvalidOperationException($"Combined configuration sources must result in a discrete value (string, int, etc.) or complex value (section, list, etc.), not both. Argument: {argumentSection.Path}"); if (argumentSection.Value != null) diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 59e8a4c..ad56848 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -15,7 +15,8 @@ public class ConfigurationSettingsTests static LoggerConfiguration ConfigFromJson(string jsonString, string secondJsonSource = null) { var builder = new ConfigurationBuilder().AddJsonString(jsonString); - if(secondJsonSource != null) builder.AddJsonString(secondJsonSource); + if (secondJsonSource != null) + builder.AddJsonString(secondJsonSource); var config = builder.Build(); return new LoggerConfiguration() .ReadFrom.Configuration(config); @@ -33,7 +34,7 @@ public void PropertyEnrichmentIsApplied() } } }"; - + var log = ConfigFromJson(json) .WriteTo.Sink(new DelegatingSink(e => evt = e)) .CreateLogger(); @@ -116,7 +117,7 @@ public void AuditSinksAreConfigured() var log = ConfigFromJson(json) .CreateLogger(); - + DummyRollingFileSink.Emitted.Clear(); DummyRollingFileAuditSink.Emitted.Clear(); @@ -229,7 +230,7 @@ public void LoggingLevelSwitchWithInvalidNameThrowsFormatException() ""LevelSwitches"": {""switchNameNotStartingWithDollar"" : ""Warning"" } } }"; - + var ex = Assert.Throws(() => ConfigFromJson(json)); Assert.Contains("\"switchNameNotStartingWithDollar\"", ex.Message); @@ -273,7 +274,7 @@ public void SettingMinimumLevelControlledByToAnUndeclaredSwitchThrows() } } }"; - + var ex = Assert.Throws(() => ConfigFromJson(json) .CreateLogger()); @@ -334,7 +335,7 @@ public void ReferencingAnUndeclaredSwitchInSinkThrows() }] } }"; - + var ex = Assert.Throws(() => ConfigFromJson(json) .CreateLogger()); @@ -546,8 +547,8 @@ public void WriteToSubLoggerWithLevelSwitchIsSupported() } }] } - }"; - + }"; + var log = ConfigFromJson(json) .CreateLogger(); @@ -590,7 +591,8 @@ public void MultipleArgumentValuesThrowsInvalidOperationException() // the multiple values are recognized; it will never attempt to locate // a matching argument. - var ex = Assert.Throws(() => ConfigFromJson(jsonDiscreteValue, jsonComplexValue)); + var ex = Assert.Throws(() + => ConfigFromJson(jsonDiscreteValue, jsonComplexValue)); Assert.Contains("Combined configuration sources", ex.Message); Assert.Contains("pathFormat", ex.Message); From 5e6d67d60720b61c144673b99d245b4ee4c55480 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Sat, 28 Apr 2018 13:36:54 -0400 Subject: [PATCH 28/61] verbiage and naming feedback --- .../Settings/Configuration/ConfigurationReader.cs | 8 +++++++- .../ConfigurationSettingsTests.cs | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 417e0cd..74ea09d 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -222,8 +222,14 @@ IConfigurationArgumentValue GetArgumentValue(IConfigurationSection argumentSecti { IConfigurationArgumentValue argumentValue; + // Reject configurations where an element has both scalar and complex + // values as a result of reading multiple configuration sources. if (argumentSection.Value != null && argumentSection.GetChildren().Any()) - throw new InvalidOperationException($"Combined configuration sources must result in a discrete value (string, int, etc.) or complex value (section, list, etc.), not both. Argument: {argumentSection.Path}"); + throw new InvalidOperationException( + $"The value for the argument {argumentSection.Path} is assigned different value " + + "types in more than one configuration source. Ensure all configurations consistently " + + "use either a scalar (int, string, boolean) or a complex (array, section, list, " + + "POCO, etc.) type for this argument value."); if (argumentSection.Value != null) { diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index ad56848..27b0e57 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -562,7 +562,7 @@ public void WriteToSubLoggerWithLevelSwitchIsSupported() [Trait("Bugfix", "#103")] [Fact] - public void MultipleArgumentValuesThrowsInvalidOperationException() + public void InconsistentComplexVsScalarArgumentValuesThrowsIOE() { var jsonDiscreteValue = @"{ ""Serilog"": { From 67e7ca84df4a61000937cbf0e271372a98c171e7 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Sat, 28 Apr 2018 13:45:17 -0400 Subject: [PATCH 29/61] more detailed test assertion and change text to match new exception --- .../Settings/Configuration/ConfigurationReader.cs | 2 +- .../ConfigurationSettingsTests.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 74ea09d..e346cd2 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -226,7 +226,7 @@ IConfigurationArgumentValue GetArgumentValue(IConfigurationSection argumentSecti // values as a result of reading multiple configuration sources. if (argumentSection.Value != null && argumentSection.GetChildren().Any()) throw new InvalidOperationException( - $"The value for the argument {argumentSection.Path} is assigned different value " + + $"The value for the argument '{argumentSection.Path}' is assigned different value " + "types in more than one configuration source. Ensure all configurations consistently " + "use either a scalar (int, string, boolean) or a complex (array, section, list, " + "POCO, etc.) type for this argument value."); diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 27b0e57..86acaf8 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -594,8 +594,8 @@ public void InconsistentComplexVsScalarArgumentValuesThrowsIOE() var ex = Assert.Throws(() => ConfigFromJson(jsonDiscreteValue, jsonComplexValue)); - Assert.Contains("Combined configuration sources", ex.Message); - Assert.Contains("pathFormat", ex.Message); + Assert.Contains("The value for the argument", ex.Message); + Assert.Contains("'Serilog:WriteTo:0:Args:pathFormat'", ex.Message); } } } From 76b0bf2614ecf7cc222c21325021e437fede550b Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Sun, 6 May 2018 11:15:46 -0400 Subject: [PATCH 30/61] support the basic destructure settings --- sample/Sample/Program.cs | 13 ++- sample/Sample/appsettings.json | 14 ++++ .../Configuration/ConfigurationReader.cs | 60 +++++++++++-- .../ConfigurationSettingsTests.cs | 84 ++++++++++++++++++- 4 files changed, 161 insertions(+), 10 deletions(-) diff --git a/sample/Sample/Program.cs b/sample/Sample/Program.cs index be63efb..d2bf4ea 100644 --- a/sample/Sample/Program.cs +++ b/sample/Sample/Program.cs @@ -32,12 +32,23 @@ public static void Main(string[] args) logger.ForContext(Constants.SourceContextPropertyName, "Microsoft").Error("Hello, world!"); logger.ForContext(Constants.SourceContextPropertyName, "MyApp.Something.Tricky").Verbose("Hello, world!"); - Console.WriteLine(); + logger.Information("Destructure with max object nesting depth:\n{@NestedObject}", + new { FiveDeep = new { Two = new { Three = new { Four = new { Five = "the end" } } } } }); + + logger.Information("Destructure with max string length:\n{@LongString}", + new { TwentyChars = "0123456789abcdefghij" }); + + logger.Information("Destructure with max collection count:\n{@BigData}", + new { TenItems = new string[] { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" } }); + + Console.WriteLine("\nPress \"q\" to quit, or any other key to run again.\n"); } while (!args.Contains("--run-once") && (Console.ReadKey().KeyChar != 'q')); } } + // The filter syntax in the sample configuration file is + // processed by the Serilog.Filters.Expressions package. public class CustomFilter : ILogEventFilter { public bool IsEnabled(LogEvent logEvent) diff --git a/sample/Sample/appsettings.json b/sample/Sample/appsettings.json index db91572..0ca1562 100644 --- a/sample/Sample/appsettings.json +++ b/sample/Sample/appsettings.json @@ -43,6 +43,20 @@ "Properties": { "Application": "Sample" }, + "Destructure": [ + { + "Name": "ToMaximumDepth", + "Args": { "maximumDestructuringDepth": 3 } + }, + { + "Name": "ToMaximumStringLength", + "Args": { "maximumStringLength": 10 } + }, + { + "Name": "ToMaximumCollectionCount", + "Args": { "maximumCollectionCount": 5 } + } + ], "Filter": [ { "Name": "ByIncludingOnly", diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index e346cd2..57b201c 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -57,6 +57,7 @@ public void Configure(LoggerConfiguration loggerConfiguration) ApplyMinimumLevel(loggerConfiguration, declaredLevelSwitches); ApplyEnrichment(loggerConfiguration, declaredLevelSwitches); ApplyFilters(loggerConfiguration, declaredLevelSwitches); + ApplyDestructuring(loggerConfiguration, declaredLevelSwitches); ApplySinks(loggerConfiguration, declaredLevelSwitches); ApplyAuditSinks(loggerConfiguration, declaredLevelSwitches); } @@ -152,6 +153,16 @@ void ApplyFilters(LoggerConfiguration loggerConfiguration, IReadOnlyDictionary declaredLevelSwitches) + { + var filterDirective = _section.GetSection("Destructure"); + if(filterDirective.GetChildren().Any()) + { + var methodCalls = GetMethodCalls(filterDirective); + CallConfigurationMethods(methodCalls, FindDestructureConfigurationMethods(_configurationAssemblies), loggerConfiguration.Destructure, declaredLevelSwitches); + } + } + void ApplySinks(LoggerConfiguration loggerConfiguration, IReadOnlyDictionary declaredLevelSwitches) { var writeToDirective = _section.GetSection("WriteTo"); @@ -339,7 +350,7 @@ internal static MethodInfo SelectConfigurationMethod(IEnumerable can internal static IList FindSinkConfigurationMethods(IReadOnlyCollection configurationAssemblies) { - var found = FindConfigurationMethods(configurationAssemblies, typeof(LoggerSinkConfiguration)); + var found = FindConfigurationExtensions(configurationAssemblies, typeof(LoggerSinkConfiguration)); if (configurationAssemblies.Contains(typeof(LoggerSinkConfiguration).GetTypeInfo().Assembly)) found.Add(GetSurrogateConfigurationMethod, LoggingLevelSwitch>((c, a, s) => Logger(c, a, LevelAlias.Minimum, s))); @@ -348,30 +359,44 @@ internal static IList FindSinkConfigurationMethods(IReadOnlyCollecti internal static IList FindAuditSinkConfigurationMethods(IReadOnlyCollection configurationAssemblies) { - var found = FindConfigurationMethods(configurationAssemblies, typeof(LoggerAuditSinkConfiguration)); + var found = FindConfigurationExtensions(configurationAssemblies, typeof(LoggerAuditSinkConfiguration)); return found; } internal static IList FindFilterConfigurationMethods(IReadOnlyCollection configurationAssemblies) { - var found = FindConfigurationMethods(configurationAssemblies, typeof(LoggerFilterConfiguration)); + var found = FindConfigurationExtensions(configurationAssemblies, typeof(LoggerFilterConfiguration)); if (configurationAssemblies.Contains(typeof(LoggerFilterConfiguration).GetTypeInfo().Assembly)) found.Add(GetSurrogateConfigurationMethod((c, f, _) => With(c, f))); return found; } + internal static IList FindDestructureConfigurationMethods(IReadOnlyCollection configurationAssemblies) + { + var found = FindConfigurationExtensions(configurationAssemblies, typeof(LoggerDestructuringConfiguration)); + if(configurationAssemblies.Contains(typeof(LoggerDestructuringConfiguration).GetTypeInfo().Assembly)) + { + found.Add(GetSurrogateConfigurationMethod((c, d, _) => With(c, d))); + found.Add(GetSurrogateConfigurationMethod((c, m, _) => ToMaximumDepth(c, m))); + found.Add(GetSurrogateConfigurationMethod((c, m, _) => ToMaximumStringLength(c, m))); + found.Add(GetSurrogateConfigurationMethod((c, m, _) => ToMaximumCollectionCount(c, m))); + } + + return found; + } + internal static IList FindEventEnricherConfigurationMethods(IReadOnlyCollection configurationAssemblies) { - var found = FindConfigurationMethods(configurationAssemblies, typeof(LoggerEnrichmentConfiguration)); + var found = FindConfigurationExtensions(configurationAssemblies, typeof(LoggerEnrichmentConfiguration)); if (configurationAssemblies.Contains(typeof(LoggerEnrichmentConfiguration).GetTypeInfo().Assembly)) found.Add(GetSurrogateConfigurationMethod((c, _, __) => FromLogContext(c))); return found; } - internal static IList FindConfigurationMethods(IReadOnlyCollection configurationAssemblies, Type configType) + internal static IList FindConfigurationExtensions(IReadOnlyCollection configurationAssemblies, Type configType) { return configurationAssemblies .SelectMany(a => a.ExportedTypes @@ -383,15 +408,34 @@ internal static IList FindConfigurationMethods(IReadOnlyCollection loggerFilterConfiguration.With(filter); - // Unlike the other configuration methods, FromLogContext is an instance method rather than an extension. + // TODO: add overload for array argument (IDestructuringPolicy[]) + internal static LoggerConfiguration With(LoggerDestructuringConfiguration loggerDestructuringConfiguration, IDestructuringPolicy policy) + => loggerDestructuringConfiguration.With(policy); + + internal static LoggerConfiguration ToMaximumDepth(LoggerDestructuringConfiguration loggerDestructuringConfiguration, int maximumDestructuringDepth) + => loggerDestructuringConfiguration.ToMaximumDepth(maximumDestructuringDepth); + + internal static LoggerConfiguration ToMaximumStringLength(LoggerDestructuringConfiguration loggerDestructuringConfiguration, int maximumStringLength) + => loggerDestructuringConfiguration.ToMaximumStringLength(maximumStringLength); + + internal static LoggerConfiguration ToMaximumCollectionCount(LoggerDestructuringConfiguration loggerDestructuringConfiguration, int maximumCollectionCount) + => loggerDestructuringConfiguration.ToMaximumCollectionCount(maximumCollectionCount); + internal static LoggerConfiguration FromLogContext(LoggerEnrichmentConfiguration loggerEnrichmentConfiguration) => loggerEnrichmentConfiguration.FromLogContext(); - // Unlike the other configuration methods, Logger is an instance method rather than an extension. internal static LoggerConfiguration Logger( LoggerSinkConfiguration loggerSinkConfiguration, Action configureLogger, diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 86acaf8..f0d8809 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -492,7 +492,6 @@ public void SinkWithIntArrayArgument() Assert.Equal(1, DummyRollingFileSink.Emitted.Count); } - [Trait("Bugfix", "#91")] [Fact] public void WriteToLoggerWithRestrictedToMinimumLevelIsSupported() @@ -597,5 +596,88 @@ public void InconsistentComplexVsScalarArgumentValuesThrowsIOE() Assert.Contains("The value for the argument", ex.Message); Assert.Contains("'Serilog:WriteTo:0:Args:pathFormat'", ex.Message); } + + [Fact] + public void DestructureLimitsNestingDepth() + { + var json = @"{ + ""Serilog"": { + ""Destructure"": [ + { + ""Name"": ""ToMaximumDepth"", + ""Args"": { ""maximumDestructuringDepth"": 3 } + }] + } + }"; + + var NestedObject = new + { + A = new + { + B = new + { + C = new + { + D = "F" + } + } + } + }; + + var msg = GetDestructuredProperty(NestedObject, json); + + Assert.Contains("C", msg); + Assert.DoesNotContain("D", msg); + } + + [Fact] + public void DestructureLimitsStringLength() + { + var json = @"{ + ""Serilog"": { + ""Destructure"": [ + { + ""Name"": ""ToMaximumStringLength"", + ""Args"": { ""maximumStringLength"": 3 } + }] + } + }"; + + var inputString = "ABCDEFGH"; + var msg = GetDestructuredProperty(inputString, json); + + Assert.Equal("\"AB…\"", msg); + } + + [Fact] + public void DestructureLimitsCollectionCount() + { + var json = @"{ + ""Serilog"": { + ""Destructure"": [ + { + ""Name"": ""ToMaximumCollectionCount"", + ""Args"": { ""maximumCollectionCount"": 3 } + }] + } + }"; + + var collection = new[] { 1, 2, 3, 4, 5, 6 }; + var msg = GetDestructuredProperty(collection, json); + + Assert.Contains("3", msg); + Assert.DoesNotContain("4", msg); + } + + private string GetDestructuredProperty(object x, string json) + { + LogEvent evt = null; + var log = ConfigFromJson(json) + .WriteTo.Sink(new DelegatingSink(e => evt = e)) + .CreateLogger(); + log.Information("{@X}", x); + var result = evt.Properties["X"].ToString(); + return result; + } } } From 4ab6ec075323328b3a52e223a92c01be64c90afd Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Sun, 6 May 2018 11:21:20 -0400 Subject: [PATCH 31/61] added destructure section to sample json --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 21e7e34..77bfeab 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,11 @@ Configuration is read from the `Serilog` section. { "Name": "File", "Args": { "path": "%TEMP%\\Logs\\serilog-configuration-sample.txt" } } ], "Enrich": ["FromLogContext", "WithMachineName", "WithThreadId"], + "Destructure": [ + { "Name": "ToMaximumDepth", "Args": { "maximumDestructuringDepth": 4 } }, + { "Name": "ToMaximumStringLength", "Args": { "maximumStringLength": 100 } }, + { "Name": "ToMaximumCollectionCount", "Args": { "maximumCollectionCount": 10 } } + ], "Properties": { "Application": "Sample" } From 43345f15222cfc8a9188f16b023f44d3bf9c43e7 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Mon, 7 May 2018 16:05:18 -0400 Subject: [PATCH 32/61] PR feedback, and added IDestructuringPolicy to sample program and README --- README.md | 1 + sample/Sample/Program.cs | 31 ++++++++++++++++++- sample/Sample/appsettings.json | 4 +++ .../Configuration/ConfigurationReader.cs | 12 +++---- 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 77bfeab..9a737a0 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Configuration is read from the `Serilog` section. ], "Enrich": ["FromLogContext", "WithMachineName", "WithThreadId"], "Destructure": [ + { "Name": "With", "Args": { "policy": "Sample.CustomPolicy, Sample" } }, { "Name": "ToMaximumDepth", "Args": { "maximumDestructuringDepth": 4 } }, { "Name": "ToMaximumStringLength", "Args": { "maximumStringLength": 100 } }, { "Name": "ToMaximumCollectionCount", "Args": { "maximumCollectionCount": 10 } } diff --git a/sample/Sample/Program.cs b/sample/Sample/Program.cs index d2bf4ea..a213a4b 100644 --- a/sample/Sample/Program.cs +++ b/sample/Sample/Program.cs @@ -6,6 +6,7 @@ using System.Linq; using Serilog.Core; using Serilog.Events; +using System.Collections.Generic; namespace Sample { @@ -41,9 +42,12 @@ public static void Main(string[] args) logger.Information("Destructure with max collection count:\n{@BigData}", new { TenItems = new string[] { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" } }); + logger.Information("Destructure with policy to strip password:\n{@LoginData}", + new LoginData { Username = "BGates", Password = "isityearoflinuxyet" }); + Console.WriteLine("\nPress \"q\" to quit, or any other key to run again.\n"); } - while (!args.Contains("--run-once") && (Console.ReadKey().KeyChar != 'q')); + while(!args.Contains("--run-once") && (Console.ReadKey().KeyChar != 'q')); } } @@ -56,4 +60,29 @@ public bool IsEnabled(LogEvent logEvent) return true; } } + + public class LoginData + { + public string Username; + public string Password; + } + + public class CustomPolicy : IDestructuringPolicy + { + public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, out LogEventPropertyValue result) + { + result = null; + + if(value is LoginData) + { + result = new StructureValue( + new List + { + new LogEventProperty("Username", new ScalarValue(((LoginData)value).Username)) + }); + } + + return (result != null); + } + } } diff --git a/sample/Sample/appsettings.json b/sample/Sample/appsettings.json index 0ca1562..2cbcccc 100644 --- a/sample/Sample/appsettings.json +++ b/sample/Sample/appsettings.json @@ -44,6 +44,10 @@ "Application": "Sample" }, "Destructure": [ + { + "Name": "With", + "Args": { "policy": "Sample.CustomPolicy, Sample" } + }, { "Name": "ToMaximumDepth", "Args": { "maximumDestructuringDepth": 3 } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 57b201c..ffb8508 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -350,7 +350,7 @@ internal static MethodInfo SelectConfigurationMethod(IEnumerable can internal static IList FindSinkConfigurationMethods(IReadOnlyCollection configurationAssemblies) { - var found = FindConfigurationExtensions(configurationAssemblies, typeof(LoggerSinkConfiguration)); + var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerSinkConfiguration)); if (configurationAssemblies.Contains(typeof(LoggerSinkConfiguration).GetTypeInfo().Assembly)) found.Add(GetSurrogateConfigurationMethod, LoggingLevelSwitch>((c, a, s) => Logger(c, a, LevelAlias.Minimum, s))); @@ -359,14 +359,14 @@ internal static IList FindSinkConfigurationMethods(IReadOnlyCollecti internal static IList FindAuditSinkConfigurationMethods(IReadOnlyCollection configurationAssemblies) { - var found = FindConfigurationExtensions(configurationAssemblies, typeof(LoggerAuditSinkConfiguration)); + var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerAuditSinkConfiguration)); return found; } internal static IList FindFilterConfigurationMethods(IReadOnlyCollection configurationAssemblies) { - var found = FindConfigurationExtensions(configurationAssemblies, typeof(LoggerFilterConfiguration)); + var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerFilterConfiguration)); if (configurationAssemblies.Contains(typeof(LoggerFilterConfiguration).GetTypeInfo().Assembly)) found.Add(GetSurrogateConfigurationMethod((c, f, _) => With(c, f))); @@ -375,7 +375,7 @@ internal static IList FindFilterConfigurationMethods(IReadOnlyCollec internal static IList FindDestructureConfigurationMethods(IReadOnlyCollection configurationAssemblies) { - var found = FindConfigurationExtensions(configurationAssemblies, typeof(LoggerDestructuringConfiguration)); + var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerDestructuringConfiguration)); if(configurationAssemblies.Contains(typeof(LoggerDestructuringConfiguration).GetTypeInfo().Assembly)) { found.Add(GetSurrogateConfigurationMethod((c, d, _) => With(c, d))); @@ -389,14 +389,14 @@ internal static IList FindDestructureConfigurationMethods(IReadOnlyC internal static IList FindEventEnricherConfigurationMethods(IReadOnlyCollection configurationAssemblies) { - var found = FindConfigurationExtensions(configurationAssemblies, typeof(LoggerEnrichmentConfiguration)); + var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerEnrichmentConfiguration)); if (configurationAssemblies.Contains(typeof(LoggerEnrichmentConfiguration).GetTypeInfo().Assembly)) found.Add(GetSurrogateConfigurationMethod((c, _, __) => FromLogContext(c))); return found; } - internal static IList FindConfigurationExtensions(IReadOnlyCollection configurationAssemblies, Type configType) + internal static IList FindConfigurationExtensionMethods(IReadOnlyCollection configurationAssemblies, Type configType) { return configurationAssemblies .SelectMany(a => a.ExportedTypes From 285a4ceeac3aeb52127af40e6ffaf228eaa19748 Mon Sep 17 00:00:00 2001 From: Matthew Erbs Date: Wed, 9 May 2018 15:40:37 +1000 Subject: [PATCH 33/61] New NuGet API key --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 467d351..0e93876 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -10,7 +10,7 @@ artifacts: deploy: - provider: NuGet api_key: - secure: nvZ/z+pMS91b3kG4DgfES5AcmwwGoBYQxr9kp4XiJHj25SAlgdIxFx++1N0lFH2x + secure: bd9z4P73oltOXudAjPehwp9iDKsPtC+HbgshOrSgoyQKr5xVK+bxJQngrDJkHdY8 skip_symbols: true on: branch: /^(master|dev)$/ @@ -20,4 +20,4 @@ deploy: artifact: /Serilog.*\.nupkg/ tag: v$(appveyor_build_version) on: - branch: master \ No newline at end of file + branch: master From 17d808319489226a9f0991f8406c3e5dbfecd9b8 Mon Sep 17 00:00:00 2001 From: Thibaud Desodt Date: Sat, 14 Jul 2018 10:13:10 +0200 Subject: [PATCH 34/61] Add test to check support for custom destructuring extension Methods --- .../ConfigurationSettingsTests.cs | 24 +++++++++++++++++++ ...DummyHardCodedStringDestructuringPolicy.cs | 22 +++++++++++++++++ .../DummyLoggerConfigurationExtensions.cs | 10 +++++++- 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 test/TestDummies/DummyHardCodedStringDestructuringPolicy.cs diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index f0d8809..efa78c9 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -679,5 +679,29 @@ private string GetDestructuredProperty(object x, string json) var result = evt.Properties["X"].ToString(); return result; } + + [Fact] + public void DestructuringWithCustomExtensionMethodIsApplied() + { + var json = @"{ + ""Serilog"": { + ""Using"": [""TestDummies""], + ""Destructure"": [ + { + ""Name"": ""WithDummyHardCodedString"", + ""Args"": { ""hardCodedString"": ""hardcoded"" } + }] + } + }"; + + LogEvent evt = null; + var log = ConfigFromJson(json) + .WriteTo.Sink(new DelegatingSink(e => evt = e)) + .CreateLogger(); + log.Information("Destructuring with hard-coded policy {@Input}", new { Foo = "Bar" }); + var formattedProperty = evt.Properties["Input"].ToString(); + + Assert.Equal("\"hardcoded\"", formattedProperty); + } } } diff --git a/test/TestDummies/DummyHardCodedStringDestructuringPolicy.cs b/test/TestDummies/DummyHardCodedStringDestructuringPolicy.cs new file mode 100644 index 0000000..25d2724 --- /dev/null +++ b/test/TestDummies/DummyHardCodedStringDestructuringPolicy.cs @@ -0,0 +1,22 @@ +using System; +using Serilog.Core; +using Serilog.Events; + +namespace TestDummies +{ + public class DummyHardCodedStringDestructuringPolicy : IDestructuringPolicy + { + readonly string _hardCodedString; + + public DummyHardCodedStringDestructuringPolicy(string hardCodedString) + { + _hardCodedString = hardCodedString ?? throw new ArgumentNullException(nameof(hardCodedString)); + } + + public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, out LogEventPropertyValue result) + { + result = new ScalarValue(_hardCodedString); + return true; + } + } +} diff --git a/test/TestDummies/DummyLoggerConfigurationExtensions.cs b/test/TestDummies/DummyLoggerConfigurationExtensions.cs index 5f1e039..c5ff18c 100644 --- a/test/TestDummies/DummyLoggerConfigurationExtensions.cs +++ b/test/TestDummies/DummyLoggerConfigurationExtensions.cs @@ -109,6 +109,14 @@ public static LoggerConfiguration Dummy( s => new DummyWrappingSink(s), wrappedSinkAction); } - + + public static LoggerConfiguration WithDummyHardCodedString( + this LoggerDestructuringConfiguration loggerDestructuringConfiguration, + string hardCodedString + ) + { + return loggerDestructuringConfiguration.With(new DummyHardCodedStringDestructuringPolicy(hardCodedString)); + } + } } From c5731c960d52a923d768c3b6a023d66a27dd5164 Mon Sep 17 00:00:00 2001 From: Thibaud Desodt Date: Sat, 14 Jul 2018 10:13:26 +0200 Subject: [PATCH 35/61] fix ReSharper warning --- sample/Sample/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sample/Sample/Program.cs b/sample/Sample/Program.cs index a213a4b..562e427 100644 --- a/sample/Sample/Program.cs +++ b/sample/Sample/Program.cs @@ -40,7 +40,7 @@ public static void Main(string[] args) new { TwentyChars = "0123456789abcdefghij" }); logger.Information("Destructure with max collection count:\n{@BigData}", - new { TenItems = new string[] { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" } }); + new { TenItems = new[] { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" } }); logger.Information("Destructure with policy to strip password:\n{@LoginData}", new LoginData { Username = "BGates", Password = "isityearoflinuxyet" }); From 24264878f70c4b84545888a8678e92bda1cfb45a Mon Sep 17 00:00:00 2001 From: Thibaud Desodt Date: Sat, 14 Jul 2018 10:31:19 +0200 Subject: [PATCH 36/61] Reformat / light refactor in ConfigurationReader renamed a variable and reindented properly --- .../Configuration/ConfigurationReader.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index a8b1823..0fb6a87 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -155,10 +155,10 @@ void ApplyFilters(LoggerConfiguration loggerConfiguration, IReadOnlyDictionary declaredLevelSwitches) { - var filterDirective = _section.GetSection("Destructure"); - if(filterDirective.GetChildren().Any()) + var destructureDirective = _section.GetSection("Destructure"); + if (destructureDirective.GetChildren().Any()) { - var methodCalls = GetMethodCalls(filterDirective); + var methodCalls = GetMethodCalls(destructureDirective); CallConfigurationMethods(methodCalls, FindDestructureConfigurationMethods(_configurationAssemblies), loggerConfiguration.Destructure, declaredLevelSwitches); } } @@ -221,9 +221,11 @@ internal ILookup> GetMet where child.Value == null let name = GetSectionName(child) let callArgs = (from argument in child.GetSection("Args").GetChildren() - select new { + select new + { Name = argument.Key, - Value = GetArgumentValue(argument) }).ToDictionary(p => p.Name, p => p.Value) + Value = GetArgumentValue(argument) + }).ToDictionary(p => p.Name, p => p.Value) select new { Name = name, Args = callArgs })) .ToLookup(p => p.Name, p => p.Args); @@ -330,7 +332,7 @@ static void CallConfigurationMethods(ILookup i.ParameterType == typeof(IConfiguration)); - if(parm != null) call[parm.Position - 1] = _configuration; + if (parm != null) call[parm.Position - 1] = _configuration; call.Insert(0, receiver); @@ -376,7 +378,7 @@ internal static IList FindFilterConfigurationMethods(IReadOnlyCollec internal static IList FindDestructureConfigurationMethods(IReadOnlyCollection configurationAssemblies) { var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerDestructuringConfiguration)); - if(configurationAssemblies.Contains(typeof(LoggerDestructuringConfiguration).GetTypeInfo().Assembly)) + if (configurationAssemblies.Contains(typeof(LoggerDestructuringConfiguration).GetTypeInfo().Assembly)) { found.Add(GetSurrogateConfigurationMethod((c, d, _) => With(c, d))); found.Add(GetSurrogateConfigurationMethod((c, m, _) => ToMaximumDepth(c, m))); From 761f01413ebc86a4ab667d8bbb81437dd7bf2ce7 Mon Sep 17 00:00:00 2001 From: Thibaud Desodt Date: Sat, 14 Jul 2018 10:32:08 +0200 Subject: [PATCH 37/61] Add support for handling arguments of type Type by accepting qualified type name --- .../Configuration/StringArgumentValue.cs | 3 ++- .../StringArgumentValueTests.cs | 24 ++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs index e5f79b0..5291f2a 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs @@ -27,7 +27,8 @@ public StringArgumentValue(Func valueProducer, Func change static readonly Dictionary> ExtendedTypeConversions = new Dictionary> { { typeof(Uri), s => new Uri(s) }, - { typeof(TimeSpan), s => TimeSpan.Parse(s) } + { typeof(TimeSpan), s => TimeSpan.Parse(s) }, + { typeof(Type), s => Type.GetType(s, throwOnError:true) }, }; public object ConvertTo(Type toType, IReadOnlyDictionary declaredLevelSwitches) diff --git a/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs b/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs index 4c1811a..e432eda 100644 --- a/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs @@ -163,5 +163,27 @@ public void ReferencingUndeclaredLevelSwitchThrows() Assert.Contains("$mySwitch", ex.Message); Assert.Contains("\"LevelSwitches\":{\"$mySwitch\":", ex.Message); } + + [Fact] + public void StringValuesConvertToTypeFromShortTypeName() + { + var shortTypeName = "System.Version"; + var stringArgumentValue = new StringArgumentValue(() => shortTypeName); + + var actual = (Type)stringArgumentValue.ConvertTo(typeof(Type), new Dictionary()); + + Assert.Equal(typeof(Version), actual); + } + + [Fact] + public void StringValuesConvertToTypeFromAssemblyQualifiedName() + { + var assemblyQualifiedName = typeof(Version).AssemblyQualifiedName; + var stringArgumentValue = new StringArgumentValue(() => assemblyQualifiedName); + + var actual = (Type)stringArgumentValue.ConvertTo(typeof(Type), new Dictionary()); + + Assert.Equal(typeof(Version), actual); + } } -} \ No newline at end of file +} From fac151ffa2172968d73756524f994617b2d69bc3 Mon Sep 17 00:00:00 2001 From: Thibaud Desodt Date: Sat, 14 Jul 2018 10:32:55 +0200 Subject: [PATCH 38/61] Expose Destructure.AsScalar(Type scalarType) to the configuration --- .../Configuration/ConfigurationReader.cs | 4 ++ .../ConfigurationSettingsTests.cs | 48 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 0fb6a87..eacb9f8 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -384,6 +384,7 @@ internal static IList FindDestructureConfigurationMethods(IReadOnlyC found.Add(GetSurrogateConfigurationMethod((c, m, _) => ToMaximumDepth(c, m))); found.Add(GetSurrogateConfigurationMethod((c, m, _) => ToMaximumStringLength(c, m))); found.Add(GetSurrogateConfigurationMethod((c, m, _) => ToMaximumCollectionCount(c, m))); + found.Add(GetSurrogateConfigurationMethod((c, t, _) => AsScalar(c, t))); } return found; @@ -435,6 +436,9 @@ internal static LoggerConfiguration ToMaximumStringLength(LoggerDestructuringCon internal static LoggerConfiguration ToMaximumCollectionCount(LoggerDestructuringConfiguration loggerDestructuringConfiguration, int maximumCollectionCount) => loggerDestructuringConfiguration.ToMaximumCollectionCount(maximumCollectionCount); + internal static LoggerConfiguration AsScalar(LoggerDestructuringConfiguration loggerDestructuringConfiguration, Type scalarType) + => loggerDestructuringConfiguration.AsScalar(scalarType); + internal static LoggerConfiguration FromLogContext(LoggerEnrichmentConfiguration loggerEnrichmentConfiguration) => loggerEnrichmentConfiguration.FromLogContext(); diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index efa78c9..85a0bac 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -703,5 +703,53 @@ public void DestructuringWithCustomExtensionMethodIsApplied() Assert.Equal("\"hardcoded\"", formattedProperty); } + + [Fact] + public void DestructuringAsScalarIsAppliedWithShortTypeName() + { + var json = @"{ + ""Serilog"": { + ""Destructure"": [ + { + ""Name"": ""AsScalar"", + ""Args"": { ""scalarType"": ""System.Version"" } + }] + } + }"; + + LogEvent evt = null; + var log = ConfigFromJson(json) + .WriteTo.Sink(new DelegatingSink(e => evt = e)) + .CreateLogger(); + + log.Information("Destructuring as scalar {@Scalarized}", new Version(2,3)); + var prop = evt.Properties["Scalarized"]; + + Assert.IsType(prop); + } + + [Fact] + public void DestructuringAsScalarIsAppliedWithAssemblyQualifiedName() + { + var json = $@"{{ + ""Serilog"": {{ + ""Destructure"": [ + {{ + ""Name"": ""AsScalar"", + ""Args"": {{ ""scalarType"": ""{typeof(Version).AssemblyQualifiedName}"" }} + }}] + }} + }}"; + + LogEvent evt = null; + var log = ConfigFromJson(json) + .WriteTo.Sink(new DelegatingSink(e => evt = e)) + .CreateLogger(); + + log.Information("Destructuring as scalar {@Scalarized}", new Version(2,3)); + var prop = evt.Properties["Scalarized"]; + + Assert.IsType(prop); + } } } From d9d6cec8841f595272f103b5d22382ae6240337e Mon Sep 17 00:00:00 2001 From: Thibaud Desodt Date: Sun, 15 Jul 2018 15:57:25 +0200 Subject: [PATCH 39/61] Refactor ConfigurationReader and move Surrogate methods to a separate class --- .../Configuration/ConfigurationReader.cs | 59 +---------- .../SurrogateConfigurationMethods.cs | 99 +++++++++++++++++++ 2 files changed, 104 insertions(+), 54 deletions(-) create mode 100644 src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index eacb9f8..0a5ffbf 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -11,7 +11,6 @@ using Serilog.Core; using Serilog.Debugging; using Serilog.Events; -using System.Linq.Expressions; using System.Text.RegularExpressions; namespace Serilog.Settings.Configuration @@ -354,7 +353,7 @@ internal static IList FindSinkConfigurationMethods(IReadOnlyCollecti { var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerSinkConfiguration)); if (configurationAssemblies.Contains(typeof(LoggerSinkConfiguration).GetTypeInfo().Assembly)) - found.Add(GetSurrogateConfigurationMethod, LoggingLevelSwitch>((c, a, s) => Logger(c, a, LevelAlias.Minimum, s))); + found.AddRange(SurrogateConfigurationMethods.WriteTo); return found; } @@ -370,7 +369,7 @@ internal static IList FindFilterConfigurationMethods(IReadOnlyCollec { var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerFilterConfiguration)); if (configurationAssemblies.Contains(typeof(LoggerFilterConfiguration).GetTypeInfo().Assembly)) - found.Add(GetSurrogateConfigurationMethod((c, f, _) => With(c, f))); + found.AddRange(SurrogateConfigurationMethods.Filter); return found; } @@ -379,13 +378,7 @@ internal static IList FindDestructureConfigurationMethods(IReadOnlyC { var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerDestructuringConfiguration)); if (configurationAssemblies.Contains(typeof(LoggerDestructuringConfiguration).GetTypeInfo().Assembly)) - { - found.Add(GetSurrogateConfigurationMethod((c, d, _) => With(c, d))); - found.Add(GetSurrogateConfigurationMethod((c, m, _) => ToMaximumDepth(c, m))); - found.Add(GetSurrogateConfigurationMethod((c, m, _) => ToMaximumStringLength(c, m))); - found.Add(GetSurrogateConfigurationMethod((c, m, _) => ToMaximumCollectionCount(c, m))); - found.Add(GetSurrogateConfigurationMethod((c, t, _) => AsScalar(c, t))); - } + found.AddRange(SurrogateConfigurationMethods.Destructure); return found; } @@ -394,12 +387,12 @@ internal static IList FindEventEnricherConfigurationMethods(IReadOnl { var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerEnrichmentConfiguration)); if (configurationAssemblies.Contains(typeof(LoggerEnrichmentConfiguration).GetTypeInfo().Assembly)) - found.Add(GetSurrogateConfigurationMethod((c, _, __) => FromLogContext(c))); + found.AddRange(SurrogateConfigurationMethods.Enrich); return found; } - internal static IList FindConfigurationExtensionMethods(IReadOnlyCollection configurationAssemblies, Type configType) + internal static List FindConfigurationExtensionMethods(IReadOnlyCollection configurationAssemblies, Type configType) { return configurationAssemblies .SelectMany(a => a.ExportedTypes @@ -411,48 +404,6 @@ internal static IList FindConfigurationExtensionMethods(IReadOnlyCol .ToList(); } - /* - Pass-through calls to various Serilog config methods which are - implemented as instance methods rather than extension methods. The - FindXXXConfigurationMethods calls (above) use these to add method - invocation expressions as surrogates so that SelectConfigurationMethod - has a way to match and invoke these instance methods. - */ - - // TODO: add overload for array argument (ILogEventEnricher[]) - internal static LoggerConfiguration With(LoggerFilterConfiguration loggerFilterConfiguration, ILogEventFilter filter) - => loggerFilterConfiguration.With(filter); - - // TODO: add overload for array argument (IDestructuringPolicy[]) - internal static LoggerConfiguration With(LoggerDestructuringConfiguration loggerDestructuringConfiguration, IDestructuringPolicy policy) - => loggerDestructuringConfiguration.With(policy); - - internal static LoggerConfiguration ToMaximumDepth(LoggerDestructuringConfiguration loggerDestructuringConfiguration, int maximumDestructuringDepth) - => loggerDestructuringConfiguration.ToMaximumDepth(maximumDestructuringDepth); - - internal static LoggerConfiguration ToMaximumStringLength(LoggerDestructuringConfiguration loggerDestructuringConfiguration, int maximumStringLength) - => loggerDestructuringConfiguration.ToMaximumStringLength(maximumStringLength); - - internal static LoggerConfiguration ToMaximumCollectionCount(LoggerDestructuringConfiguration loggerDestructuringConfiguration, int maximumCollectionCount) - => loggerDestructuringConfiguration.ToMaximumCollectionCount(maximumCollectionCount); - - internal static LoggerConfiguration AsScalar(LoggerDestructuringConfiguration loggerDestructuringConfiguration, Type scalarType) - => loggerDestructuringConfiguration.AsScalar(scalarType); - - internal static LoggerConfiguration FromLogContext(LoggerEnrichmentConfiguration loggerEnrichmentConfiguration) - => loggerEnrichmentConfiguration.FromLogContext(); - - // Unlike the other configuration methods, Logger is an instance method rather than an extension. - internal static LoggerConfiguration Logger( - LoggerSinkConfiguration loggerSinkConfiguration, - Action configureLogger, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - LoggingLevelSwitch levelSwitch = null) - => loggerSinkConfiguration.Logger(configureLogger, restrictedToMinimumLevel, levelSwitch); - - internal static MethodInfo GetSurrogateConfigurationMethod(Expression> method) - => (method.Body as MethodCallExpression)?.Method; - internal static bool IsValidSwitchName(string input) { return Regex.IsMatch(input, LevelSwitchNameRegex); diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs new file mode 100644 index 0000000..71faa7f --- /dev/null +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Reflection; +using Serilog.Configuration; +using Serilog.Core; +using Serilog.Events; + +namespace Serilog.Settings.Configuration +{ + /// + /// Contains "fake extension" methods for the Serilog configuration API. + /// By default the settings knows how to find extension methods, but some configuration + /// are actually "regular" method calls and would not be found otherwise. + /// + /// This static class contains internal methods that can be used instead. + /// + /// + static class SurrogateConfigurationMethods + { + public static IEnumerable WriteTo + { + get + { + yield return GetSurrogateConfigurationMethod, LoggingLevelSwitch>((c, a, s) => Logger(c, a, LevelAlias.Minimum, s)); + } + } + + public static IEnumerable Filter + { + get + { + yield return GetSurrogateConfigurationMethod((c, f, _) => With(c, f)); + } + } + + public static IEnumerable Destructure + { + get + { + yield return GetSurrogateConfigurationMethod((c, d, _) => With(c, d)); + yield return GetSurrogateConfigurationMethod((c, m, _) => ToMaximumDepth(c, m)); + yield return GetSurrogateConfigurationMethod((c, m, _) => ToMaximumStringLength(c, m)); + yield return GetSurrogateConfigurationMethod((c, m, _) => ToMaximumCollectionCount(c, m)); + yield return GetSurrogateConfigurationMethod((c, t, _) => AsScalar(c, t)); + } + } + + public static IEnumerable Enrich + { + get + { + yield return GetSurrogateConfigurationMethod((c, _, __) => FromLogContext(c)); + } + } + + static MethodInfo GetSurrogateConfigurationMethod(Expression> method) + => (method.Body as MethodCallExpression)?.Method; + + /* + Pass-through calls to various Serilog config methods which are + implemented as instance methods rather than extension methods. The + FindXXXConfigurationMethods calls (above) use these to add method + invocation expressions as surrogates so that SelectConfigurationMethod + has a way to match and invoke these instance methods. + */ + + // TODO: add overload for array argument (ILogEventEnricher[]) + static LoggerConfiguration With(LoggerFilterConfiguration loggerFilterConfiguration, ILogEventFilter filter) + => loggerFilterConfiguration.With(filter); + + // TODO: add overload for array argument (IDestructuringPolicy[]) + static LoggerConfiguration With(LoggerDestructuringConfiguration loggerDestructuringConfiguration, IDestructuringPolicy policy) + => loggerDestructuringConfiguration.With(policy); + + static LoggerConfiguration ToMaximumDepth(LoggerDestructuringConfiguration loggerDestructuringConfiguration, int maximumDestructuringDepth) + => loggerDestructuringConfiguration.ToMaximumDepth(maximumDestructuringDepth); + + static LoggerConfiguration ToMaximumStringLength(LoggerDestructuringConfiguration loggerDestructuringConfiguration, int maximumStringLength) + => loggerDestructuringConfiguration.ToMaximumStringLength(maximumStringLength); + + static LoggerConfiguration ToMaximumCollectionCount(LoggerDestructuringConfiguration loggerDestructuringConfiguration, int maximumCollectionCount) + => loggerDestructuringConfiguration.ToMaximumCollectionCount(maximumCollectionCount); + + static LoggerConfiguration AsScalar(LoggerDestructuringConfiguration loggerDestructuringConfiguration, Type scalarType) + => loggerDestructuringConfiguration.AsScalar(scalarType); + + static LoggerConfiguration FromLogContext(LoggerEnrichmentConfiguration loggerEnrichmentConfiguration) + => loggerEnrichmentConfiguration.FromLogContext(); + + // Unlike the other configuration methods, Logger is an instance method rather than an extension. + static LoggerConfiguration Logger( + LoggerSinkConfiguration loggerSinkConfiguration, + Action configureLogger, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch levelSwitch = null) + => loggerSinkConfiguration.Logger(configureLogger, restrictedToMinimumLevel, levelSwitch); + } +} From f9a0c5ce5c7252c0ae624cc83b623268a3d9933e Mon Sep 17 00:00:00 2001 From: Alastair Smith Date: Thu, 26 Jul 2018 12:57:21 +0100 Subject: [PATCH 40/61] Build a package version for net461 This will be resolved by NuGet for .NET Framework 4.6.1 upward, and is equivalent to the netstandard2.0 package version. --- .../Serilog.Settings.Configuration.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj index 7de6782..e24977e 100644 --- a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj +++ b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj @@ -4,7 +4,7 @@ Microsoft.Extensions.Configuration (appsettings.json) support for Serilog. 3.0.0 Serilog Contributors - netstandard2.0;net451 + netstandard2.0;net451;net461 true true Serilog.Settings.Configuration @@ -29,7 +29,7 @@ - + From 5cb8e92bc200c6d0491de0bb04553d899c90c7a3 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Wed, 29 Aug 2018 10:11:48 -0400 Subject: [PATCH 41/61] fixes #111 - case-insensitive method argument-matching --- .../Settings/Configuration/ConfigurationReader.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 0a5ffbf..ba3ac84 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -327,7 +327,7 @@ static void CallConfigurationMethods(ILookup s.Key == p.Name) + let directive = method.Value.FirstOrDefault(s => s.Key.Equals(p.Name, StringComparison.OrdinalIgnoreCase)) select directive.Key == null ? p.DefaultValue : directive.Value.ConvertTo(p.ParameterType, declaredLevelSwitches)).ToList(); var parm = methodInfo.GetParameters().FirstOrDefault(i => i.ParameterType == typeof(IConfiguration)); @@ -342,10 +342,15 @@ static void CallConfigurationMethods(ILookup candidateMethods, string name, Dictionary suppliedArgumentValues) { + // Per issue #111, it is safe to use case-insensitive matching on argument names. The CLR doesn't permit this type + // of overloading, and the Microsoft.Extensions.Configuration keys are case-insensitive (case is preserved with some + // config sources, but key-matching is case-insensitive and case-preservation does not appear to be guaranteed). return candidateMethods .Where(m => m.Name == name && - m.GetParameters().Skip(1).All(p => p.HasDefaultValue || suppliedArgumentValues.Any(s => s.Key == p.Name))) - .OrderByDescending(m => m.GetParameters().Count(p => suppliedArgumentValues.Any(s => s.Key == p.Name))) + m.GetParameters().Skip(1) + .All(p => p.HasDefaultValue || suppliedArgumentValues.Any(s => s.Key.Equals(p.Name, StringComparison.OrdinalIgnoreCase)))) + .OrderByDescending(m => + m.GetParameters().Count(p => suppliedArgumentValues.Any(s => s.Key.Equals(p.Name, StringComparison.OrdinalIgnoreCase)))) .FirstOrDefault(); } From 009585532fdb4f54ae4a766dfe7173953c8ca250 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Wed, 29 Aug 2018 13:46:28 -0400 Subject: [PATCH 42/61] unit test for case-insensitive argument matching --- .../ConfigurationSettingsTests.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 85a0bac..71d24b7 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -492,6 +492,30 @@ public void SinkWithIntArrayArgument() Assert.Equal(1, DummyRollingFileSink.Emitted.Count); } + [Trait("Bugfix", "#111")] + [Fact] + public void CaseInsensitiveArgumentNameMatching() + { + var json = @"{ + ""Serilog"": { + ""Using"": [""TestDummies""], + ""WriteTo"": [{ + ""Name"": ""DummyRollingFile"", + ""Args"": {""PATHFORMAT"" : ""C:\\""} + }] + } + }"; + + var log = ConfigFromJson(json) + .CreateLogger(); + + DummyRollingFileSink.Emitted.Clear(); + + log.Write(Some.InformationEvent()); + + Assert.Equal(1, DummyRollingFileSink.Emitted.Count); + } + [Trait("Bugfix", "#91")] [Fact] public void WriteToLoggerWithRestrictedToMinimumLevelIsSupported() From 268b7197b02d8682268f26c1d464fa924b3b0772 Mon Sep 17 00:00:00 2001 From: Nisheeth Barthwal Date: Mon, 10 Sep 2018 17:02:44 +0200 Subject: [PATCH 43/61] fixes #131 by preferring string overloaded methods --- .gitignore | 3 ++ .../Configuration/ConfigurationReader.cs | 11 ++++- .../ConfigurationReaderTests.cs | 15 +++++++ ...figurationWithMultipleMethodsExtensions.cs | 42 +++++++++++++++++++ 4 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 test/Serilog.Settings.Configuration.Tests/DummyLoggerConfigurationWithMultipleMethodsExtensions.cs diff --git a/.gitignore b/.gitignore index 0ced0ba..6796e2b 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,9 @@ bld/ # Visual Studo 2015 cache/options directory .vs/ +# JetBrains project files +.idea/ + # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 8b50e08..633622f 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -321,7 +321,16 @@ internal static MethodInfo SelectConfigurationMethod(IEnumerable can return candidateMethods .Where(m => m.Name == name && m.GetParameters().Skip(1).All(p => p.HasDefaultValue || suppliedArgumentValues.Any(s => s.Key == p.Name))) - .OrderByDescending(m => m.GetParameters().Count(p => suppliedArgumentValues.Any(s => s.Key == p.Name))) + .OrderByDescending(m => + { + var matchingArgs = m.GetParameters().Where(p => suppliedArgumentValues.Any(s => s.Key == p.Name)).ToList(); + + // Prefer the configuration method with most number of matching arguments and of those the ones with + // the most string type parameters to predict best match with least type casting + return new Tuple( + matchingArgs.Count, + matchingArgs.Count(p => p.ParameterType == typeof(string))); + }) .FirstOrDefault(); } diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs index 79df523..701cfee 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs @@ -165,5 +165,20 @@ public void MethodsAreSelectedBasedOnCountOfMatchedArguments() var selected = ConfigurationReader.SelectConfigurationMethod(options, "DummyRollingFile", suppliedArguments); Assert.Equal(typeof(ITextFormatter), selected.GetParameters()[1].ParameterType); } + + [Fact] + public void MethodsAreSelectedBasedOnCountOfMatchedArgumentsAndThenStringType() + { + var options = typeof(DummyLoggerConfigurationWithMultipleMethodsExtensions).GetTypeInfo().DeclaredMethods.ToList(); + Assert.Equal(3, options.Count(mi => mi.Name == "DummyRollingFile")); + var suppliedArguments = new Dictionary() + { + { "pathFormat", new StringArgumentValue(() => "C:\\") }, + { "formatter", new StringArgumentValue(() => "SomeFormatter, SomeAssembly") } + }; + + var selected = ConfigurationReader.SelectConfigurationMethod(options, "DummyRollingFile", suppliedArguments); + Assert.Equal(typeof(string), selected.GetParameters()[2].ParameterType); + } } } diff --git a/test/Serilog.Settings.Configuration.Tests/DummyLoggerConfigurationWithMultipleMethodsExtensions.cs b/test/Serilog.Settings.Configuration.Tests/DummyLoggerConfigurationWithMultipleMethodsExtensions.cs new file mode 100644 index 0000000..b6be570 --- /dev/null +++ b/test/Serilog.Settings.Configuration.Tests/DummyLoggerConfigurationWithMultipleMethodsExtensions.cs @@ -0,0 +1,42 @@ +using System; +using Serilog.Configuration; +using Serilog.Events; +using Serilog.Formatting; + +namespace Serilog.Settings.Configuration.Tests +{ + using System.Collections.Generic; + + static class DummyLoggerConfigurationWithMultipleMethodsExtensions + { + public static LoggerConfiguration DummyRollingFile( + LoggerSinkConfiguration loggerSinkConfiguration, + ITextFormatter formatter, + IEnumerable pathFormat, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + string outputTemplate = null, + IFormatProvider formatProvider = null) + { + return null; + } + + public static LoggerConfiguration DummyRollingFile( + LoggerSinkConfiguration loggerSinkConfiguration, + ITextFormatter formatter, + string pathFormat, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + string outputTemplate = null, + IFormatProvider formatProvider = null) + { + return null; + } + + public static LoggerConfiguration DummyRollingFile( + LoggerSinkConfiguration loggerSinkConfiguration, + string pathFormat, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) + { + return null; + } + } +} From 54ddc8485afc31c0a604a121384f1b99af2cd68b Mon Sep 17 00:00:00 2001 From: Maxime Rouiller Date: Fri, 21 Sep 2018 10:37:11 -0400 Subject: [PATCH 44/61] fixing repository url --- .../Serilog.Settings.Configuration.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj index e24977e..1c27b76 100644 --- a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj +++ b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj @@ -16,6 +16,8 @@ https://serilog.net/images/serilog-configuration-nuget.png https://github.com/serilog/serilog-settings-configuration https://www.apache.org/licenses/LICENSE-2.0 + https://github.com/serilog/serilog-settings-configuration + git Serilog From da8a88340e24223cb7115805e7af6ce8f48eb542 Mon Sep 17 00:00:00 2001 From: Nisheeth Barthwal Date: Sat, 29 Sep 2018 16:51:27 +0200 Subject: [PATCH 45/61] fixes typo --- .../Settings/Configuration/ConfigurationReader.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index c7dae65..5d52aa1 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -351,7 +351,8 @@ internal static MethodInfo SelectConfigurationMethod(IEnumerable can .All(p => p.HasDefaultValue || suppliedArgumentValues.Any(s => s.Key.Equals(p.Name, StringComparison.OrdinalIgnoreCase)))) .OrderByDescending(m => { - var matchingArgs = m.GetParameters().Where(p => suppliedArgumentValues.Any(s => s.Key.Equals(p.Name, StringComparison.OrdinalIgnoreCase)))).ToList(); + var matchingArgs = m.GetParameters().Where(p => suppliedArgumentValues.Any(s => s.Key.Equals(p.Name, StringComparison.OrdinalIgnoreCase))) + do.ToList(); // Prefer the configuration method with most number of matching arguments and of those the ones with // the most string type parameters to predict best match with least type casting From 578cfc19ca0127f73fa6f882a72a9b1800a0c08f Mon Sep 17 00:00:00 2001 From: Nisheeth Barthwal Date: Sat, 29 Sep 2018 16:51:57 +0200 Subject: [PATCH 46/61] fixe typo --- .../Settings/Configuration/ConfigurationReader.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 5d52aa1..3ef88b8 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -351,8 +351,7 @@ internal static MethodInfo SelectConfigurationMethod(IEnumerable can .All(p => p.HasDefaultValue || suppliedArgumentValues.Any(s => s.Key.Equals(p.Name, StringComparison.OrdinalIgnoreCase)))) .OrderByDescending(m => { - var matchingArgs = m.GetParameters().Where(p => suppliedArgumentValues.Any(s => s.Key.Equals(p.Name, StringComparison.OrdinalIgnoreCase))) - do.ToList(); + var matchingArgs = m.GetParameters().Where(p => suppliedArgumentValues.Any(s => s.Key.Equals(p.Name, StringComparison.OrdinalIgnoreCase))).ToList(); // Prefer the configuration method with most number of matching arguments and of those the ones with // the most string type parameters to predict best match with least type casting From cae987aedc4d4781896a8a82fcb4003eb38cde5a Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Sat, 29 Sep 2018 12:52:53 -0400 Subject: [PATCH 47/61] fixes #63 - option to control assembly source --- ...figurationLoggerConfigurationExtensions.cs | 40 +++++++++++++++---- .../LoggerConfigurationExtensionsTests.cs | 32 ++++++++++++++- 2 files changed, 62 insertions(+), 10 deletions(-) diff --git a/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs b/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs index 9f007fa..1d3d1a6 100644 --- a/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs +++ b/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs @@ -21,6 +21,22 @@ namespace Serilog { + /// + /// Defines the interpretation of a null DependencyContext configuration parameter. + /// + public enum ConfigurationAssemblySource + { + /// + /// Try to scan the assemblies already in memory. This is the default. If GetEntryAssembly is null, fallback to DLL scanning. + /// + UseLoadedAssemblies, + + /// + /// Scan for assemblies in DLLs from the working directory. This is the fallback when GetEntryAssembly is null. + /// + AlwaysScanDllFiles + } + /// /// Extends with support for System.Configuration appSettings elements. /// @@ -38,19 +54,23 @@ public static class ConfigurationLoggerConfigurationExtensions /// /// Logger setting configuration. /// A configuration object which contains a Serilog section. - /// The dependency context from which sink/enricher packages can be located. If not supplied, the platform - /// default will be used. + /// The dependency context from which sink/enricher packages can be located. + /// If dependency context is not supplied, either the platform default or DLL scanning may be used. /// An object allowing configuration to continue. public static LoggerConfiguration Configuration( this LoggerSettingsConfiguration settingConfiguration, IConfiguration configuration, - DependencyContext dependencyContext = null) + DependencyContext dependencyContext = null, + ConfigurationAssemblySource onNullDependencyContext = ConfigurationAssemblySource.UseLoadedAssemblies) { if (configuration == null) throw new ArgumentNullException(nameof(configuration)); return settingConfiguration.Settings( new ConfigurationReader( configuration, - dependencyContext ?? (Assembly.GetEntryAssembly() != null ? DependencyContext.Default : null))); + dependencyContext ?? + (onNullDependencyContext == ConfigurationAssemblySource.UseLoadedAssemblies + && Assembly.GetEntryAssembly() != null + ? DependencyContext.Default : null))); } /// @@ -59,13 +79,14 @@ public static LoggerConfiguration Configuration( /// /// Logger setting configuration. /// The Serilog configuration section - /// The dependency context from which sink/enricher packages can be located. If not supplied, the platform - /// default will be used. + /// The dependency context from which sink/enricher packages can be located. + /// If dependency context is not supplied, either the platform default or DLL scanning may be used. /// An object allowing configuration to continue. public static LoggerConfiguration ConfigurationSection( this LoggerSettingsConfiguration settingConfiguration, IConfigurationSection configSection, - DependencyContext dependencyContext = null) + DependencyContext dependencyContext = null, + ConfigurationAssemblySource onNullDependencyContext = ConfigurationAssemblySource.UseLoadedAssemblies) { if (settingConfiguration == null) throw new ArgumentNullException(nameof(settingConfiguration)); if (configSection == null) throw new ArgumentNullException(nameof(configSection)); @@ -73,7 +94,10 @@ public static LoggerConfiguration ConfigurationSection( return settingConfiguration.Settings( new ConfigurationReader( configSection, - dependencyContext ?? (Assembly.GetEntryAssembly() != null ? DependencyContext.Default : null))); + dependencyContext ?? + (onNullDependencyContext == ConfigurationAssemblySource.UseLoadedAssemblies + && Assembly.GetEntryAssembly() != null + ? DependencyContext.Default : null))); } } } diff --git a/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs b/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs index e725f6d..14d4f8c 100644 --- a/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs @@ -1,5 +1,7 @@ -using Microsoft.Extensions.Configuration; -using System; +using System; +using Microsoft.Extensions.Configuration; +using Serilog.Settings.Configuration.Tests.Support; +using TestDummies.Console; using Xunit; namespace Serilog.Settings.Configuration.Tests @@ -14,5 +16,31 @@ public void ReadFromConfigurationShouldNotThrowOnEmptyConfiguration() // should not throw act(); } + + [Fact] + public void ConfigurationAssembliesFromDllScanning() + { + var json = @"{ + ""Serilog"": { + ""Using"": [""TestDummies""], + ""WriteTo"": [""DummyConsole""] + } + }"; + + var builder = new ConfigurationBuilder().AddJsonString(json); + var config = builder.Build(); + var log = new LoggerConfiguration() + .ReadFrom.Configuration( + configuration: config, + dependencyContext: null, + onNullDependencyContext: ConfigurationAssemblySource.AlwaysScanDllFiles) + .CreateLogger(); + + DummyConsoleSink.Emitted.Clear(); + + log.Write(Some.InformationEvent()); + + Assert.Equal(1, DummyConsoleSink.Emitted.Count); + } } } From 0d4b69ec53bf02ae6248d02ca4a7c0946f47fe71 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Sat, 29 Sep 2018 14:47:12 -0400 Subject: [PATCH 48/61] moved test to avoid parallel test conflicts --- .../ConfigurationSettingsTests.cs | 26 +++++++++++++++++ .../LoggerConfigurationExtensionsTests.cs | 28 ------------------- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 71d24b7..714e36b 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -77,6 +77,32 @@ public void ParameterlessSinksAreConfigured(string syntax, string json) Assert.Equal(1, DummyWithLevelSwitchSink.Emitted.Count); } + [Fact] + public void ConfigurationAssembliesFromDllScanning() + { + var json = @"{ + ""Serilog"": { + ""Using"": [""TestDummies""], + ""WriteTo"": [""DummyConsole""] + } + }"; + + var builder = new ConfigurationBuilder().AddJsonString(json); + var config = builder.Build(); + var log = new LoggerConfiguration() + .ReadFrom.Configuration( + configuration: config, + dependencyContext: null, + onNullDependencyContext: ConfigurationAssemblySource.AlwaysScanDllFiles) + .CreateLogger(); + + DummyConsoleSink.Emitted.Clear(); + + log.Write(Some.InformationEvent()); + + Assert.Equal(1, DummyConsoleSink.Emitted.Count); + } + [Fact] public void SinksAreConfigured() { diff --git a/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs b/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs index 14d4f8c..b00c26a 100644 --- a/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs @@ -1,7 +1,5 @@ using System; using Microsoft.Extensions.Configuration; -using Serilog.Settings.Configuration.Tests.Support; -using TestDummies.Console; using Xunit; namespace Serilog.Settings.Configuration.Tests @@ -16,31 +14,5 @@ public void ReadFromConfigurationShouldNotThrowOnEmptyConfiguration() // should not throw act(); } - - [Fact] - public void ConfigurationAssembliesFromDllScanning() - { - var json = @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""WriteTo"": [""DummyConsole""] - } - }"; - - var builder = new ConfigurationBuilder().AddJsonString(json); - var config = builder.Build(); - var log = new LoggerConfiguration() - .ReadFrom.Configuration( - configuration: config, - dependencyContext: null, - onNullDependencyContext: ConfigurationAssemblySource.AlwaysScanDllFiles) - .CreateLogger(); - - DummyConsoleSink.Emitted.Clear(); - - log.Write(Some.InformationEvent()); - - Assert.Equal(1, DummyConsoleSink.Emitted.Count); - } } } From 1f24471cb6925c5c418abd5fa5a6c50375a49053 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Wed, 3 Oct 2018 16:07:36 -0400 Subject: [PATCH 49/61] PR feedback --- ...figurationLoggerConfigurationExtensions.cs | 95 ++++++++++++------- .../LoggerConfigurationSupport.cs | 32 +++++++ .../ConfigurationSettingsTests.cs | 3 +- 3 files changed, 95 insertions(+), 35 deletions(-) create mode 100644 src/Serilog.Settings.Configuration/LoggerConfigurationSupport.cs diff --git a/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs b/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs index 1d3d1a6..2de4db4 100644 --- a/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs +++ b/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs @@ -21,22 +21,6 @@ namespace Serilog { - /// - /// Defines the interpretation of a null DependencyContext configuration parameter. - /// - public enum ConfigurationAssemblySource - { - /// - /// Try to scan the assemblies already in memory. This is the default. If GetEntryAssembly is null, fallback to DLL scanning. - /// - UseLoadedAssemblies, - - /// - /// Scan for assemblies in DLLs from the working directory. This is the fallback when GetEntryAssembly is null. - /// - AlwaysScanDllFiles - } - /// /// Extends with support for System.Configuration appSettings elements. /// @@ -54,23 +38,20 @@ public static class ConfigurationLoggerConfigurationExtensions /// /// Logger setting configuration. /// A configuration object which contains a Serilog section. - /// The dependency context from which sink/enricher packages can be located. - /// If dependency context is not supplied, either the platform default or DLL scanning may be used. + /// The dependency context from which sink/enricher packages can be located. If not supplied, the platform + /// default will be used. /// An object allowing configuration to continue. public static LoggerConfiguration Configuration( this LoggerSettingsConfiguration settingConfiguration, IConfiguration configuration, - DependencyContext dependencyContext = null, - ConfigurationAssemblySource onNullDependencyContext = ConfigurationAssemblySource.UseLoadedAssemblies) + DependencyContext dependencyContext = null) { if (configuration == null) throw new ArgumentNullException(nameof(configuration)); + return settingConfiguration.Settings( new ConfigurationReader( configuration, - dependencyContext ?? - (onNullDependencyContext == ConfigurationAssemblySource.UseLoadedAssemblies - && Assembly.GetEntryAssembly() != null - ? DependencyContext.Default : null))); + dependencyContext ?? (Assembly.GetEntryAssembly() != null ? DependencyContext.Default : null))); } /// @@ -79,25 +60,73 @@ public static LoggerConfiguration Configuration( /// /// Logger setting configuration. /// The Serilog configuration section - /// The dependency context from which sink/enricher packages can be located. - /// If dependency context is not supplied, either the platform default or DLL scanning may be used. + /// The dependency context from which sink/enricher packages can be located. If not supplied, the platform + /// default will be used. /// An object allowing configuration to continue. public static LoggerConfiguration ConfigurationSection( this LoggerSettingsConfiguration settingConfiguration, IConfigurationSection configSection, - DependencyContext dependencyContext = null, - ConfigurationAssemblySource onNullDependencyContext = ConfigurationAssemblySource.UseLoadedAssemblies) + DependencyContext dependencyContext = null) { if (settingConfiguration == null) throw new ArgumentNullException(nameof(settingConfiguration)); if (configSection == null) throw new ArgumentNullException(nameof(configSection)); - + return settingConfiguration.Settings( new ConfigurationReader( configSection, - dependencyContext ?? - (onNullDependencyContext == ConfigurationAssemblySource.UseLoadedAssemblies - && Assembly.GetEntryAssembly() != null - ? DependencyContext.Default : null))); + dependencyContext ?? (Assembly.GetEntryAssembly() != null ? DependencyContext.Default : null))); + } + + /// + /// Reads logger settings from the provided configuration object using the default section name. Generally this + /// is preferable over the other method that takes a configuration section. Only this version will populate + /// IConfiguration parameters on target methods. + /// + /// Logger setting configuration. + /// A configuration object which contains a Serilog section. + /// Defines how the package identifies assemblies to scan for sinks and other Types. + /// An object allowing configuration to continue. + public static LoggerConfiguration Configuration( + this LoggerSettingsConfiguration settingConfiguration, + IConfiguration configuration, + ConfigurationAssemblySource configurationAssemblySource) + { + if (configuration == null) throw new ArgumentNullException(nameof(configuration)); + + if(configurationAssemblySource == ConfigurationAssemblySource.UseLoadedAssemblies) + { + return Configuration(settingConfiguration, configuration); + } + else + { + return settingConfiguration.Settings(new ConfigurationReader(configuration, null)); + } + } + + /// + /// Reads logger settings from the provided configuration section. Generally it is preferable to use the other + /// extension method that takes the full configuration object. + /// + /// Logger setting configuration. + /// The Serilog configuration section + /// Defines how the package identifies assemblies to scan for sinks and other Types. + /// An object allowing configuration to continue. + public static LoggerConfiguration ConfigurationSection( + this LoggerSettingsConfiguration settingConfiguration, + IConfigurationSection configSection, + ConfigurationAssemblySource configurationAssemblySource) + { + if (settingConfiguration == null) throw new ArgumentNullException(nameof(settingConfiguration)); + if (configSection == null) throw new ArgumentNullException(nameof(configSection)); + + if (configurationAssemblySource == ConfigurationAssemblySource.UseLoadedAssemblies) + { + return Configuration(settingConfiguration, configSection); + } + else + { + return settingConfiguration.Settings(new ConfigurationReader(configSection, null)); + } } } } diff --git a/src/Serilog.Settings.Configuration/LoggerConfigurationSupport.cs b/src/Serilog.Settings.Configuration/LoggerConfigurationSupport.cs new file mode 100644 index 0000000..de5800c --- /dev/null +++ b/src/Serilog.Settings.Configuration/LoggerConfigurationSupport.cs @@ -0,0 +1,32 @@ +// Copyright 2013-2016 Serilog Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Serilog.Settings.Configuration +{ + /// + /// Defines how the package will identify the assemblies which are scanned for sinks and other Type information. + /// + public enum ConfigurationAssemblySource + { + /// + /// Try to scan the assemblies already in memory. This is the default. If GetEntryAssembly is null, fallback to DLL scanning. + /// + UseLoadedAssemblies, + + /// + /// Scan for assemblies in DLLs from the working directory. This is the fallback when GetEntryAssembly is null. + /// + AlwaysScanDllFiles + } +} diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 714e36b..1cd0d70 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -92,8 +92,7 @@ public void ConfigurationAssembliesFromDllScanning() var log = new LoggerConfiguration() .ReadFrom.Configuration( configuration: config, - dependencyContext: null, - onNullDependencyContext: ConfigurationAssemblySource.AlwaysScanDllFiles) + configurationAssemblySource: ConfigurationAssemblySource.AlwaysScanDllFiles) .CreateLogger(); DummyConsoleSink.Emitted.Clear(); From 1a46ac7d5e7458fc907d0976def83ece01e46a86 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Fri, 5 Oct 2018 08:26:49 -0400 Subject: [PATCH 50/61] filename/location tweak per feedback --- .../Configuration/ConfigurationAssemblySource.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/Serilog.Settings.Configuration/{LoggerConfigurationSupport.cs => Settings/Configuration/ConfigurationAssemblySource.cs} (100%) diff --git a/src/Serilog.Settings.Configuration/LoggerConfigurationSupport.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationAssemblySource.cs similarity index 100% rename from src/Serilog.Settings.Configuration/LoggerConfigurationSupport.cs rename to src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationAssemblySource.cs From 35ff174b951fefd2940532bcb03b0738afcaf656 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Sat, 6 Oct 2018 06:22:12 -0400 Subject: [PATCH 51/61] removed unnecessary package ref --- .../Serilog.Settings.Configuration.csproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj index 1c27b76..b3bb545 100644 --- a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj +++ b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj @@ -27,13 +27,10 @@ - - - From 818f08fbb52a687979998ddafe4ec71b6b492aae Mon Sep 17 00:00:00 2001 From: Thibaud Desodt Date: Sat, 6 Oct 2018 15:23:23 +0200 Subject: [PATCH 52/61] Remove unused class `ConfigurationValueSyntax` introduced while trying to fix the broken v2.6 release --- .../Configuration/ConfigurationValueSyntax.cs | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationValueSyntax.cs diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationValueSyntax.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationValueSyntax.cs deleted file mode 100644 index 0569531..0000000 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationValueSyntax.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Text.RegularExpressions; - -namespace Serilog.Settings.Configuration -{ - static class ConfigurationValueSyntax - { - const string LevelSwitchNameRegex = @"^\$[A-Za-z]+[A-Za-z0-9]*$"; - - public static bool IsValidSwitchName(string input) - { - return Regex.IsMatch(input, LevelSwitchNameRegex); - } - } -} \ No newline at end of file From 2fe5f4fcb4b4f59a56f42ee1c68621b8f8b030b6 Mon Sep 17 00:00:00 2001 From: Thibaud Desodt Date: Sat, 6 Oct 2018 16:17:37 +0200 Subject: [PATCH 53/61] Minor refactoring to properly reset static properties --- .../ConfigurationSettingsTests.cs | 30 +++++++++---------- test/TestDummies/DummyRollingFileAuditSink.cs | 11 +++++-- test/TestDummies/DummyRollingFileSink.cs | 11 +++++-- test/TestDummies/DummyWrappingSink.cs | 13 +++++--- 4 files changed, 40 insertions(+), 25 deletions(-) diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 1cd0d70..4c75a58 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.Extensions.Configuration; using Serilog.Core; using Serilog.Events; @@ -118,8 +118,8 @@ public void SinksAreConfigured() var log = ConfigFromJson(json) .CreateLogger(); - DummyRollingFileSink.Emitted.Clear(); - DummyRollingFileAuditSink.Emitted.Clear(); + DummyRollingFileSink.Reset(); + DummyRollingFileAuditSink.Reset(); log.Write(Some.InformationEvent()); @@ -143,8 +143,8 @@ public void AuditSinksAreConfigured() var log = ConfigFromJson(json) .CreateLogger(); - DummyRollingFileSink.Emitted.Clear(); - DummyRollingFileAuditSink.Emitted.Clear(); + DummyRollingFileSink.Reset(); + DummyRollingFileAuditSink.Reset(); log.Write(Some.InformationEvent()); @@ -438,7 +438,7 @@ public void SinkWithIConfigurationArguments() var log = ConfigFromJson(json) .CreateLogger(); - DummyRollingFileSink.Emitted.Clear(); + DummyRollingFileSink.Reset(); log.Write(Some.InformationEvent()); @@ -462,7 +462,7 @@ public void SinkWithConfigurationBindingArgument() var log = ConfigFromJson(json) .CreateLogger(); - DummyRollingFileSink.Emitted.Clear(); + DummyRollingFileSink.Reset(); log.Write(Some.InformationEvent()); @@ -486,7 +486,7 @@ public void SinkWithStringArrayArgument() var log = ConfigFromJson(json) .CreateLogger(); - DummyRollingFileSink.Emitted.Clear(); + DummyRollingFileSink.Reset(); log.Write(Some.InformationEvent()); @@ -510,7 +510,7 @@ public void SinkWithIntArrayArgument() var log = ConfigFromJson(json) .CreateLogger(); - DummyRollingFileSink.Emitted.Clear(); + DummyRollingFileSink.Reset(); log.Write(Some.InformationEvent()); @@ -534,7 +534,7 @@ public void CaseInsensitiveArgumentNameMatching() var log = ConfigFromJson(json) .CreateLogger(); - DummyRollingFileSink.Emitted.Clear(); + DummyRollingFileSink.Reset(); log.Write(Some.InformationEvent()); @@ -565,7 +565,7 @@ public void WriteToLoggerWithRestrictedToMinimumLevelIsSupported() var log = ConfigFromJson(json) .CreateLogger(); - DummyRollingFileSink.Emitted.Clear(); + DummyRollingFileSink.Reset(); log.Write(Some.InformationEvent()); log.Write(Some.WarningEvent()); @@ -598,9 +598,9 @@ public void WriteToSubLoggerWithLevelSwitchIsSupported() }"; var log = ConfigFromJson(json) - .CreateLogger(); + .CreateLogger(); - DummyRollingFileSink.Emitted.Clear(); + DummyRollingFileSink.Reset(); log.Write(Some.InformationEvent()); log.Write(Some.WarningEvent()); @@ -771,7 +771,7 @@ public void DestructuringAsScalarIsAppliedWithShortTypeName() .WriteTo.Sink(new DelegatingSink(e => evt = e)) .CreateLogger(); - log.Information("Destructuring as scalar {@Scalarized}", new Version(2,3)); + log.Information("Destructuring as scalar {@Scalarized}", new Version(2, 3)); var prop = evt.Properties["Scalarized"]; Assert.IsType(prop); @@ -795,7 +795,7 @@ public void DestructuringAsScalarIsAppliedWithAssemblyQualifiedName() .WriteTo.Sink(new DelegatingSink(e => evt = e)) .CreateLogger(); - log.Information("Destructuring as scalar {@Scalarized}", new Version(2,3)); + log.Information("Destructuring as scalar {@Scalarized}", new Version(2, 3)); var prop = evt.Properties["Scalarized"]; Assert.IsType(prop); diff --git a/test/TestDummies/DummyRollingFileAuditSink.cs b/test/TestDummies/DummyRollingFileAuditSink.cs index e8618d0..2ba17e3 100644 --- a/test/TestDummies/DummyRollingFileAuditSink.cs +++ b/test/TestDummies/DummyRollingFileAuditSink.cs @@ -8,13 +8,18 @@ namespace TestDummies public class DummyRollingFileAuditSink : ILogEventSink { [ThreadStatic] - // ReSharper disable ThreadStaticFieldHasInitializer - public static List Emitted = new List(); - // ReSharper restore ThreadStaticFieldHasInitializer + static List _emitted; + + public static List Emitted => _emitted ?? (_emitted = new List()); public void Emit(LogEvent logEvent) { Emitted.Add(logEvent); } + + public static void Reset() + { + _emitted = null; + } } } diff --git a/test/TestDummies/DummyRollingFileSink.cs b/test/TestDummies/DummyRollingFileSink.cs index bc98ed2..2f6f229 100644 --- a/test/TestDummies/DummyRollingFileSink.cs +++ b/test/TestDummies/DummyRollingFileSink.cs @@ -8,13 +8,18 @@ namespace TestDummies public class DummyRollingFileSink : ILogEventSink { [ThreadStatic] - // ReSharper disable ThreadStaticFieldHasInitializer - public static List Emitted = new List(); - // ReSharper restore ThreadStaticFieldHasInitializer + static List _emitted; + + public static List Emitted => _emitted ?? (_emitted = new List()); public void Emit(LogEvent logEvent) { Emitted.Add(logEvent); } + + public static void Reset() + { + _emitted = null; + } } } diff --git a/test/TestDummies/DummyWrappingSink.cs b/test/TestDummies/DummyWrappingSink.cs index cd15d8c..cb2f048 100644 --- a/test/TestDummies/DummyWrappingSink.cs +++ b/test/TestDummies/DummyWrappingSink.cs @@ -8,11 +8,11 @@ namespace TestDummies public class DummyWrappingSink : ILogEventSink { [ThreadStatic] - // ReSharper disable ThreadStaticFieldHasInitializer - public static List Emitted = new List(); - // ReSharper restore ThreadStaticFieldHasInitializer + static List _emitted; - private readonly ILogEventSink _sink; + public static List Emitted => _emitted ?? (_emitted = new List()); + + readonly ILogEventSink _sink; public DummyWrappingSink(ILogEventSink sink) { @@ -24,5 +24,10 @@ public void Emit(LogEvent logEvent) Emitted.Add(logEvent); _sink.Emit(logEvent); } + + public static void Reset() + { + _emitted = null; + } } } From b98aba2088b5eaa73de72c4baef02f9cd88a426a Mon Sep 17 00:00:00 2001 From: Thibaud Desodt Date: Sat, 6 Oct 2018 21:42:02 +0200 Subject: [PATCH 54/61] Add support for WriteTo.Sink() Support for `WriteTo.Sink(ILogEventSink logEventSink, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, LoggingLevelSwitch levelSwitch = null)` --- .../SurrogateConfigurationMethods.cs | 10 +++ .../ConfigurationSettingsTests.cs | 83 ++++++++++++++++++- 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs index 71faa7f..869b3c0 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs @@ -23,6 +23,7 @@ public static IEnumerable WriteTo get { yield return GetSurrogateConfigurationMethod, LoggingLevelSwitch>((c, a, s) => Logger(c, a, LevelAlias.Minimum, s)); + yield return GetSurrogateConfigurationMethod((c, sink, s) => Sink(c, sink, LevelAlias.Minimum, s)); } } @@ -65,6 +66,15 @@ invocation expressions as surrogates so that SelectConfigurationMethod has a way to match and invoke these instance methods. */ + internal static LoggerConfiguration Sink( + LoggerSinkConfiguration loggerSinkConfiguration, + ILogEventSink sink, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch levelSwitch = null) + { + return loggerSinkConfiguration.Sink(sink, restrictedToMinimumLevel, levelSwitch); + } + // TODO: add overload for array argument (ILogEventEnricher[]) static LoggerConfiguration With(LoggerFilterConfiguration loggerFilterConfiguration, ILogEventFilter filter) => loggerFilterConfiguration.With(filter); diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 4c75a58..71a03d1 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.Extensions.Configuration; using Serilog.Core; using Serilog.Events; @@ -800,5 +800,86 @@ public void DestructuringAsScalarIsAppliedWithAssemblyQualifiedName() Assert.IsType(prop); } + + + [Fact] + public void WriteToSinkIsAppliedWithCustomSink() + { + var json = $@"{{ + ""Serilog"": {{ + ""Using"": [""TestDummies""], + ""WriteTo"": [ + {{ + ""Name"": ""Sink"", + ""Args"": {{ + ""sink"": ""{typeof(DummyRollingFileSink).AssemblyQualifiedName}"" + }} + }}] + }} + }}"; + + var log = ConfigFromJson(json) + .CreateLogger(); + + DummyRollingFileSink.Reset(); + log.Write(Some.InformationEvent()); + + Assert.Single(DummyRollingFileSink.Emitted); + } + + [Fact] + public void WriteToSinkIsAppliedWithCustomSinkAndMinimumLevel() + { + var json = $@"{{ + ""Serilog"": {{ + ""Using"": [""TestDummies""], + ""WriteTo"": [ + {{ + ""Name"": ""Sink"", + ""Args"": {{ + ""sink"": ""{typeof(DummyRollingFileSink).AssemblyQualifiedName}"", + ""restrictedToMinimumLevel"": ""Warning"" + }} + }}] + }} + }}"; + + var log = ConfigFromJson(json) + .CreateLogger(); + + DummyRollingFileSink.Reset(); + log.Write(Some.InformationEvent()); + log.Write(Some.WarningEvent()); + + Assert.Single(DummyRollingFileSink.Emitted); + } + + [Fact] + public void WriteToSinkIsAppliedWithCustomSinkAndLevelSwitch() + { + var json = $@"{{ + ""Serilog"": {{ + ""Using"": [""TestDummies""], + ""LevelSwitches"": {{""$switch1"": ""Warning"" }}, + ""WriteTo"": [ + {{ + ""Name"": ""Sink"", + ""Args"": {{ + ""sink"": ""{typeof(DummyRollingFileSink).AssemblyQualifiedName}"", + ""levelSwitch"": ""$switch1"" + }} + }}] + }} + }}"; + + var log = ConfigFromJson(json) + .CreateLogger(); + + DummyRollingFileSink.Reset(); + log.Write(Some.InformationEvent()); + log.Write(Some.WarningEvent()); + + Assert.Single(DummyRollingFileSink.Emitted); + } } } From ed9c04914a79c44d02101a2364985f38ecee596c Mon Sep 17 00:00:00 2001 From: Thibaud Desodt Date: Sat, 6 Oct 2018 21:55:34 +0200 Subject: [PATCH 55/61] Add support for AuditTo.Sink() Support for `AuditTo.Sink(ILogEventSink logEventSink, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, LoggingLevelSwitch levelSwitch = null)` --- .../Configuration/ConfigurationReader.cs | 3 +- .../SurrogateConfigurationMethods.cs | 43 ++++++++-- .../ConfigurationSettingsTests.cs | 81 +++++++++++++++++++ 3 files changed, 118 insertions(+), 9 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 3ef88b8..f72bb13 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -374,7 +374,8 @@ internal static IList FindSinkConfigurationMethods(IReadOnlyCollecti internal static IList FindAuditSinkConfigurationMethods(IReadOnlyCollection configurationAssemblies) { var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerAuditSinkConfiguration)); - + if (configurationAssemblies.Contains(typeof(LoggerAuditSinkConfiguration).GetTypeInfo().Assembly)) + found.AddRange(SurrogateConfigurationMethods.AuditTo); return found; } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs index 869b3c0..580a3de 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs @@ -27,6 +27,14 @@ public static IEnumerable WriteTo } } + public static IEnumerable AuditTo + { + get + { + yield return GetSurrogateConfigurationMethod((c, sink, s) => Sink(c, sink, LevelAlias.Minimum, s)); + } + } + public static IEnumerable Filter { get @@ -66,7 +74,9 @@ invocation expressions as surrogates so that SelectConfigurationMethod has a way to match and invoke these instance methods. */ - internal static LoggerConfiguration Sink( + // .WriteTo... + // ======== + static LoggerConfiguration Sink( LoggerSinkConfiguration loggerSinkConfiguration, ILogEventSink sink, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, @@ -75,10 +85,32 @@ internal static LoggerConfiguration Sink( return loggerSinkConfiguration.Sink(sink, restrictedToMinimumLevel, levelSwitch); } + static LoggerConfiguration Logger( + LoggerSinkConfiguration loggerSinkConfiguration, + Action configureLogger, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch levelSwitch = null) + => loggerSinkConfiguration.Logger(configureLogger, restrictedToMinimumLevel, levelSwitch); + + // .AuditTo... + // ======== + static LoggerConfiguration Sink( + LoggerAuditSinkConfiguration auditSinkConfiguration, + ILogEventSink sink, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch levelSwitch = null) + { + return auditSinkConfiguration.Sink(sink, restrictedToMinimumLevel, levelSwitch); + } + + // .Filter... + // ======= // TODO: add overload for array argument (ILogEventEnricher[]) static LoggerConfiguration With(LoggerFilterConfiguration loggerFilterConfiguration, ILogEventFilter filter) => loggerFilterConfiguration.With(filter); + // .Destructure... + // ============ // TODO: add overload for array argument (IDestructuringPolicy[]) static LoggerConfiguration With(LoggerDestructuringConfiguration loggerDestructuringConfiguration, IDestructuringPolicy policy) => loggerDestructuringConfiguration.With(policy); @@ -95,15 +127,10 @@ static LoggerConfiguration ToMaximumCollectionCount(LoggerDestructuringConfigura static LoggerConfiguration AsScalar(LoggerDestructuringConfiguration loggerDestructuringConfiguration, Type scalarType) => loggerDestructuringConfiguration.AsScalar(scalarType); + // .Enrich... + // ======= static LoggerConfiguration FromLogContext(LoggerEnrichmentConfiguration loggerEnrichmentConfiguration) => loggerEnrichmentConfiguration.FromLogContext(); - // Unlike the other configuration methods, Logger is an instance method rather than an extension. - static LoggerConfiguration Logger( - LoggerSinkConfiguration loggerSinkConfiguration, - Action configureLogger, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - LoggingLevelSwitch levelSwitch = null) - => loggerSinkConfiguration.Logger(configureLogger, restrictedToMinimumLevel, levelSwitch); } } diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 71a03d1..d0273fc 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -881,5 +881,86 @@ public void WriteToSinkIsAppliedWithCustomSinkAndLevelSwitch() Assert.Single(DummyRollingFileSink.Emitted); } + + + [Fact] + public void AuditToSinkIsAppliedWithCustomSink() + { + var json = $@"{{ + ""Serilog"": {{ + ""Using"": [""TestDummies""], + ""AuditTo"": [ + {{ + ""Name"": ""Sink"", + ""Args"": {{ + ""sink"": ""{typeof(DummyRollingFileSink).AssemblyQualifiedName}"" + }} + }}] + }} + }}"; + + var log = ConfigFromJson(json) + .CreateLogger(); + + DummyRollingFileSink.Reset(); + log.Write(Some.InformationEvent()); + + Assert.Single(DummyRollingFileSink.Emitted); + } + + [Fact] + public void AuditToSinkIsAppliedWithCustomSinkAndMinimumLevel() + { + var json = $@"{{ + ""Serilog"": {{ + ""Using"": [""TestDummies""], + ""AuditTo"": [ + {{ + ""Name"": ""Sink"", + ""Args"": {{ + ""sink"": ""{typeof(DummyRollingFileSink).AssemblyQualifiedName}"", + ""restrictedToMinimumLevel"": ""Warning"" + }} + }}] + }} + }}"; + + var log = ConfigFromJson(json) + .CreateLogger(); + + DummyRollingFileSink.Reset(); + log.Write(Some.InformationEvent()); + log.Write(Some.WarningEvent()); + + Assert.Single(DummyRollingFileSink.Emitted); + } + + [Fact] + public void AuditToSinkIsAppliedWithCustomSinkAndLevelSwitch() + { + var json = $@"{{ + ""Serilog"": {{ + ""Using"": [""TestDummies""], + ""LevelSwitches"": {{""$switch1"": ""Warning"" }}, + ""AuditTo"": [ + {{ + ""Name"": ""Sink"", + ""Args"": {{ + ""sink"": ""{typeof(DummyRollingFileSink).AssemblyQualifiedName}"", + ""levelSwitch"": ""$switch1"" + }} + }}] + }} + }}"; + + var log = ConfigFromJson(json) + .CreateLogger(); + + DummyRollingFileSink.Reset(); + log.Write(Some.InformationEvent()); + log.Write(Some.WarningEvent()); + + Assert.Single(DummyRollingFileSink.Emitted); + } } } From 0b3b7399797b562c6c4085d0e0a56c256e826cf3 Mon Sep 17 00:00:00 2001 From: Thibaud Desodt Date: Sat, 6 Oct 2018 22:03:47 +0200 Subject: [PATCH 56/61] Add support for Enrich.With() Support for `Enrich.With(ILogEventEnricher enricher)` --- .../SurrogateConfigurationMethods.cs | 6 ++++ .../ConfigurationSettingsTests.cs | 29 +++++++++++++++++++ test/TestDummies/DummyThreadIdEnricher.cs | 4 ++- 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs index 580a3de..36452c8 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs @@ -60,6 +60,7 @@ public static IEnumerable Enrich get { yield return GetSurrogateConfigurationMethod((c, _, __) => FromLogContext(c)); + yield return GetSurrogateConfigurationMethod((c, e, __) => With(c, e)); } } @@ -132,5 +133,10 @@ static LoggerConfiguration AsScalar(LoggerDestructuringConfiguration loggerDestr static LoggerConfiguration FromLogContext(LoggerEnrichmentConfiguration loggerEnrichmentConfiguration) => loggerEnrichmentConfiguration.FromLogContext(); + static LoggerConfiguration With(LoggerEnrichmentConfiguration loggerEnrichmentConfiguration, ILogEventEnricher enricher) + { + return loggerEnrichmentConfiguration.With(enricher); + } + } } diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index d0273fc..38a1662 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -962,5 +962,34 @@ public void AuditToSinkIsAppliedWithCustomSinkAndLevelSwitch() Assert.Single(DummyRollingFileSink.Emitted); } + + + [Fact] + public void EnrichWithIsAppliedWithCustomEnricher() + { + LogEvent evt = null; + + var json = $@"{{ + ""Serilog"": {{ + ""Using"": [""TestDummies""], + ""Enrich"": [ + {{ + ""Name"": ""With"", + ""Args"": {{ + ""enricher"": ""{typeof(DummyThreadIdEnricher).AssemblyQualifiedName}"" + }} + }}] + }} + }}"; + + var log = ConfigFromJson(json) + .WriteTo.Sink(new DelegatingSink(e => evt = e)) + .CreateLogger(); + + log.Write(Some.InformationEvent()); + + Assert.NotNull(evt); + Assert.True(evt.Properties.ContainsKey("ThreadId"), "Event should have enriched property ThreadId"); + } } } diff --git a/test/TestDummies/DummyThreadIdEnricher.cs b/test/TestDummies/DummyThreadIdEnricher.cs index 4ba740e..a640d55 100644 --- a/test/TestDummies/DummyThreadIdEnricher.cs +++ b/test/TestDummies/DummyThreadIdEnricher.cs @@ -6,7 +6,9 @@ namespace TestDummies public class DummyThreadIdEnricher : ILogEventEnricher { public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) - { + { + logEvent.AddPropertyIfAbsent(propertyFactory + .CreateProperty("ThreadId", "SomeId")); } } } From 70e4f0b1c1f88128de3f7f2875f590fddfef0f5e Mon Sep 17 00:00:00 2001 From: Thibaud Desodt Date: Sat, 6 Oct 2018 22:12:12 +0200 Subject: [PATCH 57/61] Add test for support Filter.With() Turns out support for `Filter.With(ILogEventFilter filter)` was already there since commit 4424523d1e0a9c1f41ad409ddd9d236d3daf9eca --- .../ConfigurationSettingsTests.cs | 28 +++++++++++++++++++ test/TestDummies/DummyAnonymousUserFilter.cs | 25 +++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 test/TestDummies/DummyAnonymousUserFilter.cs diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 38a1662..ac59e8e 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -991,5 +991,33 @@ public void EnrichWithIsAppliedWithCustomEnricher() Assert.NotNull(evt); Assert.True(evt.Properties.ContainsKey("ThreadId"), "Event should have enriched property ThreadId"); } + + [Fact] + public void FilterWithIsAppliedWithCustomFilter() + { + LogEvent evt = null; + + var json = $@"{{ + ""Serilog"": {{ + ""Using"": [""TestDummies""], + ""Filter"": [ + {{ + ""Name"": ""With"", + ""Args"": {{ + ""filter"": ""{typeof(DummyAnonymousUserFilter).AssemblyQualifiedName}"" + }} + }}] + }} + }}"; + + var log = ConfigFromJson(json) + .WriteTo.Sink(new DelegatingSink(e => evt = e)) + .CreateLogger(); + + log.ForContext("User", "anonymous").Write(Some.InformationEvent()); + Assert.Null(evt); + log.ForContext("User", "the user").Write(Some.InformationEvent()); + Assert.NotNull(evt); + } } } diff --git a/test/TestDummies/DummyAnonymousUserFilter.cs b/test/TestDummies/DummyAnonymousUserFilter.cs new file mode 100644 index 0000000..a47dd11 --- /dev/null +++ b/test/TestDummies/DummyAnonymousUserFilter.cs @@ -0,0 +1,25 @@ + +using Serilog.Core; +using Serilog.Events; + +namespace TestDummies +{ + public class DummyAnonymousUserFilter : ILogEventFilter + { + public bool IsEnabled(LogEvent logEvent) + { + if (logEvent.Properties.ContainsKey("User")) + { + if (logEvent.Properties["User"] is ScalarValue sv) + { + if (sv.Value is string s && s == "anonymous") + { + return false; + } + } + } + + return true; + } + } +} From a55fc9f59e20677641baf3dd78508bf754c3eeed Mon Sep 17 00:00:00 2001 From: Thibaud Desodt Date: Sat, 6 Oct 2018 22:21:14 +0200 Subject: [PATCH 58/61] Refactor `SurrogateConfigurationMethods` so that all methods in the class are dynamically discovered and exposed to Serilog settings as if they were regular extension methods --- .../SurrogateConfigurationMethods.cs | 64 +++++-------------- 1 file changed, 16 insertions(+), 48 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs index 36452c8..d6b3d6f 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using System.Linq.Expressions; +using System.Linq; using System.Reflection; using Serilog.Configuration; using Serilog.Core; @@ -10,7 +10,7 @@ namespace Serilog.Settings.Configuration { /// /// Contains "fake extension" methods for the Serilog configuration API. - /// By default the settings knows how to find extension methods, but some configuration + /// By default the settings know how to find extension methods, but some configuration /// are actually "regular" method calls and would not be found otherwise. /// /// This static class contains internal methods that can be used instead. @@ -18,54 +18,17 @@ namespace Serilog.Settings.Configuration /// static class SurrogateConfigurationMethods { - public static IEnumerable WriteTo - { - get - { - yield return GetSurrogateConfigurationMethod, LoggingLevelSwitch>((c, a, s) => Logger(c, a, LevelAlias.Minimum, s)); - yield return GetSurrogateConfigurationMethod((c, sink, s) => Sink(c, sink, LevelAlias.Minimum, s)); - } - } - - public static IEnumerable AuditTo - { - get - { - yield return GetSurrogateConfigurationMethod((c, sink, s) => Sink(c, sink, LevelAlias.Minimum, s)); - } - } - - public static IEnumerable Filter - { - get - { - yield return GetSurrogateConfigurationMethod((c, f, _) => With(c, f)); - } - } + static readonly Dictionary SurrogateMethodCandidates = typeof(SurrogateConfigurationMethods) + .GetTypeInfo().DeclaredMethods + .GroupBy(m => m.GetParameters().First().ParameterType) + .ToDictionary(g => g.Key, g => g.ToArray()); - public static IEnumerable Destructure - { - get - { - yield return GetSurrogateConfigurationMethod((c, d, _) => With(c, d)); - yield return GetSurrogateConfigurationMethod((c, m, _) => ToMaximumDepth(c, m)); - yield return GetSurrogateConfigurationMethod((c, m, _) => ToMaximumStringLength(c, m)); - yield return GetSurrogateConfigurationMethod((c, m, _) => ToMaximumCollectionCount(c, m)); - yield return GetSurrogateConfigurationMethod((c, t, _) => AsScalar(c, t)); - } - } - public static IEnumerable Enrich - { - get - { - yield return GetSurrogateConfigurationMethod((c, _, __) => FromLogContext(c)); - yield return GetSurrogateConfigurationMethod((c, e, __) => With(c, e)); - } - } - - static MethodInfo GetSurrogateConfigurationMethod(Expression> method) - => (method.Body as MethodCallExpression)?.Method; + internal static readonly MethodInfo[] WriteTo = SurrogateMethodCandidates[typeof(LoggerSinkConfiguration)]; + internal static readonly MethodInfo[] AuditTo = SurrogateMethodCandidates[typeof(LoggerAuditSinkConfiguration)]; + internal static readonly MethodInfo[] Enrich = SurrogateMethodCandidates[typeof(LoggerEnrichmentConfiguration)]; + internal static readonly MethodInfo[] Destructure = SurrogateMethodCandidates[typeof(LoggerDestructuringConfiguration)]; + internal static readonly MethodInfo[] Filter = SurrogateMethodCandidates[typeof(LoggerFilterConfiguration)]; /* Pass-through calls to various Serilog config methods which are @@ -75,6 +38,10 @@ invocation expressions as surrogates so that SelectConfigurationMethod has a way to match and invoke these instance methods. */ + // ReSharper disable UnusedMember.Local + // those methods are discovered through reflection by `SurrogateMethodCandidates` + // ReSharper has no way to see that they are actually used ... + // .WriteTo... // ======== static LoggerConfiguration Sink( @@ -138,5 +105,6 @@ static LoggerConfiguration With(LoggerEnrichmentConfiguration loggerEnrichmentCo return loggerEnrichmentConfiguration.With(enricher); } + // ReSharper restore UnusedMember.Local } } From 01b1cfd3fe0a8ec5b21cf963f5b5ebb5e06d20d9 Mon Sep 17 00:00:00 2001 From: Thibaud Desodt Date: Sat, 6 Oct 2018 22:35:56 +0200 Subject: [PATCH 59/61] Minor reformatting and documentation --- .../SurrogateConfigurationMethods.cs | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs index d6b3d6f..6350eca 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs @@ -32,10 +32,9 @@ static class SurrogateConfigurationMethods /* Pass-through calls to various Serilog config methods which are - implemented as instance methods rather than extension methods. The - FindXXXConfigurationMethods calls (above) use these to add method - invocation expressions as surrogates so that SelectConfigurationMethod - has a way to match and invoke these instance methods. + implemented as instance methods rather than extension methods. + ConfigurationReader adds those to the already discovered extension methods + so they can be invoked as well. */ // ReSharper disable UnusedMember.Local @@ -49,9 +48,7 @@ static LoggerConfiguration Sink( ILogEventSink sink, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, LoggingLevelSwitch levelSwitch = null) - { - return loggerSinkConfiguration.Sink(sink, restrictedToMinimumLevel, levelSwitch); - } + => loggerSinkConfiguration.Sink(sink, restrictedToMinimumLevel, levelSwitch); static LoggerConfiguration Logger( LoggerSinkConfiguration loggerSinkConfiguration, @@ -67,19 +64,19 @@ static LoggerConfiguration Sink( ILogEventSink sink, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, LoggingLevelSwitch levelSwitch = null) - { - return auditSinkConfiguration.Sink(sink, restrictedToMinimumLevel, levelSwitch); - } + => auditSinkConfiguration.Sink(sink, restrictedToMinimumLevel, levelSwitch); // .Filter... // ======= // TODO: add overload for array argument (ILogEventEnricher[]) + // expose `With(params ILogEventFilter[] filters)` as if it was `With(ILogEventFilter filter)` static LoggerConfiguration With(LoggerFilterConfiguration loggerFilterConfiguration, ILogEventFilter filter) => loggerFilterConfiguration.With(filter); // .Destructure... // ============ // TODO: add overload for array argument (IDestructuringPolicy[]) + // expose `With(params IDestructuringPolicy[] destructuringPolicies)` as if it was `With(IDestructuringPolicy policy)` static LoggerConfiguration With(LoggerDestructuringConfiguration loggerDestructuringConfiguration, IDestructuringPolicy policy) => loggerDestructuringConfiguration.With(policy); @@ -97,14 +94,15 @@ static LoggerConfiguration AsScalar(LoggerDestructuringConfiguration loggerDestr // .Enrich... // ======= + // expose `With(params ILogEventEnricher[] enrichers)` as if it was `With(ILogEventEnricher enricher)` + static LoggerConfiguration With( + LoggerEnrichmentConfiguration loggerEnrichmentConfiguration, + ILogEventEnricher enricher) + => loggerEnrichmentConfiguration.With(enricher); + static LoggerConfiguration FromLogContext(LoggerEnrichmentConfiguration loggerEnrichmentConfiguration) => loggerEnrichmentConfiguration.FromLogContext(); - static LoggerConfiguration With(LoggerEnrichmentConfiguration loggerEnrichmentConfiguration, ILogEventEnricher enricher) - { - return loggerEnrichmentConfiguration.With(enricher); - } - // ReSharper restore UnusedMember.Local } } From cf81e5aa9660c79049d68032a51f7717a3ee76b9 Mon Sep 17 00:00:00 2001 From: Thibaud Desodt Date: Sat, 6 Oct 2018 22:36:34 +0200 Subject: [PATCH 60/61] Mark as private the members that are not used elsewhere --- .../Settings/Configuration/ConfigurationReader.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index f72bb13..87359bb 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -362,7 +362,7 @@ internal static MethodInfo SelectConfigurationMethod(IEnumerable can .FirstOrDefault(); } - internal static IList FindSinkConfigurationMethods(IReadOnlyCollection configurationAssemblies) + static IList FindSinkConfigurationMethods(IReadOnlyCollection configurationAssemblies) { var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerSinkConfiguration)); if (configurationAssemblies.Contains(typeof(LoggerSinkConfiguration).GetTypeInfo().Assembly)) @@ -371,7 +371,7 @@ internal static IList FindSinkConfigurationMethods(IReadOnlyCollecti return found; } - internal static IList FindAuditSinkConfigurationMethods(IReadOnlyCollection configurationAssemblies) + static IList FindAuditSinkConfigurationMethods(IReadOnlyCollection configurationAssemblies) { var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerAuditSinkConfiguration)); if (configurationAssemblies.Contains(typeof(LoggerAuditSinkConfiguration).GetTypeInfo().Assembly)) @@ -379,7 +379,7 @@ internal static IList FindAuditSinkConfigurationMethods(IReadOnlyCol return found; } - internal static IList FindFilterConfigurationMethods(IReadOnlyCollection configurationAssemblies) + static IList FindFilterConfigurationMethods(IReadOnlyCollection configurationAssemblies) { var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerFilterConfiguration)); if (configurationAssemblies.Contains(typeof(LoggerFilterConfiguration).GetTypeInfo().Assembly)) @@ -388,7 +388,7 @@ internal static IList FindFilterConfigurationMethods(IReadOnlyCollec return found; } - internal static IList FindDestructureConfigurationMethods(IReadOnlyCollection configurationAssemblies) + static IList FindDestructureConfigurationMethods(IReadOnlyCollection configurationAssemblies) { var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerDestructuringConfiguration)); if (configurationAssemblies.Contains(typeof(LoggerDestructuringConfiguration).GetTypeInfo().Assembly)) @@ -397,7 +397,7 @@ internal static IList FindDestructureConfigurationMethods(IReadOnlyC return found; } - internal static IList FindEventEnricherConfigurationMethods(IReadOnlyCollection configurationAssemblies) + static IList FindEventEnricherConfigurationMethods(IReadOnlyCollection configurationAssemblies) { var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerEnrichmentConfiguration)); if (configurationAssemblies.Contains(typeof(LoggerEnrichmentConfiguration).GetTypeInfo().Assembly)) @@ -406,7 +406,7 @@ internal static IList FindEventEnricherConfigurationMethods(IReadOnl return found; } - internal static List FindConfigurationExtensionMethods(IReadOnlyCollection configurationAssemblies, Type configType) + static List FindConfigurationExtensionMethods(IReadOnlyCollection configurationAssemblies, Type configType) { return configurationAssemblies .SelectMany(a => a.ExportedTypes @@ -423,7 +423,7 @@ internal static bool IsValidSwitchName(string input) return Regex.IsMatch(input, LevelSwitchNameRegex); } - internal static LogEventLevel ParseLogEventLevel(string value) + static LogEventLevel ParseLogEventLevel(string value) { if (!Enum.TryParse(value, out LogEventLevel parsedLevel)) throw new InvalidOperationException($"The value {value} is not a valid Serilog level."); From 4c932e4368a11547385b550ce1016fb2883f019a Mon Sep 17 00:00:00 2001 From: Thibaud Desodt Date: Sat, 6 Oct 2018 22:44:29 +0200 Subject: [PATCH 61/61] Minor whitespace reformatting --- .../ConfigurationSettingsTests.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index ac59e8e..f8f7a95 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -801,7 +801,6 @@ public void DestructuringAsScalarIsAppliedWithAssemblyQualifiedName() Assert.IsType(prop); } - [Fact] public void WriteToSinkIsAppliedWithCustomSink() { @@ -882,7 +881,6 @@ public void WriteToSinkIsAppliedWithCustomSinkAndLevelSwitch() Assert.Single(DummyRollingFileSink.Emitted); } - [Fact] public void AuditToSinkIsAppliedWithCustomSink() { @@ -963,7 +961,6 @@ public void AuditToSinkIsAppliedWithCustomSinkAndLevelSwitch() Assert.Single(DummyRollingFileSink.Emitted); } - [Fact] public void EnrichWithIsAppliedWithCustomEnricher() {