BeginScope with Serilog

@Nicholas's answer is correct. I just wanted to drop a small worked example for anyone else coming to this thread to point out something that's not so obvious;

Here some example code that shows how to use Serilog context;

void Main()
{
    var log = Log.Logger = new LoggerConfiguration()
    .Enrich.FromLogContext()
    .MinimumLevel.Information()
    .WriteTo.Console()
    
    .CreateLogger();
    Log.Logger = log;
    log.Information("Starting app");
    
    Task.WaitAll(
        Task.Run (() => GetNums(10,15, 2, "evens")),
        Task.Run (() => GetNums(11, 15, 2, "odds"))
    );
    
    log.Information("Finished.");

    Console.ReadLine();
    
}

static Random R = new Random();

static void GetNums(int start, int cnt, int inc, string name)
{
    using(var log = LogContext.PushProperty("name", name))
    {
        for (int i = start; i < start + cnt; i+=inc)
        {
            Log.Information("{number},{name}", i);
            Thread.Sleep(R.Next(500));
        }
    }
}

produces this output

[17:11:31 INF] Starting app
[17:11:31 INF] 10,evens
[17:11:31 INF] 11,odds
[17:11:31 INF] 12,evens
[17:11:31 INF] 14,evens
[17:11:32 INF] 13,odds
[17:11:32 INF] 16,evens
[17:11:32 INF] 15,odds
[17:11:32 INF] 18,evens
[17:11:32 INF] 20,evens
[17:11:32 INF] 17,odds
[17:11:32 INF] 22,evens
[17:11:33 INF] 24,evens
[17:11:33 INF] 19,odds
[17:11:33 INF] 21,odds
[17:11:34 INF] 23,odds
[17:11:34 INF] 25,odds
[17:11:34 INF] Finished.

The thing I wanted to point out, is that the items logged to the context won't appear in the logs, (i.e. won't be logged) if the named value does not appear in your template.

For example, in the code above, if we change Log.Information("{number},{name}",i); to Log.Information("{number}",i); then this is what you would get, and this is similar to what you might get if you don't configure enrichment Enrich.FromLogContext(). It's subtle and easy to forget to update the template and chase your tail trying to find the bug.

without template string including the named properties included in the context

[17:22:11 INF] Starting app
[17:22:11 INF] 10
[17:22:11 INF] 11
[17:22:11 INF] 12
[17:22:11 INF] 13
[17:22:11 INF] 15
[17:22:11 INF] 17
[17:22:12 INF] 14
[17:22:12 INF] 16
[17:22:12 INF] 19
[17:22:12 INF] 18
[17:22:12 INF] 20
[17:22:12 INF] 21
[17:22:12 INF] 23
[17:22:12 INF] 22
[17:22:12 INF] 24
[17:22:13 INF] 25
[17:22:13 INF] Finished.

also, the enriched properties are appended to the end of the string, so Log.Information("{name},{number}", i); would produce

[17:35:11 INF] Starting app
[17:35:11 INF] 11,{number}
[17:35:11 INF] 10,{number}

Happy logging! Hope this helped.


Yes, Serilog has a native equivalent of this feature, called using LogContext.PushProperty().

To enable this, you first must add Enrich.FromLogContext() to your Serilog LoggerConfiguration, for example:

Log.Logger = new LoggerConfiguration()
    .Enrich.FromLogContext() // <- this line
    .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} " +
                                     "{Properties:j}{NewLine}{Exception}"))
    .CreateLogger();

Then, to push properties onto the context (equivalent to ILogger.BeginScope()), use:

using (LogContext.PushProperty("OrderId", 1234))
{
    Log.Information("Processing a new order");
    // ...etc
}

The configuration above includes {Properties:j} to ensure all event properties, such as those from the log context, are included in the output. How you'll view the additional properties will depend upon which sink you're using.