Glen Thomas
Glen Thomas

Reputation: 10754

Serilog field names for properties not used in message template

I am testing out Serilog and am having some issues field names.

I want to add log entry with one field included in the message template and the other fields just stored in the log for querying against.

I would like to do something simple like this:

logger.Debug("Recalculation performed for operation {OperationId}", 
                operationId, operationTypeId, otherId, anotherId);

But this results in the field names not being given friendly names because they are not in the message template:

{
   "@timestamp":"2016-10-20T16:57:02.0623798+01:00",
   "level":"Debug",
   "messageTemplate":"Recalculation performed for operation {OperationId}",
   "fields":{  
      "OperationId":1024,
      "__1":16,
      "__2":32,
      "__3":256,
      "SourceContext":"SerilogTest.Worker"
   }
}

I know that I can put all of the fields into a class and use the ForContext method to include them in the log entry:

internal class OperationData
{
    public int OperationId { get; set; }

    public int OperationTypeId { get; set; }

    public int OtherId { get; set; }

    public int AnotherId { get; set; }
}

var operationData = new OperationData
                {
                    OperationId = 1024,
                    OperationTypeId = 16,
                    OtherId = 32,
                    AnotherId = 256
                };

var operationLogger = logger.ForContext("OperationData", 
                        operationData, destructureObjects: true);
operationLogger.Debug("Recalculation performed for operation {OperationId}",
                         operationData.OperationId);

This kind of gets me the result I am looking for:

{
   "@timestamp":"2016-10-20T18:00:35.4956991+01:00",
   "level":"Debug",
   "messageTemplate":"Recalculation performed for operation {OperationId}",
   "fields":{  
      "OperationId":1024,
      "OperationData":{  
         "_typeTag":"RecalulationResult",
         "OperationId":1024,
         "OperationTypeId":16,
         "OtherId":32,
         "AnotherId":256
      },
      "SourceContext":"SerilogTest.Worker"
   }
}

BUT, it seems like a lot of effort to go through just to have friendly field names. I have to create a new logger instance, have a type to include all of the relevant fields for the log message and then do the log. Is there an easier way of naming the fields than this?

Upvotes: 4

Views: 2446

Answers (1)

Nicholas Blumhardt
Nicholas Blumhardt

Reputation: 31832

Anonymous types achieve what you have above with a lot less code:

logger
    .ForContext("Operation", new {operationTypeId, otherId, anotherId}, true)
    .Debug("Recalculation performed for operation {OperationId}", operationId);

Or alternatively by including everything in the event:

logger.Debug("Recalculation performed for operation {@Operation}", new {
        Id = operationId, TypeId = operationTypeId, OtherId = otherId, 
        AnotherId = anotherId
    });

If you find there are a lot of messages in which you want to include the same properties, you may be better off pushing them onto the LogContext:

using (LogContext.PushProperty("OperationId", operationId))
{
    logger.Debug("Recalculation performed");

    // ...etc...

    logger.Debug("Something else");
}

In this case, both events will have an OperationId associated with them. You can push multiple properties onto the log context. Just make sure to add Enrich.FromLogContext() to your LoggerConfiguration to use this style.

Upvotes: 5

Related Questions