Search code examples
c#serilog

Thread safety of the ambient LogContext in Serilog


According to this article, you can attach a request ID to an ambient context (the LogContext) in Serilog like this:

using (LogContext.PushProperty("RequestId", Request.Id))
{
    // Process request; all logged events will carry `RequestId`
    Log.Information("Adding {Item} to cart {CartId}", item, cart.Id);
}

And that is fine, and seems to be working. I have two scenarios where I need this:

  • Add a correlation ID to logs per request to an API.
  • Add a correlation ID per message read of a queue.

What has me confused is what happens if multiple requests hits the API at once, or we process multiple messages concurrently. Since LogContext is a static class, can I accidentally overwrite the RequestId property in the LogContext with values from another thread than the one who originally set the property?

Lets say thread 1 sets the RequestId to 1. Before it is finished, thread 2 sets the RequestId to 2 because we received another request. Will thread 1 now use 2 as the value for RequestId when logging? I'm guessing it wont, but can someone explain why? According this this, pushing a property with the same name as an existing property onto the stack overwrites it:

Pushing property onto the context will override any existing properties with the same name, until the object returned from PushProperty() is disposed, as the property A in the example demonstrates.

Does Serilog have one stack per handle returned from the LogContext.PushProperty(...) method, or something? Is there any way I can override RequestId, perhaps short of calling LogContext.PushProperty("RequestId", Request.Id) again?


Solution

  • LogContext is thread-safe via AsyncLocal/ThreadStatic. Each thread has their own LogContext despite it being accessed through a static class. Requests don't share the same LogContext.