32

I have a small application that is receiving messages from a service bus, which can send through several different types of events for different users. Based on the type of event, a different function is called. I'm logging information in each of these functions.

I currently have this:

var logger = new LoggerConfiguration()
    .MinimumLevel.Information()
    .WriteTo.Console(outputTemplate:
        "[{Timestamp:HH:mm:ss} {Level:u3}] {Message}{NewLine}{Exception}")
    .WriteTo.Loggly()
    .CreateLogger();

...

// logging an event
Log.Information("{UserId} {Event} - Here is my message", "123", "Test Event");

That works fine, but since for this application, every single log will contain both the UserId and Event data in the logs, I figured I could add them to my output template to make my logging code a little cleaner. So I've tried this:

var logger = new LoggerConfiguration()
    .MinimumLevel.Information()
    .WriteTo.Console(outputTemplate:
      "[{Timestamp:HH:mm:ss} {Level:u3}] {UserId} {Event} - {Message}{NewLine}{Exception}")
    .WriteTo.Loggly()
    .CreateLogger();

...

// logging an event
Log.Information("Here is my message", "123", "Test Event");
Log.Information("Here is my message", new { UserId = "123", Event = "Test Event"});

Neither of those work though, all it outputs is my message, it doesn't pass through the UserId or Event that I passed into it.

Am I doing this wrong? Or is there even a way to do it at all?

Steven
  • 18,761
  • 70
  • 194
  • 296

2 Answers2

38

If you want to add properties that aren't part of the message template, then you need to enrich the log context. That means adding the FromLogContext enricher, and adding your properties to your logged event a bit differently.

Log.Logger = new LoggerConfiguration()
    .Enrich.FromLogContext()
    .MinimumLevel.Information()
    .WriteTo.Console(outputTemplate:
    "[{Timestamp:HH:mm:ss} {Level:u3}] {UserId} {Event} - {Message}{NewLine}{Exception}")
    .CreateLogger();

using (LogContext.PushProperty("UserId", "123"))
using (LogContext.PushProperty("Event", "Test Event"))
{
    Log.Information("Here is my message about order {OrderNumber}", 567);
    Log.Information("Here is my message about product {ProductId}", "SomeProduct");
}

You can learn more about enrichment in the documentation.

Now I'm not sure about Loggly. I've never used it before. But you don't even need to modify the output template if you're using Seq (which is from the creators of Serilog and works best with it). Properties added will automatically be available for each event.

As Nicholas Blumhardt pointed out via comment, if you just need to one-off add a property to a single logged event, you can do that as well. I sometimes do this when I have a lot of properties for events that don't necessarily need to show up in the message, and only apply to this single event.

Log
   .ForContext("OrderNumber", orderNumber)
   .ForContext("UserId", user.Id)
   .Information("Order submitted");
mason
  • 31,774
  • 10
  • 77
  • 121
  • Aren't enricher values resolved differently though? It is like ManagedThreadId, which you can't pass to Log.Information. The enrichers implement Enrich method in which they add a property name and value for that property. – Vidmantas Blazevicius Feb 03 '18 at 11:53
  • @VidmantasBlazevicius When you use LogContext.PushProperty it will add properties to all statements within the using block's scope. If those properties are part of the output template, they will show up in the logged message. Otherwise they'll just be different properties on the logged event. – mason Feb 04 '18 at 13:46
  • thanks for clarification, i also came to conclusion that your answer is probably as close as you can get to have the behaviour that the OP wants. – Vidmantas Blazevicius Feb 04 '18 at 13:52
  • 3
    Another option is `Log.ForContext("UserId", 123).ForContext("Event", "Test Event").Information("Hello, world!")` - but `LogContext.PushProperty()` is probably the better API for this use case. – Nicholas Blumhardt Feb 04 '18 at 22:21
  • @NicholasBlumhardt Good point, I've added example of that to the answer and an explanation of when it might be appropriate to use. – mason Feb 05 '18 at 15:30
4

You can use Enrich.WithProperty("ProperyName", "PropertyValue")

Watch this https://www.youtube.com/watch?v=OKITQsF6MNc&t=4831s for various loggers in Asp.Net MVC

Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Information()
                .MinimumLevel.Override("Microsoft", Serilog.Events.LogEventLevel.Warning)
                .MinimumLevel.Override("System", Serilog.Events.LogEventLevel.Warning)
                .WriteTo.File(new Serilog.Formatting.Json.JsonFormatter(), "D:\\Temp\\Serilogs\\structuredLog.json", rollingInterval: RollingInterval.Day, restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Information)
                .Enrich.FromLogContext()
                .Enrich.WithMachineName()
                .Enrich.WithProcessId()
                .Enrich.WithThreadId()
                .Enrich.WithProperty("ApplicationName", "Serilogs DemoApplication")
                .CreateLogger();
Raj K
  • 568
  • 6
  • 11