-
Notifications
You must be signed in to change notification settings - Fork 247
Put ConsoleLogger writes on a background thread #551
Conversation
} | ||
} | ||
|
||
public bool HasQueuedMessages => _messageQueue.Count > 0; |
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.
BlockingCollection
doesn't expose .IsEmpty
from the underlying ConcurrentQueue
; but testing against .Count > 0
is a lot more expensive.
Wonder if you should use the explicit BlockingCollection(IProducerConsumerCollection<T> collection, int boundedCapacity)
ctor and pass in as the ctor you are calling does; but keep a reference to a ConcurrentQueue for the fast IsEmpty
test?
Or not expose the new HasQueuedMessages
property on ConsoleLogger...
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 make it internal, its only used for testing right now
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.
Or if it's really important, instead of using BlockingCollection, just use a ConcurrentQueue and a SemaphoreSlim.
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.
Would make it internal till someone wants it public; else its exposing a impl detail that someone will take a dependency on...
consoleLogger.ProcessLogQueue(); | ||
} | ||
|
||
private void RegisterForExit() |
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'd remove all of this and just make this disposable. Then call that from the console logger's dispose. Logger providers are disposed "at the right time" when wired up correctly.
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.
Unfortunately loggers aren't IDisposable
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.
Move this to the ConsoleLoggerProvider (those are disposable)
|
||
public void EnqueueMessage(LogMessageEntry message) | ||
{ | ||
_messageQueue.Add(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.
Does this dispatch?
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.
Just adds to the internal data structure and the GetConsumingEnumerable
below is waiting for messages to be added on a separate thread
@@ -86,6 +75,8 @@ public IConsole Console | |||
|
|||
public string Name { get; } | |||
|
|||
public bool HasQueuedMessages => _queueProcessor.HasQueuedMessages; |
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.
Remove this, I don't think we need to expose it.
{ | ||
public class ConsoleLoggerProcessor | ||
{ | ||
private const int _maxQueuedMessages = 1024; |
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 happens when you hit the limit? Does it block the producer?
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.
yes
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.
Nice
We need some throughput tests before and after. |
I'll do some perf testing today |
Test results? :) |
CPU was around 100% the whole time |
private static readonly string _loglevelPadding = ": "; | ||
private static readonly string _messagePadding; | ||
private static readonly string _newLineWithMessagePadding; | ||
|
||
// ConsoleColor does not have a value to specify the 'Default' color | ||
private readonly ConsoleColor? DefaultConsoleColor = null; | ||
|
||
private IConsole _console; | ||
private readonly ConsoleLoggerProcessor _queueProcessor = new ConsoleLoggerProcessor(); |
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 spins up a thread per logger category, we need to move this to the provider so there's a single thread and remeasure.
{ | ||
// TODO: Do after _outputTask.Wait(...) in case there are items blocked on getting added? | ||
_messageQueue.CompleteAdding(); |
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.
Why is Dispose throwing?
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.
You mean the ones we catch and ignore?
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.
Ah yes we just swallow 😄 .Good
private Func<string, LogLevel, bool> _filter; | ||
private IConsole _console; |
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.
Why not just pass the QueueProcessor
as a ctor argument and set the IConsole on the QueueProcessor
instead of on the 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.
Public constructor, internal type. I can do the second one
private static readonly string _loglevelPadding = ": "; | ||
private static readonly string _messagePadding; | ||
private static readonly string _newLineWithMessagePadding; | ||
|
||
// ConsoleColor does not have a value to specify the 'Default' color | ||
private readonly ConsoleColor? DefaultConsoleColor = null; | ||
|
||
private IConsole _console; | ||
private ConsoleLoggerProcessor _queueProcessor; |
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.
readonly
0073dbd
to
ebc1c6e
Compare
{ | ||
public class ConsoleLoggerProcessor : IDisposable | ||
{ | ||
private const int _maxQueuedMessages = 1024; |
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.
File an issue to make this configurable.
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.
LGTM
public ConsoleLoggerProcessor() | ||
{ | ||
// Start Console message queue processor | ||
_outputTask = Task.Factory.StartNew( |
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 we should file an issue to avoid using a dedicated thread. It's probably fine for the most part but it can be done in a way where we don't have a thread spun up all the time. I don't know how much of a problem it is in practice though.
7ab9fd7
to
db73396
Compare
#532
Built on top of #536 from @benaadams