42

I'm using Serilog with an MS SQL Server sink in my application. Let's assume I have defined the following class ...

public class Person
{
  public string FirstName { get; set; }
  public string LastName { get; set; }

  public DateTime BirthDate { get; set; }
  // ... more properties
}

... and created an instance:

var person = new Person
{
    FirstName = "John",
    LastName = "Doe",
    BirthDate = DateTime.UtcNow.AddYears(-25)
};

I have placed the following log call in my code:

Log.Information("New user: {FirstName:l} {LastName:l}",
    person.FirstName, person.LastName);

Is it possible to also log the BirthDate property without adding it to the message template so that it's rendered within the Properties XML column? I'd like to output it later in a details view of my application's log viewer.

I'm basically looking for a behavior similar to the object destructuring, but without printing the flat object as part of the log message.

Julian
  • 33,915
  • 22
  • 119
  • 174
Marius Schulz
  • 15,976
  • 12
  • 63
  • 97

3 Answers3

51

This is as simple as:

Log.ForContext("BirthDate", person.BirthDate)
   .Information("New user: {FirstName:l} {LastName:l}",
                           person.FirstName, person.LastName);
Nicholas Blumhardt
  • 30,271
  • 4
  • 90
  • 101
  • I see, that's pretty simple. Does that method also allow for destructuring of objects that have a complex type (such as a *Father* property of type `Person`)? I'm more interested in the properties' values than the `ToString()` representation. – Marius Schulz Jan 12 '15 at 09:31
  • 10
    Absolutely - `Log.ForContext("Father", father, destructureObjects: true)` does that. – Nicholas Blumhardt Jan 12 '15 at 12:08
  • Perfect! Exactly what I was looking for. I read the manual, but I must have somehow overlooked that. Thank you! – Marius Schulz Jan 12 '15 at 13:13
34

You can actually do this in a few different ways. In your case, the first way is probably the best:

Log.ForContext("BirthDate", person.BirthDate)
    .Information("New user: {FirstName:l} {LastName:l}",
        person.FirstName, person.LastName);

But you can also use the LogContext in other scenarios:

Log.Logger = new LoggerConfiguration()
    // Enrich all log entries with properties from LogContext
    .Enrich.FromLogContext();

using (LogContext.PushProperty("BirthDate", person.BirthDate))
{
    Log.Information("New user: {FirstName:l} {LastName:l}",
        person.FirstName, person.LastName);
}

Or, in the case where you want to log a "constant" property, you can add it like this:

Log.Logger = new LoggerConfiguration()
    // Enrich all log entries with property
    .Enrich.WithProperty("Application", "My Application");

See Context and correlation – structured logging concepts in .NET (5) for more information.

Christian Davén
  • 16,713
  • 12
  • 64
  • 77
15

If you're using the generic Microsoft ILogger interface you can use BeginScope;

using (_logger.BeginScope(new Dictionary<string, object> { { "LogEventType", logEventType }, { "UserName",  userName } }))
{
    _logger.LogInformation(message, args);
}

This is discussed here; https://blog.rsuter.com/logging-with-ilogger-recommendations-and-best-practices/

Josh
  • 3,442
  • 2
  • 23
  • 24