Skip to content

Commit b1e4661

Browse files
committed
Refactor: Introduce SettingValueResolver
To pass around the `IConfiguration` object instead of a `static` member
1 parent 71ee59d commit b1e4661

File tree

10 files changed

+101
-85
lines changed

10 files changed

+101
-85
lines changed

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

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class ConfigurationReader : IConfigurationReader
1919
{
2020
const string LevelSwitchNameRegex = @"^\$[A-Za-z]+[A-Za-z0-9]*$";
2121

22-
static IConfiguration _configuration;
22+
readonly IConfiguration _configuration;
2323

2424
readonly IConfigurationSection _section;
2525
readonly DependencyContext _dependencyContext;
@@ -43,22 +43,25 @@ public ConfigurationReader(IConfigurationSection configSection, DependencyContex
4343
}
4444

4545
// Used internally for processing nested configuration sections -- see GetMethodCalls below.
46-
internal ConfigurationReader(IConfigurationSection configSection, IReadOnlyCollection<Assembly> configurationAssemblies, DependencyContext dependencyContext)
46+
internal ConfigurationReader(IConfigurationSection configSection, IReadOnlyCollection<Assembly> configurationAssemblies, DependencyContext dependencyContext, SettingValueResolver valueResolver)
4747
{
4848
_section = configSection ?? throw new ArgumentNullException(nameof(configSection));
4949
_dependencyContext = dependencyContext;
5050
_configurationAssemblies = configurationAssemblies ?? throw new ArgumentNullException(nameof(configurationAssemblies));
51+
_configuration = valueResolver.AppConfiguration;
5152
}
5253

5354
public void Configure(LoggerConfiguration loggerConfiguration)
5455
{
5556
var declaredLevelSwitches = ProcessLevelSwitchDeclarations();
56-
ApplyMinimumLevel(loggerConfiguration, declaredLevelSwitches);
57-
ApplyEnrichment(loggerConfiguration, declaredLevelSwitches);
58-
ApplyFilters(loggerConfiguration, declaredLevelSwitches);
59-
ApplyDestructuring(loggerConfiguration, declaredLevelSwitches);
60-
ApplySinks(loggerConfiguration, declaredLevelSwitches);
61-
ApplyAuditSinks(loggerConfiguration, declaredLevelSwitches);
57+
var settingsValueResolver = new SettingValueResolver(declaredLevelSwitches, _configuration);
58+
59+
ApplyMinimumLevel(loggerConfiguration, settingsValueResolver);
60+
ApplyEnrichment(loggerConfiguration, settingsValueResolver);
61+
ApplyFilters(loggerConfiguration, settingsValueResolver);
62+
ApplyDestructuring(loggerConfiguration, settingsValueResolver);
63+
ApplySinks(loggerConfiguration, settingsValueResolver);
64+
ApplyAuditSinks(loggerConfiguration, settingsValueResolver);
6265
}
6366

6467
IReadOnlyDictionary<string, LoggingLevelSwitch> ProcessLevelSwitchDeclarations()
@@ -89,7 +92,7 @@ IReadOnlyDictionary<string, LoggingLevelSwitch> ProcessLevelSwitchDeclarations()
8992
return namedSwitches;
9093
}
9194

92-
void ApplyMinimumLevel(LoggerConfiguration loggerConfiguration, IReadOnlyDictionary<string, LoggingLevelSwitch> declaredLevelSwitches)
95+
void ApplyMinimumLevel(LoggerConfiguration loggerConfiguration, SettingValueResolver valueResolver)
9396
{
9497
var minimumLevelDirective = _section.GetSection("MinimumLevel");
9598

@@ -102,7 +105,7 @@ void ApplyMinimumLevel(LoggerConfiguration loggerConfiguration, IReadOnlyDiction
102105
var minLevelControlledByDirective = minimumLevelDirective.GetSection("ControlledBy");
103106
if (minLevelControlledByDirective.Value != null)
104107
{
105-
var globalMinimumLevelSwitch = declaredLevelSwitches.LookUpSwitchByName(minLevelControlledByDirective.Value);
108+
var globalMinimumLevelSwitch = valueResolver.LookUpSwitchByName(minLevelControlledByDirective.Value);
106109
// not calling ApplyMinimumLevel local function because here we have a reference to a LogLevelSwitch already
107110
loggerConfiguration.MinimumLevel.ControlledBy(globalMinimumLevelSwitch);
108111
}
@@ -117,7 +120,7 @@ void ApplyMinimumLevel(LoggerConfiguration loggerConfiguration, IReadOnlyDiction
117120
}
118121
else
119122
{
120-
var overrideSwitch = declaredLevelSwitches.LookUpSwitchByName(overridenLevelOrSwitch);
123+
var overrideSwitch = valueResolver.LookUpSwitchByName(overridenLevelOrSwitch);
121124
// not calling ApplyMinimumLevel local function because here we have a reference to a LogLevelSwitch already
122125
loggerConfiguration.MinimumLevel.Override(overridePrefix, overrideSwitch);
123126
}
@@ -142,59 +145,59 @@ void ApplyMinimumLevel(IConfigurationSection directive, Action<LoggerMinimumLeve
142145
}
143146
}
144147

145-
void ApplyFilters(LoggerConfiguration loggerConfiguration, IReadOnlyDictionary<string, LoggingLevelSwitch> declaredLevelSwitches)
148+
void ApplyFilters(LoggerConfiguration loggerConfiguration, SettingValueResolver valueResolver)
146149
{
147150
var filterDirective = _section.GetSection("Filter");
148151
if (filterDirective.GetChildren().Any())
149152
{
150153
var methodCalls = GetMethodCalls(filterDirective);
151-
CallConfigurationMethods(methodCalls, FindFilterConfigurationMethods(_configurationAssemblies), loggerConfiguration.Filter, declaredLevelSwitches);
154+
CallConfigurationMethods(methodCalls, FindFilterConfigurationMethods(_configurationAssemblies), loggerConfiguration.Filter, valueResolver);
152155
}
153156
}
154157

155-
void ApplyDestructuring(LoggerConfiguration loggerConfiguration, IReadOnlyDictionary<string, LoggingLevelSwitch> declaredLevelSwitches)
158+
void ApplyDestructuring(LoggerConfiguration loggerConfiguration, SettingValueResolver valueResolver)
156159
{
157160
var destructureDirective = _section.GetSection("Destructure");
158161
if (destructureDirective.GetChildren().Any())
159162
{
160163
var methodCalls = GetMethodCalls(destructureDirective);
161-
CallConfigurationMethods(methodCalls, FindDestructureConfigurationMethods(_configurationAssemblies), loggerConfiguration.Destructure, declaredLevelSwitches);
164+
CallConfigurationMethods(methodCalls, FindDestructureConfigurationMethods(_configurationAssemblies), loggerConfiguration.Destructure, valueResolver);
162165
}
163166
}
164167

165-
void ApplySinks(LoggerConfiguration loggerConfiguration, IReadOnlyDictionary<string, LoggingLevelSwitch> declaredLevelSwitches)
168+
void ApplySinks(LoggerConfiguration loggerConfiguration, SettingValueResolver valueResolver)
166169
{
167170
var writeToDirective = _section.GetSection("WriteTo");
168171
if (writeToDirective.GetChildren().Any())
169172
{
170173
var methodCalls = GetMethodCalls(writeToDirective);
171-
CallConfigurationMethods(methodCalls, FindSinkConfigurationMethods(_configurationAssemblies), loggerConfiguration.WriteTo, declaredLevelSwitches);
174+
CallConfigurationMethods(methodCalls, FindSinkConfigurationMethods(_configurationAssemblies), loggerConfiguration.WriteTo, valueResolver);
172175
}
173176
}
174177

175-
void ApplyAuditSinks(LoggerConfiguration loggerConfiguration, IReadOnlyDictionary<string, LoggingLevelSwitch> declaredLevelSwitches)
178+
void ApplyAuditSinks(LoggerConfiguration loggerConfiguration, SettingValueResolver valueResolver)
176179
{
177180
var auditToDirective = _section.GetSection("AuditTo");
178181
if (auditToDirective.GetChildren().Any())
179182
{
180183
var methodCalls = GetMethodCalls(auditToDirective);
181-
CallConfigurationMethods(methodCalls, FindAuditSinkConfigurationMethods(_configurationAssemblies), loggerConfiguration.AuditTo, declaredLevelSwitches);
184+
CallConfigurationMethods(methodCalls, FindAuditSinkConfigurationMethods(_configurationAssemblies), loggerConfiguration.AuditTo, valueResolver);
182185
}
183186
}
184187

185-
void IConfigurationReader.ApplySinks(LoggerSinkConfiguration loggerSinkConfiguration, IReadOnlyDictionary<string, LoggingLevelSwitch> declaredLevelSwitches)
188+
void IConfigurationReader.ApplySinks(LoggerSinkConfiguration loggerSinkConfiguration, SettingValueResolver valueResolver)
186189
{
187190
var methodCalls = GetMethodCalls(_section);
188-
CallConfigurationMethods(methodCalls, FindSinkConfigurationMethods(_configurationAssemblies), loggerSinkConfiguration, declaredLevelSwitches);
191+
CallConfigurationMethods(methodCalls, FindSinkConfigurationMethods(_configurationAssemblies), loggerSinkConfiguration, valueResolver);
189192
}
190193

191-
void ApplyEnrichment(LoggerConfiguration loggerConfiguration, IReadOnlyDictionary<string, LoggingLevelSwitch> declaredLevelSwitches)
194+
void ApplyEnrichment(LoggerConfiguration loggerConfiguration, SettingValueResolver valueResolver)
192195
{
193196
var enrichDirective = _section.GetSection("Enrich");
194197
if (enrichDirective.GetChildren().Any())
195198
{
196199
var methodCalls = GetMethodCalls(enrichDirective);
197-
CallConfigurationMethods(methodCalls, FindEventEnricherConfigurationMethods(_configurationAssemblies), loggerConfiguration.Enrich, declaredLevelSwitches);
200+
CallConfigurationMethods(methodCalls, FindEventEnricherConfigurationMethods(_configurationAssemblies), loggerConfiguration.Enrich, valueResolver);
198201
}
199202

200203
var propertiesDirective = _section.GetSection("Properties");
@@ -318,7 +321,7 @@ where filter(assemblyFileName)
318321
return query.ToArray();
319322
}
320323

321-
static void CallConfigurationMethods(ILookup<string, Dictionary<string, IConfigurationArgumentValue>> methods, IList<MethodInfo> configurationMethods, object receiver, IReadOnlyDictionary<string, LoggingLevelSwitch> declaredLevelSwitches)
324+
static void CallConfigurationMethods(ILookup<string, Dictionary<string, IConfigurationArgumentValue>> methods, IList<MethodInfo> configurationMethods, object receiver, SettingValueResolver valueResolver)
322325
{
323326
foreach (var method in methods.SelectMany(g => g.Select(x => new { g.Key, Value = x })))
324327
{
@@ -328,17 +331,19 @@ static void CallConfigurationMethods(ILookup<string, Dictionary<string, IConfigu
328331
{
329332
var call = (from p in methodInfo.GetParameters().Skip(1)
330333
let directive = method.Value.FirstOrDefault(s => s.Key.Equals(p.Name, StringComparison.OrdinalIgnoreCase))
331-
select directive.Key == null ? p.DefaultValue : directive.Value.ConvertTo(p.ParameterType, declaredLevelSwitches)).ToList();
334+
select directive.Key == null
335+
? p.DefaultValue
336+
: directive.Value.ConvertTo(p.ParameterType, valueResolver)).ToList();
332337

333338
var parm = methodInfo.GetParameters().FirstOrDefault(i => i.ParameterType == typeof(IConfiguration));
334339
if (parm != null && !parm.HasDefaultValue)
335340
{
336-
if (_configuration is null)
341+
if (valueResolver.AppConfiguration is null)
337342
{
338343
throw new InvalidOperationException("Trying to invoke a configuration method accepting a `IConfiguration` argument. " +
339344
$"This is not supported when only a `IConfigSection` has been provided. (method '{methodInfo}')");
340345
}
341-
call[parm.Position - 1] = _configuration;
346+
call[parm.Position - 1] = valueResolver.AppConfiguration;
342347
}
343348

344349
call.Insert(0, receiver);
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
using System;
2-
using System.Collections.Generic;
3-
using Serilog.Core;
42

53
namespace Serilog.Settings.Configuration
64
{
75
interface IConfigurationArgumentValue
86
{
9-
object ConvertTo(Type toType, IReadOnlyDictionary<string, LoggingLevelSwitch> declaredLevelSwitches);
7+
object ConvertTo(Type toType, SettingValueResolver valueResolver);
108
}
119
}
Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
using System.Collections.Generic;
2-
using Serilog.Configuration;
3-
using Serilog.Core;
1+
using Serilog.Configuration;
42

53
namespace Serilog.Settings.Configuration
64
{
75
interface IConfigurationReader : ILoggerSettings
86
{
9-
void ApplySinks(LoggerSinkConfiguration loggerSinkConfiguration, IReadOnlyDictionary<string, LoggingLevelSwitch> declaredLevelSwitches);
7+
void ApplySinks(LoggerSinkConfiguration loggerSinkConfiguration, SettingValueResolver valueResolver);
108
}
119
}

src/Serilog.Settings.Configuration/Settings/Configuration/LevelSwitchDictionaryExtensions.cs

Lines changed: 0 additions & 27 deletions
This file was deleted.

src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using Microsoft.Extensions.Configuration;
22
using Microsoft.Extensions.DependencyModel;
33
using Serilog.Configuration;
4-
using Serilog.Core;
54
using System;
65
using System.Collections.Generic;
76
using System.Reflection;
@@ -23,30 +22,30 @@ public ObjectArgumentValue(IConfigurationSection section, IReadOnlyCollection<As
2322
this.dependencyContext = dependencyContext;
2423
}
2524

26-
public object ConvertTo(Type toType, IReadOnlyDictionary<string, LoggingLevelSwitch> declaredLevelSwitches)
25+
public object ConvertTo(Type toType, SettingValueResolver valueResolver)
2726
{
2827
// return the entire section for internal processing
29-
if(toType == typeof(IConfigurationSection)) return section;
28+
if (toType == typeof(IConfigurationSection)) return section;
3029

3130
// process a nested configuration to populate an Action<> logger/sink config parameter?
3231
var typeInfo = toType.GetTypeInfo();
33-
if(typeInfo.IsGenericType &&
32+
if (typeInfo.IsGenericType &&
3433
typeInfo.GetGenericTypeDefinition() is Type genericType && genericType == typeof(Action<>))
3534
{
3635
var configType = typeInfo.GenericTypeArguments[0];
37-
if(configType != typeof(LoggerConfiguration) && configType != typeof(LoggerSinkConfiguration))
36+
if (configType != typeof(LoggerConfiguration) && configType != typeof(LoggerSinkConfiguration))
3837
throw new ArgumentException($"Configuration for Action<{configType}> is not implemented.");
3938

40-
IConfigurationReader configReader = new ConfigurationReader(section, configurationAssemblies, dependencyContext);
39+
IConfigurationReader configReader = new ConfigurationReader(section, configurationAssemblies, dependencyContext, valueResolver);
4140

42-
if(configType == typeof(LoggerConfiguration))
41+
if (configType == typeof(LoggerConfiguration))
4342
{
4443
return new Action<LoggerConfiguration>(configReader.Configure);
4544
}
4645

47-
if(configType == typeof(LoggerSinkConfiguration))
46+
if (configType == typeof(LoggerSinkConfiguration))
4847
{
49-
return new Action<LoggerSinkConfiguration>(loggerSinkConfig => configReader.ApplySinks(loggerSinkConfig, declaredLevelSwitches));
48+
return new Action<LoggerSinkConfiguration>(loggerSinkConfig => configReader.ApplySinks(loggerSinkConfig, valueResolver));
5049
}
5150
}
5251

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+

2+
3+
using System;
4+
using System.Collections.Generic;
5+
using Microsoft.Extensions.Configuration;
6+
using Serilog.Core;
7+
8+
namespace Serilog.Settings.Configuration
9+
{
10+
internal sealed class SettingValueResolver
11+
{
12+
readonly IReadOnlyDictionary<string, LoggingLevelSwitch> _declaredLevelSwitches;
13+
14+
public SettingValueResolver(IReadOnlyDictionary<string, LoggingLevelSwitch> declaredLevelSwitches, IConfiguration appConfiguration)
15+
{
16+
_declaredLevelSwitches = declaredLevelSwitches ?? throw new ArgumentNullException(nameof(declaredLevelSwitches));
17+
AppConfiguration = appConfiguration;
18+
}
19+
20+
public IConfiguration AppConfiguration { get; }
21+
22+
/// <summary>
23+
/// Looks up a switch in the declared LoggingLevelSwitches
24+
/// </summary>
25+
/// <param name="switchName">the name of a switch to look up</param>
26+
/// <returns>the LoggingLevelSwitch registered with the name</returns>
27+
/// <exception cref="InvalidOperationException">if no switch has been registered with <paramref name="switchName"/></exception>
28+
public LoggingLevelSwitch LookUpSwitchByName(string switchName)
29+
{
30+
if (_declaredLevelSwitches.TryGetValue(switchName, out var levelSwitch))
31+
{
32+
return levelSwitch;
33+
}
34+
35+
throw new InvalidOperationException($"No LoggingLevelSwitch has been declared with name \"{switchName}\". You might be missing a section \"LevelSwitches\":{{\"{switchName}\":\"InitialLevel\"}}");
36+
}
37+
38+
public static SettingValueResolver Default()
39+
{
40+
return new SettingValueResolver(new Dictionary<string, LoggingLevelSwitch>(), null);
41+
}
42+
}
43+
}

src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Linq;
44
using System.Reflection;
55
using System.Text.RegularExpressions;
6+
using Microsoft.Extensions.Configuration;
67
using Microsoft.Extensions.Primitives;
78

89
using Serilog.Core;
@@ -31,13 +32,13 @@ public StringArgumentValue(Func<string> valueProducer, Func<IChangeToken> change
3132
{ typeof(Type), s => Type.GetType(s, throwOnError:true) },
3233
};
3334

34-
public object ConvertTo(Type toType, IReadOnlyDictionary<string, LoggingLevelSwitch> declaredLevelSwitches)
35+
public object ConvertTo(Type toType, SettingValueResolver valueResolver)
3536
{
3637
var argumentValue = Environment.ExpandEnvironmentVariables(_valueProducer());
3738

3839
if (toType == typeof(LoggingLevelSwitch))
3940
{
40-
return declaredLevelSwitches.LookUpSwitchByName(argumentValue);
41+
return valueResolver.LookUpSwitchByName(argumentValue);
4142
}
4243

4344
var toTypeInfo = toType.GetTypeInfo();

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using Xunit;
44
using System.Reflection;
55
using System.Linq;
6-
using Serilog.Core;
76
using Serilog.Settings.Configuration.Tests.Support;
87

98
namespace Serilog.Settings.Configuration.Tests
@@ -75,7 +74,7 @@ public void WriteToSupportExpandedSyntaxWithArgs()
7574

7675
Assert.Equal(1, args.Length);
7776
Assert.Equal("outputTemplate", args[0].Key);
78-
Assert.Equal("{Message}", args[0].Value.ConvertTo(typeof(string), new Dictionary<string, LoggingLevelSwitch>()));
77+
Assert.Equal("{Message}", args[0].Value.ConvertTo(typeof(string), SettingValueResolver.Default()));
7978
}
8079

8180
[Fact]

0 commit comments

Comments
 (0)