Skip to content

Commit

Permalink
Merge pull request #348 from 0xced/FormatProvider
Browse files Browse the repository at this point in the history
Add support for IFormatProvider used to convert string to other types
  • Loading branch information
nblumhardt committed Mar 10, 2023
2 parents ab5d7a4 + fbc5e72 commit 0fc8b57
Show file tree
Hide file tree
Showing 12 changed files with 213 additions and 34 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,9 @@ Root section name can be changed:
```

```csharp
var options = new ConfigurationReaderOptions { SectionName = "CustomSection" };
var logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration, sectionName: "CustomSection")
.ReadFrom.Configuration(configuration, options)
.CreateLogger();
```

Expand Down Expand Up @@ -106,8 +107,9 @@ In case of [non-standard](#azure-functions-v2-v3) dependency management you can
```csharp
var functionDependencyContext = DependencyContext.Load(typeof(Startup).Assembly);

var options = new ConfigurationReaderOptions(functionDependencyContext) { SectionName = "AzureFunctionsJobHost:Serilog" };
var logger = new LoggerConfiguration()
.ReadFrom.Configuration(hostConfig, sectionName: "AzureFunctionsJobHost:Serilog", dependencyContext: functionDependencyContext)
.ReadFrom.Configuration(hostConfig, options)
.CreateLogger();
```

Expand All @@ -119,8 +121,9 @@ var configurationAssemblies = new[]
typeof(ConsoleLoggerConfigurationExtensions).Assembly,
typeof(FileLoggerConfigurationExtensions).Assembly,
};
var options = new ConfigurationReaderOptions(configurationAssemblies);
var logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration, configurationAssemblies)
.ReadFrom.Configuration(configuration, options)
.CreateLogger();
```

Expand Down Expand Up @@ -282,6 +285,8 @@ Some Serilog packages require a reference to a logger configuration object. The

When the configuration specifies a discrete value for a parameter (such as a string literal), the package will attempt to convert that value to the target method's declared CLR type of the parameter. Additional explicit handling is provided for parsing strings to `Uri`, `TimeSpan`, `enum`, arrays and custom collections.

Since version 4.0.0, conversion will use the invariant culture (`CultureInfo.InvariantCulture`) as long as the `ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptions options)` method is used. Obsolete methods use the current culture to preserve backward compatibility.

### Static member support

Static member access can be used for passing to the configuration argument via [special](https://github.com/serilog/serilog-settings-configuration/blob/dev/test/Serilog.Settings.Configuration.Tests/StringArgumentValueTests.cs#L35) syntax:
Expand Down Expand Up @@ -377,8 +382,9 @@ public class Startup : FunctionsStartup
var functionDependencyContext = DependencyContext.Load(typeof(Startup).Assembly);

var hostConfig = sp.GetRequiredService<IConfiguration>();
var options = new ConfigurationReaderOptions(functionDependencyContext) { SectionName = "AzureFunctionsJobHost:Serilog" };
var logger = new LoggerConfiguration()
.ReadFrom.Configuration(hostConfig, sectionName: "AzureFunctionsJobHost:Serilog", dependencyContext: functionDependencyContext)
.ReadFrom.Configuration(hostConfig, options)
.CreateLogger();

return new SerilogLoggerProvider(logger, dispose: true);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2013-2016 Serilog Contributors
// Copyright 2013-2016 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -42,6 +42,7 @@ public static class ConfigurationLoggerConfigurationExtensions
/// <param name="dependencyContext">The dependency context from which sink/enricher packages can be located. If not supplied, the platform
/// default will be used.</param>
/// <returns>An object allowing configuration to continue.</returns>
[Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptions readerOptions) instead.")]
public static LoggerConfiguration Configuration(
this LoggerSettingsConfiguration settingConfiguration,
IConfiguration configuration,
Expand All @@ -52,15 +53,8 @@ public static LoggerConfiguration Configuration(
if (configuration == null) throw new ArgumentNullException(nameof(configuration));
if (sectionName == null) throw new ArgumentNullException(nameof(sectionName));

var assemblyFinder = dependencyContext == null
? AssemblyFinder.Auto()
: AssemblyFinder.ForDependencyContext(dependencyContext);

return settingConfiguration.Settings(
new ConfigurationReader(
configuration.GetSection(sectionName),
assemblyFinder,
configuration));
var readerOptions = new ConfigurationReaderOptions(dependencyContext) { SectionName = sectionName, FormatProvider = null };
return Configuration(settingConfiguration, configuration, readerOptions);
}

/// <summary>
Expand All @@ -73,10 +67,11 @@ public static LoggerConfiguration Configuration(
/// <param name="dependencyContext">The dependency context from which sink/enricher packages can be located. If not supplied, the platform
/// default will be used.</param>
/// <returns>An object allowing configuration to continue.</returns>
[Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptions readerOptions) instead.")]
public static LoggerConfiguration Configuration(
this LoggerSettingsConfiguration settingConfiguration,
IConfiguration configuration,
DependencyContext dependencyContext = null)
DependencyContext dependencyContext)
=> Configuration(settingConfiguration, configuration, DefaultSectionName, dependencyContext);

/// <summary>
Expand Down Expand Up @@ -105,7 +100,8 @@ public static LoggerConfiguration ConfigurationSection(
new ConfigurationReader(
configSection,
assemblyFinder,
configuration: null));
configuration: null,
formatProvider: null));
}

/// <summary>
Expand All @@ -118,6 +114,7 @@ public static LoggerConfiguration ConfigurationSection(
/// <param name="sectionName">A section name for section which contains a Serilog section.</param>
/// <param name="configurationAssemblySource">Defines how the package identifies assemblies to scan for sinks and other types.</param>
/// <returns>An object allowing configuration to continue.</returns>
[Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptions readerOptions) instead.")]
public static LoggerConfiguration Configuration(
this LoggerSettingsConfiguration settingConfiguration,
IConfiguration configuration,
Expand All @@ -128,9 +125,8 @@ public static LoggerConfiguration Configuration(
if (configuration == null) throw new ArgumentNullException(nameof(configuration));
if (sectionName == null) throw new ArgumentNullException(nameof(sectionName));

var assemblyFinder = AssemblyFinder.ForSource(configurationAssemblySource);

return settingConfiguration.Settings(new ConfigurationReader(configuration.GetSection(sectionName), assemblyFinder, configuration));
var readerOptions = new ConfigurationReaderOptions(configurationAssemblySource) { SectionName = sectionName, FormatProvider = null };
return Configuration(settingConfiguration, configuration, readerOptions);
}

/// <summary>
Expand All @@ -142,6 +138,7 @@ public static LoggerConfiguration Configuration(
/// <param name="configuration">A configuration object which contains a Serilog section.</param>
/// <param name="configurationAssemblySource">Defines how the package identifies assemblies to scan for sinks and other types.</param>
/// <returns>An object allowing configuration to continue.</returns>
[Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptions readerOptions) instead.")]
public static LoggerConfiguration Configuration(
this LoggerSettingsConfiguration settingConfiguration,
IConfiguration configuration,
Expand All @@ -167,7 +164,7 @@ public static LoggerConfiguration ConfigurationSection(

var assemblyFinder = AssemblyFinder.ForSource(configurationAssemblySource);

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

/// <summary>
Expand All @@ -178,6 +175,7 @@ public static LoggerConfiguration ConfigurationSection(
/// <param name="sectionName">A section name for section which contains a Serilog section.</param>
/// <param name="assemblies">A collection of assemblies that contains sinks and other types.</param>
/// <returns>An object allowing configuration to continue.</returns>
[Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptions readerOptions) instead.")]
public static LoggerConfiguration Configuration(
this LoggerSettingsConfiguration settingConfiguration,
IConfiguration configuration,
Expand All @@ -188,7 +186,8 @@ public static LoggerConfiguration Configuration(
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 readerOptions = new ConfigurationReaderOptions(assemblies) { SectionName = sectionName, FormatProvider = null };
return Configuration(settingConfiguration, configuration, readerOptions);
}

/// <summary>
Expand All @@ -198,9 +197,51 @@ public static LoggerConfiguration Configuration(
/// <param name="configuration">A configuration object which contains a Serilog section.</param>
/// <param name="assemblies">A collection of assemblies that contains sinks and other types.</param>
/// <returns>An object allowing configuration to continue.</returns>
[Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptions readerOptions) instead.")]
public static LoggerConfiguration Configuration(
this LoggerSettingsConfiguration settingConfiguration,
IConfiguration configuration,
params Assembly[] assemblies)
=> Configuration(settingConfiguration, configuration, DefaultSectionName, assemblies);

/// <summary>
/// Reads logger settings from the provided configuration object using the specified context.
/// </summary>
/// <param name="settingConfiguration">Logger setting configuration.</param>
/// <param name="configuration">A configuration object which contains a Serilog section.</param>
/// <param name="readerOptions">Options to adjust how the configuration object is processed.</param>
/// <returns>An object allowing configuration to continue.</returns>
public static LoggerConfiguration Configuration(
this LoggerSettingsConfiguration settingConfiguration,
IConfiguration configuration,
ConfigurationReaderOptions readerOptions = null)
{
var configurationReader = readerOptions switch
{
{ ConfigurationAssemblySource: {} } => GetConfigurationReader(configuration, readerOptions, readerOptions.ConfigurationAssemblySource.Value),
{ Assemblies: {} } => GetConfigurationReader(configuration, readerOptions, readerOptions.Assemblies),
_ => GetConfigurationReader(configuration, readerOptions ?? new ConfigurationReaderOptions(), readerOptions?.DependencyContext),
};
return settingConfiguration.Settings(configurationReader);
}

static ConfigurationReader GetConfigurationReader(IConfiguration configuration, ConfigurationReaderOptions readerOptions, DependencyContext dependencyContext)
{
var assemblyFinder = dependencyContext == null ? AssemblyFinder.Auto() : AssemblyFinder.ForDependencyContext(dependencyContext);
var section = configuration.GetSection(readerOptions.SectionName);
return new ConfigurationReader(section, assemblyFinder, readerOptions.FormatProvider, 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);
}

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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="6.0.0" />
<PackageReference Include="PolySharp" Version="1.12.1" PrivateAssets="All" />
<PackageReference Include="Serilog" Version="2.10.0" />
<None Include="..\..\assets\icon.png" Pack="true" PackagePath="" Visible="false" />
</ItemGroup>
Expand Down
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, IConfiguration configuration = null)
public ConfigurationReader(IConfigurationSection configSection, AssemblyFinder assemblyFinder, IFormatProvider formatProvider, IConfiguration configuration = null)
{
_section = configSection ?? throw new ArgumentNullException(nameof(configSection));
_configurationAssemblies = LoadConfigurationAssemblies(_section, assemblyFinder);
_resolutionContext = new ResolutionContext(configuration);
_resolutionContext = new ResolutionContext(configuration, formatProvider);
_configurationRoot = configuration as IConfigurationRoot;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using System.Globalization;
using System.Reflection;
using Microsoft.Extensions.DependencyModel;

namespace Serilog.Settings.Configuration;

/// <summary>
/// Options to adjust how the configuration object is processed.
/// </summary>
public sealed class ConfigurationReaderOptions
{
/// <summary>
/// Initialize a new instance of the <see cref="ConfigurationReaderOptions"/> class.
/// </summary>
/// <param name="assemblies">A collection of assemblies that contains sinks and other types.</param>
/// <exception cref="ArgumentNullException">The <paramref name="assemblies"/> argument is null.</exception>
/// <exception cref="ArgumentException">The <paramref name="assemblies"/> argument is empty.</exception>
public ConfigurationReaderOptions(params Assembly[] assemblies)
{
Assemblies = assemblies ?? throw new ArgumentNullException(nameof(assemblies));
if (assemblies.Length == 0)
throw new ArgumentException("The assemblies array must not be empty.", nameof(assemblies));
}

/// <summary>
/// Initialize a new instance of the <see cref="ConfigurationReaderOptions"/> class.
/// </summary>
/// <remarks>Prefer the constructor taking explicit assemblies: <see cref="ConfigurationReaderOptions(System.Reflection.Assembly[])"/>. It's the only one supporting single-file publishing.</remarks>
public ConfigurationReaderOptions() : this(dependencyContext: null)
{
}

/// <summary>
/// Initialize a new instance of the <see cref="ConfigurationReaderOptions"/> class.
/// </summary>
/// <param name="dependencyContext">
/// The dependency context from which sink/enricher packages can be located. If <see langword="null"/>, the platform default will be used.
/// </param>
/// <remarks>Prefer the constructor taking explicit assemblies: <see cref="ConfigurationReaderOptions(System.Reflection.Assembly[])"/>. It's the only one supporting single-file publishing.</remarks>
public ConfigurationReaderOptions(DependencyContext dependencyContext) => DependencyContext = dependencyContext;

/// <summary>
/// Initialize a new instance of the <see cref="ConfigurationReaderOptions"/> class.
/// </summary>
/// <param name="configurationAssemblySource">Defines how the package identifies assemblies to scan for sinks and other types.</param>
/// <remarks>Prefer the constructor taking explicit assemblies: <see cref="ConfigurationReaderOptions(System.Reflection.Assembly[])"/>. It's the only one supporting single-file publishing.</remarks>
public ConfigurationReaderOptions(ConfigurationAssemblySource configurationAssemblySource) => ConfigurationAssemblySource = configurationAssemblySource;

/// <summary>
/// The section name for section which contains a Serilog section. Defaults to <c>Serilog</c>.
/// </summary>
public string SectionName { get; init; } = ConfigurationLoggerConfigurationExtensions.DefaultSectionName;

/// <summary>
/// The <see cref="IFormatProvider"/> used when converting strings to other object types. Defaults to the invariant culture.
/// </summary>
public IFormatProvider FormatProvider { get; init; } = CultureInfo.InvariantCulture;

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

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

public IFormatProvider FormatProvider { get; }

/// <summary>
/// Looks up a switch in the declared LoggingLevelSwitches
/// </summary>
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);
return Convert.ChangeType(argumentValue, toType, resolutionContext.FormatProvider);
}

internal static Type FindType(string typeName)
Expand Down
Loading

0 comments on commit 0fc8b57

Please sign in to comment.