serilog-settings-configuration icon indicating copy to clipboard operation
serilog-settings-configuration copied to clipboard

Read from IConfiguration without SectionKey

Open krafs opened this issue 3 years ago • 11 comments

I'm using Serilog.Settings.Configuration to configure Serilog, and I came across an issue. If I have an IConfiguration that consists of the Serilog-section, how can I configure Serilog with it? ConfigurationLoggerConfigurationExtensions.ConfigurationSection only takes an IConfigurationSection, whilst I have an IConfiguration. And even if I did have a section - it's marked as obsolete. ConfigurationLoggerConfigurationExtensions.Configuration takes an IConfiguration, but it requires an IConfiguration that is one step back, so it can use the sectionKey to get into it.

Now, I understand that you can't just add an overload to the abovementioned method, because an overload without the sectionkey already defaults to the key being "Serilog". Or is there perhaps some other way that I've missed? Apart from "just provide an IConfiguration that isn't already zoomed in to the Serilog section" :)

krafs avatar Nov 22 '21 22:11 krafs

I had a quick look and can't spot a way to achieve this currently. Whipping up your own IConfiguration implementation that wraps the one you're holding could be a quick unblocker, though.

Longer term we might look at accepting a null section name - but, it's a bit of a niche case, so not entirely likely we'd add this.

Any more info about why/how you're hitting this requirement? Thanks!

nblumhardt avatar Nov 24 '21 21:11 nblumhardt

I've inherited a code base where IConfigurations are distributed already-zoomed-into. Yeah, I've considered making my own IConfiguration-wrapper for stepping back a step into the outer config section. Definitely possible, but would prefer a native solution. It's probably also possible to get ahold of a different IConfiguration in my specific case.

Still, is there a reason I'm not able to give Serilog exactly what it needs? It doesn't feel right to provide it with the entire outer configuration block. And if there are reasons, why is that the default?

krafs avatar Nov 25 '21 15:11 krafs

@krafs thanks for the reply. Perhaps you're right - it could just be historical accident at this point.

If you're interested in investigating further and putting a PR together, it seems reasonable to take a closer look 👍

nblumhardt avatar Nov 25 '21 22:11 nblumhardt

@krafs , @nblumhardt the reason why IConfigurationSection parameters were obsoleted is to support IConfiguration sinks arguments, such as Serilog.Sinks.MSSqlServer's (in order to get root ConnectionStrings section etc). See #148 and related PRs/issues.

This is why root configuration is needed, so it is probably "by design" issue.

skomis-mm avatar Nov 26 '21 11:11 skomis-mm

Thanks for the note @skomis-mm!

I think where this gets confusing is that IConfigurationSection implements IConfiguration anyway, so the API we have here doesn't really enforce the kind of usage that the MSSQL sink requires (one can still pass a nested IConfiguration to ReadFrom.Configuration(), as long as it has a sub-key for Serilog, but it doesn't have to be the root).

Unless I'm misunderstanding the situation, I think the desire to pass both the root configuration and the one to read from as the same argument might be problematic - going back in time, would we consider separating them to allow something like this?

    .ReadFrom.Configuration(configuration.GetSection("MySerilog"), root: configuration)

where the second root parameter would be optional and really only required for MSSQL. 🤔

nblumhardt avatar Nov 26 '21 21:11 nblumhardt

I think where this gets confusing is that IConfigurationSection implements IConfiguration anyway, so the API we have here doesn't really enforce the kind of usage that the MSSQL sink requires (one can still pass a nested IConfiguration to ReadFrom.Configuration()

Indeed.. 🤔

I beleive the intent was to have overloads of .ReadFrom.Configuration(..) accepting only IConfigurationRoot type. But IConfigurationRoot is not UX friendly because of need to cast to that type in asp.net/generic host scenarios (HostBuilderContext/WebHostBuilderContext expose IConfiguration type). Because of this IConfiguration is used like a root configuration.

.ReadFrom.Configuration(configuration.GetSection("MySerilog"), root: configuration)

Looks good to me. 👍 Probably move it to .ReadFrom.ConfigurationSection(..) overloads? And since this is a new overload, we can accept IConfiguration as root parameter and add runtime check if it is really IConfiguratonRoot.

Thoughts?

skomis-mm avatar Nov 29 '21 20:11 skomis-mm

Seems like a reasonable set of trade-offs 👍

nblumhardt avatar Dec 02 '21 07:12 nblumhardt

I feel like the best fit would have been as an overload for .ReadFrom.Configuration, but seeing as the preferred signature would look something like .ReadFrom.Configuration(IConfiguration serilogConfig, IConfiguration root = null) that won't work, as it would conflict with existing overloads.

If we do add it to .ReadFrom.ConfigurationSection, how about using IConfiguration instead of IConfigurationSection? So a signature like e.g. .ReadFrom.ConfigurationSection(IConfiguration configuration)?

krafs avatar Dec 03 '21 20:12 krafs

I'm not 100% sure it would actually conflict - if we just add the IConfiguration root = null argument to the existing method, the calls would look like:

    // Section name defaults to "Serilog", argument is assumed to be at the root, everything
    // works as it does today
    .ReadFrom.Configuration(configuration)

    // Passed-in configuration is the Serilog section, root not available
    .ReadFrom.Configuration(configuration, sectionName: null)

    // Passed-in configuration is the Serilog section, root supplied for MSSQL and any other
    // sinks that need it (probably quite uncommon)
    .ReadFrom.Configuration(configuration, sectionName: null, root: ...)

Passing a null sectionName is slightly strange but not unprecedented 🤔

nblumhardt avatar Dec 08 '21 01:12 nblumhardt

Ah, right. Today, all overloads throw ArgumentNullException if the provided sectionName is null. But changing null to instead indicate that the given IConfiguration is the serilog section would work. As you say - slightly strange, but seems perfectly fine given the circumstances :)

I was going to suggest making sectionName nullable, but seems like the project doesn't have nullable reference types enabled anyway, so I guess that's not an issue.

krafs avatar Dec 08 '21 07:12 krafs

👍 I'll come up with something based on your suggestions

skomis-mm avatar Dec 08 '21 18:12 skomis-mm