I'm trying to figure it out what is a correct behaviour for capturing properties with duplicate hole names according message templates syntax which is implemented in the NLog.
Let's look an example.
Record to console with duplicate hole names but the count of parameters less than count of names.
// Targets where to log to: File and Console
var logconsole = new NLog.Targets.ConsoleTarget("logconsole");
config.AddRule(global::NLog.LogLevel.Debug, global::NLog.LogLevel.Fatal, logconsole);
// Apply config
NLog.LogManager.Configuration = config;
NLog.LogManager.GetLogger("A").Info("hello from {a} {b} {a}", 1, 2);
The output is
2020-05-26 09:47:37.5013|INFO|A|hello from {a} {b} {a}
There is no any substitutions!
Record to console with duplicate hole names with the same count of parameters.
// Targets where to log to: File and Console
var logconsole = new NLog.Targets.ConsoleTarget("logconsole");
config.AddRule(global::NLog.LogLevel.Debug, global::NLog.LogLevel.Fatal, logconsole);
// Apply config
NLog.LogManager.Configuration = config;
NLog.LogManager.GetLogger("A").Info("hello from {a} {b} {a}", 1, 2, 3);
The output is
2020-05-26 09:49:53.1436|INFO|A|hello from 1 2 3
Substitutions were apply.
So, I am expected to see substitutions in the first case, but there is no. Is it a correct behaviour in the NLog regarding message templates syntax?
I have checked the behaviour in the first case for Serilog. It does substitutions.
That is correct behavior according to NLog documentation - How to use structured logging states
The Names of the parameters should be unique
Though Capturing Rules at messagetemplates.org do not put such a restriction to parameters:
If any of the property names are non-numeric, then all arguments are captured by matching left-to-right with holes in the order in which they appear
NLog has its own interpretation of left-to-right matching - if the count of properties does not match the number of arguments, then format not considered a valid structured message template (Check ParseMessageTemplate method). Which gives you two expected results
Logger.Info("{User} {Value}", "Bob", 42); // "Bob" 42
Logger.Info("{User} {Value} {User}", "Bob", 42); // invalid template
And one not expected (but NLog has warned you to use unique properties):
Logger.Info("{User} {Value} {User}", "Bob", 42, "Joe"); // "Bob" 42 "Joe"
If you expect to get "Joe" 42 "Joe"
on the last example, then you can use Serilog or use numeric property names in message template "{0} {1} {0}"
(not recommended).