serilog / serilog-extensions-logging

Serilog provider for Microsoft.Extensions.Logging
Apache License 2.0
307 stars 97 forks source link

Technical question on BeginScope mechanism #153

Closed AroglDarthu closed 4 years ago

AroglDarthu commented 4 years ago

@nblumhardt Hi Nicholas,

The last couple of hours I have been breaking my head on the scoping mechanism of Serilog.Extensions.Logging. Although the usage pattern is clear to me, I am having a hard time grasping the technical details on it. More precisely I wonder how the scopes do not get intermingled in a multi-threaded scenario.

It seems to me that, because the same provider is being used for every BeginScope, race conditions should occur on setting the CurrentScope property of that provider and on setting the Parent on each SerilogLoggerScope.

However (and luckily so), my attempts to create a sample console app that proves loggers get 'mixed' scopes have failed miserably. Hence I am left with the question: "What keeps these scope stacks from intermingling?"

bartelink commented 4 years ago

See https://gitter.im/serilog/serilog - I suspect your question is exactly the sane as ThomasArdal's over there? (TL;DR ExecutionContext flows and LoggerContext hangs off that, then the logging grabs (serializes/destructures) the state from there when the logging happens)

AroglDarthu commented 4 years ago

Maybe... I did not drill into the code of Serilog itself. The link that I am missing is probably the one where a SerilogLoggerScope gets hooked up to the LogContext.

Just looking at the code of Serilog.Extensions.Logging that link truely escapes me. :-(

nblumhardt commented 4 years ago

Hi @AroglDarthu - the magic is in AsyncLocal<T>:

https://github.com/serilog/serilog-extensions-logging/blob/c5bad0c183ae10d448fcc3532095e298d57906a1/src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerProvider.cs#L88

There's a bit of info in: https://docs.microsoft.com/en-us/dotnet/api/system.threading.asynclocal-1?view=netframework-4.8

HTH!

daiplusplus commented 4 years ago
bartelink commented 4 years ago

There's a decent article out there on the ExecutionContext - this mechanism is a core par of the standard .NET runtime plumbing - lots of stuff would break if it wasn't threaded through correctly. When a Task hops threads etc, the context is explicitly transferred. With regard to efficiency, by definition the specific structure [for a given thread] can only have a single accessor at a time so it's pretty likely to be easy to make efficient. If you peek around the Serilog repo, you'll see lots of emphasis on perf scenarios, so TL;DR it's plenty fast and is not something to worry about using ;)

sungam3r commented 4 years ago

Are there any cases where AsyncLocal will fail to correctly capture the logging context?

There is some special API for "clearing" async context. Sometimes it is useful.