Skip to content

Commit 82375c0

Browse files
committed
confing binding and config section hinting
1 parent 90b87ba commit 82375c0

File tree

5 files changed

+72
-4
lines changed

5 files changed

+72
-4
lines changed

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,37 @@ For example, to set the minimum log level using the _Windows_ command prompt:
9191
set Serilog:MinimumLevel=Debug
9292
dotnet run
9393
```
94+
95+
### Nested configuration sections
96+
97+
Some Serilog packages require a reference to a logger configuration object. These must be "hinted" by appending a `>` character to the end of the argument name. The sample program in this project illustrates this with the following entry configuring the _Serilog.Sinks.Async_ package to wrap the _Serilog.Sinks.File_ package.
98+
99+
Note the `configure>` parameter name with the trailing hint character:
100+
101+
```json
102+
"WriteTo:Async": {
103+
"Name": "Async",
104+
"Args": {
105+
"configure>": [
106+
{
107+
"Name": "File",
108+
"Args": {
109+
"path": "%TEMP%\\Logs\\serilog-configuration-sample.txt",
110+
"outputTemplate": "{Timestamp:o} [{Level:u3}] ({Application}/{MachineName}/{ThreadId}) {Message}{NewLine}{Exception}"
111+
}
112+
}
113+
]
114+
}
115+
},
116+
```
117+
118+
### IConfiguration parameter
119+
120+
If a Serilog package requires additional external configuration information (for example, access to a `ConnectionStrings` section, which would be outside of the `Serilog` section), the sink should include an `IConfiguration` parameter in the configuration extension method. This package will automatically populate that parameter. It should not be declared in the argument list in the configuration source.
121+
122+
### Complex parameter value binding
123+
124+
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` and `TimeSpan` objects and `enum` elements.
125+
126+
If the parameter value is not a discrete value, the package will use the configuration binding system provided by _Microsoft.Extensions.Options.ConfigurationExtensions_ to attempt to populate the parameter. Almost anything that can be bound by `IConfiguration.Get<T>` should work with this package. An example of this is the optional `List<Column>` parameter used to configure the .NET Standard version of the _Serilog.Sinks.MSSqlServer_ package.
127+

sample/Sample/appsettings.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"WriteTo:Sublogger": {
1212
"Name": "Logger",
1313
"Args": {
14-
"configureLogger": {
14+
"configureLogger>": {
1515
"WriteTo": [
1616
{
1717
"Name": "Console",
@@ -28,7 +28,7 @@
2828
"WriteTo:Async": {
2929
"Name": "Async",
3030
"Args": {
31-
"configure": [
31+
"configure>": [
3232
{
3333
"Name": "File",
3434
"Args": {

src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
<PackageProjectUrl>https://github.com/serilog/serilog-settings-configuration</PackageProjectUrl>
1818
<PackageLicenseUrl>https://www.apache.org/licenses/LICENSE-2.0</PackageLicenseUrl>
1919
<RootNamespace>Serilog</RootNamespace>
20+
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
21+
<Version>3.0.0.002</Version>
2022
</PropertyGroup>
2123

2224
<ItemGroup>
@@ -26,10 +28,12 @@
2628

2729
<ItemGroup Condition="'$(TargetFramework)' == 'net451'">
2830
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="1.1.2" />
31+
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.2" />
2932
</ItemGroup>
3033

3134
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
3235
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.0.1" />
36+
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.0" />
3337
</ItemGroup>
3438

3539
</Project>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using Microsoft.Extensions.Configuration;
2+
using Serilog.Core;
3+
using System;
4+
using System.Collections.Generic;
5+
6+
namespace Serilog.Settings.Configuration
7+
{
8+
class BoundArgumentValue : IConfigurationArgumentValue
9+
{
10+
readonly IConfigurationSection section;
11+
12+
public BoundArgumentValue(IConfigurationSection section)
13+
{
14+
this.section = section;
15+
}
16+
17+
public object ConvertTo(Type toType, IReadOnlyDictionary<string, LoggingLevelSwitch> declaredLevelSwitches)
18+
{
19+
return section.Get(toType);
20+
}
21+
}
22+
}

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ namespace Serilog.Settings.Configuration
1919
class ConfigurationReader : IConfigurationReader
2020
{
2121
const string LevelSwitchNameRegex = @"^\$[A-Za-z]+[A-Za-z0-9]*$";
22+
const string ConfigSectionHintChar = ">";
2223

2324
readonly IConfigurationSection _configuration;
2425
readonly DependencyContext _dependencyContext;
@@ -209,7 +210,7 @@ internal ILookup<string, Dictionary<string, IConfigurationArgumentValue>> GetMet
209210
let name = GetSectionName(child)
210211
let callArgs = (from argument in child.GetSection("Args").GetChildren()
211212
select new {
212-
Name = argument.Key,
213+
Name = argument.Key.Replace(ConfigSectionHintChar, string.Empty),
213214
Value = GetArgumentValue(argument) }).ToDictionary(p => p.Name, p => p.Value)
214215
select new { Name = name, Args = callArgs }))
215216
.ToLookup(p => p.Name, p => p.Args);
@@ -225,7 +226,14 @@ IConfigurationArgumentValue GetArgumentValue(IConfigurationSection argumentSecti
225226
}
226227
else
227228
{
228-
argumentValue = new ConfigurationSectionArgumentValue(new ConfigurationReader(argumentSection, _configurationAssemblies, _dependencyContext));
229+
if(argumentSection.Key.EndsWith(ConfigSectionHintChar))
230+
{
231+
argumentValue = new ConfigurationSectionArgumentValue(new ConfigurationReader(argumentSection, _configurationAssemblies, _dependencyContext));
232+
}
233+
else
234+
{
235+
argumentValue = new BoundArgumentValue(argumentSection);
236+
}
229237
}
230238

231239
return argumentValue;

0 commit comments

Comments
 (0)