Skip to content

Commit

Permalink
Add a callback on the reader options to expose the log level switches
Browse files Browse the repository at this point in the history
Fixes #206
  • Loading branch information
0xced committed Mar 10, 2023
1 parent 0a29ea3 commit e487e42
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 30 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,22 @@ You can also declare `LoggingLevelSwitch`-es in custom section and reference the

Level updates to switches are also respected for a dynamic update.

Since version 4.0.0, both declared switches (i.e. `Serilog:LevelSwitches` section) and minimum level override switches (i.e. `Serilog:MinimumLevel:Override` section) are exposed through a callback on the reader options so that a reference can be kept:

```csharp
var allSwitches = new Dictionary<string, LoggingLevelSwitch>();
var options = new ConfigurationReaderOptions
{
OnAddedLevelSwitch = (switchName, logLevelSwitch) => allSwitches[switchName] = logLevelSwitch
};

var logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration, options)
.CreateLogger();

LoggingLevelSwitch controlSwitch = allSwitches["$controlSwitch"];
```

### WriteTo, Enrich, AuditTo, Destructure sections

These sections support simplified syntax, for example the following is valid if no arguments are needed by the sinks:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ public static LoggerConfiguration ConfigurationSection(
new ConfigurationReader(
configSection,
assemblyFinder,
configuration: null,
formatProvider: null));
new ConfigurationReaderOptions { FormatProvider = null },
configuration: null));
}

/// <summary>
Expand Down Expand Up @@ -164,7 +164,7 @@ public static LoggerConfiguration ConfigurationSection(

var assemblyFinder = AssemblyFinder.ForSource(configurationAssemblySource);

return settingConfiguration.Settings(new ConfigurationReader(configSection, assemblyFinder, configuration: null, formatProvider: null));
return settingConfiguration.Settings(new ConfigurationReader(configSection, assemblyFinder, new ConfigurationReaderOptions { FormatProvider = null }, configuration: null));
}

/// <summary>
Expand Down Expand Up @@ -229,19 +229,19 @@ static ConfigurationReader GetConfigurationReader(IConfiguration configuration,
{
var assemblyFinder = dependencyContext == null ? AssemblyFinder.Auto() : AssemblyFinder.ForDependencyContext(dependencyContext);
var section = configuration.GetSection(readerOptions.SectionName);
return new ConfigurationReader(section, assemblyFinder, readerOptions.FormatProvider, configuration);
return new ConfigurationReader(section, assemblyFinder, readerOptions, configuration);
}

static ConfigurationReader GetConfigurationReader(IConfiguration configuration, ConfigurationReaderOptions readerOptions, ConfigurationAssemblySource source)
{
var assemblyFinder = AssemblyFinder.ForSource(source);
var section = configuration.GetSection(readerOptions.SectionName);
return new ConfigurationReader(section, assemblyFinder, readerOptions.FormatProvider, configuration);
return new ConfigurationReader(section, assemblyFinder, readerOptions, configuration);
}

static ConfigurationReader GetConfigurationReader(IConfiguration configuration, ConfigurationReaderOptions readerOptions, IReadOnlyCollection<Assembly> assemblies)
{
var section = configuration.GetSection(readerOptions.SectionName);
return new ConfigurationReader(section, assemblies, new ResolutionContext(configuration, readerOptions.FormatProvider));
return new ConfigurationReader(section, assemblies, new ResolutionContext(configuration, readerOptions));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ class ConfigurationReader : IConfigurationReader
readonly ResolutionContext _resolutionContext;
readonly IConfigurationRoot _configurationRoot;

public ConfigurationReader(IConfigurationSection configSection, AssemblyFinder assemblyFinder, IFormatProvider formatProvider, IConfiguration configuration = null)
public ConfigurationReader(IConfigurationSection configSection, AssemblyFinder assemblyFinder, ConfigurationReaderOptions readerOptions, IConfiguration configuration = null)
{
_section = configSection ?? throw new ArgumentNullException(nameof(configSection));
_configurationAssemblies = LoadConfigurationAssemblies(_section, assemblyFinder);
_resolutionContext = new ResolutionContext(configuration, formatProvider);
_resolutionContext = new ResolutionContext(configuration, readerOptions);
_configurationRoot = configuration as IConfigurationRoot;
}

Expand Down Expand Up @@ -136,7 +136,8 @@ void ProcessLevelSwitchDeclarations()
SubscribeToLoggingLevelChanges(levelSwitchDeclaration, newSwitch);

// make them available later on when resolving argument values
_resolutionContext.AddLevelSwitch(switchName, newSwitch);
var referenceName = _resolutionContext.AddLevelSwitch(switchName, newSwitch);
_resolutionContext.ReaderOptions.OnAddedLevelSwitch?.Invoke(referenceName, newSwitch);
}
}

Expand Down Expand Up @@ -164,7 +165,11 @@ void ApplyMinimumLevel(LoggerConfiguration loggerConfiguration)
var overridenLevelOrSwitch = overrideDirective.Value;
if (Enum.TryParse(overridenLevelOrSwitch, out LogEventLevel _))
{
ApplyMinimumLevelConfiguration(overrideDirective, (configuration, levelSwitch) => configuration.Override(overridePrefix, levelSwitch));
ApplyMinimumLevelConfiguration(overrideDirective, (configuration, levelSwitch) =>
{
configuration.Override(overridePrefix, levelSwitch);
_resolutionContext.ReaderOptions.OnAddedLevelSwitch?.Invoke(overridePrefix, levelSwitch);
});
}
else
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Globalization;
using System.Reflection;
using Microsoft.Extensions.DependencyModel;
using Serilog.Core;

namespace Serilog.Settings.Configuration;

Expand Down Expand Up @@ -56,6 +57,16 @@ public ConfigurationReaderOptions() : this(dependencyContext: null)
/// </summary>
public IFormatProvider FormatProvider { get; init; } = CultureInfo.InvariantCulture;

/// <summary>
/// Called when a log level switch is created while reading the configuration.
/// Log level switches are created either from the <c>Serilog:LevelSwitches</c> section (declared switches) or the <c>Serilog:MinimumLevel:Override</c> section (minimum level override switches).
/// <list type="bullet">
/// <item>For declared switches, the switch name includes the leading <c>$</c> character.</item>
/// <item>For minimum level override switches, the switch name is the (partial) namespace or type name of the override.</item>
/// </list>
/// </summary>
public Action<string, LoggingLevelSwitch> OnAddedLevelSwitch { get; init; }

internal Assembly[] Assemblies { get; }
internal DependencyContext DependencyContext { get; }
internal ConfigurationAssemblySource? ConfigurationAssemblySource { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ sealed class ResolutionContext
readonly IDictionary<string, LoggingFilterSwitchProxy> _declaredFilterSwitches;
readonly IConfiguration _appConfiguration;

public ResolutionContext(IConfiguration appConfiguration = null, IFormatProvider formatProvider = null)
public ResolutionContext(IConfiguration appConfiguration = null, ConfigurationReaderOptions readerOptions = null)
{
_declaredLevelSwitches = new Dictionary<string, LoggingLevelSwitch>();
_declaredFilterSwitches = new Dictionary<string, LoggingFilterSwitchProxy>();
_appConfiguration = appConfiguration;
FormatProvider = formatProvider;
ReaderOptions = readerOptions ?? new ConfigurationReaderOptions();
}

public IFormatProvider FormatProvider { get; }
public ConfigurationReaderOptions ReaderOptions { get; }

/// <summary>
/// Looks up a switch in the declared LoggingLevelSwitches
Expand Down Expand Up @@ -64,18 +64,22 @@ public IConfiguration AppConfiguration
}
}

public void AddLevelSwitch(string levelSwitchName, LoggingLevelSwitch levelSwitch)
public string AddLevelSwitch(string levelSwitchName, LoggingLevelSwitch levelSwitch)
{
if (levelSwitchName == null) throw new ArgumentNullException(nameof(levelSwitchName));
if (levelSwitch == null) throw new ArgumentNullException(nameof(levelSwitch));
_declaredLevelSwitches[ToSwitchReference(levelSwitchName)] = levelSwitch;
var referenceName = ToSwitchReference(levelSwitchName);
_declaredLevelSwitches[referenceName] = levelSwitch;
return referenceName;
}

public void AddFilterSwitch(string filterSwitchName, LoggingFilterSwitchProxy filterSwitch)
public string AddFilterSwitch(string filterSwitchName, LoggingFilterSwitchProxy filterSwitch)
{
if (filterSwitchName == null) throw new ArgumentNullException(nameof(filterSwitchName));
if (filterSwitch == null) throw new ArgumentNullException(nameof(filterSwitch));
_declaredFilterSwitches[ToSwitchReference(filterSwitchName)] = filterSwitch;
var referenceName = ToSwitchReference(filterSwitchName);
_declaredFilterSwitches[referenceName] = filterSwitch;
return referenceName;
}

string ToSwitchReference(string switchName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ public object ConvertTo(Type toType, ResolutionContext resolutionContext)
}
}

return Convert.ChangeType(argumentValue, toType, resolutionContext.FormatProvider);
return Convert.ChangeType(argumentValue, toType, resolutionContext.ReaderOptions.FormatProvider);
}

internal static Type FindType(string typeName)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Globalization;
using System.Globalization;
using System.Reflection;
using Microsoft.Extensions.Configuration;
using Serilog.Events;
Expand All @@ -18,7 +18,7 @@ public ConfigurationReaderTests()
_configurationReader = new ConfigurationReader(
JsonStringConfigSource.LoadSection("{ 'Serilog': { } }", "Serilog"),
AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies),
CultureInfo.InvariantCulture);
new ConfigurationReaderOptions());
}

[Fact]
Expand Down Expand Up @@ -197,7 +197,7 @@ public void MethodsAreSelectedBasedOnCountOfMatchedArgumentsAndThenStringType()
[MemberData(nameof(FlatMinimumLevel))]
public void FlatMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, LogEventLevel expectedMinimumLevel)
{
var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), CultureInfo.InvariantCulture, root);
var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), new ConfigurationReaderOptions(), root);
var loggerConfig = new LoggerConfiguration();

reader.Configure(loggerConfig);
Expand All @@ -221,7 +221,7 @@ public void FlatMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root,
[MemberData(nameof(ObjectMinimumLevel))]
public void ObjectMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, LogEventLevel expectedMinimumLevel)
{
var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), CultureInfo.InvariantCulture, root);
var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), new ConfigurationReaderOptions(), root);
var loggerConfig = new LoggerConfiguration();

reader.Configure(loggerConfig);
Expand Down Expand Up @@ -263,7 +263,7 @@ public void ObjectMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot roo
[MemberData(nameof(MixedMinimumLevel))]
public void MixedMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root, LogEventLevel expectedMinimumLevel)
{
var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), CultureInfo.InvariantCulture, root);
var reader = new ConfigurationReader(root.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), new ConfigurationReaderOptions(), root);
var loggerConfig = new LoggerConfiguration();

reader.Configure(loggerConfig);
Expand All @@ -275,7 +275,7 @@ public void MixedMinimumLevelCorrectOneIsEnabledOnLogger(IConfigurationRoot root
public void NoConfigurationRootUsedStillValid()
{
var section = JsonStringConfigSource.LoadSection("{ 'Nest': { 'Serilog': { 'MinimumLevel': 'Error' } } }", "Nest");
var reader = new ConfigurationReader(section.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), CultureInfo.InvariantCulture, section);
var reader = new ConfigurationReader(section.GetSection("Serilog"), AssemblyFinder.ForSource(ConfigurationAssemblySource.UseLoadedAssemblies), new ConfigurationReaderOptions(), section);
var loggerConfig = new LoggerConfiguration();

reader.Configure(loggerConfig);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,24 @@ namespace Serilog.Settings.Configuration.Tests;

public class ConfigurationSettingsTests
{
static LoggerConfiguration ConfigFromJson(string jsonString, string secondJsonSource = null)
static LoggerConfiguration ConfigFromJson(string jsonString, string secondJsonSource = null, ConfigurationReaderOptions options = null)
{
return ConfigFromJson(jsonString, secondJsonSource, out _);
return ConfigFromJson(jsonString, secondJsonSource, out _, options);
}

static LoggerConfiguration ConfigFromJson(string jsonString, out IConfiguration configuration)
static LoggerConfiguration ConfigFromJson(string jsonString, out IConfiguration configuration, ConfigurationReaderOptions options = null)
{
return ConfigFromJson(jsonString, null, out configuration);
return ConfigFromJson(jsonString, null, out configuration, options);
}

static LoggerConfiguration ConfigFromJson(string jsonString, string secondJsonSource, out IConfiguration configuration)
static LoggerConfiguration ConfigFromJson(string jsonString, string secondJsonSource, out IConfiguration configuration, ConfigurationReaderOptions options)
{
var builder = new ConfigurationBuilder().AddJsonString(jsonString);
if (secondJsonSource != null)
builder.AddJsonString(secondJsonSource);
configuration = builder.Build();
return new LoggerConfiguration()
.ReadFrom.Configuration(configuration);
.ReadFrom.Configuration(configuration, options);
}

[Fact]
Expand Down Expand Up @@ -1345,4 +1345,37 @@ public void FilterWithIsAppliedWithCustomFilter()
log.ForContext("User", "the user").Write(Some.InformationEvent());
Assert.NotNull(evt);
}

[Theory]
[InlineData("$switch1")]
[InlineData("switch1")]
public void TestLogLevelSwitchesCallback(string switchName)
{
var json = $@"{{
'Serilog': {{
'LevelSwitches': {{ '{switchName}': 'Information' }},
'MinimumLevel': {{
'Override': {{
'System': 'Warning',
'System.Threading': 'Debug'
}}
}}
}}
}}";

IDictionary<string, LoggingLevelSwitch> switches = new Dictionary<string, LoggingLevelSwitch>();
var readerOptions = new ConfigurationReaderOptions { OnAddedLevelSwitch = (name, levelSwitch) => switches[name] = levelSwitch };
ConfigFromJson(json, options: readerOptions);

Assert.Equal(3, switches.Count);

var switch1 = Assert.Contains("$switch1", switches);
Assert.Equal(LogEventLevel.Information, switch1.MinimumLevel);

var system = Assert.Contains("System", switches);
Assert.Equal(LogEventLevel.Warning, system.MinimumLevel);

var systemThreading = Assert.Contains("System.Threading", switches);
Assert.Equal(LogEventLevel.Debug, systemThreading.MinimumLevel);
}
}

0 comments on commit e487e42

Please sign in to comment.