-
Notifications
You must be signed in to change notification settings - Fork 129
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multiple value exception, fixes #103 #105
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test looks good. I personally don't understand the problem well enough to say whether this is definitely the best solution
@@ -221,6 +221,10 @@ void ApplyEnrichment(LoggerConfiguration loggerConfiguration, IReadOnlyDictionar | |||
IConfigurationArgumentValue GetArgumentValue(IConfigurationSection argumentSection) | |||
{ | |||
IConfigurationArgumentValue argumentValue; | |||
|
|||
if(argumentSection.Value != null && argumentSection.GetChildren().Any()) | |||
throw new InvalidOperationException($"Combined configuration sources must result in a discrete value (string, int, etc.) or complex value (section, list, etc.), not both. Argument: {argumentSection.Path}"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rest of code uses redundant braces ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please use standard formatting (ctrl K D) can do it - e.g. if (
should have a space
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given this has the same precondition, can move inside the other if to L230
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On reflection - the rest of this file is light on redundant braces; please disregard. would be nice if the exception message can be split across lines (example from further down:
throw new InvalidOperationException(
"A zero-length or whitespace assembly name was supplied to a Serilog.Using configuration statement.");
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed about the if-space -- not my style but definitely seems to be the Serilog way.
On if/else I don't think braces are redundant. I eliminate on stand-alone one-liner ifs, but plenty of other if/else statements elsewhere in the code with braces that could be eliminated.
I think it's much clearer to perform the test separately (my guess is the compiler will optimize anyway, not that it would be measurable).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair point re it being subjective whether moving it inside there makes sense; I'll leave it to your judgement.
// the multiple values are recognized; it will never attempt to locate | ||
// a matching argument. | ||
|
||
var ex = Assert.Throws<InvalidOperationException>(() => ConfigFromJson(jsonDiscreteValue, jsonComplexValue)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you split the line please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't match other Assert.Throws
in the tests or many other lambdas throughout the project, but it doesn't bother me either way, I'll update.
{ | ||
var config = new ConfigurationBuilder().AddJsonString(jsonString).Build(); | ||
var builder = new ConfigurationBuilder().AddJsonString(jsonString); | ||
if(secondJsonSource != null) builder.AddJsonString(secondJsonSource); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please autoformat
@bartelink Thanks for the review. Are Serilog code standards posted somewhere? I just realized after I push my edits, my replies to your review comments will go away, so copying here:
|
@bartelink Maybe I can explain the issue more clearly. Microsoft.Extensions.Configuration allows an application to load configuration information from multiple sources. The most common example is when an application has an Consider these two simple bits of JSON (an extremely oversimplified example): {
"setting" : "discrete"
} versus {
"setting" : [ "foo", "bar", "baz" ]
} After reading the first one, the This package is expecting a single value to provide to a named argument, and there is no way to determine whether the contents of |
There is no serilog style. I worked in a different repo. I put in redundant braces that I hate to fit the style. There was 100% consistency in that repo about redundant braces. |
I'm sure the readme and/or the Issues document all this stuff, but I really appreciate the detailed explanation. Will see if I can provide more useful comments given that context... |
Huh, just learned the "View Changes" button also shows the old comments. I'm also doing work in the SQL sink and that repo is all over the place in terms of style, but I agree, readability is very important. I'm also tempted to add lots of comments but there doesn't seem to be much commenting in the Serilog codebase that I've seen. I did consider splitting the line on the exception message, but even then it's still very long, I'm not sure much is to be gained there. I followed the example of a much longer exception earlier in that file. And it fits just fine on my ridiculous 3200x1800 screen! 😁 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great now - thanks for taking the time to explain.
I agree with your impl being exaclty as it is and only have some wording suggestions.
I'm not a reviewer on this repo or knowledgeable about the config system though, so probably best to not consider my approval to be final, no matter how picky I've been and patient you've been ;)
""Serilog"": { | ||
""Using"": [""TestDummies""], | ||
""WriteTo"": [{ | ||
""Name"": ""DummyRollingFile"", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rolling file is deprecated, maybe example can start using File
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm, I see its used in the rest of the test codebase here; prob leave it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My thinking as well.
|
||
[Trait("Bugfix", "#103")] | ||
[Fact] | ||
public void MultipleArgumentValuesThrowsInvalidOperationException() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
InconsistentComplexVsScalarStateForAConfigurationNodeThrowsIOE
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking:
InconsistentComplexVsScalarArgumentValuesThrowsIOE
=> ConfigFromJson(jsonDiscreteValue, jsonComplexValue)); | ||
|
||
Assert.Contains("Combined configuration sources", ex.Message); | ||
Assert.Contains("pathFormat", ex.Message); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use the '
bounding in the message I added and assert on the full path (can you pin nesting/context from parent too, or is the as good as it gets) ?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what you mean about '
bounding? But the Path
property does provide some additional context. I'll have to do some checking to see exactly how much, though, I think it changes depending on how you retrieve the section. On the other hand, pathFormat
is as "deep" as you can go in this test data and still be processing anything related to the test target, so as-written it is arguably already a valid assertion.
@@ -12,9 +12,12 @@ namespace Serilog.Settings.Configuration.Tests | |||
{ | |||
public class ConfigurationSettingsTests | |||
{ | |||
static LoggerConfiguration ConfigFromJson(string jsonString) | |||
static LoggerConfiguration ConfigFromJson(string jsonString, string secondJsonSource = null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could possibly make this params
array, but rule of threee
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And YAGNI... I can't think of a test scenario where n
config sources would do anything two sources can't represent.
@@ -221,6 +221,10 @@ void ApplyEnrichment(LoggerConfiguration loggerConfiguration, IReadOnlyDictionar | |||
IConfigurationArgumentValue GetArgumentValue(IConfigurationSection argumentSection) | |||
{ | |||
IConfigurationArgumentValue argumentValue; | |||
|
|||
if (argumentSection.Value != null && argumentSection.GetChildren().Any()) | |||
throw new InvalidOperationException($"Combined configuration sources must result in a discrete value (string, int, etc.) or complex value (section, list, etc.), not both. Argument: {argumentSection.Path}"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest comment
// Reject cases where incoming config is mangled such that there are both scalar and aggregate values for this section as invalid
Suggest some elements of following message
"The configuration element: '{argumentSection.Path}', which is the result of combining multiple configuration inputs has an ambiguous set of values.\nPlease ensure inputs consistently supply either a scalar (int, string etc...) or a complex (section, list etc.) value for this argument section.");
"argument section" / "configuration element" / "scalar" are my terms and should only be used if they map to domain terms in serilog.settings.configuration or dotnetcore config stuff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah! A fellow comment-junkie. I was just thinking, I had to explain this in more detail in our discussion, the check in the code probably warrants a blurb.
Sometimes the configuration represents POCOs, for example, so I think I'll stick with "complex types" rather than aggregates. I do like scalar better, though technically MEC treats all discrete values as string -- if your JSON has an int, by the time you get it from config, it'll be the ToString
representation.
Thinking about this for a comment:
Reject configurations where an element has both scalar and complex values as a result of reading multiple configuration sources.
Revised exception message, considering these are always argument values being processed at this point in the code. I also realized there are only three valid scalar types in JSON:
The value for the argument {argumentSection.Path} is assigned different value types in more than one configuration source. Ensure all configurations consistently use either a scalar (int, string, boolean) or a complex (array, section, list, POCO, etc.) type for this argument value.
Of course, now it occurs to me that the very clear exception message probably makes the code comment redundant.
// a matching argument. | ||
|
||
var ex = Assert.Throws<InvalidOperationException>(() | ||
=> ConfigFromJson(jsonDiscreteValue, jsonComplexValue)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
think =>
shoudl go prev line ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think one line would be consistent with the rest of the code. Earlier you suggested the split (I tend to split them myself).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant that the =>
rarely starts a line in normal code and instead goes on prev line
i.e. the full () =>
on prev line
@@ -221,6 +221,10 @@ void ApplyEnrichment(LoggerConfiguration loggerConfiguration, IReadOnlyDictionar | |||
IConfigurationArgumentValue GetArgumentValue(IConfigurationSection argumentSection) | |||
{ | |||
IConfigurationArgumentValue argumentValue; | |||
|
|||
if (argumentSection.Value != null && argumentSection.GetChildren().Any()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
now you've explained it, this precondition check absolutely belongs here on its own and not nested
One can def go overboard on comments, but for example the guard you're adding means nothing to me as I've never written code in the config system but for example can imagine myself ending up reading that code if I meet the exception. In the case of that exception message, if it can't just in time teach me by having a full article (brevity is important), then a comment can do half of the jon
I try to:
as its a good forcing function for confronting the fact that you havent taken the time to write a short letter ;)
:D I have a nice monitor here but 2 monitors in work are 1080 :( In general I look at github stuff in split view too as I need all the help I can get to remember how using short lines actually tends to cause better code in the end, even if it feels artificial!) |
Great feedback. Much happier with the exception message in particular. |
Realized what you meant about
No ambiguity there! |
// a matching argument. | ||
|
||
var ex = Assert.Throws<InvalidOperationException>(() | ||
=> ConfigFromJson(jsonDiscreteValue, jsonComplexValue)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant that the =>
rarely starts a line in normal code and instead goes on prev line
i.e. the full () =>
on prev line
// values as a result of reading multiple configuration sources. | ||
if (argumentSection.Value != null && argumentSection.GetChildren().Any()) | ||
throw new InvalidOperationException( | ||
$"The value for the argument '{argumentSection.Path}' is assigned different value " + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I keep thinking ArgumentException
but I guess that's best reserved for actual method arguments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was my first inclination, too, but then I noticed IOE is used elsewhere in the code for similar "processing" errors.
👍 great discussion here :-) RE code style, unfortunately the Serilog org is pretty massive (a large number of sink repos) and consistency hasn't been our strong point. Mostly aiming for principle-of-least-surprise coding style; as much as I do love a good nitpick, there's a fair bit that comes down to taste and context. LGTM! |
Throws
InvalidOperationException
if multiple configuration sources results in an ambiguous argument value. See #103 for details.