Search code examples
c#nlogstructured-logging

NLog structured logging with duplicate hole names


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.

  1. 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!

  1. 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.


Solution

  • 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).