-
Notifications
You must be signed in to change notification settings - Fork 77
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
SLVS-1723 Introduce log contexts #5913
SLVS-1723 Introduce log contexts #5913
Conversation
771a9bc
to
c098c1b
Compare
c098c1b
to
6fdc8e7
Compare
ba2069e
to
d01d7dd
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.
I am not really happy with the design decisions as we are creating a lot of instances of the logger and the context, which will increase the memory usage and the performance overhead especially because logging is a very used operation.
I would rather make the LoggerBase and LoggerContextManager Mef exportable components with the creation policy non-shared.
Then the contextManager of a logger can be configured to hold a specific context or to enhance existing contexts.
[Export(typeof(ILoggerFactory))] | ||
[PartCreationPolicy(CreationPolicy.NonShared)] | ||
[method: ImportingConstructor] | ||
public class LoggerFactory(ILoggerContextManager loggerContextManager) : ILoggerFactory |
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.
The LoggerFactory is a level an abstraction that does not actually bring any value. It's just used in a test class and in the Container.cs.
Please remove it completely and all test classes related to it
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.
LoggerContextManager implementation is not accessible to Container.cs (as it is an internal class). I think this is a better solution than making the class public. Also it means that users of LoggerBase don't need to mef-export the manager and it also allows to use Substitute (by returning ILogger)
src/Core/Logging/LoggerBase.cs
Outdated
} | ||
|
||
private static StringBuilder AppendProperty(StringBuilder builder, string property) => | ||
builder.Append('[').Append(property).Append(']').Append(' '); |
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.
I think this is a lot more readable
builder.Append('[').Append(property).Append(']').Append(' '); | |
builder.AppendFormat("[{0}] ", property); |
src/Core/Logging/LoggerBase.cs
Outdated
builder.Append('[').Append(property).Append(']').Append(' '); | ||
|
||
private static StringBuilder AppendPropertyFormat(StringBuilder builder, string property, params object[] args) => | ||
builder.Append('[').AppendFormat(property, args).Append(']').Append(' '); |
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.
A lot more readable
builder.Append('[').AppendFormat(property, args).Append(']').Append(' '); | |
builder.AppendFormat("[{0}] ", new StringBuilder().AppendFormat(property, args)); |
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.
Technically this is faster and has less allocations
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.
Chars about 2x faster than double formatting and uses less memory. I don't think it's that unreadable.
src/Core/Logging/LoggerBase.cs
Outdated
private static StringBuilder AppendProperty(StringBuilder builder, string property) => | ||
builder.Append('[').Append(property).Append(']').Append(' '); | ||
|
||
private static StringBuilder AppendPropertyFormat(StringBuilder builder, string property, params object[] args) => |
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.
Please make the return type for both AppendProperty void. Or create extension methods for this class
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.
What's the motivation for that?
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.
The return type of AppendPropertyFormat
is not used. Additionally, it feels awkward to receive a stringBuilder instance then return it unchanged.
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.
Created extension methods
ILoggerWriter writer, | ||
ILoggerSettingsProvider settingsProvider) : ILogger | ||
{ | ||
public ILogger ForContext(params string[] context) => |
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.
Creating a new instance for each context feels like a very bad design decision as this will increase the memory usage and the performance overhead.
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.
This is not a correct conclusion. The instances are lightweight (partially thanks to immutable lists and lazy string property initialization, but in general they don't hold a lot of data apart from a few object references and the aforementioned property list and formatted string). This will not increase the overhead in any meaningful way.
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.
On top of that, most of these instances will be created in user-class constructors and therefore will be long-living.
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.
It is a correct conclusion and it will bring more pressure to the garbage collector, which will impact the performance.
On top of that, most of these instances will be created in user-class constructors and therefore will be long-living.
This is correct for the LoggerBase instances. That's why I see no reason why it shouldn't be a Mef component with a non-shared creation policy (which will basically lead to the same thing but without the need of factories).
This is not true, however for the LoggerContextManager and I think we can find better design than just creating instances for each context.
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.
Again, that's not true. It will, of course impact the performance, as pretty much every single added allocation or instruction does, but the question is will it impact it in any meaningful way? The answer is no. It has less of an impact on memory than the existing string allocations inside the logger.
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.
Uncustomized logger context manager has references to 2 empty immutable lists and 2 non-initialized lazies...
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.
Added log methods overloads for single-time-use contexts
public string FormatedContext => formatedContext.Value; | ||
public string FormatedVerboseContext => formatedVerboseContext.Value; | ||
|
||
public ILoggerContextManager CreateAugmentedContext(IEnumerable<string> additionalContexts) => new LoggerContextManager(contexts.AddRange(additionalContexts), verboseContexts); |
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.
Creating a new instance for each context feels like a very bad design decision as this will increase the memory usage and the performance overhead.
d01d7dd
to
dc2b4c0
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.
I still have some suggestions, but the implementation seems better now
} | ||
internal static class StringBuilderLoggingExtensions | ||
{ | ||
public static StringBuilder AppendProperty(this StringBuilder builder, string property) => |
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.
I would rename it
public static StringBuilder AppendProperty(this StringBuilder builder, string property) => | |
public static StringBuilder AppendContextProperty(this StringBuilder builder, string property) => |
WriteLine(messageFormat, args); | ||
} | ||
} | ||
public static StringBuilder AppendPropertyFormat(this StringBuilder builder, string property, params object[] args) => |
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.
I would rename it
public static StringBuilder AppendPropertyFormat(this StringBuilder builder, string property, params object[] args) => | |
public static StringBuilder AppendContextPropertyFormat(this StringBuilder builder, string property, params object[] args) => |
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.
Log properties also include thread id, debug, possibly other things. Context is a word I used to specifically describe the log properties that are set for a certain logical scope
/// <summary> | ||
/// Logs a message and appends a new line. | ||
/// </summary> | ||
void WriteLine(string message); |
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.
Let's get rid of this overload to have less clutter in the interface
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.
technically this overload is faster than the format overload, because string/sb format checks for {0}
in the string even if the parameters list is empty
writer, | ||
settingsProvider); | ||
|
||
public void WriteLine(string message) => |
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.
We could actually get rid of this overload.
void LogVerbose(string messageFormat, params object[] args); | ||
void LogVerbose(MessageLevelContext context, string messageFormat, params object[] args); | ||
|
||
ILogger ForContext(params string[] context); |
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.
Consider renaming these two methods (lines 38 and 40) with something like GetNewWithContext, CreateNewWithContext.
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.
I think it's enough that it has a return type of ILogger, this implies that it's a new instance
… responsibility from LoggerBase to LoggerContextManager, added StringBuilderLoggingExtensionsTests
Quality Gate passedIssues Measures |
9051497
into
feature/hardening-q4
SLVS-1723