diff --git a/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs b/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs index 4e24eef..0453c83 100644 --- a/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs +++ b/src/Serilog.Settings.Configuration/ConfigurationLoggerConfigurationExtensions.cs @@ -47,6 +47,28 @@ public static LoggerConfiguration Configuration( IConfiguration configuration, string sectionName, DependencyContext dependencyContext = null) + { + return Configuration(settingConfiguration, configuration, sectionName, out _, dependencyContext); + } + + /// + /// 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. + /// Contains information about the loaded configuration upon return. + /// 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, + out LoadedConfiguration loadedConfiguration, + DependencyContext dependencyContext = null) { if (settingConfiguration == null) throw new ArgumentNullException(nameof(settingConfiguration)); if (configuration == null) throw new ArgumentNullException(nameof(configuration)); @@ -56,11 +78,10 @@ public static LoggerConfiguration Configuration( ? AssemblyFinder.Auto() : AssemblyFinder.ForDependencyContext(dependencyContext); - return settingConfiguration.Settings( - new ConfigurationReader( - configuration.GetSection(sectionName), - assemblyFinder, - configuration)); + var configurationReader = new ConfigurationReader(configuration.GetSection(sectionName), assemblyFinder, configuration); + var loggerConfiguration = settingConfiguration.Settings(configurationReader); + loadedConfiguration = configurationReader.GetLoadedConfiguration(); + return loggerConfiguration; } /// @@ -79,6 +100,24 @@ public static LoggerConfiguration Configuration( DependencyContext dependencyContext = null) => Configuration(settingConfiguration, configuration, DefaultSectionName, dependencyContext); + /// + /// 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. + /// Contains information about the loaded configuration upon return. + /// 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, + out LoadedConfiguration loadedConfiguration, + DependencyContext dependencyContext = null) + => Configuration(settingConfiguration, configuration, DefaultSectionName, out loadedConfiguration, 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. @@ -123,6 +162,25 @@ public static LoggerConfiguration Configuration( IConfiguration configuration, string sectionName, ConfigurationAssemblySource configurationAssemblySource) + => Configuration(settingConfiguration, configuration, sectionName, configurationAssemblySource, out _); + + /// + /// 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. + /// Contains information about the loaded configuration upon return. + /// An object allowing configuration to continue. + public static LoggerConfiguration Configuration( + this LoggerSettingsConfiguration settingConfiguration, + IConfiguration configuration, + string sectionName, + ConfigurationAssemblySource configurationAssemblySource, + out LoadedConfiguration loadedConfiguration) { if (settingConfiguration == null) throw new ArgumentNullException(nameof(settingConfiguration)); if (configuration == null) throw new ArgumentNullException(nameof(configuration)); @@ -130,7 +188,10 @@ public static LoggerConfiguration Configuration( var assemblyFinder = AssemblyFinder.ForSource(configurationAssemblySource); - return settingConfiguration.Settings(new ConfigurationReader(configuration.GetSection(sectionName), assemblyFinder, configuration)); + var configurationReader = new ConfigurationReader(configuration.GetSection(sectionName), assemblyFinder, configuration); + var loggerConfiguration = settingConfiguration.Settings(configurationReader); + loadedConfiguration = configurationReader.GetLoadedConfiguration(); + return loggerConfiguration; } /// @@ -183,12 +244,32 @@ public static LoggerConfiguration Configuration( IConfiguration configuration, string sectionName, params Assembly[] assemblies) + => Configuration(settingConfiguration, configuration, sectionName, out _, assemblies); + + /// + /// 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. + /// Contains information about the loaded configuration upon return. + /// 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, + out LoadedConfiguration loadedConfiguration, + 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))); + var configurationReader = new ConfigurationReader(configuration.GetSection(sectionName), assemblies, new ResolutionContext(configuration)); + var loggerConfiguration = settingConfiguration.Settings(configurationReader); + loadedConfiguration = configurationReader.GetLoadedConfiguration(); + return loggerConfiguration; } /// diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 8b20544..6ec2406 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -58,6 +58,11 @@ public void Configure(LoggerConfiguration loggerConfiguration) ApplyAuditSinks(loggerConfiguration); } + public LoadedConfiguration GetLoadedConfiguration() + { + return new LoadedConfiguration(_resolutionContext.LogLevelSwitches); + } + void ProcessFilterSwitchDeclarations() { var filterSwitchesDirective = _section.GetSection("FilterSwitches"); diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/LoadedConfiguration.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/LoadedConfiguration.cs new file mode 100644 index 0000000..c1864a7 --- /dev/null +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/LoadedConfiguration.cs @@ -0,0 +1,20 @@ +using Serilog.Core; + +namespace Serilog.Settings.Configuration; + +/// +/// Contains information about the loaded configuration. +/// +public class LoadedConfiguration +{ + internal LoadedConfiguration(IReadOnlyDictionary logLevelSwitches) + { + LogLevelSwitches = logLevelSwitches; + } + + /// + /// The log level switches that were loaded from the LevelSwitches section of the configuration. + /// + /// The keys of the dictionary are the names of the switches, including the leading $ character. + public IReadOnlyDictionary LogLevelSwitches { get; } +} diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs index 1db02a5..b520f38 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ResolutionContext.cs @@ -9,8 +9,8 @@ namespace Serilog.Settings.Configuration; /// sealed class ResolutionContext { - readonly IDictionary _declaredLevelSwitches; - readonly IDictionary _declaredFilterSwitches; + readonly Dictionary _declaredLevelSwitches; + readonly Dictionary _declaredFilterSwitches; readonly IConfiguration _appConfiguration; public ResolutionContext(IConfiguration appConfiguration = null) @@ -20,6 +20,8 @@ public ResolutionContext(IConfiguration appConfiguration = null) _appConfiguration = appConfiguration; } + public IReadOnlyDictionary LogLevelSwitches => _declaredLevelSwitches; + /// /// Looks up a switch in the declared LoggingLevelSwitches /// diff --git a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs index 94e8ff9..b744665 100644 --- a/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/ConfigurationSettingsTests.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration; using Serilog.Core; using Serilog.Events; using Serilog.Settings.Configuration.Tests.Support; @@ -12,22 +12,27 @@ public class ConfigurationSettingsTests { static LoggerConfiguration ConfigFromJson(string jsonString, string secondJsonSource = null) { - return ConfigFromJson(jsonString, secondJsonSource, out _); + return ConfigFromJson(jsonString, secondJsonSource, out _, out _); } static LoggerConfiguration ConfigFromJson(string jsonString, out IConfiguration configuration) { - return ConfigFromJson(jsonString, null, out configuration); + return ConfigFromJson(jsonString, null, out configuration, out _); } - static LoggerConfiguration ConfigFromJson(string jsonString, string secondJsonSource, out IConfiguration configuration) + static LoggerConfiguration ConfigFromJson(string jsonString, out LoadedConfiguration loadedConfiguration) + { + return ConfigFromJson(jsonString, null, out _, out loadedConfiguration); + } + + static LoggerConfiguration ConfigFromJson(string jsonString, string secondJsonSource, out IConfiguration configuration, out LoadedConfiguration loadedConfiguration) { var builder = new ConfigurationBuilder().AddJsonString(jsonString); if (secondJsonSource != null) builder.AddJsonString(secondJsonSource); configuration = builder.Build(); return new LoggerConfiguration() - .ReadFrom.Configuration(configuration); + .ReadFrom.Configuration(configuration, out loadedConfiguration); } [Fact] @@ -384,10 +389,12 @@ public void LoggingLevelSwitchIsConfigured(string switchName) }}"; LogEvent evt = null; - var log = ConfigFromJson(json) + var log = ConfigFromJson(json, out LoadedConfiguration loadedConfiguration) .WriteTo.Sink(new DelegatingSink(e => evt = e)) .CreateLogger(); + Assert.Contains("$switch1", loadedConfiguration.LogLevelSwitches); + Assert.Equal(LogEventLevel.Warning, loadedConfiguration.LogLevelSwitches["$switch1"].MinimumLevel); log.Write(Some.DebugEvent()); Assert.True(evt is null, "LoggingLevelSwitch initial level was Warning. It should not log Debug messages"); log.Write(Some.InformationEvent()); @@ -542,7 +549,7 @@ public void SinkWithIConfigurationArguments() }"; DummyConfigurationSink.Reset(); - var log = ConfigFromJson(json, out var expectedConfig) + var log = ConfigFromJson(json, out IConfiguration expectedConfig) .CreateLogger(); log.Write(Some.InformationEvent()); @@ -566,7 +573,7 @@ public void SinkWithOptionalIConfigurationArguments() }"; DummyConfigurationSink.Reset(); - var log = ConfigFromJson(json, out var expectedConfig) + var log = ConfigFromJson(json, out IConfiguration expectedConfig) .CreateLogger(); log.Write(Some.InformationEvent()); diff --git a/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs b/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs index 1c3eb5a..87dd4a8 100644 --- a/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/DynamicLevelChangeTests.cs @@ -47,9 +47,11 @@ public void ShouldRespectDynamicLevelChanges() { using var logger = new LoggerConfiguration() .ReadFrom - .Configuration(new ConfigurationBuilder().Add(_configSource).Build()) + .Configuration(new ConfigurationBuilder().Add(_configSource).Build(), out var loadedConfiguration) .CreateLogger(); + Assert.Equal(LogEventLevel.Information, loadedConfiguration.LogLevelSwitches["$mySwitch"].MinimumLevel); + DummyConsoleSink.Emitted.Clear(); logger.Write(Some.DebugEvent()); Assert.Empty(DummyConsoleSink.Emitted); @@ -64,6 +66,7 @@ public void ShouldRespectDynamicLevelChanges() logger.Write(Some.DebugEvent()); logger.ForContext(Constants.SourceContextPropertyName, "Root.Test").Write(Some.DebugEvent()); Assert.Single(DummyConsoleSink.Emitted); + Assert.Equal(LogEventLevel.Debug, loadedConfiguration.LogLevelSwitches["$mySwitch"].MinimumLevel); DummyConsoleSink.Emitted.Clear(); UpdateConfig(overrideLevel: LogEventLevel.Debug);