From 2598d226bb6789d0d667a2b3f833bfbea916f6ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1nhegyi=20=C3=81lmos?= Date: Fri, 28 Aug 2020 18:49:51 +0200 Subject: [PATCH 01/64] Minimumlevel partial fix with IConfigurationRoot.Providers A partial fix for minimumlevel not being correctly overriden by higher priority providers. --- .../Configuration/ConfigurationReader.cs | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 4d44c324..85a9f2f5 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -22,12 +22,19 @@ class ConfigurationReader : IConfigurationReader readonly IConfigurationSection _section; readonly IReadOnlyCollection _configurationAssemblies; readonly ResolutionContext _resolutionContext; +#if NETSTANDARD || NET461 + readonly IConfigurationRoot _configurationRoot; +#endif public ConfigurationReader(IConfigurationSection configSection, AssemblyFinder assemblyFinder, IConfiguration configuration = null) { _section = configSection ?? throw new ArgumentNullException(nameof(configSection)); _configurationAssemblies = LoadConfigurationAssemblies(_section, assemblyFinder); _resolutionContext = new ResolutionContext(configuration); +#if NETSTANDARD || NET461 + _configurationRoot = configuration as IConfigurationRoot; +#endif + } // Used internally for processing nested configuration sections -- see GetMethodCalls below. @@ -85,7 +92,7 @@ void ApplyMinimumLevel(LoggerConfiguration loggerConfiguration) { var minimumLevelDirective = _section.GetSection("MinimumLevel"); - var defaultMinLevelDirective = minimumLevelDirective.Value != null ? minimumLevelDirective : minimumLevelDirective.GetSection("Default"); + IConfigurationSection defaultMinLevelDirective = GetDefaultMinLevelDirective(); if (defaultMinLevelDirective.Value != null) { ApplyMinimumLevel(defaultMinLevelDirective, (configuration, levelSwitch) => configuration.ControlledBy(levelSwitch)); @@ -124,6 +131,35 @@ void ApplyMinimumLevel(IConfigurationSection directive, Action Date: Sat, 29 Aug 2020 11:49:31 +0200 Subject: [PATCH 02/64] Added unit tests. --- .../Configuration/ConfigurationReader.cs | 5 +- .../ConfigurationReaderTests.cs | 109 ++++++++++++++++++ .../Support/ConfigurationReaderTestHelpers.cs | 65 +++++++++++ .../Support/Extensions.cs | 2 + 4 files changed, 179 insertions(+), 2 deletions(-) create mode 100644 test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 85a9f2f5..1ed25215 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -34,7 +34,6 @@ public ConfigurationReader(IConfigurationSection configSection, AssemblyFinder a #if NETSTANDARD || NET461 _configurationRoot = configuration as IConfigurationRoot; #endif - } // Used internally for processing nested configuration sections -- see GetMethodCalls below. @@ -43,6 +42,9 @@ internal ConfigurationReader(IConfigurationSection configSection, IReadOnlyColle _section = configSection ?? throw new ArgumentNullException(nameof(configSection)); _configurationAssemblies = configurationAssemblies ?? throw new ArgumentNullException(nameof(configurationAssemblies)); _resolutionContext = resolutionContext ?? throw new ArgumentNullException(nameof(resolutionContext)); + #if NETSTANDARD || NET461 + _configurationRoot = resolutionContext.HasAppConfiguration ? resolutionContext.AppConfiguration as IConfigurationRoot : null; + #endif } public void Configure(LoggerConfiguration loggerConfiguration) @@ -158,7 +160,6 @@ IConfigurationSection GetDefaultMinLevelDirective() #endif //NET451 or fallback return minimumLevelDirective.Value != null ? minimumLevelDirective : minimumLevelDirective.GetSection("Default"); - } } diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs index b24007b0..fd3417a0 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs @@ -1,9 +1,13 @@ using Xunit; +using System.Collections.Generic; using System.Reflection; using System.Linq; +using Microsoft.Extensions.Configuration; +using Serilog.Events; using Serilog.Formatting; using Serilog.Settings.Configuration.Assemblies; using Serilog.Settings.Configuration.Tests.Support; +using static Serilog.Settings.Configuration.Tests.Support.ConfigurationReaderTestHelpers; namespace Serilog.Settings.Configuration.Tests { @@ -172,5 +176,110 @@ public void MethodsAreSelectedBasedOnCountOfMatchedArgumentsAndThenStringType() var selected = ConfigurationReader.SelectConfigurationMethod(options, "DummyRollingFile", suppliedArgumentNames); Assert.Equal(typeof(string), selected.GetParameters()[2].ParameterType); } + + public static IEnumerable FlatMinimumLevel => new List + { + new object[] { GetConfigRoot(appsettingsJsonLevel: minimumLevelFlatTemplate.Format(LogEventLevel.Error)), LogEventLevel.Error }, + new object[] { GetConfigRoot(appsettingsDevelopmentJsonLevel: minimumLevelFlatTemplate.Format(LogEventLevel.Error)), LogEventLevel.Error }, + new object[] { GetConfigRoot(envVariables: new Dictionary() {{minimumLevelFlatKey, LogEventLevel.Error.ToString()}}), LogEventLevel.Error}, + new object[] { GetConfigRoot( + appsettingsJsonLevel: minimumLevelFlatTemplate.Format(LogEventLevel.Debug), + envVariables: new Dictionary() {{minimumLevelFlatKey, LogEventLevel.Error.ToString()}}), + LogEventLevel.Error + } + }; + + [Theory] + [MemberData(nameof(FlatMinimumLevel))] + public void FlatMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, LogEventLevel expectedMinimumLevel) + { + var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), root); + var loggerConfig = new LoggerConfiguration(); + + reader.Configure(loggerConfig); + + AssertLogEventLevels(loggerConfig, expectedMinimumLevel); + } + + public static IEnumerable ObjectMinimumLevel => new List + { + new object[] { GetConfigRoot(appsettingsJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error)), LogEventLevel.Error }, + new object[] { GetConfigRoot(appsettingsDevelopmentJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error)), LogEventLevel.Error }, + new object[] { GetConfigRoot(envVariables: new Dictionary(){{minimumLevelObjectKey, LogEventLevel.Error.ToString() } }), LogEventLevel.Error }, + new object[] { GetConfigRoot( + appsettingsJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error), + appsettingsDevelopmentJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Debug)), + LogEventLevel.Debug } + }; + + [Theory] + [MemberData(nameof(ObjectMinimumLevel))] + public void ObjectMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, LogEventLevel expectedMinimumLevel) + { + var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), root); + var loggerConfig = new LoggerConfiguration(); + + reader.Configure(loggerConfig); + + AssertLogEventLevels(loggerConfig, expectedMinimumLevel); + } + + #if !(NET452) + + // currently only works in the .NET 4.6.1 and .NET Standard builds of Serilog.Settings.Configuration + public static IEnumerable MixedMinimumLevel => new List + { + new object[] + { + GetConfigRoot( + appsettingsJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error), + appsettingsDevelopmentJsonLevel: minimumLevelFlatTemplate.Format(LogEventLevel.Debug)), + LogEventLevel.Debug + }, + new object[] + { + GetConfigRoot( + appsettingsJsonLevel: minimumLevelFlatTemplate.Format(LogEventLevel.Error), + appsettingsDevelopmentJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Debug)), + LogEventLevel.Debug + }, + // precedence should be flat > object if from the same source + new object[] + { + GetConfigRoot( + envVariables: new Dictionary() + { + {minimumLevelObjectKey, LogEventLevel.Error.ToString()}, + {minimumLevelFlatKey, LogEventLevel.Debug.ToString()} + }), + LogEventLevel.Debug + } + }; + + [Theory] + [MemberData(nameof(MixedMinimumLevel))] + public void MixedMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, LogEventLevel expectedMinimumLevel) + { + var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), root); + var loggerConfig = new LoggerConfiguration(); + + reader.Configure(loggerConfig); + + AssertLogEventLevels(loggerConfig, expectedMinimumLevel); + } + + #endif + + [Fact] + public void NoConfigurationRootUsedStillValid() + { + var section = JsonStringConfigSource.LoadSection(@"{ 'Nest': { 'Serilog': { 'MinimumLevel': 'Error' } } }", "Nest"); + var reader = new ConfigurationReader(section.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), section); + var loggerConfig = new LoggerConfiguration(); + + reader.Configure(loggerConfig); + + AssertLogEventLevels(loggerConfig, LogEventLevel.Error); + } } } diff --git a/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs b/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs new file mode 100644 index 00000000..d1fa6182 --- /dev/null +++ b/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Configuration; +using Serilog.Events; +using Xunit; + +namespace Serilog.Settings.Configuration.Tests.Support +{ + static class ConfigurationReaderTestHelpers + { + public const string minimumLevelFlatTemplate = @" +{{ + 'Serilog': {{ + 'MinimumLevel': '{0}' + }} +}}"; + public const string minimumLevelObjectTemplate = @" +{{ + 'Serilog': {{ + 'MinimumLevel': {{ + 'Default': '{0}' + }} + }} +}}"; + public const string minimumLevelFlatKey = "Serilog:MinimumLevel"; + public const string minimumLevelObjectKey = "Serilog:MinimumLevel:Default"; + + public static void AssertLogEventLevels(LoggerConfiguration loggerConfig, LogEventLevel expectedMinimumLevel) + { + var logger = loggerConfig.CreateLogger(); + + var logEventValues = Enum.GetValues(typeof(LogEventLevel)).Cast(); + + foreach (var logEvent in logEventValues) + { + if (logEvent < expectedMinimumLevel) + { + Assert.False(logger.IsEnabled(logEvent), + $"The log level {logEvent} should be disabled as it's lower priority than the minimum level of {expectedMinimumLevel}."); + } + else + { + Assert.True(logger.IsEnabled(logEvent), + $"The log level {logEvent} should be enabled as it's {(logEvent == expectedMinimumLevel ? "the same" : "higher")} priority {(logEvent == expectedMinimumLevel ? "as" : "than")} the minimum level of {expectedMinimumLevel}."); + } + } + } + + // the naming is only to show priority as providers + public static IConfigurationRoot GetConfigRoot( + string appsettingsJsonLevel = null, + string appsettingsDevelopmentJsonLevel = null, + Dictionary envVariables = null) + { + var configBuilder = new ConfigurationBuilder(); + + configBuilder.AddJsonString(appsettingsJsonLevel ?? "{}"); + configBuilder.AddJsonString(appsettingsDevelopmentJsonLevel ?? "{}"); + configBuilder.Add(new ReloadableConfigurationSource(envVariables ?? new Dictionary())); + + return configBuilder.Build(); + } + } +} diff --git a/test/Serilog.Settings.Configuration.Tests/Support/Extensions.cs b/test/Serilog.Settings.Configuration.Tests/Support/Extensions.cs index e02d8755..70be52b2 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/Extensions.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/Extensions.cs @@ -18,5 +18,7 @@ public static string ToValidJson(this string str) #endif return str; } + + public static string Format(this string template, params object[] paramObjects) => string.Format(template, paramObjects); } } From a14b22d4a7b9e850d3fd3c2be451c984574078cc Mon Sep 17 00:00:00 2001 From: almostchristian Date: Thu, 24 Feb 2022 22:06:14 +0800 Subject: [PATCH 03/64] Add support for delegate values #259 --- .../Configuration/StringArgumentValue.cs | 37 +++++++++++++++++++ .../StringArgumentValueTests.cs | 16 ++++++++ .../Support/StaticAccessorClasses.cs | 12 +++++- 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs index 69dceb3f..7ab929c5 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs @@ -71,6 +71,43 @@ public object ConvertTo(Type toType, ResolutionContext resolutionContext) TryParseStaticMemberAccessor(argumentValue, out var accessorTypeName, out var memberName)) { var accessorType = Type.GetType(accessorTypeName, throwOnError: true); + + // if delegate, look for a method and then construct a delegate + if (typeof(Delegate).IsAssignableFrom(toType) || typeof(MethodInfo) == toType) + { + var methodCandidates = accessorType.GetTypeInfo().DeclaredMethods + .Where(x => x.Name == memberName) + .Where(x => x.IsPublic) + .Where(x => !x.IsGenericMethod) + .Where(x => x.IsStatic) + .ToList(); + + if (methodCandidates.Count > 1 && typeof(Delegate).IsAssignableFrom(toType)) + { + // filter possible method overloads + + var delegateSig = toType.GetMethod("Invoke"); + var delegateParameters = delegateSig!.GetParameters().Select(x => x.ParameterType); + methodCandidates = methodCandidates + .Where(x => x.ReturnType == delegateSig.ReturnType && x.GetParameters().Select(y => y.ParameterType).SequenceEqual(delegateParameters)) + .ToList(); + } + + var methodCandidate = methodCandidates.SingleOrDefault(); + + if (methodCandidate != null) + { + if (typeof(MethodInfo) == toType) + { + return methodCandidate; + } + else + { + return methodCandidate.CreateDelegate(toType); + } + } + } + // is there a public static property with that name ? var publicStaticPropertyInfo = accessorType.GetTypeInfo().DeclaredProperties .Where(x => x.Name == memberName) diff --git a/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs b/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs index 8b455d55..d549901e 100644 --- a/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs @@ -99,6 +99,22 @@ public void StaticMembersAccessorsCanBeUsedForAbstractTypes(string input, Type t Assert.Equal(ConcreteImpl.Instance, actual); } + [Theory] + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::FuncIntParseField, Serilog.Settings.Configuration.Tests", typeof(Func))] + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::NamedIntParseField, Serilog.Settings.Configuration.Tests", typeof(NamedIntParse))] + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::FuncIntParseProperty, Serilog.Settings.Configuration.Tests", typeof(Func))] + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::NamedIntParseProperty, Serilog.Settings.Configuration.Tests", typeof(NamedIntParse))] + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::IntParseMethod, Serilog.Settings.Configuration.Tests", typeof(NamedIntParse))] + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::IntParseMethod, Serilog.Settings.Configuration.Tests", typeof(Func))] + public void StaticMembersAccessorsCanBeUsedForDelegateTypes(string input, Type targetType) + { + var stringArgumentValue = new StringArgumentValue(input); + + var actual = stringArgumentValue.ConvertTo(targetType, new ResolutionContext()); + + Assert.IsAssignableFrom(targetType, actual); + } + [Theory] [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::ConcreteClassProperty, Serilog.Settings.Configuration.Tests", typeof(AConcreteClass))] [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::ConcreteClassField, Serilog.Settings.Configuration.Tests", typeof(AConcreteClass))] diff --git a/test/Serilog.Settings.Configuration.Tests/Support/StaticAccessorClasses.cs b/test/Serilog.Settings.Configuration.Tests/Support/StaticAccessorClasses.cs index 03b83574..cbd5700b 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/StaticAccessorClasses.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/StaticAccessorClasses.cs @@ -1,5 +1,9 @@ -namespace Serilog.Settings.Configuration.Tests.Support +using System; + +namespace Serilog.Settings.Configuration.Tests.Support { + public delegate int NamedIntParse(string value); + public interface IAmAnInterface { } @@ -46,5 +50,11 @@ public class ClassWithStaticAccessors #pragma warning restore 169 public IAmAnInterface InstanceInterfaceProperty => ConcreteImpl.Instance; public IAmAnInterface InstanceInterfaceField = ConcreteImpl.Instance; + + public static Func FuncIntParseField = int.Parse; + public static NamedIntParse NamedIntParseField = int.Parse; + public static Func FuncIntParseProperty => int.Parse; + public static NamedIntParse NamedIntParseProperty => int.Parse; + public static int IntParseMethod(string value) => int.Parse(value); } } From 40cdc109eab192778d5a58979d5c0b398af0201d Mon Sep 17 00:00:00 2001 From: almostchristian Date: Thu, 24 Feb 2022 22:17:39 +0800 Subject: [PATCH 04/64] Improved test to demonstrate overload resolution --- .../Support/StaticAccessorClasses.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Serilog.Settings.Configuration.Tests/Support/StaticAccessorClasses.cs b/test/Serilog.Settings.Configuration.Tests/Support/StaticAccessorClasses.cs index cbd5700b..f9c9e77f 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/StaticAccessorClasses.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/StaticAccessorClasses.cs @@ -56,5 +56,7 @@ public class ClassWithStaticAccessors public static Func FuncIntParseProperty => int.Parse; public static NamedIntParse NamedIntParseProperty => int.Parse; public static int IntParseMethod(string value) => int.Parse(value); + public static int IntParseMethod(string value, string otherValue) => int.Parse(value); // will not be chosen, extra parameter + public static int IntParseMethod(object value) => throw new NotImplementedException(); // will not be chosen, wrong parameter type } } From 1cb5b7fd9d14b963cfef5893dc07520ba870a014 Mon Sep 17 00:00:00 2001 From: almostchristian Date: Fri, 25 Feb 2022 09:04:11 +0800 Subject: [PATCH 05/64] Removed redundant condition Co-authored-by: Ivan Maximov --- .../Settings/Configuration/StringArgumentValue.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs index 7ab929c5..6d946393 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs @@ -82,7 +82,7 @@ public object ConvertTo(Type toType, ResolutionContext resolutionContext) .Where(x => x.IsStatic) .ToList(); - if (methodCandidates.Count > 1 && typeof(Delegate).IsAssignableFrom(toType)) + if (methodCandidates.Count > 1) { // filter possible method overloads From 1b8b31ad2bf03c863e3f5bf014d2afcc02d6887f Mon Sep 17 00:00:00 2001 From: almostchristian Date: Fri, 25 Feb 2022 10:06:48 +0800 Subject: [PATCH 06/64] Improve test to execute method --- .../StringArgumentValueTests.cs | 2 ++ .../Support/StaticAccessorClasses.cs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs b/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs index d549901e..642e85e7 100644 --- a/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs @@ -113,6 +113,8 @@ public void StaticMembersAccessorsCanBeUsedForDelegateTypes(string input, Type t var actual = stringArgumentValue.ConvertTo(targetType, new ResolutionContext()); Assert.IsAssignableFrom(targetType, actual); + var parser = (Delegate)actual; + Assert.Equal(100, parser.DynamicInvoke("100")); } [Theory] diff --git a/test/Serilog.Settings.Configuration.Tests/Support/StaticAccessorClasses.cs b/test/Serilog.Settings.Configuration.Tests/Support/StaticAccessorClasses.cs index f9c9e77f..7db6f99e 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/StaticAccessorClasses.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/StaticAccessorClasses.cs @@ -56,7 +56,7 @@ public class ClassWithStaticAccessors public static Func FuncIntParseProperty => int.Parse; public static NamedIntParse NamedIntParseProperty => int.Parse; public static int IntParseMethod(string value) => int.Parse(value); - public static int IntParseMethod(string value, string otherValue) => int.Parse(value); // will not be chosen, extra parameter + public static int IntParseMethod(string value, string otherValue) => throw new NotImplementedException(); // will not be chosen, extra parameter public static int IntParseMethod(object value) => throw new NotImplementedException(); // will not be chosen, wrong parameter type } } From 691aefa314c575393fa09923f7f88dbdee98f7cb Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Sat, 3 Sep 2022 08:34:33 +1000 Subject: [PATCH 07/64] Dev version bump [skip ci] --- .../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 76b2299c..83774772 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. - 3.4.0 + 3.4.1 latest Serilog Contributors netstandard2.0;net451;net461 From 85e720f5657bfa68a08e1ad6326396b9cbc047e3 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 14 Sep 2022 09:04:43 +1000 Subject: [PATCH 08/64] Fix build matrix conditional deployment --- appveyor.yml | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index fc73d317..2ceba9e8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -15,23 +15,20 @@ artifacts: - path: artifacts/Serilog.*.nupkg - path: artifacts/Serilog.*.snupkg -for: -- - matrix: - only: - - image: Visual Studio - deploy: - - provider: NuGet - api_key: - secure: 60gpLnipFCiKLpS7ECI1C6EPJW27KzVwqrBVkEzX6FIMTmsG//HD3p8Oq7WdQPm8 - on: - branch: /^(main|dev)$/ - - provider: GitHub - auth_token: - secure: p4LpVhBKxGS5WqucHxFQ5c7C8cP74kbNB0Z8k9Oxx/PMaDQ1+ibmoexNqVU5ZlmX - artifacts: - /Serilog.*\.nupkg/ - /Serilog.*\.snupkg/ - tag: v$(appveyor_build_version) - on: - branch: main +deploy: +- provider: NuGet + api_key: + secure: 60gpLnipFCiKLpS7ECI1C6EPJW27KzVwqrBVkEzX6FIMTmsG//HD3p8Oq7WdQPm8 + on: + branch: /^(main|dev)$/ + image: Visual Studio 2022 +- provider: GitHub + auth_token: + secure: p4LpVhBKxGS5WqucHxFQ5c7C8cP74kbNB0Z8k9Oxx/PMaDQ1+ibmoexNqVU5ZlmX + artifacts: + /Serilog.*\.nupkg/ + /Serilog.*\.snupkg/ + tag: v$(appveyor_build_version) + on: + branch: main + image: Visual Studio 2022 From 07f0bc432376de191bccc2a88235b5de69cf42b5 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 14 Sep 2022 09:17:59 +1000 Subject: [PATCH 09/64] Backport conditional deployment fix from main [skip ci] --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 2ceba9e8..272c7d79 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,7 +21,7 @@ deploy: secure: 60gpLnipFCiKLpS7ECI1C6EPJW27KzVwqrBVkEzX6FIMTmsG//HD3p8Oq7WdQPm8 on: branch: /^(main|dev)$/ - image: Visual Studio 2022 + OS: Windows_NT - provider: GitHub auth_token: secure: p4LpVhBKxGS5WqucHxFQ5c7C8cP74kbNB0Z8k9Oxx/PMaDQ1+ibmoexNqVU5ZlmX @@ -31,4 +31,4 @@ deploy: tag: v$(appveyor_build_version) on: branch: main - image: Visual Studio 2022 + OS: Windows_NT From 13be21cfb110cbf5c0dd5d050a257be5eb3f5196 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 14 Sep 2022 09:19:27 +1000 Subject: [PATCH 10/64] Dev version bump, bringing in new features [skip ci] --- .../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 83774772..c12d61db 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. - 3.4.1 + 3.5.0 latest Serilog Contributors netstandard2.0;net451;net461 From 248749de29061bf3bf70c2265c1b1d4a5ba76da4 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 14 Sep 2022 09:53:46 +1000 Subject: [PATCH 11/64] Mark partial and invalid JSON code snippets as YAML to avoid GitHub markdown syntax highlighter errors --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index cc4c7043..6085e4d9 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ For a more sophisticated example go to the [sample](sample/Sample) folder. Root section name can be changed: -```json +```yaml { "CustomSection": { ... @@ -74,7 +74,7 @@ var logger = new LoggerConfiguration() `Using` section contains list of **assemblies** in which configuration methods (`WriteTo.File()`, `Enrich.WithThreadId()`) reside. -```json +```yaml "Serilog": { "Using": [ "Serilog.Sinks.Console", "Serilog.Enrichers.Thread", /* ... */ ], // ... @@ -83,7 +83,7 @@ var logger = new LoggerConfiguration() For .NET Core projects build tools produce `.deps.json` files and this package implements a convention using `Microsoft.Extensions.DependencyModel` to find any package among dependencies with `Serilog` anywhere in the name and pulls configuration methods from it, so the `Using` section in example above can be omitted: -```json +```yaml { "Serilog": { "MinimumLevel": "Debug", @@ -195,7 +195,7 @@ Or alternatively, the long-form (`"Name":` ...) syntax from the example above ca By `Microsoft.Extensions.Configuration.Json` convention, array syntax implicitly defines index for each element in order to make unique paths for configuration keys. So the example above is equivalent to: -```json +```yaml "WriteTo": { "0": "Console", "1": "DiagnosticTrace" @@ -204,7 +204,7 @@ By `Microsoft.Extensions.Configuration.Json` convention, array syntax implicitly And -```json +```yaml "WriteTo:0": "Console", "WriteTo:1": "DiagnosticTrace" ``` @@ -213,7 +213,7 @@ And When overriding settings with [environment variables](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#environment-variables) it becomes less convenient and fragile, so you can specify custom names: -```json +```yaml "WriteTo": { "ConsoleSink": "Console", "DiagnosticTraceSink": { "Name": "DiagnosticTrace" } @@ -228,7 +228,7 @@ This section defines a static list of key-value pairs that will enrich log event This section defines filters that will be applied to log events. It is especially usefull in combination with _[Serilog.Expressions](https://github.com/serilog/serilog-expressions)_ (or legacy _[Serilog.Filters.Expressions](https://github.com/serilog/serilog-filters-expressions)_) package so you can write expression in text form: -```json +```yaml "Filter": [{ "Name": "ByIncludingOnly", "Args": { @@ -239,7 +239,7 @@ This section defines filters that will be applied to log events. It is especiall Using this package you can also declare `LoggingFilterSwitch`-es in custom section and reference them for filter parameters: -```json +```yaml { "Serilog": { "FilterSwitches": { "filterSwitch": "Application = 'Sample'" }, @@ -260,7 +260,7 @@ Level updates to switches are also respected for a dynamic update. Some Serilog packages require a reference to a logger configuration object. The sample program in this project illustrates this with the following entry configuring the _[Serilog.Sinks.Async](https://github.com/serilog/serilog-sinks-async)_ package to wrap the _[Serilog.Sinks.File](https://github.com/serilog/serilog-sinks-file)_ package. The `configure` parameter references the File sink configuration: -```json +```yaml "WriteTo:Async": { "Name": "Async", "Args": { @@ -286,7 +286,7 @@ When the configuration specifies a discrete value for a parameter (such as a str Static member access can be used for passing to the configuration argument via [special](https://github.com/serilog/serilog-settings-configuration/blob/dev/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs#L35) syntax: -```json +```yaml { "Args": { "encoding": "System.Text.Encoding::UTF8", @@ -299,7 +299,7 @@ Static member access can be used for passing to the configuration argument via [ If the parameter value is not a discrete value, it will try to find a best matching public constructor for the argument: -```json +```yaml { "Name": "Console", "Args": { @@ -317,7 +317,7 @@ For other cases the package will use the configuration binding system provided b If parameter type is an interface or an abstract class you need to specify the full type name that implements abstract type. The implementation type should have parameterless constructor. -```json +```yaml "Destructure": [ { "Name": "With", "Args": { "policy": "Sample.CustomPolicy, Sample" } }, ... From 8391832b80cc81d2a5c420fb8c085f31d33d7e4b Mon Sep 17 00:00:00 2001 From: Sergey Komisarchik Date: Wed, 12 Oct 2022 18:56:32 +0300 Subject: [PATCH 12/64] extensions method discovery workaround for #330 --- .../Configuration/ConfigurationReader.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index c03c2be8..a3b495dc 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using Microsoft.Extensions.Configuration; @@ -548,15 +549,24 @@ static IReadOnlyCollection FindEventEnricherConfigurationMethods(IRe static List FindConfigurationExtensionMethods(IReadOnlyCollection configurationAssemblies, Type configType) { // ExtensionAttribute can be polyfilled to support extension methods - bool HasExtensionAttribute(MethodInfo m) => - m.CustomAttributes.Any(a => a.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute"); + static bool HasCustomExtensionAttribute(MethodInfo m) + { + try + { + return m.CustomAttributes.Any(a => a.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute"); + } + catch (CustomAttributeFormatException) + { + return false; + } + } return configurationAssemblies .SelectMany(a => a.ExportedTypes .Select(t => t.GetTypeInfo()) .Where(t => t.IsSealed && t.IsAbstract && !t.IsNested)) .SelectMany(t => t.DeclaredMethods) - .Where(m => m.IsStatic && m.IsPublic && HasExtensionAttribute(m)) + .Where(m => m.IsStatic && m.IsPublic && (m.IsDefined(typeof(ExtensionAttribute), false) || HasCustomExtensionAttribute(m))) .Where(m => m.GetParameters()[0].ParameterType == configType) .ToList(); } From 27870cf5a11ba110bca451797bcf57fecef6f2ca Mon Sep 17 00:00:00 2001 From: Sergey Komisarchik Date: Sat, 15 Oct 2022 11:20:18 +0300 Subject: [PATCH 13/64] case insensitive log level support --- .../Settings/Configuration/ConfigurationReader.cs | 3 +-- .../ConfigurationReaderTests.cs | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index a3b495dc..e9c75b25 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -578,10 +578,9 @@ internal static bool IsValidSwitchName(string input) static LogEventLevel ParseLogEventLevel(string value) { - if (!Enum.TryParse(value, out LogEventLevel parsedLevel)) + if (!Enum.TryParse(value, ignoreCase: true, out LogEventLevel parsedLevel)) throw new InvalidOperationException($"The value {value} is not a valid Serilog level."); return parsedLevel; } - } } diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs index b4ccbdde..8639fc0f 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs @@ -204,6 +204,7 @@ public void FlatMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, public static IEnumerable ObjectMinimumLevel => new List { new object[] { GetConfigRoot(appsettingsJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error)), LogEventLevel.Error }, + new object[] { GetConfigRoot(appsettingsJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error.ToString().ToUpper())), LogEventLevel.Error }, new object[] { GetConfigRoot(appsettingsDevelopmentJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error)), LogEventLevel.Error }, new object[] { GetConfigRoot(envVariables: new Dictionary(){{minimumLevelObjectKey, LogEventLevel.Error.ToString() } }), LogEventLevel.Error }, new object[] { GetConfigRoot( From 34d64750f6b079978959d67b2b539d639c982233 Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Mon, 19 Dec 2022 19:25:58 +0300 Subject: [PATCH 14/64] Implicit usings and file-scoped namespaces --- .editorconfig | 2 + Directory.Build.props | 1 + sample/Sample/Program.cs | 136 +- ...log-settings-configuration.sln.DotSettings | 16 +- ...figurationLoggerConfigurationExtensions.cs | 360 +++-- .../Assemblies/AssemblyFinder.cs | 67 +- .../Assemblies/ConfigurationAssemblySource.cs | 25 +- .../DependencyContextAssemblyFinder.cs | 50 +- .../Assemblies/DllScanningAssemblyFinder.cs | 87 +- .../Configuration/ConfigurationReader.cs | 870 ++++++------ .../IConfigurationArgumentValue.cs | 9 +- .../Configuration/IConfigurationReader.cs | 11 +- .../Configuration/LoggingFilterSwitchProxy.cs | 69 +- .../Configuration/ObjectArgumentValue.cs | 346 +++-- .../Configuration/ResolutionContext.cs | 119 +- .../Configuration/StringArgumentValue.cs | 292 ++--- .../SurrogateConfigurationMethods.cs | 230 ++-- .../ConfigurationReaderTests.cs | 374 +++--- .../ConfigurationSettingsTests.cs | 1165 ++++++++--------- .../DllScanningAssemblyFinderTests.cs | 93 +- .../DummyLoggerConfigurationExtensions.cs | 40 +- ...figurationWithMultipleMethodsExtensions.cs | 64 +- .../DynamicLevelChangeTests.cs | 133 +- .../LoggerConfigurationExtensionsTests.cs | 141 +- .../ObjectArgumentValueTests.cs | 103 +- ...erilog.Settings.Configuration.Tests.csproj | 4 + .../StringArgumentValueTests.cs | 370 +++--- .../Support/AbstractClass.cs | 9 +- .../Support/ConfigurationBuilderExtensions.cs | 13 +- .../Support/ConfigurationReaderTestHelpers.cs | 73 +- .../Support/CustomConsoleTheme.cs | 7 +- .../Support/DelegatingSink.cs | 46 +- .../Support/Extensions.cs | 25 +- .../Support/JsonStringConfigSource.cs | 84 +- .../Support/ReloadableConfigurationSource.cs | 46 +- .../Support/Some.cs | 184 ++- .../Support/StaticAccessorClasses.cs | 89 +- test/TestDummies/Console/DummyConsoleSink.cs | 34 +- .../Console/Themes/ConcreteConsoleTheme.cs | 7 +- .../Console/Themes/ConsoleTheme.cs | 9 +- .../Console/Themes/ConsoleThemes.cs | 9 +- .../Console/Themes/EmptyConsoleTheme.cs | 7 +- test/TestDummies/DummyAnonymousUserFilter.cs | 21 +- test/TestDummies/DummyConfigurationSink.cs | 59 +- ...DummyHardCodedStringDestructuringPolicy.cs | 28 +- .../DummyLoggerConfigurationExtensions.cs | 279 ++-- test/TestDummies/DummyPolicy.cs | 53 +- test/TestDummies/DummyRollingFileAuditSink.cs | 31 +- test/TestDummies/DummyRollingFileSink.cs | 31 +- test/TestDummies/DummyThreadIdEnricher.cs | 13 +- test/TestDummies/DummyWithLevelSwitchSink.cs | 35 +- test/TestDummies/DummyWrappingSink.cs | 43 +- test/TestDummies/TestDummies.csproj | 4 + 53 files changed, 3145 insertions(+), 3271 deletions(-) diff --git a/.editorconfig b/.editorconfig index 3687ef4e..55dcd648 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,3 +15,5 @@ end_of_line = lf [*.{cmd, bat}] end_of_line = crlf + +csharp_style_namespace_declarations = file_scoped:suggestion diff --git a/Directory.Build.props b/Directory.Build.props index c402103f..f6e3c1ba 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -3,6 +3,7 @@ latest True + enable diff --git a/sample/Sample/Program.cs b/sample/Sample/Program.cs index 4f4d7f9f..d2888aae 100644 --- a/sample/Sample/Program.cs +++ b/sample/Sample/Program.cs @@ -1,11 +1,4 @@ -using System; - -using System.IO; -using System.Linq; -using System.Collections.Generic; -using System.Threading; - -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration; using Serilog; using Serilog.Core; @@ -14,93 +7,92 @@ // ReSharper disable UnusedType.Global -namespace Sample +namespace Sample; + +public class Program { - public class Program + public static void Main(string[] args) { - public static void Main(string[] args) - { - SelfLog.Enable(Console.Error); + SelfLog.Enable(Console.Error); - Thread.CurrentThread.Name = "Main thread"; + Thread.CurrentThread.Name = "Main thread"; - var configuration = new ConfigurationBuilder() - .SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile(path: "appsettings.json", optional: false, reloadOnChange: true) - .Build(); + var configuration = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile(path: "appsettings.json", optional: false, reloadOnChange: true) + .Build(); - var logger = new LoggerConfiguration() - .ReadFrom.Configuration(configuration) - .CreateLogger(); + var logger = new LoggerConfiguration() + .ReadFrom.Configuration(configuration) + .CreateLogger(); - logger.Information("Args: {Args}", args); + logger.Information("Args: {Args}", args); - do - { - logger.ForContext().Information("Hello, world!"); - logger.ForContext().Error("Hello, world!"); - logger.ForContext(Constants.SourceContextPropertyName, "Microsoft").Warning("Hello, world!"); - logger.ForContext(Constants.SourceContextPropertyName, "Microsoft").Error("Hello, world!"); - logger.ForContext(Constants.SourceContextPropertyName, "MyApp.Something.Tricky").Verbose("Hello, world!"); + do + { + logger.ForContext().Information("Hello, world!"); + logger.ForContext().Error("Hello, world!"); + logger.ForContext(Constants.SourceContextPropertyName, "Microsoft").Warning("Hello, world!"); + logger.ForContext(Constants.SourceContextPropertyName, "Microsoft").Error("Hello, world!"); + logger.ForContext(Constants.SourceContextPropertyName, "MyApp.Something.Tricky").Verbose("Hello, world!"); - 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 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 string length:\n{@LongString}", + new { TwentyChars = "0123456789abcdefghij" }); - logger.Information("Destructure with max collection count:\n{@BigData}", - new { TenItems = new[] { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" } }); + logger.Information("Destructure with max collection count:\n{@BigData}", + 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" }); + 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')); + 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 - { - readonly LogEventLevel _levelFilter; - - public CustomFilter(LogEventLevel levelFilter = LogEventLevel.Information) - { - _levelFilter = levelFilter; - } +// The filter syntax in the sample configuration file is +// processed by the Serilog.Filters.Expressions package. +public class CustomFilter : ILogEventFilter +{ + readonly LogEventLevel _levelFilter; - public bool IsEnabled(LogEvent logEvent) - { - return logEvent.Level >= _levelFilter; - } + public CustomFilter(LogEventLevel levelFilter = LogEventLevel.Information) + { + _levelFilter = levelFilter; } - public class LoginData + public bool IsEnabled(LogEvent logEvent) { - public string Username; - // ReSharper disable once NotAccessedField.Global - public string Password; + return logEvent.Level >= _levelFilter; } +} + +public class LoginData +{ + public string Username; + // ReSharper disable once NotAccessedField.Global + public string Password; +} - public class CustomPolicy : IDestructuringPolicy +public class CustomPolicy : IDestructuringPolicy +{ + public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, out LogEventPropertyValue result) { - public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, out LogEventPropertyValue result) + result = null; + + if (value is LoginData loginData) { - result = null; - - if (value is LoginData loginData) - { - result = new StructureValue( - new List - { - new("Username", new ScalarValue(loginData.Username)) - }); - } - - return (result != null); + result = new StructureValue( + new List + { + new("Username", new ScalarValue(loginData.Username)) + }); } + + return (result != null); } } diff --git a/serilog-settings-configuration.sln.DotSettings b/serilog-settings-configuration.sln.DotSettings index 2fdb7b2f..6386ab02 100644 --- a/serilog-settings-configuration.sln.DotSettings +++ b/serilog-settings-configuration.sln.DotSettings @@ -1,6 +1,6 @@  - - + + True True True @@ -477,7 +477,7 @@ II.2.12 <HandlesEvent /> </Patterns> CustomLayout - + True False True @@ -551,11 +551,11 @@ II.2.12 <HandlesEvent /> True True True - - - - + + + + <data /> <data><IncludeFilters /><ExcludeFilters /></data> True - True \ No newline at end of file + True diff --git a/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs b/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs index aa709456..4e24eef2 100644 --- a/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs +++ b/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -using System; using System.Reflection; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyModel; @@ -20,189 +19,188 @@ using Serilog.Settings.Configuration; using Serilog.Settings.Configuration.Assemblies; -namespace Serilog +namespace Serilog; + +/// +/// Extends with support for System.Configuration appSettings elements. +/// +public static class ConfigurationLoggerConfigurationExtensions { /// - /// Extends with support for System.Configuration appSettings elements. + /// Configuration section name required by this package. + /// + public const string DefaultSectionName = "Serilog"; + + /// + /// Reads logger settings from the provided configuration object using the provided 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. + /// A section name for section 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. + public static LoggerConfiguration Configuration( + this LoggerSettingsConfiguration settingConfiguration, + IConfiguration configuration, + string sectionName, + DependencyContext dependencyContext = null) + { + if (settingConfiguration == null) throw new ArgumentNullException(nameof(settingConfiguration)); + if (configuration == null) throw new ArgumentNullException(nameof(configuration)); + if (sectionName == null) throw new ArgumentNullException(nameof(sectionName)); + + var assemblyFinder = dependencyContext == null + ? AssemblyFinder.Auto() + : AssemblyFinder.ForDependencyContext(dependencyContext); + + return settingConfiguration.Settings( + new ConfigurationReader( + configuration.GetSection(sectionName), + assemblyFinder, + configuration)); + } + + /// + /// 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. + /// 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) + => Configuration(settingConfiguration, configuration, DefaultSectionName, dependencyContext); + + /// + /// 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 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. + [Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, string sectionName, DependencyContext dependencyContext) instead.")] + public static LoggerConfiguration ConfigurationSection( + this LoggerSettingsConfiguration settingConfiguration, + IConfigurationSection configSection, + DependencyContext dependencyContext = null) + { + if (settingConfiguration == null) throw new ArgumentNullException(nameof(settingConfiguration)); + if (configSection == null) throw new ArgumentNullException(nameof(configSection)); + + var assemblyFinder = dependencyContext == null + ? AssemblyFinder.Auto() + : AssemblyFinder.ForDependencyContext(dependencyContext); + + return settingConfiguration.Settings( + new ConfigurationReader( + configSection, + assemblyFinder, + configuration: null)); + } + + /// + /// Reads logger settings from the provided configuration object using the provided 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. + /// A section name for section 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, + string sectionName, + ConfigurationAssemblySource configurationAssemblySource) + { + if (settingConfiguration == null) throw new ArgumentNullException(nameof(settingConfiguration)); + if (configuration == null) throw new ArgumentNullException(nameof(configuration)); + if (sectionName == null) throw new ArgumentNullException(nameof(sectionName)); + + var assemblyFinder = AssemblyFinder.ForSource(configurationAssemblySource); + + return settingConfiguration.Settings(new ConfigurationReader(configuration.GetSection(sectionName), assemblyFinder, configuration)); + } + + /// + /// 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. /// - public static class ConfigurationLoggerConfigurationExtensions + /// 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) + => Configuration(settingConfiguration, configuration, DefaultSectionName, configurationAssemblySource); + + /// + /// 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. + [Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, string sectionName, ConfigurationAssemblySource configurationAssemblySource) instead.")] + public static LoggerConfiguration ConfigurationSection( + this LoggerSettingsConfiguration settingConfiguration, + IConfigurationSection configSection, + ConfigurationAssemblySource configurationAssemblySource) { - /// - /// Configuration section name required by this package. - /// - public const string DefaultSectionName = "Serilog"; - - /// - /// Reads logger settings from the provided configuration object using the provided 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. - /// A section name for section 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. - public static LoggerConfiguration Configuration( - this LoggerSettingsConfiguration settingConfiguration, - IConfiguration configuration, - string sectionName, - DependencyContext dependencyContext = null) - { - if (settingConfiguration == null) throw new ArgumentNullException(nameof(settingConfiguration)); - if (configuration == null) throw new ArgumentNullException(nameof(configuration)); - if (sectionName == null) throw new ArgumentNullException(nameof(sectionName)); - - var assemblyFinder = dependencyContext == null - ? AssemblyFinder.Auto() - : AssemblyFinder.ForDependencyContext(dependencyContext); - - return settingConfiguration.Settings( - new ConfigurationReader( - configuration.GetSection(sectionName), - assemblyFinder, - configuration)); - } - - /// - /// 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. - /// 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) - => Configuration(settingConfiguration, configuration, DefaultSectionName, dependencyContext); - - /// - /// 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 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. - [Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, string sectionName, DependencyContext dependencyContext) instead.")] - public static LoggerConfiguration ConfigurationSection( - this LoggerSettingsConfiguration settingConfiguration, - IConfigurationSection configSection, - DependencyContext dependencyContext = null) - { - if (settingConfiguration == null) throw new ArgumentNullException(nameof(settingConfiguration)); - if (configSection == null) throw new ArgumentNullException(nameof(configSection)); - - var assemblyFinder = dependencyContext == null - ? AssemblyFinder.Auto() - : AssemblyFinder.ForDependencyContext(dependencyContext); - - return settingConfiguration.Settings( - new ConfigurationReader( - configSection, - assemblyFinder, - configuration: null)); - } - - /// - /// Reads logger settings from the provided configuration object using the provided 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. - /// A section name for section 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, - string sectionName, - ConfigurationAssemblySource configurationAssemblySource) - { - if (settingConfiguration == null) throw new ArgumentNullException(nameof(settingConfiguration)); - if (configuration == null) throw new ArgumentNullException(nameof(configuration)); - if (sectionName == null) throw new ArgumentNullException(nameof(sectionName)); - - var assemblyFinder = AssemblyFinder.ForSource(configurationAssemblySource); - - return settingConfiguration.Settings(new ConfigurationReader(configuration.GetSection(sectionName), assemblyFinder, configuration)); - } - - /// - /// 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) - => Configuration(settingConfiguration, configuration, DefaultSectionName, configurationAssemblySource); - - /// - /// 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. - [Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, string sectionName, ConfigurationAssemblySource configurationAssemblySource) instead.")] - 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)); - - var assemblyFinder = AssemblyFinder.ForSource(configurationAssemblySource); - - return settingConfiguration.Settings(new ConfigurationReader(configSection, assemblyFinder, configuration: null)); - } - - /// - /// Reads logger settings from the provided configuration object using the provided section name. - /// - /// Logger setting configuration. - /// A configuration object which contains a Serilog section. - /// A section name for section which contains a Serilog section. - /// A collection of assemblies that contains sinks and other types. - /// An object allowing configuration to continue. - public static LoggerConfiguration Configuration( - this LoggerSettingsConfiguration settingConfiguration, - IConfiguration configuration, - string sectionName, - params Assembly[] assemblies) - { - if (settingConfiguration == null) throw new ArgumentNullException(nameof(settingConfiguration)); - if (configuration == null) throw new ArgumentNullException(nameof(configuration)); - if (sectionName == null) throw new ArgumentNullException(nameof(sectionName)); - - return settingConfiguration.Settings(new ConfigurationReader(configuration.GetSection(sectionName), assemblies, new ResolutionContext(configuration))); - } - - /// - /// Reads logger settings from the provided configuration object using the default section name. - /// - /// Logger setting configuration. - /// A configuration object which contains a Serilog section. - /// A collection of assemblies that contains sinks and other types. - /// An object allowing configuration to continue. - public static LoggerConfiguration Configuration( - this LoggerSettingsConfiguration settingConfiguration, - IConfiguration configuration, - params Assembly[] assemblies) - => Configuration(settingConfiguration, configuration, DefaultSectionName, assemblies); + if (settingConfiguration == null) throw new ArgumentNullException(nameof(settingConfiguration)); + if (configSection == null) throw new ArgumentNullException(nameof(configSection)); + + var assemblyFinder = AssemblyFinder.ForSource(configurationAssemblySource); + + return settingConfiguration.Settings(new ConfigurationReader(configSection, assemblyFinder, configuration: null)); } + + /// + /// Reads logger settings from the provided configuration object using the provided section name. + /// + /// Logger setting configuration. + /// A configuration object which contains a Serilog section. + /// A section name for section which contains a Serilog section. + /// A collection of assemblies that contains sinks and other types. + /// An object allowing configuration to continue. + public static LoggerConfiguration Configuration( + this LoggerSettingsConfiguration settingConfiguration, + IConfiguration configuration, + string sectionName, + params Assembly[] assemblies) + { + if (settingConfiguration == null) throw new ArgumentNullException(nameof(settingConfiguration)); + if (configuration == null) throw new ArgumentNullException(nameof(configuration)); + if (sectionName == null) throw new ArgumentNullException(nameof(sectionName)); + + return settingConfiguration.Settings(new ConfigurationReader(configuration.GetSection(sectionName), assemblies, new ResolutionContext(configuration))); + } + + /// + /// Reads logger settings from the provided configuration object using the default section name. + /// + /// Logger setting configuration. + /// A configuration object which contains a Serilog section. + /// A collection of assemblies that contains sinks and other types. + /// An object allowing configuration to continue. + public static LoggerConfiguration Configuration( + this LoggerSettingsConfiguration settingConfiguration, + IConfiguration configuration, + params Assembly[] assemblies) + => Configuration(settingConfiguration, configuration, DefaultSectionName, assemblies); } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AssemblyFinder.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AssemblyFinder.cs index cc16a557..c2c34812 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AssemblyFinder.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AssemblyFinder.cs @@ -1,50 +1,47 @@ -using System; -using System.Collections.Generic; -using System.Reflection; +using System.Reflection; using Microsoft.Extensions.DependencyModel; -namespace Serilog.Settings.Configuration.Assemblies +namespace Serilog.Settings.Configuration.Assemblies; + +abstract class AssemblyFinder { - abstract class AssemblyFinder - { - public abstract IReadOnlyList FindAssembliesContainingName(string nameToFind); + public abstract IReadOnlyList FindAssembliesContainingName(string nameToFind); - protected static bool IsCaseInsensitiveMatch(string text, string textToFind) - { - return text != null && text.ToLowerInvariant().Contains(textToFind.ToLowerInvariant()); - } + protected static bool IsCaseInsensitiveMatch(string text, string textToFind) + { + return text != null && text.ToLowerInvariant().Contains(textToFind.ToLowerInvariant()); + } - public static AssemblyFinder Auto() + public static AssemblyFinder Auto() + { + try { - try - { - // Need to check `Assembly.GetEntryAssembly()` first because - // `DependencyContext.Default` throws an exception when `Assembly.GetEntryAssembly()` returns null - if (Assembly.GetEntryAssembly() != null && DependencyContext.Default != null) - { - return new DependencyContextAssemblyFinder(DependencyContext.Default); - } - } - catch (NotSupportedException) when (typeof(object).Assembly.Location is "") // bundled mode detection + // Need to check `Assembly.GetEntryAssembly()` first because + // `DependencyContext.Default` throws an exception when `Assembly.GetEntryAssembly()` returns null + if (Assembly.GetEntryAssembly() != null && DependencyContext.Default != null) { + return new DependencyContextAssemblyFinder(DependencyContext.Default); } - - return new DllScanningAssemblyFinder(); } - - public static AssemblyFinder ForSource(ConfigurationAssemblySource configurationAssemblySource) + catch (NotSupportedException) when (typeof(object).Assembly.Location is "") // bundled mode detection { - return configurationAssemblySource switch - { - ConfigurationAssemblySource.UseLoadedAssemblies => Auto(), - ConfigurationAssemblySource.AlwaysScanDllFiles => new DllScanningAssemblyFinder(), - _ => throw new ArgumentOutOfRangeException(nameof(configurationAssemblySource), configurationAssemblySource, null), - }; } - public static AssemblyFinder ForDependencyContext(DependencyContext dependencyContext) + return new DllScanningAssemblyFinder(); + } + + public static AssemblyFinder ForSource(ConfigurationAssemblySource configurationAssemblySource) + { + return configurationAssemblySource switch { - return new DependencyContextAssemblyFinder(dependencyContext); - } + ConfigurationAssemblySource.UseLoadedAssemblies => Auto(), + ConfigurationAssemblySource.AlwaysScanDllFiles => new DllScanningAssemblyFinder(), + _ => throw new ArgumentOutOfRangeException(nameof(configurationAssemblySource), configurationAssemblySource, null), + }; + } + + public static AssemblyFinder ForDependencyContext(DependencyContext dependencyContext) + { + return new DependencyContextAssemblyFinder(dependencyContext); } } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/ConfigurationAssemblySource.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/ConfigurationAssemblySource.cs index de5800cf..b6a60657 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/ConfigurationAssemblySource.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/ConfigurationAssemblySource.cs @@ -12,21 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -namespace Serilog.Settings.Configuration +namespace Serilog.Settings.Configuration; + +/// +/// Defines how the package will identify the assemblies which are scanned for sinks and other Type information. +/// +public enum ConfigurationAssemblySource { /// - /// Defines how the package will identify the assemblies which are scanned for sinks and other Type information. + /// Try to scan the assemblies already in memory. This is the default. If GetEntryAssembly is null, fallback to DLL scanning. /// - public enum ConfigurationAssemblySource - { - /// - /// Try to scan the assemblies already in memory. This is the default. If GetEntryAssembly is null, fallback to DLL scanning. - /// - UseLoadedAssemblies, + UseLoadedAssemblies, - /// - /// Scan for assemblies in DLLs from the working directory. This is the fallback when GetEntryAssembly is null. - /// - AlwaysScanDllFiles - } + /// + /// Scan for assemblies in DLLs from the working directory. This is the fallback when GetEntryAssembly is null. + /// + AlwaysScanDllFiles } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DependencyContextAssemblyFinder.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DependencyContextAssemblyFinder.cs index 7a6feaa6..13e24f23 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DependencyContextAssemblyFinder.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DependencyContextAssemblyFinder.cs @@ -1,37 +1,33 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; +using System.Reflection; using Microsoft.Extensions.DependencyModel; -namespace Serilog.Settings.Configuration.Assemblies +namespace Serilog.Settings.Configuration.Assemblies; + +sealed class DependencyContextAssemblyFinder : AssemblyFinder { - sealed class DependencyContextAssemblyFinder : AssemblyFinder + readonly DependencyContext _dependencyContext; + + public DependencyContextAssemblyFinder(DependencyContext dependencyContext) { - readonly DependencyContext _dependencyContext; + _dependencyContext = dependencyContext ?? throw new ArgumentNullException(nameof(dependencyContext)); + } - public DependencyContextAssemblyFinder(DependencyContext dependencyContext) - { - _dependencyContext = dependencyContext ?? throw new ArgumentNullException(nameof(dependencyContext)); - } + public override IReadOnlyList FindAssembliesContainingName(string nameToFind) + { + var query = from library in _dependencyContext.RuntimeLibraries + where IsReferencingSerilog(library) + from assemblyName in library.GetDefaultAssemblyNames(_dependencyContext) + where IsCaseInsensitiveMatch(assemblyName.Name, nameToFind) + select assemblyName; - public override IReadOnlyList FindAssembliesContainingName(string nameToFind) - { - var query = from library in _dependencyContext.RuntimeLibraries - where IsReferencingSerilog(library) - from assemblyName in library.GetDefaultAssemblyNames(_dependencyContext) - where IsCaseInsensitiveMatch(assemblyName.Name, nameToFind) - select assemblyName; + return query.ToList().AsReadOnly(); - return query.ToList().AsReadOnly(); - - static bool IsReferencingSerilog(Library library) - { - const string Serilog = "serilog"; - return library.Dependencies.Any(dependency => - dependency.Name.StartsWith(Serilog, StringComparison.OrdinalIgnoreCase) && - (dependency.Name.Length == Serilog.Length || dependency.Name[Serilog.Length] == '.')); - } + static bool IsReferencingSerilog(Library library) + { + const string Serilog = "serilog"; + return library.Dependencies.Any(dependency => + dependency.Name.StartsWith(Serilog, StringComparison.OrdinalIgnoreCase) && + (dependency.Name.Length == Serilog.Length || dependency.Name[Serilog.Length] == '.')); } } } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DllScanningAssemblyFinder.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DllScanningAssemblyFinder.cs index 9cc9139f..4d7570c9 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DllScanningAssemblyFinder.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DllScanningAssemblyFinder.cs @@ -1,65 +1,60 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Reflection; -namespace Serilog.Settings.Configuration.Assemblies +namespace Serilog.Settings.Configuration.Assemblies; + +sealed class DllScanningAssemblyFinder : AssemblyFinder { - sealed class DllScanningAssemblyFinder : AssemblyFinder + public override IReadOnlyList FindAssembliesContainingName(string nameToFind) { - public override IReadOnlyList FindAssembliesContainingName(string nameToFind) + var probeDirs = new List(); + + if (!string.IsNullOrEmpty(AppDomain.CurrentDomain.BaseDirectory)) { - var probeDirs = new List(); - - if (!string.IsNullOrEmpty(AppDomain.CurrentDomain.BaseDirectory)) - { - probeDirs.Add(AppDomain.CurrentDomain.BaseDirectory); + probeDirs.Add(AppDomain.CurrentDomain.BaseDirectory); #if NETFRAMEWORK - var privateBinPath = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath; - if (!string.IsNullOrEmpty(privateBinPath)) + var privateBinPath = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath; + if (!string.IsNullOrEmpty(privateBinPath)) + { + foreach (var path in privateBinPath.Split(';')) { - foreach (var path in privateBinPath.Split(';')) + if (Path.IsPathRooted(path)) + { + probeDirs.Add(path); + } + else { - if (Path.IsPathRooted(path)) - { - probeDirs.Add(path); - } - else - { - probeDirs.Add(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, path)); - } + probeDirs.Add(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, path)); } } -#endif - } - else - { - probeDirs.Add(Path.GetDirectoryName(typeof(AssemblyFinder).Assembly.Location)); } +#endif + } + else + { + probeDirs.Add(Path.GetDirectoryName(typeof(AssemblyFinder).Assembly.Location)); + } - var query = from probeDir in probeDirs - where Directory.Exists(probeDir) - from outputAssemblyPath in Directory.GetFiles(probeDir, "*.dll") - let assemblyFileName = Path.GetFileNameWithoutExtension(outputAssemblyPath) - where IsCaseInsensitiveMatch(assemblyFileName, nameToFind) - let assemblyName = TryGetAssemblyNameFrom(outputAssemblyPath) - where assemblyName != null - select assemblyName; + var query = from probeDir in probeDirs + where Directory.Exists(probeDir) + from outputAssemblyPath in Directory.GetFiles(probeDir, "*.dll") + let assemblyFileName = Path.GetFileNameWithoutExtension(outputAssemblyPath) + where IsCaseInsensitiveMatch(assemblyFileName, nameToFind) + let assemblyName = TryGetAssemblyNameFrom(outputAssemblyPath) + where assemblyName != null + select assemblyName; - return query.ToList().AsReadOnly(); + return query.ToList().AsReadOnly(); - static AssemblyName TryGetAssemblyNameFrom(string path) + static AssemblyName TryGetAssemblyNameFrom(string path) + { + try { - try - { - return AssemblyName.GetAssemblyName(path); - } - catch (BadImageFormatException) - { - return null; - } + return AssemblyName.GetAssemblyName(path); + } + catch (BadImageFormatException) + { + return null; } } } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index e9c75b25..8b20544e 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; @@ -14,573 +11,572 @@ using Serilog.Events; using Serilog.Settings.Configuration.Assemblies; -namespace Serilog.Settings.Configuration +namespace Serilog.Settings.Configuration; + +class ConfigurationReader : IConfigurationReader { - class ConfigurationReader : IConfigurationReader - { - const string LevelSwitchNameRegex = @"^\${0,1}[A-Za-z]+[A-Za-z0-9]*$"; + const string LevelSwitchNameRegex = @"^\${0,1}[A-Za-z]+[A-Za-z0-9]*$"; - readonly IConfigurationSection _section; - readonly IReadOnlyCollection _configurationAssemblies; - readonly ResolutionContext _resolutionContext; + readonly IConfigurationSection _section; + readonly IReadOnlyCollection _configurationAssemblies; + readonly ResolutionContext _resolutionContext; #if NETSTANDARD || NET461 - readonly IConfigurationRoot _configurationRoot; + readonly IConfigurationRoot _configurationRoot; #endif - public ConfigurationReader(IConfigurationSection configSection, AssemblyFinder assemblyFinder, IConfiguration configuration = null) - { - _section = configSection ?? throw new ArgumentNullException(nameof(configSection)); - _configurationAssemblies = LoadConfigurationAssemblies(_section, assemblyFinder); - _resolutionContext = new ResolutionContext(configuration); + public ConfigurationReader(IConfigurationSection configSection, AssemblyFinder assemblyFinder, IConfiguration configuration = null) + { + _section = configSection ?? throw new ArgumentNullException(nameof(configSection)); + _configurationAssemblies = LoadConfigurationAssemblies(_section, assemblyFinder); + _resolutionContext = new ResolutionContext(configuration); #if NETSTANDARD || NET461 - _configurationRoot = configuration as IConfigurationRoot; + _configurationRoot = configuration as IConfigurationRoot; #endif - } + } - // Used internally for processing nested configuration sections -- see GetMethodCalls below. - internal ConfigurationReader(IConfigurationSection configSection, IReadOnlyCollection configurationAssemblies, ResolutionContext resolutionContext) - { - _section = configSection ?? throw new ArgumentNullException(nameof(configSection)); - _configurationAssemblies = configurationAssemblies ?? throw new ArgumentNullException(nameof(configurationAssemblies)); - _resolutionContext = resolutionContext ?? throw new ArgumentNullException(nameof(resolutionContext)); - #if NETSTANDARD || NET461 - _configurationRoot = resolutionContext.HasAppConfiguration ? resolutionContext.AppConfiguration as IConfigurationRoot : null; - #endif - } + // Used internally for processing nested configuration sections -- see GetMethodCalls below. + internal ConfigurationReader(IConfigurationSection configSection, IReadOnlyCollection configurationAssemblies, ResolutionContext resolutionContext) + { + _section = configSection ?? throw new ArgumentNullException(nameof(configSection)); + _configurationAssemblies = configurationAssemblies ?? throw new ArgumentNullException(nameof(configurationAssemblies)); + _resolutionContext = resolutionContext ?? throw new ArgumentNullException(nameof(resolutionContext)); + #if NETSTANDARD || NET461 + _configurationRoot = resolutionContext.HasAppConfiguration ? resolutionContext.AppConfiguration as IConfigurationRoot : null; + #endif + } - public void Configure(LoggerConfiguration loggerConfiguration) - { - ProcessLevelSwitchDeclarations(); - ProcessFilterSwitchDeclarations(); - - ApplyMinimumLevel(loggerConfiguration); - ApplyEnrichment(loggerConfiguration); - ApplyFilters(loggerConfiguration); - ApplyDestructuring(loggerConfiguration); - ApplySinks(loggerConfiguration); - ApplyAuditSinks(loggerConfiguration); - } + public void Configure(LoggerConfiguration loggerConfiguration) + { + ProcessLevelSwitchDeclarations(); + ProcessFilterSwitchDeclarations(); + + ApplyMinimumLevel(loggerConfiguration); + ApplyEnrichment(loggerConfiguration); + ApplyFilters(loggerConfiguration); + ApplyDestructuring(loggerConfiguration); + ApplySinks(loggerConfiguration); + ApplyAuditSinks(loggerConfiguration); + } + + void ProcessFilterSwitchDeclarations() + { + var filterSwitchesDirective = _section.GetSection("FilterSwitches"); - void ProcessFilterSwitchDeclarations() + foreach (var filterSwitchDeclaration in filterSwitchesDirective.GetChildren()) { - var filterSwitchesDirective = _section.GetSection("FilterSwitches"); + var filterSwitch = LoggingFilterSwitchProxy.Create(); + if (filterSwitch == null) + { + SelfLog.WriteLine($"FilterSwitches section found, but neither Serilog.Expressions nor Serilog.Filters.Expressions is referenced."); + break; + } - foreach (var filterSwitchDeclaration in filterSwitchesDirective.GetChildren()) + var switchName = filterSwitchDeclaration.Key; + // switchName must be something like $switch to avoid ambiguities + if (!IsValidSwitchName(switchName)) { - var filterSwitch = LoggingFilterSwitchProxy.Create(); - if (filterSwitch == null) - { - SelfLog.WriteLine($"FilterSwitches section found, but neither Serilog.Expressions nor Serilog.Filters.Expressions is referenced."); - break; - } + throw new FormatException($"\"{switchName}\" is not a valid name for a Filter Switch declaration. The first character of the name must be a letter or '$' sign, like \"FilterSwitches\" : {{\"$switchName\" : \"{{FilterExpression}}\"}}"); + } - var switchName = filterSwitchDeclaration.Key; - // switchName must be something like $switch to avoid ambiguities - if (!IsValidSwitchName(switchName)) - { - throw new FormatException($"\"{switchName}\" is not a valid name for a Filter Switch declaration. The first character of the name must be a letter or '$' sign, like \"FilterSwitches\" : {{\"$switchName\" : \"{{FilterExpression}}\"}}"); - } + SetFilterSwitch(throwOnError: true); + SubscribeToFilterExpressionChanges(); - SetFilterSwitch(throwOnError: true); - SubscribeToFilterExpressionChanges(); + _resolutionContext.AddFilterSwitch(switchName, filterSwitch); - _resolutionContext.AddFilterSwitch(switchName, filterSwitch); + void SubscribeToFilterExpressionChanges() + { + ChangeToken.OnChange(filterSwitchDeclaration.GetReloadToken, () => SetFilterSwitch(throwOnError: false)); + } - void SubscribeToFilterExpressionChanges() + void SetFilterSwitch(bool throwOnError) + { + var filterExpr = filterSwitchDeclaration.Value; + if (string.IsNullOrWhiteSpace(filterExpr)) { - ChangeToken.OnChange(filterSwitchDeclaration.GetReloadToken, () => SetFilterSwitch(throwOnError: false)); + filterSwitch.Expression = null; + return; } - void SetFilterSwitch(bool throwOnError) + try { - var filterExpr = filterSwitchDeclaration.Value; - if (string.IsNullOrWhiteSpace(filterExpr)) - { - filterSwitch.Expression = null; - return; - } - - try + filterSwitch.Expression = filterExpr; + } + catch (Exception e) + { + var errMsg = $"The expression '{filterExpr}' is invalid filter expression: {e.Message}."; + if (throwOnError) { - filterSwitch.Expression = filterExpr; + throw new InvalidOperationException(errMsg, e); } - catch (Exception e) - { - var errMsg = $"The expression '{filterExpr}' is invalid filter expression: {e.Message}."; - if (throwOnError) - { - throw new InvalidOperationException(errMsg, e); - } - SelfLog.WriteLine(errMsg); - } + SelfLog.WriteLine(errMsg); } } } + } - void ProcessLevelSwitchDeclarations() + void ProcessLevelSwitchDeclarations() + { + var levelSwitchesDirective = _section.GetSection("LevelSwitches"); + foreach (var levelSwitchDeclaration in levelSwitchesDirective.GetChildren()) { - var levelSwitchesDirective = _section.GetSection("LevelSwitches"); - 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. The first character of the name must be a letter or '$' sign, like \"LevelSwitches\" : {{\"$switchName\" : \"InitialLevel\"}}"); - } + throw new FormatException($"\"{switchName}\" is not a valid name for a Level Switch declaration. The first character of the name must be a letter or '$' sign, like \"LevelSwitches\" : {{\"$switchName\" : \"InitialLevel\"}}"); + } - LoggingLevelSwitch newSwitch; - if (string.IsNullOrEmpty(switchInitialLevel)) - { - newSwitch = new LoggingLevelSwitch(); - } - else - { - var initialLevel = ParseLogEventLevel(switchInitialLevel); - newSwitch = new LoggingLevelSwitch(initialLevel); - } + LoggingLevelSwitch newSwitch; + if (string.IsNullOrEmpty(switchInitialLevel)) + { + newSwitch = new LoggingLevelSwitch(); + } + else + { + var initialLevel = ParseLogEventLevel(switchInitialLevel); + newSwitch = new LoggingLevelSwitch(initialLevel); + } - SubscribeToLoggingLevelChanges(levelSwitchDeclaration, newSwitch); + SubscribeToLoggingLevelChanges(levelSwitchDeclaration, newSwitch); - // make them available later on when resolving argument values - _resolutionContext.AddLevelSwitch(switchName, newSwitch); - } + // make them available later on when resolving argument values + _resolutionContext.AddLevelSwitch(switchName, newSwitch); + } + } + + void ApplyMinimumLevel(LoggerConfiguration loggerConfiguration) + { + var minimumLevelDirective = _section.GetSection("MinimumLevel"); + + IConfigurationSection defaultMinLevelDirective = GetDefaultMinLevelDirective(); + if (defaultMinLevelDirective.Value != null) + { + ApplyMinimumLevelConfiguration(defaultMinLevelDirective, (configuration, levelSwitch) => configuration.ControlledBy(levelSwitch)); } - void ApplyMinimumLevel(LoggerConfiguration loggerConfiguration) + var minLevelControlledByDirective = minimumLevelDirective.GetSection("ControlledBy"); + if (minLevelControlledByDirective.Value != null) { - var minimumLevelDirective = _section.GetSection("MinimumLevel"); + var globalMinimumLevelSwitch = _resolutionContext.LookUpLevelSwitchByName(minLevelControlledByDirective.Value); + // not calling ApplyMinimumLevel local function because here we have a reference to a LogLevelSwitch already + loggerConfiguration.MinimumLevel.ControlledBy(globalMinimumLevelSwitch); + } - IConfigurationSection defaultMinLevelDirective = GetDefaultMinLevelDirective(); - if (defaultMinLevelDirective.Value != null) + foreach (var overrideDirective in minimumLevelDirective.GetSection("Override").GetChildren()) + { + var overridePrefix = overrideDirective.Key; + var overridenLevelOrSwitch = overrideDirective.Value; + if (Enum.TryParse(overridenLevelOrSwitch, out LogEventLevel _)) { - ApplyMinimumLevelConfiguration(defaultMinLevelDirective, (configuration, levelSwitch) => configuration.ControlledBy(levelSwitch)); + ApplyMinimumLevelConfiguration(overrideDirective, (configuration, levelSwitch) => configuration.Override(overridePrefix, levelSwitch)); } - - var minLevelControlledByDirective = minimumLevelDirective.GetSection("ControlledBy"); - if (minLevelControlledByDirective.Value != null) + else { - var globalMinimumLevelSwitch = _resolutionContext.LookUpLevelSwitchByName(minLevelControlledByDirective.Value); + var overrideSwitch = _resolutionContext.LookUpLevelSwitchByName(overridenLevelOrSwitch); // not calling ApplyMinimumLevel local function because here we have a reference to a LogLevelSwitch already - loggerConfiguration.MinimumLevel.ControlledBy(globalMinimumLevelSwitch); + loggerConfiguration.MinimumLevel.Override(overridePrefix, overrideSwitch); } + } - foreach (var overrideDirective in minimumLevelDirective.GetSection("Override").GetChildren()) - { - var overridePrefix = overrideDirective.Key; - var overridenLevelOrSwitch = overrideDirective.Value; - if (Enum.TryParse(overridenLevelOrSwitch, out LogEventLevel _)) - { - ApplyMinimumLevelConfiguration(overrideDirective, (configuration, levelSwitch) => configuration.Override(overridePrefix, levelSwitch)); - } - else - { - var overrideSwitch = _resolutionContext.LookUpLevelSwitchByName(overridenLevelOrSwitch); - // not calling ApplyMinimumLevel local function because here we have a reference to a LogLevelSwitch already - loggerConfiguration.MinimumLevel.Override(overridePrefix, overrideSwitch); - } - } + void ApplyMinimumLevelConfiguration(IConfigurationSection directive, Action applyConfigAction) + { + var minimumLevel = ParseLogEventLevel(directive.Value); - void ApplyMinimumLevelConfiguration(IConfigurationSection directive, Action applyConfigAction) - { - var minimumLevel = ParseLogEventLevel(directive.Value); + var levelSwitch = new LoggingLevelSwitch(minimumLevel); + applyConfigAction(loggerConfiguration.MinimumLevel, levelSwitch); - var levelSwitch = new LoggingLevelSwitch(minimumLevel); - applyConfigAction(loggerConfiguration.MinimumLevel, levelSwitch); + SubscribeToLoggingLevelChanges(directive, levelSwitch); + } - SubscribeToLoggingLevelChanges(directive, levelSwitch); - } + IConfigurationSection GetDefaultMinLevelDirective() + { + #if NETSTANDARD || NET461 - IConfigurationSection GetDefaultMinLevelDirective() + var defaultLevelDirective = minimumLevelDirective.GetSection("Default"); + if (_configurationRoot != null && minimumLevelDirective.Value != null && defaultLevelDirective.Value != null) { - #if NETSTANDARD || NET461 - - var defaultLevelDirective = minimumLevelDirective.GetSection("Default"); - if (_configurationRoot != null && minimumLevelDirective.Value != null && defaultLevelDirective.Value != null) + foreach (var provider in _configurationRoot.Providers.Reverse()) { - foreach (var provider in _configurationRoot.Providers.Reverse()) + if (provider.TryGet(minimumLevelDirective.Path, out _)) { - if (provider.TryGet(minimumLevelDirective.Path, out _)) - { - return _configurationRoot.GetSection(minimumLevelDirective.Path); - } - - if (provider.TryGet(defaultLevelDirective.Path, out _)) - { - return _configurationRoot.GetSection(defaultLevelDirective.Path); - } + return _configurationRoot.GetSection(minimumLevelDirective.Path); } - return null; + if (provider.TryGet(defaultLevelDirective.Path, out _)) + { + return _configurationRoot.GetSection(defaultLevelDirective.Path); + } } - #endif //NET451 or fallback - - return minimumLevelDirective.Value != null ? minimumLevelDirective : minimumLevelDirective.GetSection("Default"); + return null; } - } - void SubscribeToLoggingLevelChanges(IConfigurationSection levelSection, LoggingLevelSwitch levelSwitch) - { - ChangeToken.OnChange( - levelSection.GetReloadToken, - () => - { - if (Enum.TryParse(levelSection.Value, out LogEventLevel minimumLevel)) - levelSwitch.MinimumLevel = minimumLevel; - else - SelfLog.WriteLine($"The value {levelSection.Value} is not a valid Serilog level."); - }); + #endif //NET451 or fallback + + return minimumLevelDirective.Value != null ? minimumLevelDirective : minimumLevelDirective.GetSection("Default"); } + } - void ApplyFilters(LoggerConfiguration loggerConfiguration) - { - var filterDirective = _section.GetSection("Filter"); - if (filterDirective.GetChildren().Any()) + void SubscribeToLoggingLevelChanges(IConfigurationSection levelSection, LoggingLevelSwitch levelSwitch) + { + ChangeToken.OnChange( + levelSection.GetReloadToken, + () => { - var methodCalls = GetMethodCalls(filterDirective); - CallConfigurationMethods(methodCalls, FindFilterConfigurationMethods(_configurationAssemblies), loggerConfiguration.Filter); - } - } + if (Enum.TryParse(levelSection.Value, out LogEventLevel minimumLevel)) + levelSwitch.MinimumLevel = minimumLevel; + else + SelfLog.WriteLine($"The value {levelSection.Value} is not a valid Serilog level."); + }); + } - void ApplyDestructuring(LoggerConfiguration loggerConfiguration) + void ApplyFilters(LoggerConfiguration loggerConfiguration) + { + var filterDirective = _section.GetSection("Filter"); + if (filterDirective.GetChildren().Any()) { - var destructureDirective = _section.GetSection("Destructure"); - if (destructureDirective.GetChildren().Any()) - { - var methodCalls = GetMethodCalls(destructureDirective); - CallConfigurationMethods(methodCalls, FindDestructureConfigurationMethods(_configurationAssemblies), loggerConfiguration.Destructure); - } + var methodCalls = GetMethodCalls(filterDirective); + CallConfigurationMethods(methodCalls, FindFilterConfigurationMethods(_configurationAssemblies), loggerConfiguration.Filter); } + } - void ApplySinks(LoggerConfiguration loggerConfiguration) + void ApplyDestructuring(LoggerConfiguration loggerConfiguration) + { + var destructureDirective = _section.GetSection("Destructure"); + if (destructureDirective.GetChildren().Any()) { - var writeToDirective = _section.GetSection("WriteTo"); - if (writeToDirective.GetChildren().Any()) - { - var methodCalls = GetMethodCalls(writeToDirective); - CallConfigurationMethods(methodCalls, FindSinkConfigurationMethods(_configurationAssemblies), loggerConfiguration.WriteTo); - } + var methodCalls = GetMethodCalls(destructureDirective); + CallConfigurationMethods(methodCalls, FindDestructureConfigurationMethods(_configurationAssemblies), loggerConfiguration.Destructure); } + } - void ApplyAuditSinks(LoggerConfiguration loggerConfiguration) + void ApplySinks(LoggerConfiguration loggerConfiguration) + { + var writeToDirective = _section.GetSection("WriteTo"); + if (writeToDirective.GetChildren().Any()) { - var auditToDirective = _section.GetSection("AuditTo"); - if (auditToDirective.GetChildren().Any()) - { - var methodCalls = GetMethodCalls(auditToDirective); - CallConfigurationMethods(methodCalls, FindAuditSinkConfigurationMethods(_configurationAssemblies), loggerConfiguration.AuditTo); - } + var methodCalls = GetMethodCalls(writeToDirective); + CallConfigurationMethods(methodCalls, FindSinkConfigurationMethods(_configurationAssemblies), loggerConfiguration.WriteTo); } + } - void IConfigurationReader.ApplySinks(LoggerSinkConfiguration loggerSinkConfiguration) + void ApplyAuditSinks(LoggerConfiguration loggerConfiguration) + { + var auditToDirective = _section.GetSection("AuditTo"); + if (auditToDirective.GetChildren().Any()) { - var methodCalls = GetMethodCalls(_section); - CallConfigurationMethods(methodCalls, FindSinkConfigurationMethods(_configurationAssemblies), loggerSinkConfiguration); + var methodCalls = GetMethodCalls(auditToDirective); + CallConfigurationMethods(methodCalls, FindAuditSinkConfigurationMethods(_configurationAssemblies), loggerConfiguration.AuditTo); } + } + + void IConfigurationReader.ApplySinks(LoggerSinkConfiguration loggerSinkConfiguration) + { + var methodCalls = GetMethodCalls(_section); + CallConfigurationMethods(methodCalls, FindSinkConfigurationMethods(_configurationAssemblies), loggerSinkConfiguration); + } + + void IConfigurationReader.ApplyEnrichment(LoggerEnrichmentConfiguration loggerEnrichmentConfiguration) + { + var methodCalls = GetMethodCalls(_section); + CallConfigurationMethods(methodCalls, FindEventEnricherConfigurationMethods(_configurationAssemblies), loggerEnrichmentConfiguration); + } - void IConfigurationReader.ApplyEnrichment(LoggerEnrichmentConfiguration loggerEnrichmentConfiguration) + void ApplyEnrichment(LoggerConfiguration loggerConfiguration) + { + var enrichDirective = _section.GetSection("Enrich"); + if (enrichDirective.GetChildren().Any()) { - var methodCalls = GetMethodCalls(_section); - CallConfigurationMethods(methodCalls, FindEventEnricherConfigurationMethods(_configurationAssemblies), loggerEnrichmentConfiguration); + var methodCalls = GetMethodCalls(enrichDirective); + CallConfigurationMethods(methodCalls, FindEventEnricherConfigurationMethods(_configurationAssemblies), loggerConfiguration.Enrich); } - void ApplyEnrichment(LoggerConfiguration loggerConfiguration) + var propertiesDirective = _section.GetSection("Properties"); + if (propertiesDirective.GetChildren().Any()) { - var enrichDirective = _section.GetSection("Enrich"); - if (enrichDirective.GetChildren().Any()) + foreach (var enrichPropertyDirective in propertiesDirective.GetChildren()) { - var methodCalls = GetMethodCalls(enrichDirective); - CallConfigurationMethods(methodCalls, FindEventEnricherConfigurationMethods(_configurationAssemblies), loggerConfiguration.Enrich); - } - - var propertiesDirective = _section.GetSection("Properties"); - if (propertiesDirective.GetChildren().Any()) - { - foreach (var enrichPropertyDirective in propertiesDirective.GetChildren()) - { - loggerConfiguration.Enrich.WithProperty(enrichPropertyDirective.Key, enrichPropertyDirective.Value); - } + loggerConfiguration.Enrich.WithProperty(enrichPropertyDirective.Key, enrichPropertyDirective.Value); } } + } - internal ILookup> GetMethodCalls(IConfigurationSection directive) + internal ILookup> GetMethodCalls(IConfigurationSection directive) + { + var children = directive.GetChildren().ToList(); + + var result = + (from child in children + where child.Value != null // Plain string + select new { Name = child.Value, Args = new Dictionary() }) + .Concat( + (from child in children + where child.Value == null + let name = GetSectionName(child) + let callArgs = (from argument in child.GetSection("Args").GetChildren() + select new + { + Name = argument.Key, + Value = GetArgumentValue(argument, _configurationAssemblies) + }).ToDictionary(p => p.Name, p => p.Value) + select new { Name = name, Args = callArgs })) + .ToLookup(p => p.Name, p => p.Args); + + return result; + + static string GetSectionName(IConfigurationSection s) { - var children = directive.GetChildren().ToList(); - - var result = - (from child in children - where child.Value != null // Plain string - select new { Name = child.Value, Args = new Dictionary() }) - .Concat( - (from child in children - where child.Value == null - let name = GetSectionName(child) - let callArgs = (from argument in child.GetSection("Args").GetChildren() - select new - { - Name = argument.Key, - Value = GetArgumentValue(argument, _configurationAssemblies) - }).ToDictionary(p => p.Name, p => p.Value) - select new { Name = name, Args = callArgs })) - .ToLookup(p => p.Name, p => p.Args); - - return result; - - static string GetSectionName(IConfigurationSection s) - { - var name = s.GetSection("Name"); - if (name.Value == null) - throw new InvalidOperationException($"The configuration value in {name.Path} has no 'Name' element."); + var name = s.GetSection("Name"); + if (name.Value == null) + throw new InvalidOperationException($"The configuration value in {name.Path} has no 'Name' element."); - return name.Value; - } + return name.Value; } + } - internal static IConfigurationArgumentValue GetArgumentValue(IConfigurationSection argumentSection, IReadOnlyCollection configurationAssemblies) + internal static IConfigurationArgumentValue GetArgumentValue(IConfigurationSection argumentSection, IReadOnlyCollection configurationAssemblies) + { + 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( + $"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) { - 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( - $"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) - { - argumentValue = new StringArgumentValue(argumentSection.Value); - } - else - { - argumentValue = new ObjectArgumentValue(argumentSection, configurationAssemblies); - } - - return argumentValue; + argumentValue = new StringArgumentValue(argumentSection.Value); } - - static IReadOnlyCollection LoadConfigurationAssemblies(IConfigurationSection section, AssemblyFinder assemblyFinder) + else { - var serilogAssembly = typeof(ILogger).Assembly; - var assemblies = new Dictionary { [serilogAssembly.FullName] = serilogAssembly }; + argumentValue = new ObjectArgumentValue(argumentSection, configurationAssemblies); + } - var usingSection = section.GetSection("Using"); - if (usingSection.GetChildren().Any()) - { - foreach (var simpleName in usingSection.GetChildren().Select(c => c.Value)) - { - if (string.IsNullOrWhiteSpace(simpleName)) - throw new InvalidOperationException( - "A zero-length or whitespace assembly name was supplied to a Serilog.Using configuration statement."); + return argumentValue; + } - var assembly = Assembly.Load(new AssemblyName(simpleName)); - if (!assemblies.ContainsKey(assembly.FullName)) - assemblies.Add(assembly.FullName, assembly); - } - } + static IReadOnlyCollection LoadConfigurationAssemblies(IConfigurationSection section, AssemblyFinder assemblyFinder) + { + var serilogAssembly = typeof(ILogger).Assembly; + var assemblies = new Dictionary { [serilogAssembly.FullName] = serilogAssembly }; - foreach (var assemblyName in assemblyFinder.FindAssembliesContainingName("serilog")) + var usingSection = section.GetSection("Using"); + if (usingSection.GetChildren().Any()) + { + foreach (var simpleName in usingSection.GetChildren().Select(c => c.Value)) { - var assumed = Assembly.Load(assemblyName); - if (assumed != null && !assemblies.ContainsKey(assumed.FullName)) - assemblies.Add(assumed.FullName, assumed); + if (string.IsNullOrWhiteSpace(simpleName)) + throw new InvalidOperationException( + "A zero-length or whitespace assembly name was supplied to a Serilog.Using configuration statement."); + + var assembly = Assembly.Load(new AssemblyName(simpleName)); + if (!assemblies.ContainsKey(assembly.FullName)) + assemblies.Add(assembly.FullName, assembly); } + } - return assemblies.Values.ToList().AsReadOnly(); + foreach (var assemblyName in assemblyFinder.FindAssembliesContainingName("serilog")) + { + var assumed = Assembly.Load(assemblyName); + if (assumed != null && !assemblies.ContainsKey(assumed.FullName)) + assemblies.Add(assumed.FullName, assumed); } - void CallConfigurationMethods(ILookup> methods, IReadOnlyCollection configurationMethods, object receiver) + return assemblies.Values.ToList().AsReadOnly(); + } + + void CallConfigurationMethods(ILookup> methods, IReadOnlyCollection configurationMethods, object receiver) + { + foreach (var method in methods.SelectMany(g => g.Select(x => new { g.Key, Value = x }))) { - foreach (var method in methods.SelectMany(g => g.Select(x => new { g.Key, Value = x }))) - { - var methodInfo = SelectConfigurationMethod(configurationMethods, method.Key, method.Value.Keys.ToList()); + var methodInfo = SelectConfigurationMethod(configurationMethods, method.Key, method.Value.Keys.ToList()); - if (methodInfo != null) - { - var call = (from p in methodInfo.GetParameters().Skip(1) - let directive = method.Value.FirstOrDefault(s => ParameterNameMatches(p.Name, s.Key)) - select directive.Key == null - ? GetImplicitValueForNotSpecifiedKey(p, methodInfo) - : directive.Value.ConvertTo(p.ParameterType, _resolutionContext)).ToList(); - - call.Insert(0, receiver); - methodInfo.Invoke(null, call.ToArray()); - } + if (methodInfo != null) + { + var call = (from p in methodInfo.GetParameters().Skip(1) + let directive = method.Value.FirstOrDefault(s => ParameterNameMatches(p.Name, s.Key)) + select directive.Key == null + ? GetImplicitValueForNotSpecifiedKey(p, methodInfo) + : directive.Value.ConvertTo(p.ParameterType, _resolutionContext)).ToList(); + + call.Insert(0, receiver); + methodInfo.Invoke(null, call.ToArray()); } } + } - static bool HasImplicitValueWhenNotSpecified(ParameterInfo paramInfo) + static bool HasImplicitValueWhenNotSpecified(ParameterInfo paramInfo) + { + return paramInfo.HasDefaultValue + // parameters of type IConfiguration are implicitly populated with provided Configuration + || paramInfo.ParameterType == typeof(IConfiguration); + } + + object GetImplicitValueForNotSpecifiedKey(ParameterInfo parameter, MethodInfo methodToInvoke) + { + if (!HasImplicitValueWhenNotSpecified(parameter)) { - return paramInfo.HasDefaultValue - // parameters of type IConfiguration are implicitly populated with provided Configuration - || paramInfo.ParameterType == typeof(IConfiguration); + throw new InvalidOperationException("GetImplicitValueForNotSpecifiedKey() should only be called for parameters for which HasImplicitValueWhenNotSpecified() is true. " + + "This means something is wrong in the Serilog.Settings.Configuration code."); } - object GetImplicitValueForNotSpecifiedKey(ParameterInfo parameter, MethodInfo methodToInvoke) + if (parameter.ParameterType == typeof(IConfiguration)) { - if (!HasImplicitValueWhenNotSpecified(parameter)) + if (_resolutionContext.HasAppConfiguration) { - throw new InvalidOperationException("GetImplicitValueForNotSpecifiedKey() should only be called for parameters for which HasImplicitValueWhenNotSpecified() is true. " + - "This means something is wrong in the Serilog.Settings.Configuration code."); + return _resolutionContext.AppConfiguration; } - - if (parameter.ParameterType == typeof(IConfiguration)) + if (parameter.HasDefaultValue) { - if (_resolutionContext.HasAppConfiguration) - { - return _resolutionContext.AppConfiguration; - } - if (parameter.HasDefaultValue) - { - return parameter.DefaultValue; - } - - throw new InvalidOperationException("Trying to invoke a configuration method accepting a `IConfiguration` argument. " + - $"This is not supported when only a `IConfigSection` has been provided. (method '{methodToInvoke}')"); + return parameter.DefaultValue; } - return parameter.DefaultValue; + throw new InvalidOperationException("Trying to invoke a configuration method accepting a `IConfiguration` argument. " + + $"This is not supported when only a `IConfigSection` has been provided. (method '{methodToInvoke}')"); } - internal static MethodInfo SelectConfigurationMethod(IReadOnlyCollection candidateMethods, string name, IReadOnlyCollection suppliedArgumentNames) - { - // 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). - var selectedMethod = candidateMethods - .Where(m => m.Name == name) - .Where(m => m.GetParameters() - .Skip(1) - .All(p => HasImplicitValueWhenNotSpecified(p) || - ParameterNameMatches(p.Name, suppliedArgumentNames))) - .OrderByDescending(m => - { - var matchingArgs = m.GetParameters().Where(p => ParameterNameMatches(p.Name, suppliedArgumentNames)).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(); + return parameter.DefaultValue; + } - if (selectedMethod == null) + internal static MethodInfo SelectConfigurationMethod(IReadOnlyCollection candidateMethods, string name, IReadOnlyCollection suppliedArgumentNames) + { + // 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). + var selectedMethod = candidateMethods + .Where(m => m.Name == name) + .Where(m => m.GetParameters() + .Skip(1) + .All(p => HasImplicitValueWhenNotSpecified(p) || + ParameterNameMatches(p.Name, suppliedArgumentNames))) + .OrderByDescending(m => { - var methodsByName = candidateMethods - .Where(m => m.Name == name) - .Select(m => $"{m.Name}({string.Join(", ", m.GetParameters().Skip(1).Select(p => p.Name))})") - .ToList(); - - if (!methodsByName.Any()) - SelfLog.WriteLine($"Unable to find a method called {name}. Candidate methods are:{Environment.NewLine}{string.Join(Environment.NewLine, candidateMethods)}"); - else - SelfLog.WriteLine($"Unable to find a method called {name} " - + (suppliedArgumentNames.Any() - ? "for supplied arguments: " + string.Join(", ", suppliedArgumentNames) - : "with no supplied arguments") - + ". Candidate methods are:" - + Environment.NewLine - + string.Join(Environment.NewLine, methodsByName)); - } + var matchingArgs = m.GetParameters().Where(p => ParameterNameMatches(p.Name, suppliedArgumentNames)).ToList(); - return selectedMethod; - } + // 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(); - static bool ParameterNameMatches(string actualParameterName, string suppliedName) + if (selectedMethod == null) { - return suppliedName.Equals(actualParameterName, StringComparison.OrdinalIgnoreCase); - } + var methodsByName = candidateMethods + .Where(m => m.Name == name) + .Select(m => $"{m.Name}({string.Join(", ", m.GetParameters().Skip(1).Select(p => p.Name))})") + .ToList(); - static bool ParameterNameMatches(string actualParameterName, IEnumerable suppliedNames) - { - return suppliedNames.Any(s => ParameterNameMatches(actualParameterName, s)); + if (!methodsByName.Any()) + SelfLog.WriteLine($"Unable to find a method called {name}. Candidate methods are:{Environment.NewLine}{string.Join(Environment.NewLine, candidateMethods)}"); + else + SelfLog.WriteLine($"Unable to find a method called {name} " + + (suppliedArgumentNames.Any() + ? "for supplied arguments: " + string.Join(", ", suppliedArgumentNames) + : "with no supplied arguments") + + ". Candidate methods are:" + + Environment.NewLine + + string.Join(Environment.NewLine, methodsByName)); } - static IReadOnlyCollection FindSinkConfigurationMethods(IReadOnlyCollection configurationAssemblies) - { - var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerSinkConfiguration)); - if (configurationAssemblies.Contains(typeof(LoggerSinkConfiguration).GetTypeInfo().Assembly)) - found.AddRange(SurrogateConfigurationMethods.WriteTo); + return selectedMethod; + } - return found; - } + static bool ParameterNameMatches(string actualParameterName, string suppliedName) + { + return suppliedName.Equals(actualParameterName, StringComparison.OrdinalIgnoreCase); + } - static IReadOnlyCollection FindAuditSinkConfigurationMethods(IReadOnlyCollection configurationAssemblies) - { - var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerAuditSinkConfiguration)); - if (configurationAssemblies.Contains(typeof(LoggerAuditSinkConfiguration).GetTypeInfo().Assembly)) - found.AddRange(SurrogateConfigurationMethods.AuditTo); - return found; - } + static bool ParameterNameMatches(string actualParameterName, IEnumerable suppliedNames) + { + return suppliedNames.Any(s => ParameterNameMatches(actualParameterName, s)); + } - static IReadOnlyCollection FindFilterConfigurationMethods(IReadOnlyCollection configurationAssemblies) - { - var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerFilterConfiguration)); - if (configurationAssemblies.Contains(typeof(LoggerFilterConfiguration).GetTypeInfo().Assembly)) - found.AddRange(SurrogateConfigurationMethods.Filter); + static IReadOnlyCollection FindSinkConfigurationMethods(IReadOnlyCollection configurationAssemblies) + { + var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerSinkConfiguration)); + if (configurationAssemblies.Contains(typeof(LoggerSinkConfiguration).GetTypeInfo().Assembly)) + found.AddRange(SurrogateConfigurationMethods.WriteTo); - return found; - } + return found; + } - static IReadOnlyCollection FindDestructureConfigurationMethods(IReadOnlyCollection configurationAssemblies) - { - var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerDestructuringConfiguration)); - if (configurationAssemblies.Contains(typeof(LoggerDestructuringConfiguration).GetTypeInfo().Assembly)) - found.AddRange(SurrogateConfigurationMethods.Destructure); + static IReadOnlyCollection FindAuditSinkConfigurationMethods(IReadOnlyCollection configurationAssemblies) + { + var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerAuditSinkConfiguration)); + if (configurationAssemblies.Contains(typeof(LoggerAuditSinkConfiguration).GetTypeInfo().Assembly)) + found.AddRange(SurrogateConfigurationMethods.AuditTo); + return found; + } - return found; - } + static IReadOnlyCollection FindFilterConfigurationMethods(IReadOnlyCollection configurationAssemblies) + { + var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerFilterConfiguration)); + if (configurationAssemblies.Contains(typeof(LoggerFilterConfiguration).GetTypeInfo().Assembly)) + found.AddRange(SurrogateConfigurationMethods.Filter); - static IReadOnlyCollection FindEventEnricherConfigurationMethods(IReadOnlyCollection configurationAssemblies) - { - var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerEnrichmentConfiguration)); - if (configurationAssemblies.Contains(typeof(LoggerEnrichmentConfiguration).GetTypeInfo().Assembly)) - found.AddRange(SurrogateConfigurationMethods.Enrich); + return found; + } - return found; - } + static IReadOnlyCollection FindDestructureConfigurationMethods(IReadOnlyCollection configurationAssemblies) + { + var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerDestructuringConfiguration)); + if (configurationAssemblies.Contains(typeof(LoggerDestructuringConfiguration).GetTypeInfo().Assembly)) + found.AddRange(SurrogateConfigurationMethods.Destructure); + + return found; + } + + static IReadOnlyCollection FindEventEnricherConfigurationMethods(IReadOnlyCollection configurationAssemblies) + { + var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerEnrichmentConfiguration)); + if (configurationAssemblies.Contains(typeof(LoggerEnrichmentConfiguration).GetTypeInfo().Assembly)) + found.AddRange(SurrogateConfigurationMethods.Enrich); + + return found; + } - static List FindConfigurationExtensionMethods(IReadOnlyCollection configurationAssemblies, Type configType) + static List FindConfigurationExtensionMethods(IReadOnlyCollection configurationAssemblies, Type configType) + { + // ExtensionAttribute can be polyfilled to support extension methods + static bool HasCustomExtensionAttribute(MethodInfo m) { - // ExtensionAttribute can be polyfilled to support extension methods - static bool HasCustomExtensionAttribute(MethodInfo m) + try { - try - { - return m.CustomAttributes.Any(a => a.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute"); - } - catch (CustomAttributeFormatException) - { - return false; - } + return m.CustomAttributes.Any(a => a.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute"); + } + catch (CustomAttributeFormatException) + { + return false; } - - return configurationAssemblies - .SelectMany(a => a.ExportedTypes - .Select(t => t.GetTypeInfo()) - .Where(t => t.IsSealed && t.IsAbstract && !t.IsNested)) - .SelectMany(t => t.DeclaredMethods) - .Where(m => m.IsStatic && m.IsPublic && (m.IsDefined(typeof(ExtensionAttribute), false) || HasCustomExtensionAttribute(m))) - .Where(m => m.GetParameters()[0].ParameterType == configType) - .ToList(); } - internal static bool IsValidSwitchName(string input) - { - return Regex.IsMatch(input, LevelSwitchNameRegex); - } + return configurationAssemblies + .SelectMany(a => a.ExportedTypes + .Select(t => t.GetTypeInfo()) + .Where(t => t.IsSealed && t.IsAbstract && !t.IsNested)) + .SelectMany(t => t.DeclaredMethods) + .Where(m => m.IsStatic && m.IsPublic && (m.IsDefined(typeof(ExtensionAttribute), false) || HasCustomExtensionAttribute(m))) + .Where(m => m.GetParameters()[0].ParameterType == configType) + .ToList(); + } - static LogEventLevel ParseLogEventLevel(string value) - { - if (!Enum.TryParse(value, ignoreCase: true, out LogEventLevel parsedLevel)) - throw new InvalidOperationException($"The value {value} is not a valid Serilog level."); - return parsedLevel; - } + internal static bool IsValidSwitchName(string input) + { + return Regex.IsMatch(input, LevelSwitchNameRegex); + } + + static LogEventLevel ParseLogEventLevel(string value) + { + if (!Enum.TryParse(value, ignoreCase: true, out LogEventLevel parsedLevel)) + throw new InvalidOperationException($"The value {value} is not a valid Serilog level."); + return parsedLevel; } } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/IConfigurationArgumentValue.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/IConfigurationArgumentValue.cs index e24e8e02..d39015cd 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/IConfigurationArgumentValue.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/IConfigurationArgumentValue.cs @@ -1,9 +1,6 @@ -using System; +namespace Serilog.Settings.Configuration; -namespace Serilog.Settings.Configuration +interface IConfigurationArgumentValue { - interface IConfigurationArgumentValue - { - object ConvertTo(Type toType, ResolutionContext resolutionContext); - } + object ConvertTo(Type toType, ResolutionContext resolutionContext); } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/IConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/IConfigurationReader.cs index af815af2..f3dd36ff 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/IConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/IConfigurationReader.cs @@ -1,10 +1,9 @@ using Serilog.Configuration; -namespace Serilog.Settings.Configuration +namespace Serilog.Settings.Configuration; + +interface IConfigurationReader : ILoggerSettings { - interface IConfigurationReader : ILoggerSettings - { - void ApplySinks(LoggerSinkConfiguration loggerSinkConfiguration); - void ApplyEnrichment(LoggerEnrichmentConfiguration loggerEnrichmentConfiguration); - } + void ApplySinks(LoggerSinkConfiguration loggerSinkConfiguration); + void ApplyEnrichment(LoggerEnrichmentConfiguration loggerEnrichmentConfiguration); } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/LoggingFilterSwitchProxy.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/LoggingFilterSwitchProxy.cs index 676a2dd8..a35d6b1c 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/LoggingFilterSwitchProxy.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/LoggingFilterSwitchProxy.cs @@ -1,50 +1,47 @@ -using System; +namespace Serilog.Settings.Configuration; -namespace Serilog.Settings.Configuration +class LoggingFilterSwitchProxy { - class LoggingFilterSwitchProxy + readonly Action _setProxy; + readonly Func _getProxy; + + LoggingFilterSwitchProxy(object realSwitch) { - readonly Action _setProxy; - readonly Func _getProxy; + RealSwitch = realSwitch ?? throw new ArgumentNullException(nameof(realSwitch)); - LoggingFilterSwitchProxy(object realSwitch) - { - RealSwitch = realSwitch ?? throw new ArgumentNullException(nameof(realSwitch)); + var type = realSwitch.GetType(); + var expressionProperty = type.GetProperty("Expression") ?? throw new MissingMemberException(type.FullName, "Expression"); + + _setProxy = (Action)Delegate.CreateDelegate( + typeof(Action), + realSwitch, + expressionProperty.GetSetMethod()); - var type = realSwitch.GetType(); - var expressionProperty = type.GetProperty("Expression") ?? throw new MissingMemberException(type.FullName, "Expression"); + _getProxy = (Func)Delegate.CreateDelegate( + typeof(Func), + realSwitch, + expressionProperty.GetGetMethod()); + } - _setProxy = (Action)Delegate.CreateDelegate( - typeof(Action), - realSwitch, - expressionProperty.GetSetMethod()); + public object RealSwitch { get; } - _getProxy = (Func)Delegate.CreateDelegate( - typeof(Func), - realSwitch, - expressionProperty.GetGetMethod()); - } + public string Expression + { + get => _getProxy(); + set => _setProxy(value); + } - public object RealSwitch { get; } + public static LoggingFilterSwitchProxy Create(string expression = null) + { + var filterSwitchType = + Type.GetType("Serilog.Expressions.LoggingFilterSwitch, Serilog.Expressions") ?? + Type.GetType("Serilog.Filters.Expressions.LoggingFilterSwitch, Serilog.Filters.Expressions"); - public string Expression + if (filterSwitchType is null) { - get => _getProxy(); - set => _setProxy(value); + return null; } - public static LoggingFilterSwitchProxy Create(string expression = null) - { - var filterSwitchType = - Type.GetType("Serilog.Expressions.LoggingFilterSwitch, Serilog.Expressions") ?? - Type.GetType("Serilog.Filters.Expressions.LoggingFilterSwitch, Serilog.Filters.Expressions"); - - if (filterSwitchType is null) - { - return null; - } - - return new LoggingFilterSwitchProxy(Activator.CreateInstance(filterSwitchType, expression)); - } + return new LoggingFilterSwitchProxy(Activator.CreateInstance(filterSwitchType, expression)); } } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs index d22f78b1..87d8ace3 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -8,234 +5,233 @@ using Serilog.Configuration; -namespace Serilog.Settings.Configuration +namespace Serilog.Settings.Configuration; + +class ObjectArgumentValue : IConfigurationArgumentValue { - class ObjectArgumentValue : IConfigurationArgumentValue + readonly IConfigurationSection _section; + readonly IReadOnlyCollection _configurationAssemblies; + + public ObjectArgumentValue(IConfigurationSection section, IReadOnlyCollection configurationAssemblies) { - readonly IConfigurationSection _section; - readonly IReadOnlyCollection _configurationAssemblies; + _section = section ?? throw new ArgumentNullException(nameof(section)); - public ObjectArgumentValue(IConfigurationSection section, IReadOnlyCollection configurationAssemblies) - { - _section = section ?? throw new ArgumentNullException(nameof(section)); + // used by nested logger configurations to feed a new pass by ConfigurationReader + _configurationAssemblies = configurationAssemblies ?? throw new ArgumentNullException(nameof(configurationAssemblies)); + } - // used by nested logger configurations to feed a new pass by ConfigurationReader - _configurationAssemblies = configurationAssemblies ?? throw new ArgumentNullException(nameof(configurationAssemblies)); - } + public object ConvertTo(Type toType, ResolutionContext resolutionContext) + { + // return the entire section for internal processing + if (toType == typeof(IConfigurationSection)) return _section; - public object ConvertTo(Type toType, ResolutionContext resolutionContext) + // 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<>)) { - // return the entire section for internal processing - if (toType == typeof(IConfigurationSection)) return _section; + var configType = typeInfo.GenericTypeArguments[0]; + IConfigurationReader configReader = new ConfigurationReader(_section, _configurationAssemblies, resolutionContext); - // 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<>)) + return configType switch { - var configType = typeInfo.GenericTypeArguments[0]; - IConfigurationReader configReader = new ConfigurationReader(_section, _configurationAssemblies, resolutionContext); - - return configType switch - { - _ when configType == typeof(LoggerConfiguration) => new Action(configReader.Configure), - _ when configType == typeof(LoggerSinkConfiguration) => new Action(configReader.ApplySinks), - _ when configType == typeof(LoggerEnrichmentConfiguration) => new Action(configReader.ApplyEnrichment), - _ => throw new ArgumentException($"Configuration resolution for Action<{configType.Name}> parameter type at the path {_section.Path} is not implemented.") - }; - } + _ when configType == typeof(LoggerConfiguration) => new Action(configReader.Configure), + _ when configType == typeof(LoggerSinkConfiguration) => new Action(configReader.ApplySinks), + _ when configType == typeof(LoggerEnrichmentConfiguration) => new Action(configReader.ApplyEnrichment), + _ => throw new ArgumentException($"Configuration resolution for Action<{configType.Name}> parameter type at the path {_section.Path} is not implemented.") + }; + } - if (toType.IsArray) - return CreateArray(); + if (toType.IsArray) + return CreateArray(); - if (IsContainer(toType, out var elementType) && TryCreateContainer(out var container)) - return container; + if (IsContainer(toType, out var elementType) && TryCreateContainer(out var container)) + return container; - if (TryBuildCtorExpression(_section, toType, resolutionContext, out var ctorExpression)) - { - return Expression.Lambda>(ctorExpression).Compile().Invoke(); - } + if (TryBuildCtorExpression(_section, toType, resolutionContext, out var ctorExpression)) + { + return Expression.Lambda>(ctorExpression).Compile().Invoke(); + } - // MS Config binding can work with a limited set of primitive types and collections - return _section.Get(toType); + // MS Config binding can work with a limited set of primitive types and collections + return _section.Get(toType); - object CreateArray() + object CreateArray() + { + var arrayElementType = toType.GetElementType()!; + var configurationElements = _section.GetChildren().ToArray(); + var array = Array.CreateInstance(arrayElementType, configurationElements.Length); + for (int i = 0; i < configurationElements.Length; ++i) { - var arrayElementType = toType.GetElementType()!; - var configurationElements = _section.GetChildren().ToArray(); - var array = Array.CreateInstance(arrayElementType, configurationElements.Length); - for (int i = 0; i < configurationElements.Length; ++i) - { - var argumentValue = ConfigurationReader.GetArgumentValue(configurationElements[i], _configurationAssemblies); - var value = argumentValue.ConvertTo(arrayElementType, resolutionContext); - array.SetValue(value, i); - } - - return array; + var argumentValue = ConfigurationReader.GetArgumentValue(configurationElements[i], _configurationAssemblies); + var value = argumentValue.ConvertTo(arrayElementType, resolutionContext); + array.SetValue(value, i); } - bool TryCreateContainer(out object result) - { - result = null; + return array; + } - if (toType.GetConstructor(Type.EmptyTypes) == null) - return false; + bool TryCreateContainer(out object result) + { + result = null; - // https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers#collection-initializers - var addMethod = toType.GetMethods().FirstOrDefault(m => !m.IsStatic && m.Name == "Add" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == elementType); - if (addMethod == null) - return false; + if (toType.GetConstructor(Type.EmptyTypes) == null) + return false; - var configurationElements = _section.GetChildren().ToArray(); - result = Activator.CreateInstance(toType); + // https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers#collection-initializers + var addMethod = toType.GetMethods().FirstOrDefault(m => !m.IsStatic && m.Name == "Add" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == elementType); + if (addMethod == null) + return false; - for (int i = 0; i < configurationElements.Length; ++i) - { - var argumentValue = ConfigurationReader.GetArgumentValue(configurationElements[i], _configurationAssemblies); - var value = argumentValue.ConvertTo(elementType, resolutionContext); - addMethod.Invoke(result, new[] { value }); - } + var configurationElements = _section.GetChildren().ToArray(); + result = Activator.CreateInstance(toType); - return true; + for (int i = 0; i < configurationElements.Length; ++i) + { + var argumentValue = ConfigurationReader.GetArgumentValue(configurationElements[i], _configurationAssemblies); + var value = argumentValue.ConvertTo(elementType, resolutionContext); + addMethod.Invoke(result, new[] { value }); } + + return true; } + } - internal static bool TryBuildCtorExpression( - IConfigurationSection section, Type parameterType, ResolutionContext resolutionContext, out NewExpression ctorExpression) - { - ctorExpression = null; + internal static bool TryBuildCtorExpression( + IConfigurationSection section, Type parameterType, ResolutionContext resolutionContext, out NewExpression ctorExpression) + { + ctorExpression = null; - var typeDirective = section.GetValue("$type") switch + var typeDirective = section.GetValue("$type") switch + { + not null => "$type", + null => section.GetValue("type") switch { - not null => "$type", - null => section.GetValue("type") switch - { - not null => "type", - null => null, - }, - }; + not null => "type", + null => null, + }, + }; - var type = typeDirective switch - { - not null => Type.GetType(section.GetValue(typeDirective), throwOnError: false), - null => parameterType, - }; + var type = typeDirective switch + { + not null => Type.GetType(section.GetValue(typeDirective), throwOnError: false), + null => parameterType, + }; - if (type is null or { IsAbstract: true }) - { - return false; - } + if (type is null or { IsAbstract: true }) + { + return false; + } - var suppliedArguments = section.GetChildren().Where(s => s.Key != typeDirective) - .ToDictionary(s => s.Key, StringComparer.OrdinalIgnoreCase); + var suppliedArguments = section.GetChildren().Where(s => s.Key != typeDirective) + .ToDictionary(s => s.Key, StringComparer.OrdinalIgnoreCase); - if (suppliedArguments.Count == 0 && - type.GetConstructor(Type.EmptyTypes) is ConstructorInfo parameterlessCtor) - { - ctorExpression = Expression.New(parameterlessCtor); - return true; - } + if (suppliedArguments.Count == 0 && + type.GetConstructor(Type.EmptyTypes) is ConstructorInfo parameterlessCtor) + { + ctorExpression = Expression.New(parameterlessCtor); + return true; + } - var ctor = - (from c in type.GetConstructors() - from p in c.GetParameters() - let argumentBindResult = suppliedArguments.TryGetValue(p.Name, out var argValue) switch - { - true => new { success = true, hasMatch = true, value = (object)argValue }, - false => p.HasDefaultValue switch - { - true => new { success = true, hasMatch = false, value = p.DefaultValue }, - false => new { success = false, hasMatch = false, value = (object)null }, - }, - } - group new { argumentBindResult, p.ParameterType } by c into gr - where gr.All(z => z.argumentBindResult.success) - let matchedArgs = gr.Where(z => z.argumentBindResult.hasMatch).ToList() - orderby matchedArgs.Count descending, - matchedArgs.Count(p => p.ParameterType == typeof(string)) descending - select new + var ctor = + (from c in type.GetConstructors() + from p in c.GetParameters() + let argumentBindResult = suppliedArguments.TryGetValue(p.Name, out var argValue) switch + { + true => new { success = true, hasMatch = true, value = (object)argValue }, + false => p.HasDefaultValue switch { - ConstructorInfo = gr.Key, - ArgumentValues = gr.Select(z => new { Value = z.argumentBindResult.value, Type = z.ParameterType }) - .ToList() - }).FirstOrDefault(); + true => new { success = true, hasMatch = false, value = p.DefaultValue }, + false => new { success = false, hasMatch = false, value = (object)null }, + }, + } + group new { argumentBindResult, p.ParameterType } by c into gr + where gr.All(z => z.argumentBindResult.success) + let matchedArgs = gr.Where(z => z.argumentBindResult.hasMatch).ToList() + orderby matchedArgs.Count descending, + matchedArgs.Count(p => p.ParameterType == typeof(string)) descending + select new + { + ConstructorInfo = gr.Key, + ArgumentValues = gr.Select(z => new { Value = z.argumentBindResult.value, Type = z.ParameterType }) + .ToList() + }).FirstOrDefault(); + + if (ctor is null) + { + return false; + } - if (ctor is null) + var ctorArguments = new List(); + foreach (var argumentValue in ctor.ArgumentValues) + { + if (TryBindToCtorArgument(argumentValue.Value, argumentValue.Type, resolutionContext, out var argumentExpression)) { - return false; + ctorArguments.Add(argumentExpression); } - - var ctorArguments = new List(); - foreach (var argumentValue in ctor.ArgumentValues) + else { - if (TryBindToCtorArgument(argumentValue.Value, argumentValue.Type, resolutionContext, out var argumentExpression)) - { - ctorArguments.Add(argumentExpression); - } - else - { - return false; - } + return false; } + } - ctorExpression = Expression.New(ctor.ConstructorInfo, ctorArguments); - return true; + ctorExpression = Expression.New(ctor.ConstructorInfo, ctorArguments); + return true; - static bool TryBindToCtorArgument(object value, Type type, ResolutionContext resolutionContext, out Expression argumentExpression) - { - argumentExpression = null; + static bool TryBindToCtorArgument(object value, Type type, ResolutionContext resolutionContext, out Expression argumentExpression) + { + argumentExpression = null; - if (value is IConfigurationSection s) + if (value is IConfigurationSection s) + { + if (s.Value is string argValue) { - if (s.Value is string argValue) + var stringArgumentValue = new StringArgumentValue(argValue); + try { - var stringArgumentValue = new StringArgumentValue(argValue); - try - { - argumentExpression = Expression.Constant( - stringArgumentValue.ConvertTo(type, resolutionContext), - type); - - return true; - } - catch (Exception) - { - return false; - } + argumentExpression = Expression.Constant( + stringArgumentValue.ConvertTo(type, resolutionContext), + type); + + return true; } - else if (s.GetChildren().Any()) + catch (Exception) { - if (TryBuildCtorExpression(s, type, resolutionContext, out var ctorExpression)) - { - argumentExpression = ctorExpression; - return true; - } - return false; } } + else if (s.GetChildren().Any()) + { + if (TryBuildCtorExpression(s, type, resolutionContext, out var ctorExpression)) + { + argumentExpression = ctorExpression; + return true; + } - argumentExpression = Expression.Constant(value, type); - return true; + return false; + } } + + argumentExpression = Expression.Constant(value, type); + return true; } + } - static bool IsContainer(Type type, out Type elementType) + static bool IsContainer(Type type, out Type elementType) + { + elementType = null; + foreach (var iface in type.GetInterfaces()) { - elementType = null; - foreach (var iface in type.GetInterfaces()) + if (iface.IsGenericType) { - if (iface.IsGenericType) + if (iface.GetGenericTypeDefinition() == typeof(IEnumerable<>)) { - if (iface.GetGenericTypeDefinition() == typeof(IEnumerable<>)) - { - elementType = iface.GetGenericArguments()[0]; - return true; - } + elementType = iface.GetGenericArguments()[0]; + return true; } } - - return false; } + + return false; } } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs index 2963a64e..1db02a59 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs @@ -1,85 +1,82 @@ -using System; -using System.Collections.Generic; -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration; using Serilog.Core; -namespace Serilog.Settings.Configuration +namespace Serilog.Settings.Configuration; + +/// +/// Keeps track of available elements that are useful when resolving values in the settings system. +/// +sealed class ResolutionContext { + readonly IDictionary _declaredLevelSwitches; + readonly IDictionary _declaredFilterSwitches; + readonly IConfiguration _appConfiguration; + + public ResolutionContext(IConfiguration appConfiguration = null) + { + _declaredLevelSwitches = new Dictionary(); + _declaredFilterSwitches = new Dictionary(); + _appConfiguration = appConfiguration; + } + /// - /// Keeps track of available elements that are useful when resolving values in the settings system. + /// Looks up a switch in the declared LoggingLevelSwitches /// - sealed class ResolutionContext + /// the name of a switch to look up + /// the LoggingLevelSwitch registered with the name + /// if no switch has been registered with + public LoggingLevelSwitch LookUpLevelSwitchByName(string switchName) { - readonly IDictionary _declaredLevelSwitches; - readonly IDictionary _declaredFilterSwitches; - readonly IConfiguration _appConfiguration; - - public ResolutionContext(IConfiguration appConfiguration = null) + if (_declaredLevelSwitches.TryGetValue(switchName, out var levelSwitch)) { - _declaredLevelSwitches = new Dictionary(); - _declaredFilterSwitches = new Dictionary(); - _appConfiguration = appConfiguration; + return levelSwitch; } - /// - /// Looks up a switch in the declared LoggingLevelSwitches - /// - /// the name of a switch to look up - /// the LoggingLevelSwitch registered with the name - /// if no switch has been registered with - public LoggingLevelSwitch LookUpLevelSwitchByName(string switchName) - { - if (_declaredLevelSwitches.TryGetValue(switchName, out var levelSwitch)) - { - return levelSwitch; - } - - throw new InvalidOperationException($"No LoggingLevelSwitch has been declared with name \"{switchName}\". You might be missing a section \"LevelSwitches\":{{\"{switchName}\":\"InitialLevel\"}}"); - } + throw new InvalidOperationException($"No LoggingLevelSwitch has been declared with name \"{switchName}\". You might be missing a section \"LevelSwitches\":{{\"{switchName}\":\"InitialLevel\"}}"); + } - public LoggingFilterSwitchProxy LookUpFilterSwitchByName(string switchName) + public LoggingFilterSwitchProxy LookUpFilterSwitchByName(string switchName) + { + if (_declaredFilterSwitches.TryGetValue(switchName, out var filterSwitch)) { - if (_declaredFilterSwitches.TryGetValue(switchName, out var filterSwitch)) - { - return filterSwitch; - } - - throw new InvalidOperationException($"No LoggingFilterSwitch has been declared with name \"{switchName}\". You might be missing a section \"FilterSwitches\":{{\"{switchName}\":\"{{FilterExpression}}\"}}"); + return filterSwitch; } - public bool HasAppConfiguration => _appConfiguration != null; + throw new InvalidOperationException($"No LoggingFilterSwitch has been declared with name \"{switchName}\". You might be missing a section \"FilterSwitches\":{{\"{switchName}\":\"{{FilterExpression}}\"}}"); + } - public IConfiguration AppConfiguration + public bool HasAppConfiguration => _appConfiguration != null; + + public IConfiguration AppConfiguration + { + get { - get + if (!HasAppConfiguration) { - if (!HasAppConfiguration) - { - throw new InvalidOperationException("AppConfiguration is not available"); - } - - return _appConfiguration; + throw new InvalidOperationException("AppConfiguration is not available"); } - } - public void AddLevelSwitch(string levelSwitchName, LoggingLevelSwitch levelSwitch) - { - if (levelSwitchName == null) throw new ArgumentNullException(nameof(levelSwitchName)); - if (levelSwitch == null) throw new ArgumentNullException(nameof(levelSwitch)); - _declaredLevelSwitches[ToSwitchReference(levelSwitchName)] = levelSwitch; + return _appConfiguration; } + } - public void AddFilterSwitch(string filterSwitchName, LoggingFilterSwitchProxy filterSwitch) - { - if (filterSwitchName == null) throw new ArgumentNullException(nameof(filterSwitchName)); - if (filterSwitch == null) throw new ArgumentNullException(nameof(filterSwitch)); - _declaredFilterSwitches[ToSwitchReference(filterSwitchName)] = filterSwitch; - } + public void AddLevelSwitch(string levelSwitchName, LoggingLevelSwitch levelSwitch) + { + if (levelSwitchName == null) throw new ArgumentNullException(nameof(levelSwitchName)); + if (levelSwitch == null) throw new ArgumentNullException(nameof(levelSwitch)); + _declaredLevelSwitches[ToSwitchReference(levelSwitchName)] = levelSwitch; + } - string ToSwitchReference(string switchName) - { - return switchName.StartsWith("$") ? switchName : $"${switchName}"; - } + public void AddFilterSwitch(string filterSwitchName, LoggingFilterSwitchProxy filterSwitch) + { + if (filterSwitchName == null) throw new ArgumentNullException(nameof(filterSwitchName)); + if (filterSwitch == null) throw new ArgumentNullException(nameof(filterSwitch)); + _declaredFilterSwitches[ToSwitchReference(filterSwitchName)] = filterSwitch; + } + + string ToSwitchReference(string switchName) + { + return switchName.StartsWith("$") ? switchName : $"${switchName}"; } } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs index 6d946393..c15c4b17 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs @@ -1,202 +1,198 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; +using System.Reflection; using System.Text.RegularExpressions; using Serilog.Core; -namespace Serilog.Settings.Configuration +namespace Serilog.Settings.Configuration; + +class StringArgumentValue : IConfigurationArgumentValue { - class StringArgumentValue : IConfigurationArgumentValue - { - readonly string _providedValue; + readonly string _providedValue; + + static readonly Regex StaticMemberAccessorRegex = new Regex("^(?[^:]+)::(?[A-Za-z][A-Za-z0-9]*)(?[^:]*)$"); - static readonly Regex StaticMemberAccessorRegex = new Regex("^(?[^:]+)::(?[A-Za-z][A-Za-z0-9]*)(?[^:]*)$"); + public StringArgumentValue(string providedValue) + { + _providedValue = providedValue ?? throw new ArgumentNullException(nameof(providedValue)); + } - public StringArgumentValue(string providedValue) + static readonly Dictionary> ExtendedTypeConversions = new Dictionary> { - _providedValue = providedValue ?? throw new ArgumentNullException(nameof(providedValue)); - } + { typeof(Uri), s => new Uri(s) }, + { typeof(TimeSpan), s => TimeSpan.Parse(s) }, + { typeof(Type), s => Type.GetType(s, throwOnError:true) }, + }; - static readonly Dictionary> ExtendedTypeConversions = new Dictionary> - { - { typeof(Uri), s => new Uri(s) }, - { typeof(TimeSpan), s => TimeSpan.Parse(s) }, - { typeof(Type), s => Type.GetType(s, throwOnError:true) }, - }; + public object ConvertTo(Type toType, ResolutionContext resolutionContext) + { + var argumentValue = Environment.ExpandEnvironmentVariables(_providedValue); - public object ConvertTo(Type toType, ResolutionContext resolutionContext) + if (toType == typeof(LoggingLevelSwitch)) { - var argumentValue = Environment.ExpandEnvironmentVariables(_providedValue); - - if (toType == typeof(LoggingLevelSwitch)) - { - return resolutionContext.LookUpLevelSwitchByName(argumentValue); - } + return resolutionContext.LookUpLevelSwitchByName(argumentValue); + } - if (toType.FullName == "Serilog.Expressions.LoggingFilterSwitch" || - toType.FullName == "Serilog.Filters.Expressions.LoggingFilterSwitch") - { - return resolutionContext.LookUpFilterSwitchByName(argumentValue).RealSwitch; - } + if (toType.FullName == "Serilog.Expressions.LoggingFilterSwitch" || + toType.FullName == "Serilog.Filters.Expressions.LoggingFilterSwitch") + { + return resolutionContext.LookUpFilterSwitchByName(argumentValue).RealSwitch; + } - var toTypeInfo = toType.GetTypeInfo(); - if (toTypeInfo.IsGenericType && toType.GetGenericTypeDefinition() == typeof(Nullable<>)) - { - if (string.IsNullOrEmpty(argumentValue)) - return null; + var toTypeInfo = toType.GetTypeInfo(); + if (toTypeInfo.IsGenericType && toType.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + if (string.IsNullOrEmpty(argumentValue)) + return null; - // unwrap Nullable<> type since we're not handling null situations - toType = toTypeInfo.GenericTypeArguments[0]; - toTypeInfo = toType.GetTypeInfo(); - } + // unwrap Nullable<> type since we're not handling null situations + toType = toTypeInfo.GenericTypeArguments[0]; + toTypeInfo = toType.GetTypeInfo(); + } - if (toTypeInfo.IsEnum) - return Enum.Parse(toType, argumentValue); + if (toTypeInfo.IsEnum) + return Enum.Parse(toType, argumentValue); - var convertor = ExtendedTypeConversions - .Where(t => t.Key.GetTypeInfo().IsAssignableFrom(toTypeInfo)) - .Select(t => t.Value) - .FirstOrDefault(); + var convertor = ExtendedTypeConversions + .Where(t => t.Key.GetTypeInfo().IsAssignableFrom(toTypeInfo)) + .Select(t => t.Value) + .FirstOrDefault(); - if (convertor != null) - return convertor(argumentValue); + if (convertor != null) + return convertor(argumentValue); - if (!string.IsNullOrWhiteSpace(argumentValue)) + if (!string.IsNullOrWhiteSpace(argumentValue)) + { + // check if value looks like a static property or field directive + // like "Namespace.TypeName::StaticProperty, AssemblyName" + if (toType != typeof(string) && + TryParseStaticMemberAccessor(argumentValue, out var accessorTypeName, out var memberName)) { - // check if value looks like a static property or field directive - // like "Namespace.TypeName::StaticProperty, AssemblyName" - if (toType != typeof(string) && - TryParseStaticMemberAccessor(argumentValue, out var accessorTypeName, out var memberName)) + var accessorType = Type.GetType(accessorTypeName, throwOnError: true); + + // if delegate, look for a method and then construct a delegate + if (typeof(Delegate).IsAssignableFrom(toType) || typeof(MethodInfo) == toType) { - var accessorType = Type.GetType(accessorTypeName, throwOnError: true); + var methodCandidates = accessorType.GetTypeInfo().DeclaredMethods + .Where(x => x.Name == memberName) + .Where(x => x.IsPublic) + .Where(x => !x.IsGenericMethod) + .Where(x => x.IsStatic) + .ToList(); - // if delegate, look for a method and then construct a delegate - if (typeof(Delegate).IsAssignableFrom(toType) || typeof(MethodInfo) == toType) + if (methodCandidates.Count > 1) { - var methodCandidates = accessorType.GetTypeInfo().DeclaredMethods - .Where(x => x.Name == memberName) - .Where(x => x.IsPublic) - .Where(x => !x.IsGenericMethod) - .Where(x => x.IsStatic) + // filter possible method overloads + + var delegateSig = toType.GetMethod("Invoke"); + var delegateParameters = delegateSig!.GetParameters().Select(x => x.ParameterType); + methodCandidates = methodCandidates + .Where(x => x.ReturnType == delegateSig.ReturnType && x.GetParameters().Select(y => y.ParameterType).SequenceEqual(delegateParameters)) .ToList(); + } - if (methodCandidates.Count > 1) - { - // filter possible method overloads + var methodCandidate = methodCandidates.SingleOrDefault(); - var delegateSig = toType.GetMethod("Invoke"); - var delegateParameters = delegateSig!.GetParameters().Select(x => x.ParameterType); - methodCandidates = methodCandidates - .Where(x => x.ReturnType == delegateSig.ReturnType && x.GetParameters().Select(y => y.ParameterType).SequenceEqual(delegateParameters)) - .ToList(); + if (methodCandidate != null) + { + if (typeof(MethodInfo) == toType) + { + return methodCandidate; } - - var methodCandidate = methodCandidates.SingleOrDefault(); - - if (methodCandidate != null) + else { - if (typeof(MethodInfo) == toType) - { - return methodCandidate; - } - else - { - return methodCandidate.CreateDelegate(toType); - } + return methodCandidate.CreateDelegate(toType); } } + } - // is there a public static property with that name ? - var publicStaticPropertyInfo = accessorType.GetTypeInfo().DeclaredProperties - .Where(x => x.Name == memberName) - .Where(x => x.GetMethod != null) - .Where(x => x.GetMethod.IsPublic) - .FirstOrDefault(x => x.GetMethod.IsStatic); - - if (publicStaticPropertyInfo != null) - { - return publicStaticPropertyInfo.GetValue(null); // static property, no instance to pass - } + // is there a public static property with that name ? + var publicStaticPropertyInfo = accessorType.GetTypeInfo().DeclaredProperties + .Where(x => x.Name == memberName) + .Where(x => x.GetMethod != null) + .Where(x => x.GetMethod.IsPublic) + .FirstOrDefault(x => x.GetMethod.IsStatic); - // no property ? look for a public static field - var publicStaticFieldInfo = accessorType.GetTypeInfo().DeclaredFields - .Where(x => x.Name == memberName) - .Where(x => x.IsPublic) - .FirstOrDefault(x => x.IsStatic); + if (publicStaticPropertyInfo != null) + { + return publicStaticPropertyInfo.GetValue(null); // static property, no instance to pass + } - if (publicStaticFieldInfo != null) - { - return publicStaticFieldInfo.GetValue(null); // static field, no instance to pass - } + // no property ? look for a public static field + var publicStaticFieldInfo = accessorType.GetTypeInfo().DeclaredFields + .Where(x => x.Name == memberName) + .Where(x => x.IsPublic) + .FirstOrDefault(x => x.IsStatic); - throw new InvalidOperationException($"Could not find a public static property or field with name `{memberName}` on type `{accessorTypeName}`"); + if (publicStaticFieldInfo != null) + { + return publicStaticFieldInfo.GetValue(null); // static field, no instance to pass } - if (toTypeInfo.IsInterface || toTypeInfo.IsAbstract) + throw new InvalidOperationException($"Could not find a public static property or field with name `{memberName}` on type `{accessorTypeName}`"); + } + + if (toTypeInfo.IsInterface || toTypeInfo.IsAbstract) + { + // maybe it's the assembly-qualified type name of a concrete implementation + // with a default constructor + var type = FindType(argumentValue.Trim()); + if (type == null) { - // maybe it's the assembly-qualified type name of a concrete implementation - // with a default constructor - var type = FindType(argumentValue.Trim()); - if (type == null) - { - throw new InvalidOperationException($"Type {argumentValue} was not found."); - } + throw new InvalidOperationException($"Type {argumentValue} was not found."); + } - var ctor = type.GetTypeInfo().DeclaredConstructors.Where(ci => !ci.IsStatic).FirstOrDefault(ci => - { - var parameters = ci.GetParameters(); - return parameters.Length == 0 || parameters.All(pi => pi.HasDefaultValue); - }); + var ctor = type.GetTypeInfo().DeclaredConstructors.Where(ci => !ci.IsStatic).FirstOrDefault(ci => + { + var parameters = ci.GetParameters(); + return parameters.Length == 0 || parameters.All(pi => pi.HasDefaultValue); + }); - if (ctor == null) - throw new InvalidOperationException($"A default constructor was not found on {type.FullName}."); + if (ctor == null) + throw new InvalidOperationException($"A default constructor was not found on {type.FullName}."); - var call = ctor.GetParameters().Select(pi => pi.DefaultValue).ToArray(); - return ctor.Invoke(call); - } + var call = ctor.GetParameters().Select(pi => pi.DefaultValue).ToArray(); + return ctor.Invoke(call); } - - return Convert.ChangeType(argumentValue, toType); } - internal static Type FindType(string typeName) + return Convert.ChangeType(argumentValue, toType); + } + + internal static Type FindType(string typeName) + { + var type = Type.GetType(typeName); + if (type == null) { - var type = Type.GetType(typeName); - if (type == null) + if (!typeName.Contains(',')) { - if (!typeName.Contains(',')) - { - type = Type.GetType($"{typeName}, Serilog"); - } + type = Type.GetType($"{typeName}, Serilog"); } - - return type; } - internal static bool TryParseStaticMemberAccessor(string input, out string accessorTypeName, out string memberName) + return type; + } + + internal static bool TryParseStaticMemberAccessor(string input, out string accessorTypeName, out string memberName) + { + if (input == null) { - if (input == null) - { - accessorTypeName = null; - memberName = null; - return false; - } - if (StaticMemberAccessorRegex.IsMatch(input)) - { - var match = StaticMemberAccessorRegex.Match(input); - var shortAccessorTypeName = match.Groups["shortTypeName"].Value; - var rawMemberName = match.Groups["memberName"].Value; - var extraQualifiers = match.Groups["typeNameExtraQualifiers"].Value; - - memberName = rawMemberName.Trim(); - accessorTypeName = shortAccessorTypeName.Trim() + extraQualifiers.TrimEnd(); - return true; - } accessorTypeName = null; memberName = null; return false; } + if (StaticMemberAccessorRegex.IsMatch(input)) + { + var match = StaticMemberAccessorRegex.Match(input); + var shortAccessorTypeName = match.Groups["shortTypeName"].Value; + var rawMemberName = match.Groups["memberName"].Value; + var extraQualifiers = match.Groups["typeNameExtraQualifiers"].Value; + + memberName = rawMemberName.Trim(); + accessorTypeName = shortAccessorTypeName.Trim() + extraQualifiers.TrimEnd(); + return true; + } + accessorTypeName = null; + memberName = null; + return false; } } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs index 4b460b32..cbc2869c 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs @@ -1,124 +1,120 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; +using System.Reflection; using Serilog.Configuration; using Serilog.Core; using Serilog.Events; -namespace Serilog.Settings.Configuration +namespace Serilog.Settings.Configuration; + +/// +/// Contains "fake extension" methods for the Serilog configuration API. +/// 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. +/// +/// +static class SurrogateConfigurationMethods { - /// - /// Contains "fake extension" methods for the Serilog configuration API. - /// 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. - /// - /// - static class SurrogateConfigurationMethods - { - static readonly Dictionary SurrogateMethodCandidates = typeof(SurrogateConfigurationMethods) - .GetTypeInfo().DeclaredMethods - .GroupBy(m => m.GetParameters().First().ParameterType) - .ToDictionary(g => g.Key, g => g.ToArray()); - - - 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 - 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 - // those methods are discovered through reflection by `SurrogateMethodCandidates` - // ReSharper has no way to see that they are actually used ... - - // .WriteTo... - // ======== - static LoggerConfiguration Sink( - LoggerSinkConfiguration loggerSinkConfiguration, - ILogEventSink sink, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - LoggingLevelSwitch levelSwitch = null) - => 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) - => auditSinkConfiguration.Sink(sink, restrictedToMinimumLevel, levelSwitch); - - static LoggerConfiguration Logger( - LoggerAuditSinkConfiguration auditSinkConfiguration, - Action configureLogger, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - LoggingLevelSwitch levelSwitch = null) - => auditSinkConfiguration.Logger(configureLogger, 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); - - 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); - - // .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 AtLevel( - LoggerEnrichmentConfiguration loggerEnrichmentConfiguration, - Action configureEnricher, - LogEventLevel enrichFromLevel = LevelAlias.Minimum, - LoggingLevelSwitch levelSwitch = null) - => levelSwitch != null ? loggerEnrichmentConfiguration.AtLevel(levelSwitch, configureEnricher) - : loggerEnrichmentConfiguration.AtLevel(enrichFromLevel, configureEnricher); - - static LoggerConfiguration FromLogContext(LoggerEnrichmentConfiguration loggerEnrichmentConfiguration) - => loggerEnrichmentConfiguration.FromLogContext(); - - // ReSharper restore UnusedMember.Local - } + static readonly Dictionary SurrogateMethodCandidates = typeof(SurrogateConfigurationMethods) + .GetTypeInfo().DeclaredMethods + .GroupBy(m => m.GetParameters().First().ParameterType) + .ToDictionary(g => g.Key, g => g.ToArray()); + + + 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 + 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 + // those methods are discovered through reflection by `SurrogateMethodCandidates` + // ReSharper has no way to see that they are actually used ... + + // .WriteTo... + // ======== + static LoggerConfiguration Sink( + LoggerSinkConfiguration loggerSinkConfiguration, + ILogEventSink sink, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch levelSwitch = null) + => 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) + => auditSinkConfiguration.Sink(sink, restrictedToMinimumLevel, levelSwitch); + + static LoggerConfiguration Logger( + LoggerAuditSinkConfiguration auditSinkConfiguration, + Action configureLogger, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch levelSwitch = null) + => auditSinkConfiguration.Logger(configureLogger, 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); + + 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); + + // .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 AtLevel( + LoggerEnrichmentConfiguration loggerEnrichmentConfiguration, + Action configureEnricher, + LogEventLevel enrichFromLevel = LevelAlias.Minimum, + LoggingLevelSwitch levelSwitch = null) + => levelSwitch != null ? loggerEnrichmentConfiguration.AtLevel(levelSwitch, configureEnricher) + : loggerEnrichmentConfiguration.AtLevel(enrichFromLevel, configureEnricher); + + static LoggerConfiguration FromLogContext(LoggerEnrichmentConfiguration loggerEnrichmentConfiguration) + => loggerEnrichmentConfiguration.FromLogContext(); + + // ReSharper restore UnusedMember.Local } diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs index 8639fc0f..bbee31b4 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs @@ -1,7 +1,4 @@ -using Xunit; -using System.Collections.Generic; -using System.Reflection; -using System.Linq; +using System.Reflection; using Microsoft.Extensions.Configuration; using Serilog.Events; using Serilog.Formatting; @@ -9,57 +6,57 @@ using Serilog.Settings.Configuration.Tests.Support; using static Serilog.Settings.Configuration.Tests.Support.ConfigurationReaderTestHelpers; -namespace Serilog.Settings.Configuration.Tests +namespace Serilog.Settings.Configuration.Tests; + +public class ConfigurationReaderTests { - public class ConfigurationReaderTests - { - readonly ConfigurationReader _configurationReader; + readonly ConfigurationReader _configurationReader; - public ConfigurationReaderTests() - { - _configurationReader = new ConfigurationReader( - JsonStringConfigSource.LoadSection(@"{ 'Serilog': { } }", "Serilog"), - AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies)); - } + public ConfigurationReaderTests() + { + _configurationReader = new ConfigurationReader( + JsonStringConfigSource.LoadSection(@"{ 'Serilog': { } }", "Serilog"), + AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies)); + } - [Fact] - public void WriteToSupportSimplifiedSyntax() - { - var json = @" + [Fact] + public void WriteToSupportSimplifiedSyntax() + { + var json = @" { 'WriteTo': [ 'LiterateConsole', 'DiagnosticTrace' ] }"; - var result = _configurationReader.GetMethodCalls(JsonStringConfigSource.LoadSection(json, "WriteTo")); - Assert.Equal(2, result.Count); - Assert.True(result.Contains("LiterateConsole")); - Assert.True(result.Contains("DiagnosticTrace")); + var result = _configurationReader.GetMethodCalls(JsonStringConfigSource.LoadSection(json, "WriteTo")); + Assert.Equal(2, result.Count); + Assert.True(result.Contains("LiterateConsole")); + Assert.True(result.Contains("DiagnosticTrace")); - Assert.Single(result["LiterateConsole"]); - Assert.Single(result["DiagnosticTrace"]); - } + Assert.Single(result["LiterateConsole"]); + Assert.Single(result["DiagnosticTrace"]); + } - [Fact] - public void WriteToSupportExpandedSyntaxWithoutArgs() - { - var json = @" + [Fact] + public void WriteToSupportExpandedSyntaxWithoutArgs() + { + var json = @" { 'WriteTo': [ { 'Name': 'LiterateConsole' }] }"; - var result = _configurationReader.GetMethodCalls(JsonStringConfigSource.LoadSection(json, "WriteTo")); - Assert.Equal(1, result.Count); - Assert.True(result.Contains("LiterateConsole")); + var result = _configurationReader.GetMethodCalls(JsonStringConfigSource.LoadSection(json, "WriteTo")); + Assert.Equal(1, result.Count); + Assert.True(result.Contains("LiterateConsole")); - Assert.Single(result["LiterateConsole"]); - } + Assert.Single(result["LiterateConsole"]); + } - [Fact] - public void WriteToSupportExpandedSyntaxWithArgs() - { - var json = @" + [Fact] + public void WriteToSupportExpandedSyntaxWithArgs() + { + var json = @" { 'WriteTo': [ { 'Name': 'LiterateConsole', @@ -69,24 +66,24 @@ public void WriteToSupportExpandedSyntaxWithArgs() }] }"; - var result = _configurationReader.GetMethodCalls(JsonStringConfigSource.LoadSection(json, "WriteTo")); + var result = _configurationReader.GetMethodCalls(JsonStringConfigSource.LoadSection(json, "WriteTo")); - Assert.Single(result); - Assert.True(result.Contains("LiterateConsole")); + Assert.Single(result); + Assert.True(result.Contains("LiterateConsole")); - Assert.Single(result["LiterateConsole"]); + Assert.Single(result["LiterateConsole"]); - var args = result["LiterateConsole"].Single().ToArray(); + var args = result["LiterateConsole"].Single().ToArray(); - Assert.Single(args); - Assert.Equal("outputTemplate", args[0].Key); - Assert.Equal("{Message}", args[0].Value.ConvertTo(typeof(string), new ResolutionContext())); - } + Assert.Single(args); + Assert.Equal("outputTemplate", args[0].Key); + Assert.Equal("{Message}", args[0].Value.ConvertTo(typeof(string), new ResolutionContext())); + } - [Fact] - public void WriteToSupportMultipleSinksOfTheSameKind() - { - var json = @" + [Fact] + public void WriteToSupportMultipleSinksOfTheSameKind() + { + var json = @" { 'WriteTo': [ { @@ -111,176 +108,175 @@ public void WriteToSupportMultipleSinksOfTheSameKind() } }"; - var result = _configurationReader.GetMethodCalls(JsonStringConfigSource.LoadSection(json, "WriteTo")); + var result = _configurationReader.GetMethodCalls(JsonStringConfigSource.LoadSection(json, "WriteTo")); - Assert.Equal(3, result.Count); - Assert.True(result.Contains("LiterateConsole")); - Assert.True(result.Contains("DiagnosticTrace")); - Assert.True(result.Contains("File")); + Assert.Equal(3, result.Count); + Assert.True(result.Contains("LiterateConsole")); + Assert.True(result.Contains("DiagnosticTrace")); + Assert.True(result.Contains("File")); - Assert.Single(result["LiterateConsole"]); - Assert.Single(result["DiagnosticTrace"]); - Assert.Equal(2, result["File"].Count()); - } + Assert.Single(result["LiterateConsole"]); + Assert.Single(result["DiagnosticTrace"]); + Assert.Equal(2, result["File"].Count()); + } - [Fact] - public void Enrich_SupportSimplifiedSyntax() - { - var json = @" + [Fact] + public void Enrich_SupportSimplifiedSyntax() + { + var json = @" { 'Enrich': [ 'FromLogContext', 'WithMachineName', 'WithThreadId' ] }"; - var result = _configurationReader.GetMethodCalls(JsonStringConfigSource.LoadSection(json, "Enrich")); - Assert.Equal(3, result.Count); - Assert.True(result.Contains("FromLogContext")); - Assert.True(result.Contains("WithMachineName")); - Assert.True(result.Contains("WithThreadId")); + var result = _configurationReader.GetMethodCalls(JsonStringConfigSource.LoadSection(json, "Enrich")); + Assert.Equal(3, result.Count); + Assert.True(result.Contains("FromLogContext")); + Assert.True(result.Contains("WithMachineName")); + Assert.True(result.Contains("WithThreadId")); - Assert.Single(result["FromLogContext"]); - Assert.Single(result["WithMachineName"]); - Assert.Single(result["WithThreadId"]); - } + Assert.Single(result["FromLogContext"]); + Assert.Single(result["WithMachineName"]); + Assert.Single(result["WithThreadId"]); + } - [Fact] - public void CallableMethodsAreSelected() - { - var options = typeof(DummyLoggerConfigurationExtensions).GetTypeInfo().DeclaredMethods.ToList(); - Assert.Equal(2, options.Count(mi => mi.Name == "DummyRollingFile")); - var suppliedArgumentNames = new[] { "pathFormat" }; + [Fact] + public void CallableMethodsAreSelected() + { + var options = typeof(DummyLoggerConfigurationExtensions).GetTypeInfo().DeclaredMethods.ToList(); + Assert.Equal(2, options.Count(mi => mi.Name == "DummyRollingFile")); + var suppliedArgumentNames = new[] { "pathFormat" }; - var selected = ConfigurationReader.SelectConfigurationMethod(options, "DummyRollingFile", suppliedArgumentNames); - Assert.Equal(typeof(string), selected.GetParameters()[1].ParameterType); - } + var selected = ConfigurationReader.SelectConfigurationMethod(options, "DummyRollingFile", suppliedArgumentNames); + Assert.Equal(typeof(string), selected.GetParameters()[1].ParameterType); + } - [Fact] - public void MethodsAreSelectedBasedOnCountOfMatchedArguments() - { - var options = typeof(DummyLoggerConfigurationExtensions).GetTypeInfo().DeclaredMethods.ToList(); - Assert.Equal(2, options.Count(mi => mi.Name == "DummyRollingFile")); + [Fact] + public void MethodsAreSelectedBasedOnCountOfMatchedArguments() + { + var options = typeof(DummyLoggerConfigurationExtensions).GetTypeInfo().DeclaredMethods.ToList(); + Assert.Equal(2, options.Count(mi => mi.Name == "DummyRollingFile")); - var suppliedArgumentNames = new[] { "pathFormat", "formatter" }; + var suppliedArgumentNames = new[] { "pathFormat", "formatter" }; - var selected = ConfigurationReader.SelectConfigurationMethod(options, "DummyRollingFile", suppliedArgumentNames); - Assert.Equal(typeof(ITextFormatter), selected.GetParameters()[1].ParameterType); - } + var selected = ConfigurationReader.SelectConfigurationMethod(options, "DummyRollingFile", suppliedArgumentNames); + 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")); + [Fact] + public void MethodsAreSelectedBasedOnCountOfMatchedArgumentsAndThenStringType() + { + var options = typeof(DummyLoggerConfigurationWithMultipleMethodsExtensions).GetTypeInfo().DeclaredMethods.ToList(); + Assert.Equal(3, options.Count(mi => mi.Name == "DummyRollingFile")); - var suppliedArgumentNames = new[] { "pathFormat", "formatter" }; + var suppliedArgumentNames = new[] { "pathFormat", "formatter" }; - var selected = ConfigurationReader.SelectConfigurationMethod(options, "DummyRollingFile", suppliedArgumentNames); - Assert.Equal(typeof(string), selected.GetParameters()[2].ParameterType); + var selected = ConfigurationReader.SelectConfigurationMethod(options, "DummyRollingFile", suppliedArgumentNames); + Assert.Equal(typeof(string), selected.GetParameters()[2].ParameterType); + } + + public static IEnumerable FlatMinimumLevel => new List + { + new object[] { GetConfigRoot(appsettingsJsonLevel: minimumLevelFlatTemplate.Format(LogEventLevel.Error)), LogEventLevel.Error }, + new object[] { GetConfigRoot(appsettingsDevelopmentJsonLevel: minimumLevelFlatTemplate.Format(LogEventLevel.Error)), LogEventLevel.Error }, + new object[] { GetConfigRoot(envVariables: new Dictionary() {{minimumLevelFlatKey, LogEventLevel.Error.ToString()}}), LogEventLevel.Error}, + new object[] { GetConfigRoot( + appsettingsJsonLevel: minimumLevelFlatTemplate.Format(LogEventLevel.Debug), + envVariables: new Dictionary() {{minimumLevelFlatKey, LogEventLevel.Error.ToString()}}), + LogEventLevel.Error } + }; - public static IEnumerable FlatMinimumLevel => new List - { - new object[] { GetConfigRoot(appsettingsJsonLevel: minimumLevelFlatTemplate.Format(LogEventLevel.Error)), LogEventLevel.Error }, - new object[] { GetConfigRoot(appsettingsDevelopmentJsonLevel: minimumLevelFlatTemplate.Format(LogEventLevel.Error)), LogEventLevel.Error }, - new object[] { GetConfigRoot(envVariables: new Dictionary() {{minimumLevelFlatKey, LogEventLevel.Error.ToString()}}), LogEventLevel.Error}, - new object[] { GetConfigRoot( - appsettingsJsonLevel: minimumLevelFlatTemplate.Format(LogEventLevel.Debug), - envVariables: new Dictionary() {{minimumLevelFlatKey, LogEventLevel.Error.ToString()}}), - LogEventLevel.Error - } - }; - - [Theory] - [MemberData(nameof(FlatMinimumLevel))] - public void FlatMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, LogEventLevel expectedMinimumLevel) - { - var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), root); - var loggerConfig = new LoggerConfiguration(); + [Theory] + [MemberData(nameof(FlatMinimumLevel))] + public void FlatMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, LogEventLevel expectedMinimumLevel) + { + var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), root); + var loggerConfig = new LoggerConfiguration(); - reader.Configure(loggerConfig); + reader.Configure(loggerConfig); - AssertLogEventLevels(loggerConfig, expectedMinimumLevel); - } - - public static IEnumerable ObjectMinimumLevel => new List - { - new object[] { GetConfigRoot(appsettingsJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error)), LogEventLevel.Error }, - new object[] { GetConfigRoot(appsettingsJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error.ToString().ToUpper())), LogEventLevel.Error }, - new object[] { GetConfigRoot(appsettingsDevelopmentJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error)), LogEventLevel.Error }, - new object[] { GetConfigRoot(envVariables: new Dictionary(){{minimumLevelObjectKey, LogEventLevel.Error.ToString() } }), LogEventLevel.Error }, - new object[] { GetConfigRoot( - appsettingsJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error), - appsettingsDevelopmentJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Debug)), - LogEventLevel.Debug } - }; + AssertLogEventLevels(loggerConfig, expectedMinimumLevel); + } - [Theory] - [MemberData(nameof(ObjectMinimumLevel))] - public void ObjectMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, LogEventLevel expectedMinimumLevel) - { - var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), root); - var loggerConfig = new LoggerConfiguration(); + public static IEnumerable ObjectMinimumLevel => new List + { + new object[] { GetConfigRoot(appsettingsJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error)), LogEventLevel.Error }, + new object[] { GetConfigRoot(appsettingsJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error.ToString().ToUpper())), LogEventLevel.Error }, + new object[] { GetConfigRoot(appsettingsDevelopmentJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error)), LogEventLevel.Error }, + new object[] { GetConfigRoot(envVariables: new Dictionary(){{minimumLevelObjectKey, LogEventLevel.Error.ToString() } }), LogEventLevel.Error }, + new object[] { GetConfigRoot( + appsettingsJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error), + appsettingsDevelopmentJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Debug)), + LogEventLevel.Debug } + }; + + [Theory] + [MemberData(nameof(ObjectMinimumLevel))] + public void ObjectMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, LogEventLevel expectedMinimumLevel) + { + var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), root); + var loggerConfig = new LoggerConfiguration(); - reader.Configure(loggerConfig); + reader.Configure(loggerConfig); - AssertLogEventLevels(loggerConfig, expectedMinimumLevel); - } + AssertLogEventLevels(loggerConfig, expectedMinimumLevel); + } - #if !(NET452) + #if !(NET452) - // currently only works in the .NET 4.6.1 and .NET Standard builds of Serilog.Settings.Configuration - public static IEnumerable MixedMinimumLevel => new List + // currently only works in the .NET 4.6.1 and .NET Standard builds of Serilog.Settings.Configuration + public static IEnumerable MixedMinimumLevel => new List + { + new object[] + { + GetConfigRoot( + appsettingsJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error), + appsettingsDevelopmentJsonLevel: minimumLevelFlatTemplate.Format(LogEventLevel.Debug)), + LogEventLevel.Debug + }, + new object[] { - new object[] - { - GetConfigRoot( - appsettingsJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error), - appsettingsDevelopmentJsonLevel: minimumLevelFlatTemplate.Format(LogEventLevel.Debug)), - LogEventLevel.Debug - }, - new object[] - { - GetConfigRoot( - appsettingsJsonLevel: minimumLevelFlatTemplate.Format(LogEventLevel.Error), - appsettingsDevelopmentJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Debug)), - LogEventLevel.Debug - }, - // precedence should be flat > object if from the same source - new object[] - { - GetConfigRoot( - envVariables: new Dictionary() - { - {minimumLevelObjectKey, LogEventLevel.Error.ToString()}, - {minimumLevelFlatKey, LogEventLevel.Debug.ToString()} - }), - LogEventLevel.Debug - } - }; - - [Theory] - [MemberData(nameof(MixedMinimumLevel))] - public void MixedMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, LogEventLevel expectedMinimumLevel) + GetConfigRoot( + appsettingsJsonLevel: minimumLevelFlatTemplate.Format(LogEventLevel.Error), + appsettingsDevelopmentJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Debug)), + LogEventLevel.Debug + }, + // precedence should be flat > object if from the same source + new object[] { - var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), root); - var loggerConfig = new LoggerConfiguration(); + GetConfigRoot( + envVariables: new Dictionary() + { + {minimumLevelObjectKey, LogEventLevel.Error.ToString()}, + {minimumLevelFlatKey, LogEventLevel.Debug.ToString()} + }), + LogEventLevel.Debug + } + }; + + [Theory] + [MemberData(nameof(MixedMinimumLevel))] + public void MixedMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, LogEventLevel expectedMinimumLevel) + { + var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), root); + var loggerConfig = new LoggerConfiguration(); - reader.Configure(loggerConfig); + reader.Configure(loggerConfig); - AssertLogEventLevels(loggerConfig, expectedMinimumLevel); - } + AssertLogEventLevels(loggerConfig, expectedMinimumLevel); + } - #endif + #endif - [Fact] - public void NoConfigurationRootUsedStillValid() - { - var section = JsonStringConfigSource.LoadSection(@"{ 'Nest': { 'Serilog': { 'MinimumLevel': 'Error' } } }", "Nest"); - var reader = new ConfigurationReader(section.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), section); - var loggerConfig = new LoggerConfiguration(); + [Fact] + public void NoConfigurationRootUsedStillValid() + { + var section = JsonStringConfigSource.LoadSection(@"{ 'Nest': { 'Serilog': { 'MinimumLevel': 'Error' } } }", "Nest"); + var reader = new ConfigurationReader(section.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), section); + var loggerConfig = new LoggerConfiguration(); - reader.Configure(loggerConfig); + reader.Configure(loggerConfig); - AssertLogEventLevels(loggerConfig, LogEventLevel.Error); - } + AssertLogEventLevels(loggerConfig, LogEventLevel.Error); } } diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index b9f163a2..94e8ff9b 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -1,43 +1,41 @@ -using System; -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration; using Serilog.Core; using Serilog.Events; using Serilog.Settings.Configuration.Tests.Support; using TestDummies; using TestDummies.Console; using TestDummies.Console.Themes; -using Xunit; -namespace Serilog.Settings.Configuration.Tests +namespace Serilog.Settings.Configuration.Tests; + +public class ConfigurationSettingsTests { - public class ConfigurationSettingsTests + static LoggerConfiguration ConfigFromJson(string jsonString, string secondJsonSource = null) { - static LoggerConfiguration ConfigFromJson(string jsonString, string secondJsonSource = null) - { - return ConfigFromJson(jsonString, secondJsonSource, out _); - } + return ConfigFromJson(jsonString, secondJsonSource, out _); + } - static LoggerConfiguration ConfigFromJson(string jsonString, out IConfiguration configuration) - { - return ConfigFromJson(jsonString, null, out configuration); - } + static LoggerConfiguration ConfigFromJson(string jsonString, out IConfiguration configuration) + { + return ConfigFromJson(jsonString, null, out configuration); + } - static LoggerConfiguration ConfigFromJson(string jsonString, string secondJsonSource, out IConfiguration configuration) - { - var builder = new ConfigurationBuilder().AddJsonString(jsonString); - if (secondJsonSource != null) - builder.AddJsonString(secondJsonSource); - configuration = builder.Build(); - return new LoggerConfiguration() - .ReadFrom.Configuration(configuration); - } - - [Fact] - public void PropertyEnrichmentIsApplied() - { - LogEvent evt = null; + static LoggerConfiguration ConfigFromJson(string jsonString, string secondJsonSource, out IConfiguration configuration) + { + var builder = new ConfigurationBuilder().AddJsonString(jsonString); + if (secondJsonSource != null) + builder.AddJsonString(secondJsonSource); + configuration = builder.Build(); + return new LoggerConfiguration() + .ReadFrom.Configuration(configuration); + } + + [Fact] + public void PropertyEnrichmentIsApplied() + { + LogEvent evt = null; - var json = @"{ + var json = @"{ ""Serilog"": { ""Properties"": { ""App"": ""Test"" @@ -45,19 +43,19 @@ public void PropertyEnrichmentIsApplied() } }"; - var log = ConfigFromJson(json) - .WriteTo.Sink(new DelegatingSink(e => evt = e)) - .CreateLogger(); + var log = ConfigFromJson(json) + .WriteTo.Sink(new DelegatingSink(e => evt = e)) + .CreateLogger(); - log.Information("Has a test property"); + log.Information("Has a test property"); - Assert.NotNull(evt); - Assert.Equal("Test", evt.Properties["App"].LiteralValue()); - } + Assert.NotNull(evt); + Assert.Equal("Test", evt.Properties["App"].LiteralValue()); + } - [Theory] - [InlineData("extended syntax", - @"{ + [Theory] + [InlineData("extended syntax", + @"{ ""Serilog"": { ""Using"": [""TestDummies""], ""WriteTo"": [ @@ -66,58 +64,58 @@ public void PropertyEnrichmentIsApplied() ] } }")] - [InlineData("simplified syntax", - @"{ + [InlineData("simplified syntax", + @"{ ""Serilog"": { ""Using"": [""TestDummies""], ""WriteTo"": [""DummyConsole"", ""DummyWithLevelSwitch"" ] } }")] - public void ParameterlessSinksAreConfigured(string syntax, string json) - { - _ = syntax; + public void ParameterlessSinksAreConfigured(string syntax, string json) + { + _ = syntax; - var log = ConfigFromJson(json) - .CreateLogger(); + var log = ConfigFromJson(json) + .CreateLogger(); - DummyConsoleSink.Emitted.Clear(); - DummyWithLevelSwitchSink.Emitted.Clear(); + DummyConsoleSink.Emitted.Clear(); + DummyWithLevelSwitchSink.Emitted.Clear(); - log.Write(Some.InformationEvent()); + log.Write(Some.InformationEvent()); - Assert.Single(DummyConsoleSink.Emitted); - Assert.Single(DummyWithLevelSwitchSink.Emitted); - } + Assert.Single(DummyConsoleSink.Emitted); + Assert.Single(DummyWithLevelSwitchSink.Emitted); + } - [Fact] - public void ConfigurationAssembliesFromDllScanning() - { - var json = @"{ + [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, - configurationAssemblySource: ConfigurationAssemblySource.AlwaysScanDllFiles) - .CreateLogger(); + var builder = new ConfigurationBuilder().AddJsonString(json); + var config = builder.Build(); + var log = new LoggerConfiguration() + .ReadFrom.Configuration( + configuration: config, + configurationAssemblySource: ConfigurationAssemblySource.AlwaysScanDllFiles) + .CreateLogger(); - DummyConsoleSink.Emitted.Clear(); + DummyConsoleSink.Emitted.Clear(); - log.Write(Some.InformationEvent()); + log.Write(Some.InformationEvent()); - Assert.Single(DummyConsoleSink.Emitted); - } + Assert.Single(DummyConsoleSink.Emitted); + } - [Fact] - public void SinksAreConfigured() - { - var json = @"{ + [Fact] + public void SinksAreConfigured() + { + var json = @"{ ""Serilog"": { ""Using"": [""TestDummies""], ""WriteTo"": [{ @@ -127,22 +125,22 @@ public void SinksAreConfigured() } }"; - var log = ConfigFromJson(json) - .CreateLogger(); + var log = ConfigFromJson(json) + .CreateLogger(); - DummyRollingFileSink.Reset(); - DummyRollingFileAuditSink.Reset(); + DummyRollingFileSink.Reset(); + DummyRollingFileAuditSink.Reset(); - log.Write(Some.InformationEvent()); + log.Write(Some.InformationEvent()); - Assert.Single(DummyRollingFileSink.Emitted); - Assert.Empty(DummyRollingFileAuditSink.Emitted); - } + Assert.Single(DummyRollingFileSink.Emitted); + Assert.Empty(DummyRollingFileAuditSink.Emitted); + } - [Fact] - public void AuditSinksAreConfigured() - { - var json = @"{ + [Fact] + public void AuditSinksAreConfigured() + { + var json = @"{ ""Serilog"": { ""Using"": [""TestDummies""], ""AuditTo"": [{ @@ -152,22 +150,22 @@ public void AuditSinksAreConfigured() } }"; - var log = ConfigFromJson(json) - .CreateLogger(); + var log = ConfigFromJson(json) + .CreateLogger(); - DummyRollingFileSink.Reset(); - DummyRollingFileAuditSink.Reset(); + DummyRollingFileSink.Reset(); + DummyRollingFileAuditSink.Reset(); - log.Write(Some.InformationEvent()); + log.Write(Some.InformationEvent()); - Assert.Empty(DummyRollingFileSink.Emitted); - Assert.Single(DummyRollingFileAuditSink.Emitted); - } + Assert.Empty(DummyRollingFileSink.Emitted); + Assert.Single(DummyRollingFileAuditSink.Emitted); + } - [Fact] - public void AuditToSubLoggersAreConfigured() - { - var json = @"{ + [Fact] + public void AuditToSubLoggersAreConfigured() + { + var json = @"{ ""Serilog"": { ""Using"": [""TestDummies""], ""AuditTo"": [{ @@ -183,22 +181,22 @@ public void AuditToSubLoggersAreConfigured() } }"; - var log = ConfigFromJson(json) - .CreateLogger(); + var log = ConfigFromJson(json) + .CreateLogger(); - DummyRollingFileSink.Reset(); - DummyRollingFileAuditSink.Reset(); + DummyRollingFileSink.Reset(); + DummyRollingFileAuditSink.Reset(); - log.Write(Some.InformationEvent()); + log.Write(Some.InformationEvent()); - Assert.Empty(DummyRollingFileSink.Emitted); - Assert.Single(DummyRollingFileAuditSink.Emitted); - } + Assert.Empty(DummyRollingFileSink.Emitted); + Assert.Single(DummyRollingFileAuditSink.Emitted); + } - [Fact] - public void TestMinimumLevelOverrides() - { - var json = @"{ + [Fact] + public void TestMinimumLevelOverrides() + { + var json = @"{ ""Serilog"": { ""MinimumLevel"" : { ""Override"" : { @@ -208,29 +206,29 @@ public void TestMinimumLevelOverrides() } }"; - LogEvent evt = null; + LogEvent evt = null; - var log = ConfigFromJson(json) - .WriteTo.Sink(new DelegatingSink(e => evt = e)) - .CreateLogger(); + var log = ConfigFromJson(json) + .WriteTo.Sink(new DelegatingSink(e => evt = e)) + .CreateLogger(); - var systemLogger = log.ForContext(); - systemLogger.Write(Some.InformationEvent()); + var systemLogger = log.ForContext(); + systemLogger.Write(Some.InformationEvent()); - Assert.Null(evt); + Assert.Null(evt); - systemLogger.Warning("Bad things"); - Assert.NotNull(evt); + systemLogger.Warning("Bad things"); + Assert.NotNull(evt); - evt = null; - log.Write(Some.InformationEvent()); - Assert.NotNull(evt); - } + evt = null; + log.Write(Some.InformationEvent()); + Assert.NotNull(evt); + } - [Fact] - public void TestMinimumLevelOverridesForChildContext() - { - var json = @"{ + [Fact] + public void TestMinimumLevelOverridesForChildContext() + { + var json = @"{ ""Serilog"": { ""MinimumLevel"" : { ""Default"" : ""Warning"", @@ -242,29 +240,29 @@ public void TestMinimumLevelOverridesForChildContext() } }"; - LogEvent evt = null; + LogEvent evt = null; - var log = ConfigFromJson(json) - .WriteTo.Sink(new DelegatingSink(e => evt = e)) - .CreateLogger(); + var log = ConfigFromJson(json) + .WriteTo.Sink(new DelegatingSink(e => evt = e)) + .CreateLogger(); - log.Write(Some.DebugEvent()); - Assert.Null(evt); + log.Write(Some.DebugEvent()); + Assert.Null(evt); - var custom = log.ForContext(Constants.SourceContextPropertyName, typeof(System.Threading.Tasks.Task).FullName + "<42>"); - custom.Write(Some.DebugEvent()); - Assert.NotNull(evt); + var custom = log.ForContext(Constants.SourceContextPropertyName, typeof(System.Threading.Tasks.Task).FullName + "<42>"); + custom.Write(Some.DebugEvent()); + Assert.NotNull(evt); - evt = null; - var systemThreadingLogger = log.ForContext(); - systemThreadingLogger.Write(Some.DebugEvent()); - Assert.NotNull(evt); - } + evt = null; + var systemThreadingLogger = log.ForContext(); + systemThreadingLogger.Write(Some.DebugEvent()); + Assert.NotNull(evt); + } - [Fact] - public void SinksWithAbstractParamsAreConfiguredWithTypeName() - { - var json = @"{ + [Fact] + public void SinksWithAbstractParamsAreConfiguredWithTypeName() + { + var json = @"{ ""Serilog"": { ""Using"": [""TestDummies""], ""WriteTo"": [{ @@ -274,19 +272,19 @@ public void SinksWithAbstractParamsAreConfiguredWithTypeName() } }"; - DummyConsoleSink.Theme = null; + DummyConsoleSink.Theme = null; - ConfigFromJson(json) - .CreateLogger(); + ConfigFromJson(json) + .CreateLogger(); - Assert.NotNull(DummyConsoleSink.Theme); - Assert.IsType(DummyConsoleSink.Theme); - } + Assert.NotNull(DummyConsoleSink.Theme); + Assert.IsType(DummyConsoleSink.Theme); + } - [Fact] - public void SinksAreConfiguredWithStaticMember() - { - var json = @"{ + [Fact] + public void SinksAreConfiguredWithStaticMember() + { + var json = @"{ ""Serilog"": { ""Using"": [""TestDummies""], ""WriteTo"": [{ @@ -296,58 +294,58 @@ public void SinksAreConfiguredWithStaticMember() } }"; - DummyConsoleSink.Theme = null; + DummyConsoleSink.Theme = null; - ConfigFromJson(json) - .CreateLogger(); - - Assert.Equal(ConsoleThemes.Theme1, DummyConsoleSink.Theme); - } - - [Theory] - [InlineData("$switchName", true)] - [InlineData("$SwitchName", true)] - [InlineData("SwitchName", true)] - [InlineData("$switch1", true)] - [InlineData("$sw1tch0", true)] - [InlineData("sw1tch0", true)] - [InlineData("$SWITCHNAME", true)] - [InlineData("$$switchname", false)] - [InlineData("$switchname$", false)] - [InlineData("switch$name", false)] - [InlineData("$", false)] - [InlineData("", false)] - [InlineData(" ", false)] - [InlineData("$1switch", false)] - [InlineData("$switch_name", false)] - public void LoggingLevelSwitchNameValidityScenarios(string switchName, bool expectedValid) - { - Assert.True(ConfigurationReader.IsValidSwitchName(switchName) == expectedValid, - $"expected IsValidSwitchName({switchName}) to return {expectedValid} "); - } + ConfigFromJson(json) + .CreateLogger(); - [Fact] - public void LoggingLevelSwitchWithInvalidNameThrowsFormatException() - { - var json = @"{ + Assert.Equal(ConsoleThemes.Theme1, DummyConsoleSink.Theme); + } + + [Theory] + [InlineData("$switchName", true)] + [InlineData("$SwitchName", true)] + [InlineData("SwitchName", true)] + [InlineData("$switch1", true)] + [InlineData("$sw1tch0", true)] + [InlineData("sw1tch0", true)] + [InlineData("$SWITCHNAME", true)] + [InlineData("$$switchname", false)] + [InlineData("$switchname$", false)] + [InlineData("switch$name", false)] + [InlineData("$", false)] + [InlineData("", false)] + [InlineData(" ", false)] + [InlineData("$1switch", false)] + [InlineData("$switch_name", false)] + public void LoggingLevelSwitchNameValidityScenarios(string switchName, bool expectedValid) + { + Assert.True(ConfigurationReader.IsValidSwitchName(switchName) == expectedValid, + $"expected IsValidSwitchName({switchName}) to return {expectedValid} "); + } + + [Fact] + public void LoggingLevelSwitchWithInvalidNameThrowsFormatException() + { + var json = @"{ ""Serilog"": { ""LevelSwitches"": {""1InvalidSwitchName"" : ""Warning"" } } }"; - var ex = Assert.Throws(() => ConfigFromJson(json)); + var ex = Assert.Throws(() => ConfigFromJson(json)); - Assert.Contains("\"1InvalidSwitchName\"", ex.Message); - Assert.Contains("'$' sign", ex.Message); - Assert.Contains("\"LevelSwitches\" : {\"$switchName\" :", ex.Message); - } + Assert.Contains("\"1InvalidSwitchName\"", ex.Message); + Assert.Contains("'$' sign", ex.Message); + Assert.Contains("\"LevelSwitches\" : {\"$switchName\" :", ex.Message); + } - [Theory] - [InlineData("$mySwitch")] - [InlineData("mySwitch")] - public void LoggingFilterSwitchIsConfigured(string switchName) - { - var json = $@"{{ + [Theory] + [InlineData("$mySwitch")] + [InlineData("mySwitch")] + public void LoggingFilterSwitchIsConfigured(string switchName) + { + var json = $@"{{ 'Serilog': {{ 'FilterSwitches': {{ '{switchName}': 'Prop = 42' }}, 'Filter:BySwitch': {{ @@ -358,25 +356,25 @@ public void LoggingFilterSwitchIsConfigured(string switchName) }} }} }}"; - LogEvent evt = null; + LogEvent evt = null; - var log = ConfigFromJson(json) - .WriteTo.Sink(new DelegatingSink(e => evt = e)) - .CreateLogger(); + var log = ConfigFromJson(json) + .WriteTo.Sink(new DelegatingSink(e => evt = e)) + .CreateLogger(); - log.Write(Some.InformationEvent()); - Assert.Null(evt); + log.Write(Some.InformationEvent()); + Assert.Null(evt); - log.ForContext("Prop", 42).Write(Some.InformationEvent()); - Assert.NotNull(evt); - } + log.ForContext("Prop", 42).Write(Some.InformationEvent()); + Assert.NotNull(evt); + } - [Theory] - [InlineData("$switch1")] - [InlineData("switch1")] - public void LoggingLevelSwitchIsConfigured(string switchName) - { - var json = $@"{{ + [Theory] + [InlineData("$switch1")] + [InlineData("switch1")] + public void LoggingLevelSwitchIsConfigured(string switchName) + { + var json = $@"{{ 'Serilog': {{ 'LevelSwitches': {{ '{switchName}' : 'Warning' }}, 'MinimumLevel' : {{ @@ -384,24 +382,24 @@ public void LoggingLevelSwitchIsConfigured(string switchName) }} }} }}"; - LogEvent evt = null; - - var log = ConfigFromJson(json) - .WriteTo.Sink(new DelegatingSink(e => evt = e)) - .CreateLogger(); - - log.Write(Some.DebugEvent()); - Assert.True(evt is null, "LoggingLevelSwitch initial level was Warning. It should not log Debug messages"); - log.Write(Some.InformationEvent()); - Assert.True(evt is null, "LoggingLevelSwitch initial level was Warning. It should not log Information messages"); - log.Write(Some.WarningEvent()); - Assert.True(evt != null, "LoggingLevelSwitch initial level was Warning. It should log Warning messages"); - } - - [Fact] - public void SettingMinimumLevelControlledByToAnUndeclaredSwitchThrows() - { - var json = @"{ + LogEvent evt = null; + + var log = ConfigFromJson(json) + .WriteTo.Sink(new DelegatingSink(e => evt = e)) + .CreateLogger(); + + log.Write(Some.DebugEvent()); + Assert.True(evt is null, "LoggingLevelSwitch initial level was Warning. It should not log Debug messages"); + log.Write(Some.InformationEvent()); + Assert.True(evt is null, "LoggingLevelSwitch initial level was Warning. It should not log Information messages"); + log.Write(Some.WarningEvent()); + Assert.True(evt != null, "LoggingLevelSwitch initial level was Warning. It should log Warning messages"); + } + + [Fact] + public void SettingMinimumLevelControlledByToAnUndeclaredSwitchThrows() + { + var json = @"{ ""Serilog"": { ""LevelSwitches"": {""$switch1"" : ""Warning"" }, ""MinimumLevel"" : { @@ -410,18 +408,18 @@ public void SettingMinimumLevelControlledByToAnUndeclaredSwitchThrows() } }"; - var ex = Assert.Throws(() => - ConfigFromJson(json) - .CreateLogger()); + var ex = Assert.Throws(() => + ConfigFromJson(json) + .CreateLogger()); - Assert.Contains("$switch2", ex.Message); - Assert.Contains("\"LevelSwitches\":{\"$switch2\":", ex.Message); - } + Assert.Contains("$switch2", ex.Message); + Assert.Contains("\"LevelSwitches\":{\"$switch2\":", ex.Message); + } - [Fact] - public void LoggingLevelSwitchIsPassedToSinks() - { - var json = @"{ + [Fact] + public void LoggingLevelSwitchIsPassedToSinks() + { + var json = @"{ ""Serilog"": { ""Using"": [""TestDummies""], ""LevelSwitches"": {""$switch1"" : ""Information"" }, @@ -435,29 +433,29 @@ public void LoggingLevelSwitchIsPassedToSinks() } }"; - LogEvent evt = null; + LogEvent evt = null; - var log = ConfigFromJson(json) - .WriteTo.Sink(new DelegatingSink(e => evt = e)) - .CreateLogger(); + var log = ConfigFromJson(json) + .WriteTo.Sink(new DelegatingSink(e => evt = e)) + .CreateLogger(); - Assert.False(DummyWithLevelSwitchSink.ControlLevelSwitch == null, "Sink ControlLevelSwitch should have been initialized"); + Assert.False(DummyWithLevelSwitchSink.ControlLevelSwitch == null, "Sink ControlLevelSwitch should have been initialized"); - var controlSwitch = DummyWithLevelSwitchSink.ControlLevelSwitch; - Assert.NotNull(controlSwitch); + var controlSwitch = DummyWithLevelSwitchSink.ControlLevelSwitch; + Assert.NotNull(controlSwitch); - log.Write(Some.DebugEvent()); - Assert.True(evt is null, "LoggingLevelSwitch initial level was information. It should not log Debug messages"); + log.Write(Some.DebugEvent()); + Assert.True(evt is null, "LoggingLevelSwitch initial level was information. It should not log Debug messages"); - controlSwitch.MinimumLevel = LogEventLevel.Debug; - log.Write(Some.DebugEvent()); - Assert.True(evt != null, "LoggingLevelSwitch level was changed to Debug. It should log Debug messages"); - } + controlSwitch.MinimumLevel = LogEventLevel.Debug; + log.Write(Some.DebugEvent()); + Assert.True(evt != null, "LoggingLevelSwitch level was changed to Debug. It should log Debug messages"); + } - [Fact] - public void ReferencingAnUndeclaredSwitchInSinkThrows() - { - var json = @"{ + [Fact] + public void ReferencingAnUndeclaredSwitchInSinkThrows() + { + var json = @"{ ""Serilog"": { ""Using"": [""TestDummies""], ""LevelSwitches"": {""$switch1"" : ""Information"" }, @@ -471,18 +469,18 @@ public void ReferencingAnUndeclaredSwitchInSinkThrows() } }"; - var ex = Assert.Throws(() => - ConfigFromJson(json) - .CreateLogger()); + var ex = Assert.Throws(() => + ConfigFromJson(json) + .CreateLogger()); - Assert.Contains("$switch2", ex.Message); - Assert.Contains("\"LevelSwitches\":{\"$switch2\":", ex.Message); - } + Assert.Contains("$switch2", ex.Message); + Assert.Contains("\"LevelSwitches\":{\"$switch2\":", ex.Message); + } - [Fact] - public void LoggingLevelSwitchCanBeUsedForMinimumLevelOverrides() - { - var json = @"{ + [Fact] + public void LoggingLevelSwitchCanBeUsedForMinimumLevelOverrides() + { + var json = @"{ ""Serilog"": { ""Using"": [""TestDummies""], ""LevelSwitches"": {""$specificSwitch"" : ""Warning"" }, @@ -499,41 +497,41 @@ public void LoggingLevelSwitchCanBeUsedForMinimumLevelOverrides() } }"; - LogEvent evt = null; + LogEvent evt = null; - var log = ConfigFromJson(json) - .WriteTo.Sink(new DelegatingSink(e => evt = e)) - .CreateLogger(); + var log = ConfigFromJson(json) + .WriteTo.Sink(new DelegatingSink(e => evt = e)) + .CreateLogger(); - var systemLogger = log.ForContext(Constants.SourceContextPropertyName, "System.Bar"); + var systemLogger = log.ForContext(Constants.SourceContextPropertyName, "System.Bar"); - log.Write(Some.InformationEvent()); - Assert.False(evt is null, "Minimum level is Debug. It should log Information messages"); + log.Write(Some.InformationEvent()); + Assert.False(evt is null, "Minimum level is Debug. It should log Information messages"); - evt = null; - // ReSharper disable HeuristicUnreachableCode - systemLogger.Write(Some.InformationEvent()); - Assert.True(evt is null, "LoggingLevelSwitch initial level was Warning for logger System.*. It should not log Information messages for SourceContext System.Bar"); + evt = null; + // ReSharper disable HeuristicUnreachableCode + systemLogger.Write(Some.InformationEvent()); + Assert.True(evt is null, "LoggingLevelSwitch initial level was Warning for logger System.*. It should not log Information messages for SourceContext System.Bar"); - systemLogger.Write(Some.WarningEvent()); - Assert.False(evt is null, "LoggingLevelSwitch initial level was Warning for logger System.*. It should log Warning messages for SourceContext System.Bar"); + systemLogger.Write(Some.WarningEvent()); + Assert.False(evt is null, "LoggingLevelSwitch initial level was Warning for logger System.*. It should log Warning messages for SourceContext System.Bar"); - evt = null; - var controlSwitch = DummyWithLevelSwitchSink.ControlLevelSwitch; + evt = null; + var controlSwitch = DummyWithLevelSwitchSink.ControlLevelSwitch; - controlSwitch.MinimumLevel = LogEventLevel.Information; - systemLogger.Write(Some.InformationEvent()); - 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 - } + controlSwitch.MinimumLevel = LogEventLevel.Information; + systemLogger.Write(Some.InformationEvent()); + 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 + } - [Fact] + [Fact] - [Trait("BugFix", "https://github.com/serilog/serilog-settings-configuration/issues/142")] - public void SinkWithIConfigurationArguments() - { - var json = @"{ + [Trait("BugFix", "https://github.com/serilog/serilog-settings-configuration/issues/142")] + public void SinkWithIConfigurationArguments() + { + var json = @"{ ""Serilog"": { ""Using"": [""TestDummies""], ""WriteTo"": [{ @@ -543,21 +541,21 @@ public void SinkWithIConfigurationArguments() } }"; - DummyConfigurationSink.Reset(); - var log = ConfigFromJson(json, out var expectedConfig) - .CreateLogger(); + DummyConfigurationSink.Reset(); + var log = ConfigFromJson(json, out var expectedConfig) + .CreateLogger(); - log.Write(Some.InformationEvent()); + log.Write(Some.InformationEvent()); - Assert.NotNull(DummyConfigurationSink.Configuration); - Assert.Same(expectedConfig, DummyConfigurationSink.Configuration); - } + Assert.NotNull(DummyConfigurationSink.Configuration); + Assert.Same(expectedConfig, DummyConfigurationSink.Configuration); + } - [Fact] - [Trait("BugFix", "https://github.com/serilog/serilog-settings-configuration/issues/142")] - public void SinkWithOptionalIConfigurationArguments() - { - var json = @"{ + [Fact] + [Trait("BugFix", "https://github.com/serilog/serilog-settings-configuration/issues/142")] + public void SinkWithOptionalIConfigurationArguments() + { + var json = @"{ ""Serilog"": { ""Using"": [""TestDummies""], ""WriteTo"": [{ @@ -567,21 +565,21 @@ public void SinkWithOptionalIConfigurationArguments() } }"; - DummyConfigurationSink.Reset(); - var log = ConfigFromJson(json, out var expectedConfig) - .CreateLogger(); + DummyConfigurationSink.Reset(); + var log = ConfigFromJson(json, out var expectedConfig) + .CreateLogger(); - log.Write(Some.InformationEvent()); + log.Write(Some.InformationEvent()); - // null is the default value, but we have a configuration to provide - Assert.NotNull(DummyConfigurationSink.Configuration); - Assert.Same(expectedConfig, DummyConfigurationSink.Configuration); - } + // null is the default value, but we have a configuration to provide + Assert.NotNull(DummyConfigurationSink.Configuration); + Assert.Same(expectedConfig, DummyConfigurationSink.Configuration); + } - [Fact] - public void SinkWithIConfigSectionArguments() - { - var json = @"{ + [Fact] + public void SinkWithIConfigSectionArguments() + { + var json = @"{ ""Serilog"": { ""Using"": [""TestDummies""], ""WriteTo"": [{ @@ -591,21 +589,21 @@ public void SinkWithIConfigSectionArguments() } }"; - DummyConfigurationSink.Reset(); - var log = ConfigFromJson(json) - .CreateLogger(); + DummyConfigurationSink.Reset(); + var log = ConfigFromJson(json) + .CreateLogger(); - log.Write(Some.InformationEvent()); + log.Write(Some.InformationEvent()); - Assert.NotNull(DummyConfigurationSink.ConfigSection); - Assert.Equal("bar", DummyConfigurationSink.ConfigSection["foo"]); - } + Assert.NotNull(DummyConfigurationSink.ConfigSection); + Assert.Equal("bar", DummyConfigurationSink.ConfigSection["foo"]); + } - [Fact] - public void SinkWithConfigurationBindingArgument() - { - var json = @"{ + [Fact] + public void SinkWithConfigurationBindingArgument() + { + var json = @"{ ""Serilog"": { ""Using"": [""TestDummies""], ""WriteTo"": [{ @@ -616,20 +614,20 @@ public void SinkWithConfigurationBindingArgument() } }"; - var log = ConfigFromJson(json) - .CreateLogger(); + var log = ConfigFromJson(json) + .CreateLogger(); - DummyRollingFileSink.Reset(); + DummyRollingFileSink.Reset(); - log.Write(Some.InformationEvent()); + log.Write(Some.InformationEvent()); - Assert.Single(DummyRollingFileSink.Emitted); - } + Assert.Single(DummyRollingFileSink.Emitted); + } - [Fact] - public void SinkWithStringArrayArgument() - { - var json = @"{ + [Fact] + public void SinkWithStringArrayArgument() + { + var json = @"{ ""Serilog"": { ""Using"": [""TestDummies""], ""WriteTo"": [{ @@ -640,20 +638,20 @@ public void SinkWithStringArrayArgument() } }"; - var log = ConfigFromJson(json) - .CreateLogger(); + var log = ConfigFromJson(json) + .CreateLogger(); - DummyRollingFileSink.Reset(); + DummyRollingFileSink.Reset(); - log.Write(Some.InformationEvent()); + log.Write(Some.InformationEvent()); - Assert.Single(DummyRollingFileSink.Emitted); - } + Assert.Single(DummyRollingFileSink.Emitted); + } - [Fact] - public void DestructureWithCollectionsOfTypeArgument() - { - var json = @"{ + [Fact] + public void DestructureWithCollectionsOfTypeArgument() + { + var json = @"{ ""Serilog"": { ""Using"": [ ""TestDummies"" ], ""Destructure"": [{ @@ -679,22 +677,22 @@ public void DestructureWithCollectionsOfTypeArgument() } }"; - DummyPolicy.Current = null; + DummyPolicy.Current = null; - ConfigFromJson(json); + ConfigFromJson(json); - Assert.NotNull(DummyPolicy.Current); - Assert.Equal(typeof(TimeSpan), DummyPolicy.Current.Type); - Assert.Equal(new[] { typeof(int), typeof(string) }, DummyPolicy.Current.Array); - Assert.Equal(new[] { typeof(byte), typeof(short) }, DummyPolicy.Current.List); - Assert.Equal(typeof(long), DummyPolicy.Current.Custom.First); - Assert.Equal("System.UInt32", DummyPolicy.Current.CustomStrings.First); - } + Assert.NotNull(DummyPolicy.Current); + Assert.Equal(typeof(TimeSpan), DummyPolicy.Current.Type); + Assert.Equal(new[] { typeof(int), typeof(string) }, DummyPolicy.Current.Array); + Assert.Equal(new[] { typeof(byte), typeof(short) }, DummyPolicy.Current.List); + Assert.Equal(typeof(long), DummyPolicy.Current.Custom.First); + Assert.Equal("System.UInt32", DummyPolicy.Current.CustomStrings.First); + } - [Fact] - public void SinkWithIntArrayArgument() - { - var json = @"{ + [Fact] + public void SinkWithIntArrayArgument() + { + var json = @"{ ""Serilog"": { ""Using"": [""TestDummies""], ""WriteTo"": [{ @@ -705,21 +703,21 @@ public void SinkWithIntArrayArgument() } }"; - var log = ConfigFromJson(json) - .CreateLogger(); + var log = ConfigFromJson(json) + .CreateLogger(); - DummyRollingFileSink.Reset(); + DummyRollingFileSink.Reset(); - log.Write(Some.InformationEvent()); + log.Write(Some.InformationEvent()); - Assert.Single(DummyRollingFileSink.Emitted); - } + Assert.Single(DummyRollingFileSink.Emitted); + } - [Trait("Bugfix", "#111")] - [Fact] - public void CaseInsensitiveArgumentNameMatching() - { - var json = @"{ + [Trait("Bugfix", "#111")] + [Fact] + public void CaseInsensitiveArgumentNameMatching() + { + var json = @"{ ""Serilog"": { ""Using"": [""TestDummies""], ""WriteTo"": [{ @@ -729,21 +727,21 @@ public void CaseInsensitiveArgumentNameMatching() } }"; - var log = ConfigFromJson(json) - .CreateLogger(); + var log = ConfigFromJson(json) + .CreateLogger(); - DummyRollingFileSink.Reset(); + DummyRollingFileSink.Reset(); - log.Write(Some.InformationEvent()); + log.Write(Some.InformationEvent()); - Assert.Single(DummyRollingFileSink.Emitted); - } + Assert.Single(DummyRollingFileSink.Emitted); + } - [Trait("Bugfix", "#91")] - [Fact] - public void WriteToLoggerWithRestrictedToMinimumLevelIsSupported() - { - var json = @"{ + [Trait("Bugfix", "#91")] + [Fact] + public void WriteToLoggerWithRestrictedToMinimumLevelIsSupported() + { + var json = @"{ ""Serilog"": { ""Using"": [""TestDummies""], ""WriteTo"": [{ @@ -760,22 +758,22 @@ public void WriteToLoggerWithRestrictedToMinimumLevelIsSupported() } }"; - var log = ConfigFromJson(json) - .CreateLogger(); + var log = ConfigFromJson(json) + .CreateLogger(); - DummyRollingFileSink.Reset(); + DummyRollingFileSink.Reset(); - log.Write(Some.InformationEvent()); - log.Write(Some.WarningEvent()); + log.Write(Some.InformationEvent()); + log.Write(Some.WarningEvent()); - Assert.Single(DummyRollingFileSink.Emitted); - } + Assert.Single(DummyRollingFileSink.Emitted); + } - [Trait("Bugfix", "#91")] - [Fact] - public void WriteToSubLoggerWithLevelSwitchIsSupported() - { - var json = @"{ + [Trait("Bugfix", "#91")] + [Fact] + public void WriteToSubLoggerWithLevelSwitchIsSupported() + { + var json = @"{ ""Serilog"": { ""Using"": [""TestDummies""], ""LevelSwitches"": {""$switch1"" : ""Warning"" }, @@ -795,22 +793,22 @@ public void WriteToSubLoggerWithLevelSwitchIsSupported() } }"; - var log = ConfigFromJson(json) - .CreateLogger(); + var log = ConfigFromJson(json) + .CreateLogger(); - DummyRollingFileSink.Reset(); + DummyRollingFileSink.Reset(); - log.Write(Some.InformationEvent()); - log.Write(Some.WarningEvent()); + log.Write(Some.InformationEvent()); + log.Write(Some.WarningEvent()); - Assert.Single(DummyRollingFileSink.Emitted); - } + Assert.Single(DummyRollingFileSink.Emitted); + } - [Trait("Bugfix", "#103")] - [Fact] - public void InconsistentComplexVsScalarArgumentValuesThrowsIOE() - { - var jsonDiscreteValue = @"{ + [Trait("Bugfix", "#103")] + [Fact] + public void InconsistentComplexVsScalarArgumentValuesThrowsIOE() + { + var jsonDiscreteValue = @"{ ""Serilog"": { ""Using"": [""TestDummies""], ""WriteTo"": [{ @@ -820,7 +818,7 @@ public void InconsistentComplexVsScalarArgumentValuesThrowsIOE() } }"; - var jsonComplexValue = @"{ + var jsonComplexValue = @"{ ""Serilog"": { ""Using"": [""TestDummies""], ""WriteTo"": [{ @@ -830,24 +828,24 @@ public void InconsistentComplexVsScalarArgumentValuesThrowsIOE() } }"; - // 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. + // 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)); + var ex = Assert.Throws(() + => ConfigFromJson(jsonDiscreteValue, jsonComplexValue)); - Assert.Contains("The value for the argument", ex.Message); - Assert.Contains("'Serilog:WriteTo:0:Args:pathFormat'", ex.Message); - } + Assert.Contains("The value for the argument", ex.Message); + Assert.Contains("'Serilog:WriteTo:0:Args:pathFormat'", ex.Message); + } - [Fact] - public void DestructureLimitsNestingDepth() - { - var json = @"{ + [Fact] + public void DestructureLimitsNestingDepth() + { + var json = @"{ ""Serilog"": { ""Destructure"": [ { @@ -857,30 +855,30 @@ public void DestructureLimitsNestingDepth() } }"; - var NestedObject = new + var NestedObject = new + { + A = new { - A = new + B = new { - B = new + C = new { - C = new - { - D = "F" - } + D = "F" } } - }; + } + }; - var msg = GetDestructuredProperty(NestedObject, json); + var msg = GetDestructuredProperty(NestedObject, json); - Assert.Contains("C", msg); - Assert.DoesNotContain("D", msg); - } + Assert.Contains("C", msg); + Assert.DoesNotContain("D", msg); + } - [Fact] - public void DestructureLimitsStringLength() - { - var json = @"{ + [Fact] + public void DestructureLimitsStringLength() + { + var json = @"{ ""Serilog"": { ""Destructure"": [ { @@ -890,16 +888,16 @@ public void DestructureLimitsStringLength() } }"; - var inputString = "ABCDEFGH"; - var msg = GetDestructuredProperty(inputString, json); + var inputString = "ABCDEFGH"; + var msg = GetDestructuredProperty(inputString, json); - Assert.Equal("\"AB…\"", msg); - } + Assert.Equal("\"AB…\"", msg); + } - [Fact] - public void DestructureLimitsCollectionCount() - { - var json = @"{ + [Fact] + public void DestructureLimitsCollectionCount() + { + var json = @"{ ""Serilog"": { ""Destructure"": [ { @@ -909,28 +907,28 @@ public void DestructureLimitsCollectionCount() } }"; - var collection = new[] { 1, 2, 3, 4, 5, 6 }; - var msg = GetDestructuredProperty(collection, json); + var collection = new[] { 1, 2, 3, 4, 5, 6 }; + var msg = GetDestructuredProperty(collection, json); - Assert.Contains("3", msg); - Assert.DoesNotContain("4", msg); - } + Assert.Contains("3", msg); + Assert.DoesNotContain("4", msg); + } - private static 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; - } - - [Fact] - public void DestructuringWithCustomExtensionMethodIsApplied() - { - var json = @"{ + private static 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; + } + + [Fact] + public void DestructuringWithCustomExtensionMethodIsApplied() + { + var json = @"{ ""Serilog"": { ""Using"": [""TestDummies""], ""Destructure"": [ @@ -941,20 +939,20 @@ public void DestructuringWithCustomExtensionMethodIsApplied() } }"; - 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(); + 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); - } + Assert.Equal("\"hardcoded\"", formattedProperty); + } - [Fact] - public void DestructuringAsScalarIsAppliedWithShortTypeName() - { - var json = @"{ + [Fact] + public void DestructuringAsScalarIsAppliedWithShortTypeName() + { + var json = @"{ ""Serilog"": { ""Destructure"": [ { @@ -964,21 +962,21 @@ public void DestructuringAsScalarIsAppliedWithShortTypeName() } }"; - LogEvent evt = null; - var log = ConfigFromJson(json) - .WriteTo.Sink(new DelegatingSink(e => evt = e)) - .CreateLogger(); + 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"]; + log.Information("Destructuring as scalar {@Scalarized}", new Version(2, 3)); + var prop = evt.Properties["Scalarized"]; - Assert.IsType(prop); - } + Assert.IsType(prop); + } - [Fact] - public void DestructuringAsScalarIsAppliedWithAssemblyQualifiedName() - { - var json = $@"{{ + [Fact] + public void DestructuringAsScalarIsAppliedWithAssemblyQualifiedName() + { + var json = $@"{{ ""Serilog"": {{ ""Destructure"": [ {{ @@ -988,21 +986,21 @@ public void DestructuringAsScalarIsAppliedWithAssemblyQualifiedName() }} }}"; - LogEvent evt = null; - var log = ConfigFromJson(json) - .WriteTo.Sink(new DelegatingSink(e => evt = e)) - .CreateLogger(); + 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"]; + log.Information("Destructuring as scalar {@Scalarized}", new Version(2, 3)); + var prop = evt.Properties["Scalarized"]; - Assert.IsType(prop); - } + Assert.IsType(prop); + } - [Fact] - public void WriteToSinkIsAppliedWithCustomSink() - { - var json = $@"{{ + [Fact] + public void WriteToSinkIsAppliedWithCustomSink() + { + var json = $@"{{ ""Serilog"": {{ ""Using"": [""TestDummies""], ""WriteTo"": [ @@ -1015,19 +1013,19 @@ public void WriteToSinkIsAppliedWithCustomSink() }} }}"; - var log = ConfigFromJson(json) - .CreateLogger(); + var log = ConfigFromJson(json) + .CreateLogger(); - DummyRollingFileSink.Reset(); - log.Write(Some.InformationEvent()); + DummyRollingFileSink.Reset(); + log.Write(Some.InformationEvent()); - Assert.Single(DummyRollingFileSink.Emitted); - } + Assert.Single(DummyRollingFileSink.Emitted); + } - [Fact] - public void WriteToSinkIsAppliedWithCustomSinkAndMinimumLevel() - { - var json = $@"{{ + [Fact] + public void WriteToSinkIsAppliedWithCustomSinkAndMinimumLevel() + { + var json = $@"{{ ""Serilog"": {{ ""Using"": [""TestDummies""], ""WriteTo"": [ @@ -1041,20 +1039,20 @@ public void WriteToSinkIsAppliedWithCustomSinkAndMinimumLevel() }} }}"; - var log = ConfigFromJson(json) - .CreateLogger(); + var log = ConfigFromJson(json) + .CreateLogger(); - DummyRollingFileSink.Reset(); - log.Write(Some.InformationEvent()); - log.Write(Some.WarningEvent()); + DummyRollingFileSink.Reset(); + log.Write(Some.InformationEvent()); + log.Write(Some.WarningEvent()); - Assert.Single(DummyRollingFileSink.Emitted); - } + Assert.Single(DummyRollingFileSink.Emitted); + } - [Fact] - public void WriteToSinkIsAppliedWithCustomSinkAndLevelSwitch() - { - var json = $@"{{ + [Fact] + public void WriteToSinkIsAppliedWithCustomSinkAndLevelSwitch() + { + var json = $@"{{ ""Serilog"": {{ ""Using"": [""TestDummies""], ""LevelSwitches"": {{""$switch1"": ""Warning"" }}, @@ -1069,20 +1067,20 @@ public void WriteToSinkIsAppliedWithCustomSinkAndLevelSwitch() }} }}"; - var log = ConfigFromJson(json) - .CreateLogger(); + var log = ConfigFromJson(json) + .CreateLogger(); - DummyRollingFileSink.Reset(); - log.Write(Some.InformationEvent()); - log.Write(Some.WarningEvent()); + DummyRollingFileSink.Reset(); + log.Write(Some.InformationEvent()); + log.Write(Some.WarningEvent()); - Assert.Single(DummyRollingFileSink.Emitted); - } + Assert.Single(DummyRollingFileSink.Emitted); + } - [Fact] - public void AuditToSinkIsAppliedWithCustomSink() - { - var json = $@"{{ + [Fact] + public void AuditToSinkIsAppliedWithCustomSink() + { + var json = $@"{{ ""Serilog"": {{ ""Using"": [""TestDummies""], ""AuditTo"": [ @@ -1095,19 +1093,19 @@ public void AuditToSinkIsAppliedWithCustomSink() }} }}"; - var log = ConfigFromJson(json) - .CreateLogger(); + var log = ConfigFromJson(json) + .CreateLogger(); - DummyRollingFileSink.Reset(); - log.Write(Some.InformationEvent()); + DummyRollingFileSink.Reset(); + log.Write(Some.InformationEvent()); - Assert.Single(DummyRollingFileSink.Emitted); - } + Assert.Single(DummyRollingFileSink.Emitted); + } - [Fact] - public void AuditToSinkIsAppliedWithCustomSinkAndMinimumLevel() - { - var json = $@"{{ + [Fact] + public void AuditToSinkIsAppliedWithCustomSinkAndMinimumLevel() + { + var json = $@"{{ ""Serilog"": {{ ""Using"": [""TestDummies""], ""AuditTo"": [ @@ -1121,20 +1119,20 @@ public void AuditToSinkIsAppliedWithCustomSinkAndMinimumLevel() }} }}"; - var log = ConfigFromJson(json) - .CreateLogger(); + var log = ConfigFromJson(json) + .CreateLogger(); - DummyRollingFileSink.Reset(); - log.Write(Some.InformationEvent()); - log.Write(Some.WarningEvent()); + DummyRollingFileSink.Reset(); + log.Write(Some.InformationEvent()); + log.Write(Some.WarningEvent()); - Assert.Single(DummyRollingFileSink.Emitted); - } + Assert.Single(DummyRollingFileSink.Emitted); + } - [Fact] - public void AuditToSinkIsAppliedWithCustomSinkAndLevelSwitch() - { - var json = $@"{{ + [Fact] + public void AuditToSinkIsAppliedWithCustomSinkAndLevelSwitch() + { + var json = $@"{{ ""Serilog"": {{ ""Using"": [""TestDummies""], ""LevelSwitches"": {{""$switch1"": ""Warning"" }}, @@ -1149,22 +1147,22 @@ public void AuditToSinkIsAppliedWithCustomSinkAndLevelSwitch() }} }}"; - var log = ConfigFromJson(json) - .CreateLogger(); + var log = ConfigFromJson(json) + .CreateLogger(); - DummyRollingFileSink.Reset(); - log.Write(Some.InformationEvent()); - log.Write(Some.WarningEvent()); + DummyRollingFileSink.Reset(); + log.Write(Some.InformationEvent()); + log.Write(Some.WarningEvent()); - Assert.Single(DummyRollingFileSink.Emitted); - } + Assert.Single(DummyRollingFileSink.Emitted); + } - [Fact] - public void EnrichWithIsAppliedWithCustomEnricher() - { - LogEvent evt = null; + [Fact] + public void EnrichWithIsAppliedWithCustomEnricher() + { + LogEvent evt = null; - var json = $@"{{ + var json = $@"{{ ""Serilog"": {{ ""Using"": [""TestDummies""], ""Enrich"": [ @@ -1177,22 +1175,22 @@ public void EnrichWithIsAppliedWithCustomEnricher() }} }}"; - var log = ConfigFromJson(json) - .WriteTo.Sink(new DelegatingSink(e => evt = e)) - .CreateLogger(); + var log = ConfigFromJson(json) + .WriteTo.Sink(new DelegatingSink(e => evt = e)) + .CreateLogger(); - log.Write(Some.InformationEvent()); + log.Write(Some.InformationEvent()); - Assert.NotNull(evt); - Assert.True(evt.Properties.ContainsKey("ThreadId"), "Event should have enriched property ThreadId"); - } + Assert.NotNull(evt); + Assert.True(evt.Properties.ContainsKey("ThreadId"), "Event should have enriched property ThreadId"); + } - [Fact] - public void FilterWithIsAppliedWithCustomFilter() - { - LogEvent evt = null; + [Fact] + public void FilterWithIsAppliedWithCustomFilter() + { + LogEvent evt = null; - var json = $@"{{ + var json = $@"{{ ""Serilog"": {{ ""Using"": [""TestDummies""], ""Filter"": [ @@ -1205,14 +1203,13 @@ public void FilterWithIsAppliedWithCustomFilter() }} }}"; - var log = ConfigFromJson(json) - .WriteTo.Sink(new DelegatingSink(e => evt = e)) - .CreateLogger(); + 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); - } + 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/Serilog.Settings.Configuration.Tests/DllScanningAssemblyFinderTests.cs b/test/Serilog.Settings.Configuration.Tests/DllScanningAssemblyFinderTests.cs index 050bf540..120ac3c6 100644 --- a/test/Serilog.Settings.Configuration.Tests/DllScanningAssemblyFinderTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/DllScanningAssemblyFinderTests.cs @@ -3,65 +3,62 @@ using System.IO; #endif -using Xunit; - using Serilog.Settings.Configuration.Assemblies; -namespace Serilog.Settings.Configuration.Tests +namespace Serilog.Settings.Configuration.Tests; + +public class DllScanningAssemblyFinderTests { - public class DllScanningAssemblyFinderTests - { - const string BinDir1 = "bin1"; - const string BinDir2 = "bin2"; - const string BinDir3 = "bin3"; + const string BinDir1 = "bin1"; + const string BinDir2 = "bin2"; + const string BinDir3 = "bin3"; - [Fact] - public void ShouldProbeCurrentDirectory() - { - var assemblyNames = new DllScanningAssemblyFinder().FindAssembliesContainingName("TestDummies"); - Assert.Single(assemblyNames); - } + [Fact] + public void ShouldProbeCurrentDirectory() + { + var assemblyNames = new DllScanningAssemblyFinder().FindAssembliesContainingName("TestDummies"); + Assert.Single(assemblyNames); + } #if NETFRAMEWORK - [Fact] - public void ShouldProbePrivateBinPath() - { - var d1 = GetOrCreateDirectory(BinDir1); - var d2 = GetOrCreateDirectory(BinDir2); - var d3 = GetOrCreateDirectory(BinDir3); - - DirectoryInfo GetOrCreateDirectory(string name) - => Directory.Exists(name) ? new DirectoryInfo(name) : Directory.CreateDirectory(name); + [Fact] + public void ShouldProbePrivateBinPath() + { + var d1 = GetOrCreateDirectory(BinDir1); + var d2 = GetOrCreateDirectory(BinDir2); + var d3 = GetOrCreateDirectory(BinDir3); - File.Copy("TestDummies.dll", $"{BinDir1}/customSink1.dll", true); - File.Copy("TestDummies.dll", $"{BinDir2}/customSink2.dll", true); - File.Copy("TestDummies.dll", $"{BinDir3}/thirdpartydependency.dll", true); + DirectoryInfo GetOrCreateDirectory(string name) + => Directory.Exists(name) ? new DirectoryInfo(name) : Directory.CreateDirectory(name); - var ad = AppDomain.CreateDomain("serilog", null, - new AppDomainSetup - { - ApplicationBase = AppDomain.CurrentDomain.BaseDirectory, - PrivateBinPath = $"{d1.Name};{d2.FullName};{d3.Name}" - }); + File.Copy("TestDummies.dll", $"{BinDir1}/customSink1.dll", true); + File.Copy("TestDummies.dll", $"{BinDir2}/customSink2.dll", true); + File.Copy("TestDummies.dll", $"{BinDir3}/thirdpartydependency.dll", true); - try + var ad = AppDomain.CreateDomain("serilog", null, + new AppDomainSetup { - ad.DoCallBack(DoTestInner); - } - finally - { - AppDomain.Unload(ad); - Directory.Delete(BinDir1, true); - Directory.Delete(BinDir2, true); - Directory.Delete(BinDir3, true); - } + ApplicationBase = AppDomain.CurrentDomain.BaseDirectory, + PrivateBinPath = $"{d1.Name};{d2.FullName};{d3.Name}" + }); - static void DoTestInner() - { - var assemblyNames = new DllScanningAssemblyFinder().FindAssembliesContainingName("customSink"); - Assert.Equal(2, assemblyNames.Count); - } + try + { + ad.DoCallBack(DoTestInner); + } + finally + { + AppDomain.Unload(ad); + Directory.Delete(BinDir1, true); + Directory.Delete(BinDir2, true); + Directory.Delete(BinDir3, true); + } + + static void DoTestInner() + { + var assemblyNames = new DllScanningAssemblyFinder().FindAssembliesContainingName("customSink"); + Assert.Equal(2, assemblyNames.Count); } -#endif } +#endif } diff --git a/test/Serilog.Settings.Configuration.Tests/DummyLoggerConfigurationExtensions.cs b/test/Serilog.Settings.Configuration.Tests/DummyLoggerConfigurationExtensions.cs index 96754464..774c8cb6 100644 --- a/test/Serilog.Settings.Configuration.Tests/DummyLoggerConfigurationExtensions.cs +++ b/test/Serilog.Settings.Configuration.Tests/DummyLoggerConfigurationExtensions.cs @@ -1,29 +1,27 @@ -using System; -using Serilog.Configuration; +using Serilog.Configuration; using Serilog.Events; using Serilog.Formatting; -namespace Serilog.Settings.Configuration.Tests +namespace Serilog.Settings.Configuration.Tests; + +static class DummyLoggerConfigurationExtensions { - static class DummyLoggerConfigurationExtensions + public static LoggerConfiguration DummyRollingFile( + LoggerSinkConfiguration loggerSinkConfiguration, + string pathFormat, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + string outputTemplate = null, + IFormatProvider formatProvider = null) { - public static LoggerConfiguration DummyRollingFile( - LoggerSinkConfiguration loggerSinkConfiguration, - string pathFormat, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - string outputTemplate = null, - IFormatProvider formatProvider = null) - { - return null; - } + return null; + } - public static LoggerConfiguration DummyRollingFile( - LoggerSinkConfiguration loggerSinkConfiguration, - ITextFormatter formatter, - string pathFormat, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) - { - return null; - } + public static LoggerConfiguration DummyRollingFile( + LoggerSinkConfiguration loggerSinkConfiguration, + ITextFormatter formatter, + string pathFormat, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) + { + return null; } } diff --git a/test/Serilog.Settings.Configuration.Tests/DummyLoggerConfigurationWithMultipleMethodsExtensions.cs b/test/Serilog.Settings.Configuration.Tests/DummyLoggerConfigurationWithMultipleMethodsExtensions.cs index b6be5706..5bc743f1 100644 --- a/test/Serilog.Settings.Configuration.Tests/DummyLoggerConfigurationWithMultipleMethodsExtensions.cs +++ b/test/Serilog.Settings.Configuration.Tests/DummyLoggerConfigurationWithMultipleMethodsExtensions.cs @@ -1,42 +1,40 @@ -using System; -using Serilog.Configuration; +using Serilog.Configuration; using Serilog.Events; using Serilog.Formatting; -namespace Serilog.Settings.Configuration.Tests -{ - using System.Collections.Generic; +namespace Serilog.Settings.Configuration.Tests; + +using System.Collections.Generic; - static class DummyLoggerConfigurationWithMultipleMethodsExtensions +static class DummyLoggerConfigurationWithMultipleMethodsExtensions +{ + public static LoggerConfiguration DummyRollingFile( + LoggerSinkConfiguration loggerSinkConfiguration, + ITextFormatter formatter, + IEnumerable pathFormat, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + string outputTemplate = null, + IFormatProvider formatProvider = null) { - public static LoggerConfiguration DummyRollingFile( - LoggerSinkConfiguration loggerSinkConfiguration, - ITextFormatter formatter, - IEnumerable pathFormat, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - string outputTemplate = null, - IFormatProvider formatProvider = null) - { - return 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, + 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; - } + public static LoggerConfiguration DummyRollingFile( + LoggerSinkConfiguration loggerSinkConfiguration, + string pathFormat, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) + { + return null; } } diff --git a/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs b/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs index 6b439f6e..8df43fb5 100644 --- a/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs @@ -1,17 +1,15 @@ using Serilog.Core; using Serilog.Events; using Serilog.Settings.Configuration.Tests.Support; - -using Xunit; using Microsoft.Extensions.Configuration; using TestDummies.Console; -namespace Serilog.Settings.Configuration.Tests +namespace Serilog.Settings.Configuration.Tests; + +public class DynamicLevelChangeTests { - public class DynamicLevelChangeTests - { - const string DefaultConfig = @"{ + const string DefaultConfig = @"{ 'Serilog': { 'Using': [ 'TestDummies' ], 'MinimumLevel': { @@ -37,78 +35,77 @@ public class DynamicLevelChangeTests } }"; - readonly ReloadableConfigurationSource _configSource; + readonly ReloadableConfigurationSource _configSource; + + public DynamicLevelChangeTests() + { + _configSource = new ReloadableConfigurationSource(JsonStringConfigSource.LoadData(DefaultConfig)); + } - public DynamicLevelChangeTests() + [Fact] + public void ShouldRespectDynamicLevelChanges() + { + using (var logger = new LoggerConfiguration() + .ReadFrom + .Configuration(new ConfigurationBuilder().Add(_configSource).Build()) + .CreateLogger()) { - _configSource = new ReloadableConfigurationSource(JsonStringConfigSource.LoadData(DefaultConfig)); + DummyConsoleSink.Emitted.Clear(); + logger.Write(Some.DebugEvent()); + Assert.Empty(DummyConsoleSink.Emitted); + + DummyConsoleSink.Emitted.Clear(); + UpdateConfig(minimumLevel: LogEventLevel.Debug); + logger.Write(Some.DebugEvent()); + Assert.Empty(DummyConsoleSink.Emitted); + + DummyConsoleSink.Emitted.Clear(); + UpdateConfig(switchLevel: LogEventLevel.Debug); + logger.Write(Some.DebugEvent()); + logger.ForContext(Constants.SourceContextPropertyName, "Root.Test").Write(Some.DebugEvent()); + Assert.Single(DummyConsoleSink.Emitted); + + DummyConsoleSink.Emitted.Clear(); + UpdateConfig(overrideLevel: LogEventLevel.Debug); + logger.ForContext(Constants.SourceContextPropertyName, "Root.Test").Write(Some.DebugEvent()); + Assert.Single(DummyConsoleSink.Emitted); + + DummyConsoleSink.Emitted.Clear(); + UpdateConfig(filterExpression: "Prop = 'Val_1'"); + logger.Write(Some.DebugEvent()); + logger.ForContext("Prop", "Val_1").Write(Some.DebugEvent()); + Assert.Single(DummyConsoleSink.Emitted); + + DummyConsoleSink.Emitted.Clear(); + UpdateConfig(filterExpression: "Prop = 'Val_2'"); + logger.Write(Some.DebugEvent()); + logger.ForContext("Prop", "Val_1").Write(Some.DebugEvent()); + Assert.Empty(DummyConsoleSink.Emitted); } + } - [Fact] - public void ShouldRespectDynamicLevelChanges() + void UpdateConfig(LogEventLevel? minimumLevel = null, LogEventLevel? switchLevel = null, LogEventLevel? overrideLevel = null, string filterExpression = null) + { + if (minimumLevel.HasValue) { - using (var logger = new LoggerConfiguration() - .ReadFrom - .Configuration(new ConfigurationBuilder().Add(_configSource).Build()) - .CreateLogger()) - { - DummyConsoleSink.Emitted.Clear(); - logger.Write(Some.DebugEvent()); - Assert.Empty(DummyConsoleSink.Emitted); - - DummyConsoleSink.Emitted.Clear(); - UpdateConfig(minimumLevel: LogEventLevel.Debug); - logger.Write(Some.DebugEvent()); - Assert.Empty(DummyConsoleSink.Emitted); - - DummyConsoleSink.Emitted.Clear(); - UpdateConfig(switchLevel: LogEventLevel.Debug); - logger.Write(Some.DebugEvent()); - logger.ForContext(Constants.SourceContextPropertyName, "Root.Test").Write(Some.DebugEvent()); - Assert.Single(DummyConsoleSink.Emitted); - - DummyConsoleSink.Emitted.Clear(); - UpdateConfig(overrideLevel: LogEventLevel.Debug); - logger.ForContext(Constants.SourceContextPropertyName, "Root.Test").Write(Some.DebugEvent()); - Assert.Single(DummyConsoleSink.Emitted); - - DummyConsoleSink.Emitted.Clear(); - UpdateConfig(filterExpression: "Prop = 'Val_1'"); - logger.Write(Some.DebugEvent()); - logger.ForContext("Prop", "Val_1").Write(Some.DebugEvent()); - Assert.Single(DummyConsoleSink.Emitted); - - DummyConsoleSink.Emitted.Clear(); - UpdateConfig(filterExpression: "Prop = 'Val_2'"); - logger.Write(Some.DebugEvent()); - logger.ForContext("Prop", "Val_1").Write(Some.DebugEvent()); - Assert.Empty(DummyConsoleSink.Emitted); - } + _configSource.Set("Serilog:MinimumLevel:Default", minimumLevel.Value.ToString()); } - void UpdateConfig(LogEventLevel? minimumLevel = null, LogEventLevel? switchLevel = null, LogEventLevel? overrideLevel = null, string filterExpression = null) + if (switchLevel.HasValue) { - if (minimumLevel.HasValue) - { - _configSource.Set("Serilog:MinimumLevel:Default", minimumLevel.Value.ToString()); - } - - if (switchLevel.HasValue) - { - _configSource.Set("Serilog:LevelSwitches:$mySwitch", switchLevel.Value.ToString()); - } - - if (overrideLevel.HasValue) - { - _configSource.Set("Serilog:MinimumLevel:Override:Root.Test", overrideLevel.Value.ToString()); - } + _configSource.Set("Serilog:LevelSwitches:$mySwitch", switchLevel.Value.ToString()); + } - if (filterExpression != null) - { - _configSource.Set("Serilog:FilterSwitches:$myFilter", filterExpression); - } + if (overrideLevel.HasValue) + { + _configSource.Set("Serilog:MinimumLevel:Override:Root.Test", overrideLevel.Value.ToString()); + } - _configSource.Reload(); + if (filterExpression != null) + { + _configSource.Set("Serilog:FilterSwitches:$myFilter", filterExpression); } + + _configSource.Reload(); } } diff --git a/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs b/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs index f7eee44f..c022aee3 100644 --- a/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs @@ -1,29 +1,27 @@ -using System; using Microsoft.Extensions.Configuration; using Serilog.Events; using Serilog.Settings.Configuration.Tests.Support; -using Xunit; -namespace Serilog.Settings.Configuration.Tests +namespace Serilog.Settings.Configuration.Tests; + +public class LoggerConfigurationExtensionsTests { - public class LoggerConfigurationExtensionsTests + [Fact] + public void ReadFromConfigurationShouldNotThrowOnEmptyConfiguration() + { + Action act = () => new LoggerConfiguration().ReadFrom.Configuration(new ConfigurationBuilder().Build()); + + // should not throw + act(); + } + + [Fact] + [Trait("BugFix", "https://github.com/serilog/serilog-settings-configuration/issues/143")] + public void ReadFromConfigurationSectionReadsFromAnArbitrarySection() { - [Fact] - public void ReadFromConfigurationShouldNotThrowOnEmptyConfiguration() - { - Action act = () => new LoggerConfiguration().ReadFrom.Configuration(new ConfigurationBuilder().Build()); - - // should not throw - act(); - } - - [Fact] - [Trait("BugFix", "https://github.com/serilog/serilog-settings-configuration/issues/143")] - public void ReadFromConfigurationSectionReadsFromAnArbitrarySection() - { - LogEvent evt = null; - - var json = @"{ + LogEvent evt = null; + + var json = @"{ ""NotSerilog"": { ""Properties"": { ""App"": ""Test"" @@ -31,28 +29,28 @@ public void ReadFromConfigurationSectionReadsFromAnArbitrarySection() } }"; - var config = new ConfigurationBuilder() - .AddJsonString(json) - .Build(); + var config = new ConfigurationBuilder() + .AddJsonString(json) + .Build(); #pragma warning disable CS0618 // Type or member is obsolete - var log = new LoggerConfiguration() - .ReadFrom.ConfigurationSection(config.GetSection("NotSerilog")) + var log = new LoggerConfiguration() + .ReadFrom.ConfigurationSection(config.GetSection("NotSerilog")) #pragma warning restore CS0618 // Type or member is obsolete - .WriteTo.Sink(new DelegatingSink(e => evt = e)) - .CreateLogger(); + .WriteTo.Sink(new DelegatingSink(e => evt = e)) + .CreateLogger(); - log.Information("Has a test property"); + log.Information("Has a test property"); - Assert.NotNull(evt); - Assert.Equal("Test", evt.Properties["App"].LiteralValue()); - } + Assert.NotNull(evt); + Assert.Equal("Test", evt.Properties["App"].LiteralValue()); + } - [Fact] - [Trait("BugFix", "https://github.com/serilog/serilog-settings-configuration/issues/143")] - public void ReadFromConfigurationSectionThrowsWhenTryingToCallConfigurationMethodWithIConfigurationParam() - { - var json = @"{ + [Fact] + [Trait("BugFix", "https://github.com/serilog/serilog-settings-configuration/issues/143")] + public void ReadFromConfigurationSectionThrowsWhenTryingToCallConfigurationMethodWithIConfigurationParam() + { + var json = @"{ ""NotSerilog"": { ""Using"": [""TestDummies""], ""WriteTo"": [{ @@ -62,28 +60,28 @@ public void ReadFromConfigurationSectionThrowsWhenTryingToCallConfigurationMetho } }"; - var config = new ConfigurationBuilder() - .AddJsonString(json) - .Build(); + var config = new ConfigurationBuilder() + .AddJsonString(json) + .Build(); - var exception = Assert.Throws(() => + var exception = Assert.Throws(() => #pragma warning disable CS0618 // Type or member is obsolete - new LoggerConfiguration() - .ReadFrom.ConfigurationSection(config.GetSection("NotSerilog")) + new LoggerConfiguration() + .ReadFrom.ConfigurationSection(config.GetSection("NotSerilog")) #pragma warning restore CS0618 // Type or member is obsolete - .CreateLogger()); + .CreateLogger()); - Assert.Equal("Trying to invoke a configuration method accepting a `IConfiguration` argument. " + - "This is not supported when only a `IConfigSection` has been provided. " + - "(method 'Serilog.LoggerConfiguration DummyWithConfiguration(Serilog.Configuration.LoggerSinkConfiguration, Microsoft.Extensions.Configuration.IConfiguration, Serilog.Events.LogEventLevel)')", - exception.Message); + Assert.Equal("Trying to invoke a configuration method accepting a `IConfiguration` argument. " + + "This is not supported when only a `IConfigSection` has been provided. " + + "(method 'Serilog.LoggerConfiguration DummyWithConfiguration(Serilog.Configuration.LoggerSinkConfiguration, Microsoft.Extensions.Configuration.IConfiguration, Serilog.Events.LogEventLevel)')", + exception.Message); - } + } - [Fact] - public void ReadFromConfigurationDoesNotThrowWhenTryingToCallConfigurationMethodWithIConfigurationParam() - { - var json = @"{ + [Fact] + public void ReadFromConfigurationDoesNotThrowWhenTryingToCallConfigurationMethodWithIConfigurationParam() + { + var json = @"{ ""NotSerilog"": { ""Using"": [""TestDummies""], ""WriteTo"": [{ @@ -93,21 +91,21 @@ public void ReadFromConfigurationDoesNotThrowWhenTryingToCallConfigurationMethod } }"; - var config = new ConfigurationBuilder() - .AddJsonString(json) - .Build(); + var config = new ConfigurationBuilder() + .AddJsonString(json) + .Build(); - _ = new LoggerConfiguration() - .ReadFrom.Configuration(config, "NotSerilog") - .CreateLogger(); + _ = new LoggerConfiguration() + .ReadFrom.Configuration(config, "NotSerilog") + .CreateLogger(); - } + } - [Fact] - [Trait("BugFix", "https://github.com/serilog/serilog-settings-configuration/issues/143")] - public void ReadFromConfigurationSectionDoesNotThrowWhenTryingToCallConfigurationMethodWithOptionalIConfigurationParam() - { - var json = @"{ + [Fact] + [Trait("BugFix", "https://github.com/serilog/serilog-settings-configuration/issues/143")] + public void ReadFromConfigurationSectionDoesNotThrowWhenTryingToCallConfigurationMethodWithOptionalIConfigurationParam() + { + var json = @"{ ""NotSerilog"": { ""Using"": [""TestDummies""], ""WriteTo"": [{ @@ -117,17 +115,16 @@ public void ReadFromConfigurationSectionDoesNotThrowWhenTryingToCallConfiguratio } }"; - var config = new ConfigurationBuilder() - .AddJsonString(json) - .Build(); + var config = new ConfigurationBuilder() + .AddJsonString(json) + .Build(); - // this should not throw because DummyWithOptionalConfiguration accepts an optional config + // this should not throw because DummyWithOptionalConfiguration accepts an optional config #pragma warning disable CS0618 // Type or member is obsolete - new LoggerConfiguration() - .ReadFrom.ConfigurationSection(config.GetSection("NotSerilog")) + new LoggerConfiguration() + .ReadFrom.ConfigurationSection(config.GetSection("NotSerilog")) #pragma warning restore CS0618 // Type or member is obsolete - .CreateLogger(); + .CreateLogger(); - } } } diff --git a/test/Serilog.Settings.Configuration.Tests/ObjectArgumentValueTests.cs b/test/Serilog.Settings.Configuration.Tests/ObjectArgumentValueTests.cs index 9772424e..ee775e4e 100644 --- a/test/Serilog.Settings.Configuration.Tests/ObjectArgumentValueTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ObjectArgumentValueTests.cs @@ -1,72 +1,67 @@ -using System; - -using Microsoft.Extensions.Configuration; - -using Xunit; +using Microsoft.Extensions.Configuration; // ReSharper disable UnusedMember.Local // ReSharper disable UnusedParameter.Local // ReSharper disable UnusedType.Local -namespace Serilog.Settings.Configuration.Tests +namespace Serilog.Settings.Configuration.Tests; + +public class ObjectArgumentValueTests { - public class ObjectArgumentValueTests - { - readonly IConfigurationRoot _config; + readonly IConfigurationRoot _config; - public ObjectArgumentValueTests() - { - _config = new ConfigurationBuilder() - .AddJsonFile("ObjectArgumentValueTests.json") - .Build(); - } + public ObjectArgumentValueTests() + { + _config = new ConfigurationBuilder() + .AddJsonFile("ObjectArgumentValueTests.json") + .Build(); + } - [Theory] - [InlineData("case_1", typeof(A), "new A(1, 23:59:59, http://dot.com/, \"d\")")] - [InlineData("case_2", typeof(B), "new B(2, new A(3, new D()), null)")] - [InlineData("case_3", typeof(E), "new E(\"1\", \"2\", \"3\")")] - [InlineData("case_4", typeof(F), "new F(\"paramType\", new E(1, 2, 3, 4))")] - [InlineData("case_5", typeof(G), "new G()")] - [InlineData("case_6", typeof(G), "new G(3, 4)")] - public void ShouldBindToConstructorArguments(string caseSection, Type targetType, string expectedExpression) - { - var testSection = _config.GetSection(caseSection); + [Theory] + [InlineData("case_1", typeof(A), "new A(1, 23:59:59, http://dot.com/, \"d\")")] + [InlineData("case_2", typeof(B), "new B(2, new A(3, new D()), null)")] + [InlineData("case_3", typeof(E), "new E(\"1\", \"2\", \"3\")")] + [InlineData("case_4", typeof(F), "new F(\"paramType\", new E(1, 2, 3, 4))")] + [InlineData("case_5", typeof(G), "new G()")] + [InlineData("case_6", typeof(G), "new G(3, 4)")] + public void ShouldBindToConstructorArguments(string caseSection, Type targetType, string expectedExpression) + { + var testSection = _config.GetSection(caseSection); - Assert.True(ObjectArgumentValue.TryBuildCtorExpression(testSection, targetType, new(), out var ctorExpression)); - Assert.Equal(expectedExpression, ctorExpression.ToString()); - } + Assert.True(ObjectArgumentValue.TryBuildCtorExpression(testSection, targetType, new(), out var ctorExpression)); + Assert.Equal(expectedExpression, ctorExpression.ToString()); + } - class A - { - public A(int a, TimeSpan b, Uri c, string d = "d") { } - public A(int a, C c) { } - } + class A + { + public A(int a, TimeSpan b, Uri c, string d = "d") { } + public A(int a, C c) { } + } - class B - { - public B(int b, A a, long? c = null) { } - } + class B + { + public B(int b, A a, long? c = null) { } + } - interface C { } + interface C { } - class D : C { } + class D : C { } - class E - { - public E(int a, int b, int c, int d = 4) { } - public E(int a, string b, string c) { } - public E(string a, string b, string c) { } - } + class E + { + public E(int a, int b, int c, int d = 4) { } + public E(int a, string b, string c) { } + public E(string a, string b, string c) { } + } - class F - { - public F(string type, E e) { } - } + class F + { + public F(string type, E e) { } + } - class G - { - public G() { } - public G(int a = 1, int b = 2) { } - } + class G + { + public G() { } + public G(int a = 1, int b = 2) { } } } 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 1453c836..a08c9bbd 100644 --- a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj +++ b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj @@ -28,4 +28,8 @@ + + + + diff --git a/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs b/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs index 642e85e7..08599f1b 100644 --- a/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs @@ -1,238 +1,234 @@ -using System; -using Serilog.Core; +using Serilog.Core; using Serilog.Events; using Serilog.Formatting; using Serilog.Formatting.Json; using Serilog.Settings.Configuration.Tests.Support; -using Xunit; +namespace Serilog.Settings.Configuration.Tests; -namespace Serilog.Settings.Configuration.Tests +public class StringArgumentValueTests { - public class StringArgumentValueTests + [Fact] + public void StringValuesConvertToDefaultInstancesIfTargetIsInterface() { - [Fact] - public void StringValuesConvertToDefaultInstancesIfTargetIsInterface() - { - var stringArgumentValue = new StringArgumentValue("Serilog.Formatting.Json.JsonFormatter, Serilog"); + var stringArgumentValue = new StringArgumentValue("Serilog.Formatting.Json.JsonFormatter, Serilog"); - var result = stringArgumentValue.ConvertTo(typeof(ITextFormatter), new ResolutionContext()); + var result = stringArgumentValue.ConvertTo(typeof(ITextFormatter), new ResolutionContext()); - Assert.IsType(result); - } + Assert.IsType(result); + } - [Fact] - public void StringValuesConvertToDefaultInstancesIfTargetIsAbstractClass() - { - var stringArgumentValue = new StringArgumentValue("Serilog.Settings.Configuration.Tests.Support.ConcreteClass, Serilog.Settings.Configuration.Tests"); + [Fact] + public void StringValuesConvertToDefaultInstancesIfTargetIsAbstractClass() + { + var stringArgumentValue = new StringArgumentValue("Serilog.Settings.Configuration.Tests.Support.ConcreteClass, Serilog.Settings.Configuration.Tests"); - var result = stringArgumentValue.ConvertTo(typeof(AbstractClass), new ResolutionContext()); + var result = stringArgumentValue.ConvertTo(typeof(AbstractClass), new ResolutionContext()); - Assert.IsType(result); - } + Assert.IsType(result); + } - [Theory] - [InlineData("My.NameSpace.Class+InnerClass::Member", - "My.NameSpace.Class+InnerClass", "Member")] - [InlineData(" TrimMe.NameSpace.Class::NeedsTrimming ", - "TrimMe.NameSpace.Class", "NeedsTrimming")] - [InlineData("My.NameSpace.Class::Member", - "My.NameSpace.Class", "Member")] - [InlineData("My.NameSpace.Class::Member, MyAssembly", - "My.NameSpace.Class, MyAssembly", "Member")] - [InlineData("My.NameSpace.Class::Member, MyAssembly, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", - "My.NameSpace.Class, MyAssembly, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "Member")] - [InlineData("Just a random string with :: in it", - null, null)] - [InlineData("Its::a::trapWithColonsAppearingTwice", - null, null)] - [InlineData("ThereIsNoMemberHere::", - null, null)] - [InlineData(null, - null, null)] - [InlineData(" ", - null, null)] - // a full-qualified type name should not be considered a static member accessor - [InlineData("My.NameSpace.Class, MyAssembly, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", - null, null)] - public void TryParseStaticMemberAccessorReturnsExpectedResults(string input, string expectedAccessorType, string expectedPropertyName) + [Theory] + [InlineData("My.NameSpace.Class+InnerClass::Member", + "My.NameSpace.Class+InnerClass", "Member")] + [InlineData(" TrimMe.NameSpace.Class::NeedsTrimming ", + "TrimMe.NameSpace.Class", "NeedsTrimming")] + [InlineData("My.NameSpace.Class::Member", + "My.NameSpace.Class", "Member")] + [InlineData("My.NameSpace.Class::Member, MyAssembly", + "My.NameSpace.Class, MyAssembly", "Member")] + [InlineData("My.NameSpace.Class::Member, MyAssembly, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "My.NameSpace.Class, MyAssembly, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "Member")] + [InlineData("Just a random string with :: in it", + null, null)] + [InlineData("Its::a::trapWithColonsAppearingTwice", + null, null)] + [InlineData("ThereIsNoMemberHere::", + null, null)] + [InlineData(null, + null, null)] + [InlineData(" ", + null, null)] + // a full-qualified type name should not be considered a static member accessor + [InlineData("My.NameSpace.Class, MyAssembly, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + null, null)] + public void TryParseStaticMemberAccessorReturnsExpectedResults(string input, string expectedAccessorType, string expectedPropertyName) + { + var actual = StringArgumentValue.TryParseStaticMemberAccessor(input, + out var actualAccessorType, + out var actualMemberName); + + if (expectedAccessorType == null) { - var actual = StringArgumentValue.TryParseStaticMemberAccessor(input, - out var actualAccessorType, - out var actualMemberName); - - if (expectedAccessorType == null) - { - Assert.False(actual, $"Should not parse {input}"); - } - else - { - Assert.True(actual, $"should successfully parse {input}"); - Assert.Equal(expectedAccessorType, actualAccessorType); - Assert.Equal(expectedPropertyName, actualMemberName); - } + Assert.False(actual, $"Should not parse {input}"); } - - [Theory] - [InlineData("Serilog.Formatting.Json.JsonFormatter", typeof(JsonFormatter))] - [InlineData("Serilog.Formatting.Json.JsonFormatter, Serilog", typeof(JsonFormatter))] - [InlineData("Serilog.ConfigurationLoggerConfigurationExtensions", typeof(ConfigurationLoggerConfigurationExtensions))] - public void FindTypeSupportsSimpleNamesForSerilogTypes(string input, Type targetType) + else { - var type = StringArgumentValue.FindType(input); - Assert.Equal(targetType, type); + Assert.True(actual, $"should successfully parse {input}"); + Assert.Equal(expectedAccessorType, actualAccessorType); + Assert.Equal(expectedPropertyName, actualMemberName); } + } - [Theory] - [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::InterfaceProperty, Serilog.Settings.Configuration.Tests", typeof(IAmAnInterface))] - [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::AbstractProperty, Serilog.Settings.Configuration.Tests", typeof(AnAbstractClass))] - [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::InterfaceField, Serilog.Settings.Configuration.Tests", typeof(IAmAnInterface))] - [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::AbstractField, Serilog.Settings.Configuration.Tests", typeof(AnAbstractClass))] - public void StaticMembersAccessorsCanBeUsedForAbstractTypes(string input, Type targetType) - { - var stringArgumentValue = new StringArgumentValue(input); + [Theory] + [InlineData("Serilog.Formatting.Json.JsonFormatter", typeof(JsonFormatter))] + [InlineData("Serilog.Formatting.Json.JsonFormatter, Serilog", typeof(JsonFormatter))] + [InlineData("Serilog.ConfigurationLoggerConfigurationExtensions", typeof(ConfigurationLoggerConfigurationExtensions))] + public void FindTypeSupportsSimpleNamesForSerilogTypes(string input, Type targetType) + { + var type = StringArgumentValue.FindType(input); + Assert.Equal(targetType, type); + } - var actual = stringArgumentValue.ConvertTo(targetType, new ResolutionContext()); + [Theory] + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::InterfaceProperty, Serilog.Settings.Configuration.Tests", typeof(IAmAnInterface))] + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::AbstractProperty, Serilog.Settings.Configuration.Tests", typeof(AnAbstractClass))] + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::InterfaceField, Serilog.Settings.Configuration.Tests", typeof(IAmAnInterface))] + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::AbstractField, Serilog.Settings.Configuration.Tests", typeof(AnAbstractClass))] + public void StaticMembersAccessorsCanBeUsedForAbstractTypes(string input, Type targetType) + { + var stringArgumentValue = new StringArgumentValue(input); - Assert.IsAssignableFrom(targetType, actual); - Assert.Equal(ConcreteImpl.Instance, actual); - } + var actual = stringArgumentValue.ConvertTo(targetType, new ResolutionContext()); - [Theory] - [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::FuncIntParseField, Serilog.Settings.Configuration.Tests", typeof(Func))] - [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::NamedIntParseField, Serilog.Settings.Configuration.Tests", typeof(NamedIntParse))] - [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::FuncIntParseProperty, Serilog.Settings.Configuration.Tests", typeof(Func))] - [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::NamedIntParseProperty, Serilog.Settings.Configuration.Tests", typeof(NamedIntParse))] - [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::IntParseMethod, Serilog.Settings.Configuration.Tests", typeof(NamedIntParse))] - [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::IntParseMethod, Serilog.Settings.Configuration.Tests", typeof(Func))] - public void StaticMembersAccessorsCanBeUsedForDelegateTypes(string input, Type targetType) - { - var stringArgumentValue = new StringArgumentValue(input); + Assert.IsAssignableFrom(targetType, actual); + Assert.Equal(ConcreteImpl.Instance, actual); + } - var actual = stringArgumentValue.ConvertTo(targetType, new ResolutionContext()); + [Theory] + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::FuncIntParseField, Serilog.Settings.Configuration.Tests", typeof(Func))] + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::NamedIntParseField, Serilog.Settings.Configuration.Tests", typeof(NamedIntParse))] + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::FuncIntParseProperty, Serilog.Settings.Configuration.Tests", typeof(Func))] + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::NamedIntParseProperty, Serilog.Settings.Configuration.Tests", typeof(NamedIntParse))] + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::IntParseMethod, Serilog.Settings.Configuration.Tests", typeof(NamedIntParse))] + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::IntParseMethod, Serilog.Settings.Configuration.Tests", typeof(Func))] + public void StaticMembersAccessorsCanBeUsedForDelegateTypes(string input, Type targetType) + { + var stringArgumentValue = new StringArgumentValue(input); - Assert.IsAssignableFrom(targetType, actual); - var parser = (Delegate)actual; - Assert.Equal(100, parser.DynamicInvoke("100")); - } + var actual = stringArgumentValue.ConvertTo(targetType, new ResolutionContext()); - [Theory] - [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::ConcreteClassProperty, Serilog.Settings.Configuration.Tests", typeof(AConcreteClass))] - [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::ConcreteClassField, Serilog.Settings.Configuration.Tests", typeof(AConcreteClass))] - public void StaticMembersAccessorsCanBeUsedForConcreteReferenceTypes(string input, Type targetType) - { - var stringArgumentValue = new StringArgumentValue(input); + Assert.IsAssignableFrom(targetType, actual); + var parser = (Delegate)actual; + Assert.Equal(100, parser.DynamicInvoke("100")); + } - var actual = stringArgumentValue.ConvertTo(targetType, new ResolutionContext()); + [Theory] + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::ConcreteClassProperty, Serilog.Settings.Configuration.Tests", typeof(AConcreteClass))] + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::ConcreteClassField, Serilog.Settings.Configuration.Tests", typeof(AConcreteClass))] + public void StaticMembersAccessorsCanBeUsedForConcreteReferenceTypes(string input, Type targetType) + { + var stringArgumentValue = new StringArgumentValue(input); - Assert.IsAssignableFrom(targetType, actual); - Assert.Equal(ConcreteImplOfConcreteClass.Instance, actual); - } + var actual = stringArgumentValue.ConvertTo(targetType, new ResolutionContext()); - [Theory] - [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::IntProperty, Serilog.Settings.Configuration.Tests", typeof(int), 42)] - [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::StringProperty, Serilog.Settings.Configuration.Tests", typeof(string), - "Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::StringProperty, Serilog.Settings.Configuration.Tests")] - public void StaticMembersAccessorsCanBeUsedForBuiltInTypes(string input, Type targetType, object expected) - { - var stringArgumentValue = new StringArgumentValue(input); + Assert.IsAssignableFrom(targetType, actual); + Assert.Equal(ConcreteImplOfConcreteClass.Instance, actual); + } - var actual = stringArgumentValue.ConvertTo(targetType, new ResolutionContext()); + [Theory] + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::IntProperty, Serilog.Settings.Configuration.Tests", typeof(int), 42)] + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::StringProperty, Serilog.Settings.Configuration.Tests", typeof(string), + "Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::StringProperty, Serilog.Settings.Configuration.Tests")] + public void StaticMembersAccessorsCanBeUsedForBuiltInTypes(string input, Type targetType, object expected) + { + var stringArgumentValue = new StringArgumentValue(input); - Assert.Equal(expected, actual); - } + var actual = stringArgumentValue.ConvertTo(targetType, new ResolutionContext()); - [Theory] - // unknown type - [InlineData("Namespace.ThisIsNotAKnownType::InterfaceProperty, Serilog.Settings.Configuration.Tests", typeof(IAmAnInterface))] - // good type name, but wrong namespace - [InlineData("Random.Namespace.ClassWithStaticAccessors::InterfaceProperty, Serilog.Settings.Configuration.Tests", typeof(IAmAnInterface))] - // good full type name, but missing or wrong assembly - [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::InterfaceProperty", typeof(IAmAnInterface))] - public void StaticAccessorOnUnknownTypeThrowsTypeLoadException(string input, Type targetType) - { - var stringArgumentValue = new StringArgumentValue($"{input}"); - Assert.Throws(() => - stringArgumentValue.ConvertTo(targetType, new ResolutionContext()) - ); - } + Assert.Equal(expected, actual); + } - [Theory] - // unknown member - [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::UnknownMember, Serilog.Settings.Configuration.Tests", typeof(IAmAnInterface))] - // static property exists but it's private - [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::PrivateInterfaceProperty, Serilog.Settings.Configuration.Tests", typeof(IAmAnInterface))] - // static field exists but it's private - [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::PrivateInterfaceField, Serilog.Settings.Configuration.Tests", typeof(IAmAnInterface))] - // public property exists but it's not static - [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::InstanceInterfaceProperty, Serilog.Settings.Configuration.Tests", typeof(IAmAnInterface))] - // public field exists but it's not static - [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::InstanceInterfaceField, Serilog.Settings.Configuration.Tests", typeof(IAmAnInterface))] - public void StaticAccessorWithInvalidMemberThrowsInvalidOperationException(string input, Type targetType) - { - var stringArgumentValue = new StringArgumentValue($"{input}"); - var exception = Assert.Throws(() => - stringArgumentValue.ConvertTo(targetType, new ResolutionContext()) - ); + [Theory] + // unknown type + [InlineData("Namespace.ThisIsNotAKnownType::InterfaceProperty, Serilog.Settings.Configuration.Tests", typeof(IAmAnInterface))] + // good type name, but wrong namespace + [InlineData("Random.Namespace.ClassWithStaticAccessors::InterfaceProperty, Serilog.Settings.Configuration.Tests", typeof(IAmAnInterface))] + // good full type name, but missing or wrong assembly + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::InterfaceProperty", typeof(IAmAnInterface))] + public void StaticAccessorOnUnknownTypeThrowsTypeLoadException(string input, Type targetType) + { + var stringArgumentValue = new StringArgumentValue($"{input}"); + Assert.Throws(() => + stringArgumentValue.ConvertTo(targetType, new ResolutionContext()) + ); + } - Assert.Contains("Could not find a public static property or field ", exception.Message); - Assert.Contains("on type `Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors, Serilog.Settings.Configuration.Tests`", exception.Message); - } + [Theory] + // unknown member + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::UnknownMember, Serilog.Settings.Configuration.Tests", typeof(IAmAnInterface))] + // static property exists but it's private + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::PrivateInterfaceProperty, Serilog.Settings.Configuration.Tests", typeof(IAmAnInterface))] + // static field exists but it's private + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::PrivateInterfaceField, Serilog.Settings.Configuration.Tests", typeof(IAmAnInterface))] + // public property exists but it's not static + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::InstanceInterfaceProperty, Serilog.Settings.Configuration.Tests", typeof(IAmAnInterface))] + // public field exists but it's not static + [InlineData("Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors::InstanceInterfaceField, Serilog.Settings.Configuration.Tests", typeof(IAmAnInterface))] + public void StaticAccessorWithInvalidMemberThrowsInvalidOperationException(string input, Type targetType) + { + var stringArgumentValue = new StringArgumentValue($"{input}"); + var exception = Assert.Throws(() => + stringArgumentValue.ConvertTo(targetType, new ResolutionContext()) + ); - [Fact] - public void LevelSwitchesCanBeLookedUpByName() - { - var @switch = new LoggingLevelSwitch(LogEventLevel.Verbose); - var switchName = "$theSwitch"; - var resolutionContext = new ResolutionContext(); - resolutionContext.AddLevelSwitch(switchName, @switch); + Assert.Contains("Could not find a public static property or field ", exception.Message); + Assert.Contains("on type `Serilog.Settings.Configuration.Tests.Support.ClassWithStaticAccessors, Serilog.Settings.Configuration.Tests`", exception.Message); + } - var stringArgumentValue = new StringArgumentValue(switchName); + [Fact] + public void LevelSwitchesCanBeLookedUpByName() + { + var @switch = new LoggingLevelSwitch(LogEventLevel.Verbose); + var switchName = "$theSwitch"; + var resolutionContext = new ResolutionContext(); + resolutionContext.AddLevelSwitch(switchName, @switch); - var resolvedSwitch = stringArgumentValue.ConvertTo(typeof(LoggingLevelSwitch), resolutionContext); + var stringArgumentValue = new StringArgumentValue(switchName); - Assert.IsType(resolvedSwitch); - Assert.Same(@switch, resolvedSwitch); - } + var resolvedSwitch = stringArgumentValue.ConvertTo(typeof(LoggingLevelSwitch), resolutionContext); + Assert.IsType(resolvedSwitch); + Assert.Same(@switch, resolvedSwitch); + } - [Fact] - public void ReferencingUndeclaredLevelSwitchThrows() - { - var resolutionContext = new ResolutionContext(); - resolutionContext.AddLevelSwitch("$anotherSwitch", new LoggingLevelSwitch(LogEventLevel.Verbose)); - var stringArgumentValue = new StringArgumentValue("$mySwitch"); + [Fact] + public void ReferencingUndeclaredLevelSwitchThrows() + { + var resolutionContext = new ResolutionContext(); + resolutionContext.AddLevelSwitch("$anotherSwitch", new LoggingLevelSwitch(LogEventLevel.Verbose)); - var ex = Assert.Throws(() => - stringArgumentValue.ConvertTo(typeof(LoggingLevelSwitch), resolutionContext) - ); + var stringArgumentValue = new StringArgumentValue("$mySwitch"); - Assert.Contains("$mySwitch", ex.Message); - Assert.Contains("\"LevelSwitches\":{\"$mySwitch\":", ex.Message); - } + var ex = Assert.Throws(() => + stringArgumentValue.ConvertTo(typeof(LoggingLevelSwitch), resolutionContext) + ); - [Fact] - public void StringValuesConvertToTypeFromShortTypeName() - { - var shortTypeName = "System.Version"; - var stringArgumentValue = new StringArgumentValue(shortTypeName); + Assert.Contains("$mySwitch", ex.Message); + Assert.Contains("\"LevelSwitches\":{\"$mySwitch\":", ex.Message); + } - var actual = (Type)stringArgumentValue.ConvertTo(typeof(Type), new ResolutionContext()); + [Fact] + public void StringValuesConvertToTypeFromShortTypeName() + { + var shortTypeName = "System.Version"; + var stringArgumentValue = new StringArgumentValue(shortTypeName); - Assert.Equal(typeof(Version), actual); - } + var actual = (Type)stringArgumentValue.ConvertTo(typeof(Type), new ResolutionContext()); - [Fact] - public void StringValuesConvertToTypeFromAssemblyQualifiedName() - { - var assemblyQualifiedName = typeof(Version).AssemblyQualifiedName; - var stringArgumentValue = new StringArgumentValue(assemblyQualifiedName); + Assert.Equal(typeof(Version), actual); + } - var actual = (Type)stringArgumentValue.ConvertTo(typeof(Type), new ResolutionContext()); + [Fact] + public void StringValuesConvertToTypeFromAssemblyQualifiedName() + { + var assemblyQualifiedName = typeof(Version).AssemblyQualifiedName; + var stringArgumentValue = new StringArgumentValue(assemblyQualifiedName); - Assert.Equal(typeof(Version), actual); - } + var actual = (Type)stringArgumentValue.ConvertTo(typeof(Type), new ResolutionContext()); + + Assert.Equal(typeof(Version), actual); } } diff --git a/test/Serilog.Settings.Configuration.Tests/Support/AbstractClass.cs b/test/Serilog.Settings.Configuration.Tests/Support/AbstractClass.cs index a894668e..695f02e4 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/AbstractClass.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/AbstractClass.cs @@ -1,6 +1,5 @@ -namespace Serilog.Settings.Configuration.Tests.Support -{ - public abstract class AbstractClass { } +namespace Serilog.Settings.Configuration.Tests.Support; - public class ConcreteClass : AbstractClass { } -} +public abstract class AbstractClass { } + +public class ConcreteClass : AbstractClass { } diff --git a/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationBuilderExtensions.cs b/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationBuilderExtensions.cs index 212cd0db..d63b3c77 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationBuilderExtensions.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationBuilderExtensions.cs @@ -1,12 +1,11 @@ using Microsoft.Extensions.Configuration; -namespace Serilog.Settings.Configuration.Tests.Support +namespace Serilog.Settings.Configuration.Tests.Support; + +public static class ConfigurationBuilderExtensions { - public static class ConfigurationBuilderExtensions + public static IConfigurationBuilder AddJsonString(this IConfigurationBuilder builder, string json) { - public static IConfigurationBuilder AddJsonString(this IConfigurationBuilder builder, string json) - { - return builder.Add(new JsonStringConfigSource(json)); - } + return builder.Add(new JsonStringConfigSource(json)); } -} \ No newline at end of file +} diff --git a/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs b/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs index d1fa6182..4c783818 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs @@ -1,21 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration; using Serilog.Events; -using Xunit; -namespace Serilog.Settings.Configuration.Tests.Support +namespace Serilog.Settings.Configuration.Tests.Support; + +static class ConfigurationReaderTestHelpers { - static class ConfigurationReaderTestHelpers - { - public const string minimumLevelFlatTemplate = @" + public const string minimumLevelFlatTemplate = @" {{ 'Serilog': {{ 'MinimumLevel': '{0}' }} }}"; - public const string minimumLevelObjectTemplate = @" + public const string minimumLevelObjectTemplate = @" {{ 'Serilog': {{ 'MinimumLevel': {{ @@ -23,43 +19,42 @@ static class ConfigurationReaderTestHelpers }} }} }}"; - public const string minimumLevelFlatKey = "Serilog:MinimumLevel"; - public const string minimumLevelObjectKey = "Serilog:MinimumLevel:Default"; + public const string minimumLevelFlatKey = "Serilog:MinimumLevel"; + public const string minimumLevelObjectKey = "Serilog:MinimumLevel:Default"; - public static void AssertLogEventLevels(LoggerConfiguration loggerConfig, LogEventLevel expectedMinimumLevel) - { - var logger = loggerConfig.CreateLogger(); + public static void AssertLogEventLevels(LoggerConfiguration loggerConfig, LogEventLevel expectedMinimumLevel) + { + var logger = loggerConfig.CreateLogger(); - var logEventValues = Enum.GetValues(typeof(LogEventLevel)).Cast(); + var logEventValues = Enum.GetValues(typeof(LogEventLevel)).Cast(); - foreach (var logEvent in logEventValues) + foreach (var logEvent in logEventValues) + { + if (logEvent < expectedMinimumLevel) { - if (logEvent < expectedMinimumLevel) - { - Assert.False(logger.IsEnabled(logEvent), - $"The log level {logEvent} should be disabled as it's lower priority than the minimum level of {expectedMinimumLevel}."); - } - else - { - Assert.True(logger.IsEnabled(logEvent), - $"The log level {logEvent} should be enabled as it's {(logEvent == expectedMinimumLevel ? "the same" : "higher")} priority {(logEvent == expectedMinimumLevel ? "as" : "than")} the minimum level of {expectedMinimumLevel}."); - } + Assert.False(logger.IsEnabled(logEvent), + $"The log level {logEvent} should be disabled as it's lower priority than the minimum level of {expectedMinimumLevel}."); + } + else + { + Assert.True(logger.IsEnabled(logEvent), + $"The log level {logEvent} should be enabled as it's {(logEvent == expectedMinimumLevel ? "the same" : "higher")} priority {(logEvent == expectedMinimumLevel ? "as" : "than")} the minimum level of {expectedMinimumLevel}."); } } + } - // the naming is only to show priority as providers - public static IConfigurationRoot GetConfigRoot( - string appsettingsJsonLevel = null, - string appsettingsDevelopmentJsonLevel = null, - Dictionary envVariables = null) - { - var configBuilder = new ConfigurationBuilder(); + // the naming is only to show priority as providers + public static IConfigurationRoot GetConfigRoot( + string appsettingsJsonLevel = null, + string appsettingsDevelopmentJsonLevel = null, + Dictionary envVariables = null) + { + var configBuilder = new ConfigurationBuilder(); - configBuilder.AddJsonString(appsettingsJsonLevel ?? "{}"); - configBuilder.AddJsonString(appsettingsDevelopmentJsonLevel ?? "{}"); - configBuilder.Add(new ReloadableConfigurationSource(envVariables ?? new Dictionary())); + configBuilder.AddJsonString(appsettingsJsonLevel ?? "{}"); + configBuilder.AddJsonString(appsettingsDevelopmentJsonLevel ?? "{}"); + configBuilder.Add(new ReloadableConfigurationSource(envVariables ?? new Dictionary())); - return configBuilder.Build(); - } + return configBuilder.Build(); } } diff --git a/test/Serilog.Settings.Configuration.Tests/Support/CustomConsoleTheme.cs b/test/Serilog.Settings.Configuration.Tests/Support/CustomConsoleTheme.cs index 0e3030a7..79050ce9 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/CustomConsoleTheme.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/CustomConsoleTheme.cs @@ -1,8 +1,7 @@ using TestDummies.Console.Themes; -namespace Serilog.Settings.Configuration.Tests.Support +namespace Serilog.Settings.Configuration.Tests.Support; + +class CustomConsoleTheme : ConsoleTheme { - class CustomConsoleTheme : ConsoleTheme - { - } } diff --git a/test/Serilog.Settings.Configuration.Tests/Support/DelegatingSink.cs b/test/Serilog.Settings.Configuration.Tests/Support/DelegatingSink.cs index 07a848bd..60096c05 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/DelegatingSink.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/DelegatingSink.cs @@ -1,33 +1,31 @@ -using System; -using Serilog.Core; +using Serilog.Core; using Serilog.Events; -namespace Serilog.Settings.Configuration.Tests.Support +namespace Serilog.Settings.Configuration.Tests.Support; + +public class DelegatingSink : ILogEventSink { - public class DelegatingSink : ILogEventSink - { - readonly Action _write; + readonly Action _write; - public DelegatingSink(Action write) - { - _write = write ?? throw new ArgumentNullException(nameof(write)); - } + public DelegatingSink(Action write) + { + _write = write ?? throw new ArgumentNullException(nameof(write)); + } - public void Emit(LogEvent logEvent) - { - _write(logEvent); - } + public void Emit(LogEvent logEvent) + { + _write(logEvent); + } - public static LogEvent GetLogEvent(Action writeAction) - { - LogEvent result = null; - var l = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.Sink(new DelegatingSink(le => result = le)) - .CreateLogger(); + public static LogEvent GetLogEvent(Action writeAction) + { + LogEvent result = null; + var l = new LoggerConfiguration() + .MinimumLevel.Verbose() + .WriteTo.Sink(new DelegatingSink(le => result = le)) + .CreateLogger(); - writeAction(l); - return result; - } + writeAction(l); + return result; } } diff --git a/test/Serilog.Settings.Configuration.Tests/Support/Extensions.cs b/test/Serilog.Settings.Configuration.Tests/Support/Extensions.cs index 1ea1f475..9353653e 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/Extensions.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/Extensions.cs @@ -1,20 +1,19 @@ using Serilog.Events; -namespace Serilog.Settings.Configuration.Tests.Support +namespace Serilog.Settings.Configuration.Tests.Support; + +public static class Extensions { - public static class Extensions + public static object LiteralValue(this LogEventPropertyValue @this) { - public static object LiteralValue(this LogEventPropertyValue @this) - { - return ((ScalarValue)@this).Value; - } - - public static string ToValidJson(this string str) - { - str = str.Replace('\'', '"'); - return str; - } + return ((ScalarValue)@this).Value; + } - public static string Format(this string template, params object[] paramObjects) => string.Format(template, paramObjects); + public static string ToValidJson(this string str) + { + str = str.Replace('\'', '"'); + return str; } + + public static string Format(this string template, params object[] paramObjects) => string.Format(template, paramObjects); } diff --git a/test/Serilog.Settings.Configuration.Tests/Support/JsonStringConfigSource.cs b/test/Serilog.Settings.Configuration.Tests/Support/JsonStringConfigSource.cs index 1175ee4e..41667086 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/JsonStringConfigSource.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/JsonStringConfigSource.cs @@ -1,63 +1,59 @@ -using System.Collections.Generic; -using System.IO; - -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.Json; -namespace Serilog.Settings.Configuration.Tests.Support +namespace Serilog.Settings.Configuration.Tests.Support; + +class JsonStringConfigSource : IConfigurationSource { - class JsonStringConfigSource : IConfigurationSource + readonly string _json; + + public JsonStringConfigSource(string json) + { + _json = json; + } + + public IConfigurationProvider Build(IConfigurationBuilder builder) + { + return new JsonStringConfigProvider(_json); + } + + public static IConfigurationSection LoadSection(string json, string section) + { + return new ConfigurationBuilder().Add(new JsonStringConfigSource(json)).Build().GetSection(section); + } + + public static IDictionary LoadData(string json) + { + var provider = new JsonStringConfigProvider(json); + provider.Load(); + return provider.Data; + } + + class JsonStringConfigProvider : JsonConfigurationProvider { readonly string _json; - public JsonStringConfigSource(string json) + public JsonStringConfigProvider(string json) : base(new JsonConfigurationSource { Optional = true }) { _json = json; } - public IConfigurationProvider Build(IConfigurationBuilder builder) - { - return new JsonStringConfigProvider(_json); - } + public new IDictionary Data => base.Data; - public static IConfigurationSection LoadSection(string json, string section) + public override void Load() { - return new ConfigurationBuilder().Add(new JsonStringConfigSource(json)).Build().GetSection(section); + Load(StringToStream(_json.ToValidJson())); } - public static IDictionary LoadData(string json) + static Stream StringToStream(string str) { - var provider = new JsonStringConfigProvider(json); - provider.Load(); - return provider.Data; - } + var memStream = new MemoryStream(); + var textWriter = new StreamWriter(memStream); + textWriter.Write(str); + textWriter.Flush(); + memStream.Seek(0, SeekOrigin.Begin); - class JsonStringConfigProvider : JsonConfigurationProvider - { - readonly string _json; - - public JsonStringConfigProvider(string json) : base(new JsonConfigurationSource { Optional = true }) - { - _json = json; - } - - public new IDictionary Data => base.Data; - - public override void Load() - { - Load(StringToStream(_json.ToValidJson())); - } - - static Stream StringToStream(string str) - { - var memStream = new MemoryStream(); - var textWriter = new StreamWriter(memStream); - textWriter.Write(str); - textWriter.Flush(); - memStream.Seek(0, SeekOrigin.Begin); - - return memStream; - } + return memStream; } } } diff --git a/test/Serilog.Settings.Configuration.Tests/Support/ReloadableConfigurationSource.cs b/test/Serilog.Settings.Configuration.Tests/Support/ReloadableConfigurationSource.cs index 8f90b348..22f2cb05 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/ReloadableConfigurationSource.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/ReloadableConfigurationSource.cs @@ -1,37 +1,35 @@ -using System.Collections.Generic; -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration; -namespace Serilog.Settings.Configuration.Tests.Support +namespace Serilog.Settings.Configuration.Tests.Support; + +class ReloadableConfigurationSource : IConfigurationSource { - class ReloadableConfigurationSource : IConfigurationSource + readonly ReloadableConfigurationProvider _configProvider; + readonly IDictionary _source; + + public ReloadableConfigurationSource(IDictionary source) { - readonly ReloadableConfigurationProvider _configProvider; - readonly IDictionary _source; + _source = source; + _configProvider = new ReloadableConfigurationProvider(source); + } - public ReloadableConfigurationSource(IDictionary source) - { - _source = source; - _configProvider = new ReloadableConfigurationProvider(source); - } + public IConfigurationProvider Build(IConfigurationBuilder builder) => _configProvider; - public IConfigurationProvider Build(IConfigurationBuilder builder) => _configProvider; + public void Reload() => _configProvider.Reload(); - public void Reload() => _configProvider.Reload(); + public void Set(string key, string value) => _source[key] = value; - public void Set(string key, string value) => _source[key] = value; + class ReloadableConfigurationProvider : ConfigurationProvider + { + readonly IDictionary _source; - class ReloadableConfigurationProvider : ConfigurationProvider + public ReloadableConfigurationProvider(IDictionary source) { - readonly IDictionary _source; - - public ReloadableConfigurationProvider(IDictionary source) - { - _source = source; - } + _source = source; + } - public override void Load() => Data = _source; + public override void Load() => Data = _source; - public void Reload() => OnReload(); - } + public void Reload() => OnReload(); } } diff --git a/test/Serilog.Settings.Configuration.Tests/Support/Some.cs b/test/Serilog.Settings.Configuration.Tests/Support/Some.cs index 9131b6d9..226d7490 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/Some.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/Some.cs @@ -1,101 +1,95 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using Serilog.Core; +using Serilog.Core; using Serilog.Events; using Serilog.Parsing; -namespace Serilog.Settings.Configuration.Tests.Support +namespace Serilog.Settings.Configuration.Tests.Support; + +static class Some { - static class Some - { - static int Counter; - - public static int Int() - { - return Interlocked.Increment(ref Counter); - } - - public static decimal Decimal() - { - return Int() + 0.123m; - } - - public static string String(string tag = null) - { - return (tag ?? "") + "__" + Int(); - } - - public static TimeSpan TimeSpan() - { - return System.TimeSpan.FromMinutes(Int()); - } - - public static DateTime Instant() - { - return new DateTime(2012, 10, 28) + TimeSpan(); - } - - public static DateTimeOffset OffsetInstant() - { - return new DateTimeOffset(Instant()); - } - - public static LogEvent LogEvent(string sourceContext, DateTimeOffset? timestamp = null, LogEventLevel level = LogEventLevel.Information) - { - return new LogEvent(timestamp ?? OffsetInstant(), level, - null, MessageTemplate(), - new List { new LogEventProperty(Constants.SourceContextPropertyName, new ScalarValue(sourceContext)) }); - } - - public static LogEvent LogEvent(DateTimeOffset? timestamp = null, LogEventLevel level = LogEventLevel.Information) - { - return new LogEvent(timestamp ?? OffsetInstant(), level, - null, MessageTemplate(), Enumerable.Empty()); - } - - public static LogEvent InformationEvent(DateTimeOffset? timestamp = null) - { - return LogEvent(timestamp, LogEventLevel.Information); - } - - public static LogEvent DebugEvent(DateTimeOffset? timestamp = null) - { - return LogEvent(timestamp, LogEventLevel.Debug); - } - - public static LogEvent WarningEvent(DateTimeOffset? timestamp = null) - { - return LogEvent(timestamp, LogEventLevel.Warning); - } - - public static LogEventProperty LogEventProperty() - { - return new LogEventProperty(String(), new ScalarValue(Int())); - } - - public static string NonexistentTempFilePath() - { - return Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".txt"); - } - - public static string TempFilePath() - { - return Path.GetTempFileName(); - } - - public static string TempFolderPath() - { - var dir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - Directory.CreateDirectory(dir); - return dir; - } - - public static MessageTemplate MessageTemplate() - { - return new MessageTemplateParser().Parse(String()); - } + static int Counter; + + public static int Int() + { + return Interlocked.Increment(ref Counter); + } + + public static decimal Decimal() + { + return Int() + 0.123m; + } + + public static string String(string tag = null) + { + return (tag ?? "") + "__" + Int(); + } + + public static TimeSpan TimeSpan() + { + return System.TimeSpan.FromMinutes(Int()); + } + + public static DateTime Instant() + { + return new DateTime(2012, 10, 28) + TimeSpan(); + } + + public static DateTimeOffset OffsetInstant() + { + return new DateTimeOffset(Instant()); + } + + public static LogEvent LogEvent(string sourceContext, DateTimeOffset? timestamp = null, LogEventLevel level = LogEventLevel.Information) + { + return new LogEvent(timestamp ?? OffsetInstant(), level, + null, MessageTemplate(), + new List { new LogEventProperty(Constants.SourceContextPropertyName, new ScalarValue(sourceContext)) }); + } + + public static LogEvent LogEvent(DateTimeOffset? timestamp = null, LogEventLevel level = LogEventLevel.Information) + { + return new LogEvent(timestamp ?? OffsetInstant(), level, + null, MessageTemplate(), Enumerable.Empty()); + } + + public static LogEvent InformationEvent(DateTimeOffset? timestamp = null) + { + return LogEvent(timestamp, LogEventLevel.Information); + } + + public static LogEvent DebugEvent(DateTimeOffset? timestamp = null) + { + return LogEvent(timestamp, LogEventLevel.Debug); + } + + public static LogEvent WarningEvent(DateTimeOffset? timestamp = null) + { + return LogEvent(timestamp, LogEventLevel.Warning); + } + + public static LogEventProperty LogEventProperty() + { + return new LogEventProperty(String(), new ScalarValue(Int())); + } + + public static string NonexistentTempFilePath() + { + return Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".txt"); + } + + public static string TempFilePath() + { + return Path.GetTempFileName(); + } + + public static string TempFolderPath() + { + var dir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(dir); + return dir; + } + + public static MessageTemplate MessageTemplate() + { + return new MessageTemplateParser().Parse(String()); } } diff --git a/test/Serilog.Settings.Configuration.Tests/Support/StaticAccessorClasses.cs b/test/Serilog.Settings.Configuration.Tests/Support/StaticAccessorClasses.cs index 7db6f99e..babd2d69 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/StaticAccessorClasses.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/StaticAccessorClasses.cs @@ -1,62 +1,59 @@ -using System; +namespace Serilog.Settings.Configuration.Tests.Support; -namespace Serilog.Settings.Configuration.Tests.Support +public delegate int NamedIntParse(string value); + +public interface IAmAnInterface { - public delegate int NamedIntParse(string value); +} - public interface IAmAnInterface - { - } +public abstract class AnAbstractClass +{ +} - public abstract class AnAbstractClass +class ConcreteImpl : AnAbstractClass, IAmAnInterface +{ + private ConcreteImpl() { } - class ConcreteImpl : AnAbstractClass, IAmAnInterface - { - private ConcreteImpl() - { - } - - public static ConcreteImpl Instance { get; } = new ConcreteImpl(); - } + public static ConcreteImpl Instance { get; } = new ConcreteImpl(); +} - public class AConcreteClass - { - } +public class AConcreteClass +{ +} - class ConcreteImplOfConcreteClass : AConcreteClass - { - public static ConcreteImplOfConcreteClass Instance { get; } = new ConcreteImplOfConcreteClass(); - } +class ConcreteImplOfConcreteClass : AConcreteClass +{ + public static ConcreteImplOfConcreteClass Instance { get; } = new ConcreteImplOfConcreteClass(); +} - public class ClassWithStaticAccessors - { - public static IAmAnInterface InterfaceProperty => ConcreteImpl.Instance; - public static AnAbstractClass AbstractProperty => ConcreteImpl.Instance; - public static AConcreteClass ConcreteClassProperty => ConcreteImplOfConcreteClass.Instance; - public static int IntProperty => 42; - public static string StringProperty => "don't see me"; +public class ClassWithStaticAccessors +{ + public static IAmAnInterface InterfaceProperty => ConcreteImpl.Instance; + public static AnAbstractClass AbstractProperty => ConcreteImpl.Instance; + public static AConcreteClass ConcreteClassProperty => ConcreteImplOfConcreteClass.Instance; + public static int IntProperty => 42; + public static string StringProperty => "don't see me"; - public static IAmAnInterface InterfaceField = ConcreteImpl.Instance; - public static AnAbstractClass AbstractField = ConcreteImpl.Instance; - public static AConcreteClass ConcreteClassField = ConcreteImplOfConcreteClass.Instance; + public static IAmAnInterface InterfaceField = ConcreteImpl.Instance; + public static AnAbstractClass AbstractField = ConcreteImpl.Instance; + public static AConcreteClass ConcreteClassField = ConcreteImplOfConcreteClass.Instance; - // ReSharper disable once UnusedMember.Local - private static IAmAnInterface PrivateInterfaceProperty => ConcreteImpl.Instance; + // ReSharper disable once UnusedMember.Local + private static IAmAnInterface PrivateInterfaceProperty => ConcreteImpl.Instance; #pragma warning disable 169 - private static IAmAnInterface PrivateInterfaceField = ConcreteImpl.Instance; + private static IAmAnInterface PrivateInterfaceField = ConcreteImpl.Instance; #pragma warning restore 169 - public IAmAnInterface InstanceInterfaceProperty => ConcreteImpl.Instance; - public IAmAnInterface InstanceInterfaceField = ConcreteImpl.Instance; - - public static Func FuncIntParseField = int.Parse; - public static NamedIntParse NamedIntParseField = int.Parse; - public static Func FuncIntParseProperty => int.Parse; - public static NamedIntParse NamedIntParseProperty => int.Parse; - public static int IntParseMethod(string value) => int.Parse(value); - public static int IntParseMethod(string value, string otherValue) => throw new NotImplementedException(); // will not be chosen, extra parameter - public static int IntParseMethod(object value) => throw new NotImplementedException(); // will not be chosen, wrong parameter type - } + public IAmAnInterface InstanceInterfaceProperty => ConcreteImpl.Instance; + public IAmAnInterface InstanceInterfaceField = ConcreteImpl.Instance; + + public static Func FuncIntParseField = int.Parse; + public static NamedIntParse NamedIntParseField = int.Parse; + public static Func FuncIntParseProperty => int.Parse; + public static NamedIntParse NamedIntParseProperty => int.Parse; + public static int IntParseMethod(string value) => int.Parse(value); + public static int IntParseMethod(string value, string otherValue) => throw new NotImplementedException(); // will not be chosen, extra parameter + public static int IntParseMethod(object value) => throw new NotImplementedException(); // will not be chosen, wrong parameter type } diff --git a/test/TestDummies/Console/DummyConsoleSink.cs b/test/TestDummies/Console/DummyConsoleSink.cs index 79816b89..67134e57 100644 --- a/test/TestDummies/Console/DummyConsoleSink.cs +++ b/test/TestDummies/Console/DummyConsoleSink.cs @@ -1,31 +1,27 @@ -using System; -using System.Collections.Generic; -using Serilog.Core; +using Serilog.Core; using Serilog.Events; using TestDummies.Console.Themes; -namespace TestDummies.Console +namespace TestDummies.Console; + +public class DummyConsoleSink : ILogEventSink { - public class DummyConsoleSink : ILogEventSink + public DummyConsoleSink(ConsoleTheme theme = null) { - public DummyConsoleSink(ConsoleTheme theme = null) - { - Theme = theme ?? ConsoleTheme.None; - } + Theme = theme ?? ConsoleTheme.None; + } - [ThreadStatic] - public static ConsoleTheme Theme; + [ThreadStatic] + public static ConsoleTheme Theme; - [ThreadStatic] - static List EmittedList; + [ThreadStatic] + static List EmittedList; - public static List Emitted => EmittedList ?? (EmittedList = new List()); + public static List Emitted => EmittedList ?? (EmittedList = new List()); - public void Emit(LogEvent logEvent) - { - Emitted.Add(logEvent); - } + public void Emit(LogEvent logEvent) + { + Emitted.Add(logEvent); } - } diff --git a/test/TestDummies/Console/Themes/ConcreteConsoleTheme.cs b/test/TestDummies/Console/Themes/ConcreteConsoleTheme.cs index 8b3b041b..67d8343e 100644 --- a/test/TestDummies/Console/Themes/ConcreteConsoleTheme.cs +++ b/test/TestDummies/Console/Themes/ConcreteConsoleTheme.cs @@ -1,6 +1,5 @@ -namespace TestDummies.Console.Themes +namespace TestDummies.Console.Themes; + +class ConcreteConsoleTheme : ConsoleTheme { - class ConcreteConsoleTheme : ConsoleTheme - { - } } diff --git a/test/TestDummies/Console/Themes/ConsoleTheme.cs b/test/TestDummies/Console/Themes/ConsoleTheme.cs index 1c5aaf5d..ff14007b 100644 --- a/test/TestDummies/Console/Themes/ConsoleTheme.cs +++ b/test/TestDummies/Console/Themes/ConsoleTheme.cs @@ -1,7 +1,6 @@ -namespace TestDummies.Console.Themes +namespace TestDummies.Console.Themes; + +public abstract class ConsoleTheme { - public abstract class ConsoleTheme - { - public static ConsoleTheme None { get; } = new EmptyConsoleTheme(); - } + public static ConsoleTheme None { get; } = new EmptyConsoleTheme(); } diff --git a/test/TestDummies/Console/Themes/ConsoleThemes.cs b/test/TestDummies/Console/Themes/ConsoleThemes.cs index 7bb414cb..2804ba71 100644 --- a/test/TestDummies/Console/Themes/ConsoleThemes.cs +++ b/test/TestDummies/Console/Themes/ConsoleThemes.cs @@ -1,7 +1,6 @@ -namespace TestDummies.Console.Themes +namespace TestDummies.Console.Themes; + +public static class ConsoleThemes { - public static class ConsoleThemes - { - public static ConsoleTheme Theme1 { get; } = new ConcreteConsoleTheme(); - } + public static ConsoleTheme Theme1 { get; } = new ConcreteConsoleTheme(); } diff --git a/test/TestDummies/Console/Themes/EmptyConsoleTheme.cs b/test/TestDummies/Console/Themes/EmptyConsoleTheme.cs index 100e89e8..f01847f8 100644 --- a/test/TestDummies/Console/Themes/EmptyConsoleTheme.cs +++ b/test/TestDummies/Console/Themes/EmptyConsoleTheme.cs @@ -1,6 +1,5 @@ -namespace TestDummies.Console.Themes +namespace TestDummies.Console.Themes; + +class EmptyConsoleTheme : ConsoleTheme { - class EmptyConsoleTheme : ConsoleTheme - { - } } diff --git a/test/TestDummies/DummyAnonymousUserFilter.cs b/test/TestDummies/DummyAnonymousUserFilter.cs index a47dd11b..89bae72b 100644 --- a/test/TestDummies/DummyAnonymousUserFilter.cs +++ b/test/TestDummies/DummyAnonymousUserFilter.cs @@ -2,24 +2,23 @@ using Serilog.Core; using Serilog.Events; -namespace TestDummies +namespace TestDummies; + +public class DummyAnonymousUserFilter : ILogEventFilter { - public class DummyAnonymousUserFilter : ILogEventFilter + public bool IsEnabled(LogEvent logEvent) { - public bool IsEnabled(LogEvent logEvent) + if (logEvent.Properties.ContainsKey("User")) { - if (logEvent.Properties.ContainsKey("User")) + if (logEvent.Properties["User"] is ScalarValue sv) { - if (logEvent.Properties["User"] is ScalarValue sv) + if (sv.Value is string s && s == "anonymous") { - if (sv.Value is string s && s == "anonymous") - { - return false; - } + return false; } } - - return true; } + + return true; } } diff --git a/test/TestDummies/DummyConfigurationSink.cs b/test/TestDummies/DummyConfigurationSink.cs index d2250e74..c522f1b8 100644 --- a/test/TestDummies/DummyConfigurationSink.cs +++ b/test/TestDummies/DummyConfigurationSink.cs @@ -1,46 +1,43 @@ -using System; -using System.Collections.Generic; -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration; using Serilog.Core; using Serilog.Events; -namespace TestDummies -{ - public class DummyConfigurationSink : ILogEventSink - { - [ThreadStatic] - static List _emitted; +namespace TestDummies; - [ThreadStatic] - static IConfiguration _configuration; +public class DummyConfigurationSink : ILogEventSink +{ + [ThreadStatic] + static List _emitted; - [ThreadStatic] - static IConfigurationSection _configSection; + [ThreadStatic] + static IConfiguration _configuration; - public static List Emitted => _emitted ?? (_emitted = new List()); + [ThreadStatic] + static IConfigurationSection _configSection; - public static IConfiguration Configuration => _configuration; + public static List Emitted => _emitted ?? (_emitted = new List()); - public static IConfigurationSection ConfigSection => _configSection; + public static IConfiguration Configuration => _configuration; + public static IConfigurationSection ConfigSection => _configSection; - public DummyConfigurationSink(IConfiguration configuration, IConfigurationSection configSection) - { - _configuration = configuration; - _configSection = configSection; - } - public void Emit(LogEvent logEvent) - { - Emitted.Add(logEvent); - } + public DummyConfigurationSink(IConfiguration configuration, IConfigurationSection configSection) + { + _configuration = configuration; + _configSection = configSection; + } - public static void Reset() - { - _emitted = null; - _configuration = null; - _configSection = null; - } + public void Emit(LogEvent logEvent) + { + Emitted.Add(logEvent); + } + public static void Reset() + { + _emitted = null; + _configuration = null; + _configSection = null; } + } diff --git a/test/TestDummies/DummyHardCodedStringDestructuringPolicy.cs b/test/TestDummies/DummyHardCodedStringDestructuringPolicy.cs index 25d27249..727a46e4 100644 --- a/test/TestDummies/DummyHardCodedStringDestructuringPolicy.cs +++ b/test/TestDummies/DummyHardCodedStringDestructuringPolicy.cs @@ -1,22 +1,20 @@ -using System; -using Serilog.Core; +using Serilog.Core; using Serilog.Events; -namespace TestDummies +namespace TestDummies; + +public class DummyHardCodedStringDestructuringPolicy : IDestructuringPolicy { - public class DummyHardCodedStringDestructuringPolicy : IDestructuringPolicy - { - readonly string _hardCodedString; + readonly string _hardCodedString; - public DummyHardCodedStringDestructuringPolicy(string hardCodedString) - { - _hardCodedString = hardCodedString ?? throw new ArgumentNullException(nameof(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; - } + 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 2148e80b..6d973791 100644 --- a/test/TestDummies/DummyLoggerConfigurationExtensions.cs +++ b/test/TestDummies/DummyLoggerConfigurationExtensions.cs @@ -4,159 +4,156 @@ using Serilog.Core; using Serilog.Events; using Serilog.Formatting; -using System; -using System.Collections.Generic; using TestDummies.Console; using TestDummies.Console.Themes; -namespace TestDummies +namespace TestDummies; + +public static class DummyLoggerConfigurationExtensions { - public static class DummyLoggerConfigurationExtensions + public static LoggerConfiguration WithDummyThreadId(this LoggerEnrichmentConfiguration enrich) { - public static LoggerConfiguration WithDummyThreadId(this LoggerEnrichmentConfiguration enrich) - { - return enrich.With(new DummyThreadIdEnricher()); - } - - public static LoggerConfiguration DummyRollingFile( - this LoggerSinkConfiguration loggerSinkConfiguration, - string pathFormat, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - string outputTemplate = null, - IFormatProvider formatProvider = null) - { - return loggerSinkConfiguration.Sink(new DummyRollingFileSink(), restrictedToMinimumLevel); - } - - public static LoggerConfiguration DummyRollingFile( - this LoggerSinkConfiguration loggerSinkConfiguration, - ITextFormatter formatter, - string pathFormat, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) - { - return loggerSinkConfiguration.Sink(new DummyRollingFileSink(), restrictedToMinimumLevel); - } + return enrich.With(new DummyThreadIdEnricher()); + } - public static LoggerConfiguration DummyWithConfiguration( - this LoggerSinkConfiguration loggerSinkConfiguration, - IConfiguration appConfiguration, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) - { - return loggerSinkConfiguration.Sink(new DummyConfigurationSink(appConfiguration, null), restrictedToMinimumLevel); - } + public static LoggerConfiguration DummyRollingFile( + this LoggerSinkConfiguration loggerSinkConfiguration, + string pathFormat, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + string outputTemplate = null, + IFormatProvider formatProvider = null) + { + return loggerSinkConfiguration.Sink(new DummyRollingFileSink(), restrictedToMinimumLevel); + } - public static LoggerConfiguration DummyWithOptionalConfiguration( - this LoggerSinkConfiguration loggerSinkConfiguration, - IConfiguration appConfiguration = null, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) - { - return loggerSinkConfiguration.Sink(new DummyConfigurationSink(appConfiguration, null), restrictedToMinimumLevel); - } + public static LoggerConfiguration DummyRollingFile( + this LoggerSinkConfiguration loggerSinkConfiguration, + ITextFormatter formatter, + string pathFormat, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) + { + return loggerSinkConfiguration.Sink(new DummyRollingFileSink(), restrictedToMinimumLevel); + } - public static LoggerConfiguration DummyWithConfigSection( - this LoggerSinkConfiguration loggerSinkConfiguration, - IConfigurationSection configurationSection, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) - { - return loggerSinkConfiguration.Sink(new DummyConfigurationSink(null, configurationSection), 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 DummyWithConfiguration( + this LoggerSinkConfiguration loggerSinkConfiguration, + IConfiguration appConfiguration, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) + { + return loggerSinkConfiguration.Sink(new DummyConfigurationSink(appConfiguration, null), restrictedToMinimumLevel); + } - public class Binding - { - public string Foo { get; set; } + public static LoggerConfiguration DummyWithOptionalConfiguration( + this LoggerSinkConfiguration loggerSinkConfiguration, + IConfiguration appConfiguration = null, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) + { + return loggerSinkConfiguration.Sink(new DummyConfigurationSink(appConfiguration, null), restrictedToMinimumLevel); + } - public string Abc { get; set; } - } + public static LoggerConfiguration DummyWithConfigSection( + this LoggerSinkConfiguration loggerSinkConfiguration, + IConfigurationSection configurationSection, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) + { + return loggerSinkConfiguration.Sink(new DummyConfigurationSink(null, configurationSection), 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, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - string outputTemplate = null, - IFormatProvider formatProvider = null) - { - return loggerSinkConfiguration.Sink(new DummyRollingFileAuditSink(), 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 DummyWithLevelSwitch( - this LoggerSinkConfiguration loggerSinkConfiguration, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - LoggingLevelSwitch controlLevelSwitch = null) - { - return loggerSinkConfiguration.Sink(new DummyWithLevelSwitchSink(controlLevelSwitch), restrictedToMinimumLevel); - } - - public static LoggerConfiguration DummyConsole( - this LoggerSinkConfiguration loggerSinkConfiguration, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - LoggingLevelSwitch levelSwitch = null, - ConsoleTheme theme = null) - { - return loggerSinkConfiguration.Sink(new DummyConsoleSink(theme), restrictedToMinimumLevel, levelSwitch); - } + public class Binding + { + public string Foo { get; set; } - public static LoggerConfiguration Dummy( - this LoggerSinkConfiguration loggerSinkConfiguration, - Action wrappedSinkAction) - { - return LoggerSinkConfiguration.Wrap( - loggerSinkConfiguration, - s => new DummyWrappingSink(s), - wrappedSinkAction, - LogEventLevel.Verbose, - levelSwitch: null); - } - - public static LoggerConfiguration WithDummyHardCodedString( - this LoggerDestructuringConfiguration loggerDestructuringConfiguration, - string hardCodedString - ) - { - return loggerDestructuringConfiguration.With(new DummyHardCodedStringDestructuringPolicy(hardCodedString)); - } - - public static LoggerConfiguration DummyArrayOfType(this LoggerDestructuringConfiguration loggerSinkConfiguration, - List list, - Type[] array = null, - Type type = null, - CustomCollection custom = null, - CustomCollection customString = null) - { - return loggerSinkConfiguration.With(DummyPolicy.Current = new DummyPolicy - { - List = list, - Array = array, - Type = type, - Custom = custom, - CustomStrings = customString, - }); - } + public string Abc { get; set; } + } + + 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, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + string outputTemplate = null, + IFormatProvider formatProvider = null) + { + return loggerSinkConfiguration.Sink(new DummyRollingFileAuditSink(), restrictedToMinimumLevel); + } + + public static LoggerConfiguration DummyWithLevelSwitch( + this LoggerSinkConfiguration loggerSinkConfiguration, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch controlLevelSwitch = null) + { + return loggerSinkConfiguration.Sink(new DummyWithLevelSwitchSink(controlLevelSwitch), restrictedToMinimumLevel); + } + + public static LoggerConfiguration DummyConsole( + this LoggerSinkConfiguration loggerSinkConfiguration, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch levelSwitch = null, + ConsoleTheme theme = null) + { + return loggerSinkConfiguration.Sink(new DummyConsoleSink(theme), restrictedToMinimumLevel, levelSwitch); + } + + public static LoggerConfiguration Dummy( + this LoggerSinkConfiguration loggerSinkConfiguration, + Action wrappedSinkAction) + { + return LoggerSinkConfiguration.Wrap( + loggerSinkConfiguration, + s => new DummyWrappingSink(s), + wrappedSinkAction, + LogEventLevel.Verbose, + levelSwitch: null); + } + + public static LoggerConfiguration WithDummyHardCodedString( + this LoggerDestructuringConfiguration loggerDestructuringConfiguration, + string hardCodedString + ) + { + return loggerDestructuringConfiguration.With(new DummyHardCodedStringDestructuringPolicy(hardCodedString)); + } + + public static LoggerConfiguration DummyArrayOfType(this LoggerDestructuringConfiguration loggerSinkConfiguration, + List list, + Type[] array = null, + Type type = null, + CustomCollection custom = null, + CustomCollection customString = null) + { + return loggerSinkConfiguration.With(DummyPolicy.Current = new DummyPolicy + { + List = list, + Array = array, + Type = type, + Custom = custom, + CustomStrings = customString, + }); } } diff --git a/test/TestDummies/DummyPolicy.cs b/test/TestDummies/DummyPolicy.cs index ada4c2f7..cf138327 100644 --- a/test/TestDummies/DummyPolicy.cs +++ b/test/TestDummies/DummyPolicy.cs @@ -1,48 +1,45 @@ using Serilog.Core; using Serilog.Events; -using System; using System.Collections; -using System.Collections.Generic; -namespace TestDummies +namespace TestDummies; + +public class DummyPolicy : IDestructuringPolicy { - public class DummyPolicy : IDestructuringPolicy - { - public static DummyPolicy Current { get; set; } + public static DummyPolicy Current { get; set; } - public Type[] Array { get; set; } + public Type[] Array { get; set; } - public List List { get; set; } + public List List { get; set; } - public CustomCollection Custom { get; set; } + public CustomCollection Custom { get; set; } - public CustomCollection CustomStrings { get; set; } + public CustomCollection CustomStrings { get; set; } - public Type Type { get; set; } + public Type Type { get; set; } - public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, out LogEventPropertyValue result) - { - result = null; - return false; - } + public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, out LogEventPropertyValue result) + { + result = null; + return false; } +} - public class CustomCollection : IEnumerable - { - private readonly List inner = new List(); +public class CustomCollection : IEnumerable +{ + private readonly List inner = new List(); - public void Add(T item) => inner.Add(item); + public void Add(T item) => inner.Add(item); - // wrong signature for collection initializer - public int Add() => 0; + // wrong signature for collection initializer + public int Add() => 0; - // wrong signature for collection initializer - public void Add(string a, byte b) { } + // wrong signature for collection initializer + public void Add(string a, byte b) { } - public T First => inner.Count > 0 ? inner[0] : default; + public T First => inner.Count > 0 ? inner[0] : default; - public IEnumerator GetEnumerator() => inner.GetEnumerator(); + public IEnumerator GetEnumerator() => inner.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => inner.GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() => inner.GetEnumerator(); } diff --git a/test/TestDummies/DummyRollingFileAuditSink.cs b/test/TestDummies/DummyRollingFileAuditSink.cs index 2ba17e35..28f794c7 100644 --- a/test/TestDummies/DummyRollingFileAuditSink.cs +++ b/test/TestDummies/DummyRollingFileAuditSink.cs @@ -1,25 +1,22 @@ -using System; -using System.Collections.Generic; -using Serilog.Core; +using Serilog.Core; using Serilog.Events; -namespace TestDummies +namespace TestDummies; + +public class DummyRollingFileAuditSink : ILogEventSink { - public class DummyRollingFileAuditSink : ILogEventSink - { - [ThreadStatic] - static List _emitted; + [ThreadStatic] + static List _emitted; - public static List Emitted => _emitted ?? (_emitted = new List()); + public static List Emitted => _emitted ?? (_emitted = new List()); - public void Emit(LogEvent logEvent) - { - Emitted.Add(logEvent); - } + public void Emit(LogEvent logEvent) + { + Emitted.Add(logEvent); + } - public static void Reset() - { - _emitted = null; - } + public static void Reset() + { + _emitted = null; } } diff --git a/test/TestDummies/DummyRollingFileSink.cs b/test/TestDummies/DummyRollingFileSink.cs index 2f6f229e..f7304cd1 100644 --- a/test/TestDummies/DummyRollingFileSink.cs +++ b/test/TestDummies/DummyRollingFileSink.cs @@ -1,25 +1,22 @@ -using System; -using System.Collections.Generic; -using Serilog.Core; +using Serilog.Core; using Serilog.Events; -namespace TestDummies +namespace TestDummies; + +public class DummyRollingFileSink : ILogEventSink { - public class DummyRollingFileSink : ILogEventSink - { - [ThreadStatic] - static List _emitted; + [ThreadStatic] + static List _emitted; - public static List Emitted => _emitted ?? (_emitted = new List()); + public static List Emitted => _emitted ?? (_emitted = new List()); - public void Emit(LogEvent logEvent) - { - Emitted.Add(logEvent); - } + public void Emit(LogEvent logEvent) + { + Emitted.Add(logEvent); + } - public static void Reset() - { - _emitted = null; - } + public static void Reset() + { + _emitted = null; } } diff --git a/test/TestDummies/DummyThreadIdEnricher.cs b/test/TestDummies/DummyThreadIdEnricher.cs index a640d55e..c9ae9d86 100644 --- a/test/TestDummies/DummyThreadIdEnricher.cs +++ b/test/TestDummies/DummyThreadIdEnricher.cs @@ -1,14 +1,13 @@ using Serilog.Core; using Serilog.Events; -namespace TestDummies +namespace TestDummies; + +public class DummyThreadIdEnricher : ILogEventEnricher { - public class DummyThreadIdEnricher : ILogEventEnricher + public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) { - public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) - { - logEvent.AddPropertyIfAbsent(propertyFactory - .CreateProperty("ThreadId", "SomeId")); - } + logEvent.AddPropertyIfAbsent(propertyFactory + .CreateProperty("ThreadId", "SomeId")); } } diff --git a/test/TestDummies/DummyWithLevelSwitchSink.cs b/test/TestDummies/DummyWithLevelSwitchSink.cs index 4e1d76fc..90856d3a 100644 --- a/test/TestDummies/DummyWithLevelSwitchSink.cs +++ b/test/TestDummies/DummyWithLevelSwitchSink.cs @@ -1,28 +1,25 @@ -using System; -using System.Collections.Generic; -using Serilog.Core; +using Serilog.Core; using Serilog.Events; -namespace TestDummies +namespace TestDummies; + +public class DummyWithLevelSwitchSink : ILogEventSink { - public class DummyWithLevelSwitchSink : ILogEventSink + public DummyWithLevelSwitchSink(LoggingLevelSwitch loggingControlLevelSwitch) { - public DummyWithLevelSwitchSink(LoggingLevelSwitch loggingControlLevelSwitch) - { - ControlLevelSwitch = loggingControlLevelSwitch; - } + ControlLevelSwitch = loggingControlLevelSwitch; + } - [ThreadStatic] - public static LoggingLevelSwitch ControlLevelSwitch; + [ThreadStatic] + public static LoggingLevelSwitch ControlLevelSwitch; - [ThreadStatic] - // ReSharper disable ThreadStaticFieldHasInitializer - public static List Emitted = new List(); - // ReSharper restore ThreadStaticFieldHasInitializer + [ThreadStatic] + // ReSharper disable ThreadStaticFieldHasInitializer + public static List Emitted = new List(); + // ReSharper restore ThreadStaticFieldHasInitializer - public void Emit(LogEvent logEvent) - { - Emitted.Add(logEvent); - } + public void Emit(LogEvent logEvent) + { + Emitted.Add(logEvent); } } diff --git a/test/TestDummies/DummyWrappingSink.cs b/test/TestDummies/DummyWrappingSink.cs index cb2f048f..b4bc3ddb 100644 --- a/test/TestDummies/DummyWrappingSink.cs +++ b/test/TestDummies/DummyWrappingSink.cs @@ -1,33 +1,30 @@ -using System; -using Serilog.Core; +using Serilog.Core; using Serilog.Events; -using System.Collections.Generic; -namespace TestDummies +namespace TestDummies; + +public class DummyWrappingSink : ILogEventSink { - public class DummyWrappingSink : ILogEventSink - { - [ThreadStatic] - static List _emitted; + [ThreadStatic] + static List _emitted; - public static List Emitted => _emitted ?? (_emitted = new List()); + public static List Emitted => _emitted ?? (_emitted = new List()); - readonly ILogEventSink _sink; + readonly ILogEventSink _sink; - public DummyWrappingSink(ILogEventSink sink) - { - _sink = sink; - } + public DummyWrappingSink(ILogEventSink sink) + { + _sink = sink; + } - public void Emit(LogEvent logEvent) - { - Emitted.Add(logEvent); - _sink.Emit(logEvent); - } + public void Emit(LogEvent logEvent) + { + Emitted.Add(logEvent); + _sink.Emit(logEvent); + } - public static void Reset() - { - _emitted = null; - } + public static void Reset() + { + _emitted = null; } } diff --git a/test/TestDummies/TestDummies.csproj b/test/TestDummies/TestDummies.csproj index 24f9c252..93ea6cc8 100644 --- a/test/TestDummies/TestDummies.csproj +++ b/test/TestDummies/TestDummies.csproj @@ -16,4 +16,8 @@ + + + + From e0ca4636eac212162609acc51d4bac6940f08375 Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Fri, 6 Jan 2023 14:44:51 +0300 Subject: [PATCH 15/64] Small cleanup --- README.md | 2 +- .../Serilog.Settings.Configuration.csproj | 6 +- .../DynamicLevelChangeTests.cs | 69 +++++++++---------- 3 files changed, 36 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 6085e4d9..8d3e8fdb 100644 --- a/README.md +++ b/README.md @@ -226,7 +226,7 @@ This section defines a static list of key-value pairs that will enrich log event ### Filter section -This section defines filters that will be applied to log events. It is especially usefull in combination with _[Serilog.Expressions](https://github.com/serilog/serilog-expressions)_ (or legacy _[Serilog.Filters.Expressions](https://github.com/serilog/serilog-filters-expressions)_) package so you can write expression in text form: +This section defines filters that will be applied to log events. It is especially useful in combination with _[Serilog.Expressions](https://github.com/serilog/serilog-expressions)_ (or legacy _[Serilog.Filters.Expressions](https://github.com/serilog/serilog-filters-expressions)_) package so you can write expression in text form: ```yaml "Filter": [{ diff --git a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj index c12d61db..a2d81608 100644 --- a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj +++ b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj @@ -3,19 +3,15 @@ Microsoft.Extensions.Configuration (appsettings.json) support for Serilog. 3.5.0 - latest Serilog Contributors netstandard2.0;net451;net461 - true true Serilog.Settings.Configuration ../../assets/Serilog.snk true true - Serilog.Settings.Configuration serilog;json icon.png - https://github.com/serilog/serilog-settings-configuration/ Apache-2.0 Serilog true @@ -28,7 +24,7 @@ - + diff --git a/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs b/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs index 8df43fb5..1c3eb5ac 100644 --- a/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs @@ -45,43 +45,42 @@ public DynamicLevelChangeTests() [Fact] public void ShouldRespectDynamicLevelChanges() { - using (var logger = new LoggerConfiguration() + using var logger = new LoggerConfiguration() .ReadFrom .Configuration(new ConfigurationBuilder().Add(_configSource).Build()) - .CreateLogger()) - { - DummyConsoleSink.Emitted.Clear(); - logger.Write(Some.DebugEvent()); - Assert.Empty(DummyConsoleSink.Emitted); - - DummyConsoleSink.Emitted.Clear(); - UpdateConfig(minimumLevel: LogEventLevel.Debug); - logger.Write(Some.DebugEvent()); - Assert.Empty(DummyConsoleSink.Emitted); - - DummyConsoleSink.Emitted.Clear(); - UpdateConfig(switchLevel: LogEventLevel.Debug); - logger.Write(Some.DebugEvent()); - logger.ForContext(Constants.SourceContextPropertyName, "Root.Test").Write(Some.DebugEvent()); - Assert.Single(DummyConsoleSink.Emitted); - - DummyConsoleSink.Emitted.Clear(); - UpdateConfig(overrideLevel: LogEventLevel.Debug); - logger.ForContext(Constants.SourceContextPropertyName, "Root.Test").Write(Some.DebugEvent()); - Assert.Single(DummyConsoleSink.Emitted); - - DummyConsoleSink.Emitted.Clear(); - UpdateConfig(filterExpression: "Prop = 'Val_1'"); - logger.Write(Some.DebugEvent()); - logger.ForContext("Prop", "Val_1").Write(Some.DebugEvent()); - Assert.Single(DummyConsoleSink.Emitted); - - DummyConsoleSink.Emitted.Clear(); - UpdateConfig(filterExpression: "Prop = 'Val_2'"); - logger.Write(Some.DebugEvent()); - logger.ForContext("Prop", "Val_1").Write(Some.DebugEvent()); - Assert.Empty(DummyConsoleSink.Emitted); - } + .CreateLogger(); + + DummyConsoleSink.Emitted.Clear(); + logger.Write(Some.DebugEvent()); + Assert.Empty(DummyConsoleSink.Emitted); + + DummyConsoleSink.Emitted.Clear(); + UpdateConfig(minimumLevel: LogEventLevel.Debug); + logger.Write(Some.DebugEvent()); + Assert.Empty(DummyConsoleSink.Emitted); + + DummyConsoleSink.Emitted.Clear(); + UpdateConfig(switchLevel: LogEventLevel.Debug); + logger.Write(Some.DebugEvent()); + logger.ForContext(Constants.SourceContextPropertyName, "Root.Test").Write(Some.DebugEvent()); + Assert.Single(DummyConsoleSink.Emitted); + + DummyConsoleSink.Emitted.Clear(); + UpdateConfig(overrideLevel: LogEventLevel.Debug); + logger.ForContext(Constants.SourceContextPropertyName, "Root.Test").Write(Some.DebugEvent()); + Assert.Single(DummyConsoleSink.Emitted); + + DummyConsoleSink.Emitted.Clear(); + UpdateConfig(filterExpression: "Prop = 'Val_1'"); + logger.Write(Some.DebugEvent()); + logger.ForContext("Prop", "Val_1").Write(Some.DebugEvent()); + Assert.Single(DummyConsoleSink.Emitted); + + DummyConsoleSink.Emitted.Clear(); + UpdateConfig(filterExpression: "Prop = 'Val_2'"); + logger.Write(Some.DebugEvent()); + logger.ForContext("Prop", "Val_1").Write(Some.DebugEvent()); + Assert.Empty(DummyConsoleSink.Emitted); } void UpdateConfig(LogEventLevel? minimumLevel = null, LogEventLevel? switchLevel = null, LogEventLevel? overrideLevel = null, string filterExpression = null) From beefbfe4f8ea2c28f4f9e61f1a4d57b30e84838a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Sat, 18 Feb 2023 00:36:05 +0100 Subject: [PATCH 16/64] Drop support for .NET Framework 4.5.1 Support for .NET Framework 4, 4.5, and 4.5.1 [ended on January 12, 2016](https://learn.microsoft.com/en-GB/lifecycle/faq/dotnet-framework), that was 7 years ago! Also bump Microsoft.Extensions.Configuration.Binder and Microsoft.Extensions.DependencyModel to version 6.0.0 since version 2.0.0 and 3.0.0 respectively have been deprecated as part of the [.NET Package Deprecation effort](https://github.com/dotnet/announcements/issues/217). Finally, this change allows to remove all conditional compilation. Note that removing a target framework is a breaking change so it will require a major version bump, i.e. 4.0.0. --- .../Serilog.Settings.Configuration.csproj | 16 +++++++--------- .../Configuration/ConfigurationReader.cs | 10 ---------- .../ConfigurationReaderTests.cs | 4 ---- 3 files changed, 7 insertions(+), 23 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj index a2d81608..4896d640 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.5.0 Serilog Contributors - netstandard2.0;net451;net461 + netstandard2.0;net461 true Serilog.Settings.Configuration ../../assets/Serilog.snk @@ -20,18 +20,16 @@ snupkg + + + + - + + - - - - - - - diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 8b20544e..828dee16 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -20,18 +20,14 @@ class ConfigurationReader : IConfigurationReader readonly IConfigurationSection _section; readonly IReadOnlyCollection _configurationAssemblies; readonly ResolutionContext _resolutionContext; -#if NETSTANDARD || NET461 readonly IConfigurationRoot _configurationRoot; -#endif public ConfigurationReader(IConfigurationSection configSection, AssemblyFinder assemblyFinder, IConfiguration configuration = null) { _section = configSection ?? throw new ArgumentNullException(nameof(configSection)); _configurationAssemblies = LoadConfigurationAssemblies(_section, assemblyFinder); _resolutionContext = new ResolutionContext(configuration); -#if NETSTANDARD || NET461 _configurationRoot = configuration as IConfigurationRoot; -#endif } // Used internally for processing nested configuration sections -- see GetMethodCalls below. @@ -40,9 +36,7 @@ internal ConfigurationReader(IConfigurationSection configSection, IReadOnlyColle _section = configSection ?? throw new ArgumentNullException(nameof(configSection)); _configurationAssemblies = configurationAssemblies ?? throw new ArgumentNullException(nameof(configurationAssemblies)); _resolutionContext = resolutionContext ?? throw new ArgumentNullException(nameof(resolutionContext)); - #if NETSTANDARD || NET461 _configurationRoot = resolutionContext.HasAppConfiguration ? resolutionContext.AppConfiguration as IConfigurationRoot : null; - #endif } public void Configure(LoggerConfiguration loggerConfiguration) @@ -192,8 +186,6 @@ void ApplyMinimumLevelConfiguration(IConfigurationSection directive, Action MixedMinimumLevel => new List { @@ -266,8 +264,6 @@ public void MixedMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root AssertLogEventLevels(loggerConfig, expectedMinimumLevel); } - #endif - [Fact] public void NoConfigurationRootUsedStillValid() { From ab5d7a4415ab145d79b9fd776443217c32127718 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 9 Mar 2023 16:00:09 +1000 Subject: [PATCH 17/64] .NET 4.5.1 support dropped --- .../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 4896d640..bd8809e4 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. - 3.5.0 + 4.0.0 Serilog Contributors netstandard2.0;net461 true From fbc5e72b06787fd7639ab9c3179c856db34ec030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Sat, 11 Feb 2023 09:28:56 +0100 Subject: [PATCH 18/64] Add support for IFormatProvider used to convert string to other types Also introduce a new `ConfigurationReaderOptions` class to avoid `ReadFrom.Configuration()` methods exponential growth when adding new options. All _older_ `Configuration()` methods go through the newly introduced `Configuration(LoggerSettingsConfiguration, IConfiguration, ConfigurationReaderOptions)` method that takes an `ConfigurationReaderOptions` instance. Older methods explicitly set the `FormatProvider` option to `null` in order to preserve backward compatibility. By using the new `Configuration()` method, users opt into the new default of having the invariant culture as the format provider. Note: the `= null` default value in the `Configuration()` method taking a `DependencyContext` has been removed in order to make sure the CS0121 compilation does not occur: > [CS0121] The call is ambiguous between the following methods or properties: 'ConfigurationLoggerConfigurationExtensions.Configuration(LoggerSettingsConfiguration, IConfiguration, DependencyContext)' and 'ConfigurationLoggerConfigurationExtensions.Configuration(LoggerSettingsConfiguration, IConfiguration, ConfigurationReaderOptions)' Fixes #325 --- README.md | 14 +++- ...figurationLoggerConfigurationExtensions.cs | 75 ++++++++++++++----- .../Serilog.Settings.Configuration.csproj | 1 + .../Configuration/ConfigurationReader.cs | 4 +- .../ConfigurationReaderOptions.cs | 62 +++++++++++++++ .../Configuration/ResolutionContext.cs | 5 +- .../Configuration/StringArgumentValue.cs | 2 +- .../ConfigurationReaderTests.cs | 14 ++-- .../ConfigurationSettingsTests.cs | 49 +++++++++++- .../LoggerConfigurationExtensionsTests.cs | 2 +- .../DummyLoggerConfigurationExtensions.cs | 13 ++++ test/TestDummies/DummyPolicy.cs | 6 ++ 12 files changed, 213 insertions(+), 34 deletions(-) create mode 100644 src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs diff --git a/README.md b/README.md index 8d3e8fdb..744aa13a 100644 --- a/README.md +++ b/README.md @@ -65,8 +65,9 @@ Root section name can be changed: ``` ```csharp +var options = new ConfigurationReaderOptions { SectionName = "CustomSection" }; var logger = new LoggerConfiguration() - .ReadFrom.Configuration(configuration, sectionName: "CustomSection") + .ReadFrom.Configuration(configuration, options) .CreateLogger(); ``` @@ -106,8 +107,9 @@ In case of [non-standard](#azure-functions-v2-v3) dependency management you can ```csharp var functionDependencyContext = DependencyContext.Load(typeof(Startup).Assembly); +var options = new ConfigurationReaderOptions(functionDependencyContext) { SectionName = "AzureFunctionsJobHost:Serilog" }; var logger = new LoggerConfiguration() - .ReadFrom.Configuration(hostConfig, sectionName: "AzureFunctionsJobHost:Serilog", dependencyContext: functionDependencyContext) + .ReadFrom.Configuration(hostConfig, options) .CreateLogger(); ``` @@ -119,8 +121,9 @@ var configurationAssemblies = new[] typeof(ConsoleLoggerConfigurationExtensions).Assembly, typeof(FileLoggerConfigurationExtensions).Assembly, }; +var options = new ConfigurationReaderOptions(configurationAssemblies); var logger = new LoggerConfiguration() - .ReadFrom.Configuration(configuration, configurationAssemblies) + .ReadFrom.Configuration(configuration, options) .CreateLogger(); ``` @@ -282,6 +285,8 @@ Some Serilog packages require a reference to a logger configuration object. The 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`, `TimeSpan`, `enum`, arrays and custom collections. +Since version 4.0.0, conversion will use the invariant culture (`CultureInfo.InvariantCulture`) as long as the `ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptions options)` method is used. Obsolete methods use the current culture to preserve backward compatibility. + ### Static member support Static member access can be used for passing to the configuration argument via [special](https://github.com/serilog/serilog-settings-configuration/blob/dev/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs#L35) syntax: @@ -377,8 +382,9 @@ public class Startup : FunctionsStartup var functionDependencyContext = DependencyContext.Load(typeof(Startup).Assembly); var hostConfig = sp.GetRequiredService(); + var options = new ConfigurationReaderOptions(functionDependencyContext) { SectionName = "AzureFunctionsJobHost:Serilog" }; var logger = new LoggerConfiguration() - .ReadFrom.Configuration(hostConfig, sectionName: "AzureFunctionsJobHost:Serilog", dependencyContext: functionDependencyContext) + .ReadFrom.Configuration(hostConfig, options) .CreateLogger(); return new SerilogLoggerProvider(logger, dispose: true); diff --git a/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs b/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs index 4e24eef2..4ddf35db 100644 --- a/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs +++ b/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs @@ -1,4 +1,4 @@ -// Copyright 2013-2016 Serilog Contributors +// 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. @@ -42,6 +42,7 @@ public static class ConfigurationLoggerConfigurationExtensions /// 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. + [Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptions readerOptions) instead.")] public static LoggerConfiguration Configuration( this LoggerSettingsConfiguration settingConfiguration, IConfiguration configuration, @@ -52,15 +53,8 @@ public static LoggerConfiguration Configuration( if (configuration == null) throw new ArgumentNullException(nameof(configuration)); if (sectionName == null) throw new ArgumentNullException(nameof(sectionName)); - var assemblyFinder = dependencyContext == null - ? AssemblyFinder.Auto() - : AssemblyFinder.ForDependencyContext(dependencyContext); - - return settingConfiguration.Settings( - new ConfigurationReader( - configuration.GetSection(sectionName), - assemblyFinder, - configuration)); + var readerOptions = new ConfigurationReaderOptions(dependencyContext) { SectionName = sectionName, FormatProvider = null }; + return Configuration(settingConfiguration, configuration, readerOptions); } /// @@ -73,10 +67,11 @@ public static LoggerConfiguration Configuration( /// 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. + [Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptions readerOptions) instead.")] public static LoggerConfiguration Configuration( this LoggerSettingsConfiguration settingConfiguration, IConfiguration configuration, - DependencyContext dependencyContext = null) + DependencyContext dependencyContext) => Configuration(settingConfiguration, configuration, DefaultSectionName, dependencyContext); /// @@ -105,7 +100,8 @@ public static LoggerConfiguration ConfigurationSection( new ConfigurationReader( configSection, assemblyFinder, - configuration: null)); + configuration: null, + formatProvider: null)); } /// @@ -118,6 +114,7 @@ public static LoggerConfiguration ConfigurationSection( /// A section name for section which contains a Serilog section. /// Defines how the package identifies assemblies to scan for sinks and other types. /// An object allowing configuration to continue. + [Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptions readerOptions) instead.")] public static LoggerConfiguration Configuration( this LoggerSettingsConfiguration settingConfiguration, IConfiguration configuration, @@ -128,9 +125,8 @@ public static LoggerConfiguration Configuration( if (configuration == null) throw new ArgumentNullException(nameof(configuration)); if (sectionName == null) throw new ArgumentNullException(nameof(sectionName)); - var assemblyFinder = AssemblyFinder.ForSource(configurationAssemblySource); - - return settingConfiguration.Settings(new ConfigurationReader(configuration.GetSection(sectionName), assemblyFinder, configuration)); + var readerOptions = new ConfigurationReaderOptions(configurationAssemblySource) { SectionName = sectionName, FormatProvider = null }; + return Configuration(settingConfiguration, configuration, readerOptions); } /// @@ -142,6 +138,7 @@ public static LoggerConfiguration 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. + [Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptions readerOptions) instead.")] public static LoggerConfiguration Configuration( this LoggerSettingsConfiguration settingConfiguration, IConfiguration configuration, @@ -167,7 +164,7 @@ public static LoggerConfiguration ConfigurationSection( var assemblyFinder = AssemblyFinder.ForSource(configurationAssemblySource); - return settingConfiguration.Settings(new ConfigurationReader(configSection, assemblyFinder, configuration: null)); + return settingConfiguration.Settings(new ConfigurationReader(configSection, assemblyFinder, configuration: null, formatProvider: null)); } /// @@ -178,6 +175,7 @@ public static LoggerConfiguration ConfigurationSection( /// A section name for section which contains a Serilog section. /// A collection of assemblies that contains sinks and other types. /// An object allowing configuration to continue. + [Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptions readerOptions) instead.")] public static LoggerConfiguration Configuration( this LoggerSettingsConfiguration settingConfiguration, IConfiguration configuration, @@ -188,7 +186,8 @@ public static LoggerConfiguration Configuration( if (configuration == null) throw new ArgumentNullException(nameof(configuration)); if (sectionName == null) throw new ArgumentNullException(nameof(sectionName)); - return settingConfiguration.Settings(new ConfigurationReader(configuration.GetSection(sectionName), assemblies, new ResolutionContext(configuration))); + var readerOptions = new ConfigurationReaderOptions(assemblies) { SectionName = sectionName, FormatProvider = null }; + return Configuration(settingConfiguration, configuration, readerOptions); } /// @@ -198,9 +197,51 @@ public static LoggerConfiguration Configuration( /// A configuration object which contains a Serilog section. /// A collection of assemblies that contains sinks and other types. /// An object allowing configuration to continue. + [Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptions readerOptions) instead.")] public static LoggerConfiguration Configuration( this LoggerSettingsConfiguration settingConfiguration, IConfiguration configuration, params Assembly[] assemblies) => Configuration(settingConfiguration, configuration, DefaultSectionName, assemblies); + + /// + /// Reads logger settings from the provided configuration object using the specified context. + /// + /// Logger setting configuration. + /// A configuration object which contains a Serilog section. + /// Options to adjust how the configuration object is processed. + /// An object allowing configuration to continue. + public static LoggerConfiguration Configuration( + this LoggerSettingsConfiguration settingConfiguration, + IConfiguration configuration, + ConfigurationReaderOptions readerOptions = null) + { + var configurationReader = readerOptions switch + { + { ConfigurationAssemblySource: {} } => GetConfigurationReader(configuration, readerOptions, readerOptions.ConfigurationAssemblySource.Value), + { Assemblies: {} } => GetConfigurationReader(configuration, readerOptions, readerOptions.Assemblies), + _ => GetConfigurationReader(configuration, readerOptions ?? new ConfigurationReaderOptions(), readerOptions?.DependencyContext), + }; + return settingConfiguration.Settings(configurationReader); + } + + static ConfigurationReader GetConfigurationReader(IConfiguration configuration, ConfigurationReaderOptions readerOptions, DependencyContext dependencyContext) + { + var assemblyFinder = dependencyContext == null ? AssemblyFinder.Auto() : AssemblyFinder.ForDependencyContext(dependencyContext); + var section = configuration.GetSection(readerOptions.SectionName); + return new ConfigurationReader(section, assemblyFinder, readerOptions.FormatProvider, configuration); + } + + static ConfigurationReader GetConfigurationReader(IConfiguration configuration, ConfigurationReaderOptions readerOptions, ConfigurationAssemblySource source) + { + var assemblyFinder = AssemblyFinder.ForSource(source); + var section = configuration.GetSection(readerOptions.SectionName); + return new ConfigurationReader(section, assemblyFinder, readerOptions.FormatProvider, configuration); + } + + static ConfigurationReader GetConfigurationReader(IConfiguration configuration, ConfigurationReaderOptions readerOptions, IReadOnlyCollection assemblies) + { + var section = configuration.GetSection(readerOptions.SectionName); + return new ConfigurationReader(section, assemblies, new ResolutionContext(configuration, readerOptions.FormatProvider)); + } } diff --git a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj index bd8809e4..da033d11 100644 --- a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj +++ b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj @@ -28,6 +28,7 @@ + diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 828dee16..cd10c3e5 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -22,11 +22,11 @@ class ConfigurationReader : IConfigurationReader readonly ResolutionContext _resolutionContext; readonly IConfigurationRoot _configurationRoot; - public ConfigurationReader(IConfigurationSection configSection, AssemblyFinder assemblyFinder, IConfiguration configuration = null) + public ConfigurationReader(IConfigurationSection configSection, AssemblyFinder assemblyFinder, IFormatProvider formatProvider, IConfiguration configuration = null) { _section = configSection ?? throw new ArgumentNullException(nameof(configSection)); _configurationAssemblies = LoadConfigurationAssemblies(_section, assemblyFinder); - _resolutionContext = new ResolutionContext(configuration); + _resolutionContext = new ResolutionContext(configuration, formatProvider); _configurationRoot = configuration as IConfigurationRoot; } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs new file mode 100644 index 00000000..a4061ae6 --- /dev/null +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs @@ -0,0 +1,62 @@ +using System.Globalization; +using System.Reflection; +using Microsoft.Extensions.DependencyModel; + +namespace Serilog.Settings.Configuration; + +/// +/// Options to adjust how the configuration object is processed. +/// +public sealed class ConfigurationReaderOptions +{ + /// + /// Initialize a new instance of the class. + /// + /// A collection of assemblies that contains sinks and other types. + /// The argument is null. + /// The argument is empty. + public ConfigurationReaderOptions(params Assembly[] assemblies) + { + Assemblies = assemblies ?? throw new ArgumentNullException(nameof(assemblies)); + if (assemblies.Length == 0) + throw new ArgumentException("The assemblies array must not be empty.", nameof(assemblies)); + } + + /// + /// Initialize a new instance of the class. + /// + /// Prefer the constructor taking explicit assemblies: . It's the only one supporting single-file publishing. + public ConfigurationReaderOptions() : this(dependencyContext: null) + { + } + + /// + /// Initialize a new instance of the class. + /// + /// + /// The dependency context from which sink/enricher packages can be located. If , the platform default will be used. + /// + /// Prefer the constructor taking explicit assemblies: . It's the only one supporting single-file publishing. + public ConfigurationReaderOptions(DependencyContext dependencyContext) => DependencyContext = dependencyContext; + + /// + /// Initialize a new instance of the class. + /// + /// Defines how the package identifies assemblies to scan for sinks and other types. + /// Prefer the constructor taking explicit assemblies: . It's the only one supporting single-file publishing. + public ConfigurationReaderOptions(ConfigurationAssemblySource configurationAssemblySource) => ConfigurationAssemblySource = configurationAssemblySource; + + /// + /// The section name for section which contains a Serilog section. Defaults to Serilog. + /// + public string SectionName { get; init; } = ConfigurationLoggerConfigurationExtensions.DefaultSectionName; + + /// + /// The used when converting strings to other object types. Defaults to the invariant culture. + /// + public IFormatProvider FormatProvider { get; init; } = CultureInfo.InvariantCulture; + + internal Assembly[] Assemblies { get; } + internal DependencyContext DependencyContext { get; } + internal ConfigurationAssemblySource? ConfigurationAssemblySource { get; } +} diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs index 1db02a59..8959cdaa 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs @@ -13,13 +13,16 @@ sealed class ResolutionContext readonly IDictionary _declaredFilterSwitches; readonly IConfiguration _appConfiguration; - public ResolutionContext(IConfiguration appConfiguration = null) + public ResolutionContext(IConfiguration appConfiguration = null, IFormatProvider formatProvider = null) { _declaredLevelSwitches = new Dictionary(); _declaredFilterSwitches = new Dictionary(); _appConfiguration = appConfiguration; + FormatProvider = formatProvider; } + public IFormatProvider FormatProvider { get; } + /// /// Looks up a switch in the declared LoggingLevelSwitches /// diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs index c15c4b17..89db54d0 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs @@ -155,7 +155,7 @@ public object ConvertTo(Type toType, ResolutionContext resolutionContext) } } - return Convert.ChangeType(argumentValue, toType); + return Convert.ChangeType(argumentValue, toType, resolutionContext.FormatProvider); } internal static Type FindType(string typeName) diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs index 65cc7926..4658f703 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs @@ -1,4 +1,5 @@ -using System.Reflection; +using System.Globalization; +using System.Reflection; using Microsoft.Extensions.Configuration; using Serilog.Events; using Serilog.Formatting; @@ -16,7 +17,8 @@ public ConfigurationReaderTests() { _configurationReader = new ConfigurationReader( JsonStringConfigSource.LoadSection(@"{ 'Serilog': { } }", "Serilog"), - AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies)); + AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), + CultureInfo.InvariantCulture); } [Fact] @@ -190,7 +192,7 @@ public void MethodsAreSelectedBasedOnCountOfMatchedArgumentsAndThenStringType() [MemberData(nameof(FlatMinimumLevel))] public void FlatMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, LogEventLevel expectedMinimumLevel) { - var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), root); + var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), CultureInfo.InvariantCulture, root); var loggerConfig = new LoggerConfiguration(); reader.Configure(loggerConfig); @@ -214,7 +216,7 @@ public void FlatMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, [MemberData(nameof(ObjectMinimumLevel))] public void ObjectMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, LogEventLevel expectedMinimumLevel) { - var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), root); + var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), CultureInfo.InvariantCulture, root); var loggerConfig = new LoggerConfiguration(); reader.Configure(loggerConfig); @@ -256,7 +258,7 @@ public void ObjectMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot roo [MemberData(nameof(MixedMinimumLevel))] public void MixedMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, LogEventLevel expectedMinimumLevel) { - var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), root); + var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), CultureInfo.InvariantCulture, root); var loggerConfig = new LoggerConfiguration(); reader.Configure(loggerConfig); @@ -268,7 +270,7 @@ public void MixedMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root public void NoConfigurationRootUsedStillValid() { var section = JsonStringConfigSource.LoadSection(@"{ 'Nest': { 'Serilog': { 'MinimumLevel': 'Error' } } }", "Nest"); - var reader = new ConfigurationReader(section.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), section); + var reader = new ConfigurationReader(section.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), CultureInfo.InvariantCulture, section); var loggerConfig = new LoggerConfiguration(); reader.Configure(loggerConfig); diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 94e8ff9b..9689c05d 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Configuration; +using System.Globalization; +using Microsoft.Extensions.Configuration; using Serilog.Core; using Serilog.Events; using Serilog.Settings.Configuration.Tests.Support; @@ -102,7 +103,7 @@ public void ConfigurationAssembliesFromDllScanning() var log = new LoggerConfiguration() .ReadFrom.Configuration( configuration: config, - configurationAssemblySource: ConfigurationAssemblySource.AlwaysScanDllFiles) + readerOptions: new ConfigurationReaderOptions(ConfigurationAssemblySource.AlwaysScanDllFiles)) .CreateLogger(); DummyConsoleSink.Emitted.Clear(); @@ -648,6 +649,50 @@ public void SinkWithStringArrayArgument() Assert.Single(DummyRollingFileSink.Emitted); } + [Theory] + [InlineData(".")] + [InlineData(",")] + [Trait("BugFix", "https://github.com/serilog/serilog-settings-configuration/issues/325")] + public void DestructureNumericNumbers(string numberDecimalSeparator) + { + var originalCulture = Thread.CurrentThread.CurrentCulture; + + var culture = (CultureInfo)CultureInfo.InvariantCulture.Clone(); + culture.NumberFormat.NumberDecimalSeparator = numberDecimalSeparator; + + Thread.CurrentThread.CurrentCulture = culture; + + try + { + var json = @"{ + ""Serilog"": { + ""Using"": [ ""TestDummies"" ], + ""Destructure"": [{ + ""Name"": ""DummyNumbers"", + ""Args"": { + ""floatValue"": 0.1, + ""doubleValue"": 0.2, + ""decimalValue"": 0.3 + } + }] + } + }"; + + DummyPolicy.Current = null; + + ConfigFromJson(json); + + Assert.NotNull(DummyPolicy.Current); + Assert.Equal(0.1f, DummyPolicy.Current.Float); + Assert.Equal(0.2d, DummyPolicy.Current.Double); + Assert.Equal(0.3m, DummyPolicy.Current.Decimal); + } + finally + { + Thread.CurrentThread.CurrentCulture = originalCulture; + } + } + [Fact] public void DestructureWithCollectionsOfTypeArgument() { diff --git a/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs b/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs index c022aee3..0c76cc34 100644 --- a/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs @@ -96,7 +96,7 @@ public void ReadFromConfigurationDoesNotThrowWhenTryingToCallConfigurationMethod .Build(); _ = new LoggerConfiguration() - .ReadFrom.Configuration(config, "NotSerilog") + .ReadFrom.Configuration(config, new ConfigurationReaderOptions { SectionName = "NotSerilog" }) .CreateLogger(); } diff --git a/test/TestDummies/DummyLoggerConfigurationExtensions.cs b/test/TestDummies/DummyLoggerConfigurationExtensions.cs index 6d973791..7dfccc0c 100644 --- a/test/TestDummies/DummyLoggerConfigurationExtensions.cs +++ b/test/TestDummies/DummyLoggerConfigurationExtensions.cs @@ -156,4 +156,17 @@ public static LoggerConfiguration DummyArrayOfType(this LoggerDestructuringConfi CustomStrings = customString, }); } + + public static LoggerConfiguration DummyNumbers(this LoggerDestructuringConfiguration loggerSinkConfiguration, + float floatValue, + double doubleValue, + decimal decimalValue) + { + return loggerSinkConfiguration.With(DummyPolicy.Current = new DummyPolicy + { + Float = floatValue, + Double = doubleValue, + Decimal = decimalValue, + }); + } } diff --git a/test/TestDummies/DummyPolicy.cs b/test/TestDummies/DummyPolicy.cs index cf138327..f289818f 100644 --- a/test/TestDummies/DummyPolicy.cs +++ b/test/TestDummies/DummyPolicy.cs @@ -18,6 +18,12 @@ public class DummyPolicy : IDestructuringPolicy public Type Type { get; set; } + public float Float { get; set; } + + public double Double { get; set; } + + public decimal Decimal { get; set; } + public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, out LogEventPropertyValue result) { result = null; From 65285b199fe9a87549f22112dcc216c9e384fd4a Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Fri, 10 Mar 2023 14:52:37 +0300 Subject: [PATCH 19/64] Use raw string literals --- .../ConfigurationReaderTests.cs | 103 +- .../ConfigurationSettingsTests.cs | 896 ++++++++++-------- .../DynamicLevelChangeTests.cs | 6 +- .../LoggerConfigurationExtensionsTests.cs | 64 +- .../Support/ConfigurationReaderTestHelpers.cs | 24 +- 5 files changed, 599 insertions(+), 494 deletions(-) diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs index 4658f703..2c91abf6 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs @@ -16,7 +16,7 @@ public class ConfigurationReaderTests public ConfigurationReaderTests() { _configurationReader = new ConfigurationReader( - JsonStringConfigSource.LoadSection(@"{ 'Serilog': { } }", "Serilog"), + JsonStringConfigSource.LoadSection("{ 'Serilog': { } }", "Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), CultureInfo.InvariantCulture); } @@ -24,10 +24,11 @@ public ConfigurationReaderTests() [Fact] public void WriteToSupportSimplifiedSyntax() { - var json = @" -{ - 'WriteTo': [ 'LiterateConsole', 'DiagnosticTrace' ] -}"; + var json = """ + { + 'WriteTo': [ 'LiterateConsole', 'DiagnosticTrace' ] + } + """; var result = _configurationReader.GetMethodCalls(JsonStringConfigSource.LoadSection(json, "WriteTo")); Assert.Equal(2, result.Count); @@ -41,12 +42,13 @@ public void WriteToSupportSimplifiedSyntax() [Fact] public void WriteToSupportExpandedSyntaxWithoutArgs() { - var json = @" -{ - 'WriteTo': [ { - 'Name': 'LiterateConsole' - }] -}"; + var json = """ + { + 'WriteTo': [ { + 'Name': 'LiterateConsole' + }] + } + """; var result = _configurationReader.GetMethodCalls(JsonStringConfigSource.LoadSection(json, "WriteTo")); Assert.Equal(1, result.Count); @@ -58,15 +60,16 @@ public void WriteToSupportExpandedSyntaxWithoutArgs() [Fact] public void WriteToSupportExpandedSyntaxWithArgs() { - var json = @" -{ - 'WriteTo': [ { - 'Name': 'LiterateConsole', - 'Args': { - 'outputTemplate': '{Message}' - }, - }] -}"; + var json = """ + { + 'WriteTo': [ { + 'Name': 'LiterateConsole', + 'Args': { + 'outputTemplate': '{Message}' + }, + }] + } + """; var result = _configurationReader.GetMethodCalls(JsonStringConfigSource.LoadSection(json, "WriteTo")); @@ -85,30 +88,31 @@ public void WriteToSupportExpandedSyntaxWithArgs() [Fact] public void WriteToSupportMultipleSinksOfTheSameKind() { - var json = @" -{ - 'WriteTo': [ - { - 'Name': 'LiterateConsole', - 'Args': { - 'outputTemplate': '{Message}' - }, - }, - 'DiagnosticTrace' - ], - 'WriteTo:File1': { - 'Name': 'File', - 'Args': { - 'outputTemplate': '{Message}' - }, - }, - 'WriteTo:File2': { - 'Name': 'File', - 'Args': { - 'outputTemplate': '{Message}' - }, - } -}"; + var json = """ + { + 'WriteTo': [ + { + 'Name': 'LiterateConsole', + 'Args': { + 'outputTemplate': '{Message}' + }, + }, + 'DiagnosticTrace' + ], + 'WriteTo:File1': { + 'Name': 'File', + 'Args': { + 'outputTemplate': '{Message}' + }, + }, + 'WriteTo:File2': { + 'Name': 'File', + 'Args': { + 'outputTemplate': '{Message}' + }, + } + } + """; var result = _configurationReader.GetMethodCalls(JsonStringConfigSource.LoadSection(json, "WriteTo")); @@ -125,10 +129,11 @@ public void WriteToSupportMultipleSinksOfTheSameKind() [Fact] public void Enrich_SupportSimplifiedSyntax() { - var json = @" -{ - 'Enrich': [ 'FromLogContext', 'WithMachineName', 'WithThreadId' ] -}"; + var json = """ + { + 'Enrich': [ 'FromLogContext', 'WithMachineName', 'WithThreadId' ] + } + """; var result = _configurationReader.GetMethodCalls(JsonStringConfigSource.LoadSection(json, "Enrich")); Assert.Equal(3, result.Count); @@ -269,7 +274,7 @@ public void MixedMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root [Fact] public void NoConfigurationRootUsedStillValid() { - var section = JsonStringConfigSource.LoadSection(@"{ 'Nest': { 'Serilog': { 'MinimumLevel': 'Error' } } }", "Nest"); + var section = JsonStringConfigSource.LoadSection("{ 'Nest': { 'Serilog': { 'MinimumLevel': 'Error' } } }", "Nest"); var reader = new ConfigurationReader(section.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), CultureInfo.InvariantCulture, section); var loggerConfig = new LoggerConfiguration(); diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 9689c05d..9d823484 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -36,13 +36,15 @@ public void PropertyEnrichmentIsApplied() { LogEvent evt = null; - var json = @"{ - ""Serilog"": { - ""Properties"": { - ""App"": ""Test"" + var json = """ + { + "Serilog": { + "Properties": { + "App": "Test" } } - }"; + } + """; var log = ConfigFromJson(json) .WriteTo.Sink(new DelegatingSink(e => evt = e)) @@ -55,23 +57,25 @@ public void PropertyEnrichmentIsApplied() } [Theory] - [InlineData("extended syntax", - @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""WriteTo"": [ - { ""Name"": ""DummyConsole""}, - { ""Name"": ""DummyWithLevelSwitch""}, - ] - } - }")] - [InlineData("simplified syntax", - @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""WriteTo"": [""DummyConsole"", ""DummyWithLevelSwitch"" ] - } - }")] + [InlineData("extended syntax", """ + { + "Serilog": { + "Using": ["TestDummies"], + "WriteTo": [ + { "Name": "DummyConsole"}, + { "Name": "DummyWithLevelSwitch"}, + ] + } + } + """)] + [InlineData("simplified syntax", """ + { + "Serilog": { + "Using": ["TestDummies"], + "WriteTo": ["DummyConsole", "DummyWithLevelSwitch" ] + } + } + """)] public void ParameterlessSinksAreConfigured(string syntax, string json) { _ = syntax; @@ -91,12 +95,14 @@ public void ParameterlessSinksAreConfigured(string syntax, string json) [Fact] public void ConfigurationAssembliesFromDllScanning() { - var json = @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""WriteTo"": [""DummyConsole""] + var json = """ + { + "Serilog": { + "Using": ["TestDummies"], + "WriteTo": ["DummyConsole"] } - }"; + } + """; var builder = new ConfigurationBuilder().AddJsonString(json); var config = builder.Build(); @@ -116,15 +122,17 @@ public void ConfigurationAssembliesFromDllScanning() [Fact] public void SinksAreConfigured() { - var json = @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""WriteTo"": [{ - ""Name"": ""DummyRollingFile"", - ""Args"": {""pathFormat"" : ""C:\\""} + var json = """ + { + "Serilog": { + "Using": ["TestDummies"], + "WriteTo": [{ + "Name": "DummyRollingFile", + "Args": {"pathFormat" : "C:\\"} }] } - }"; + } + """; var log = ConfigFromJson(json) .CreateLogger(); @@ -141,15 +149,17 @@ public void SinksAreConfigured() [Fact] public void AuditSinksAreConfigured() { - var json = @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""AuditTo"": [{ - ""Name"": ""DummyRollingFile"", - ""Args"": {""pathFormat"" : ""C:\\""} + var json = """ + { + "Serilog": { + "Using": ["TestDummies"], + "AuditTo": [{ + "Name": "DummyRollingFile", + "Args": {"pathFormat" : "C:\\"} }] } - }"; + } + """; var log = ConfigFromJson(json) .CreateLogger(); @@ -166,21 +176,23 @@ public void AuditSinksAreConfigured() [Fact] public void AuditToSubLoggersAreConfigured() { - var json = @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""AuditTo"": [{ - ""Name"": ""Logger"", - ""Args"": { - ""configureLogger"" : { - ""AuditTo"": [{ - ""Name"": ""DummyRollingFile"", - ""Args"": {""pathFormat"" : ""C:\\""} - }]} - } - }] + var json = """ + { + "Serilog": { + "Using": ["TestDummies"], + "AuditTo": [{ + "Name": "Logger", + "Args": { + "configureLogger" : { + "AuditTo": [{ + "Name": "DummyRollingFile", + "Args": {"pathFormat" : "C:\\"} + }]} + } + }] + } } - }"; + """; var log = ConfigFromJson(json) .CreateLogger(); @@ -197,15 +209,17 @@ public void AuditToSubLoggersAreConfigured() [Fact] public void TestMinimumLevelOverrides() { - var json = @"{ - ""Serilog"": { - ""MinimumLevel"" : { - ""Override"" : { - ""System"" : ""Warning"" + var json = """ + { + "Serilog": { + "MinimumLevel" : { + "Override" : { + "System" : "Warning" } } } - }"; + } + """; LogEvent evt = null; @@ -229,17 +243,19 @@ public void TestMinimumLevelOverrides() [Fact] public void TestMinimumLevelOverridesForChildContext() { - var json = @"{ - ""Serilog"": { - ""MinimumLevel"" : { - ""Default"" : ""Warning"", - ""Override"" : { - ""System"" : ""Warning"", - ""System.Threading"": ""Debug"" + var json = """ + { + "Serilog": { + "MinimumLevel" : { + "Default" : "Warning", + "Override" : { + "System" : "Warning", + "System.Threading": "Debug" } } } - }"; + } + """; LogEvent evt = null; @@ -263,15 +279,17 @@ public void TestMinimumLevelOverridesForChildContext() [Fact] public void SinksWithAbstractParamsAreConfiguredWithTypeName() { - var json = @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""WriteTo"": [{ - ""Name"": ""DummyConsole"", - ""Args"": {""theme"" : ""Serilog.Settings.Configuration.Tests.Support.CustomConsoleTheme, Serilog.Settings.Configuration.Tests""} + var json = """ + { + "Serilog": { + "Using": ["TestDummies"], + "WriteTo": [{ + "Name": "DummyConsole", + "Args": {"theme" : "Serilog.Settings.Configuration.Tests.Support.CustomConsoleTheme, Serilog.Settings.Configuration.Tests"} }] } - }"; + } + """; DummyConsoleSink.Theme = null; @@ -285,15 +303,17 @@ public void SinksWithAbstractParamsAreConfiguredWithTypeName() [Fact] public void SinksAreConfiguredWithStaticMember() { - var json = @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""WriteTo"": [{ - ""Name"": ""DummyConsole"", - ""Args"": {""theme"" : ""TestDummies.Console.Themes.ConsoleThemes::Theme1, TestDummies""} + var json = """ + { + "Serilog": { + "Using": ["TestDummies"], + "WriteTo": [{ + "Name": "DummyConsole", + "Args": {"theme" : "TestDummies.Console.Themes.ConsoleThemes::Theme1, TestDummies"} }] } - }"; + } + """; DummyConsoleSink.Theme = null; @@ -328,11 +348,13 @@ public void LoggingLevelSwitchNameValidityScenarios(string switchName, bool expe [Fact] public void LoggingLevelSwitchWithInvalidNameThrowsFormatException() { - var json = @"{ - ""Serilog"": { - ""LevelSwitches"": {""1InvalidSwitchName"" : ""Warning"" } + var json = """ + { + "Serilog": { + "LevelSwitches": {"1InvalidSwitchName" : "Warning" } } - }"; + } + """; var ex = Assert.Throws(() => ConfigFromJson(json)); @@ -346,17 +368,19 @@ public void LoggingLevelSwitchWithInvalidNameThrowsFormatException() [InlineData("mySwitch")] public void LoggingFilterSwitchIsConfigured(string switchName) { - var json = $@"{{ - 'Serilog': {{ - 'FilterSwitches': {{ '{switchName}': 'Prop = 42' }}, - 'Filter:BySwitch': {{ + var json = $$""" + { + 'Serilog': { + 'FilterSwitches': { '{{switchName}}': 'Prop = 42' }, + 'Filter:BySwitch': { 'Name': 'ControlledBy', - 'Args': {{ + 'Args': { 'switch': '$mySwitch' - }} - }} - }} - }}"; + } + } + } + } + """; LogEvent evt = null; var log = ConfigFromJson(json) @@ -375,14 +399,16 @@ public void LoggingFilterSwitchIsConfigured(string switchName) [InlineData("switch1")] public void LoggingLevelSwitchIsConfigured(string switchName) { - var json = $@"{{ - 'Serilog': {{ - 'LevelSwitches': {{ '{switchName}' : 'Warning' }}, - 'MinimumLevel' : {{ + var json = $$""" + { + 'Serilog': { + 'LevelSwitches': { '{{switchName}}' : 'Warning' }, + 'MinimumLevel' : { 'ControlledBy' : '$switch1' - }} - }} - }}"; + } + } + } + """; LogEvent evt = null; var log = ConfigFromJson(json) @@ -400,14 +426,16 @@ public void LoggingLevelSwitchIsConfigured(string switchName) [Fact] public void SettingMinimumLevelControlledByToAnUndeclaredSwitchThrows() { - var json = @"{ - ""Serilog"": { - ""LevelSwitches"": {""$switch1"" : ""Warning"" }, - ""MinimumLevel"" : { - ""ControlledBy"" : ""$switch2"" + var json = """ + { + "Serilog": { + "LevelSwitches": {"$switch1" : "Warning" }, + "MinimumLevel" : { + "ControlledBy" : "$switch2" } } - }"; + } + """; var ex = Assert.Throws(() => ConfigFromJson(json) @@ -420,19 +448,21 @@ public void SettingMinimumLevelControlledByToAnUndeclaredSwitchThrows() [Fact] public void LoggingLevelSwitchIsPassedToSinks() { - var json = @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""LevelSwitches"": {""$switch1"" : ""Information"" }, - ""MinimumLevel"" : { - ""ControlledBy"" : ""$switch1"" + var json = """ + { + "Serilog": { + "Using": ["TestDummies"], + "LevelSwitches": {"$switch1" : "Information" }, + "MinimumLevel" : { + "ControlledBy" : "$switch1" }, - ""WriteTo"": [{ - ""Name"": ""DummyWithLevelSwitch"", - ""Args"": {""controlLevelSwitch"" : ""$switch1""} + "WriteTo": [{ + "Name": "DummyWithLevelSwitch", + "Args": {"controlLevelSwitch" : "$switch1"} }] } - }"; + } + """; LogEvent evt = null; @@ -456,19 +486,21 @@ public void LoggingLevelSwitchIsPassedToSinks() [Fact] public void ReferencingAnUndeclaredSwitchInSinkThrows() { - var json = @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""LevelSwitches"": {""$switch1"" : ""Information"" }, - ""MinimumLevel"" : { - ""ControlledBy"" : ""$switch1"" + var json = """ + { + "Serilog": { + "Using": ["TestDummies"], + "LevelSwitches": {"$switch1" : "Information" }, + "MinimumLevel" : { + "ControlledBy" : "$switch1" }, - ""WriteTo"": [{ - ""Name"": ""DummyWithLevelSwitch"", - ""Args"": {""controlLevelSwitch"" : ""$switch2""} + "WriteTo": [{ + "Name": "DummyWithLevelSwitch", + "Args": {"controlLevelSwitch" : "$switch2"} }] } - }"; + } + """; var ex = Assert.Throws(() => ConfigFromJson(json) @@ -481,22 +513,24 @@ public void ReferencingAnUndeclaredSwitchInSinkThrows() [Fact] public void LoggingLevelSwitchCanBeUsedForMinimumLevelOverrides() { - var json = @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""LevelSwitches"": {""$specificSwitch"" : ""Warning"" }, - ""MinimumLevel"" : { - ""Default"" : ""Debug"", - ""Override"" : { - ""System"" : ""$specificSwitch"" + var json = """ + { + "Serilog": { + "Using": ["TestDummies"], + "LevelSwitches": {"$specificSwitch" : "Warning" }, + "MinimumLevel" : { + "Default" : "Debug", + "Override" : { + "System" : "$specificSwitch" } }, - ""WriteTo"": [{ - ""Name"": ""DummyWithLevelSwitch"", - ""Args"": {""controlLevelSwitch"" : ""$specificSwitch""} + "WriteTo": [{ + "Name": "DummyWithLevelSwitch", + "Args": {"controlLevelSwitch" : "$specificSwitch"} }] } - }"; + } + """; LogEvent evt = null; @@ -532,15 +566,17 @@ public void LoggingLevelSwitchCanBeUsedForMinimumLevelOverrides() [Trait("BugFix", "https://github.com/serilog/serilog-settings-configuration/issues/142")] public void SinkWithIConfigurationArguments() { - var json = @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""WriteTo"": [{ - ""Name"": ""DummyWithConfiguration"", - ""Args"": {} + var json = """ + { + "Serilog": { + "Using": ["TestDummies"], + "WriteTo": [{ + "Name": "DummyWithConfiguration", + "Args": {} }] } - }"; + } + """; DummyConfigurationSink.Reset(); var log = ConfigFromJson(json, out var expectedConfig) @@ -556,15 +592,17 @@ public void SinkWithIConfigurationArguments() [Trait("BugFix", "https://github.com/serilog/serilog-settings-configuration/issues/142")] public void SinkWithOptionalIConfigurationArguments() { - var json = @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""WriteTo"": [{ - ""Name"": ""DummyWithOptionalConfiguration"", - ""Args"": {} + var json = """ + { + "Serilog": { + "Using": ["TestDummies"], + "WriteTo": [{ + "Name": "DummyWithOptionalConfiguration", + "Args": {} }] } - }"; + } + """; DummyConfigurationSink.Reset(); var log = ConfigFromJson(json, out var expectedConfig) @@ -580,15 +618,17 @@ public void SinkWithOptionalIConfigurationArguments() [Fact] public void SinkWithIConfigSectionArguments() { - var json = @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""WriteTo"": [{ - ""Name"": ""DummyWithConfigSection"", - ""Args"": {""configurationSection"" : { ""foo"" : ""bar"" } } + var json = """ + { + "Serilog": { + "Using": ["TestDummies"], + "WriteTo": [{ + "Name": "DummyWithConfigSection", + "Args": {"configurationSection" : { "foo" : "bar" } } }] } - }"; + } + """; DummyConfigurationSink.Reset(); var log = ConfigFromJson(json) @@ -604,16 +644,18 @@ public void SinkWithIConfigSectionArguments() [Fact] public void SinkWithConfigurationBindingArgument() { - var json = @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""WriteTo"": [{ - ""Name"": ""DummyRollingFile"", - ""Args"": {""pathFormat"" : ""C:\\"", - ""objectBinding"" : [ { ""foo"" : ""bar"" }, { ""abc"" : ""xyz"" } ] } + var json = """ + { + "Serilog": { + "Using": ["TestDummies"], + "WriteTo": [{ + "Name": "DummyRollingFile", + "Args": {"pathFormat" : "C:\\", + "objectBinding" : [ { "foo" : "bar" }, { "abc" : "xyz" } ] } }] } - }"; + } + """; var log = ConfigFromJson(json) .CreateLogger(); @@ -628,16 +670,18 @@ public void SinkWithConfigurationBindingArgument() [Fact] public void SinkWithStringArrayArgument() { - var json = @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""WriteTo"": [{ - ""Name"": ""DummyRollingFile"", - ""Args"": {""pathFormat"" : ""C:\\"", - ""stringArrayBinding"" : [ ""foo"", ""bar"", ""baz"" ] } + var json = """ + { + "Serilog": { + "Using": ["TestDummies"], + "WriteTo": [{ + "Name": "DummyRollingFile", + "Args": {"pathFormat" : "C:\\", + "stringArrayBinding" : [ "foo", "bar", "baz" ] } }] } - }"; + } + """; var log = ConfigFromJson(json) .CreateLogger(); @@ -664,19 +708,21 @@ public void DestructureNumericNumbers(string numberDecimalSeparator) try { - var json = @"{ - ""Serilog"": { - ""Using"": [ ""TestDummies"" ], - ""Destructure"": [{ - ""Name"": ""DummyNumbers"", - ""Args"": { - ""floatValue"": 0.1, - ""doubleValue"": 0.2, - ""decimalValue"": 0.3 + var json = """ + { + "Serilog": { + "Using": [ "TestDummies" ], + "Destructure": [{ + "Name": "DummyNumbers", + "Args": { + "floatValue": 0.1, + "doubleValue": 0.2, + "decimalValue": 0.3 } }] } - }"; + } + """; DummyPolicy.Current = null; @@ -696,31 +742,33 @@ public void DestructureNumericNumbers(string numberDecimalSeparator) [Fact] public void DestructureWithCollectionsOfTypeArgument() { - var json = @"{ - ""Serilog"": { - ""Using"": [ ""TestDummies"" ], - ""Destructure"": [{ - ""Name"": ""DummyArrayOfType"", - ""Args"": { - ""list"": [ - ""System.Byte"", - ""System.Int16"" + var json = """ + { + "Serilog": { + "Using": [ "TestDummies" ], + "Destructure": [{ + "Name": "DummyArrayOfType", + "Args": { + "list": [ + "System.Byte", + "System.Int16" ], - ""array"" : [ - ""System.Int32"", - ""System.String"" + "array" : [ + "System.Int32", + "System.String" ], - ""type"" : ""System.TimeSpan"", - ""custom"" : [ - ""System.Int64"" + "type" : "System.TimeSpan", + "custom" : [ + "System.Int64" ], - ""customString"" : [ - ""System.UInt32"" + "customString" : [ + "System.UInt32" ] } }] } - }"; + } + """; DummyPolicy.Current = null; @@ -737,16 +785,18 @@ public void DestructureWithCollectionsOfTypeArgument() [Fact] public void SinkWithIntArrayArgument() { - var json = @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""WriteTo"": [{ - ""Name"": ""DummyRollingFile"", - ""Args"": {""pathFormat"" : ""C:\\"", - ""intArrayBinding"" : [ 1,2,3,4,5 ] } + var json = """ + { + "Serilog": { + "Using": ["TestDummies"], + "WriteTo": [{ + "Name": "DummyRollingFile", + "Args": {"pathFormat" : "C:\\", + "intArrayBinding" : [ 1,2,3,4,5 ] } }] } - }"; + } + """; var log = ConfigFromJson(json) .CreateLogger(); @@ -762,15 +812,17 @@ public void SinkWithIntArrayArgument() [Fact] public void CaseInsensitiveArgumentNameMatching() { - var json = @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""WriteTo"": [{ - ""Name"": ""DummyRollingFile"", - ""Args"": {""PATHFORMAT"" : ""C:\\""} + var json = """ + { + "Serilog": { + "Using": ["TestDummies"], + "WriteTo": [{ + "Name": "DummyRollingFile", + "Args": {"PATHFORMAT" : "C:\\"} }] } - }"; + } + """; var log = ConfigFromJson(json) .CreateLogger(); @@ -786,22 +838,24 @@ public void CaseInsensitiveArgumentNameMatching() [Fact] public void WriteToLoggerWithRestrictedToMinimumLevelIsSupported() { - var json = @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""WriteTo"": [{ - ""Name"": ""Logger"", - ""Args"": { - ""configureLogger"" : { - ""WriteTo"": [{ - ""Name"": ""DummyRollingFile"", - ""Args"": {""pathFormat"" : ""C:\\""} + var json = """ + { + "Serilog": { + "Using": ["TestDummies"], + "WriteTo": [{ + "Name": "Logger", + "Args": { + "configureLogger" : { + "WriteTo": [{ + "Name": "DummyRollingFile", + "Args": {"pathFormat" : "C:\\"} }]}, - ""restrictedToMinimumLevel"": ""Warning"" + "restrictedToMinimumLevel": "Warning" } }] } - }"; + } + """; var log = ConfigFromJson(json) .CreateLogger(); @@ -818,25 +872,27 @@ public void WriteToLoggerWithRestrictedToMinimumLevelIsSupported() [Fact] public void WriteToSubLoggerWithLevelSwitchIsSupported() { - var json = @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""LevelSwitches"": {""$switch1"" : ""Warning"" }, - ""MinimumLevel"" : { - ""ControlledBy"" : ""$switch1"" + var json = """ + { + "Serilog": { + "Using": ["TestDummies"], + "LevelSwitches": {"$switch1" : "Warning" }, + "MinimumLevel" : { + "ControlledBy" : "$switch1" }, - ""WriteTo"": [{ - ""Name"": ""Logger"", - ""Args"": { - ""configureLogger"" : { - ""WriteTo"": [{ - ""Name"": ""DummyRollingFile"", - ""Args"": {""pathFormat"" : ""C:\\""} + "WriteTo": [{ + "Name": "Logger", + "Args": { + "configureLogger" : { + "WriteTo": [{ + "Name": "DummyRollingFile", + "Args": {"pathFormat" : "C:\\"} }]} } }] } - }"; + } + """; var log = ConfigFromJson(json) .CreateLogger(); @@ -853,25 +909,29 @@ public void WriteToSubLoggerWithLevelSwitchIsSupported() [Fact] public void InconsistentComplexVsScalarArgumentValuesThrowsIOE() { - var jsonDiscreteValue = @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""WriteTo"": [{ - ""Name"": ""DummyRollingFile"", - ""Args"": {""pathFormat"" : ""C:\\""} + var jsonDiscreteValue = """ + { + "Serilog": { + "Using": ["TestDummies"], + "WriteTo": [{ + "Name": "DummyRollingFile", + "Args": {"pathFormat" : "C:\\"} }] } - }"; - - var jsonComplexValue = @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""WriteTo"": [{ - ""Name"": ""DummyRollingFile"", - ""Args"": {""pathFormat"" : { ""foo"" : ""bar"" } } + } + """; + + 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 @@ -890,15 +950,17 @@ public void InconsistentComplexVsScalarArgumentValuesThrowsIOE() [Fact] public void DestructureLimitsNestingDepth() { - var json = @"{ - ""Serilog"": { - ""Destructure"": [ + var json = """ + { + "Serilog": { + "Destructure": [ { - ""Name"": ""ToMaximumDepth"", - ""Args"": { ""maximumDestructuringDepth"": 3 } + "Name": "ToMaximumDepth", + "Args": { "maximumDestructuringDepth": 3 } }] } - }"; + } + """; var NestedObject = new { @@ -923,15 +985,17 @@ public void DestructureLimitsNestingDepth() [Fact] public void DestructureLimitsStringLength() { - var json = @"{ - ""Serilog"": { - ""Destructure"": [ + var json = """ + { + "Serilog": { + "Destructure": [ { - ""Name"": ""ToMaximumStringLength"", - ""Args"": { ""maximumStringLength"": 3 } + "Name": "ToMaximumStringLength", + "Args": { "maximumStringLength": 3 } }] } - }"; + } + """; var inputString = "ABCDEFGH"; var msg = GetDestructuredProperty(inputString, json); @@ -942,15 +1006,17 @@ public void DestructureLimitsStringLength() [Fact] public void DestructureLimitsCollectionCount() { - var json = @"{ - ""Serilog"": { - ""Destructure"": [ + var json = """ + { + "Serilog": { + "Destructure": [ { - ""Name"": ""ToMaximumCollectionCount"", - ""Args"": { ""maximumCollectionCount"": 3 } + "Name": "ToMaximumCollectionCount", + "Args": { "maximumCollectionCount": 3 } }] } - }"; + } + """; var collection = new[] { 1, 2, 3, 4, 5, 6 }; var msg = GetDestructuredProperty(collection, json); @@ -973,16 +1039,18 @@ private static string GetDestructuredProperty(object x, string json) [Fact] public void DestructuringWithCustomExtensionMethodIsApplied() { - var json = @"{ - ""Serilog"": { - ""Using"": [""TestDummies""], - ""Destructure"": [ + var json = """ + { + "Serilog": { + "Using": ["TestDummies"], + "Destructure": [ { - ""Name"": ""WithDummyHardCodedString"", - ""Args"": { ""hardCodedString"": ""hardcoded"" } + "Name": "WithDummyHardCodedString", + "Args": { "hardCodedString": "hardcoded" } }] } - }"; + } + """; LogEvent evt = null; var log = ConfigFromJson(json) @@ -997,15 +1065,17 @@ public void DestructuringWithCustomExtensionMethodIsApplied() [Fact] public void DestructuringAsScalarIsAppliedWithShortTypeName() { - var json = @"{ - ""Serilog"": { - ""Destructure"": [ + var json = """ + { + "Serilog": { + "Destructure": [ { - ""Name"": ""AsScalar"", - ""Args"": { ""scalarType"": ""System.Version"" } + "Name": "AsScalar", + "Args": { "scalarType": "System.Version" } }] } - }"; + } + """; LogEvent evt = null; var log = ConfigFromJson(json) @@ -1021,15 +1091,17 @@ public void DestructuringAsScalarIsAppliedWithShortTypeName() [Fact] public void DestructuringAsScalarIsAppliedWithAssemblyQualifiedName() { - var json = $@"{{ - ""Serilog"": {{ - ""Destructure"": [ - {{ - ""Name"": ""AsScalar"", - ""Args"": {{ ""scalarType"": ""{typeof(Version).AssemblyQualifiedName}"" }} - }}] - }} - }}"; + var json = $$""" + { + "Serilog": { + "Destructure": [ + { + "Name": "AsScalar", + "Args": { "scalarType": "{{typeof(Version).AssemblyQualifiedName}}" } + }] + } + } + """; LogEvent evt = null; var log = ConfigFromJson(json) @@ -1045,18 +1117,20 @@ public void DestructuringAsScalarIsAppliedWithAssemblyQualifiedName() [Fact] public void WriteToSinkIsAppliedWithCustomSink() { - var json = $@"{{ - ""Serilog"": {{ - ""Using"": [""TestDummies""], - ""WriteTo"": [ - {{ - ""Name"": ""Sink"", - ""Args"": {{ - ""sink"": ""{typeof(DummyRollingFileSink).AssemblyQualifiedName}"" - }} - }}] - }} - }}"; + var json = $$""" + { + "Serilog": { + "Using": ["TestDummies"], + "WriteTo": [ + { + "Name": "Sink", + "Args": { + "sink": "{{typeof(DummyRollingFileSink).AssemblyQualifiedName}}" + } + }] + } + } + """; var log = ConfigFromJson(json) .CreateLogger(); @@ -1070,19 +1144,21 @@ public void WriteToSinkIsAppliedWithCustomSink() [Fact] public void WriteToSinkIsAppliedWithCustomSinkAndMinimumLevel() { - var json = $@"{{ - ""Serilog"": {{ - ""Using"": [""TestDummies""], - ""WriteTo"": [ - {{ - ""Name"": ""Sink"", - ""Args"": {{ - ""sink"": ""{typeof(DummyRollingFileSink).AssemblyQualifiedName}"", - ""restrictedToMinimumLevel"": ""Warning"" - }} - }}] - }} - }}"; + var json = $$""" + { + "Serilog": { + "Using": ["TestDummies"], + "WriteTo": [ + { + "Name": "Sink", + "Args": { + "sink": "{{typeof(DummyRollingFileSink).AssemblyQualifiedName}}", + "restrictedToMinimumLevel": "Warning" + } + }] + } + } + """; var log = ConfigFromJson(json) .CreateLogger(); @@ -1097,20 +1173,22 @@ public void WriteToSinkIsAppliedWithCustomSinkAndMinimumLevel() [Fact] public void WriteToSinkIsAppliedWithCustomSinkAndLevelSwitch() { - var json = $@"{{ - ""Serilog"": {{ - ""Using"": [""TestDummies""], - ""LevelSwitches"": {{""$switch1"": ""Warning"" }}, - ""WriteTo"": [ - {{ - ""Name"": ""Sink"", - ""Args"": {{ - ""sink"": ""{typeof(DummyRollingFileSink).AssemblyQualifiedName}"", - ""levelSwitch"": ""$switch1"" - }} - }}] - }} - }}"; + var json = $$""" + { + "Serilog": { + "Using": ["TestDummies"], + "LevelSwitches": {"$switch1": "Warning" }, + "WriteTo": [ + { + "Name": "Sink", + "Args": { + "sink": "{{typeof(DummyRollingFileSink).AssemblyQualifiedName}}", + "levelSwitch": "$switch1" + } + }] + } + } + """; var log = ConfigFromJson(json) .CreateLogger(); @@ -1125,18 +1203,20 @@ public void WriteToSinkIsAppliedWithCustomSinkAndLevelSwitch() [Fact] public void AuditToSinkIsAppliedWithCustomSink() { - var json = $@"{{ - ""Serilog"": {{ - ""Using"": [""TestDummies""], - ""AuditTo"": [ - {{ - ""Name"": ""Sink"", - ""Args"": {{ - ""sink"": ""{typeof(DummyRollingFileSink).AssemblyQualifiedName}"" - }} - }}] - }} - }}"; + var json = $$""" + { + "Serilog": { + "Using": ["TestDummies"], + "AuditTo": [ + { + "Name": "Sink", + "Args": { + "sink": "{{typeof(DummyRollingFileSink).AssemblyQualifiedName}}" + } + }] + } + } + """; var log = ConfigFromJson(json) .CreateLogger(); @@ -1150,19 +1230,21 @@ public void AuditToSinkIsAppliedWithCustomSink() [Fact] public void AuditToSinkIsAppliedWithCustomSinkAndMinimumLevel() { - var json = $@"{{ - ""Serilog"": {{ - ""Using"": [""TestDummies""], - ""AuditTo"": [ - {{ - ""Name"": ""Sink"", - ""Args"": {{ - ""sink"": ""{typeof(DummyRollingFileSink).AssemblyQualifiedName}"", - ""restrictedToMinimumLevel"": ""Warning"" - }} - }}] - }} - }}"; + var json = $$""" + { + "Serilog": { + "Using": ["TestDummies"], + "AuditTo": [ + { + "Name": "Sink", + "Args": { + "sink": "{{typeof(DummyRollingFileSink).AssemblyQualifiedName}}", + "restrictedToMinimumLevel": "Warning" + } + }] + } + } + """; var log = ConfigFromJson(json) .CreateLogger(); @@ -1177,20 +1259,22 @@ public void AuditToSinkIsAppliedWithCustomSinkAndMinimumLevel() [Fact] public void AuditToSinkIsAppliedWithCustomSinkAndLevelSwitch() { - var json = $@"{{ - ""Serilog"": {{ - ""Using"": [""TestDummies""], - ""LevelSwitches"": {{""$switch1"": ""Warning"" }}, - ""AuditTo"": [ - {{ - ""Name"": ""Sink"", - ""Args"": {{ - ""sink"": ""{typeof(DummyRollingFileSink).AssemblyQualifiedName}"", - ""levelSwitch"": ""$switch1"" - }} - }}] - }} - }}"; + var json = $$""" + { + "Serilog": { + "Using": ["TestDummies"], + "LevelSwitches": {"$switch1": "Warning" }, + "AuditTo": [ + { + "Name": "Sink", + "Args": { + "sink": "{{typeof(DummyRollingFileSink).AssemblyQualifiedName}}", + "levelSwitch": "$switch1" + } + }] + } + } + """; var log = ConfigFromJson(json) .CreateLogger(); @@ -1207,18 +1291,20 @@ public void EnrichWithIsAppliedWithCustomEnricher() { LogEvent evt = null; - var json = $@"{{ - ""Serilog"": {{ - ""Using"": [""TestDummies""], - ""Enrich"": [ - {{ - ""Name"": ""With"", - ""Args"": {{ - ""enricher"": ""{typeof(DummyThreadIdEnricher).AssemblyQualifiedName}"" - }} - }}] - }} - }}"; + var json = $$""" + { + "Serilog": { + "Using": ["TestDummies"], + "Enrich": [ + { + "Name": "With", + "Args": { + "enricher": "{{typeof(DummyThreadIdEnricher).AssemblyQualifiedName}}" + } + }] + } + } + """; var log = ConfigFromJson(json) .WriteTo.Sink(new DelegatingSink(e => evt = e)) @@ -1235,18 +1321,20 @@ public void FilterWithIsAppliedWithCustomFilter() { LogEvent evt = null; - var json = $@"{{ - ""Serilog"": {{ - ""Using"": [""TestDummies""], - ""Filter"": [ - {{ - ""Name"": ""With"", - ""Args"": {{ - ""filter"": ""{typeof(DummyAnonymousUserFilter).AssemblyQualifiedName}"" - }} - }}] - }} - }}"; + var json = $$""" + { + "Serilog": { + "Using": ["TestDummies"], + "Filter": [ + { + "Name": "With", + "Args": { + "filter": "{{typeof(DummyAnonymousUserFilter).AssemblyQualifiedName}}" + } + }] + } + } + """; var log = ConfigFromJson(json) .WriteTo.Sink(new DelegatingSink(e => evt = e)) diff --git a/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs b/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs index 1c3eb5ac..f3c0b909 100644 --- a/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs @@ -9,7 +9,8 @@ namespace Serilog.Settings.Configuration.Tests; public class DynamicLevelChangeTests { - const string DefaultConfig = @"{ + const string DefaultConfig = """ + { 'Serilog': { 'Using': [ 'TestDummies' ], 'MinimumLevel': { @@ -33,7 +34,8 @@ public class DynamicLevelChangeTests } } } - }"; + } + """; readonly ReloadableConfigurationSource _configSource; diff --git a/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs b/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs index 0c76cc34..c7ef68cc 100644 --- a/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs @@ -21,13 +21,15 @@ public void ReadFromConfigurationSectionReadsFromAnArbitrarySection() { LogEvent evt = null; - var json = @"{ - ""NotSerilog"": { - ""Properties"": { - ""App"": ""Test"" - } - } - }"; + var json = """ + { + "NotSerilog": { + "Properties": { + "App": "Test" + } + } + } + """; var config = new ConfigurationBuilder() .AddJsonString(json) @@ -50,15 +52,17 @@ public void ReadFromConfigurationSectionReadsFromAnArbitrarySection() [Trait("BugFix", "https://github.com/serilog/serilog-settings-configuration/issues/143")] public void ReadFromConfigurationSectionThrowsWhenTryingToCallConfigurationMethodWithIConfigurationParam() { - var json = @"{ - ""NotSerilog"": { - ""Using"": [""TestDummies""], - ""WriteTo"": [{ - ""Name"": ""DummyWithConfiguration"", - ""Args"": {} + var json = """ + { + "NotSerilog": { + "Using": ["TestDummies"], + "WriteTo": [{ + "Name": "DummyWithConfiguration", + "Args": {} }] } - }"; + } + """; var config = new ConfigurationBuilder() .AddJsonString(json) @@ -81,15 +85,17 @@ public void ReadFromConfigurationSectionThrowsWhenTryingToCallConfigurationMetho [Fact] public void ReadFromConfigurationDoesNotThrowWhenTryingToCallConfigurationMethodWithIConfigurationParam() { - var json = @"{ - ""NotSerilog"": { - ""Using"": [""TestDummies""], - ""WriteTo"": [{ - ""Name"": ""DummyWithConfiguration"", - ""Args"": {} + var json = """ + { + "NotSerilog": { + "Using": ["TestDummies"], + "WriteTo": [{ + "Name": "DummyWithConfiguration", + "Args": {} }] } - }"; + } + """; var config = new ConfigurationBuilder() .AddJsonString(json) @@ -105,15 +111,17 @@ public void ReadFromConfigurationDoesNotThrowWhenTryingToCallConfigurationMethod [Trait("BugFix", "https://github.com/serilog/serilog-settings-configuration/issues/143")] public void ReadFromConfigurationSectionDoesNotThrowWhenTryingToCallConfigurationMethodWithOptionalIConfigurationParam() { - var json = @"{ - ""NotSerilog"": { - ""Using"": [""TestDummies""], - ""WriteTo"": [{ - ""Name"": ""DummyWithOptionalConfiguration"", - ""Args"": {} + var json = """ + { + "NotSerilog": { + "Using": ["TestDummies"], + "WriteTo": [{ + "Name": "DummyWithOptionalConfiguration", + "Args": {} }] } - }"; + } + """; var config = new ConfigurationBuilder() .AddJsonString(json) diff --git a/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs b/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs index 4c783818..3a8c18f3 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs @@ -5,20 +5,22 @@ namespace Serilog.Settings.Configuration.Tests.Support; static class ConfigurationReaderTestHelpers { - public const string minimumLevelFlatTemplate = @" -{{ - 'Serilog': {{ - 'MinimumLevel': '{0}' + public const string minimumLevelFlatTemplate = """ + {{ + 'Serilog': {{ + 'MinimumLevel': '{0}' + }} }} -}}"; - public const string minimumLevelObjectTemplate = @" -{{ - 'Serilog': {{ - 'MinimumLevel': {{ - 'Default': '{0}' + """; + public const string minimumLevelObjectTemplate = """ + {{ + 'Serilog': {{ + 'MinimumLevel': {{ + 'Default': '{0}' + }} }} }} -}}"; + """; public const string minimumLevelFlatKey = "Serilog:MinimumLevel"; public const string minimumLevelObjectKey = "Serilog:MinimumLevel:Default"; From b483da5de199d764f0dc36fa2418bd086dee0715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Wed, 8 Feb 2023 15:41:11 +0100 Subject: [PATCH 20/64] Add a callback on the reader options to expose the log level switches Fixes #206 --- README.md | 16 +++++++ ...figurationLoggerConfigurationExtensions.cs | 12 ++--- .../Configuration/ConfigurationReader.cs | 13 ++++-- .../ConfigurationReaderOptions.cs | 11 +++++ .../Configuration/ResolutionContext.cs | 18 +++++--- .../Configuration/StringArgumentValue.cs | 2 +- .../ConfigurationReaderTests.cs | 12 ++--- .../ConfigurationSettingsTests.cs | 45 ++++++++++++++++--- 8 files changed, 99 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 744aa13a..eb640526 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,22 @@ You can also declare `LoggingLevelSwitch`-es in custom section and reference the Level updates to switches are also respected for a dynamic update. +Since version 4.0.0, both declared switches (i.e. `Serilog:LevelSwitches` section) and minimum level override switches (i.e. `Serilog:MinimumLevel:Override` section) are exposed through a callback on the reader options so that a reference can be kept: + +```csharp +var allSwitches = new Dictionary(); +var options = new ConfigurationReaderOptions +{ + OnLevelSwitchCreated = (switchName, levelSwitch) => allSwitches[switchName] = levelSwitch +}; + +var logger = new LoggerConfiguration() + .ReadFrom.Configuration(configuration, options) + .CreateLogger(); + +LoggingLevelSwitch controlSwitch = allSwitches["$controlSwitch"]; +``` + ### WriteTo, Enrich, AuditTo, Destructure sections These sections support simplified syntax, for example the following is valid if no arguments are needed by the sinks: diff --git a/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs b/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs index 4ddf35db..1e99aa40 100644 --- a/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs +++ b/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs @@ -100,8 +100,8 @@ public static LoggerConfiguration ConfigurationSection( new ConfigurationReader( configSection, assemblyFinder, - configuration: null, - formatProvider: null)); + new ConfigurationReaderOptions { FormatProvider = null }, + configuration: null)); } /// @@ -164,7 +164,7 @@ public static LoggerConfiguration ConfigurationSection( var assemblyFinder = AssemblyFinder.ForSource(configurationAssemblySource); - return settingConfiguration.Settings(new ConfigurationReader(configSection, assemblyFinder, configuration: null, formatProvider: null)); + return settingConfiguration.Settings(new ConfigurationReader(configSection, assemblyFinder, new ConfigurationReaderOptions { FormatProvider = null }, configuration: null)); } /// @@ -229,19 +229,19 @@ static ConfigurationReader GetConfigurationReader(IConfiguration configuration, { var assemblyFinder = dependencyContext == null ? AssemblyFinder.Auto() : AssemblyFinder.ForDependencyContext(dependencyContext); var section = configuration.GetSection(readerOptions.SectionName); - return new ConfigurationReader(section, assemblyFinder, readerOptions.FormatProvider, configuration); + return new ConfigurationReader(section, assemblyFinder, readerOptions, configuration); } static ConfigurationReader GetConfigurationReader(IConfiguration configuration, ConfigurationReaderOptions readerOptions, ConfigurationAssemblySource source) { var assemblyFinder = AssemblyFinder.ForSource(source); var section = configuration.GetSection(readerOptions.SectionName); - return new ConfigurationReader(section, assemblyFinder, readerOptions.FormatProvider, configuration); + return new ConfigurationReader(section, assemblyFinder, readerOptions, configuration); } static ConfigurationReader GetConfigurationReader(IConfiguration configuration, ConfigurationReaderOptions readerOptions, IReadOnlyCollection assemblies) { var section = configuration.GetSection(readerOptions.SectionName); - return new ConfigurationReader(section, assemblies, new ResolutionContext(configuration, readerOptions.FormatProvider)); + return new ConfigurationReader(section, assemblies, new ResolutionContext(configuration, readerOptions)); } } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index cd10c3e5..f0202f27 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -22,11 +22,11 @@ class ConfigurationReader : IConfigurationReader readonly ResolutionContext _resolutionContext; readonly IConfigurationRoot _configurationRoot; - public ConfigurationReader(IConfigurationSection configSection, AssemblyFinder assemblyFinder, IFormatProvider formatProvider, IConfiguration configuration = null) + public ConfigurationReader(IConfigurationSection configSection, AssemblyFinder assemblyFinder, ConfigurationReaderOptions readerOptions, IConfiguration configuration = null) { _section = configSection ?? throw new ArgumentNullException(nameof(configSection)); _configurationAssemblies = LoadConfigurationAssemblies(_section, assemblyFinder); - _resolutionContext = new ResolutionContext(configuration, formatProvider); + _resolutionContext = new ResolutionContext(configuration, readerOptions); _configurationRoot = configuration as IConfigurationRoot; } @@ -136,7 +136,8 @@ void ProcessLevelSwitchDeclarations() SubscribeToLoggingLevelChanges(levelSwitchDeclaration, newSwitch); // make them available later on when resolving argument values - _resolutionContext.AddLevelSwitch(switchName, newSwitch); + var referenceName = _resolutionContext.AddLevelSwitch(switchName, newSwitch); + _resolutionContext.ReaderOptions.OnLevelSwitchCreated?.Invoke(referenceName, newSwitch); } } @@ -164,7 +165,11 @@ void ApplyMinimumLevel(LoggerConfiguration loggerConfiguration) var overridenLevelOrSwitch = overrideDirective.Value; if (Enum.TryParse(overridenLevelOrSwitch, out LogEventLevel _)) { - ApplyMinimumLevelConfiguration(overrideDirective, (configuration, levelSwitch) => configuration.Override(overridePrefix, levelSwitch)); + ApplyMinimumLevelConfiguration(overrideDirective, (configuration, levelSwitch) => + { + configuration.Override(overridePrefix, levelSwitch); + _resolutionContext.ReaderOptions.OnLevelSwitchCreated?.Invoke(overridePrefix, levelSwitch); + }); } else { diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs index a4061ae6..7fa22a50 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs @@ -1,6 +1,7 @@ using System.Globalization; using System.Reflection; using Microsoft.Extensions.DependencyModel; +using Serilog.Core; namespace Serilog.Settings.Configuration; @@ -56,6 +57,16 @@ public ConfigurationReaderOptions() : this(dependencyContext: null) /// public IFormatProvider FormatProvider { get; init; } = CultureInfo.InvariantCulture; + /// + /// Called when a log level switch is created while reading the configuration. + /// Log level switches are created either from the Serilog:LevelSwitches section (declared switches) or the Serilog:MinimumLevel:Override section (minimum level override switches). + /// + /// For declared switches, the switch name includes the leading $ character. + /// For minimum level override switches, the switch name is the (partial) namespace or type name of the override. + /// + /// + public Action OnLevelSwitchCreated { get; init; } + internal Assembly[] Assemblies { get; } internal DependencyContext DependencyContext { get; } internal ConfigurationAssemblySource? ConfigurationAssemblySource { get; } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs index 8959cdaa..d0fdf408 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs @@ -13,15 +13,15 @@ sealed class ResolutionContext readonly IDictionary _declaredFilterSwitches; readonly IConfiguration _appConfiguration; - public ResolutionContext(IConfiguration appConfiguration = null, IFormatProvider formatProvider = null) + public ResolutionContext(IConfiguration appConfiguration = null, ConfigurationReaderOptions readerOptions = null) { _declaredLevelSwitches = new Dictionary(); _declaredFilterSwitches = new Dictionary(); _appConfiguration = appConfiguration; - FormatProvider = formatProvider; + ReaderOptions = readerOptions ?? new ConfigurationReaderOptions(); } - public IFormatProvider FormatProvider { get; } + public ConfigurationReaderOptions ReaderOptions { get; } /// /// Looks up a switch in the declared LoggingLevelSwitches @@ -64,18 +64,22 @@ public IConfiguration AppConfiguration } } - public void AddLevelSwitch(string levelSwitchName, LoggingLevelSwitch levelSwitch) + public string AddLevelSwitch(string levelSwitchName, LoggingLevelSwitch levelSwitch) { if (levelSwitchName == null) throw new ArgumentNullException(nameof(levelSwitchName)); if (levelSwitch == null) throw new ArgumentNullException(nameof(levelSwitch)); - _declaredLevelSwitches[ToSwitchReference(levelSwitchName)] = levelSwitch; + var referenceName = ToSwitchReference(levelSwitchName); + _declaredLevelSwitches[referenceName] = levelSwitch; + return referenceName; } - public void AddFilterSwitch(string filterSwitchName, LoggingFilterSwitchProxy filterSwitch) + public string AddFilterSwitch(string filterSwitchName, LoggingFilterSwitchProxy filterSwitch) { if (filterSwitchName == null) throw new ArgumentNullException(nameof(filterSwitchName)); if (filterSwitch == null) throw new ArgumentNullException(nameof(filterSwitch)); - _declaredFilterSwitches[ToSwitchReference(filterSwitchName)] = filterSwitch; + var referenceName = ToSwitchReference(filterSwitchName); + _declaredFilterSwitches[referenceName] = filterSwitch; + return referenceName; } string ToSwitchReference(string switchName) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs index 89db54d0..ab437eb9 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs @@ -155,7 +155,7 @@ public object ConvertTo(Type toType, ResolutionContext resolutionContext) } } - return Convert.ChangeType(argumentValue, toType, resolutionContext.FormatProvider); + return Convert.ChangeType(argumentValue, toType, resolutionContext.ReaderOptions.FormatProvider); } internal static Type FindType(string typeName) diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs index 2c91abf6..87b70677 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs @@ -1,4 +1,4 @@ -using System.Globalization; +using System.Globalization; using System.Reflection; using Microsoft.Extensions.Configuration; using Serilog.Events; @@ -18,7 +18,7 @@ public ConfigurationReaderTests() _configurationReader = new ConfigurationReader( JsonStringConfigSource.LoadSection("{ 'Serilog': { } }", "Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), - CultureInfo.InvariantCulture); + new ConfigurationReaderOptions()); } [Fact] @@ -197,7 +197,7 @@ public void MethodsAreSelectedBasedOnCountOfMatchedArgumentsAndThenStringType() [MemberData(nameof(FlatMinimumLevel))] public void FlatMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, LogEventLevel expectedMinimumLevel) { - var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), CultureInfo.InvariantCulture, root); + var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), new ConfigurationReaderOptions(), root); var loggerConfig = new LoggerConfiguration(); reader.Configure(loggerConfig); @@ -221,7 +221,7 @@ public void FlatMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, [MemberData(nameof(ObjectMinimumLevel))] public void ObjectMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, LogEventLevel expectedMinimumLevel) { - var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), CultureInfo.InvariantCulture, root); + var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), new ConfigurationReaderOptions(), root); var loggerConfig = new LoggerConfiguration(); reader.Configure(loggerConfig); @@ -263,7 +263,7 @@ public void ObjectMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot roo [MemberData(nameof(MixedMinimumLevel))] public void MixedMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, LogEventLevel expectedMinimumLevel) { - var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), CultureInfo.InvariantCulture, root); + var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), new ConfigurationReaderOptions(), root); var loggerConfig = new LoggerConfiguration(); reader.Configure(loggerConfig); @@ -275,7 +275,7 @@ public void MixedMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root public void NoConfigurationRootUsedStillValid() { var section = JsonStringConfigSource.LoadSection("{ 'Nest': { 'Serilog': { 'MinimumLevel': 'Error' } } }", "Nest"); - var reader = new ConfigurationReader(section.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), CultureInfo.InvariantCulture, section); + var reader = new ConfigurationReader(section.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), new ConfigurationReaderOptions(), section); var loggerConfig = new LoggerConfiguration(); reader.Configure(loggerConfig); diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 9d823484..738d880c 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -11,24 +11,24 @@ namespace Serilog.Settings.Configuration.Tests; public class ConfigurationSettingsTests { - static LoggerConfiguration ConfigFromJson(string jsonString, string secondJsonSource = null) + static LoggerConfiguration ConfigFromJson(string jsonString, string secondJsonSource = null, ConfigurationReaderOptions options = null) { - return ConfigFromJson(jsonString, secondJsonSource, out _); + return ConfigFromJson(jsonString, secondJsonSource, out _, options); } - static LoggerConfiguration ConfigFromJson(string jsonString, out IConfiguration configuration) + static LoggerConfiguration ConfigFromJson(string jsonString, out IConfiguration configuration, ConfigurationReaderOptions options = null) { - return ConfigFromJson(jsonString, null, out configuration); + return ConfigFromJson(jsonString, null, out configuration, options); } - static LoggerConfiguration ConfigFromJson(string jsonString, string secondJsonSource, out IConfiguration configuration) + static LoggerConfiguration ConfigFromJson(string jsonString, string secondJsonSource, out IConfiguration configuration, ConfigurationReaderOptions options) { var builder = new ConfigurationBuilder().AddJsonString(jsonString); if (secondJsonSource != null) builder.AddJsonString(secondJsonSource); configuration = builder.Build(); return new LoggerConfiguration() - .ReadFrom.Configuration(configuration); + .ReadFrom.Configuration(configuration, options); } [Fact] @@ -1345,4 +1345,37 @@ public void FilterWithIsAppliedWithCustomFilter() log.ForContext("User", "the user").Write(Some.InformationEvent()); Assert.NotNull(evt); } + + [Theory] + [InlineData("$switch1")] + [InlineData("switch1")] + public void TestLogLevelSwitchesCallback(string switchName) + { + var json = $@"{{ + 'Serilog': {{ + 'LevelSwitches': {{ '{switchName}': 'Information' }}, + 'MinimumLevel': {{ + 'Override': {{ + 'System': 'Warning', + 'System.Threading': 'Debug' + }} + }} + }} + }}"; + + IDictionary switches = new Dictionary(); + var readerOptions = new ConfigurationReaderOptions { OnLevelSwitchCreated = (name, levelSwitch) => switches[name] = levelSwitch }; + ConfigFromJson(json, options: readerOptions); + + Assert.Equal(3, switches.Count); + + var switch1 = Assert.Contains("$switch1", switches); + Assert.Equal(LogEventLevel.Information, switch1.MinimumLevel); + + var system = Assert.Contains("System", switches); + Assert.Equal(LogEventLevel.Warning, system.MinimumLevel); + + var systemThreading = Assert.Contains("System.Threading", switches); + Assert.Equal(LogEventLevel.Debug, systemThreading.MinimumLevel); + } } From 5a4eb14611416e2e29e9611581aaa402d99cc055 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Sat, 11 Mar 2023 11:49:52 +1100 Subject: [PATCH 21/64] add globaljson --- global.json | 7 +++++++ serilog-settings-configuration.sln | 1 + 2 files changed, 8 insertions(+) create mode 100644 global.json diff --git a/global.json b/global.json new file mode 100644 index 00000000..9acdfa69 --- /dev/null +++ b/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "7.0.102", + "allowPrerelease": false, + "rollForward": "latestFeature" + } +} diff --git a/serilog-settings-configuration.sln b/serilog-settings-configuration.sln index ec9c9b22..a0f41b68 100644 --- a/serilog-settings-configuration.sln +++ b/serilog-settings-configuration.sln @@ -17,6 +17,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{62D0B9 LICENSE = LICENSE README.md = README.md serilog-settings-configuration.sln.DotSettings = serilog-settings-configuration.sln.DotSettings + global.json = global.json EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{D551DCB0-7771-4D01-BEBD-F7B57D1CF0E3}" From 0931bc736eb9f4cd91c6e25899abb34ddde6dbb6 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Sat, 11 Mar 2023 11:53:34 +1100 Subject: [PATCH 22/64] leverage Directory.Build.props --- Directory.Build.props | 8 +++----- .../Serilog.Settings.Configuration.csproj | 3 --- .../Serilog.Settings.Configuration.Tests.csproj | 3 --- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index f6e3c1ba..031340d1 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,11 +2,9 @@ latest True - + true + $(MSBuildThisFileDirectory)assets/Serilog.snk enable + false - - - - diff --git a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj index da033d11..bb1c1928 100644 --- a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj +++ b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj @@ -7,9 +7,6 @@ netstandard2.0;net461 true Serilog.Settings.Configuration - ../../assets/Serilog.snk - true - true serilog;json icon.png Apache-2.0 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 a08c9bbd..e438099d 100644 --- a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj +++ b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj @@ -4,9 +4,6 @@ net6.0;netcoreapp3.1;net462 latest Serilog.Settings.Configuration.Tests - ../../assets/Serilog.snk - true - true From 5e72f969477a7c5fbbcd00a246b3fbd252a8d41d Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Sat, 11 Mar 2023 09:22:04 +0300 Subject: [PATCH 23/64] Switch to raw string literal --- .../ConfigurationSettingsTests.cs | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 738d880c..fbeeaf0c 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -1351,17 +1351,19 @@ public void FilterWithIsAppliedWithCustomFilter() [InlineData("switch1")] public void TestLogLevelSwitchesCallback(string switchName) { - var json = $@"{{ - 'Serilog': {{ - 'LevelSwitches': {{ '{switchName}': 'Information' }}, - 'MinimumLevel': {{ - 'Override': {{ + var json = $$""" + { + 'Serilog': { + 'LevelSwitches': { '{{switchName}}': 'Information' }, + 'MinimumLevel': { + 'Override': { 'System': 'Warning', 'System.Threading': 'Debug' - }} - }} - }} - }}"; + } + } + } + } + """; IDictionary switches = new Dictionary(); var readerOptions = new ConfigurationReaderOptions { OnLevelSwitchCreated = (name, levelSwitch) => switches[name] = levelSwitch }; From 139aa669c911fce58a46ee1d7132297c57378234 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Sun, 12 Mar 2023 21:39:42 +1100 Subject: [PATCH 24/64] build on macos --- appveyor.yml | 20 +++++++++++++++++++- global.json | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 272c7d79..b8049994 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,9 +5,27 @@ skip_tags: true image: - Visual Studio 2022 - Ubuntu + - macOS build_script: -- pwsh: ./Build.ps1 +- pwsh: | + if ($isWindows) { + Invoke-WebRequest "https://dot.net/v1/dotnet-install.ps1" -OutFile "./dotnet-install.ps1" + ./dotnet-install.ps1 -JSonFile global.json -Architecture x64 -InstallDir 'C:\Program Files\dotnet' + ./Build.ps1 + } + if ($isLinux) { + Invoke-WebRequest "https://dot.net/v1/dotnet-install.sh" -OutFile "./dotnet-install.sh" + sudo chmod u+x dotnet-install.sh + sudo ./dotnet-install.sh --jsonfile global.json --architecture x64 --install-dir '/usr/share/dotnet' + ./Build.ps1 + } + if ($isMacOS) { + Invoke-WebRequest "https://dot.net/v1/dotnet-install.sh" -OutFile "./dotnet-install.sh" + sudo chmod u+x dotnet-install.sh + sudo ./dotnet-install.sh --jsonfile global.json --architecture x64 --install-dir '/usr/local/share/dotnet' + ./Build.ps1 + } test: off diff --git a/global.json b/global.json index 9acdfa69..1d927afd 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.102", + "version": "7.0.201", "allowPrerelease": false, "rollForward": "latestFeature" } From e6662a7bbb65fdcdc42f052d9350d9e7a94ee9a8 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Sun, 12 Mar 2023 22:14:26 +1100 Subject: [PATCH 25/64] Update serilog-settings-configuration.sln.DotSettings --- serilog-settings-configuration.sln.DotSettings | 2 ++ 1 file changed, 2 insertions(+) diff --git a/serilog-settings-configuration.sln.DotSettings b/serilog-settings-configuration.sln.DotSettings index 6386ab02..c9460776 100644 --- a/serilog-settings-configuration.sln.DotSettings +++ b/serilog-settings-configuration.sln.DotSettings @@ -538,11 +538,13 @@ II.2.12 <HandlesEvent /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> <Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + True True True True True True + True True True True From 5a2ade3897f43a424299958fd3ceca073268b735 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Sun, 12 Mar 2023 22:41:12 +1100 Subject: [PATCH 26/64] update test refs (#359) --- .../Serilog.Settings.Configuration.Tests.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 e438099d..4b15d986 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 @@  - net6.0;netcoreapp3.1;net462 + net7.0;netcoreapp3.1;net48 latest Serilog.Settings.Configuration.Tests @@ -19,11 +19,11 @@ - - + + - + From 062a760aaf523405dcb0e104609043579103b405 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Sun, 12 Mar 2023 22:51:43 +1100 Subject: [PATCH 27/64] Update Build.ps1 --- Build.ps1 | 45 ++++++++++----------------------------------- 1 file changed, 10 insertions(+), 35 deletions(-) diff --git a/Build.ps1 b/Build.ps1 index f58ea2ec..cfedf6af 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -7,8 +7,6 @@ if(Test-Path .\artifacts) { Remove-Item .\artifacts -Force -Recurse } -& dotnet restore --no-cache - $branch = @{ $true = $env:APPVEYOR_REPO_BRANCH; $false = $(git symbolic-ref --short -q HEAD) }[$env:APPVEYOR_REPO_BRANCH -ne $NULL]; $revision = @{ $true = "{0:00000}" -f [convert]::ToInt32("0" + $env:APPVEYOR_BUILD_NUMBER, 10); $false = "local" }[$env:APPVEYOR_BUILD_NUMBER -ne $NULL]; $suffix = @{ $true = ""; $false = "$($branch.Substring(0, [math]::Min(10,$branch.Length)))-$revision"}[$branch -eq "main" -and $revision -ne "local"] @@ -18,43 +16,20 @@ $buildSuffix = @{ $true = "$($suffix)-$($commitHash)"; $false = "$($branch)-$($c echo "build: Package version suffix is $suffix" echo "build: Build version suffix is $buildSuffix" -foreach ($src in gci src/*) { - Push-Location $src - - echo "build: Packaging project in $src" - - & dotnet build -c Release --version-suffix=$buildSuffix - - if($suffix) { - & dotnet pack -c Release --include-source --no-build -o ../../artifacts --version-suffix=$suffix -p:ContinuousIntegrationBuild=true - } else { - & dotnet pack -c Release --include-source --no-build -o ../../artifacts -p:ContinuousIntegrationBuild=true - } - if($LASTEXITCODE -ne 0) { exit 1 } - - Pop-Location -} - -foreach ($test in gci test/*.Tests) { - Push-Location $test - - echo "build: Testing project in $test" +& dotnet build --configuration Release --version-suffix=$buildSuffix /p:ContinuousIntegrationBuild=true - & dotnet test -c Release - if($LASTEXITCODE -ne 0) { exit 3 } +if($LASTEXITCODE -ne 0) { exit 1 } - Pop-Location +if($suffix) { + & dotnet pack src\Serilog --configuration Release --no-build --no-restore -o artifacts --version-suffix=$suffix +} else { + & dotnet pack src\Serilog --configuration Release --no-build --no-restore -o artifacts } -foreach ($test in gci test/*.PerformanceTests) { - Push-Location $test +if($LASTEXITCODE -ne 0) { exit 2 } - echo "build: Building performance test project in $test" +Write-Output "build: Testing" - & dotnet build -c Release - if($LASTEXITCODE -ne 0) { exit 2 } - - Pop-Location -} +& dotnet test test\Serilog.Tests --configuration Release --no-build --no-restore -Pop-Location \ No newline at end of file +if($LASTEXITCODE -ne 0) { exit 3 } \ No newline at end of file From 85a1680e58f9753d0ee20bad2d0169eb041acefd Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Sun, 12 Mar 2023 23:00:47 +1100 Subject: [PATCH 28/64] Update Build.ps1 --- Build.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Build.ps1 b/Build.ps1 index cfedf6af..7989cd29 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -21,15 +21,15 @@ echo "build: Build version suffix is $buildSuffix" if($LASTEXITCODE -ne 0) { exit 1 } if($suffix) { - & dotnet pack src\Serilog --configuration Release --no-build --no-restore -o artifacts --version-suffix=$suffix + & dotnet pack src\Serilog.Settings.Configuration --configuration Release --no-build --no-restore -o artifacts --version-suffix=$suffix } else { - & dotnet pack src\Serilog --configuration Release --no-build --no-restore -o artifacts + & dotnet pack src\Serilog.Settings.Configuration --configuration Release --no-build --no-restore -o artifacts } if($LASTEXITCODE -ne 0) { exit 2 } Write-Output "build: Testing" -& dotnet test test\Serilog.Tests --configuration Release --no-build --no-restore +& dotnet test test\Serilog.Settings.Configuration.Tests --configuration Release --no-build --no-restore if($LASTEXITCODE -ne 0) { exit 3 } \ No newline at end of file From 6deeec3c12c409b3fc395a3f7703585beba3ca1c Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Sun, 12 Mar 2023 23:07:18 +1100 Subject: [PATCH 29/64] redundant project settings (#360) --- .../Serilog.Settings.Configuration.Tests.csproj | 2 -- test/TestDummies/TestDummies.csproj | 4 ---- 2 files changed, 6 deletions(-) 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 4b15d986..f768c86b 100644 --- a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj +++ b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj @@ -2,8 +2,6 @@ net7.0;netcoreapp3.1;net48 - latest - Serilog.Settings.Configuration.Tests diff --git a/test/TestDummies/TestDummies.csproj b/test/TestDummies/TestDummies.csproj index 93ea6cc8..e410bbb5 100644 --- a/test/TestDummies/TestDummies.csproj +++ b/test/TestDummies/TestDummies.csproj @@ -2,10 +2,6 @@ netstandard2.0;net462 - TestDummies - ../../assets/Serilog.snk - true - true From 3b2d346db3f7f22efa363384c477fcceeb133018 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Sun, 12 Mar 2023 23:17:42 +1100 Subject: [PATCH 30/64] Update Serilog.Settings.Configuration.Tests.csproj --- .../Serilog.Settings.Configuration.Tests.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 f768c86b..5836bfb6 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,8 @@  - net7.0;netcoreapp3.1;net48 + net48 + $(TargetFrameworks);net7.0;netcoreapp3.1 From 2d5e1dd1dbdf4c684ed691e8690b1835fe2be01d Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Mon, 13 Mar 2023 09:32:30 +0300 Subject: [PATCH 31/64] Add API approval test --- .gitignore | 5 +- .../ApiApprovalTests.cs | 25 ++++++++++ ...erilog.Settings.Configuration.Tests.csproj | 3 +- ...erilog.Settings.Configuration.approved.txt | 50 +++++++++++++++++++ 4 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 test/Serilog.Settings.Configuration.Tests/ApiApprovalTests.cs create mode 100644 test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt diff --git a/.gitignore b/.gitignore index 29e6b229..c90bd95b 100644 --- a/.gitignore +++ b/.gitignore @@ -132,7 +132,7 @@ publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings +# TODO: Comment the next line if you want to checkin your web deploy settings # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj @@ -200,7 +200,4 @@ FakesAssemblies/ project.lock.json -#Test files -*.txt - artifacts/ diff --git a/test/Serilog.Settings.Configuration.Tests/ApiApprovalTests.cs b/test/Serilog.Settings.Configuration.Tests/ApiApprovalTests.cs new file mode 100644 index 00000000..08bf7f46 --- /dev/null +++ b/test/Serilog.Settings.Configuration.Tests/ApiApprovalTests.cs @@ -0,0 +1,25 @@ +#if NET7_0 + +using PublicApiGenerator; +using Shouldly; + +namespace Serilog.Settings.Configuration.Tests; + +public class ApiApprovalTests +{ + [Fact] + public void PublicApi_Should_Not_Change_Unintentionally() + { + var assembly = typeof(ConfigurationReaderOptions).Assembly; + var publicApi = assembly.GeneratePublicApi( + new() + { + IncludeAssemblyAttributes = false, + ExcludeAttributes = new[] { "System.Diagnostics.DebuggerDisplayAttribute" }, + }); + + publicApi.ShouldMatchApproved(options => options.WithFilenameGenerator((_, _, fileType, fileExtension) => $"{assembly.GetName().Name!}.{fileType}.{fileExtension}")); + } +} + +#endif 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 5836bfb6..246d9765 100644 --- a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj +++ b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj @@ -19,10 +19,11 @@ - + + diff --git a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt new file mode 100644 index 00000000..912f1716 --- /dev/null +++ b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt @@ -0,0 +1,50 @@ +namespace Serilog +{ + public static class ConfigurationLoggerConfigurationExtensions + { + public const string DefaultSectionName = "Serilog"; + [System.Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptio" + + "ns readerOptions) instead.")] + public static Serilog.LoggerConfiguration Configuration(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfiguration configuration, Microsoft.Extensions.DependencyModel.DependencyContext dependencyContext) { } + [System.Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptio" + + "ns readerOptions) instead.")] + public static Serilog.LoggerConfiguration Configuration(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfiguration configuration, Serilog.Settings.Configuration.ConfigurationAssemblySource configurationAssemblySource) { } + public static Serilog.LoggerConfiguration Configuration(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfiguration configuration, Serilog.Settings.Configuration.ConfigurationReaderOptions readerOptions = null) { } + [System.Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptio" + + "ns readerOptions) instead.")] + public static Serilog.LoggerConfiguration Configuration(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfiguration configuration, params System.Reflection.Assembly[] assemblies) { } + [System.Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptio" + + "ns readerOptions) instead.")] + public static Serilog.LoggerConfiguration Configuration(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfiguration configuration, string sectionName, Microsoft.Extensions.DependencyModel.DependencyContext dependencyContext = null) { } + [System.Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptio" + + "ns readerOptions) instead.")] + public static Serilog.LoggerConfiguration Configuration(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfiguration configuration, string sectionName, Serilog.Settings.Configuration.ConfigurationAssemblySource configurationAssemblySource) { } + [System.Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptio" + + "ns readerOptions) instead.")] + public static Serilog.LoggerConfiguration Configuration(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfiguration configuration, string sectionName, params System.Reflection.Assembly[] assemblies) { } + [System.Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, string sectionName, Depe" + + "ndencyContext dependencyContext) instead.")] + public static Serilog.LoggerConfiguration ConfigurationSection(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfigurationSection configSection, Microsoft.Extensions.DependencyModel.DependencyContext dependencyContext = null) { } + [System.Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, string sectionName, Conf" + + "igurationAssemblySource configurationAssemblySource) instead.")] + public static Serilog.LoggerConfiguration ConfigurationSection(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfigurationSection configSection, Serilog.Settings.Configuration.ConfigurationAssemblySource configurationAssemblySource) { } + } +} +namespace Serilog.Settings.Configuration +{ + public enum ConfigurationAssemblySource + { + UseLoadedAssemblies = 0, + AlwaysScanDllFiles = 1, + } + public sealed class ConfigurationReaderOptions + { + public ConfigurationReaderOptions() { } + public ConfigurationReaderOptions(Microsoft.Extensions.DependencyModel.DependencyContext dependencyContext) { } + public ConfigurationReaderOptions(Serilog.Settings.Configuration.ConfigurationAssemblySource configurationAssemblySource) { } + public ConfigurationReaderOptions(params System.Reflection.Assembly[] assemblies) { } + public System.IFormatProvider FormatProvider { get; init; } + public System.Action OnLevelSwitchCreated { get; init; } + public string SectionName { get; init; } + } +} \ No newline at end of file From 058cb9d2f9a33887ed6884f7e2a96c4101f6294c Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Mon, 13 Mar 2023 10:08:38 +0300 Subject: [PATCH 32/64] Allow configuration from contents of Serilog section --- ...figurationLoggerConfigurationExtensions.cs | 6 ++--- .../Configuration/ConfigurationReader.cs | 10 ++++---- .../ConfigurationSettingsTests.cs | 25 +++++++++++++++++++ 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs b/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs index 1e99aa40..b4ce0910 100644 --- a/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs +++ b/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs @@ -228,20 +228,20 @@ public static LoggerConfiguration Configuration( static ConfigurationReader GetConfigurationReader(IConfiguration configuration, ConfigurationReaderOptions readerOptions, DependencyContext dependencyContext) { var assemblyFinder = dependencyContext == null ? AssemblyFinder.Auto() : AssemblyFinder.ForDependencyContext(dependencyContext); - var section = configuration.GetSection(readerOptions.SectionName); + var section = string.IsNullOrWhiteSpace(readerOptions.SectionName) ? configuration : configuration.GetSection(readerOptions.SectionName); return new ConfigurationReader(section, assemblyFinder, readerOptions, configuration); } static ConfigurationReader GetConfigurationReader(IConfiguration configuration, ConfigurationReaderOptions readerOptions, ConfigurationAssemblySource source) { var assemblyFinder = AssemblyFinder.ForSource(source); - var section = configuration.GetSection(readerOptions.SectionName); + var section = string.IsNullOrWhiteSpace(readerOptions.SectionName) ? configuration : configuration.GetSection(readerOptions.SectionName); return new ConfigurationReader(section, assemblyFinder, readerOptions, configuration); } static ConfigurationReader GetConfigurationReader(IConfiguration configuration, ConfigurationReaderOptions readerOptions, IReadOnlyCollection assemblies) { - var section = configuration.GetSection(readerOptions.SectionName); + var section = string.IsNullOrWhiteSpace(readerOptions.SectionName) ? configuration : configuration.GetSection(readerOptions.SectionName); return new ConfigurationReader(section, assemblies, new ResolutionContext(configuration, readerOptions)); } } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index f0202f27..8d166359 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -17,12 +17,12 @@ class ConfigurationReader : IConfigurationReader { const string LevelSwitchNameRegex = @"^\${0,1}[A-Za-z]+[A-Za-z0-9]*$"; - readonly IConfigurationSection _section; + readonly IConfiguration _section; readonly IReadOnlyCollection _configurationAssemblies; readonly ResolutionContext _resolutionContext; readonly IConfigurationRoot _configurationRoot; - public ConfigurationReader(IConfigurationSection configSection, AssemblyFinder assemblyFinder, ConfigurationReaderOptions readerOptions, IConfiguration configuration = null) + public ConfigurationReader(IConfiguration configSection, AssemblyFinder assemblyFinder, ConfigurationReaderOptions readerOptions, IConfiguration configuration = null) { _section = configSection ?? throw new ArgumentNullException(nameof(configSection)); _configurationAssemblies = LoadConfigurationAssemblies(_section, assemblyFinder); @@ -31,7 +31,7 @@ public ConfigurationReader(IConfigurationSection configSection, AssemblyFinder a } // Used internally for processing nested configuration sections -- see GetMethodCalls below. - internal ConfigurationReader(IConfigurationSection configSection, IReadOnlyCollection configurationAssemblies, ResolutionContext resolutionContext) + internal ConfigurationReader(IConfiguration configSection, IReadOnlyCollection configurationAssemblies, ResolutionContext resolutionContext) { _section = configSection ?? throw new ArgumentNullException(nameof(configSection)); _configurationAssemblies = configurationAssemblies ?? throw new ArgumentNullException(nameof(configurationAssemblies)); @@ -298,7 +298,7 @@ void ApplyEnrichment(LoggerConfiguration loggerConfiguration) } } - internal ILookup> GetMethodCalls(IConfigurationSection directive) + internal ILookup> GetMethodCalls(IConfiguration directive) { var children = directive.GetChildren().ToList(); @@ -356,7 +356,7 @@ internal static IConfigurationArgumentValue GetArgumentValue(IConfigurationSecti return argumentValue; } - static IReadOnlyCollection LoadConfigurationAssemblies(IConfigurationSection section, AssemblyFinder assemblyFinder) + static IReadOnlyCollection LoadConfigurationAssemblies(IConfiguration section, AssemblyFinder assemblyFinder) { var serilogAssembly = typeof(ILogger).Assembly; var assemblies = new Dictionary { [serilogAssembly.FullName] = serilogAssembly }; diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index fbeeaf0c..5b4414c9 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -56,6 +56,31 @@ public void PropertyEnrichmentIsApplied() Assert.Equal("Test", evt.Properties["App"].LiteralValue()); } + [Theory] + [InlineData(null)] + [InlineData("")] + public void CanReadWithoutSerilogSection(string sectionName) + { + LogEvent evt = null; + + var json = """ + { + "Properties": { + "App": "Test" + } + } + """; + + var log = ConfigFromJson(json, options: new ConfigurationReaderOptions { SectionName = sectionName }) + .WriteTo.Sink(new DelegatingSink(e => evt = e)) + .CreateLogger(); + + log.Information("Has a test property"); + + Assert.NotNull(evt); + Assert.Equal("Test", evt.Properties["App"].LiteralValue()); + } + [Theory] [InlineData("extended syntax", """ { From 8e2d9a972faa08b2f9329ae61d925411c8cbcca5 Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Mon, 13 Mar 2023 11:08:05 +0300 Subject: [PATCH 33/64] Allow internal types and methods --- .../Configuration/ConfigurationReader.cs | 42 +++++----- .../ConfigurationReaderOptions.cs | 10 +++ .../ConfigurationSettingsTests.cs | 81 +++++++++++++++++++ ...erilog.Settings.Configuration.approved.txt | 2 + .../DummyLoggerConfigurationExtensions.cs | 30 +++++++ 5 files changed, 144 insertions(+), 21 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index f0202f27..79f00cc7 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -233,7 +233,7 @@ void ApplyFilters(LoggerConfiguration loggerConfiguration) if (filterDirective.GetChildren().Any()) { var methodCalls = GetMethodCalls(filterDirective); - CallConfigurationMethods(methodCalls, FindFilterConfigurationMethods(_configurationAssemblies), loggerConfiguration.Filter); + CallConfigurationMethods(methodCalls, FindFilterConfigurationMethods(_configurationAssemblies, _resolutionContext.ReaderOptions.AllowInternalTypes, _resolutionContext.ReaderOptions.AllowInternalMethods), loggerConfiguration.Filter); } } @@ -243,7 +243,7 @@ void ApplyDestructuring(LoggerConfiguration loggerConfiguration) if (destructureDirective.GetChildren().Any()) { var methodCalls = GetMethodCalls(destructureDirective); - CallConfigurationMethods(methodCalls, FindDestructureConfigurationMethods(_configurationAssemblies), loggerConfiguration.Destructure); + CallConfigurationMethods(methodCalls, FindDestructureConfigurationMethods(_configurationAssemblies, _resolutionContext.ReaderOptions.AllowInternalTypes, _resolutionContext.ReaderOptions.AllowInternalMethods), loggerConfiguration.Destructure); } } @@ -253,7 +253,7 @@ void ApplySinks(LoggerConfiguration loggerConfiguration) if (writeToDirective.GetChildren().Any()) { var methodCalls = GetMethodCalls(writeToDirective); - CallConfigurationMethods(methodCalls, FindSinkConfigurationMethods(_configurationAssemblies), loggerConfiguration.WriteTo); + CallConfigurationMethods(methodCalls, FindSinkConfigurationMethods(_configurationAssemblies, _resolutionContext.ReaderOptions.AllowInternalTypes, _resolutionContext.ReaderOptions.AllowInternalMethods), loggerConfiguration.WriteTo); } } @@ -263,20 +263,20 @@ void ApplyAuditSinks(LoggerConfiguration loggerConfiguration) if (auditToDirective.GetChildren().Any()) { var methodCalls = GetMethodCalls(auditToDirective); - CallConfigurationMethods(methodCalls, FindAuditSinkConfigurationMethods(_configurationAssemblies), loggerConfiguration.AuditTo); + CallConfigurationMethods(methodCalls, FindAuditSinkConfigurationMethods(_configurationAssemblies, _resolutionContext.ReaderOptions.AllowInternalTypes, _resolutionContext.ReaderOptions.AllowInternalMethods), loggerConfiguration.AuditTo); } } void IConfigurationReader.ApplySinks(LoggerSinkConfiguration loggerSinkConfiguration) { var methodCalls = GetMethodCalls(_section); - CallConfigurationMethods(methodCalls, FindSinkConfigurationMethods(_configurationAssemblies), loggerSinkConfiguration); + CallConfigurationMethods(methodCalls, FindSinkConfigurationMethods(_configurationAssemblies, _resolutionContext.ReaderOptions.AllowInternalTypes, _resolutionContext.ReaderOptions.AllowInternalMethods), loggerSinkConfiguration); } void IConfigurationReader.ApplyEnrichment(LoggerEnrichmentConfiguration loggerEnrichmentConfiguration) { var methodCalls = GetMethodCalls(_section); - CallConfigurationMethods(methodCalls, FindEventEnricherConfigurationMethods(_configurationAssemblies), loggerEnrichmentConfiguration); + CallConfigurationMethods(methodCalls, FindEventEnricherConfigurationMethods(_configurationAssemblies, _resolutionContext.ReaderOptions.AllowInternalTypes, _resolutionContext.ReaderOptions.AllowInternalMethods), loggerEnrichmentConfiguration); } void ApplyEnrichment(LoggerConfiguration loggerConfiguration) @@ -285,7 +285,7 @@ void ApplyEnrichment(LoggerConfiguration loggerConfiguration) if (enrichDirective.GetChildren().Any()) { var methodCalls = GetMethodCalls(enrichDirective); - CallConfigurationMethods(methodCalls, FindEventEnricherConfigurationMethods(_configurationAssemblies), loggerConfiguration.Enrich); + CallConfigurationMethods(methodCalls, FindEventEnricherConfigurationMethods(_configurationAssemblies, _resolutionContext.ReaderOptions.AllowInternalTypes, _resolutionContext.ReaderOptions.AllowInternalMethods), loggerConfiguration.Enrich); } var propertiesDirective = _section.GetSection("Properties"); @@ -494,51 +494,51 @@ static bool ParameterNameMatches(string actualParameterName, IEnumerable return suppliedNames.Any(s => ParameterNameMatches(actualParameterName, s)); } - static IReadOnlyCollection FindSinkConfigurationMethods(IReadOnlyCollection configurationAssemblies) + static IReadOnlyCollection FindSinkConfigurationMethods(IReadOnlyCollection configurationAssemblies, bool allowInternalTypes, bool allowInternalMethods) { - var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerSinkConfiguration)); + var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerSinkConfiguration), allowInternalTypes, allowInternalMethods); if (configurationAssemblies.Contains(typeof(LoggerSinkConfiguration).GetTypeInfo().Assembly)) found.AddRange(SurrogateConfigurationMethods.WriteTo); return found; } - static IReadOnlyCollection FindAuditSinkConfigurationMethods(IReadOnlyCollection configurationAssemblies) + static IReadOnlyCollection FindAuditSinkConfigurationMethods(IReadOnlyCollection configurationAssemblies, bool allowInternalTypes, bool allowInternalMethods) { - var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerAuditSinkConfiguration)); + var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerAuditSinkConfiguration), allowInternalTypes, allowInternalMethods); if (configurationAssemblies.Contains(typeof(LoggerAuditSinkConfiguration).GetTypeInfo().Assembly)) found.AddRange(SurrogateConfigurationMethods.AuditTo); return found; } - static IReadOnlyCollection FindFilterConfigurationMethods(IReadOnlyCollection configurationAssemblies) + static IReadOnlyCollection FindFilterConfigurationMethods(IReadOnlyCollection configurationAssemblies, bool allowInternalTypes, bool allowInternalMethods) { - var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerFilterConfiguration)); + var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerFilterConfiguration), allowInternalTypes, allowInternalMethods); if (configurationAssemblies.Contains(typeof(LoggerFilterConfiguration).GetTypeInfo().Assembly)) found.AddRange(SurrogateConfigurationMethods.Filter); return found; } - static IReadOnlyCollection FindDestructureConfigurationMethods(IReadOnlyCollection configurationAssemblies) + static IReadOnlyCollection FindDestructureConfigurationMethods(IReadOnlyCollection configurationAssemblies, bool allowInternalTypes, bool allowInternalMethods) { - var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerDestructuringConfiguration)); + var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerDestructuringConfiguration), allowInternalTypes, allowInternalMethods); if (configurationAssemblies.Contains(typeof(LoggerDestructuringConfiguration).GetTypeInfo().Assembly)) found.AddRange(SurrogateConfigurationMethods.Destructure); return found; } - static IReadOnlyCollection FindEventEnricherConfigurationMethods(IReadOnlyCollection configurationAssemblies) + static IReadOnlyCollection FindEventEnricherConfigurationMethods(IReadOnlyCollection configurationAssemblies, bool allowInternalTypes, bool allowInternalMethods) { - var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerEnrichmentConfiguration)); + var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerEnrichmentConfiguration), allowInternalTypes, allowInternalMethods); if (configurationAssemblies.Contains(typeof(LoggerEnrichmentConfiguration).GetTypeInfo().Assembly)) found.AddRange(SurrogateConfigurationMethods.Enrich); return found; } - static List FindConfigurationExtensionMethods(IReadOnlyCollection configurationAssemblies, Type configType) + static List FindConfigurationExtensionMethods(IReadOnlyCollection configurationAssemblies, Type configType, bool allowInternalTypes, bool allowInternalMethods) { // ExtensionAttribute can be polyfilled to support extension methods static bool HasCustomExtensionAttribute(MethodInfo m) @@ -554,11 +554,11 @@ static bool HasCustomExtensionAttribute(MethodInfo m) } return configurationAssemblies - .SelectMany(a => a.ExportedTypes + .SelectMany(a => (allowInternalTypes ? a.GetTypes() : a.ExportedTypes) .Select(t => t.GetTypeInfo()) - .Where(t => t.IsSealed && t.IsAbstract && !t.IsNested)) + .Where(t => t.IsSealed && t.IsAbstract && !t.IsNested && (t.IsPublic || allowInternalTypes && !t.IsVisible))) .SelectMany(t => t.DeclaredMethods) - .Where(m => m.IsStatic && m.IsPublic && (m.IsDefined(typeof(ExtensionAttribute), false) || HasCustomExtensionAttribute(m))) + .Where(m => m.IsStatic && (m.IsPublic || allowInternalMethods && m.IsAssembly) && (m.IsDefined(typeof(ExtensionAttribute), false) || HasCustomExtensionAttribute(m))) .Where(m => m.GetParameters()[0].ParameterType == configType) .ToList(); } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs index 7fa22a50..bc682084 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs @@ -57,6 +57,16 @@ public ConfigurationReaderOptions() : this(dependencyContext: null) /// public IFormatProvider FormatProvider { get; init; } = CultureInfo.InvariantCulture; + /// + /// Allows to use internal types for extension methods for sink configuration. Defaults to . + /// + public bool AllowInternalTypes { get; set; } + + /// + /// Allows to use internal extension methods for sink configuration. Defaults to . + /// + public bool AllowInternalMethods { get; set; } + /// /// Called when a log level switch is created while reading the configuration. /// Log level switches are created either from the Serilog:LevelSwitches section (declared switches) or the Serilog:MinimumLevel:Override section (minimum level override switches). diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index fbeeaf0c..78af9d07 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -119,6 +119,87 @@ public void ConfigurationAssembliesFromDllScanning() Assert.Single(DummyConsoleSink.Emitted); } + [Fact] + public void ConfigurationAssembliesFromDllScanningWithInternalMethodInPublicClass() + { + var json = """ + { + "Serilog": { + "Using": ["TestDummies"], + "WriteTo": ["DummyConsoleInternal"] + } + } + """; + + var builder = new ConfigurationBuilder().AddJsonString(json); + var config = builder.Build(); + var log = new LoggerConfiguration() + .ReadFrom.Configuration( + configuration: config, + readerOptions: new ConfigurationReaderOptions(ConfigurationAssemblySource.AlwaysScanDllFiles) { AllowInternalMethods = true }) + .CreateLogger(); + + DummyConsoleSink.Emitted.Clear(); + + log.Write(Some.InformationEvent()); + + Assert.Single(DummyConsoleSink.Emitted); + } + + [Fact] + public void ConfigurationAssembliesFromDllScanningWithPublicMethodInInternalClass() + { + var json = """ + { + "Serilog": { + "Using": ["TestDummies"], + "WriteTo": ["DummyConsolePublicInInternal"] + } + } + """; + + var builder = new ConfigurationBuilder().AddJsonString(json); + var config = builder.Build(); + var log = new LoggerConfiguration() + .ReadFrom.Configuration( + configuration: config, + readerOptions: new ConfigurationReaderOptions(ConfigurationAssemblySource.AlwaysScanDllFiles) { AllowInternalTypes = true }) + .CreateLogger(); + + DummyConsoleSink.Emitted.Clear(); + + log.Write(Some.InformationEvent()); + + Assert.Single(DummyConsoleSink.Emitted); + } + + [Fact] + public void ConfigurationAssembliesFromDllScanningWithInternalMethodInInternalClass() + { + var json = """ + { + "Serilog": { + "Using": ["TestDummies"], + "WriteTo": ["DummyConsoleInternalInInternal"] + } + } + """; + + var builder = new ConfigurationBuilder().AddJsonString(json); + var config = builder.Build(); + var log = new LoggerConfiguration() + .ReadFrom.Configuration( + configuration: config, + readerOptions: new ConfigurationReaderOptions(ConfigurationAssemblySource.AlwaysScanDllFiles) { AllowInternalTypes = true, AllowInternalMethods = true }) + .CreateLogger(); + + DummyConsoleSink.Emitted.Clear(); + + log.Write(Some.InformationEvent()); + + Assert.Single(DummyConsoleSink.Emitted); + } + [Fact] public void SinksAreConfigured() { diff --git a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt index 912f1716..a5c46503 100644 --- a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt +++ b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt @@ -43,6 +43,8 @@ namespace Serilog.Settings.Configuration public ConfigurationReaderOptions(Microsoft.Extensions.DependencyModel.DependencyContext dependencyContext) { } public ConfigurationReaderOptions(Serilog.Settings.Configuration.ConfigurationAssemblySource configurationAssemblySource) { } public ConfigurationReaderOptions(params System.Reflection.Assembly[] assemblies) { } + public bool AllowInternalMethods { get; set; } + public bool AllowInternalTypes { get; set; } public System.IFormatProvider FormatProvider { get; init; } public System.Action OnLevelSwitchCreated { get; init; } public string SectionName { get; init; } diff --git a/test/TestDummies/DummyLoggerConfigurationExtensions.cs b/test/TestDummies/DummyLoggerConfigurationExtensions.cs index 7dfccc0c..909e9b5b 100644 --- a/test/TestDummies/DummyLoggerConfigurationExtensions.cs +++ b/test/TestDummies/DummyLoggerConfigurationExtensions.cs @@ -120,6 +120,15 @@ public static LoggerConfiguration DummyConsole( return loggerSinkConfiguration.Sink(new DummyConsoleSink(theme), restrictedToMinimumLevel, levelSwitch); } + internal static LoggerConfiguration DummyConsoleInternal( + this LoggerSinkConfiguration loggerSinkConfiguration, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch levelSwitch = null, + ConsoleTheme theme = null) + { + return loggerSinkConfiguration.Sink(new DummyConsoleSink(theme), restrictedToMinimumLevel, levelSwitch); + } + public static LoggerConfiguration Dummy( this LoggerSinkConfiguration loggerSinkConfiguration, Action wrappedSinkAction) @@ -170,3 +179,24 @@ public static LoggerConfiguration DummyNumbers(this LoggerDestructuringConfigura }); } } + +internal static class DummyLoggerConfigurationExtensionsInternal +{ + public static LoggerConfiguration DummyConsolePublicInInternal( + this LoggerSinkConfiguration loggerSinkConfiguration, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch levelSwitch = null, + ConsoleTheme theme = null) + { + return loggerSinkConfiguration.Sink(new DummyConsoleSink(theme), restrictedToMinimumLevel, levelSwitch); + } + + internal static LoggerConfiguration DummyConsoleInternalInInternal( + this LoggerSinkConfiguration loggerSinkConfiguration, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch levelSwitch = null, + ConsoleTheme theme = null) + { + return loggerSinkConfiguration.Sink(new DummyConsoleSink(theme), restrictedToMinimumLevel, levelSwitch); + } +} From 6aa905abb98ed3cb0b8acebcf82782fc41cfeb19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Sat, 11 Mar 2023 02:08:11 +0100 Subject: [PATCH 34/64] Enable nullable reference types --- Directory.Build.props | 1 + sample/Sample/Program.cs | 6 +-- ...figurationLoggerConfigurationExtensions.cs | 8 +-- .../Assemblies/DllScanningAssemblyFinder.cs | 2 +- .../Configuration/ConfigurationReader.cs | 12 ++--- .../ConfigurationReaderOptions.cs | 12 ++--- .../IConfigurationArgumentValue.cs | 2 +- .../Configuration/LoggingFilterSwitchProxy.cs | 16 +++--- .../Configuration/ObjectArgumentValue.cs | 17 ++++--- .../Configuration/ResolutionContext.cs | 17 ++----- .../Configuration/StringArgumentValue.cs | 9 ++-- .../SurrogateConfigurationMethods.cs | 10 ++-- .../ConfigurationReaderTests.cs | 6 +-- .../ConfigurationSettingsTests.cs | 49 ++++++++++--------- .../DummyLoggerConfigurationExtensions.cs | 8 +-- ...figurationWithMultipleMethodsExtensions.cs | 14 +++--- .../DynamicLevelChangeTests.cs | 2 +- .../LoggerConfigurationExtensionsTests.cs | 4 +- .../ObjectArgumentValueTests.cs | 2 +- ...erilog.Settings.Configuration.approved.txt | 14 +++--- .../StringArgumentValueTests.cs | 10 ++-- .../Support/ConfigurationReaderTestHelpers.cs | 6 +-- .../Support/DelegatingSink.cs | 4 +- .../Support/Some.cs | 2 +- test/TestDummies/Console/DummyConsoleSink.cs | 8 +-- test/TestDummies/DummyConfigurationSink.cs | 12 ++--- .../DummyLoggerConfigurationExtensions.cs | 28 +++++------ test/TestDummies/DummyPolicy.cs | 16 +++--- test/TestDummies/DummyRollingFileAuditSink.cs | 4 +- test/TestDummies/DummyRollingFileSink.cs | 4 +- test/TestDummies/DummyWithLevelSwitchSink.cs | 4 +- test/TestDummies/DummyWrappingSink.cs | 4 +- 32 files changed, 153 insertions(+), 160 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 031340d1..736814b0 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -6,5 +6,6 @@ $(MSBuildThisFileDirectory)assets/Serilog.snk enable false + enable diff --git a/sample/Sample/Program.cs b/sample/Sample/Program.cs index d2888aae..ae2422c1 100644 --- a/sample/Sample/Program.cs +++ b/sample/Sample/Program.cs @@ -73,14 +73,14 @@ public bool IsEnabled(LogEvent logEvent) public class LoginData { - public string Username; + public string? Username; // ReSharper disable once NotAccessedField.Global - public string Password; + public string? Password; } public class CustomPolicy : IDestructuringPolicy { - public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, out LogEventPropertyValue result) + public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, out LogEventPropertyValue? result) { result = null; diff --git a/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs b/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs index b4ce0910..2282b1bc 100644 --- a/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs +++ b/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs @@ -47,7 +47,7 @@ public static LoggerConfiguration Configuration( this LoggerSettingsConfiguration settingConfiguration, IConfiguration configuration, string sectionName, - DependencyContext dependencyContext = null) + DependencyContext? dependencyContext = null) { if (settingConfiguration == null) throw new ArgumentNullException(nameof(settingConfiguration)); if (configuration == null) throw new ArgumentNullException(nameof(configuration)); @@ -87,7 +87,7 @@ public static LoggerConfiguration Configuration( public static LoggerConfiguration ConfigurationSection( this LoggerSettingsConfiguration settingConfiguration, IConfigurationSection configSection, - DependencyContext dependencyContext = null) + DependencyContext? dependencyContext = null) { if (settingConfiguration == null) throw new ArgumentNullException(nameof(settingConfiguration)); if (configSection == null) throw new ArgumentNullException(nameof(configSection)); @@ -214,7 +214,7 @@ public static LoggerConfiguration Configuration( public static LoggerConfiguration Configuration( this LoggerSettingsConfiguration settingConfiguration, IConfiguration configuration, - ConfigurationReaderOptions readerOptions = null) + ConfigurationReaderOptions? readerOptions = null) { var configurationReader = readerOptions switch { @@ -225,7 +225,7 @@ public static LoggerConfiguration Configuration( return settingConfiguration.Settings(configurationReader); } - static ConfigurationReader GetConfigurationReader(IConfiguration configuration, ConfigurationReaderOptions readerOptions, DependencyContext dependencyContext) + static ConfigurationReader GetConfigurationReader(IConfiguration configuration, ConfigurationReaderOptions readerOptions, DependencyContext? dependencyContext) { var assemblyFinder = dependencyContext == null ? AssemblyFinder.Auto() : AssemblyFinder.ForDependencyContext(dependencyContext); var section = string.IsNullOrWhiteSpace(readerOptions.SectionName) ? configuration : configuration.GetSection(readerOptions.SectionName); diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DllScanningAssemblyFinder.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DllScanningAssemblyFinder.cs index 4d7570c9..e571157f 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DllScanningAssemblyFinder.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DllScanningAssemblyFinder.cs @@ -46,7 +46,7 @@ where IsCaseInsensitiveMatch(assemblyFileName, nameToFind) return query.ToList().AsReadOnly(); - static AssemblyName TryGetAssemblyNameFrom(string path) + static AssemblyName? TryGetAssemblyNameFrom(string path) { try { diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 8d166359..b9f5ddcb 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -20,9 +20,9 @@ class ConfigurationReader : IConfigurationReader readonly IConfiguration _section; readonly IReadOnlyCollection _configurationAssemblies; readonly ResolutionContext _resolutionContext; - readonly IConfigurationRoot _configurationRoot; + readonly IConfigurationRoot? _configurationRoot; - public ConfigurationReader(IConfiguration configSection, AssemblyFinder assemblyFinder, ConfigurationReaderOptions readerOptions, IConfiguration configuration = null) + public ConfigurationReader(IConfiguration configSection, AssemblyFinder assemblyFinder, ConfigurationReaderOptions readerOptions, IConfiguration? configuration = null) { _section = configSection ?? throw new ArgumentNullException(nameof(configSection)); _configurationAssemblies = LoadConfigurationAssemblies(_section, assemblyFinder); @@ -145,8 +145,8 @@ void ApplyMinimumLevel(LoggerConfiguration loggerConfiguration) { var minimumLevelDirective = _section.GetSection("MinimumLevel"); - IConfigurationSection defaultMinLevelDirective = GetDefaultMinLevelDirective(); - if (defaultMinLevelDirective.Value != null) + IConfigurationSection? defaultMinLevelDirective = GetDefaultMinLevelDirective(); + if (defaultMinLevelDirective?.Value != null) { ApplyMinimumLevelConfiguration(defaultMinLevelDirective, (configuration, levelSwitch) => configuration.ControlledBy(levelSwitch)); } @@ -189,7 +189,7 @@ void ApplyMinimumLevelConfiguration(IConfigurationSection directive, Action candidateMethods, string name, IReadOnlyCollection suppliedArgumentNames) + internal static MethodInfo? SelectConfigurationMethod(IReadOnlyCollection candidateMethods, string name, IReadOnlyCollection suppliedArgumentNames) { // 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 diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs index 7fa22a50..3956cf08 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs @@ -38,7 +38,7 @@ public ConfigurationReaderOptions() : this(dependencyContext: null) /// The dependency context from which sink/enricher packages can be located. If , the platform default will be used. /// /// Prefer the constructor taking explicit assemblies: . It's the only one supporting single-file publishing. - public ConfigurationReaderOptions(DependencyContext dependencyContext) => DependencyContext = dependencyContext; + public ConfigurationReaderOptions(DependencyContext? dependencyContext) => DependencyContext = dependencyContext; /// /// Initialize a new instance of the class. @@ -50,12 +50,12 @@ public ConfigurationReaderOptions() : this(dependencyContext: null) /// /// The section name for section which contains a Serilog section. Defaults to Serilog. /// - public string SectionName { get; init; } = ConfigurationLoggerConfigurationExtensions.DefaultSectionName; + public string? SectionName { get; init; } = ConfigurationLoggerConfigurationExtensions.DefaultSectionName; /// /// The used when converting strings to other object types. Defaults to the invariant culture. /// - public IFormatProvider FormatProvider { get; init; } = CultureInfo.InvariantCulture; + public IFormatProvider? FormatProvider { get; init; } = CultureInfo.InvariantCulture; /// /// Called when a log level switch is created while reading the configuration. @@ -65,9 +65,9 @@ public ConfigurationReaderOptions() : this(dependencyContext: null) /// For minimum level override switches, the switch name is the (partial) namespace or type name of the override. /// /// - public Action OnLevelSwitchCreated { get; init; } + public Action? OnLevelSwitchCreated { get; init; } - internal Assembly[] Assemblies { get; } - internal DependencyContext DependencyContext { get; } + internal Assembly[]? Assemblies { get; } + internal DependencyContext? DependencyContext { get; } internal ConfigurationAssemblySource? ConfigurationAssemblySource { get; } } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/IConfigurationArgumentValue.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/IConfigurationArgumentValue.cs index d39015cd..8a1e86fd 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/IConfigurationArgumentValue.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/IConfigurationArgumentValue.cs @@ -2,5 +2,5 @@ interface IConfigurationArgumentValue { - object ConvertTo(Type toType, ResolutionContext resolutionContext); + object? ConvertTo(Type toType, ResolutionContext resolutionContext); } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/LoggingFilterSwitchProxy.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/LoggingFilterSwitchProxy.cs index a35d6b1c..a35974ed 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/LoggingFilterSwitchProxy.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/LoggingFilterSwitchProxy.cs @@ -2,8 +2,8 @@ class LoggingFilterSwitchProxy { - readonly Action _setProxy; - readonly Func _getProxy; + readonly Action _setProxy; + readonly Func _getProxy; LoggingFilterSwitchProxy(object realSwitch) { @@ -12,26 +12,26 @@ class LoggingFilterSwitchProxy var type = realSwitch.GetType(); var expressionProperty = type.GetProperty("Expression") ?? throw new MissingMemberException(type.FullName, "Expression"); - _setProxy = (Action)Delegate.CreateDelegate( - typeof(Action), + _setProxy = (Action)Delegate.CreateDelegate( + typeof(Action), realSwitch, expressionProperty.GetSetMethod()); - _getProxy = (Func)Delegate.CreateDelegate( - typeof(Func), + _getProxy = (Func)Delegate.CreateDelegate( + typeof(Func), realSwitch, expressionProperty.GetGetMethod()); } public object RealSwitch { get; } - public string Expression + public string? Expression { get => _getProxy(); set => _setProxy(value); } - public static LoggingFilterSwitchProxy Create(string expression = null) + public static LoggingFilterSwitchProxy? Create(string? expression = null) { var filterSwitchType = Type.GetType("Serilog.Expressions.LoggingFilterSwitch, Serilog.Expressions") ?? diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs index 87d8ace3..6cc07b4d 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Reflection; @@ -20,7 +21,7 @@ public ObjectArgumentValue(IConfigurationSection section, IReadOnlyCollection new { success = true, hasMatch = true, value = (object)argValue }, + true => new { success = true, hasMatch = true, value = (object?)argValue }, false => p.HasDefaultValue switch { - true => new { success = true, hasMatch = false, value = p.DefaultValue }, - false => new { success = false, hasMatch = false, value = (object)null }, + true => new { success = true, hasMatch = false, value = (object?)p.DefaultValue }, + false => new { success = false, hasMatch = false, value = (object?)null }, }, } group new { argumentBindResult, p.ParameterType } by c into gr @@ -178,7 +179,7 @@ where gr.All(z => z.argumentBindResult.success) ctorExpression = Expression.New(ctor.ConstructorInfo, ctorArguments); return true; - static bool TryBindToCtorArgument(object value, Type type, ResolutionContext resolutionContext, out Expression argumentExpression) + static bool TryBindToCtorArgument(object value, Type type, ResolutionContext resolutionContext, [NotNullWhen(true)] out Expression? argumentExpression) { argumentExpression = null; @@ -217,7 +218,7 @@ static bool TryBindToCtorArgument(object value, Type type, ResolutionContext res } } - static bool IsContainer(Type type, out Type elementType) + static bool IsContainer(Type type, [NotNullWhen(true)] out Type? elementType) { elementType = null; foreach (var iface in type.GetInterfaces()) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs index d0fdf408..d0026205 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs @@ -11,9 +11,9 @@ sealed class ResolutionContext { readonly IDictionary _declaredLevelSwitches; readonly IDictionary _declaredFilterSwitches; - readonly IConfiguration _appConfiguration; + readonly IConfiguration? _appConfiguration; - public ResolutionContext(IConfiguration appConfiguration = null, ConfigurationReaderOptions readerOptions = null) + public ResolutionContext(IConfiguration? appConfiguration = null, ConfigurationReaderOptions? readerOptions = null) { _declaredLevelSwitches = new Dictionary(); _declaredFilterSwitches = new Dictionary(); @@ -51,18 +51,7 @@ public LoggingFilterSwitchProxy LookUpFilterSwitchByName(string switchName) public bool HasAppConfiguration => _appConfiguration != null; - public IConfiguration AppConfiguration - { - get - { - if (!HasAppConfiguration) - { - throw new InvalidOperationException("AppConfiguration is not available"); - } - - return _appConfiguration; - } - } + public IConfiguration AppConfiguration => _appConfiguration ?? throw new InvalidOperationException("AppConfiguration is not available"); public string AddLevelSwitch(string levelSwitchName, LoggingLevelSwitch levelSwitch) { diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs index ab437eb9..2c87b275 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs @@ -1,4 +1,5 @@ -using System.Reflection; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; using System.Text.RegularExpressions; using Serilog.Core; @@ -23,7 +24,7 @@ public StringArgumentValue(string providedValue) { typeof(Type), s => Type.GetType(s, throwOnError:true) }, }; - public object ConvertTo(Type toType, ResolutionContext resolutionContext) + public object? ConvertTo(Type toType, ResolutionContext resolutionContext) { var argumentValue = Environment.ExpandEnvironmentVariables(_providedValue); @@ -158,7 +159,7 @@ public object ConvertTo(Type toType, ResolutionContext resolutionContext) return Convert.ChangeType(argumentValue, toType, resolutionContext.ReaderOptions.FormatProvider); } - internal static Type FindType(string typeName) + internal static Type? FindType(string typeName) { var type = Type.GetType(typeName); if (type == null) @@ -172,7 +173,7 @@ internal static Type FindType(string typeName) return type; } - internal static bool TryParseStaticMemberAccessor(string input, out string accessorTypeName, out string memberName) + internal static bool TryParseStaticMemberAccessor(string input, [NotNullWhen(true)] out string? accessorTypeName, [NotNullWhen(true)] out string? memberName) { if (input == null) { diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs index cbc2869c..b758d7c3 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs @@ -45,14 +45,14 @@ static LoggerConfiguration Sink( LoggerSinkConfiguration loggerSinkConfiguration, ILogEventSink sink, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - LoggingLevelSwitch levelSwitch = null) + LoggingLevelSwitch? levelSwitch = null) => loggerSinkConfiguration.Sink(sink, restrictedToMinimumLevel, levelSwitch); static LoggerConfiguration Logger( LoggerSinkConfiguration loggerSinkConfiguration, Action configureLogger, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - LoggingLevelSwitch levelSwitch = null) + LoggingLevelSwitch? levelSwitch = null) => loggerSinkConfiguration.Logger(configureLogger, restrictedToMinimumLevel, levelSwitch); // .AuditTo... @@ -61,14 +61,14 @@ static LoggerConfiguration Sink( LoggerAuditSinkConfiguration auditSinkConfiguration, ILogEventSink sink, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - LoggingLevelSwitch levelSwitch = null) + LoggingLevelSwitch? levelSwitch = null) => auditSinkConfiguration.Sink(sink, restrictedToMinimumLevel, levelSwitch); static LoggerConfiguration Logger( LoggerAuditSinkConfiguration auditSinkConfiguration, Action configureLogger, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - LoggingLevelSwitch levelSwitch = null) + LoggingLevelSwitch? levelSwitch = null) => auditSinkConfiguration.Logger(configureLogger, restrictedToMinimumLevel, levelSwitch); // .Filter... @@ -109,7 +109,7 @@ static LoggerConfiguration AtLevel( LoggerEnrichmentConfiguration loggerEnrichmentConfiguration, Action configureEnricher, LogEventLevel enrichFromLevel = LevelAlias.Minimum, - LoggingLevelSwitch levelSwitch = null) + LoggingLevelSwitch? levelSwitch = null) => levelSwitch != null ? loggerEnrichmentConfiguration.AtLevel(levelSwitch, configureEnricher) : loggerEnrichmentConfiguration.AtLevel(enrichFromLevel, configureEnricher); diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs index 87b70677..0f8ae952 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs @@ -154,7 +154,7 @@ public void CallableMethodsAreSelected() var suppliedArgumentNames = new[] { "pathFormat" }; var selected = ConfigurationReader.SelectConfigurationMethod(options, "DummyRollingFile", suppliedArgumentNames); - Assert.Equal(typeof(string), selected.GetParameters()[1].ParameterType); + Assert.Equal(typeof(string), selected?.GetParameters()[1].ParameterType); } [Fact] @@ -166,7 +166,7 @@ public void MethodsAreSelectedBasedOnCountOfMatchedArguments() var suppliedArgumentNames = new[] { "pathFormat", "formatter" }; var selected = ConfigurationReader.SelectConfigurationMethod(options, "DummyRollingFile", suppliedArgumentNames); - Assert.Equal(typeof(ITextFormatter), selected.GetParameters()[1].ParameterType); + Assert.Equal(typeof(ITextFormatter), selected?.GetParameters()[1].ParameterType); } [Fact] @@ -178,7 +178,7 @@ public void MethodsAreSelectedBasedOnCountOfMatchedArgumentsAndThenStringType() var suppliedArgumentNames = new[] { "pathFormat", "formatter" }; var selected = ConfigurationReader.SelectConfigurationMethod(options, "DummyRollingFile", suppliedArgumentNames); - Assert.Equal(typeof(string), selected.GetParameters()[2].ParameterType); + Assert.Equal(typeof(string), selected?.GetParameters()[2].ParameterType); } public static IEnumerable FlatMinimumLevel => new List diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 5b4414c9..9e3f96fb 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -11,17 +11,17 @@ namespace Serilog.Settings.Configuration.Tests; public class ConfigurationSettingsTests { - static LoggerConfiguration ConfigFromJson(string jsonString, string secondJsonSource = null, ConfigurationReaderOptions options = null) + static LoggerConfiguration ConfigFromJson(string jsonString, string? secondJsonSource = null, ConfigurationReaderOptions? options = null) { return ConfigFromJson(jsonString, secondJsonSource, out _, options); } - static LoggerConfiguration ConfigFromJson(string jsonString, out IConfiguration configuration, ConfigurationReaderOptions options = null) + static LoggerConfiguration ConfigFromJson(string jsonString, out IConfiguration configuration, ConfigurationReaderOptions? options = null) { return ConfigFromJson(jsonString, null, out configuration, options); } - static LoggerConfiguration ConfigFromJson(string jsonString, string secondJsonSource, out IConfiguration configuration, ConfigurationReaderOptions options) + static LoggerConfiguration ConfigFromJson(string jsonString, string? secondJsonSource, out IConfiguration configuration, ConfigurationReaderOptions? options) { var builder = new ConfigurationBuilder().AddJsonString(jsonString); if (secondJsonSource != null) @@ -34,7 +34,7 @@ static LoggerConfiguration ConfigFromJson(string jsonString, string secondJsonSo [Fact] public void PropertyEnrichmentIsApplied() { - LogEvent evt = null; + LogEvent? evt = null; var json = """ { @@ -61,7 +61,7 @@ public void PropertyEnrichmentIsApplied() [InlineData("")] public void CanReadWithoutSerilogSection(string sectionName) { - LogEvent evt = null; + LogEvent? evt = null; var json = """ { @@ -246,7 +246,7 @@ public void TestMinimumLevelOverrides() } """; - LogEvent evt = null; + LogEvent? evt = null; var log = ConfigFromJson(json) .WriteTo.Sink(new DelegatingSink(e => evt = e)) @@ -282,7 +282,7 @@ public void TestMinimumLevelOverridesForChildContext() } """; - LogEvent evt = null; + LogEvent? evt = null; var log = ConfigFromJson(json) .WriteTo.Sink(new DelegatingSink(e => evt = e)) @@ -406,7 +406,7 @@ public void LoggingFilterSwitchIsConfigured(string switchName) } } """; - LogEvent evt = null; + LogEvent? evt = null; var log = ConfigFromJson(json) .WriteTo.Sink(new DelegatingSink(e => evt = e)) @@ -434,7 +434,7 @@ public void LoggingLevelSwitchIsConfigured(string switchName) } } """; - LogEvent evt = null; + LogEvent? evt = null; var log = ConfigFromJson(json) .WriteTo.Sink(new DelegatingSink(e => evt = e)) @@ -489,7 +489,7 @@ public void LoggingLevelSwitchIsPassedToSinks() } """; - LogEvent evt = null; + LogEvent? evt = null; var log = ConfigFromJson(json) .WriteTo.Sink(new DelegatingSink(e => evt = e)) @@ -557,7 +557,7 @@ public void LoggingLevelSwitchCanBeUsedForMinimumLevelOverrides() } """; - LogEvent evt = null; + LogEvent? evt = null; var log = ConfigFromJson(json) .WriteTo.Sink(new DelegatingSink(e => evt = e)) @@ -579,6 +579,7 @@ public void LoggingLevelSwitchCanBeUsedForMinimumLevelOverrides() evt = null; var controlSwitch = DummyWithLevelSwitchSink.ControlLevelSwitch; + Assert.NotNull(controlSwitch); controlSwitch.MinimumLevel = LogEventLevel.Information; systemLogger.Write(Some.InformationEvent()); @@ -802,9 +803,9 @@ public void DestructureWithCollectionsOfTypeArgument() Assert.NotNull(DummyPolicy.Current); Assert.Equal(typeof(TimeSpan), DummyPolicy.Current.Type); Assert.Equal(new[] { typeof(int), typeof(string) }, DummyPolicy.Current.Array); - Assert.Equal(new[] { typeof(byte), typeof(short) }, DummyPolicy.Current.List); - Assert.Equal(typeof(long), DummyPolicy.Current.Custom.First); - Assert.Equal("System.UInt32", DummyPolicy.Current.CustomStrings.First); + Assert.Equal(new[] { typeof(byte), typeof(short) }, DummyPolicy.Current.List!); + Assert.Equal(typeof(long), DummyPolicy.Current.Custom?.First); + Assert.Equal("System.UInt32", DummyPolicy.Current.CustomStrings?.First); } [Fact] @@ -1052,12 +1053,12 @@ public void DestructureLimitsCollectionCount() private static string GetDestructuredProperty(object x, string json) { - LogEvent evt = null; + 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(); + var result = evt!.Properties["X"].ToString(); return result; } @@ -1077,12 +1078,12 @@ public void DestructuringWithCustomExtensionMethodIsApplied() } """; - LogEvent evt = null; + 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(); + var formattedProperty = evt?.Properties["Input"].ToString(); Assert.Equal("\"hardcoded\"", formattedProperty); } @@ -1102,13 +1103,13 @@ public void DestructuringAsScalarIsAppliedWithShortTypeName() } """; - LogEvent evt = null; + 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"]; + var prop = evt?.Properties["Scalarized"]; Assert.IsType(prop); } @@ -1128,13 +1129,13 @@ public void DestructuringAsScalarIsAppliedWithAssemblyQualifiedName() } """; - LogEvent evt = null; + 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"]; + var prop = evt?.Properties["Scalarized"]; Assert.IsType(prop); } @@ -1314,7 +1315,7 @@ public void AuditToSinkIsAppliedWithCustomSinkAndLevelSwitch() [Fact] public void EnrichWithIsAppliedWithCustomEnricher() { - LogEvent evt = null; + LogEvent? evt = null; var json = $$""" { @@ -1344,7 +1345,7 @@ public void EnrichWithIsAppliedWithCustomEnricher() [Fact] public void FilterWithIsAppliedWithCustomFilter() { - LogEvent evt = null; + LogEvent? evt = null; var json = $$""" { diff --git a/test/Serilog.Settings.Configuration.Tests/DummyLoggerConfigurationExtensions.cs b/test/Serilog.Settings.Configuration.Tests/DummyLoggerConfigurationExtensions.cs index 774c8cb6..c8a00d66 100644 --- a/test/Serilog.Settings.Configuration.Tests/DummyLoggerConfigurationExtensions.cs +++ b/test/Serilog.Settings.Configuration.Tests/DummyLoggerConfigurationExtensions.cs @@ -6,17 +6,17 @@ namespace Serilog.Settings.Configuration.Tests; static class DummyLoggerConfigurationExtensions { - public static LoggerConfiguration DummyRollingFile( + public static LoggerConfiguration? DummyRollingFile( LoggerSinkConfiguration loggerSinkConfiguration, string pathFormat, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - string outputTemplate = null, - IFormatProvider formatProvider = null) + string? outputTemplate = null, + IFormatProvider? formatProvider = null) { return null; } - public static LoggerConfiguration DummyRollingFile( + public static LoggerConfiguration? DummyRollingFile( LoggerSinkConfiguration loggerSinkConfiguration, ITextFormatter formatter, string pathFormat, diff --git a/test/Serilog.Settings.Configuration.Tests/DummyLoggerConfigurationWithMultipleMethodsExtensions.cs b/test/Serilog.Settings.Configuration.Tests/DummyLoggerConfigurationWithMultipleMethodsExtensions.cs index 5bc743f1..1ad5c222 100644 --- a/test/Serilog.Settings.Configuration.Tests/DummyLoggerConfigurationWithMultipleMethodsExtensions.cs +++ b/test/Serilog.Settings.Configuration.Tests/DummyLoggerConfigurationWithMultipleMethodsExtensions.cs @@ -8,29 +8,29 @@ namespace Serilog.Settings.Configuration.Tests; static class DummyLoggerConfigurationWithMultipleMethodsExtensions { - public static LoggerConfiguration DummyRollingFile( + public static LoggerConfiguration? DummyRollingFile( LoggerSinkConfiguration loggerSinkConfiguration, ITextFormatter formatter, IEnumerable pathFormat, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - string outputTemplate = null, - IFormatProvider formatProvider = null) + string? outputTemplate = null, + IFormatProvider? formatProvider = null) { return null; } - public static LoggerConfiguration DummyRollingFile( + public static LoggerConfiguration? DummyRollingFile( LoggerSinkConfiguration loggerSinkConfiguration, ITextFormatter formatter, string pathFormat, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - string outputTemplate = null, - IFormatProvider formatProvider = null) + string? outputTemplate = null, + IFormatProvider? formatProvider = null) { return null; } - public static LoggerConfiguration DummyRollingFile( + public static LoggerConfiguration? DummyRollingFile( LoggerSinkConfiguration loggerSinkConfiguration, string pathFormat, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) diff --git a/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs b/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs index f3c0b909..cbe9ab7e 100644 --- a/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs @@ -85,7 +85,7 @@ public void ShouldRespectDynamicLevelChanges() Assert.Empty(DummyConsoleSink.Emitted); } - void UpdateConfig(LogEventLevel? minimumLevel = null, LogEventLevel? switchLevel = null, LogEventLevel? overrideLevel = null, string filterExpression = null) + void UpdateConfig(LogEventLevel? minimumLevel = null, LogEventLevel? switchLevel = null, LogEventLevel? overrideLevel = null, string? filterExpression = null) { if (minimumLevel.HasValue) { diff --git a/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs b/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs index c7ef68cc..e73ae14e 100644 --- a/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs @@ -19,7 +19,7 @@ public void ReadFromConfigurationShouldNotThrowOnEmptyConfiguration() [Trait("BugFix", "https://github.com/serilog/serilog-settings-configuration/issues/143")] public void ReadFromConfigurationSectionReadsFromAnArbitrarySection() { - LogEvent evt = null; + LogEvent? evt = null; var json = """ { @@ -45,7 +45,7 @@ public void ReadFromConfigurationSectionReadsFromAnArbitrarySection() log.Information("Has a test property"); Assert.NotNull(evt); - Assert.Equal("Test", evt.Properties["App"].LiteralValue()); + Assert.Equal("Test", evt?.Properties["App"].LiteralValue()); } [Fact] diff --git a/test/Serilog.Settings.Configuration.Tests/ObjectArgumentValueTests.cs b/test/Serilog.Settings.Configuration.Tests/ObjectArgumentValueTests.cs index ee775e4e..cfba77e7 100644 --- a/test/Serilog.Settings.Configuration.Tests/ObjectArgumentValueTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ObjectArgumentValueTests.cs @@ -29,7 +29,7 @@ public void ShouldBindToConstructorArguments(string caseSection, Type targetType var testSection = _config.GetSection(caseSection); Assert.True(ObjectArgumentValue.TryBuildCtorExpression(testSection, targetType, new(), out var ctorExpression)); - Assert.Equal(expectedExpression, ctorExpression.ToString()); + Assert.Equal(expectedExpression, ctorExpression?.ToString()); } class A diff --git a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt index 912f1716..63d982c2 100644 --- a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt +++ b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt @@ -9,13 +9,13 @@ namespace Serilog [System.Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptio" + "ns readerOptions) instead.")] public static Serilog.LoggerConfiguration Configuration(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfiguration configuration, Serilog.Settings.Configuration.ConfigurationAssemblySource configurationAssemblySource) { } - public static Serilog.LoggerConfiguration Configuration(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfiguration configuration, Serilog.Settings.Configuration.ConfigurationReaderOptions readerOptions = null) { } + public static Serilog.LoggerConfiguration Configuration(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfiguration configuration, Serilog.Settings.Configuration.ConfigurationReaderOptions? readerOptions = null) { } [System.Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptio" + "ns readerOptions) instead.")] public static Serilog.LoggerConfiguration Configuration(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfiguration configuration, params System.Reflection.Assembly[] assemblies) { } [System.Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptio" + "ns readerOptions) instead.")] - public static Serilog.LoggerConfiguration Configuration(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfiguration configuration, string sectionName, Microsoft.Extensions.DependencyModel.DependencyContext dependencyContext = null) { } + public static Serilog.LoggerConfiguration Configuration(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfiguration configuration, string sectionName, Microsoft.Extensions.DependencyModel.DependencyContext? dependencyContext = null) { } [System.Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptio" + "ns readerOptions) instead.")] public static Serilog.LoggerConfiguration Configuration(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfiguration configuration, string sectionName, Serilog.Settings.Configuration.ConfigurationAssemblySource configurationAssemblySource) { } @@ -24,7 +24,7 @@ namespace Serilog public static Serilog.LoggerConfiguration Configuration(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfiguration configuration, string sectionName, params System.Reflection.Assembly[] assemblies) { } [System.Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, string sectionName, Depe" + "ndencyContext dependencyContext) instead.")] - public static Serilog.LoggerConfiguration ConfigurationSection(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfigurationSection configSection, Microsoft.Extensions.DependencyModel.DependencyContext dependencyContext = null) { } + public static Serilog.LoggerConfiguration ConfigurationSection(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfigurationSection configSection, Microsoft.Extensions.DependencyModel.DependencyContext? dependencyContext = null) { } [System.Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, string sectionName, Conf" + "igurationAssemblySource configurationAssemblySource) instead.")] public static Serilog.LoggerConfiguration ConfigurationSection(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfigurationSection configSection, Serilog.Settings.Configuration.ConfigurationAssemblySource configurationAssemblySource) { } @@ -40,11 +40,11 @@ namespace Serilog.Settings.Configuration public sealed class ConfigurationReaderOptions { public ConfigurationReaderOptions() { } - public ConfigurationReaderOptions(Microsoft.Extensions.DependencyModel.DependencyContext dependencyContext) { } + public ConfigurationReaderOptions(Microsoft.Extensions.DependencyModel.DependencyContext? dependencyContext) { } public ConfigurationReaderOptions(Serilog.Settings.Configuration.ConfigurationAssemblySource configurationAssemblySource) { } public ConfigurationReaderOptions(params System.Reflection.Assembly[] assemblies) { } - public System.IFormatProvider FormatProvider { get; init; } - public System.Action OnLevelSwitchCreated { get; init; } - public string SectionName { get; init; } + public System.IFormatProvider? FormatProvider { get; init; } + public System.Action? OnLevelSwitchCreated { get; init; } + public string? SectionName { get; init; } } } \ No newline at end of file diff --git a/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs b/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs index 08599f1b..535449f0 100644 --- a/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs @@ -110,8 +110,8 @@ public void StaticMembersAccessorsCanBeUsedForDelegateTypes(string input, Type t var actual = stringArgumentValue.ConvertTo(targetType, new ResolutionContext()); Assert.IsAssignableFrom(targetType, actual); - var parser = (Delegate)actual; - Assert.Equal(100, parser.DynamicInvoke("100")); + var parser = (Delegate?)actual; + Assert.Equal(100, parser?.DynamicInvoke("100")); } [Theory] @@ -216,7 +216,7 @@ public void StringValuesConvertToTypeFromShortTypeName() var shortTypeName = "System.Version"; var stringArgumentValue = new StringArgumentValue(shortTypeName); - var actual = (Type)stringArgumentValue.ConvertTo(typeof(Type), new ResolutionContext()); + var actual = (Type?)stringArgumentValue.ConvertTo(typeof(Type), new ResolutionContext()); Assert.Equal(typeof(Version), actual); } @@ -224,10 +224,10 @@ public void StringValuesConvertToTypeFromShortTypeName() [Fact] public void StringValuesConvertToTypeFromAssemblyQualifiedName() { - var assemblyQualifiedName = typeof(Version).AssemblyQualifiedName; + var assemblyQualifiedName = typeof(Version).AssemblyQualifiedName!; var stringArgumentValue = new StringArgumentValue(assemblyQualifiedName); - var actual = (Type)stringArgumentValue.ConvertTo(typeof(Type), new ResolutionContext()); + var actual = (Type?)stringArgumentValue.ConvertTo(typeof(Type), new ResolutionContext()); Assert.Equal(typeof(Version), actual); } diff --git a/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs b/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs index 3a8c18f3..33192271 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs @@ -47,9 +47,9 @@ public static void AssertLogEventLevels(LoggerConfiguration loggerConfig, LogEve // the naming is only to show priority as providers public static IConfigurationRoot GetConfigRoot( - string appsettingsJsonLevel = null, - string appsettingsDevelopmentJsonLevel = null, - Dictionary envVariables = null) + string? appsettingsJsonLevel = null, + string? appsettingsDevelopmentJsonLevel = null, + Dictionary? envVariables = null) { var configBuilder = new ConfigurationBuilder(); diff --git a/test/Serilog.Settings.Configuration.Tests/Support/DelegatingSink.cs b/test/Serilog.Settings.Configuration.Tests/Support/DelegatingSink.cs index 60096c05..0c1bf815 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/DelegatingSink.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/DelegatingSink.cs @@ -17,9 +17,9 @@ public void Emit(LogEvent logEvent) _write(logEvent); } - public static LogEvent GetLogEvent(Action writeAction) + public static LogEvent? GetLogEvent(Action writeAction) { - LogEvent result = null; + LogEvent? result = null; var l = new LoggerConfiguration() .MinimumLevel.Verbose() .WriteTo.Sink(new DelegatingSink(le => result = le)) diff --git a/test/Serilog.Settings.Configuration.Tests/Support/Some.cs b/test/Serilog.Settings.Configuration.Tests/Support/Some.cs index 226d7490..ba4f6af4 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/Some.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/Some.cs @@ -18,7 +18,7 @@ public static decimal Decimal() return Int() + 0.123m; } - public static string String(string tag = null) + public static string String(string? tag = null) { return (tag ?? "") + "__" + Int(); } diff --git a/test/TestDummies/Console/DummyConsoleSink.cs b/test/TestDummies/Console/DummyConsoleSink.cs index 67134e57..0486eaef 100644 --- a/test/TestDummies/Console/DummyConsoleSink.cs +++ b/test/TestDummies/Console/DummyConsoleSink.cs @@ -6,18 +6,18 @@ namespace TestDummies.Console; public class DummyConsoleSink : ILogEventSink { - public DummyConsoleSink(ConsoleTheme theme = null) + public DummyConsoleSink(ConsoleTheme? theme = null) { Theme = theme ?? ConsoleTheme.None; } [ThreadStatic] - public static ConsoleTheme Theme; + public static ConsoleTheme? Theme; [ThreadStatic] - static List EmittedList; + static List? EmittedList; - public static List Emitted => EmittedList ?? (EmittedList = new List()); + public static List Emitted => EmittedList ??= new List(); public void Emit(LogEvent logEvent) { diff --git a/test/TestDummies/DummyConfigurationSink.cs b/test/TestDummies/DummyConfigurationSink.cs index c522f1b8..51620add 100644 --- a/test/TestDummies/DummyConfigurationSink.cs +++ b/test/TestDummies/DummyConfigurationSink.cs @@ -7,22 +7,22 @@ namespace TestDummies; public class DummyConfigurationSink : ILogEventSink { [ThreadStatic] - static List _emitted; + static List? _emitted; [ThreadStatic] - static IConfiguration _configuration; + static IConfiguration? _configuration; [ThreadStatic] - static IConfigurationSection _configSection; + static IConfigurationSection? _configSection; public static List Emitted => _emitted ?? (_emitted = new List()); - public static IConfiguration Configuration => _configuration; + public static IConfiguration? Configuration => _configuration; - public static IConfigurationSection ConfigSection => _configSection; + public static IConfigurationSection? ConfigSection => _configSection; - public DummyConfigurationSink(IConfiguration configuration, IConfigurationSection configSection) + public DummyConfigurationSink(IConfiguration? configuration, IConfigurationSection? configSection) { _configuration = configuration; _configSection = configSection; diff --git a/test/TestDummies/DummyLoggerConfigurationExtensions.cs b/test/TestDummies/DummyLoggerConfigurationExtensions.cs index 7dfccc0c..886e3177 100644 --- a/test/TestDummies/DummyLoggerConfigurationExtensions.cs +++ b/test/TestDummies/DummyLoggerConfigurationExtensions.cs @@ -20,8 +20,8 @@ public static LoggerConfiguration DummyRollingFile( this LoggerSinkConfiguration loggerSinkConfiguration, string pathFormat, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - string outputTemplate = null, - IFormatProvider formatProvider = null) + string? outputTemplate = null, + IFormatProvider? formatProvider = null) { return loggerSinkConfiguration.Sink(new DummyRollingFileSink(), restrictedToMinimumLevel); } @@ -45,7 +45,7 @@ public static LoggerConfiguration DummyWithConfiguration( public static LoggerConfiguration DummyWithOptionalConfiguration( this LoggerSinkConfiguration loggerSinkConfiguration, - IConfiguration appConfiguration = null, + IConfiguration? appConfiguration = null, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) { return loggerSinkConfiguration.Sink(new DummyConfigurationSink(appConfiguration, null), restrictedToMinimumLevel); @@ -70,9 +70,9 @@ public static LoggerConfiguration DummyRollingFile( public class Binding { - public string Foo { get; set; } + public string? Foo { get; set; } - public string Abc { get; set; } + public string? Abc { get; set; } } public static LoggerConfiguration DummyRollingFile( @@ -97,8 +97,8 @@ public static LoggerConfiguration DummyRollingFile( this LoggerAuditSinkConfiguration loggerSinkConfiguration, string pathFormat, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - string outputTemplate = null, - IFormatProvider formatProvider = null) + string? outputTemplate = null, + IFormatProvider? formatProvider = null) { return loggerSinkConfiguration.Sink(new DummyRollingFileAuditSink(), restrictedToMinimumLevel); } @@ -106,7 +106,7 @@ public static LoggerConfiguration DummyRollingFile( public static LoggerConfiguration DummyWithLevelSwitch( this LoggerSinkConfiguration loggerSinkConfiguration, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - LoggingLevelSwitch controlLevelSwitch = null) + LoggingLevelSwitch? controlLevelSwitch = null) { return loggerSinkConfiguration.Sink(new DummyWithLevelSwitchSink(controlLevelSwitch), restrictedToMinimumLevel); } @@ -114,8 +114,8 @@ public static LoggerConfiguration DummyWithLevelSwitch( public static LoggerConfiguration DummyConsole( this LoggerSinkConfiguration loggerSinkConfiguration, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - LoggingLevelSwitch levelSwitch = null, - ConsoleTheme theme = null) + LoggingLevelSwitch? levelSwitch = null, + ConsoleTheme? theme = null) { return loggerSinkConfiguration.Sink(new DummyConsoleSink(theme), restrictedToMinimumLevel, levelSwitch); } @@ -142,10 +142,10 @@ string hardCodedString public static LoggerConfiguration DummyArrayOfType(this LoggerDestructuringConfiguration loggerSinkConfiguration, List list, - Type[] array = null, - Type type = null, - CustomCollection custom = null, - CustomCollection customString = null) + Type[]? array = null, + Type? type = null, + CustomCollection? custom = null, + CustomCollection? customString = null) { return loggerSinkConfiguration.With(DummyPolicy.Current = new DummyPolicy { diff --git a/test/TestDummies/DummyPolicy.cs b/test/TestDummies/DummyPolicy.cs index f289818f..2104abfc 100644 --- a/test/TestDummies/DummyPolicy.cs +++ b/test/TestDummies/DummyPolicy.cs @@ -6,17 +6,17 @@ namespace TestDummies; public class DummyPolicy : IDestructuringPolicy { - public static DummyPolicy Current { get; set; } + public static DummyPolicy? Current { get; set; } - public Type[] Array { get; set; } + public Type[]? Array { get; set; } - public List List { get; set; } + public List? List { get; set; } - public CustomCollection Custom { get; set; } + public CustomCollection? Custom { get; set; } - public CustomCollection CustomStrings { get; set; } + public CustomCollection? CustomStrings { get; set; } - public Type Type { get; set; } + public Type? Type { get; set; } public float Float { get; set; } @@ -24,7 +24,7 @@ public class DummyPolicy : IDestructuringPolicy public decimal Decimal { get; set; } - public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, out LogEventPropertyValue result) + public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, out LogEventPropertyValue? result) { result = null; return false; @@ -43,7 +43,7 @@ public class CustomCollection : IEnumerable // wrong signature for collection initializer public void Add(string a, byte b) { } - public T First => inner.Count > 0 ? inner[0] : default; + public T? First => inner.Count > 0 ? inner[0] : default; public IEnumerator GetEnumerator() => inner.GetEnumerator(); diff --git a/test/TestDummies/DummyRollingFileAuditSink.cs b/test/TestDummies/DummyRollingFileAuditSink.cs index 28f794c7..3365ba55 100644 --- a/test/TestDummies/DummyRollingFileAuditSink.cs +++ b/test/TestDummies/DummyRollingFileAuditSink.cs @@ -6,9 +6,9 @@ namespace TestDummies; public class DummyRollingFileAuditSink : ILogEventSink { [ThreadStatic] - static List _emitted; + static List? _emitted; - public static List Emitted => _emitted ?? (_emitted = new List()); + public static List Emitted => _emitted ??= new List(); public void Emit(LogEvent logEvent) { diff --git a/test/TestDummies/DummyRollingFileSink.cs b/test/TestDummies/DummyRollingFileSink.cs index f7304cd1..3f8fd0e7 100644 --- a/test/TestDummies/DummyRollingFileSink.cs +++ b/test/TestDummies/DummyRollingFileSink.cs @@ -6,9 +6,9 @@ namespace TestDummies; public class DummyRollingFileSink : ILogEventSink { [ThreadStatic] - static List _emitted; + static List? _emitted; - public static List Emitted => _emitted ?? (_emitted = new List()); + public static List Emitted => _emitted ??= new List(); public void Emit(LogEvent logEvent) { diff --git a/test/TestDummies/DummyWithLevelSwitchSink.cs b/test/TestDummies/DummyWithLevelSwitchSink.cs index 90856d3a..2a3fadb4 100644 --- a/test/TestDummies/DummyWithLevelSwitchSink.cs +++ b/test/TestDummies/DummyWithLevelSwitchSink.cs @@ -5,13 +5,13 @@ namespace TestDummies; public class DummyWithLevelSwitchSink : ILogEventSink { - public DummyWithLevelSwitchSink(LoggingLevelSwitch loggingControlLevelSwitch) + public DummyWithLevelSwitchSink(LoggingLevelSwitch? loggingControlLevelSwitch) { ControlLevelSwitch = loggingControlLevelSwitch; } [ThreadStatic] - public static LoggingLevelSwitch ControlLevelSwitch; + public static LoggingLevelSwitch? ControlLevelSwitch; [ThreadStatic] // ReSharper disable ThreadStaticFieldHasInitializer diff --git a/test/TestDummies/DummyWrappingSink.cs b/test/TestDummies/DummyWrappingSink.cs index b4bc3ddb..9dc0d35c 100644 --- a/test/TestDummies/DummyWrappingSink.cs +++ b/test/TestDummies/DummyWrappingSink.cs @@ -6,9 +6,9 @@ namespace TestDummies; public class DummyWrappingSink : ILogEventSink { [ThreadStatic] - static List _emitted; + static List? _emitted; - public static List Emitted => _emitted ?? (_emitted = new List()); + public static List Emitted => _emitted ??= new List(); readonly ILogEventSink _sink; From 68719477ed37c4e600d6aa9cac9f7f57936ae167 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Wed, 15 Mar 2023 00:38:25 +0100 Subject: [PATCH 35/64] Fix some nullable reference type issues Not sure why they did not show up as warnings then as errors (since TreatWarningsAsErrors is true) --- .../Settings/Configuration/Assemblies/AssemblyFinder.cs | 2 +- .../Settings/Configuration/ConfigurationReader.cs | 2 +- .../Settings/Configuration/StringArgumentValue.cs | 2 +- .../ObjectArgumentValueTests.cs | 2 +- .../StringArgumentValueTests.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AssemblyFinder.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AssemblyFinder.cs index c2c34812..7fcbe383 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AssemblyFinder.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AssemblyFinder.cs @@ -7,7 +7,7 @@ abstract class AssemblyFinder { public abstract IReadOnlyList FindAssembliesContainingName(string nameToFind); - protected static bool IsCaseInsensitiveMatch(string text, string textToFind) + protected static bool IsCaseInsensitiveMatch(string? text, string textToFind) { return text != null && text.ToLowerInvariant().Contains(textToFind.ToLowerInvariant()); } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index b9f5ddcb..d5a3a929 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -413,7 +413,7 @@ static bool HasImplicitValueWhenNotSpecified(ParameterInfo paramInfo) || paramInfo.ParameterType == typeof(IConfiguration); } - object GetImplicitValueForNotSpecifiedKey(ParameterInfo parameter, MethodInfo methodToInvoke) + object? GetImplicitValueForNotSpecifiedKey(ParameterInfo parameter, MethodInfo methodToInvoke) { if (!HasImplicitValueWhenNotSpecified(parameter)) { diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs index 2c87b275..c26139bd 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs @@ -173,7 +173,7 @@ public StringArgumentValue(string providedValue) return type; } - internal static bool TryParseStaticMemberAccessor(string input, [NotNullWhen(true)] out string? accessorTypeName, [NotNullWhen(true)] out string? memberName) + internal static bool TryParseStaticMemberAccessor(string? input, [NotNullWhen(true)] out string? accessorTypeName, [NotNullWhen(true)] out string? memberName) { if (input == null) { diff --git a/test/Serilog.Settings.Configuration.Tests/ObjectArgumentValueTests.cs b/test/Serilog.Settings.Configuration.Tests/ObjectArgumentValueTests.cs index cfba77e7..ee775e4e 100644 --- a/test/Serilog.Settings.Configuration.Tests/ObjectArgumentValueTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ObjectArgumentValueTests.cs @@ -29,7 +29,7 @@ public void ShouldBindToConstructorArguments(string caseSection, Type targetType var testSection = _config.GetSection(caseSection); Assert.True(ObjectArgumentValue.TryBuildCtorExpression(testSection, targetType, new(), out var ctorExpression)); - Assert.Equal(expectedExpression, ctorExpression?.ToString()); + Assert.Equal(expectedExpression, ctorExpression.ToString()); } class A diff --git a/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs b/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs index 535449f0..c28b1bd2 100644 --- a/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs @@ -53,7 +53,7 @@ public void StringValuesConvertToDefaultInstancesIfTargetIsAbstractClass() // a full-qualified type name should not be considered a static member accessor [InlineData("My.NameSpace.Class, MyAssembly, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", null, null)] - public void TryParseStaticMemberAccessorReturnsExpectedResults(string input, string expectedAccessorType, string expectedPropertyName) + public void TryParseStaticMemberAccessorReturnsExpectedResults(string input, string? expectedAccessorType, string expectedPropertyName) { var actual = StringArgumentValue.TryParseStaticMemberAccessor(input, out var actualAccessorType, From 70d8b95e86554e8177eadc0fe5b358fa4b08a73e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Thu, 16 Mar 2023 17:59:11 +0100 Subject: [PATCH 36/64] Make all JSON strings valid in tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So that we can get rid of the `ToValidJson` extension method. Now that raw string literals have been introduced (in #354) having valid _readable_ JSON in strings for tests becomes easy. There is no need to escape quotes anymore with either `\"` or `""`. This will also make it easier for newcomers to understand the tests at first glance. I had my WTF moment when I first saw JSON with single quotes. 🤔 --- .../ConfigurationReaderTests.cs | 57 +++++++++++-------- .../ConfigurationSettingsTests.cs | 32 +++++------ .../DynamicLevelChangeTests.cs | 32 +++++------ .../Support/ConfigurationReaderTestHelpers.cs | 10 ++-- .../Support/Extensions.cs | 6 -- .../Support/JsonStringConfigSource.cs | 2 +- 6 files changed, 71 insertions(+), 68 deletions(-) diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs index 0f8ae952..2b0775c1 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs @@ -1,4 +1,3 @@ -using System.Globalization; using System.Reflection; using Microsoft.Extensions.Configuration; using Serilog.Events; @@ -16,7 +15,7 @@ public class ConfigurationReaderTests public ConfigurationReaderTests() { _configurationReader = new ConfigurationReader( - JsonStringConfigSource.LoadSection("{ 'Serilog': { } }", "Serilog"), + JsonStringConfigSource.LoadSection("{ \"Serilog\": { } }", "Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), new ConfigurationReaderOptions()); } @@ -26,7 +25,7 @@ public void WriteToSupportSimplifiedSyntax() { var json = """ { - 'WriteTo': [ 'LiterateConsole', 'DiagnosticTrace' ] + "WriteTo": [ "LiterateConsole", "DiagnosticTrace" ] } """; @@ -44,8 +43,8 @@ public void WriteToSupportExpandedSyntaxWithoutArgs() { var json = """ { - 'WriteTo': [ { - 'Name': 'LiterateConsole' + "WriteTo": [ { + "Name": "LiterateConsole" }] } """; @@ -62,10 +61,10 @@ public void WriteToSupportExpandedSyntaxWithArgs() { var json = """ { - 'WriteTo': [ { - 'Name': 'LiterateConsole', - 'Args': { - 'outputTemplate': '{Message}' + "WriteTo": [ { + "Name": "LiterateConsole", + "Args": { + "outputTemplate": "{Message}" }, }] } @@ -90,25 +89,25 @@ public void WriteToSupportMultipleSinksOfTheSameKind() { var json = """ { - 'WriteTo': [ + "WriteTo": [ { - 'Name': 'LiterateConsole', - 'Args': { - 'outputTemplate': '{Message}' + "Name": "LiterateConsole", + "Args": { + "outputTemplate": "{Message}" }, }, - 'DiagnosticTrace' + "DiagnosticTrace" ], - 'WriteTo:File1': { - 'Name': 'File', - 'Args': { - 'outputTemplate': '{Message}' + "WriteTo:File1": { + "Name": "File", + "Args": { + "outputTemplate": "{Message}" }, }, - 'WriteTo:File2': { - 'Name': 'File', - 'Args': { - 'outputTemplate': '{Message}' + "WriteTo:File2": { + "Name": "File", + "Args": { + "outputTemplate": "{Message}" }, } } @@ -131,7 +130,7 @@ public void Enrich_SupportSimplifiedSyntax() { var json = """ { - 'Enrich': [ 'FromLogContext', 'WithMachineName', 'WithThreadId' ] + "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ] } """; @@ -274,7 +273,17 @@ public void MixedMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root [Fact] public void NoConfigurationRootUsedStillValid() { - var section = JsonStringConfigSource.LoadSection("{ 'Nest': { 'Serilog': { 'MinimumLevel': 'Error' } } }", "Nest"); + var json = """ + { + "Nest": { + "Serilog": { + "MinimumLevel": "Error" + } + } + } + """; + + var section = JsonStringConfigSource.LoadSection(json, "Nest"); var reader = new ConfigurationReader(section.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), new ConfigurationReaderOptions(), section); var loggerConfig = new LoggerConfiguration(); diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 9e3f96fb..bff6703f 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -395,12 +395,12 @@ public void LoggingFilterSwitchIsConfigured(string switchName) { var json = $$""" { - 'Serilog': { - 'FilterSwitches': { '{{switchName}}': 'Prop = 42' }, - 'Filter:BySwitch': { - 'Name': 'ControlledBy', - 'Args': { - 'switch': '$mySwitch' + "Serilog": { + "FilterSwitches": { "{{switchName}}": "Prop = 42" }, + "Filter:BySwitch": { + "Name": "ControlledBy", + "Args": { + "switch": "$mySwitch" } } } @@ -426,10 +426,10 @@ public void LoggingLevelSwitchIsConfigured(string switchName) { var json = $$""" { - 'Serilog': { - 'LevelSwitches': { '{{switchName}}' : 'Warning' }, - 'MinimumLevel' : { - 'ControlledBy' : '$switch1' + "Serilog": { + "LevelSwitches": { "{{switchName}}" : "Warning" }, + "MinimumLevel" : { + "ControlledBy" : "$switch1" } } } @@ -1379,12 +1379,12 @@ public void TestLogLevelSwitchesCallback(string switchName) { var json = $$""" { - 'Serilog': { - 'LevelSwitches': { '{{switchName}}': 'Information' }, - 'MinimumLevel': { - 'Override': { - 'System': 'Warning', - 'System.Threading': 'Debug' + "Serilog": { + "LevelSwitches": { "{{switchName}}": "Information" }, + "MinimumLevel": { + "Override": { + "System": "Warning", + "System.Threading": "Debug" } } } diff --git a/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs b/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs index cbe9ab7e..c5e8e45e 100644 --- a/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs @@ -11,26 +11,26 @@ public class DynamicLevelChangeTests { const string DefaultConfig = """ { - 'Serilog': { - 'Using': [ 'TestDummies' ], - 'MinimumLevel': { - 'Default': 'Information', - 'Override': { - 'Root.Test': 'Information' + "Serilog": { + "Using": [ "TestDummies" ], + "MinimumLevel": { + "Default": "Information", + "Override": { + "Root.Test": "Information" } }, - 'LevelSwitches': { '$mySwitch': 'Information' }, - 'FilterSwitches': { '$myFilter': null }, - 'Filter:Dummy': { - 'Name': 'ControlledBy', - 'Args': { - 'switch': '$myFilter' + "LevelSwitches": { "$mySwitch": "Information" }, + "FilterSwitches": { "$myFilter": null }, + "Filter:Dummy": { + "Name": "ControlledBy", + "Args": { + "switch": "$myFilter" } }, - 'WriteTo:Dummy': { - 'Name': 'DummyConsole', - 'Args': { - 'levelSwitch': '$mySwitch' + "WriteTo:Dummy": { + "Name": "DummyConsole", + "Args": { + "levelSwitch": "$mySwitch" } } } diff --git a/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs b/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs index 33192271..55bb3603 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs @@ -7,16 +7,16 @@ static class ConfigurationReaderTestHelpers { public const string minimumLevelFlatTemplate = """ {{ - 'Serilog': {{ - 'MinimumLevel': '{0}' + "Serilog": {{ + "MinimumLevel": "{0}" }} }} """; public const string minimumLevelObjectTemplate = """ {{ - 'Serilog': {{ - 'MinimumLevel': {{ - 'Default': '{0}' + "Serilog": {{ + "MinimumLevel": {{ + "Default": "{0}" }} }} }} diff --git a/test/Serilog.Settings.Configuration.Tests/Support/Extensions.cs b/test/Serilog.Settings.Configuration.Tests/Support/Extensions.cs index 9353653e..2b7c7f44 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/Extensions.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/Extensions.cs @@ -9,11 +9,5 @@ public static object LiteralValue(this LogEventPropertyValue @this) return ((ScalarValue)@this).Value; } - public static string ToValidJson(this string str) - { - str = str.Replace('\'', '"'); - return str; - } - public static string Format(this string template, params object[] paramObjects) => string.Format(template, paramObjects); } diff --git a/test/Serilog.Settings.Configuration.Tests/Support/JsonStringConfigSource.cs b/test/Serilog.Settings.Configuration.Tests/Support/JsonStringConfigSource.cs index 41667086..923656c5 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/JsonStringConfigSource.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/JsonStringConfigSource.cs @@ -42,7 +42,7 @@ public JsonStringConfigProvider(string json) : base(new JsonConfigurationSource public override void Load() { - Load(StringToStream(_json.ToValidJson())); + Load(StringToStream(_json)); } static Stream StringToStream(string str) From b6fe2cff1e12f9b42b6342591dab2fb9466fa043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Thu, 16 Mar 2023 18:31:41 +0100 Subject: [PATCH 37/64] Enable JSON language injection So that your Integrated Development Environment (IDE) can perform nice syntax highlighting. See [Language injections in C#][1] in Rider for example. Also remove some trailing commas that were highlighted after adding the // language=json comment [1]: https://www.jetbrains.com/help/rider/Language_Injections.html --- .../ConfigurationReaderTests.cs | 14 ++++-- .../ConfigurationSettingsTests.cs | 45 +++++++++++++++++++ .../DynamicLevelChangeTests.cs | 1 + .../LoggerConfigurationExtensionsTests.cs | 4 ++ 4 files changed, 60 insertions(+), 4 deletions(-) diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs index 2b0775c1..56ae8ce7 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs @@ -23,6 +23,7 @@ public ConfigurationReaderTests() [Fact] public void WriteToSupportSimplifiedSyntax() { + // language=json var json = """ { "WriteTo": [ "LiterateConsole", "DiagnosticTrace" ] @@ -41,6 +42,7 @@ public void WriteToSupportSimplifiedSyntax() [Fact] public void WriteToSupportExpandedSyntaxWithoutArgs() { + // language=json var json = """ { "WriteTo": [ { @@ -59,13 +61,14 @@ public void WriteToSupportExpandedSyntaxWithoutArgs() [Fact] public void WriteToSupportExpandedSyntaxWithArgs() { + // language=json var json = """ { "WriteTo": [ { "Name": "LiterateConsole", "Args": { "outputTemplate": "{Message}" - }, + } }] } """; @@ -87,6 +90,7 @@ public void WriteToSupportExpandedSyntaxWithArgs() [Fact] public void WriteToSupportMultipleSinksOfTheSameKind() { + // language=json var json = """ { "WriteTo": [ @@ -94,7 +98,7 @@ public void WriteToSupportMultipleSinksOfTheSameKind() "Name": "LiterateConsole", "Args": { "outputTemplate": "{Message}" - }, + } }, "DiagnosticTrace" ], @@ -102,13 +106,13 @@ public void WriteToSupportMultipleSinksOfTheSameKind() "Name": "File", "Args": { "outputTemplate": "{Message}" - }, + } }, "WriteTo:File2": { "Name": "File", "Args": { "outputTemplate": "{Message}" - }, + } } } """; @@ -128,6 +132,7 @@ public void WriteToSupportMultipleSinksOfTheSameKind() [Fact] public void Enrich_SupportSimplifiedSyntax() { + // language=json var json = """ { "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ] @@ -273,6 +278,7 @@ public void MixedMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root [Fact] public void NoConfigurationRootUsedStillValid() { + // language=json var json = """ { "Nest": { diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index bff6703f..7c77dc2e 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -36,6 +36,7 @@ public void PropertyEnrichmentIsApplied() { LogEvent? evt = null; + // language=json var json = """ { "Serilog": { @@ -63,6 +64,7 @@ public void CanReadWithoutSerilogSection(string sectionName) { LogEvent? evt = null; + // language=json var json = """ { "Properties": { @@ -120,6 +122,7 @@ public void ParameterlessSinksAreConfigured(string syntax, string json) [Fact] public void ConfigurationAssembliesFromDllScanning() { + // language=json var json = """ { "Serilog": { @@ -147,6 +150,7 @@ public void ConfigurationAssembliesFromDllScanning() [Fact] public void SinksAreConfigured() { + // language=json var json = """ { "Serilog": { @@ -174,6 +178,7 @@ public void SinksAreConfigured() [Fact] public void AuditSinksAreConfigured() { + // language=json var json = """ { "Serilog": { @@ -201,6 +206,7 @@ public void AuditSinksAreConfigured() [Fact] public void AuditToSubLoggersAreConfigured() { + // language=json var json = """ { "Serilog": { @@ -234,6 +240,7 @@ public void AuditToSubLoggersAreConfigured() [Fact] public void TestMinimumLevelOverrides() { + // language=json var json = """ { "Serilog": { @@ -268,6 +275,7 @@ public void TestMinimumLevelOverrides() [Fact] public void TestMinimumLevelOverridesForChildContext() { + // language=json var json = """ { "Serilog": { @@ -304,6 +312,7 @@ public void TestMinimumLevelOverridesForChildContext() [Fact] public void SinksWithAbstractParamsAreConfiguredWithTypeName() { + // language=json var json = """ { "Serilog": { @@ -328,6 +337,7 @@ public void SinksWithAbstractParamsAreConfiguredWithTypeName() [Fact] public void SinksAreConfiguredWithStaticMember() { + // language=json var json = """ { "Serilog": { @@ -373,6 +383,7 @@ public void LoggingLevelSwitchNameValidityScenarios(string switchName, bool expe [Fact] public void LoggingLevelSwitchWithInvalidNameThrowsFormatException() { + // language=json var json = """ { "Serilog": { @@ -393,6 +404,7 @@ public void LoggingLevelSwitchWithInvalidNameThrowsFormatException() [InlineData("mySwitch")] public void LoggingFilterSwitchIsConfigured(string switchName) { + // language=json var json = $$""" { "Serilog": { @@ -424,6 +436,7 @@ public void LoggingFilterSwitchIsConfigured(string switchName) [InlineData("switch1")] public void LoggingLevelSwitchIsConfigured(string switchName) { + // language=json var json = $$""" { "Serilog": { @@ -451,6 +464,7 @@ public void LoggingLevelSwitchIsConfigured(string switchName) [Fact] public void SettingMinimumLevelControlledByToAnUndeclaredSwitchThrows() { + // language=json var json = """ { "Serilog": { @@ -473,6 +487,7 @@ public void SettingMinimumLevelControlledByToAnUndeclaredSwitchThrows() [Fact] public void LoggingLevelSwitchIsPassedToSinks() { + // language=json var json = """ { "Serilog": { @@ -511,6 +526,7 @@ public void LoggingLevelSwitchIsPassedToSinks() [Fact] public void ReferencingAnUndeclaredSwitchInSinkThrows() { + // language=json var json = """ { "Serilog": { @@ -538,6 +554,7 @@ public void ReferencingAnUndeclaredSwitchInSinkThrows() [Fact] public void LoggingLevelSwitchCanBeUsedForMinimumLevelOverrides() { + // language=json var json = """ { "Serilog": { @@ -592,6 +609,7 @@ public void LoggingLevelSwitchCanBeUsedForMinimumLevelOverrides() [Trait("BugFix", "https://github.com/serilog/serilog-settings-configuration/issues/142")] public void SinkWithIConfigurationArguments() { + // language=json var json = """ { "Serilog": { @@ -618,6 +636,7 @@ public void SinkWithIConfigurationArguments() [Trait("BugFix", "https://github.com/serilog/serilog-settings-configuration/issues/142")] public void SinkWithOptionalIConfigurationArguments() { + // language=json var json = """ { "Serilog": { @@ -644,6 +663,7 @@ public void SinkWithOptionalIConfigurationArguments() [Fact] public void SinkWithIConfigSectionArguments() { + // language=json var json = """ { "Serilog": { @@ -670,6 +690,7 @@ public void SinkWithIConfigSectionArguments() [Fact] public void SinkWithConfigurationBindingArgument() { + // language=json var json = """ { "Serilog": { @@ -696,6 +717,7 @@ public void SinkWithConfigurationBindingArgument() [Fact] public void SinkWithStringArrayArgument() { + // language=json var json = """ { "Serilog": { @@ -734,6 +756,7 @@ public void DestructureNumericNumbers(string numberDecimalSeparator) try { + // language=json var json = """ { "Serilog": { @@ -768,6 +791,7 @@ public void DestructureNumericNumbers(string numberDecimalSeparator) [Fact] public void DestructureWithCollectionsOfTypeArgument() { + // language=json var json = """ { "Serilog": { @@ -811,6 +835,7 @@ public void DestructureWithCollectionsOfTypeArgument() [Fact] public void SinkWithIntArrayArgument() { + // language=json var json = """ { "Serilog": { @@ -838,6 +863,7 @@ public void SinkWithIntArrayArgument() [Fact] public void CaseInsensitiveArgumentNameMatching() { + // language=json var json = """ { "Serilog": { @@ -864,6 +890,7 @@ public void CaseInsensitiveArgumentNameMatching() [Fact] public void WriteToLoggerWithRestrictedToMinimumLevelIsSupported() { + // language=json var json = """ { "Serilog": { @@ -898,6 +925,7 @@ public void WriteToLoggerWithRestrictedToMinimumLevelIsSupported() [Fact] public void WriteToSubLoggerWithLevelSwitchIsSupported() { + // language=json var json = """ { "Serilog": { @@ -935,6 +963,7 @@ public void WriteToSubLoggerWithLevelSwitchIsSupported() [Fact] public void InconsistentComplexVsScalarArgumentValuesThrowsIOE() { + // language=json var jsonDiscreteValue = """ { "Serilog": { @@ -947,6 +976,7 @@ public void InconsistentComplexVsScalarArgumentValuesThrowsIOE() } """; + // language=json var jsonComplexValue = """ { "Serilog": { @@ -976,6 +1006,7 @@ public void InconsistentComplexVsScalarArgumentValuesThrowsIOE() [Fact] public void DestructureLimitsNestingDepth() { + // language=json var json = """ { "Serilog": { @@ -1011,6 +1042,7 @@ public void DestructureLimitsNestingDepth() [Fact] public void DestructureLimitsStringLength() { + // language=json var json = """ { "Serilog": { @@ -1032,6 +1064,7 @@ public void DestructureLimitsStringLength() [Fact] public void DestructureLimitsCollectionCount() { + // language=json var json = """ { "Serilog": { @@ -1065,6 +1098,7 @@ private static string GetDestructuredProperty(object x, string json) [Fact] public void DestructuringWithCustomExtensionMethodIsApplied() { + // language=json var json = """ { "Serilog": { @@ -1091,6 +1125,7 @@ public void DestructuringWithCustomExtensionMethodIsApplied() [Fact] public void DestructuringAsScalarIsAppliedWithShortTypeName() { + // language=json var json = """ { "Serilog": { @@ -1117,6 +1152,7 @@ public void DestructuringAsScalarIsAppliedWithShortTypeName() [Fact] public void DestructuringAsScalarIsAppliedWithAssemblyQualifiedName() { + // language=json var json = $$""" { "Serilog": { @@ -1143,6 +1179,7 @@ public void DestructuringAsScalarIsAppliedWithAssemblyQualifiedName() [Fact] public void WriteToSinkIsAppliedWithCustomSink() { + // language=json var json = $$""" { "Serilog": { @@ -1170,6 +1207,7 @@ public void WriteToSinkIsAppliedWithCustomSink() [Fact] public void WriteToSinkIsAppliedWithCustomSinkAndMinimumLevel() { + // language=json var json = $$""" { "Serilog": { @@ -1199,6 +1237,7 @@ public void WriteToSinkIsAppliedWithCustomSinkAndMinimumLevel() [Fact] public void WriteToSinkIsAppliedWithCustomSinkAndLevelSwitch() { + // language=json var json = $$""" { "Serilog": { @@ -1229,6 +1268,7 @@ public void WriteToSinkIsAppliedWithCustomSinkAndLevelSwitch() [Fact] public void AuditToSinkIsAppliedWithCustomSink() { + // language=json var json = $$""" { "Serilog": { @@ -1256,6 +1296,7 @@ public void AuditToSinkIsAppliedWithCustomSink() [Fact] public void AuditToSinkIsAppliedWithCustomSinkAndMinimumLevel() { + // language=json var json = $$""" { "Serilog": { @@ -1285,6 +1326,7 @@ public void AuditToSinkIsAppliedWithCustomSinkAndMinimumLevel() [Fact] public void AuditToSinkIsAppliedWithCustomSinkAndLevelSwitch() { + // language=json var json = $$""" { "Serilog": { @@ -1317,6 +1359,7 @@ public void EnrichWithIsAppliedWithCustomEnricher() { LogEvent? evt = null; + // language=json var json = $$""" { "Serilog": { @@ -1347,6 +1390,7 @@ public void FilterWithIsAppliedWithCustomFilter() { LogEvent? evt = null; + // language=json var json = $$""" { "Serilog": { @@ -1377,6 +1421,7 @@ public void FilterWithIsAppliedWithCustomFilter() [InlineData("switch1")] public void TestLogLevelSwitchesCallback(string switchName) { + // language=json var json = $$""" { "Serilog": { diff --git a/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs b/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs index c5e8e45e..4cc743a5 100644 --- a/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs @@ -9,6 +9,7 @@ namespace Serilog.Settings.Configuration.Tests; public class DynamicLevelChangeTests { + // language=json const string DefaultConfig = """ { "Serilog": { diff --git a/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs b/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs index e73ae14e..39a0df9c 100644 --- a/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/LoggerConfigurationExtensionsTests.cs @@ -21,6 +21,7 @@ public void ReadFromConfigurationSectionReadsFromAnArbitrarySection() { LogEvent? evt = null; + // language=json var json = """ { "NotSerilog": { @@ -52,6 +53,7 @@ public void ReadFromConfigurationSectionReadsFromAnArbitrarySection() [Trait("BugFix", "https://github.com/serilog/serilog-settings-configuration/issues/143")] public void ReadFromConfigurationSectionThrowsWhenTryingToCallConfigurationMethodWithIConfigurationParam() { + // language=json var json = """ { "NotSerilog": { @@ -85,6 +87,7 @@ public void ReadFromConfigurationSectionThrowsWhenTryingToCallConfigurationMetho [Fact] public void ReadFromConfigurationDoesNotThrowWhenTryingToCallConfigurationMethodWithIConfigurationParam() { + // language=json var json = """ { "NotSerilog": { @@ -111,6 +114,7 @@ public void ReadFromConfigurationDoesNotThrowWhenTryingToCallConfigurationMethod [Trait("BugFix", "https://github.com/serilog/serilog-settings-configuration/issues/143")] public void ReadFromConfigurationSectionDoesNotThrowWhenTryingToCallConfigurationMethodWithOptionalIConfigurationParam() { + // language=json var json = """ { "NotSerilog": { From ccf4dc832ac5e55cfdd175122448927c7acef221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Thu, 16 Mar 2023 09:09:50 +0100 Subject: [PATCH 38/64] Improve SelfLog when a configuration method is not found List candidate methods only if there are any. Discovered this weird log while working on #353: > Unable to find a method called WithThreadName. Candidate methods are: --- .../Settings/Configuration/ConfigurationReader.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index d5a3a929..a2c71c5d 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -470,8 +470,15 @@ static bool HasImplicitValueWhenNotSpecified(ParameterInfo paramInfo) .ToList(); if (!methodsByName.Any()) - SelfLog.WriteLine($"Unable to find a method called {name}. Candidate methods are:{Environment.NewLine}{string.Join(Environment.NewLine, candidateMethods)}"); + { + SelfLog.WriteLine($"Unable to find a method called {name}."); + if (candidateMethods.Any()) + { + SelfLog.WriteLine($"Candidate methods are:{Environment.NewLine}{string.Join(Environment.NewLine, candidateMethods)}"); + } + } else + { SelfLog.WriteLine($"Unable to find a method called {name} " + (suppliedArgumentNames.Any() ? "for supplied arguments: " + string.Join(", ", suppliedArgumentNames) @@ -479,8 +486,8 @@ static bool HasImplicitValueWhenNotSpecified(ParameterInfo paramInfo) + ". Candidate methods are:" + Environment.NewLine + string.Join(Environment.NewLine, methodsByName)); + } } - return selectedMethod; } From 3849967b9b27ee71ba9de0c1f43b7707ff7f895f Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Fri, 17 Mar 2023 08:50:10 +0300 Subject: [PATCH 39/64] fix --- ...erilog.Settings.Configuration.approved.txt | 4 +- ...erilog.Settings.Configuration.received.txt | 52 ------------------- 2 files changed, 2 insertions(+), 54 deletions(-) delete mode 100644 test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.received.txt diff --git a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt index 175708e7..4c7fc7cf 100644 --- a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt +++ b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt @@ -43,8 +43,8 @@ namespace Serilog.Settings.Configuration public ConfigurationReaderOptions(Microsoft.Extensions.DependencyModel.DependencyContext? dependencyContext) { } public ConfigurationReaderOptions(Serilog.Settings.Configuration.ConfigurationAssemblySource configurationAssemblySource) { } public ConfigurationReaderOptions(params System.Reflection.Assembly[] assemblies) { } - public bool AllowInternalMethods { get; set; } - public bool AllowInternalTypes { get; set; } + public bool AllowInternalMethods { get; init; } + public bool AllowInternalTypes { get; init; } public System.IFormatProvider? FormatProvider { get; init; } public System.Action? OnLevelSwitchCreated { get; init; } public string? SectionName { get; init; } diff --git a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.received.txt b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.received.txt deleted file mode 100644 index 4c7fc7cf..00000000 --- a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.received.txt +++ /dev/null @@ -1,52 +0,0 @@ -namespace Serilog -{ - public static class ConfigurationLoggerConfigurationExtensions - { - public const string DefaultSectionName = "Serilog"; - [System.Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptio" + - "ns readerOptions) instead.")] - public static Serilog.LoggerConfiguration Configuration(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfiguration configuration, Microsoft.Extensions.DependencyModel.DependencyContext dependencyContext) { } - [System.Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptio" + - "ns readerOptions) instead.")] - public static Serilog.LoggerConfiguration Configuration(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfiguration configuration, Serilog.Settings.Configuration.ConfigurationAssemblySource configurationAssemblySource) { } - public static Serilog.LoggerConfiguration Configuration(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfiguration configuration, Serilog.Settings.Configuration.ConfigurationReaderOptions? readerOptions = null) { } - [System.Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptio" + - "ns readerOptions) instead.")] - public static Serilog.LoggerConfiguration Configuration(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfiguration configuration, params System.Reflection.Assembly[] assemblies) { } - [System.Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptio" + - "ns readerOptions) instead.")] - public static Serilog.LoggerConfiguration Configuration(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfiguration configuration, string sectionName, Microsoft.Extensions.DependencyModel.DependencyContext? dependencyContext = null) { } - [System.Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptio" + - "ns readerOptions) instead.")] - public static Serilog.LoggerConfiguration Configuration(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfiguration configuration, string sectionName, Serilog.Settings.Configuration.ConfigurationAssemblySource configurationAssemblySource) { } - [System.Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptio" + - "ns readerOptions) instead.")] - public static Serilog.LoggerConfiguration Configuration(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfiguration configuration, string sectionName, params System.Reflection.Assembly[] assemblies) { } - [System.Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, string sectionName, Depe" + - "ndencyContext dependencyContext) instead.")] - public static Serilog.LoggerConfiguration ConfigurationSection(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfigurationSection configSection, Microsoft.Extensions.DependencyModel.DependencyContext? dependencyContext = null) { } - [System.Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, string sectionName, Conf" + - "igurationAssemblySource configurationAssemblySource) instead.")] - public static Serilog.LoggerConfiguration ConfigurationSection(this Serilog.Configuration.LoggerSettingsConfiguration settingConfiguration, Microsoft.Extensions.Configuration.IConfigurationSection configSection, Serilog.Settings.Configuration.ConfigurationAssemblySource configurationAssemblySource) { } - } -} -namespace Serilog.Settings.Configuration -{ - public enum ConfigurationAssemblySource - { - UseLoadedAssemblies = 0, - AlwaysScanDllFiles = 1, - } - public sealed class ConfigurationReaderOptions - { - public ConfigurationReaderOptions() { } - public ConfigurationReaderOptions(Microsoft.Extensions.DependencyModel.DependencyContext? dependencyContext) { } - public ConfigurationReaderOptions(Serilog.Settings.Configuration.ConfigurationAssemblySource configurationAssemblySource) { } - public ConfigurationReaderOptions(params System.Reflection.Assembly[] assemblies) { } - public bool AllowInternalMethods { get; init; } - public bool AllowInternalTypes { get; init; } - public System.IFormatProvider? FormatProvider { get; init; } - public System.Action? OnLevelSwitchCreated { get; init; } - public string? SectionName { get; init; } - } -} \ No newline at end of file From 264d3bea89db31e6ceb90602bc558753ddb4f57f Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Fri, 17 Mar 2023 08:57:25 +0300 Subject: [PATCH 40/64] tests --- .../ConfigurationSettingsTests.cs | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 72f82891..f9e8886e 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -144,8 +144,10 @@ public void ConfigurationAssembliesFromDllScanning() Assert.Single(DummyConsoleSink.Emitted); } - [Fact] - public void ConfigurationAssembliesWithInternalMethodInPublicClass() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void ConfigurationAssembliesWithInternalMethodInPublicClass(bool allowInternalMethods) { var json = """ { @@ -161,18 +163,23 @@ public void ConfigurationAssembliesWithInternalMethodInPublicClass() var log = new LoggerConfiguration() .ReadFrom.Configuration( configuration: config, - readerOptions: new ConfigurationReaderOptions(ConfigurationAssemblySource.AlwaysScanDllFiles) { AllowInternalMethods = true }) + readerOptions: new ConfigurationReaderOptions(ConfigurationAssemblySource.AlwaysScanDllFiles) { AllowInternalMethods = allowInternalMethods }) .CreateLogger(); DummyConsoleSink.Emitted.Clear(); log.Write(Some.InformationEvent()); - Assert.Single(DummyConsoleSink.Emitted); + if (allowInternalMethods) + Assert.Single(DummyConsoleSink.Emitted); + else + Assert.Empty(DummyConsoleSink.Emitted); } - [Fact] - public void ConfigurationAssembliesWithPublicMethodInInternalClass() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void ConfigurationAssembliesWithPublicMethodInInternalClass(bool allowInternalTypes) { var json = """ { @@ -188,18 +195,23 @@ public void ConfigurationAssembliesWithPublicMethodInInternalClass() var log = new LoggerConfiguration() .ReadFrom.Configuration( configuration: config, - readerOptions: new ConfigurationReaderOptions(ConfigurationAssemblySource.AlwaysScanDllFiles) { AllowInternalTypes = true }) + readerOptions: new ConfigurationReaderOptions(ConfigurationAssemblySource.AlwaysScanDllFiles) { AllowInternalTypes = allowInternalTypes }) .CreateLogger(); DummyConsoleSink.Emitted.Clear(); log.Write(Some.InformationEvent()); - Assert.Single(DummyConsoleSink.Emitted); + if (allowInternalTypes) + Assert.Single(DummyConsoleSink.Emitted); + else + Assert.Empty(DummyConsoleSink.Emitted); } - [Fact] - public void ConfigurationAssembliesWithInternalMethodInInternalClass() + [Theory] + [InlineData(false, false)] + [InlineData(true, true)] + public void ConfigurationAssembliesWithInternalMethodInInternalClass(bool allowInternalTypes, bool allowInternalMethods) { var json = """ { @@ -215,14 +227,17 @@ public void ConfigurationAssembliesWithInternalMethodInInternalClass() var log = new LoggerConfiguration() .ReadFrom.Configuration( configuration: config, - readerOptions: new ConfigurationReaderOptions(ConfigurationAssemblySource.AlwaysScanDllFiles) { AllowInternalTypes = true, AllowInternalMethods = true }) + readerOptions: new ConfigurationReaderOptions(ConfigurationAssemblySource.AlwaysScanDllFiles) { AllowInternalTypes = allowInternalTypes, AllowInternalMethods = allowInternalMethods }) .CreateLogger(); DummyConsoleSink.Emitted.Clear(); log.Write(Some.InformationEvent()); - Assert.Single(DummyConsoleSink.Emitted); + if (allowInternalTypes && allowInternalMethods) + Assert.Single(DummyConsoleSink.Emitted); + else + Assert.Empty(DummyConsoleSink.Emitted); } [Fact] From 4ab71a97bc35e5184f0c02ff0d07fbb6d7faf677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Fri, 17 Mar 2023 08:32:33 +0100 Subject: [PATCH 41/64] Update src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs Co-authored-by: Ivan Maximov --- .../Settings/Configuration/ConfigurationReader.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index a2c71c5d..7dd5b9bd 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -471,10 +471,13 @@ static bool HasImplicitValueWhenNotSpecified(ParameterInfo paramInfo) if (!methodsByName.Any()) { - SelfLog.WriteLine($"Unable to find a method called {name}."); if (candidateMethods.Any()) { - SelfLog.WriteLine($"Candidate methods are:{Environment.NewLine}{string.Join(Environment.NewLine, candidateMethods)}"); + SelfLog.WriteLine($"Unable to find a method called {name}. Candidate methods are:{Environment.NewLine}{string.Join(Environment.NewLine, candidateMethods)}"); + } + else + { + SelfLog.WriteLine($"Unable to find a method called {name}. No candidates found."); } } else From 5a91b7dc711dda05561f9a3f6aef9269437a3979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Fri, 17 Mar 2023 09:05:13 +0100 Subject: [PATCH 42/64] Add more nullable type reference safeties Discovered by temporarily adding a net6.0 target framework which has better nullable reference type annotations. --- .../Configuration/Assemblies/DllScanningAssemblyFinder.cs | 6 +++++- .../Settings/Configuration/ConfigurationReader.cs | 4 ++-- .../Settings/Configuration/LoggingFilterSwitchProxy.cs | 7 ++++--- .../Settings/Configuration/ObjectArgumentValue.cs | 4 ++-- .../Settings/Configuration/StringArgumentValue.cs | 8 +++----- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DllScanningAssemblyFinder.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DllScanningAssemblyFinder.cs index e571157f..38ff3c4a 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DllScanningAssemblyFinder.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DllScanningAssemblyFinder.cs @@ -32,7 +32,11 @@ public override IReadOnlyList FindAssembliesContainingName(string } else { - probeDirs.Add(Path.GetDirectoryName(typeof(AssemblyFinder).Assembly.Location)); + var assemblyLocation = Path.GetDirectoryName(typeof(AssemblyFinder).Assembly.Location); + if (assemblyLocation != null) + { + probeDirs.Add(assemblyLocation); + } } var query = from probeDir in probeDirs diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index d5a3a929..d5ed5e3f 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -484,12 +484,12 @@ static bool HasImplicitValueWhenNotSpecified(ParameterInfo paramInfo) return selectedMethod; } - static bool ParameterNameMatches(string actualParameterName, string suppliedName) + static bool ParameterNameMatches(string? actualParameterName, string suppliedName) { return suppliedName.Equals(actualParameterName, StringComparison.OrdinalIgnoreCase); } - static bool ParameterNameMatches(string actualParameterName, IEnumerable suppliedNames) + static bool ParameterNameMatches(string? actualParameterName, IEnumerable suppliedNames) { return suppliedNames.Any(s => ParameterNameMatches(actualParameterName, s)); } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/LoggingFilterSwitchProxy.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/LoggingFilterSwitchProxy.cs index a35974ed..3b4997dd 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/LoggingFilterSwitchProxy.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/LoggingFilterSwitchProxy.cs @@ -15,12 +15,12 @@ class LoggingFilterSwitchProxy _setProxy = (Action)Delegate.CreateDelegate( typeof(Action), realSwitch, - expressionProperty.GetSetMethod()); + expressionProperty.GetSetMethod() ?? throw new MissingMethodException(type.FullName, "set_Expression")); _getProxy = (Func)Delegate.CreateDelegate( typeof(Func), realSwitch, - expressionProperty.GetGetMethod()); + expressionProperty.GetGetMethod() ?? throw new MissingMethodException(type.FullName, "get_Expression")); } public object RealSwitch { get; } @@ -42,6 +42,7 @@ public string? Expression return null; } - return new LoggingFilterSwitchProxy(Activator.CreateInstance(filterSwitchType, expression)); + var realSwitch = Activator.CreateInstance(filterSwitchType, expression) ?? throw new InvalidOperationException($"Activator.CreateInstance returned null for {filterSwitchType}"); + return new LoggingFilterSwitchProxy(realSwitch); } } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs index 6cc07b4d..3a154573 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs @@ -85,7 +85,7 @@ bool TryCreateContainer([NotNullWhen(true)] out object? result) return false; var configurationElements = _section.GetChildren().ToArray(); - result = Activator.CreateInstance(toType); + result = Activator.CreateInstance(toType) ?? throw new InvalidOperationException($"Activator.CreateInstance returned null for {toType}"); for (int i = 0; i < configurationElements.Length; ++i) { @@ -137,7 +137,7 @@ internal static bool TryBuildCtorExpression( var ctor = (from c in type.GetConstructors() from p in c.GetParameters() - let argumentBindResult = suppliedArguments.TryGetValue(p.Name, out var argValue) switch + let argumentBindResult = suppliedArguments.TryGetValue(p.Name ?? "", out var argValue) switch { true => new { success = true, hasMatch = true, value = (object?)argValue }, false => p.HasDefaultValue switch diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs index c26139bd..ebf3d2ea 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs @@ -21,7 +21,7 @@ public StringArgumentValue(string providedValue) { { typeof(Uri), s => new Uri(s) }, { typeof(TimeSpan), s => TimeSpan.Parse(s) }, - { typeof(Type), s => Type.GetType(s, throwOnError:true) }, + { typeof(Type), s => Type.GetType(s, throwOnError:true)! }, }; public object? ConvertTo(Type toType, ResolutionContext resolutionContext) @@ -68,7 +68,7 @@ public StringArgumentValue(string providedValue) if (toType != typeof(string) && TryParseStaticMemberAccessor(argumentValue, out var accessorTypeName, out var memberName)) { - var accessorType = Type.GetType(accessorTypeName, throwOnError: true); + var accessorType = Type.GetType(accessorTypeName, throwOnError: true)!; // if delegate, look for a method and then construct a delegate if (typeof(Delegate).IsAssignableFrom(toType) || typeof(MethodInfo) == toType) @@ -109,9 +109,7 @@ public StringArgumentValue(string providedValue) // is there a public static property with that name ? var publicStaticPropertyInfo = accessorType.GetTypeInfo().DeclaredProperties .Where(x => x.Name == memberName) - .Where(x => x.GetMethod != null) - .Where(x => x.GetMethod.IsPublic) - .FirstOrDefault(x => x.GetMethod.IsStatic); + .FirstOrDefault(x => x.GetMethod != null && x.GetMethod.IsPublic && x.GetMethod.IsStatic); if (publicStaticPropertyInfo != null) { From e8b2a39b0b26ef4878d5fd52e17bc75387531586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Thu, 16 Mar 2023 02:09:54 +0100 Subject: [PATCH 43/64] Make sure that single-file apps can find assemblies that contains sinks Before this commit, when single-file app was detected, the behaviour was to fallback on DLL scanning. But DLL scanning would not find anything for an app published as a single-file, by sheer definition of single-file app! After this commit, an exception is thrown if the app is published as a single-file AND no `Serilog:Using` section is defined in the configuration. The error message explains that either a `Serilog:Using` section must be added or show how to explicitly configure assemblies through the `ConfigurationReaderOptions`. --- .../Assemblies/AssemblyFinder.cs | 15 +----------- .../Assemblies/AutoAssemblyFinder.cs | 23 +++++++++++++++++++ .../DependencyContextAssemblyFinder.cs | 11 +++++---- .../Assemblies/DllScanningAssemblyFinder.cs | 2 +- .../Configuration/ConfigurationReader.cs | 16 +++++++++++-- 5 files changed, 46 insertions(+), 21 deletions(-) create mode 100644 src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AutoAssemblyFinder.cs diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AssemblyFinder.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AssemblyFinder.cs index 7fcbe383..0ca10db0 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AssemblyFinder.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AssemblyFinder.cs @@ -14,20 +14,7 @@ protected static bool IsCaseInsensitiveMatch(string? text, string textToFind) public static AssemblyFinder Auto() { - try - { - // Need to check `Assembly.GetEntryAssembly()` first because - // `DependencyContext.Default` throws an exception when `Assembly.GetEntryAssembly()` returns null - if (Assembly.GetEntryAssembly() != null && DependencyContext.Default != null) - { - return new DependencyContextAssemblyFinder(DependencyContext.Default); - } - } - catch (NotSupportedException) when (typeof(object).Assembly.Location is "") // bundled mode detection - { - } - - return new DllScanningAssemblyFinder(); + return new AutoAssemblyFinder(new DependencyContextAssemblyFinder(DependencyContext.Default), new DllScanningAssemblyFinder()); } public static AssemblyFinder ForSource(ConfigurationAssemblySource configurationAssemblySource) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AutoAssemblyFinder.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AutoAssemblyFinder.cs new file mode 100644 index 00000000..89aa1a45 --- /dev/null +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AutoAssemblyFinder.cs @@ -0,0 +1,23 @@ +using System.Reflection; + +namespace Serilog.Settings.Configuration.Assemblies; + +class AutoAssemblyFinder : AssemblyFinder +{ + readonly AssemblyFinder[] _assemblyFinders; + + public AutoAssemblyFinder(params AssemblyFinder[] assemblyFinders) + { + _assemblyFinders = assemblyFinders; + } + + public override IReadOnlyList FindAssembliesContainingName(string nameToFind) + { + var assemblyNames = new List(); + foreach (var assemblyFinder in _assemblyFinders) + { + assemblyNames.AddRange(assemblyFinder.FindAssembliesContainingName(nameToFind)); + } + return assemblyNames; + } +} diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DependencyContextAssemblyFinder.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DependencyContextAssemblyFinder.cs index 13e24f23..2ed07ce9 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DependencyContextAssemblyFinder.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DependencyContextAssemblyFinder.cs @@ -5,22 +5,25 @@ namespace Serilog.Settings.Configuration.Assemblies; sealed class DependencyContextAssemblyFinder : AssemblyFinder { - readonly DependencyContext _dependencyContext; + readonly DependencyContext? _dependencyContext; - public DependencyContextAssemblyFinder(DependencyContext dependencyContext) + public DependencyContextAssemblyFinder(DependencyContext? dependencyContext) { - _dependencyContext = dependencyContext ?? throw new ArgumentNullException(nameof(dependencyContext)); + _dependencyContext = dependencyContext; } public override IReadOnlyList FindAssembliesContainingName(string nameToFind) { + if (_dependencyContext == null) + return Array.Empty(); + var query = from library in _dependencyContext.RuntimeLibraries where IsReferencingSerilog(library) from assemblyName in library.GetDefaultAssemblyNames(_dependencyContext) where IsCaseInsensitiveMatch(assemblyName.Name, nameToFind) select assemblyName; - return query.ToList().AsReadOnly(); + return query.ToList(); static bool IsReferencingSerilog(Library library) { diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DllScanningAssemblyFinder.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DllScanningAssemblyFinder.cs index 38ff3c4a..c38c88bf 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DllScanningAssemblyFinder.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/DllScanningAssemblyFinder.cs @@ -48,7 +48,7 @@ where IsCaseInsensitiveMatch(assemblyFileName, nameToFind) where assemblyName != null select assemblyName; - return query.ToList().AsReadOnly(); + return query.ToList(); static AssemblyName? TryGetAssemblyNameFrom(string path) { diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 46eca3c9..b44ad958 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -368,7 +368,7 @@ static IReadOnlyCollection LoadConfigurationAssemblies(IConfiguration { if (string.IsNullOrWhiteSpace(simpleName)) throw new InvalidOperationException( - "A zero-length or whitespace assembly name was supplied to a Serilog.Using configuration statement."); + $"A zero-length or whitespace assembly name was supplied to a {usingSection.Path} configuration statement."); var assembly = Assembly.Load(new AssemblyName(simpleName)); if (!assemblies.ContainsKey(assembly.FullName)) @@ -383,7 +383,19 @@ static IReadOnlyCollection LoadConfigurationAssemblies(IConfiguration assemblies.Add(assumed.FullName, assumed); } - return assemblies.Values.ToList().AsReadOnly(); + if (assemblies.Count == 1) + { + var message = $""" + No {usingSection.Path} configuration section is defined and no Serilog assemblies were found. + This is most likely because the application is published as single-file. + Either add a {usingSection.Path} section or explicitly specify assemblies that contains sinks and other types through the reader options. For example: + var options = new ConfigurationReaderOptions(typeof(ConsoleLoggerConfigurationExtensions).Assembly, typeof(SerilogExpression).Assembly); + new LoggerConfiguration().ReadFrom.Configuration(configuration, options); + """; + throw new InvalidOperationException(message); + } + + return assemblies.Values; } void CallConfigurationMethods(ILookup> methods, IReadOnlyCollection configurationMethods, object receiver) From ecddb134ef6bae0ddee8947382dbfdff6526c2f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Wed, 15 Mar 2023 18:25:42 +0100 Subject: [PATCH 44/64] Add integration tests --- serilog-settings-configuration.sln | 5 + .../PublishSingleFileTests.cs | 113 ++++++++++++++ ...erilog.Settings.Configuration.Tests.csproj | 5 + .../TestApp.cs | 140 ++++++++++++++++++ .../xunit.runner.json | 4 + test/TestApp/.gitignore | 2 + test/TestApp/Program.cs | 56 +++++++ test/TestApp/TestApp.csproj | 26 ++++ 8 files changed, 351 insertions(+) create mode 100644 test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs create mode 100644 test/Serilog.Settings.Configuration.Tests/TestApp.cs create mode 100644 test/Serilog.Settings.Configuration.Tests/xunit.runner.json create mode 100644 test/TestApp/.gitignore create mode 100644 test/TestApp/Program.cs create mode 100644 test/TestApp/TestApp.csproj diff --git a/serilog-settings-configuration.sln b/serilog-settings-configuration.sln index a0f41b68..f0209fd7 100644 --- a/serilog-settings-configuration.sln +++ b/serilog-settings-configuration.sln @@ -32,6 +32,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample", "sample\Sample\Sam EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestDummies", "test\TestDummies\TestDummies.csproj", "{B7CF5068-DD19-4868-A268-5280BDE90361}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApp", "test\TestApp\TestApp.csproj", "{1B6E08F3-16C9-4912-BEEE-57DB78C92A12}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -54,6 +56,8 @@ Global {B7CF5068-DD19-4868-A268-5280BDE90361}.Debug|Any CPU.Build.0 = Debug|Any CPU {B7CF5068-DD19-4868-A268-5280BDE90361}.Release|Any CPU.ActiveCfg = Release|Any CPU {B7CF5068-DD19-4868-A268-5280BDE90361}.Release|Any CPU.Build.0 = Release|Any CPU + {1B6E08F3-16C9-4912-BEEE-57DB78C92A12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1B6E08F3-16C9-4912-BEEE-57DB78C92A12}.Release|Any CPU.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -63,6 +67,7 @@ Global {F793C6E8-C40A-4018-8884-C97E2BE38A54} = {D551DCB0-7771-4D01-BEBD-F7B57D1CF0E3} {A00E5E32-54F9-401A-BBA1-2F6FCB6366CD} = {D24872B9-57F3-42A7-BC8D-F9DA222FCE1B} {B7CF5068-DD19-4868-A268-5280BDE90361} = {D551DCB0-7771-4D01-BEBD-F7B57D1CF0E3} + {1B6E08F3-16C9-4912-BEEE-57DB78C92A12} = {D551DCB0-7771-4D01-BEBD-F7B57D1CF0E3} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {485F8843-42D7-4267-B5FB-20FE9181DEE9} diff --git a/test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs b/test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs new file mode 100644 index 00000000..9d2ab279 --- /dev/null +++ b/test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs @@ -0,0 +1,113 @@ +using System.Text; +using CliWrap; +using FluentAssertions; +using FluentAssertions.Execution; + +namespace Serilog.Settings.Configuration.Tests; + +public sealed class PublishSingleFileTests : IDisposable, IClassFixture +{ + readonly TestApp _testApp; + readonly AssertionScope _scope; + + public PublishSingleFileTests(TestApp testApp) + { + _testApp = testApp; + _scope = new AssertionScope(); + } + + public void Dispose() + { + _scope.Dispose(); + } + + [Theory] + [CombinatorialData] + public async Task RunTestApp_NoUsingAndNoAssembly(bool singleFile) + { + var (isSingleFile, stdOut, stdErr) = await RunTestAppAsync(singleFile); + stdOut.Should().Be(isSingleFile ? "Expected exception" : "(Main thread) [Information] Expected success"); + stdErr.Should().BeEmpty(); + } + + [Theory] + [CombinatorialData] + public async Task RunTestApp_UsingConsole(bool singleFile) + { + var (isSingleFile, stdOut, stdErr) = await RunTestAppAsync(singleFile, "--using-console"); + stdOut.Should().Be(isSingleFile ? "() [Information] Expected success" : "(Main thread) [Information] Expected success"); + if (isSingleFile) + stdErr.Should().Contain("Unable to find a method called WithThreadName"); + else + stdErr.Should().BeEmpty(); + } + + [Theory] + [CombinatorialData] + public async Task RunTestApp_UsingThread(bool singleFile) + { + var (isSingleFile, stdOut, stdErr) = await RunTestAppAsync(singleFile, "--using-thread"); + stdOut.Should().Be(isSingleFile ? "" : "(Main thread) [Information] Expected success"); + if (isSingleFile) + stdErr.Should().Contain("Unable to find a method called Console"); + else + stdErr.Should().BeEmpty(); + } + + [Theory] + [CombinatorialData] + public async Task RunTestApp_AssemblyThread(bool singleFile) + { + var (_, stdOut, stdErr) = await RunTestAppAsync(singleFile, "--assembly-thread"); + stdOut.Should().BeEmpty(); + stdErr.Should().Contain("Unable to find a method called Console"); + } + + [Theory] + [CombinatorialData] + public async Task RunTestApp_AssemblyConsole(bool singleFile) + { + var (_, stdOut, stdErr) = await RunTestAppAsync(singleFile, "--assembly-console"); + stdOut.Should().Be("() [Information] Expected success"); + stdErr.Should().Contain("Unable to find a method called WithThreadName"); + } + + [Theory] + [CombinatorialData] + public async Task RunTestApp_ConsoleAndThread(bool singleFile, [CombinatorialValues("using", "assembly")] string strategy) + { + var (_, stdOut, stdErr) = await RunTestAppAsync(singleFile, $"--{strategy}-console", $"--{strategy}-thread"); + stdOut.Should().Be("(Main thread) [Information] Expected success"); + stdErr.Should().BeEmpty(); + } + + async Task<(bool IsSingleFile, string StdOut, string StdErr)> RunTestAppAsync(bool singleFile, params string[] args) + { + // Determine whether the app is a _true_ single file, i.e. not a .NET Core 3.x version which + // [extracts bundled files to disk][1] and thus can find dlls. + // [1]: https://github.com/dotnet/designs/blob/main/accepted/2020/single-file/extract.md + var (isSingleFile, _) = await RunTestAppInternalAsync(singleFile, "is-single-file"); + var (stdOut, stdErr) = await RunTestAppInternalAsync(singleFile, args); + return (bool.Parse(isSingleFile), stdOut, stdErr); + } + + async Task<(string StdOut, string StdErr)> RunTestAppInternalAsync(bool singleExe, params string[] args) + { + var stdout = new StringBuilder(); + var stderr = new StringBuilder(); + var testAppPath = singleExe ? _testApp.SingleFileExe.FullName : _testApp.StandardExe.FullName; + var result = await Cli.Wrap(testAppPath) + .WithArguments(args) + .WithValidation(CommandResultValidation.None) + .WithStandardOutputPipe(PipeTarget.ToStringBuilder(stdout)) + .WithStandardErrorPipe(PipeTarget.ToStringBuilder(stderr)) + .ExecuteAsync(); + + if (result.ExitCode != 0) + { + throw new Exception($"An unexpected exception has occurred while running {testAppPath}. {stderr}"); + } + + return (stdout.ToString().Trim(), stderr.ToString().Trim()); + } +} 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 246d9765..b435f009 100644 --- a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj +++ b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj @@ -17,9 +17,14 @@ + + + + + diff --git a/test/Serilog.Settings.Configuration.Tests/TestApp.cs b/test/Serilog.Settings.Configuration.Tests/TestApp.cs new file mode 100644 index 00000000..6acd38a0 --- /dev/null +++ b/test/Serilog.Settings.Configuration.Tests/TestApp.cs @@ -0,0 +1,140 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Xml.Linq; +using System.Xml.XPath; +using CliWrap; +using FluentAssertions; +using Medallion.Threading; +using Medallion.Threading.FileSystem; +using NuGet.Frameworks; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Serilog.Settings.Configuration.Tests; + +public class TestApp : IAsyncLifetime +{ + readonly IMessageSink _messageSink; + readonly DirectoryInfo _workingDirectory; + readonly List _directoriesToCleanup; + readonly IDistributedLock _lock; + IDistributedSynchronizationHandle? _lockHandle; + + public TestApp(IMessageSink messageSink) + { + _messageSink = messageSink; + _workingDirectory = GetDirectory("test", "TestApp"); + _directoriesToCleanup = new List(); + _lock = new FileDistributedLock(new FileInfo(Path.Combine(_workingDirectory.FullName, "dotnet-restore.lock"))); + } + + public async Task InitializeAsync() + { + _lockHandle = await _lock.AcquireAsync(); + + var targetFrameworkAttribute = typeof(TestApp).Assembly.GetCustomAttribute(); + if (targetFrameworkAttribute == null) + { + throw new Exception($"Assembly {typeof(TestApp).Assembly} does not have a {nameof(TargetFrameworkAttribute)}"); + } + + var targetFramework = NuGetFramework.Parse(targetFrameworkAttribute.FrameworkName); + foreach (var singleFile in new[] { true, false }) + { + var framework = targetFramework.GetShortFolderName(); + var isDesktop = targetFramework.IsDesktop(); + + var outputDirectory = new DirectoryInfo(Path.Combine(_workingDirectory.FullName, framework, singleFile ? "publish-single-file" : "publish-standard")); + _directoriesToCleanup.Add(outputDirectory.Parent!); + + var restoreArgs = new[] { "restore", "--no-dependencies", $"-p:TargetFrameworks={string.Join("%3B", GetProjectTargetFrameworks().Append(framework).Distinct())}" }; + await RunDotnetAsync(_workingDirectory, restoreArgs); + + File.WriteAllText(Path.Combine(_workingDirectory.FullName, "FodyWeavers.xml"), singleFile && isDesktop ? "" : ""); + + var args = new[] { "publish", "--no-restore", "--configuration", "Release", "--output", outputDirectory.FullName, $"-p:TargetFramework={framework}" }; + await RunDotnetAsync(_workingDirectory, isDesktop ? args : args.Append($"-p:PublishSingleFile={singleFile}").ToArray()); + + var executableFileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "TestApp.exe" : "TestApp"; + var executableFile = new FileInfo(Path.Combine(outputDirectory.FullName, executableFileName)); + executableFile.Exists.Should().BeTrue(); + var dlls = executableFile.Directory!.EnumerateFiles("*.dll"); + if (singleFile) + { + dlls.Should().BeEmpty(because: "the test app was published as single-file"); + executableFile.Directory.EnumerateFiles().Should().ContainSingle().Which.FullName.Should().Be(executableFile.FullName); + SingleFileExe = executableFile; + } + else + { + dlls.Should().NotBeEmpty(because: "the test app was _not_ published as single-file"); + StandardExe = executableFile; + } + } + } + + public async Task DisposeAsync() + { + try + { + foreach (var directoryToCleanup in _directoriesToCleanup.Where(e => e.Exists)) + { + directoryToCleanup.Delete(recursive: true); + } + } + finally + { + await _lockHandle!.DisposeAsync(); + } + } + + public FileInfo SingleFileExe { get; private set; } = null!; + public FileInfo StandardExe { get; private set; } = null!; + + async Task RunDotnetAsync(DirectoryInfo workingDirectory, params string[] arguments) + { + _messageSink.OnMessage(new DiagnosticMessage($"cd {workingDirectory}")); + _messageSink.OnMessage(new DiagnosticMessage($"dotnet {string.Join(" ", arguments)}")); + var messageSinkTarget = PipeTarget.ToDelegate(line => _messageSink.OnMessage(new DiagnosticMessage(line))); + await Cli.Wrap("dotnet") + .WithWorkingDirectory(workingDirectory.FullName) + .WithArguments(arguments) + .WithStandardOutputPipe(messageSinkTarget) + .WithStandardErrorPipe(messageSinkTarget) + .ExecuteAsync(); + } + + static IEnumerable GetProjectTargetFrameworks() + { + var projectFile = GetFile("src", "Serilog.Settings.Configuration", "Serilog.Settings.Configuration.csproj"); + var project = XDocument.Load(projectFile.FullName); + var targetFrameworks = project.XPathSelectElement("/Project/PropertyGroup/TargetFrameworks") ?? throw new Exception($"TargetFrameworks element not found in {projectFile}"); + return targetFrameworks.Value.Split(';'); + } + + static DirectoryInfo GetDirectory(params string[] paths) + { + var directory = new DirectoryInfo(GetFullPath(paths)); + if (!directory.Exists) + { + throw new DirectoryNotFoundException($"The {directory.Name} directory must exist at {directory.FullName}"); + } + return directory; + } + + static FileInfo GetFile(params string[] paths) + { + var file = new FileInfo(GetFullPath(paths)); + if (!file.Exists) + { + throw new FileNotFoundException($"The {file.Name} file must exist at {file.FullName}"); + } + return file; + } + + static string GetFullPath(params string[] paths) => Path.GetFullPath(Path.Combine(new[] { GetThisDirectory(), "..", ".." }.Concat(paths).ToArray())); + + static string GetThisDirectory([CallerFilePath] string path = "") => Path.GetDirectoryName(path)!; +} diff --git a/test/Serilog.Settings.Configuration.Tests/xunit.runner.json b/test/Serilog.Settings.Configuration.Tests/xunit.runner.json new file mode 100644 index 00000000..6c0d1e49 --- /dev/null +++ b/test/Serilog.Settings.Configuration.Tests/xunit.runner.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "diagnosticMessages": true +} diff --git a/test/TestApp/.gitignore b/test/TestApp/.gitignore new file mode 100644 index 00000000..1cc29f16 --- /dev/null +++ b/test/TestApp/.gitignore @@ -0,0 +1,2 @@ +FodyWeavers.xml +FodyWeavers.xsd diff --git a/test/TestApp/Program.cs b/test/TestApp/Program.cs new file mode 100644 index 00000000..56a3235b --- /dev/null +++ b/test/TestApp/Program.cs @@ -0,0 +1,56 @@ +using System.Reflection; +using Microsoft.Extensions.Configuration; +using Serilog; +using Serilog.Debugging; +using Serilog.Settings.Configuration; + +if (args.Length == 1 && args[0] == "is-single-file") +{ + if (typeof(Program).Assembly.GetManifestResourceNames().Any(e => e.StartsWith("costura."))) + { + Console.WriteLine(true); + return 0; + } + // IL3000: 'System.Reflection.Assembly.Location' always returns an empty string for assemblies embedded in a single-file app +#pragma warning disable IL3000 + Console.WriteLine(string.IsNullOrEmpty(Assembly.GetEntryAssembly()?.Location)); +#pragma warning restore + return 0; +} + +SelfLog.Enable(Console.Error); + +Thread.CurrentThread.Name = "Main thread"; + +var configurationValues = new Dictionary +{ + ["Serilog:Enrich:0"] = "WithThreadName", + ["Serilog:WriteTo:0:Name"] = "Console", + ["Serilog:WriteTo:0:Args:outputTemplate"] = "({ThreadName}) [{Level}] {Message}{NewLine}", +}; + +if (args.Contains("--using-thread")) configurationValues["Serilog:Using:Thread"] = "Serilog.Enrichers.Thread"; +if (args.Contains("--using-console")) configurationValues["Serilog:Using:Console"] = "Serilog.Sinks.Console"; + +var assemblies = new List(); +if (args.Contains("--assembly-thread")) assemblies.Add(typeof(ThreadLoggerConfigurationExtensions).Assembly); +if (args.Contains("--assembly-console")) assemblies.Add(typeof(ConsoleLoggerConfigurationExtensions).Assembly); + +try +{ + var configuration = new ConfigurationBuilder().AddInMemoryCollection(configurationValues).Build(); + var options = assemblies.Count > 0 ? new ConfigurationReaderOptions(assemblies.ToArray()) : null; + var logger = new LoggerConfiguration().ReadFrom.Configuration(configuration, options).CreateLogger(); + logger.Information("Expected success"); + return 0; +} +catch (InvalidOperationException exception) when (exception.Message.StartsWith("No Serilog:Using configuration section is defined and no Serilog assemblies were found.")) +{ + Console.WriteLine("Expected exception"); + return 0; +} +catch (Exception exception) +{ + Console.Error.WriteLine(exception); + return 1; +} diff --git a/test/TestApp/TestApp.csproj b/test/TestApp/TestApp.csproj new file mode 100644 index 00000000..8adcef37 --- /dev/null +++ b/test/TestApp/TestApp.csproj @@ -0,0 +1,26 @@ + + + + Exe + net48 + embedded + false + false + false + none + false + true + + + + + + + + + + + + + + From a812ab18423cee7ebf0ca8733df9ce8800327f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Thu, 16 Mar 2023 08:16:08 +0100 Subject: [PATCH 45/64] Rename AutoAssemblyFinder into CompositeAssemblyFinder --- .../Settings/Configuration/Assemblies/AssemblyFinder.cs | 2 +- .../{AutoAssemblyFinder.cs => CompositeAssemblyFinder.cs} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/{AutoAssemblyFinder.cs => CompositeAssemblyFinder.cs} (81%) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AssemblyFinder.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AssemblyFinder.cs index 0ca10db0..9582e6a4 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AssemblyFinder.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AssemblyFinder.cs @@ -14,7 +14,7 @@ protected static bool IsCaseInsensitiveMatch(string? text, string textToFind) public static AssemblyFinder Auto() { - return new AutoAssemblyFinder(new DependencyContextAssemblyFinder(DependencyContext.Default), new DllScanningAssemblyFinder()); + return new CompositeAssemblyFinder(new DependencyContextAssemblyFinder(DependencyContext.Default), new DllScanningAssemblyFinder()); } public static AssemblyFinder ForSource(ConfigurationAssemblySource configurationAssemblySource) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AutoAssemblyFinder.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/CompositeAssemblyFinder.cs similarity index 81% rename from src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AutoAssemblyFinder.cs rename to src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/CompositeAssemblyFinder.cs index 89aa1a45..9f3c9ef2 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AutoAssemblyFinder.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/CompositeAssemblyFinder.cs @@ -2,11 +2,11 @@ namespace Serilog.Settings.Configuration.Assemblies; -class AutoAssemblyFinder : AssemblyFinder +class CompositeAssemblyFinder : AssemblyFinder { readonly AssemblyFinder[] _assemblyFinders; - public AutoAssemblyFinder(params AssemblyFinder[] assemblyFinders) + public CompositeAssemblyFinder(params AssemblyFinder[] assemblyFinders) { _assemblyFinders = assemblyFinders; } From 9a28018d4de1eb78a74c900ebd6050bf2a390eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Thu, 16 Mar 2023 09:05:29 +0100 Subject: [PATCH 46/64] Add some diagnostics with ITestOutputHelper when running the TestApp exe --- .../PublishSingleFileTests.cs | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs b/test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs index 9d2ab279..37f7dcc7 100644 --- a/test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs @@ -1,17 +1,21 @@ -using System.Text; +using System.Diagnostics; +using System.Text; using CliWrap; using FluentAssertions; using FluentAssertions.Execution; +using Xunit.Abstractions; namespace Serilog.Settings.Configuration.Tests; public sealed class PublishSingleFileTests : IDisposable, IClassFixture { + readonly ITestOutputHelper _outputHelper; readonly TestApp _testApp; readonly AssertionScope _scope; - public PublishSingleFileTests(TestApp testApp) + public PublishSingleFileTests(ITestOutputHelper outputHelper, TestApp testApp) { + _outputHelper = outputHelper; _testApp = testApp; _scope = new AssertionScope(); } @@ -93,21 +97,34 @@ public async Task RunTestApp_ConsoleAndThread(bool singleFile, [CombinatorialVal async Task<(string StdOut, string StdErr)> RunTestAppInternalAsync(bool singleExe, params string[] args) { - var stdout = new StringBuilder(); - var stderr = new StringBuilder(); - var testAppPath = singleExe ? _testApp.SingleFileExe.FullName : _testApp.StandardExe.FullName; - var result = await Cli.Wrap(testAppPath) + var stdOutBuilder = new StringBuilder(); + var stdErrBuilder = new StringBuilder(); + + var command = Cli.Wrap(singleExe ? _testApp.SingleFileExe.FullName : _testApp.StandardExe.FullName) .WithArguments(args) .WithValidation(CommandResultValidation.None) - .WithStandardOutputPipe(PipeTarget.ToStringBuilder(stdout)) - .WithStandardErrorPipe(PipeTarget.ToStringBuilder(stderr)) - .ExecuteAsync(); + .WithStandardOutputPipe(PipeTarget.ToStringBuilder(stdOutBuilder)) + .WithStandardErrorPipe(PipeTarget.ToStringBuilder(stdErrBuilder)); + + _outputHelper.WriteLine(command.ToString()); + + var stopwatch = Stopwatch.StartNew(); + var result = await command.ExecuteAsync(); + var executionTime = stopwatch.ElapsedMilliseconds; + + var stdOut = stdOutBuilder.ToString().Trim(); + var stdErr = stdErrBuilder.ToString().Trim(); + + _outputHelper.WriteLine($"Executed in {executionTime} ms"); + _outputHelper.WriteLine(stdOut.Length > 0 ? $"stdout: {stdOut}" : "nothing on stdout"); + _outputHelper.WriteLine(stdErr.Length > 0 ? $"stderr: {stdErr}" : "nothing on stderr"); + _outputHelper.WriteLine(""); if (result.ExitCode != 0) { - throw new Exception($"An unexpected exception has occurred while running {testAppPath}. {stderr}"); + throw new Exception($"An unexpected exception has occurred while running {command}. {stdErr}".Trim()); } - return (stdout.ToString().Trim(), stderr.ToString().Trim()); + return (stdOut, stdErr); } } From 92d37fc6cce9d6357781e1924dafba9eaf039c2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Thu, 16 Mar 2023 16:52:13 +0100 Subject: [PATCH 47/64] Run tests on the TestApp when published as self-contained This is when the default DependencyContext is actually null. Also run all the tests on .NET 6 which is a Long Term Support (LTS) release. --- .../PublishMode.cs | 25 +++++++ .../PublishModeExtensions.cs | 31 ++++++++ .../PublishSingleFileTests.cs | 73 +++++++++++++------ ...erilog.Settings.Configuration.Tests.csproj | 3 +- .../TestApp.cs | 44 +++++------ test/TestApp/TestApp.csproj | 1 - 6 files changed, 123 insertions(+), 54 deletions(-) create mode 100644 test/Serilog.Settings.Configuration.Tests/PublishMode.cs create mode 100644 test/Serilog.Settings.Configuration.Tests/PublishModeExtensions.cs diff --git a/test/Serilog.Settings.Configuration.Tests/PublishMode.cs b/test/Serilog.Settings.Configuration.Tests/PublishMode.cs new file mode 100644 index 00000000..d20afaa3 --- /dev/null +++ b/test/Serilog.Settings.Configuration.Tests/PublishMode.cs @@ -0,0 +1,25 @@ +namespace Serilog.Settings.Configuration.Tests; + +/// +/// The possible application publish modes for the TestApp. +/// See also the .NET application publishing overview documentation. +/// +public enum PublishMode +{ + /// + /// Standard app publish, all dlls and related files are copied along the main executable. + /// + Standard, + + /// + /// Publish a single file as a framework-dependent binary. + /// + /// On .NET Framework, Costura is used to publish as a single file. + SingleFile, + + /// + /// Publish a single file as a self contained binary, i.e. including the .NET libraries and target runtime. + /// + /// This mode is ignored on .NET Framework as it doesn't make sense. + SelfContained, +} diff --git a/test/Serilog.Settings.Configuration.Tests/PublishModeExtensions.cs b/test/Serilog.Settings.Configuration.Tests/PublishModeExtensions.cs new file mode 100644 index 00000000..0fee8b1d --- /dev/null +++ b/test/Serilog.Settings.Configuration.Tests/PublishModeExtensions.cs @@ -0,0 +1,31 @@ +using System.Reflection; +using System.Runtime.Versioning; +using NuGet.Frameworks; + +namespace Serilog.Settings.Configuration.Tests; + +public static class PublishModeExtensions +{ + static PublishModeExtensions() + { + var targetFrameworkAttribute = typeof(TestApp).Assembly.GetCustomAttribute(); + if (targetFrameworkAttribute == null) + { + throw new Exception($"Assembly {typeof(TestApp).Assembly} does not have a {nameof(TargetFrameworkAttribute)}"); + } + + var framework = NuGetFramework.Parse(targetFrameworkAttribute.FrameworkName); + + TargetFramework = framework.GetShortFolderName(); + IsDesktop = framework.IsDesktop(); + } + + public static bool IsDesktop { get; } + + public static string TargetFramework { get; } + + public static IEnumerable GetPublishModes() + { + return IsDesktop ? new[] { PublishMode.Standard, PublishMode.SingleFile } : Enum.GetValues(typeof(PublishMode)).Cast(); + } +} diff --git a/test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs b/test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs index 37f7dcc7..86495f92 100644 --- a/test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs @@ -26,19 +26,19 @@ public void Dispose() } [Theory] - [CombinatorialData] - public async Task RunTestApp_NoUsingAndNoAssembly(bool singleFile) + [ClassData(typeof(PublishModeTheoryData))] + public async Task RunTestApp_NoUsingAndNoAssembly(PublishMode publishMode) { - var (isSingleFile, stdOut, stdErr) = await RunTestAppAsync(singleFile); + var (isSingleFile, stdOut, stdErr) = await RunTestAppAsync(publishMode); stdOut.Should().Be(isSingleFile ? "Expected exception" : "(Main thread) [Information] Expected success"); stdErr.Should().BeEmpty(); } [Theory] - [CombinatorialData] - public async Task RunTestApp_UsingConsole(bool singleFile) + [ClassData(typeof(PublishModeTheoryData))] + public async Task RunTestApp_UsingConsole(PublishMode publishMode) { - var (isSingleFile, stdOut, stdErr) = await RunTestAppAsync(singleFile, "--using-console"); + var (isSingleFile, stdOut, stdErr) = await RunTestAppAsync(publishMode, "--using-console"); stdOut.Should().Be(isSingleFile ? "() [Information] Expected success" : "(Main thread) [Information] Expected success"); if (isSingleFile) stdErr.Should().Contain("Unable to find a method called WithThreadName"); @@ -47,10 +47,10 @@ public async Task RunTestApp_UsingConsole(bool singleFile) } [Theory] - [CombinatorialData] - public async Task RunTestApp_UsingThread(bool singleFile) + [ClassData(typeof(PublishModeTheoryData))] + public async Task RunTestApp_UsingThread(PublishMode publishMode) { - var (isSingleFile, stdOut, stdErr) = await RunTestAppAsync(singleFile, "--using-thread"); + var (isSingleFile, stdOut, stdErr) = await RunTestAppAsync(publishMode, "--using-thread"); stdOut.Should().Be(isSingleFile ? "" : "(Main thread) [Information] Expected success"); if (isSingleFile) stdErr.Should().Contain("Unable to find a method called Console"); @@ -59,48 +59,48 @@ public async Task RunTestApp_UsingThread(bool singleFile) } [Theory] - [CombinatorialData] - public async Task RunTestApp_AssemblyThread(bool singleFile) + [ClassData(typeof(PublishModeTheoryData))] + public async Task RunTestApp_AssemblyThread(PublishMode publishMode) { - var (_, stdOut, stdErr) = await RunTestAppAsync(singleFile, "--assembly-thread"); + var (_, stdOut, stdErr) = await RunTestAppAsync(publishMode, "--assembly-thread"); stdOut.Should().BeEmpty(); stdErr.Should().Contain("Unable to find a method called Console"); } [Theory] - [CombinatorialData] - public async Task RunTestApp_AssemblyConsole(bool singleFile) + [ClassData(typeof(PublishModeTheoryData))] + public async Task RunTestApp_AssemblyConsole(PublishMode publishMode) { - var (_, stdOut, stdErr) = await RunTestAppAsync(singleFile, "--assembly-console"); + var (_, stdOut, stdErr) = await RunTestAppAsync(publishMode, "--assembly-console"); stdOut.Should().Be("() [Information] Expected success"); stdErr.Should().Contain("Unable to find a method called WithThreadName"); } [Theory] - [CombinatorialData] - public async Task RunTestApp_ConsoleAndThread(bool singleFile, [CombinatorialValues("using", "assembly")] string strategy) + [ClassData(typeof(PublishModeAndStrategyTheoryData))] + public async Task RunTestApp_ConsoleAndThread(PublishMode publishMode, string strategy) { - var (_, stdOut, stdErr) = await RunTestAppAsync(singleFile, $"--{strategy}-console", $"--{strategy}-thread"); + var (_, stdOut, stdErr) = await RunTestAppAsync(publishMode, $"--{strategy}-console", $"--{strategy}-thread"); stdOut.Should().Be("(Main thread) [Information] Expected success"); stdErr.Should().BeEmpty(); } - async Task<(bool IsSingleFile, string StdOut, string StdErr)> RunTestAppAsync(bool singleFile, params string[] args) + async Task<(bool IsSingleFile, string StdOut, string StdErr)> RunTestAppAsync(PublishMode publishMode, params string[] args) { // Determine whether the app is a _true_ single file, i.e. not a .NET Core 3.x version which // [extracts bundled files to disk][1] and thus can find dlls. // [1]: https://github.com/dotnet/designs/blob/main/accepted/2020/single-file/extract.md - var (isSingleFile, _) = await RunTestAppInternalAsync(singleFile, "is-single-file"); - var (stdOut, stdErr) = await RunTestAppInternalAsync(singleFile, args); + var (isSingleFile, _) = await RunTestAppInternalAsync(publishMode, "is-single-file"); + var (stdOut, stdErr) = await RunTestAppInternalAsync(publishMode, args); return (bool.Parse(isSingleFile), stdOut, stdErr); } - async Task<(string StdOut, string StdErr)> RunTestAppInternalAsync(bool singleExe, params string[] args) + async Task<(string StdOut, string StdErr)> RunTestAppInternalAsync(PublishMode publishMode, params string[] args) { var stdOutBuilder = new StringBuilder(); var stdErrBuilder = new StringBuilder(); - var command = Cli.Wrap(singleExe ? _testApp.SingleFileExe.FullName : _testApp.StandardExe.FullName) + var command = Cli.Wrap(_testApp.GetExecutablePath(publishMode)) .WithArguments(args) .WithValidation(CommandResultValidation.None) .WithStandardOutputPipe(PipeTarget.ToStringBuilder(stdOutBuilder)) @@ -122,9 +122,34 @@ public async Task RunTestApp_ConsoleAndThread(bool singleFile, [CombinatorialVal if (result.ExitCode != 0) { - throw new Exception($"An unexpected exception has occurred while running {command}. {stdErr}".Trim()); + throw new Exception($"An unexpected exception has occurred while running {command}{Environment.NewLine}{stdErr}".Trim()); } return (stdOut, stdErr); } + + class PublishModeTheoryData : TheoryData + { + public PublishModeTheoryData() + { + foreach (var publishMode in PublishModeExtensions.GetPublishModes()) + { + Add(publishMode); + } + } + } + + class PublishModeAndStrategyTheoryData : TheoryData + { + public PublishModeAndStrategyTheoryData() + { + foreach (var publishMode in PublishModeExtensions.GetPublishModes()) + { + foreach (var strategy in new[] { "using", "assembly" }) + { + Add(publishMode, strategy); + } + } + } + } } 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 b435f009..7da015cc 100644 --- a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj +++ b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj @@ -2,7 +2,7 @@ net48 - $(TargetFrameworks);net7.0;netcoreapp3.1 + $(TargetFrameworks);net7.0;net6.0;netcoreapp3.1 @@ -24,7 +24,6 @@ - diff --git a/test/Serilog.Settings.Configuration.Tests/TestApp.cs b/test/Serilog.Settings.Configuration.Tests/TestApp.cs index 6acd38a0..51f25d7f 100644 --- a/test/Serilog.Settings.Configuration.Tests/TestApp.cs +++ b/test/Serilog.Settings.Configuration.Tests/TestApp.cs @@ -1,16 +1,14 @@ -using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Runtime.Versioning; using System.Xml.Linq; using System.Xml.XPath; using CliWrap; using FluentAssertions; using Medallion.Threading; using Medallion.Threading.FileSystem; -using NuGet.Frameworks; using Xunit.Abstractions; using Xunit.Sdk; +using static Serilog.Settings.Configuration.Tests.PublishModeExtensions; namespace Serilog.Settings.Configuration.Tests; @@ -19,6 +17,7 @@ public class TestApp : IAsyncLifetime readonly IMessageSink _messageSink; readonly DirectoryInfo _workingDirectory; readonly List _directoriesToCleanup; + readonly Dictionary _executables; readonly IDistributedLock _lock; IDistributedSynchronizationHandle? _lockHandle; @@ -27,6 +26,7 @@ public TestApp(IMessageSink messageSink) _messageSink = messageSink; _workingDirectory = GetDirectory("test", "TestApp"); _directoriesToCleanup = new List(); + _executables = new Dictionary(); _lock = new FileDistributedLock(new FileInfo(Path.Combine(_workingDirectory.FullName, "dotnet-restore.lock"))); } @@ -34,44 +34,35 @@ public async Task InitializeAsync() { _lockHandle = await _lock.AcquireAsync(); - var targetFrameworkAttribute = typeof(TestApp).Assembly.GetCustomAttribute(); - if (targetFrameworkAttribute == null) + foreach (var publishMode in GetPublishModes()) { - throw new Exception($"Assembly {typeof(TestApp).Assembly} does not have a {nameof(TargetFrameworkAttribute)}"); - } - - var targetFramework = NuGetFramework.Parse(targetFrameworkAttribute.FrameworkName); - foreach (var singleFile in new[] { true, false }) - { - var framework = targetFramework.GetShortFolderName(); - var isDesktop = targetFramework.IsDesktop(); - - var outputDirectory = new DirectoryInfo(Path.Combine(_workingDirectory.FullName, framework, singleFile ? "publish-single-file" : "publish-standard")); + var outputDirectory = new DirectoryInfo(Path.Combine(_workingDirectory.FullName, TargetFramework, publishMode.ToString())); _directoriesToCleanup.Add(outputDirectory.Parent!); - var restoreArgs = new[] { "restore", "--no-dependencies", $"-p:TargetFrameworks={string.Join("%3B", GetProjectTargetFrameworks().Append(framework).Distinct())}" }; + var restoreArgs = new[] { "restore", $"-p:TargetFrameworks={string.Join("%3B", GetProjectTargetFrameworks().Append(TargetFramework).Distinct())}" }; await RunDotnetAsync(_workingDirectory, restoreArgs); - File.WriteAllText(Path.Combine(_workingDirectory.FullName, "FodyWeavers.xml"), singleFile && isDesktop ? "" : ""); + File.WriteAllText(Path.Combine(_workingDirectory.FullName, "FodyWeavers.xml"), publishMode == PublishMode.SingleFile && IsDesktop ? "" : ""); - var args = new[] { "publish", "--no-restore", "--configuration", "Release", "--output", outputDirectory.FullName, $"-p:TargetFramework={framework}" }; - await RunDotnetAsync(_workingDirectory, isDesktop ? args : args.Append($"-p:PublishSingleFile={singleFile}").ToArray()); + var args = new[] { "publish", "--no-restore", "--configuration", "Release", "--output", outputDirectory.FullName, $"-p:TargetFramework={TargetFramework}" }; + var publishSingleFile = $"-p:PublishSingleFile={publishMode is PublishMode.SingleFile or PublishMode.SelfContained}"; + var selfContained = $"-p:SelfContained={publishMode is PublishMode.SelfContained}"; + await RunDotnetAsync(_workingDirectory, IsDesktop ? args : args.Append(publishSingleFile).Append(selfContained).ToArray()); var executableFileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "TestApp.exe" : "TestApp"; var executableFile = new FileInfo(Path.Combine(outputDirectory.FullName, executableFileName)); executableFile.Exists.Should().BeTrue(); var dlls = executableFile.Directory!.EnumerateFiles("*.dll"); - if (singleFile) + if (publishMode == PublishMode.Standard) { - dlls.Should().BeEmpty(because: "the test app was published as single-file"); - executableFile.Directory.EnumerateFiles().Should().ContainSingle().Which.FullName.Should().Be(executableFile.FullName); - SingleFileExe = executableFile; + dlls.Should().NotBeEmpty(because: $"the test app was _not_ published as single-file ({publishMode})"); } else { - dlls.Should().NotBeEmpty(because: "the test app was _not_ published as single-file"); - StandardExe = executableFile; + dlls.Should().BeEmpty(because: $"the test app was published as single-file ({publishMode})"); + executableFile.Directory.EnumerateFiles().Should().ContainSingle().Which.FullName.Should().Be(executableFile.FullName); } + _executables[publishMode] = executableFile; } } @@ -90,8 +81,7 @@ public async Task DisposeAsync() } } - public FileInfo SingleFileExe { get; private set; } = null!; - public FileInfo StandardExe { get; private set; } = null!; + public string GetExecutablePath(PublishMode publishMode) => _executables[publishMode].FullName; async Task RunDotnetAsync(DirectoryInfo workingDirectory, params string[] arguments) { diff --git a/test/TestApp/TestApp.csproj b/test/TestApp/TestApp.csproj index 8adcef37..437221b5 100644 --- a/test/TestApp/TestApp.csproj +++ b/test/TestApp/TestApp.csproj @@ -8,7 +8,6 @@ false false none - false true From 74402f836148f746f05a06f87cd8b15fee387155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Fri, 17 Mar 2023 09:01:51 +0100 Subject: [PATCH 48/64] Simplify loading assemblies Use a HashSet instead of a Dictionary keyed by the assembly full name. If an assembly is loaded twice with the same AssemblyName it will be the same instance so a HashSet (without even a custom IEqualityComparer) is the perfect solution. Also, Assembly.Load can throw many exceptions but won't return null. --- .../Settings/Configuration/ConfigurationReader.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index b44ad958..a85f8169 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -359,7 +359,7 @@ internal static IConfigurationArgumentValue GetArgumentValue(IConfigurationSecti static IReadOnlyCollection LoadConfigurationAssemblies(IConfiguration section, AssemblyFinder assemblyFinder) { var serilogAssembly = typeof(ILogger).Assembly; - var assemblies = new Dictionary { [serilogAssembly.FullName] = serilogAssembly }; + var assemblies = new HashSet { serilogAssembly }; var usingSection = section.GetSection("Using"); if (usingSection.GetChildren().Any()) @@ -371,16 +371,14 @@ static IReadOnlyCollection LoadConfigurationAssemblies(IConfiguration $"A zero-length or whitespace assembly name was supplied to a {usingSection.Path} configuration statement."); var assembly = Assembly.Load(new AssemblyName(simpleName)); - if (!assemblies.ContainsKey(assembly.FullName)) - assemblies.Add(assembly.FullName, assembly); + assemblies.Add(assembly); } } foreach (var assemblyName in assemblyFinder.FindAssembliesContainingName("serilog")) { var assumed = Assembly.Load(assemblyName); - if (assumed != null && !assemblies.ContainsKey(assumed.FullName)) - assemblies.Add(assumed.FullName, assumed); + assemblies.Add(assumed); } if (assemblies.Count == 1) @@ -395,7 +393,7 @@ This is most likely because the application is published as single-file. throw new InvalidOperationException(message); } - return assemblies.Values; + return assemblies; } void CallConfigurationMethods(ILookup> methods, IReadOnlyCollection configurationMethods, object receiver) From 2b6c040a4c9471f9515933830cd0003c9868cbc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Sat, 25 Mar 2023 12:40:48 +0100 Subject: [PATCH 49/64] Make tests successfully run concurrently across multiple target frameworks --- .../PublishSingleFileTests.cs | 4 +- ...erilog.Settings.Configuration.Tests.csproj | 2 +- .../Support/DirectoryInfoExtensions.cs | 10 ++ .../{ => Support}/PublishMode.cs | 2 +- .../{ => Support}/PublishModeExtensions.cs | 2 +- .../Support/TestApp.cs | 151 ++++++++++++++++++ .../TestApp.cs | 130 --------------- test/TestApp/TestApp.csproj | 6 +- 8 files changed, 172 insertions(+), 135 deletions(-) create mode 100644 test/Serilog.Settings.Configuration.Tests/Support/DirectoryInfoExtensions.cs rename test/Serilog.Settings.Configuration.Tests/{ => Support}/PublishMode.cs (94%) rename test/Serilog.Settings.Configuration.Tests/{ => Support}/PublishModeExtensions.cs (94%) create mode 100644 test/Serilog.Settings.Configuration.Tests/Support/TestApp.cs delete mode 100644 test/Serilog.Settings.Configuration.Tests/TestApp.cs diff --git a/test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs b/test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs index 86495f92..57f82e52 100644 --- a/test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs @@ -1,8 +1,10 @@ using System.Diagnostics; using System.Text; using CliWrap; +using CliWrap.Exceptions; using FluentAssertions; using FluentAssertions.Execution; +using Serilog.Settings.Configuration.Tests.Support; using Xunit.Abstractions; namespace Serilog.Settings.Configuration.Tests; @@ -122,7 +124,7 @@ public async Task RunTestApp_ConsoleAndThread(PublishMode publishMode, string st if (result.ExitCode != 0) { - throw new Exception($"An unexpected exception has occurred while running {command}{Environment.NewLine}{stdErr}".Trim()); + throw new CommandExecutionException(command, result.ExitCode, $"An unexpected exception has occurred while running {command}{Environment.NewLine}{stdErr}".Trim()); } return (stdOut, stdErr); 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 7da015cc..cf2b08a1 100644 --- a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj +++ b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj @@ -18,11 +18,11 @@ - + diff --git a/test/Serilog.Settings.Configuration.Tests/Support/DirectoryInfoExtensions.cs b/test/Serilog.Settings.Configuration.Tests/Support/DirectoryInfoExtensions.cs new file mode 100644 index 00000000..2d2e214f --- /dev/null +++ b/test/Serilog.Settings.Configuration.Tests/Support/DirectoryInfoExtensions.cs @@ -0,0 +1,10 @@ +namespace Serilog.Settings.Configuration.Tests; + +static class DirectoryInfoExtensions +{ + public static DirectoryInfo SubDirectory(this DirectoryInfo directory, params string[] paths) + => new(Path.GetFullPath(Path.Combine(paths.Prepend(directory.FullName).ToArray()))); + + public static FileInfo File(this DirectoryInfo directory, params string[] paths) + => new(Path.GetFullPath(Path.Combine(paths.Prepend(directory.FullName).ToArray()))); +} diff --git a/test/Serilog.Settings.Configuration.Tests/PublishMode.cs b/test/Serilog.Settings.Configuration.Tests/Support/PublishMode.cs similarity index 94% rename from test/Serilog.Settings.Configuration.Tests/PublishMode.cs rename to test/Serilog.Settings.Configuration.Tests/Support/PublishMode.cs index d20afaa3..082d1dfc 100644 --- a/test/Serilog.Settings.Configuration.Tests/PublishMode.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/PublishMode.cs @@ -1,4 +1,4 @@ -namespace Serilog.Settings.Configuration.Tests; +namespace Serilog.Settings.Configuration.Tests.Support; /// /// The possible application publish modes for the TestApp. diff --git a/test/Serilog.Settings.Configuration.Tests/PublishModeExtensions.cs b/test/Serilog.Settings.Configuration.Tests/Support/PublishModeExtensions.cs similarity index 94% rename from test/Serilog.Settings.Configuration.Tests/PublishModeExtensions.cs rename to test/Serilog.Settings.Configuration.Tests/Support/PublishModeExtensions.cs index 0fee8b1d..d3f49825 100644 --- a/test/Serilog.Settings.Configuration.Tests/PublishModeExtensions.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/PublishModeExtensions.cs @@ -2,7 +2,7 @@ using System.Runtime.Versioning; using NuGet.Frameworks; -namespace Serilog.Settings.Configuration.Tests; +namespace Serilog.Settings.Configuration.Tests.Support; public static class PublishModeExtensions { diff --git a/test/Serilog.Settings.Configuration.Tests/Support/TestApp.cs b/test/Serilog.Settings.Configuration.Tests/Support/TestApp.cs new file mode 100644 index 00000000..ab81df2c --- /dev/null +++ b/test/Serilog.Settings.Configuration.Tests/Support/TestApp.cs @@ -0,0 +1,151 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using CliWrap; +using CliWrap.Exceptions; +using FluentAssertions; +using Polly; +using Xunit.Abstractions; +using Xunit.Sdk; +using static Serilog.Settings.Configuration.Tests.Support.PublishModeExtensions; + +namespace Serilog.Settings.Configuration.Tests.Support; + +public class TestApp : IAsyncLifetime +{ + readonly IMessageSink _messageSink; + readonly DirectoryInfo _workingDirectory; + readonly Dictionary _executables; + + public TestApp(IMessageSink messageSink) + { + _messageSink = messageSink; + _workingDirectory = GetDirectory("test", $"TestApp-{TargetFramework}"); + _workingDirectory.Create(); + foreach (var file in GetDirectory("test", "TestApp").EnumerateFiles()) + { + file.CopyTo(_workingDirectory.File(file.Name).FullName, overwrite: true); + } + _executables = new Dictionary(); + } + + async Task IAsyncLifetime.InitializeAsync() + { + // Retry 3 times because pack / restore / publish may try to access the same files across different target frameworks and fail with System.IO.IOException: + // The process cannot access the file [Serilog.Settings.Configuration.deps.json or Serilog.Settings.Configuration.dll] because it is being used by another process. + var retryPolicy = Policy.Handle().RetryAsync(3); + await retryPolicy.ExecuteAsync(CreateTestAppAsync); + } + + Task IAsyncLifetime.DisposeAsync() + { + _workingDirectory.Delete(recursive: true); + return Task.CompletedTask; + } + + public string GetExecutablePath(PublishMode publishMode) => _executables[publishMode].FullName; + + async Task CreateTestAppAsync() + { + await PackAsync(); + await RestoreAsync(); + + var publishDirectory = _workingDirectory.SubDirectory("publish"); + var fodyWeaversXml = _workingDirectory.File("FodyWeavers.xml"); + + foreach (var publishMode in GetPublishModes()) + { + var outputDirectory = publishDirectory.SubDirectory(publishMode.ToString()); + + File.WriteAllText(fodyWeaversXml.FullName, publishMode == PublishMode.SingleFile && IsDesktop ? "" : ""); + + var publishArgs = new[] { + "publish", + "--no-restore", + "--configuration", "Release", + "--output", outputDirectory.FullName, + $"-p:TargetFramework={TargetFramework}" + }; + var publishSingleFile = $"-p:PublishSingleFile={publishMode is PublishMode.SingleFile or PublishMode.SelfContained}"; + var selfContained = $"-p:SelfContained={publishMode is PublishMode.SelfContained}"; + await RunDotnetAsync(_workingDirectory, IsDesktop ? publishArgs : publishArgs.Append(publishSingleFile).Append(selfContained).ToArray()); + + var executableFileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "TestApp.exe" : "TestApp"; + var executableFile = new FileInfo(Path.Combine(outputDirectory.FullName, executableFileName)); + executableFile.Exists.Should().BeTrue(); + var dlls = executableFile.Directory!.EnumerateFiles("*.dll"); + if (publishMode == PublishMode.Standard) + { + dlls.Should().NotBeEmpty(because: $"the test app was _not_ published as single-file ({publishMode})"); + } + else + { + dlls.Should().BeEmpty(because: $"the test app was published as single-file ({publishMode})"); + executableFile.Directory.EnumerateFiles().Should().ContainSingle().Which.FullName.Should().Be(executableFile.FullName); + } + _executables[publishMode] = executableFile; + } + } + + async Task PackAsync() + { + var projectFile = GetFile("src", "Serilog.Settings.Configuration", "Serilog.Settings.Configuration.csproj"); + var packArgs = new[] { + "pack", projectFile.FullName, + "--configuration", "Release", + "--output", _workingDirectory.FullName, + "-p:Version=0.0.0-IntegrationTest.0", + }; + await RunDotnetAsync(_workingDirectory, packArgs); + } + + async Task RestoreAsync() + { + var packagesDirectory = _workingDirectory.SubDirectory("packages"); + var restoreArgs = new[] { + "restore", + "--packages", packagesDirectory.FullName, + "--source", ".", + "--source", "https://api.nuget.org/v3/index.json", + "-p:Configuration=Release", + $"-p:TargetFramework={TargetFramework}" + }; + await RunDotnetAsync(_workingDirectory, restoreArgs); + } + + async Task RunDotnetAsync(DirectoryInfo workingDirectory, params string[] arguments) + { + _messageSink.OnMessage(new DiagnosticMessage($"cd {workingDirectory}")); + _messageSink.OnMessage(new DiagnosticMessage($"dotnet {string.Join(" ", arguments)}")); + var outBuilder = new StringBuilder(); + var errBuilder = new StringBuilder(); + var command = Cli.Wrap("dotnet") + .WithValidation(CommandResultValidation.None) + .WithWorkingDirectory(workingDirectory.FullName) + .WithArguments(arguments) + .WithStandardOutputPipe(PipeTarget.ToDelegate(line => + { + outBuilder.AppendLine(line); + _messageSink.OnMessage(new DiagnosticMessage($"==> out: {line}")); + })) + .WithStandardErrorPipe(PipeTarget.ToDelegate(line => + { + errBuilder.AppendLine(line); + _messageSink.OnMessage(new DiagnosticMessage($"==> err: {line}")); + })); + + var result = await command.ExecuteAsync(); + if (result.ExitCode != 0) + { + throw new CommandExecutionException(command, result.ExitCode, $"An unexpected exception has occurred while running {command}{Environment.NewLine}{errBuilder}{outBuilder}".Trim()); + } + } + + static DirectoryInfo GetDirectory(params string[] paths) => new(GetFullPath(paths)); + + static FileInfo GetFile(params string[] paths) => new(GetFullPath(paths)); + + static string GetFullPath(params string[] paths) => Path.GetFullPath(Path.Combine(new[] { GetThisDirectory(), "..", "..", ".." }.Concat(paths).ToArray())); + + static string GetThisDirectory([CallerFilePath] string path = "") => Path.GetDirectoryName(path)!; +} diff --git a/test/Serilog.Settings.Configuration.Tests/TestApp.cs b/test/Serilog.Settings.Configuration.Tests/TestApp.cs deleted file mode 100644 index 51f25d7f..00000000 --- a/test/Serilog.Settings.Configuration.Tests/TestApp.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Xml.Linq; -using System.Xml.XPath; -using CliWrap; -using FluentAssertions; -using Medallion.Threading; -using Medallion.Threading.FileSystem; -using Xunit.Abstractions; -using Xunit.Sdk; -using static Serilog.Settings.Configuration.Tests.PublishModeExtensions; - -namespace Serilog.Settings.Configuration.Tests; - -public class TestApp : IAsyncLifetime -{ - readonly IMessageSink _messageSink; - readonly DirectoryInfo _workingDirectory; - readonly List _directoriesToCleanup; - readonly Dictionary _executables; - readonly IDistributedLock _lock; - IDistributedSynchronizationHandle? _lockHandle; - - public TestApp(IMessageSink messageSink) - { - _messageSink = messageSink; - _workingDirectory = GetDirectory("test", "TestApp"); - _directoriesToCleanup = new List(); - _executables = new Dictionary(); - _lock = new FileDistributedLock(new FileInfo(Path.Combine(_workingDirectory.FullName, "dotnet-restore.lock"))); - } - - public async Task InitializeAsync() - { - _lockHandle = await _lock.AcquireAsync(); - - foreach (var publishMode in GetPublishModes()) - { - var outputDirectory = new DirectoryInfo(Path.Combine(_workingDirectory.FullName, TargetFramework, publishMode.ToString())); - _directoriesToCleanup.Add(outputDirectory.Parent!); - - var restoreArgs = new[] { "restore", $"-p:TargetFrameworks={string.Join("%3B", GetProjectTargetFrameworks().Append(TargetFramework).Distinct())}" }; - await RunDotnetAsync(_workingDirectory, restoreArgs); - - File.WriteAllText(Path.Combine(_workingDirectory.FullName, "FodyWeavers.xml"), publishMode == PublishMode.SingleFile && IsDesktop ? "" : ""); - - var args = new[] { "publish", "--no-restore", "--configuration", "Release", "--output", outputDirectory.FullName, $"-p:TargetFramework={TargetFramework}" }; - var publishSingleFile = $"-p:PublishSingleFile={publishMode is PublishMode.SingleFile or PublishMode.SelfContained}"; - var selfContained = $"-p:SelfContained={publishMode is PublishMode.SelfContained}"; - await RunDotnetAsync(_workingDirectory, IsDesktop ? args : args.Append(publishSingleFile).Append(selfContained).ToArray()); - - var executableFileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "TestApp.exe" : "TestApp"; - var executableFile = new FileInfo(Path.Combine(outputDirectory.FullName, executableFileName)); - executableFile.Exists.Should().BeTrue(); - var dlls = executableFile.Directory!.EnumerateFiles("*.dll"); - if (publishMode == PublishMode.Standard) - { - dlls.Should().NotBeEmpty(because: $"the test app was _not_ published as single-file ({publishMode})"); - } - else - { - dlls.Should().BeEmpty(because: $"the test app was published as single-file ({publishMode})"); - executableFile.Directory.EnumerateFiles().Should().ContainSingle().Which.FullName.Should().Be(executableFile.FullName); - } - _executables[publishMode] = executableFile; - } - } - - public async Task DisposeAsync() - { - try - { - foreach (var directoryToCleanup in _directoriesToCleanup.Where(e => e.Exists)) - { - directoryToCleanup.Delete(recursive: true); - } - } - finally - { - await _lockHandle!.DisposeAsync(); - } - } - - public string GetExecutablePath(PublishMode publishMode) => _executables[publishMode].FullName; - - async Task RunDotnetAsync(DirectoryInfo workingDirectory, params string[] arguments) - { - _messageSink.OnMessage(new DiagnosticMessage($"cd {workingDirectory}")); - _messageSink.OnMessage(new DiagnosticMessage($"dotnet {string.Join(" ", arguments)}")); - var messageSinkTarget = PipeTarget.ToDelegate(line => _messageSink.OnMessage(new DiagnosticMessage(line))); - await Cli.Wrap("dotnet") - .WithWorkingDirectory(workingDirectory.FullName) - .WithArguments(arguments) - .WithStandardOutputPipe(messageSinkTarget) - .WithStandardErrorPipe(messageSinkTarget) - .ExecuteAsync(); - } - - static IEnumerable GetProjectTargetFrameworks() - { - var projectFile = GetFile("src", "Serilog.Settings.Configuration", "Serilog.Settings.Configuration.csproj"); - var project = XDocument.Load(projectFile.FullName); - var targetFrameworks = project.XPathSelectElement("/Project/PropertyGroup/TargetFrameworks") ?? throw new Exception($"TargetFrameworks element not found in {projectFile}"); - return targetFrameworks.Value.Split(';'); - } - - static DirectoryInfo GetDirectory(params string[] paths) - { - var directory = new DirectoryInfo(GetFullPath(paths)); - if (!directory.Exists) - { - throw new DirectoryNotFoundException($"The {directory.Name} directory must exist at {directory.FullName}"); - } - return directory; - } - - static FileInfo GetFile(params string[] paths) - { - var file = new FileInfo(GetFullPath(paths)); - if (!file.Exists) - { - throw new FileNotFoundException($"The {file.Name} file must exist at {file.FullName}"); - } - return file; - } - - static string GetFullPath(params string[] paths) => Path.GetFullPath(Path.Combine(new[] { GetThisDirectory(), "..", ".." }.Concat(paths).ToArray())); - - static string GetThisDirectory([CallerFilePath] string path = "") => Path.GetDirectoryName(path)!; -} diff --git a/test/TestApp/TestApp.csproj b/test/TestApp/TestApp.csproj index 437221b5..88f0ca1f 100644 --- a/test/TestApp/TestApp.csproj +++ b/test/TestApp/TestApp.csproj @@ -11,9 +11,13 @@ true - + + + + + From e71c5494fcbdae33cd105e1b1a1733f3e6ceb8fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Sun, 26 Mar 2023 18:38:15 +0200 Subject: [PATCH 50/64] Parallelize tests across target frameworks on AppVeyor --- Build.ps1 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Build.ps1 b/Build.ps1 index 7989cd29..080f8809 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -30,6 +30,9 @@ if($LASTEXITCODE -ne 0) { exit 2 } Write-Output "build: Testing" -& dotnet test test\Serilog.Settings.Configuration.Tests --configuration Release --no-build --no-restore +# Dotnet test doesn't run separate TargetFrameworks in parallel: https://github.com/dotnet/sdk/issues/19147 +# Workaround: use `dotnet test` on dlls directly in order to pass the `--parallel` option to vstest. +# The _reported_ runtime is wrong but the _actual_ used runtime is correct, see https://github.com/microsoft/vstest/issues/2037#issuecomment-720549173 +& dotnet test test\Serilog.Settings.Configuration.Tests\bin\Release\*\Serilog.Settings.Configuration.Tests.dll --parallel if($LASTEXITCODE -ne 0) { exit 3 } \ No newline at end of file From 48281232bf1821790906fcba9e62457d54dd70d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Mon, 27 Mar 2023 11:30:43 +0200 Subject: [PATCH 51/64] Use a nuget.config file instead of specifying multiple sources on the command line This works around https://github.com/dotnet/sdk/issues/27202 It also has the benefit of using settings _only_ from the specified config file, ignoring the global nuget.config where package source mapping could interfere with the local source. --- .../Support/TestApp.cs | 9 ++++----- test/TestApp/nuget.config | 11 +++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 test/TestApp/nuget.config diff --git a/test/Serilog.Settings.Configuration.Tests/Support/TestApp.cs b/test/Serilog.Settings.Configuration.Tests/Support/TestApp.cs index ab81df2c..a39fe98f 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/TestApp.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/TestApp.cs @@ -101,14 +101,13 @@ async Task PackAsync() async Task RestoreAsync() { - var packagesDirectory = _workingDirectory.SubDirectory("packages"); + // Can't use "--source . --source https://api.nuget.org/v3/index.json" because of https://github.com/dotnet/sdk/issues/27202 => a nuget.config file is used instead. + // It also has the benefit of using settings _only_ from the specified config file, ignoring the global nuget.config where package source mapping could interfere with the local source. var restoreArgs = new[] { "restore", - "--packages", packagesDirectory.FullName, - "--source", ".", - "--source", "https://api.nuget.org/v3/index.json", + "--configfile", "nuget.config", "-p:Configuration=Release", - $"-p:TargetFramework={TargetFramework}" + $"-p:TargetFramework={TargetFramework}", }; await RunDotnetAsync(_workingDirectory, restoreArgs); } diff --git a/test/TestApp/nuget.config b/test/TestApp/nuget.config new file mode 100644 index 00000000..cfec8fcc --- /dev/null +++ b/test/TestApp/nuget.config @@ -0,0 +1,11 @@ + + + + + + + + + + + From 2cb45f5f772b026d56a7ef870fefe6d7b550809c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Sun, 2 Apr 2023 17:53:41 +0200 Subject: [PATCH 52/64] Refactor the TestApp fixture Add a new PublishAsync(PublishMode) method and also add a comment explaining why it would not work to publish multiple apps in parallel. --- .../Support/TestApp.cs | 75 +++++++++++-------- 1 file changed, 42 insertions(+), 33 deletions(-) diff --git a/test/Serilog.Settings.Configuration.Tests/Support/TestApp.cs b/test/Serilog.Settings.Configuration.Tests/Support/TestApp.cs index a39fe98f..913bbe20 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/TestApp.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/TestApp.cs @@ -47,43 +47,15 @@ Task IAsyncLifetime.DisposeAsync() async Task CreateTestAppAsync() { + // It might be tempting to do pack -> restore -> build --no-restore -> publish --no-build (and parallelize over publish modes) + // But this would fail because of https://github.com/dotnet/sdk/issues/17526 and probably because of other unforeseen bugs + // preventing from running multiple `dotnet publish` commands with different parameters. + await PackAsync(); await RestoreAsync(); - - var publishDirectory = _workingDirectory.SubDirectory("publish"); - var fodyWeaversXml = _workingDirectory.File("FodyWeavers.xml"); - foreach (var publishMode in GetPublishModes()) { - var outputDirectory = publishDirectory.SubDirectory(publishMode.ToString()); - - File.WriteAllText(fodyWeaversXml.FullName, publishMode == PublishMode.SingleFile && IsDesktop ? "" : ""); - - var publishArgs = new[] { - "publish", - "--no-restore", - "--configuration", "Release", - "--output", outputDirectory.FullName, - $"-p:TargetFramework={TargetFramework}" - }; - var publishSingleFile = $"-p:PublishSingleFile={publishMode is PublishMode.SingleFile or PublishMode.SelfContained}"; - var selfContained = $"-p:SelfContained={publishMode is PublishMode.SelfContained}"; - await RunDotnetAsync(_workingDirectory, IsDesktop ? publishArgs : publishArgs.Append(publishSingleFile).Append(selfContained).ToArray()); - - var executableFileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "TestApp.exe" : "TestApp"; - var executableFile = new FileInfo(Path.Combine(outputDirectory.FullName, executableFileName)); - executableFile.Exists.Should().BeTrue(); - var dlls = executableFile.Directory!.EnumerateFiles("*.dll"); - if (publishMode == PublishMode.Standard) - { - dlls.Should().NotBeEmpty(because: $"the test app was _not_ published as single-file ({publishMode})"); - } - else - { - dlls.Should().BeEmpty(because: $"the test app was published as single-file ({publishMode})"); - executableFile.Directory.EnumerateFiles().Should().ContainSingle().Which.FullName.Should().Be(executableFile.FullName); - } - _executables[publishMode] = executableFile; + await PublishAsync(publishMode); } } @@ -112,6 +84,43 @@ async Task RestoreAsync() await RunDotnetAsync(_workingDirectory, restoreArgs); } + async Task PublishAsync(PublishMode publishMode) + { + var publishDirectory = _workingDirectory.SubDirectory("publish"); + var fodyWeaversXml = _workingDirectory.File("FodyWeavers.xml"); + + var outputDirectory = publishDirectory.SubDirectory(publishMode.ToString()); + + File.WriteAllText(fodyWeaversXml.FullName, publishMode == PublishMode.SingleFile && IsDesktop ? "" : ""); + + var publishArgs = new[] { + "publish", + "--no-restore", + "--configuration", "Release", + "--output", outputDirectory.FullName, + $"-p:TargetFramework={TargetFramework}" + }; + var publishSingleFile = $"-p:PublishSingleFile={publishMode is PublishMode.SingleFile or PublishMode.SelfContained}"; + var selfContained = $"-p:SelfContained={publishMode is PublishMode.SelfContained}"; + await RunDotnetAsync(_workingDirectory, IsDesktop ? publishArgs : publishArgs.Append(publishSingleFile).Append(selfContained).ToArray()); + + var executableFileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "TestApp.exe" : "TestApp"; + var executableFile = new FileInfo(Path.Combine(outputDirectory.FullName, executableFileName)); + executableFile.Exists.Should().BeTrue(); + var dlls = executableFile.Directory!.EnumerateFiles("*.dll"); + if (publishMode == PublishMode.Standard) + { + dlls.Should().NotBeEmpty(because: $"the test app was _not_ published as single-file ({publishMode})"); + } + else + { + dlls.Should().BeEmpty(because: $"the test app was published as single-file ({publishMode})"); + executableFile.Directory.EnumerateFiles().Should().ContainSingle().Which.FullName.Should().Be(executableFile.FullName); + } + + _executables[publishMode] = executableFile; + } + async Task RunDotnetAsync(DirectoryInfo workingDirectory, params string[] arguments) { _messageSink.OnMessage(new DiagnosticMessage($"cd {workingDirectory}")); From 895f83d3bbdd7440e909bef395adbd8b3cd86e10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Thu, 6 Apr 2023 21:41:12 +0200 Subject: [PATCH 53/64] Mark integration tests as such with [Trait("Category", "Integration")] --- .../PublishSingleFileTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs b/test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs index 57f82e52..2197e78c 100644 --- a/test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs @@ -9,6 +9,7 @@ namespace Serilog.Settings.Configuration.Tests; +[Trait("Category", "Integration")] public sealed class PublishSingleFileTests : IDisposable, IClassFixture { readonly ITestOutputHelper _outputHelper; From fb1edbdb4a30d14c339811817e5762d03be5e82e Mon Sep 17 00:00:00 2001 From: Alvaro F <87340855+DevAlvaroF@users.noreply.github.com> Date: Thu, 20 Apr 2023 23:33:57 +0200 Subject: [PATCH 54/64] Added missing closing bracket on Complex parameter value binding summary --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index eb640526..f8b8d860 100644 --- a/README.md +++ b/README.md @@ -328,6 +328,7 @@ If the parameter value is not a discrete value, it will try to find a best match // `type` (or $type) is optional, must be specified for abstract declared parameter types "type": "Serilog.Templates.ExpressionTemplate, Serilog.Expressions", "template": "[{@t:HH:mm:ss} {@l:u3} {Coalesce(SourceContext, '')}] {@m}\n{@x}" + } } } ``` From 31451b459115d05299ba87919ebc80fd660b9d7e Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 3 May 2023 16:24:43 +1000 Subject: [PATCH 55/64] v7.0 - pin to MEC v7, including matching target frameworks --- sample/Sample/CustomFilter.cs | 21 ++++ sample/Sample/CustomPolicy.cs | 24 ++++ sample/Sample/LoginData.cs | 8 ++ sample/Sample/Program.cs | 106 +++++------------- sample/Sample/Sample.csproj | 5 +- ...figurationLoggerConfigurationExtensions.cs | 6 +- .../Serilog.Settings.Configuration.csproj | 18 ++- .../Configuration/ConfigurationReader.cs | 21 ++-- .../Configuration/ObjectArgumentValue.cs | 2 +- .../ConfigurationReaderTests.cs | 8 +- ...erilog.Settings.Configuration.Tests.csproj | 4 +- .../Support/ConfigurationReaderTestHelpers.cs | 4 +- .../Support/Extensions.cs | 2 +- .../Support/JsonStringConfigSource.cs | 4 +- .../Support/ReloadableConfigurationSource.cs | 8 +- 15 files changed, 125 insertions(+), 116 deletions(-) create mode 100644 sample/Sample/CustomFilter.cs create mode 100644 sample/Sample/CustomPolicy.cs create mode 100644 sample/Sample/LoginData.cs diff --git a/sample/Sample/CustomFilter.cs b/sample/Sample/CustomFilter.cs new file mode 100644 index 00000000..99083c61 --- /dev/null +++ b/sample/Sample/CustomFilter.cs @@ -0,0 +1,21 @@ +using Serilog.Core; +using Serilog.Events; + +namespace Sample; + +// The filter syntax in the sample configuration file is +// processed by the Serilog.Filters.Expressions package. +public class CustomFilter : ILogEventFilter +{ + readonly LogEventLevel _levelFilter; + + public CustomFilter(LogEventLevel levelFilter = LogEventLevel.Information) + { + _levelFilter = levelFilter; + } + + public bool IsEnabled(LogEvent logEvent) + { + return logEvent.Level >= _levelFilter; + } +} diff --git a/sample/Sample/CustomPolicy.cs b/sample/Sample/CustomPolicy.cs new file mode 100644 index 00000000..56e1b2ae --- /dev/null +++ b/sample/Sample/CustomPolicy.cs @@ -0,0 +1,24 @@ +using System.Diagnostics.CodeAnalysis; +using Serilog.Core; +using Serilog.Events; + +namespace Sample; + +public class CustomPolicy : IDestructuringPolicy +{ + public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, [NotNullWhen(true)] out LogEventPropertyValue? result) + { + result = null; + + if (value is LoginData loginData) + { + result = new StructureValue( + new List + { + new("Username", new ScalarValue(loginData.Username)) + }); + } + + return (result != null); + } +} diff --git a/sample/Sample/LoginData.cs b/sample/Sample/LoginData.cs new file mode 100644 index 00000000..2d517a74 --- /dev/null +++ b/sample/Sample/LoginData.cs @@ -0,0 +1,8 @@ +namespace Sample; + +public class LoginData +{ + public string? Username; + // ReSharper disable once NotAccessedField.Global + public string? Password; +} diff --git a/sample/Sample/Program.cs b/sample/Sample/Program.cs index ae2422c1..8440c57f 100644 --- a/sample/Sample/Program.cs +++ b/sample/Sample/Program.cs @@ -1,98 +1,46 @@ using Microsoft.Extensions.Configuration; - +using Sample; using Serilog; using Serilog.Core; -using Serilog.Events; using Serilog.Debugging; // ReSharper disable UnusedType.Global -namespace Sample; - -public class Program -{ - public static void Main(string[] args) - { - SelfLog.Enable(Console.Error); - - Thread.CurrentThread.Name = "Main thread"; - - var configuration = new ConfigurationBuilder() - .SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile(path: "appsettings.json", optional: false, reloadOnChange: true) - .Build(); - - var logger = new LoggerConfiguration() - .ReadFrom.Configuration(configuration) - .CreateLogger(); - - logger.Information("Args: {Args}", args); - - do - { - logger.ForContext().Information("Hello, world!"); - logger.ForContext().Error("Hello, world!"); - logger.ForContext(Constants.SourceContextPropertyName, "Microsoft").Warning("Hello, world!"); - logger.ForContext(Constants.SourceContextPropertyName, "Microsoft").Error("Hello, world!"); - logger.ForContext(Constants.SourceContextPropertyName, "MyApp.Something.Tricky").Verbose("Hello, world!"); +SelfLog.Enable(Console.Error); - logger.Information("Destructure with max object nesting depth:\n{@NestedObject}", - new { FiveDeep = new { Two = new { Three = new { Four = new { Five = "the end" } } } } }); +Thread.CurrentThread.Name = "Main thread"; - logger.Information("Destructure with max string length:\n{@LongString}", - new { TwentyChars = "0123456789abcdefghij" }); +var configuration = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile(path: "appsettings.json", optional: false, reloadOnChange: true) + .Build(); - logger.Information("Destructure with max collection count:\n{@BigData}", - new { TenItems = new[] { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" } }); +var logger = new LoggerConfiguration() + .ReadFrom.Configuration(configuration) + .CreateLogger(); - logger.Information("Destructure with policy to strip password:\n{@LoginData}", - new LoginData { Username = "BGates", Password = "isityearoflinuxyet" }); +logger.Information("Args: {Args}", args); - 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 +do { - readonly LogEventLevel _levelFilter; + logger.ForContext().Information("Hello, world!"); + logger.ForContext().Error("Hello, world!"); + logger.ForContext(Constants.SourceContextPropertyName, "Microsoft").Warning("Hello, world!"); + logger.ForContext(Constants.SourceContextPropertyName, "Microsoft").Error("Hello, world!"); + logger.ForContext(Constants.SourceContextPropertyName, "MyApp.Something.Tricky").Verbose("Hello, world!"); - public CustomFilter(LogEventLevel levelFilter = LogEventLevel.Information) - { - _levelFilter = levelFilter; - } + logger.Information("Destructure with max object nesting depth:\n{@NestedObject}", + new { FiveDeep = new { Two = new { Three = new { Four = new { Five = "the end" } } } } }); - public bool IsEnabled(LogEvent logEvent) - { - return logEvent.Level >= _levelFilter; - } -} + logger.Information("Destructure with max string length:\n{@LongString}", + new { TwentyChars = "0123456789abcdefghij" }); -public class LoginData -{ - public string? Username; - // ReSharper disable once NotAccessedField.Global - public string? Password; -} - -public class CustomPolicy : IDestructuringPolicy -{ - public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, out LogEventPropertyValue? result) - { - result = null; + logger.Information("Destructure with max collection count:\n{@BigData}", + new { TenItems = new[] { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" } }); - if (value is LoginData loginData) - { - result = new StructureValue( - new List - { - new("Username", new ScalarValue(loginData.Username)) - }); - } + logger.Information("Destructure with policy to strip password:\n{@LoginData}", + new LoginData { Username = "BGates", Password = "isityearoflinuxyet" }); - return (result != null); - } + Console.WriteLine("\nPress \"q\" to quit, or any other key to run again.\n"); } +while (!args.Contains("--run-once") && (Console.ReadKey().KeyChar != 'q')); diff --git a/sample/Sample/Sample.csproj b/sample/Sample/Sample.csproj index 25aedf5b..6357b2be 100644 --- a/sample/Sample/Sample.csproj +++ b/sample/Sample/Sample.csproj @@ -1,7 +1,7 @@  - net6.0;netcoreapp3.1;net462 + net6.0;net7.0;net462 Exe @@ -14,7 +14,7 @@ - + @@ -22,6 +22,7 @@ + diff --git a/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs b/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs index 2282b1bc..0a2bb7d3 100644 --- a/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs +++ b/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs @@ -228,20 +228,20 @@ public static LoggerConfiguration Configuration( static ConfigurationReader GetConfigurationReader(IConfiguration configuration, ConfigurationReaderOptions readerOptions, DependencyContext? dependencyContext) { var assemblyFinder = dependencyContext == null ? AssemblyFinder.Auto() : AssemblyFinder.ForDependencyContext(dependencyContext); - var section = string.IsNullOrWhiteSpace(readerOptions.SectionName) ? configuration : configuration.GetSection(readerOptions.SectionName); + var section = string.IsNullOrWhiteSpace(readerOptions.SectionName) ? configuration : configuration.GetSection(readerOptions.SectionName!); return new ConfigurationReader(section, assemblyFinder, readerOptions, configuration); } static ConfigurationReader GetConfigurationReader(IConfiguration configuration, ConfigurationReaderOptions readerOptions, ConfigurationAssemblySource source) { var assemblyFinder = AssemblyFinder.ForSource(source); - var section = string.IsNullOrWhiteSpace(readerOptions.SectionName) ? configuration : configuration.GetSection(readerOptions.SectionName); + var section = string.IsNullOrWhiteSpace(readerOptions.SectionName) ? configuration : configuration.GetSection(readerOptions.SectionName!); return new ConfigurationReader(section, assemblyFinder, readerOptions, configuration); } static ConfigurationReader GetConfigurationReader(IConfiguration configuration, ConfigurationReaderOptions readerOptions, IReadOnlyCollection assemblies) { - var section = string.IsNullOrWhiteSpace(readerOptions.SectionName) ? configuration : configuration.GetSection(readerOptions.SectionName); + var section = string.IsNullOrWhiteSpace(readerOptions.SectionName) ? configuration : configuration.GetSection(readerOptions.SectionName!); return new ConfigurationReader(section, assemblies, new ResolutionContext(configuration, readerOptions)); } } diff --git a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj index bb1c1928..ed870186 100644 --- a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj +++ b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj @@ -2,9 +2,12 @@ Microsoft.Extensions.Configuration (appsettings.json) support for Serilog. - 4.0.0 + + 7.0.0 Serilog Contributors - netstandard2.0;net461 + + net462;netstandard2.0;net6.0;net7.0 true Serilog.Settings.Configuration serilog;json @@ -23,11 +26,14 @@ - - - - + + + + + + + diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 46eca3c9..931391cf 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -129,7 +129,7 @@ void ProcessLevelSwitchDeclarations() } else { - var initialLevel = ParseLogEventLevel(switchInitialLevel); + var initialLevel = ParseLogEventLevel(switchInitialLevel!); newSwitch = new LoggingLevelSwitch(initialLevel); } @@ -171,9 +171,9 @@ void ApplyMinimumLevel(LoggerConfiguration loggerConfiguration) _resolutionContext.ReaderOptions.OnLevelSwitchCreated?.Invoke(overridePrefix, levelSwitch); }); } - else + else if (!string.IsNullOrEmpty(overridenLevelOrSwitch)) { - var overrideSwitch = _resolutionContext.LookUpLevelSwitchByName(overridenLevelOrSwitch); + var overrideSwitch = _resolutionContext.LookUpLevelSwitchByName(overridenLevelOrSwitch!); // not calling ApplyMinimumLevel local function because here we have a reference to a LogLevelSwitch already loggerConfiguration.MinimumLevel.Override(overridePrefix, overrideSwitch); } @@ -181,7 +181,7 @@ void ApplyMinimumLevel(LoggerConfiguration loggerConfiguration) void ApplyMinimumLevelConfiguration(IConfigurationSection directive, Action applyConfigAction) { - var minimumLevel = ParseLogEventLevel(directive.Value); + var minimumLevel = ParseLogEventLevel(directive.Value!); var levelSwitch = new LoggingLevelSwitch(minimumLevel); applyConfigAction(loggerConfiguration.MinimumLevel, levelSwitch); @@ -293,7 +293,8 @@ void ApplyEnrichment(LoggerConfiguration loggerConfiguration) { foreach (var enrichPropertyDirective in propertiesDirective.GetChildren()) { - loggerConfiguration.Enrich.WithProperty(enrichPropertyDirective.Key, enrichPropertyDirective.Value); + // Null is an acceptable value here; annotations on Serilog need updating. + loggerConfiguration.Enrich.WithProperty(enrichPropertyDirective.Key, enrichPropertyDirective.Value!); } } } @@ -359,7 +360,7 @@ internal static IConfigurationArgumentValue GetArgumentValue(IConfigurationSecti static IReadOnlyCollection LoadConfigurationAssemblies(IConfiguration section, AssemblyFinder assemblyFinder) { var serilogAssembly = typeof(ILogger).Assembly; - var assemblies = new Dictionary { [serilogAssembly.FullName] = serilogAssembly }; + var assemblies = new Dictionary { [serilogAssembly.FullName!] = serilogAssembly }; var usingSection = section.GetSection("Using"); if (usingSection.GetChildren().Any()) @@ -371,16 +372,16 @@ static IReadOnlyCollection LoadConfigurationAssemblies(IConfiguration "A zero-length or whitespace assembly name was supplied to a Serilog.Using configuration statement."); var assembly = Assembly.Load(new AssemblyName(simpleName)); - if (!assemblies.ContainsKey(assembly.FullName)) - assemblies.Add(assembly.FullName, assembly); + if (!assemblies.ContainsKey(assembly.FullName!)) + assemblies.Add(assembly.FullName!, assembly); } } foreach (var assemblyName in assemblyFinder.FindAssembliesContainingName("serilog")) { var assumed = Assembly.Load(assemblyName); - if (assumed != null && !assemblies.ContainsKey(assumed.FullName)) - assemblies.Add(assumed.FullName, assumed); + if (assumed != null && !assemblies.ContainsKey(assumed.FullName!)) + assemblies.Add(assumed.FullName!, assumed); } return assemblies.Values.ToList().AsReadOnly(); diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs index 3a154573..151ce5b5 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs @@ -115,7 +115,7 @@ internal static bool TryBuildCtorExpression( var type = typeDirective switch { - not null => Type.GetType(section.GetValue(typeDirective), throwOnError: false), + not null => Type.GetType(section.GetValue(typeDirective)!, throwOnError: false), null => parameterType, }; diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs index 56ae8ce7..d235e8f2 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs @@ -189,10 +189,10 @@ public void MethodsAreSelectedBasedOnCountOfMatchedArgumentsAndThenStringType() { new object[] { GetConfigRoot(appsettingsJsonLevel: minimumLevelFlatTemplate.Format(LogEventLevel.Error)), LogEventLevel.Error }, new object[] { GetConfigRoot(appsettingsDevelopmentJsonLevel: minimumLevelFlatTemplate.Format(LogEventLevel.Error)), LogEventLevel.Error }, - new object[] { GetConfigRoot(envVariables: new Dictionary() {{minimumLevelFlatKey, LogEventLevel.Error.ToString()}}), LogEventLevel.Error}, + new object[] { GetConfigRoot(envVariables: new Dictionary {{minimumLevelFlatKey, LogEventLevel.Error.ToString()}}), LogEventLevel.Error}, new object[] { GetConfigRoot( appsettingsJsonLevel: minimumLevelFlatTemplate.Format(LogEventLevel.Debug), - envVariables: new Dictionary() {{minimumLevelFlatKey, LogEventLevel.Error.ToString()}}), + envVariables: new Dictionary {{minimumLevelFlatKey, LogEventLevel.Error.ToString()}}), LogEventLevel.Error } }; @@ -214,7 +214,7 @@ public void FlatMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, new object[] { GetConfigRoot(appsettingsJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error)), LogEventLevel.Error }, new object[] { GetConfigRoot(appsettingsJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error.ToString().ToUpper())), LogEventLevel.Error }, new object[] { GetConfigRoot(appsettingsDevelopmentJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error)), LogEventLevel.Error }, - new object[] { GetConfigRoot(envVariables: new Dictionary(){{minimumLevelObjectKey, LogEventLevel.Error.ToString() } }), LogEventLevel.Error }, + new object[] { GetConfigRoot(envVariables: new Dictionary{{minimumLevelObjectKey, LogEventLevel.Error.ToString() } }), LogEventLevel.Error }, new object[] { GetConfigRoot( appsettingsJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error), appsettingsDevelopmentJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Debug)), @@ -254,7 +254,7 @@ public void ObjectMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot roo new object[] { GetConfigRoot( - envVariables: new Dictionary() + envVariables: new Dictionary() { {minimumLevelObjectKey, LogEventLevel.Error.ToString()}, {minimumLevelFlatKey, LogEventLevel.Debug.ToString()} 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 246d9765..d75874e7 100644 --- a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj +++ b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj @@ -2,7 +2,7 @@ net48 - $(TargetFrameworks);net7.0;netcoreapp3.1 + $(TargetFrameworks);net7.0 @@ -17,7 +17,7 @@ - + diff --git a/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs b/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs index 55bb3603..ae76b43f 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/ConfigurationReaderTestHelpers.cs @@ -49,13 +49,13 @@ public static void AssertLogEventLevels(LoggerConfiguration loggerConfig, LogEve public static IConfigurationRoot GetConfigRoot( string? appsettingsJsonLevel = null, string? appsettingsDevelopmentJsonLevel = null, - Dictionary? envVariables = null) + Dictionary? envVariables = null) { var configBuilder = new ConfigurationBuilder(); configBuilder.AddJsonString(appsettingsJsonLevel ?? "{}"); configBuilder.AddJsonString(appsettingsDevelopmentJsonLevel ?? "{}"); - configBuilder.Add(new ReloadableConfigurationSource(envVariables ?? new Dictionary())); + configBuilder.Add(new ReloadableConfigurationSource(envVariables ?? new Dictionary())); return configBuilder.Build(); } diff --git a/test/Serilog.Settings.Configuration.Tests/Support/Extensions.cs b/test/Serilog.Settings.Configuration.Tests/Support/Extensions.cs index 2b7c7f44..3bf62ffa 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/Extensions.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/Extensions.cs @@ -4,7 +4,7 @@ namespace Serilog.Settings.Configuration.Tests.Support; public static class Extensions { - public static object LiteralValue(this LogEventPropertyValue @this) + public static object? LiteralValue(this LogEventPropertyValue @this) { return ((ScalarValue)@this).Value; } diff --git a/test/Serilog.Settings.Configuration.Tests/Support/JsonStringConfigSource.cs b/test/Serilog.Settings.Configuration.Tests/Support/JsonStringConfigSource.cs index 923656c5..eb26e60d 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/JsonStringConfigSource.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/JsonStringConfigSource.cs @@ -22,7 +22,7 @@ public static IConfigurationSection LoadSection(string json, string section) return new ConfigurationBuilder().Add(new JsonStringConfigSource(json)).Build().GetSection(section); } - public static IDictionary LoadData(string json) + public static IDictionary LoadData(string json) { var provider = new JsonStringConfigProvider(json); provider.Load(); @@ -38,7 +38,7 @@ public JsonStringConfigProvider(string json) : base(new JsonConfigurationSource _json = json; } - public new IDictionary Data => base.Data; + public new IDictionary Data => base.Data; public override void Load() { diff --git a/test/Serilog.Settings.Configuration.Tests/Support/ReloadableConfigurationSource.cs b/test/Serilog.Settings.Configuration.Tests/Support/ReloadableConfigurationSource.cs index 22f2cb05..4e8b8011 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/ReloadableConfigurationSource.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/ReloadableConfigurationSource.cs @@ -5,9 +5,9 @@ namespace Serilog.Settings.Configuration.Tests.Support; class ReloadableConfigurationSource : IConfigurationSource { readonly ReloadableConfigurationProvider _configProvider; - readonly IDictionary _source; + readonly IDictionary _source; - public ReloadableConfigurationSource(IDictionary source) + public ReloadableConfigurationSource(IDictionary source) { _source = source; _configProvider = new ReloadableConfigurationProvider(source); @@ -21,9 +21,9 @@ public ReloadableConfigurationSource(IDictionary source) class ReloadableConfigurationProvider : ConfigurationProvider { - readonly IDictionary _source; + readonly IDictionary _source; - public ReloadableConfigurationProvider(IDictionary source) + public ReloadableConfigurationProvider(IDictionary source) { _source = source; } From 6be97a718ebb6511f81c99468148c032b1cdab03 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 3 May 2023 18:53:12 +1000 Subject: [PATCH 56/64] Quick README update to note the versioning policy --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index f8b8d860..b1ebd24b 100644 --- a/README.md +++ b/README.md @@ -428,3 +428,7 @@ In order to make auto-discovery of configuration assemblies work, modify Functio ``` + +### Versioning + +This package tracks the versioning and target framework support of its [_Microsoft.Extensions.Configuration_](https://nuget.org/packages/Microsoft.Extensions.Configuration) dependency. From e87d4e896b596c06387ae6b940dafc4a219ef309 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Fri, 5 May 2023 10:20:45 +1000 Subject: [PATCH 57/64] Fix bad merge, drop obsolete target --- .../Settings/Configuration/ConfigurationReader.cs | 2 +- .../Serilog.Settings.Configuration.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 9ce2dc78..1f8ff5ad 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -396,7 +396,7 @@ This is most likely because the application is published as single-file. throw new InvalidOperationException(message); } - return assemblies; + return assemblies.Values; } void CallConfigurationMethods(ILookup> methods, IReadOnlyCollection configurationMethods, object receiver) 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 1dbea4e2..f1915c1c 100644 --- a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj +++ b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.Tests.csproj @@ -2,7 +2,7 @@ net48 - $(TargetFrameworks);net7.0;net6.0;netcoreapp3.1 + $(TargetFrameworks);net7.0;net6.0 From b98e589403da7dc87b7b24b4a2b67679e57fbdab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Fri, 5 May 2023 10:50:28 +0200 Subject: [PATCH 58/64] Simplify loading assemblies Use a HashSet instead of a Dictionary keyed by the assembly full name. If an assembly is loaded twice with the same AssemblyName it will be the same instance so a HashSet (without even a custom IEqualityComparer) is the perfect solution. Also, Assembly.Load can throw many exceptions but won't return null. This was initially introduced in 74402f836148f746f05a06f87cd8b15fee387155 but merged incorrectly in ac1b8c863307ceb598fbd6457ce9714ff2d1bde3. --- .../Settings/Configuration/ConfigurationReader.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 1f8ff5ad..68400737 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -360,7 +360,7 @@ internal static IConfigurationArgumentValue GetArgumentValue(IConfigurationSecti static IReadOnlyCollection LoadConfigurationAssemblies(IConfiguration section, AssemblyFinder assemblyFinder) { var serilogAssembly = typeof(ILogger).Assembly; - var assemblies = new Dictionary { [serilogAssembly.FullName!] = serilogAssembly }; + var assemblies = new HashSet { serilogAssembly }; var usingSection = section.GetSection("Using"); if (usingSection.GetChildren().Any()) @@ -372,16 +372,14 @@ static IReadOnlyCollection LoadConfigurationAssemblies(IConfiguration $"A zero-length or whitespace assembly name was supplied to a {usingSection.Path} configuration statement."); var assembly = Assembly.Load(new AssemblyName(simpleName)); - if (!assemblies.ContainsKey(assembly.FullName!)) - assemblies.Add(assembly.FullName!, assembly); + assemblies.Add(assembly); } } foreach (var assemblyName in assemblyFinder.FindAssembliesContainingName("serilog")) { var assumed = Assembly.Load(assemblyName); - if (assumed != null && !assemblies.ContainsKey(assumed.FullName!)) - assemblies.Add(assumed.FullName!, assumed); + assemblies.Add(assumed); } if (assemblies.Count == 1) @@ -396,7 +394,7 @@ This is most likely because the application is published as single-file. throw new InvalidOperationException(message); } - return assemblies.Values; + return assemblies; } void CallConfigurationMethods(ILookup> methods, IReadOnlyCollection configurationMethods, object receiver) From d22b9fe71329974dd308cce7614194475240791b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Wed, 15 Mar 2023 01:52:15 +0100 Subject: [PATCH 59/64] Add a callback on the reader options to expose the log filter switches Just like it was done for log level switches in #352. --- README.md | 16 ++++++++++ .../Configuration/ConfigurationReader.cs | 3 +- .../ConfigurationReaderOptions.cs | 6 ++++ .../Configuration/ILoggingFilterSwitch.cs | 16 ++++++++++ .../Configuration/LoggingFilterSwitchProxy.cs | 2 +- .../ConfigurationSettingsTests.cs | 31 +++++++++++++++++++ ...erilog.Settings.Configuration.approved.txt | 5 +++ 7 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 src/Serilog.Settings.Configuration/Settings/Configuration/ILoggingFilterSwitch.cs diff --git a/README.md b/README.md index b1ebd24b..f2f26300 100644 --- a/README.md +++ b/README.md @@ -275,6 +275,22 @@ Using this package you can also declare `LoggingFilterSwitch`-es in custom secti Level updates to switches are also respected for a dynamic update. +Since version 4.0.0, filter switches are exposed through a callback on the reader options so that a reference can be kept: + +```csharp +var filterSwitches = new Dictionary(); +var options = new ConfigurationReaderOptions +{ + OnFilterSwitchCreated = (switchName, filterSwitch) => filterSwitches[switchName] = filterSwitch +}; + +var logger = new LoggerConfiguration() + .ReadFrom.Configuration(configuration, options) + .CreateLogger(); + +ILoggingFilterSwitch filterSwitch = filterSwitches["$filterSwitch"]; +``` + ### Nested configuration sections Some Serilog packages require a reference to a logger configuration object. The sample program in this project illustrates this with the following entry configuring the _[Serilog.Sinks.Async](https://github.com/serilog/serilog-sinks-async)_ package to wrap the _[Serilog.Sinks.File](https://github.com/serilog/serilog-sinks-file)_ package. The `configure` parameter references the File sink configuration: diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 68400737..67459d2a 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -75,7 +75,8 @@ void ProcessFilterSwitchDeclarations() SetFilterSwitch(throwOnError: true); SubscribeToFilterExpressionChanges(); - _resolutionContext.AddFilterSwitch(switchName, filterSwitch); + var referenceName = _resolutionContext.AddFilterSwitch(switchName, filterSwitch); + _resolutionContext.ReaderOptions.OnFilterSwitchCreated?.Invoke(referenceName, filterSwitch); void SubscribeToFilterExpressionChanges() { diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs index 7eedf709..d0696a63 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs @@ -77,6 +77,12 @@ public ConfigurationReaderOptions() : this(dependencyContext: null) /// public Action? OnLevelSwitchCreated { get; init; } + /// + /// Called when a log filter switch is created while reading the Serilog:FilterSwitches section of the configuration. + /// The switch name includes the leading $ character. + /// + public Action? OnFilterSwitchCreated { get; init; } + internal Assembly[]? Assemblies { get; } internal DependencyContext? DependencyContext { get; } internal ConfigurationAssemblySource? ConfigurationAssemblySource { get; } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ILoggingFilterSwitch.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ILoggingFilterSwitch.cs new file mode 100644 index 00000000..321295bb --- /dev/null +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ILoggingFilterSwitch.cs @@ -0,0 +1,16 @@ +namespace Serilog.Settings.Configuration; + +/// +/// A log event filter that can be modified at runtime. +/// +/// +/// Under the hood, the logging filter switch is either a Serilog.Expressions.LoggingFilterSwitch or a Serilog.Filters.Expressions.LoggingFilterSwitch instance. +/// +public interface ILoggingFilterSwitch +{ + /// + /// A filter expression against which log events will be tested. + /// Only expressions that evaluate to true are included by the filter. A null expression will accept all events. + /// + public string? Expression { get; set; } +} diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/LoggingFilterSwitchProxy.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/LoggingFilterSwitchProxy.cs index 3b4997dd..8da70d07 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/LoggingFilterSwitchProxy.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/LoggingFilterSwitchProxy.cs @@ -1,6 +1,6 @@ namespace Serilog.Settings.Configuration; -class LoggingFilterSwitchProxy +class LoggingFilterSwitchProxy : ILoggingFilterSwitch { readonly Action _setProxy; readonly Func _getProxy; diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 942fad93..23f41e25 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -1547,4 +1547,35 @@ public void TestLogLevelSwitchesCallback(string switchName) var systemThreading = Assert.Contains("System.Threading", switches); Assert.Equal(LogEventLevel.Debug, systemThreading.MinimumLevel); } + + [Theory] + [InlineData("$switch1", "$switch2")] + [InlineData("$switch1", "switch2")] + [InlineData("switch1", "$switch2")] + [InlineData("switch1", "switch2")] + public void TestLogFilterSwitchesCallback(string switch1Name, string switch2Name) + { + var json = $$""" + { + 'Serilog': { + 'FilterSwitches': { + '{{switch1Name}}': 'Prop = 1', + '{{switch2Name}}': 'Prop = 2' + } + } + } + """; + + IDictionary switches = new Dictionary(); + var readerOptions = new ConfigurationReaderOptions { OnFilterSwitchCreated = (name, filterSwitch) => switches[name] = filterSwitch }; + ConfigFromJson(json, options: readerOptions); + + Assert.Equal(2, switches.Count); + + var switch1 = Assert.Contains("$switch1", switches); + Assert.Equal("Prop = 1", switch1.Expression); + + var switch2 = Assert.Contains("$switch2", switches); + Assert.Equal("Prop = 2", switch2.Expression); + } } diff --git a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt index 4c7fc7cf..ca43b43a 100644 --- a/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt +++ b/test/Serilog.Settings.Configuration.Tests/Serilog.Settings.Configuration.approved.txt @@ -46,7 +46,12 @@ namespace Serilog.Settings.Configuration public bool AllowInternalMethods { get; init; } public bool AllowInternalTypes { get; init; } public System.IFormatProvider? FormatProvider { get; init; } + public System.Action? OnFilterSwitchCreated { get; init; } public System.Action? OnLevelSwitchCreated { get; init; } public string? SectionName { get; init; } } + public interface ILoggingFilterSwitch + { + string? Expression { get; set; } + } } \ No newline at end of file From 7f5c27fcc3f240aae62d8be34deea743278712ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Mon, 8 May 2023 08:34:37 +0200 Subject: [PATCH 60/64] Do not force filter switch names with a leading $ in OnFilterSwitchCreated callback --- README.md | 2 +- .../Configuration/ConfigurationReader.cs | 4 ++-- .../ConfigurationReaderOptions.cs | 1 - .../Configuration/ResolutionContext.cs | 3 +-- .../ConfigurationSettingsTests.cs | 21 ++++++++----------- 5 files changed, 13 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index f2f26300..5ea2c23d 100644 --- a/README.md +++ b/README.md @@ -288,7 +288,7 @@ var logger = new LoggerConfiguration() .ReadFrom.Configuration(configuration, options) .CreateLogger(); -ILoggingFilterSwitch filterSwitch = filterSwitches["$filterSwitch"]; +ILoggingFilterSwitch filterSwitch = filterSwitches["filterSwitch"]; ``` ### Nested configuration sections diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 67459d2a..2a6c3170 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -75,8 +75,8 @@ void ProcessFilterSwitchDeclarations() SetFilterSwitch(throwOnError: true); SubscribeToFilterExpressionChanges(); - var referenceName = _resolutionContext.AddFilterSwitch(switchName, filterSwitch); - _resolutionContext.ReaderOptions.OnFilterSwitchCreated?.Invoke(referenceName, filterSwitch); + _resolutionContext.AddFilterSwitch(switchName, filterSwitch); + _resolutionContext.ReaderOptions.OnFilterSwitchCreated?.Invoke(switchName, filterSwitch); void SubscribeToFilterExpressionChanges() { diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs index d0696a63..4510aa9f 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReaderOptions.cs @@ -79,7 +79,6 @@ public ConfigurationReaderOptions() : this(dependencyContext: null) /// /// Called when a log filter switch is created while reading the Serilog:FilterSwitches section of the configuration. - /// The switch name includes the leading $ character. /// public Action? OnFilterSwitchCreated { get; init; } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs index d0026205..61d40d86 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs @@ -62,13 +62,12 @@ public string AddLevelSwitch(string levelSwitchName, LoggingLevelSwitch levelSwi return referenceName; } - public string AddFilterSwitch(string filterSwitchName, LoggingFilterSwitchProxy filterSwitch) + public void AddFilterSwitch(string filterSwitchName, LoggingFilterSwitchProxy filterSwitch) { if (filterSwitchName == null) throw new ArgumentNullException(nameof(filterSwitchName)); if (filterSwitch == null) throw new ArgumentNullException(nameof(filterSwitch)); var referenceName = ToSwitchReference(filterSwitchName); _declaredFilterSwitches[referenceName] = filterSwitch; - return referenceName; } string ToSwitchReference(string switchName) diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 23f41e25..4916f352 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -1548,19 +1548,16 @@ public void TestLogLevelSwitchesCallback(string switchName) Assert.Equal(LogEventLevel.Debug, systemThreading.MinimumLevel); } - [Theory] - [InlineData("$switch1", "$switch2")] - [InlineData("$switch1", "switch2")] - [InlineData("switch1", "$switch2")] - [InlineData("switch1", "switch2")] - public void TestLogFilterSwitchesCallback(string switch1Name, string switch2Name) + [Fact] + public void TestLogFilterSwitchesCallback() { - var json = $$""" + // language=json + var json = """ { - 'Serilog': { - 'FilterSwitches': { - '{{switch1Name}}': 'Prop = 1', - '{{switch2Name}}': 'Prop = 2' + "Serilog": { + "FilterSwitches": { + "switch1": "Prop = 1", + "$switch2": "Prop = 2" } } } @@ -1572,7 +1569,7 @@ public void TestLogFilterSwitchesCallback(string switch1Name, string switch2Name Assert.Equal(2, switches.Count); - var switch1 = Assert.Contains("$switch1", switches); + var switch1 = Assert.Contains("switch1", switches); Assert.Equal("Prop = 1", switch1.Expression); var switch2 = Assert.Contains("$switch2", switches); From 4ec33650ecbfc9a970ca30b339716840a440eb18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Mon, 8 May 2023 09:37:49 +0200 Subject: [PATCH 61/64] Fix tests on .NET Framework 4.8 Was getting this exception because of assembly version mismatch: > System.IO.FileLoadException: Could not load file or assembly 'Microsoft.Extensions.Configuration.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040) --- test/TestApp/Program.cs | 2 +- test/TestApp/TestApp.csproj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/TestApp/Program.cs b/test/TestApp/Program.cs index 56a3235b..b376df4b 100644 --- a/test/TestApp/Program.cs +++ b/test/TestApp/Program.cs @@ -22,7 +22,7 @@ Thread.CurrentThread.Name = "Main thread"; -var configurationValues = new Dictionary +var configurationValues = new Dictionary { ["Serilog:Enrich:0"] = "WithThreadName", ["Serilog:WriteTo:0:Name"] = "Console", diff --git a/test/TestApp/TestApp.csproj b/test/TestApp/TestApp.csproj index 88f0ca1f..e60f2b73 100644 --- a/test/TestApp/TestApp.csproj +++ b/test/TestApp/TestApp.csproj @@ -14,14 +14,14 @@ - + - + From 1e370b5cef06efdf3e70961e0e9636f078ed439b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Fri, 5 May 2023 08:42:29 +0200 Subject: [PATCH 62/64] Fix build script Use `throw` to exit when a `dotnet` command fails so that errors are properly reported on AppVeyor. See also #372 --- Build.ps1 | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Build.ps1 b/Build.ps1 index 080f8809..15133822 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -1,10 +1,10 @@ -echo "build: Build started" +Write-Output "build: Build started" Push-Location $PSScriptRoot if(Test-Path .\artifacts) { - echo "build: Cleaning .\artifacts" - Remove-Item .\artifacts -Force -Recurse + Write-Output "build: Cleaning .\artifacts" + Remove-Item .\artifacts -Force -Recurse } $branch = @{ $true = $env:APPVEYOR_REPO_BRANCH; $false = $(git symbolic-ref --short -q HEAD) }[$env:APPVEYOR_REPO_BRANCH -ne $NULL]; @@ -13,12 +13,12 @@ $suffix = @{ $true = ""; $false = "$($branch.Substring(0, [math]::Min(10,$branch $commitHash = $(git rev-parse --short HEAD) $buildSuffix = @{ $true = "$($suffix)-$($commitHash)"; $false = "$($branch)-$($commitHash)" }[$suffix -ne ""] -echo "build: Package version suffix is $suffix" -echo "build: Build version suffix is $buildSuffix" +Write-Output "build: Package version suffix is $suffix" +Write-Output "build: Build version suffix is $buildSuffix" & dotnet build --configuration Release --version-suffix=$buildSuffix /p:ContinuousIntegrationBuild=true -if($LASTEXITCODE -ne 0) { exit 1 } +if($LASTEXITCODE -ne 0) { throw 'build failed' } if($suffix) { & dotnet pack src\Serilog.Settings.Configuration --configuration Release --no-build --no-restore -o artifacts --version-suffix=$suffix @@ -26,7 +26,7 @@ if($suffix) { & dotnet pack src\Serilog.Settings.Configuration --configuration Release --no-build --no-restore -o artifacts } -if($LASTEXITCODE -ne 0) { exit 2 } +if($LASTEXITCODE -ne 0) { throw 'pack failed' } Write-Output "build: Testing" @@ -35,4 +35,4 @@ Write-Output "build: Testing" # The _reported_ runtime is wrong but the _actual_ used runtime is correct, see https://github.com/microsoft/vstest/issues/2037#issuecomment-720549173 & dotnet test test\Serilog.Settings.Configuration.Tests\bin\Release\*\Serilog.Settings.Configuration.Tests.dll --parallel -if($LASTEXITCODE -ne 0) { exit 3 } \ No newline at end of file +if($LASTEXITCODE -ne 0) { throw 'unit tests failed' } \ No newline at end of file From 74ba30e657b0f70f8dc4c87352fe32b365668064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Mon, 8 May 2023 18:57:48 +0200 Subject: [PATCH 63/64] Fix tests on .NET Framework 4.8 (really) Was getting this exception because of assembly version mismatch: > System.IO.FileLoadException: Could not load file or assembly 'System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040) Automatically generating binding redirects fixes the issue. --- .../Serilog.Settings.Configuration.Tests/Support/TestApp.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/Serilog.Settings.Configuration.Tests/Support/TestApp.cs b/test/Serilog.Settings.Configuration.Tests/Support/TestApp.cs index 913bbe20..9263a66a 100644 --- a/test/Serilog.Settings.Configuration.Tests/Support/TestApp.cs +++ b/test/Serilog.Settings.Configuration.Tests/Support/TestApp.cs @@ -93,16 +93,18 @@ async Task PublishAsync(PublishMode publishMode) File.WriteAllText(fodyWeaversXml.FullName, publishMode == PublishMode.SingleFile && IsDesktop ? "" : ""); - var publishArgs = new[] { + var publishArgsBase = new[] { "publish", "--no-restore", "--configuration", "Release", "--output", outputDirectory.FullName, $"-p:TargetFramework={TargetFramework}" }; + var autoGenerateBindingRedirects = $"-p:AutoGenerateBindingRedirects={publishMode is PublishMode.Standard}"; var publishSingleFile = $"-p:PublishSingleFile={publishMode is PublishMode.SingleFile or PublishMode.SelfContained}"; var selfContained = $"-p:SelfContained={publishMode is PublishMode.SelfContained}"; - await RunDotnetAsync(_workingDirectory, IsDesktop ? publishArgs : publishArgs.Append(publishSingleFile).Append(selfContained).ToArray()); + var publishArgs = (IsDesktop ? publishArgsBase.Append(autoGenerateBindingRedirects) : publishArgsBase.Append(publishSingleFile).Append(selfContained)).ToArray(); + await RunDotnetAsync(_workingDirectory, publishArgs); var executableFileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "TestApp.exe" : "TestApp"; var executableFile = new FileInfo(Path.Combine(outputDirectory.FullName, executableFileName)); From d7e0dbd7239df3b230a0dd330fefbe19bb6818bf Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 10 May 2023 15:11:05 +1000 Subject: [PATCH 64/64] Update README.md references to 4.0 -> 7.0 --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5ea2c23d..9695b7b7 100644 --- a/README.md +++ b/README.md @@ -186,7 +186,7 @@ You can also declare `LoggingLevelSwitch`-es in custom section and reference the Level updates to switches are also respected for a dynamic update. -Since version 4.0.0, both declared switches (i.e. `Serilog:LevelSwitches` section) and minimum level override switches (i.e. `Serilog:MinimumLevel:Override` section) are exposed through a callback on the reader options so that a reference can be kept: +Since version 7.0.0, both declared switches (i.e. `Serilog:LevelSwitches` section) and minimum level override switches (i.e. `Serilog:MinimumLevel:Override` section) are exposed through a callback on the reader options so that a reference can be kept: ```csharp var allSwitches = new Dictionary(); @@ -275,7 +275,7 @@ Using this package you can also declare `LoggingFilterSwitch`-es in custom secti Level updates to switches are also respected for a dynamic update. -Since version 4.0.0, filter switches are exposed through a callback on the reader options so that a reference can be kept: +Since version 7.0.0, filter switches are exposed through a callback on the reader options so that a reference can be kept: ```csharp var filterSwitches = new Dictionary(); @@ -317,7 +317,7 @@ Some Serilog packages require a reference to a logger configuration object. The 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`, `TimeSpan`, `enum`, arrays and custom collections. -Since version 4.0.0, conversion will use the invariant culture (`CultureInfo.InvariantCulture`) as long as the `ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptions options)` method is used. Obsolete methods use the current culture to preserve backward compatibility. +Since version 7.0.0, conversion will use the invariant culture (`CultureInfo.InvariantCulture`) as long as the `ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptions options)` method is used. Obsolete methods use the current culture to preserve backward compatibility. ### Static member support