Skip to content

Commit 375c5bb

Browse files
authored
Merge pull request #233 from HexHunter97/minimumlevel-partial-fix
Minimumlevel partial fix with IConfigurationRoot.Providers
2 parents 13be21c + 890516c commit 375c5bb

File tree

4 files changed

+214
-1
lines changed

4 files changed

+214
-1
lines changed

src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,18 @@ class ConfigurationReader : IConfigurationReader
2222
readonly IConfigurationSection _section;
2323
readonly IReadOnlyCollection<Assembly> _configurationAssemblies;
2424
readonly ResolutionContext _resolutionContext;
25+
#if NETSTANDARD || NET461
26+
readonly IConfigurationRoot _configurationRoot;
27+
#endif
2528

2629
public ConfigurationReader(IConfigurationSection configSection, AssemblyFinder assemblyFinder, IConfiguration configuration = null)
2730
{
2831
_section = configSection ?? throw new ArgumentNullException(nameof(configSection));
2932
_configurationAssemblies = LoadConfigurationAssemblies(_section, assemblyFinder);
3033
_resolutionContext = new ResolutionContext(configuration);
34+
#if NETSTANDARD || NET461
35+
_configurationRoot = configuration as IConfigurationRoot;
36+
#endif
3137
}
3238

3339
// Used internally for processing nested configuration sections -- see GetMethodCalls below.
@@ -36,6 +42,9 @@ internal ConfigurationReader(IConfigurationSection configSection, IReadOnlyColle
3642
_section = configSection ?? throw new ArgumentNullException(nameof(configSection));
3743
_configurationAssemblies = configurationAssemblies ?? throw new ArgumentNullException(nameof(configurationAssemblies));
3844
_resolutionContext = resolutionContext ?? throw new ArgumentNullException(nameof(resolutionContext));
45+
#if NETSTANDARD || NET461
46+
_configurationRoot = resolutionContext.HasAppConfiguration ? resolutionContext.AppConfiguration as IConfigurationRoot : null;
47+
#endif
3948
}
4049

4150
public void Configure(LoggerConfiguration loggerConfiguration)
@@ -143,7 +152,7 @@ void ApplyMinimumLevel(LoggerConfiguration loggerConfiguration)
143152
{
144153
var minimumLevelDirective = _section.GetSection("MinimumLevel");
145154

146-
var defaultMinLevelDirective = minimumLevelDirective.Value != null ? minimumLevelDirective : minimumLevelDirective.GetSection("Default");
155+
IConfigurationSection defaultMinLevelDirective = GetDefaultMinLevelDirective();
147156
if (defaultMinLevelDirective.Value != null)
148157
{
149158
ApplyMinimumLevelConfiguration(defaultMinLevelDirective, (configuration, levelSwitch) => configuration.ControlledBy(levelSwitch));
@@ -182,6 +191,34 @@ void ApplyMinimumLevelConfiguration(IConfigurationSection directive, Action<Logg
182191

183192
SubscribeToLoggingLevelChanges(directive, levelSwitch);
184193
}
194+
195+
IConfigurationSection GetDefaultMinLevelDirective()
196+
{
197+
#if NETSTANDARD || NET461
198+
199+
var defaultLevelDirective = minimumLevelDirective.GetSection("Default");
200+
if (_configurationRoot != null && minimumLevelDirective.Value != null && defaultLevelDirective.Value != null)
201+
{
202+
foreach (var provider in _configurationRoot.Providers.Reverse())
203+
{
204+
if (provider.TryGet(minimumLevelDirective.Path, out _))
205+
{
206+
return _configurationRoot.GetSection(minimumLevelDirective.Path);
207+
}
208+
209+
if (provider.TryGet(defaultLevelDirective.Path, out _))
210+
{
211+
return _configurationRoot.GetSection(defaultLevelDirective.Path);
212+
}
213+
}
214+
215+
return null;
216+
}
217+
218+
#endif //NET451 or fallback
219+
220+
return minimumLevelDirective.Value != null ? minimumLevelDirective : minimumLevelDirective.GetSection("Default");
221+
}
185222
}
186223

187224
void SubscribeToLoggingLevelChanges(IConfigurationSection levelSection, LoggingLevelSwitch levelSwitch)

test/Serilog.Settings.Configuration.Tests/ConfigurationReaderTests.cs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
using Xunit;
2+
using System.Collections.Generic;
23
using System.Reflection;
34
using System.Linq;
5+
using Microsoft.Extensions.Configuration;
6+
using Serilog.Events;
47
using Serilog.Formatting;
58
using Serilog.Settings.Configuration.Assemblies;
69
using Serilog.Settings.Configuration.Tests.Support;
10+
using static Serilog.Settings.Configuration.Tests.Support.ConfigurationReaderTestHelpers;
711

812
namespace Serilog.Settings.Configuration.Tests
913
{
@@ -172,5 +176,110 @@ public void MethodsAreSelectedBasedOnCountOfMatchedArgumentsAndThenStringType()
172176
var selected = ConfigurationReader.SelectConfigurationMethod(options, "DummyRollingFile", suppliedArgumentNames);
173177
Assert.Equal(typeof(string), selected.GetParameters()[2].ParameterType);
174178
}
179+
180+
public static IEnumerable<object[]> FlatMinimumLevel => new List<object[]>
181+
{
182+
new object[] { GetConfigRoot(appsettingsJsonLevel: minimumLevelFlatTemplate.Format(LogEventLevel.Error)), LogEventLevel.Error },
183+
new object[] { GetConfigRoot(appsettingsDevelopmentJsonLevel: minimumLevelFlatTemplate.Format(LogEventLevel.Error)), LogEventLevel.Error },
184+
new object[] { GetConfigRoot(envVariables: new Dictionary<string, string>() {{minimumLevelFlatKey, LogEventLevel.Error.ToString()}}), LogEventLevel.Error},
185+
new object[] { GetConfigRoot(
186+
appsettingsJsonLevel: minimumLevelFlatTemplate.Format(LogEventLevel.Debug),
187+
envVariables: new Dictionary<string, string>() {{minimumLevelFlatKey, LogEventLevel.Error.ToString()}}),
188+
LogEventLevel.Error
189+
}
190+
};
191+
192+
[Theory]
193+
[MemberData(nameof(FlatMinimumLevel))]
194+
public void FlatMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, LogEventLevel expectedMinimumLevel)
195+
{
196+
var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), root);
197+
var loggerConfig = new LoggerConfiguration();
198+
199+
reader.Configure(loggerConfig);
200+
201+
AssertLogEventLevels(loggerConfig, expectedMinimumLevel);
202+
}
203+
204+
public static IEnumerable<object[]> ObjectMinimumLevel => new List<object[]>
205+
{
206+
new object[] { GetConfigRoot(appsettingsJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error)), LogEventLevel.Error },
207+
new object[] { GetConfigRoot(appsettingsDevelopmentJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error)), LogEventLevel.Error },
208+
new object[] { GetConfigRoot(envVariables: new Dictionary<string, string>(){{minimumLevelObjectKey, LogEventLevel.Error.ToString() } }), LogEventLevel.Error },
209+
new object[] { GetConfigRoot(
210+
appsettingsJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error),
211+
appsettingsDevelopmentJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Debug)),
212+
LogEventLevel.Debug }
213+
};
214+
215+
[Theory]
216+
[MemberData(nameof(ObjectMinimumLevel))]
217+
public void ObjectMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, LogEventLevel expectedMinimumLevel)
218+
{
219+
var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), root);
220+
var loggerConfig = new LoggerConfiguration();
221+
222+
reader.Configure(loggerConfig);
223+
224+
AssertLogEventLevels(loggerConfig, expectedMinimumLevel);
225+
}
226+
227+
#if !(NET452)
228+
229+
// currently only works in the .NET 4.6.1 and .NET Standard builds of Serilog.Settings.Configuration
230+
public static IEnumerable<object[]> MixedMinimumLevel => new List<object[]>
231+
{
232+
new object[]
233+
{
234+
GetConfigRoot(
235+
appsettingsJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Error),
236+
appsettingsDevelopmentJsonLevel: minimumLevelFlatTemplate.Format(LogEventLevel.Debug)),
237+
LogEventLevel.Debug
238+
},
239+
new object[]
240+
{
241+
GetConfigRoot(
242+
appsettingsJsonLevel: minimumLevelFlatTemplate.Format(LogEventLevel.Error),
243+
appsettingsDevelopmentJsonLevel: minimumLevelObjectTemplate.Format(LogEventLevel.Debug)),
244+
LogEventLevel.Debug
245+
},
246+
// precedence should be flat > object if from the same source
247+
new object[]
248+
{
249+
GetConfigRoot(
250+
envVariables: new Dictionary<string, string>()
251+
{
252+
{minimumLevelObjectKey, LogEventLevel.Error.ToString()},
253+
{minimumLevelFlatKey, LogEventLevel.Debug.ToString()}
254+
}),
255+
LogEventLevel.Debug
256+
}
257+
};
258+
259+
[Theory]
260+
[MemberData(nameof(MixedMinimumLevel))]
261+
public void MixedMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, LogEventLevel expectedMinimumLevel)
262+
{
263+
var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), root);
264+
var loggerConfig = new LoggerConfiguration();
265+
266+
reader.Configure(loggerConfig);
267+
268+
AssertLogEventLevels(loggerConfig, expectedMinimumLevel);
269+
}
270+
271+
#endif
272+
273+
[Fact]
274+
public void NoConfigurationRootUsedStillValid()
275+
{
276+
var section = JsonStringConfigSource.LoadSection(@"{ 'Nest': { 'Serilog': { 'MinimumLevel': 'Error' } } }", "Nest");
277+
var reader = new ConfigurationReader(section.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), section);
278+
var loggerConfig = new LoggerConfiguration();
279+
280+
reader.Configure(loggerConfig);
281+
282+
AssertLogEventLevels(loggerConfig, LogEventLevel.Error);
283+
}
175284
}
176285
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Microsoft.Extensions.Configuration;
5+
using Serilog.Events;
6+
using Xunit;
7+
8+
namespace Serilog.Settings.Configuration.Tests.Support
9+
{
10+
static class ConfigurationReaderTestHelpers
11+
{
12+
public const string minimumLevelFlatTemplate = @"
13+
{{
14+
'Serilog': {{
15+
'MinimumLevel': '{0}'
16+
}}
17+
}}";
18+
public const string minimumLevelObjectTemplate = @"
19+
{{
20+
'Serilog': {{
21+
'MinimumLevel': {{
22+
'Default': '{0}'
23+
}}
24+
}}
25+
}}";
26+
public const string minimumLevelFlatKey = "Serilog:MinimumLevel";
27+
public const string minimumLevelObjectKey = "Serilog:MinimumLevel:Default";
28+
29+
public static void AssertLogEventLevels(LoggerConfiguration loggerConfig, LogEventLevel expectedMinimumLevel)
30+
{
31+
var logger = loggerConfig.CreateLogger();
32+
33+
var logEventValues = Enum.GetValues(typeof(LogEventLevel)).Cast<LogEventLevel>();
34+
35+
foreach (var logEvent in logEventValues)
36+
{
37+
if (logEvent < expectedMinimumLevel)
38+
{
39+
Assert.False(logger.IsEnabled(logEvent),
40+
$"The log level {logEvent} should be disabled as it's lower priority than the minimum level of {expectedMinimumLevel}.");
41+
}
42+
else
43+
{
44+
Assert.True(logger.IsEnabled(logEvent),
45+
$"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}.");
46+
}
47+
}
48+
}
49+
50+
// the naming is only to show priority as providers
51+
public static IConfigurationRoot GetConfigRoot(
52+
string appsettingsJsonLevel = null,
53+
string appsettingsDevelopmentJsonLevel = null,
54+
Dictionary<string, string> envVariables = null)
55+
{
56+
var configBuilder = new ConfigurationBuilder();
57+
58+
configBuilder.AddJsonString(appsettingsJsonLevel ?? "{}");
59+
configBuilder.AddJsonString(appsettingsDevelopmentJsonLevel ?? "{}");
60+
configBuilder.Add(new ReloadableConfigurationSource(envVariables ?? new Dictionary<string, string>()));
61+
62+
return configBuilder.Build();
63+
}
64+
}
65+
}

test/Serilog.Settings.Configuration.Tests/Support/Extensions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,7 @@ public static string ToValidJson(this string str)
1414
str = str.Replace('\'', '"');
1515
return str;
1616
}
17+
18+
public static string Format(this string template, params object[] paramObjects) => string.Format(template, paramObjects);
1719
}
1820
}

0 commit comments

Comments
 (0)