-
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
ObjectArgumentValue can fail to bind to proper Constructors when parameter names are identical #420
Comments
Thanks for the suggestion! Which sink currently has overlapping parameter names of different types? While I agree we could try to succeed in this scenario, it'd also be preferable that we avoid creating sinks that exhibit this behavior - I don't think UX will be great even if we disambiguated by type. |
(Also - things get much more murky when possibilities overlap due to e.g. abstract types or interfaces; probably more complexity than we would want to add here.) |
EventHubProducerClient(string fullyQualifiedNamespace,string eventHubName,AzureNamedKeyCredential credential,EventHubProducerClientOptions clientOptions = default)
EventHubProducerClient(string fullyQualifiedNamespace,string eventHubName,AzureSasCredential credential,EventHubProducerClientOptions clientOptions = default)
EventHubProducerClient(string fullyQualifiedNamespace,string eventHubName,TokenCredential credential,EventHubProducerClientOptions clientOptions = default) Hence the actual constructors come from Azure SDK code and themselves are not controlled by Serilog packages/Sinks.
It's definitely been a struggle trying to work out why it wasn't working for sure, and finding out that the Sink config code literally cannot use the constructor desired via proper config structuring means we've had to fallback to a 'less-than-ideal' solution in that regard.
When working with reflection it's definitely a situation of "try our best" since there are so many combinations of what is possible... I just think maybe a touch more than "if the name matches, it's accurate" would help... this clearly isn't a prevalent situation; probably most other sinks and dependencies properly setup unique constructor signatures based solely on parameter names... either by coincidence or intentional design. |
Essentially, I'm under the expectation that this Serilog config JSON should properly configure an AzureEventHub logging Sink by way of matching this particular {
"Serilog": {
"WriteTo": {
"AzureEventHubSink": {
"Name": "AzureEventHub",
"Args": {
"eventHubClient": {
"$type": "Azure.Messaging.EventHubs.Producer.EventHubProducerClient, Azure.Messaging.EventHubs",
"fullyQualifiedNamespace": "[NAMESPACE].servicebus.windows.net",
"eventHubName": "[NAME]",
"credential": {
"$type": "Azure.Identity.ManagedIdentityCredential, Azure.Identity",
"clientId": "[GUID]"
}
}
}
}
}
} |
Open to exploring what the solution would look like. 1:1 exact type matching might keep expectations (and scope) reasonable. Some careful, minimal refactoring would be needed to avoid spaghettifying some already fairly complex code, I think :) |
In my particular case, an exact type match also wouldn't work since it is creating an overridden type which matches an abstract type on the constructor. Alternatively, I feel a better exception message would go a long way when the chosen constructor signature fails to be instantiated as to explaining the internal logic which is attempting to match up the parameters. Currently the failure will bubble out from the internal reflection binding specifying that the parameter type is incorrect. And again if the provided config JSON does not properly parse into objects or fully match to a constructor, the code falls out to the base attempt of creating the object from the logging configuration and we see "no parameter-less constructor exists", which I found quite confusing as to why the code was trying to make a parameter-less construction when I was clearly providing parameter values in the JSON. |
As a separate workaround which is actually much cleaner for anyone who runs into this in the future, I found it was simple to create a new i.e. concerning the specific above example: Copy paste of the original sink source code, but with overridden 'eventHubClient' parameterpublic static LoggerConfiguration CustomAzureEventHub(
this LoggerSinkConfiguration loggerConfiguration,
CustomEventHubProducerClient eventHubClient,
string outputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] {Message}{NewLine}{Exception}",
IFormatProvider formatProvider = null,
LogEventLevel restrictedToMinimumLevel = LogEventLevel.Verbose,
bool writeInBatches = false,
TimeSpan? period = null,
int? batchPostingLimit = null)
{
if (loggerConfiguration == null) { throw new ArgumentNullException(nameof(loggerConfiguration)); }
if (eventHubClient == null) { throw new ArgumentNullException(nameof(eventHubClient)); }
MessageTemplateTextFormatter formatter = outputTemplate != null
? new MessageTemplateTextFormatter(outputTemplate, formatProvider)
: throw new ArgumentNullException(nameof(outputTemplate));
return loggerConfiguration.AzureEventHub(
formatter,
eventHubClient,
restrictedToMinimumLevel,
writeInBatches,
period,
batchPostingLimit);
} Overridden
|
In situations where the Configuration binding matches identically to multiple public constructors via the naming of parameters all matched between themselves, the complex
ObjectArgumentValue
binding code will only attempt to use the first matched constructor. This will obviously lead to failure in the case where the first constructor has invalid matching types against the provided arguments within a Serilog configuration section.serilog-settings-configuration/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs
Line 187 in cfe1b52
i.e. (rough examples to demonstrate the issue)
Ideally, this could be solved while still picking the first constructor, by matching parameter names in addition to matching criteria for the Types of each corresponding Parameter, which is currently ignored.
serilog-settings-configuration/src/Serilog.Settings.Configuration/Settings/Configuration/ObjectArgumentValue.cs
Lines 168 to 176 in d0a1ba0
The text was updated successfully, but these errors were encountered: