diff --git a/src/Serilog/Configuration/LoggerSettingsConfiguration.cs b/src/Serilog/Configuration/LoggerSettingsConfiguration.cs index afd1499d3..cfde0fa1e 100644 --- a/src/Serilog/Configuration/LoggerSettingsConfiguration.cs +++ b/src/Serilog/Configuration/LoggerSettingsConfiguration.cs @@ -14,6 +14,8 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; using Serilog.Settings.KeyValuePairs; namespace Serilog.Configuration @@ -52,7 +54,9 @@ public LoggerConfiguration Settings(ILoggerSettings settings) public LoggerConfiguration KeyValuePairs(IEnumerable> settings) { if (settings == null) throw new ArgumentNullException(nameof(settings)); - return Settings(new KeyValuePairSettings(settings)); + + var settingsDictionary = new ReadOnlyDictionary(settings.ToDictionary(x => x.Key, x => x.Value)); + return Settings(new KeyValuePairSettings(settingsDictionary)); } } } diff --git a/src/Serilog/Settings/KeyValuePairs/KeyValuePairSettings.cs b/src/Serilog/Settings/KeyValuePairs/KeyValuePairSettings.cs index 69c619efd..51bbcb1d1 100644 --- a/src/Serilog/Settings/KeyValuePairs/KeyValuePairSettings.cs +++ b/src/Serilog/Settings/KeyValuePairs/KeyValuePairSettings.cs @@ -73,21 +73,18 @@ class KeyValuePairSettings : ILoggerSettings [typeof(LoggerFilterConfiguration)] = lc => lc.Filter }; - readonly Dictionary _settings; + readonly IReadOnlyDictionary _settings; - public KeyValuePairSettings(IEnumerable> settings) + public KeyValuePairSettings(IReadOnlyDictionary settings) { - if (settings == null) throw new ArgumentNullException(nameof(settings)); - _settings = settings.ToDictionary(s => s.Key, s => s.Value); + _settings = settings ?? throw new ArgumentNullException(nameof(settings)); } public void Configure(LoggerConfiguration loggerConfiguration) { if (loggerConfiguration == null) throw new ArgumentNullException(nameof(loggerConfiguration)); - - var directives = _settings.Keys - .Where(k => _supportedDirectives.Any(k.StartsWith)) - .ToDictionary(k => k, k => _settings[k]); + + var directives = ExtractDirectives(_settings); var declaredLevelSwitches = ParseNamedLevelSwitchDeclarationDirectives(directives); @@ -164,12 +161,21 @@ where matchCallables.IsMatch(wt.Key) } } + internal static IReadOnlyDictionary ExtractDirectives(IReadOnlyDictionary settings) + { + var directives = settings + .Where(kvp => _supportedDirectives.Any(kvp.Key.StartsWith)) + .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + + return new ReadOnlyDictionary(directives); + } + internal static bool IsValidSwitchName(string input) { return Regex.IsMatch(input, LevelSwitchNameRegex); } - static IReadOnlyDictionary ParseNamedLevelSwitchDeclarationDirectives(Dictionary directives) + static IReadOnlyDictionary ParseNamedLevelSwitchDeclarationDirectives(IReadOnlyDictionary directives) { var matchLevelSwitchDeclarations = new Regex(LevelSwitchDeclarationDirectiveRegex); @@ -255,7 +261,7 @@ internal static MethodInfo SelectConfigurationMethod(IEnumerable can .FirstOrDefault(); } - internal static IEnumerable LoadConfigurationAssemblies(Dictionary directives) + internal static IEnumerable LoadConfigurationAssemblies(IReadOnlyDictionary directives) { var configurationAssemblies = new List { typeof(ILogger).GetTypeInfo().Assembly }; diff --git a/test/Serilog.Tests/Settings/KeyValuePairSettingsTests.cs b/test/Serilog.Tests/Settings/KeyValuePairSettingsTests.cs index 9b7e7f9fd..405a13a29 100644 --- a/test/Serilog.Tests/Settings/KeyValuePairSettingsTests.cs +++ b/test/Serilog.Tests/Settings/KeyValuePairSettingsTests.cs @@ -15,6 +15,35 @@ namespace Serilog.Tests.Settings { public class KeyValuePairSettingsTests { + [Fact] + public void DuplicateKeysCauseArgumentException() + { + Action action = () => new LoggerConfiguration() + .ReadFrom.KeyValuePairs(new List> + { + new KeyValuePair("setting", "Value"), + new KeyValuePair("setting", "SameSettingOtherValue"), + }); + + var ex = Assert.ThrowsAny(action); + Assert.NotNull(ex); + Assert.Contains("An item with the same key has already been added.", ex.Message); + } + + [Fact] + public void IrrelevantKeysAreIgnored() + { + var irrelevantSettings = new Dictionary() + { + ["whatever:foo:bar"] = "willBeIgnored", + ["irrelevant"] = "willBeIgnored", + }; + + var directives = KeyValuePairSettings.ExtractDirectives(irrelevantSettings); + + Assert.False(directives.Any()); + } + [Fact] public void FindsConfigurationAssemblies() { @@ -181,7 +210,7 @@ public void LoggingLevelSwitchWithInvalidNameThrowsFormatException() ["level-switch:switchNameNotStartingWithDollar"] = "Warning", }; - var ex = Assert.Throws(() => new LoggerConfiguration() + var ex = Assert.Throws(() => new LoggerConfiguration() .ReadFrom.KeyValuePairs(settings)); Assert.Contains("\"switchNameNotStartingWithDollar\"", ex.Message); @@ -301,7 +330,7 @@ public void LoggingLevelSwitchCanBeUsedForMinimumLevelOverrides() .CreateLogger(); var systemLogger = log.ForContext(Constants.SourceContextPropertyName, "System.Bar"); - + log.Write(Some.InformationEvent()); Assert.False(evt is null, "Minimul level is Debug. It should log Information messages");