From 31451b459115d05299ba87919ebc80fd660b9d7e Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 3 May 2023 16:24:43 +1000 Subject: [PATCH 1/2] 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 2/2] 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.