-
Notifications
You must be signed in to change notification settings - Fork 206
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Phase one logger optimisations #2503
Conversation
- Cache scoped loggers to avoid allocations per span, transaction and error
edcaf70
to
02c433f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall I totally agree with the direction. 👍
Below one thing about replacing the ?.
notation to check log levels. Less relevant from the allocation's perspective, but I think it'd be still worth keeping that.
@ArminShoeibi Formatters is |
My bad, it has a lock itself |
No worries. It's hard to keep track! |
095cb52
to
d98737d
Compare
d98737d
to
b30ad1b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the updates.
lgtm
This PR dramatically reduces the overhead of scoped logging, even for scenarios where the configured log level verbosity was low (e.g., Warning). The most critical improvement is caching scoped loggers within Spans, Traces, and Errors. We lose convenience in no longer having the span ID in the scope, but it's generally available from the Span argument.
Previously, a new
ScopedLogger
was created for every span and transaction, and a newConditionalWeakTable
was also allocated. Due to the use of Finalisers on the CWT, this had an even more significant GC impact. We now maintain a cache of scoped loggers (of which there are relatively few).The ID creation for the Spans has been switched to
ActivitySpanId.CreateRandom().ToString()
from the MS diagnostics library, as this has been more heavily optimised than our original version and achieves the same goal.In
Tracer.End()
, we used anAssertion
with a large interpolated string. This was being interpolated for each transaction as theAssertion
path was enabled. However, the condition we assert is generally not met. Therefore, we now check this manually to only create the message if we are in an invalid state.I've also switched two structs to record structs, as these were used as keys in dictionaries and ended up being boxed due to a lack of
IEquality
implementations.These are phase one of optimisations for the logging implementation and address the general production scenario of using a less verbose log level.
Profiling
Profiling was achieved using a .NET 9 ASP.NET Core minimal API project with 20 concurrent connections and 100,000 requests.
Before Memory Profile
After Memory Profile
Before Top Retained
After Top Retained
Closes #2500