Skip to content
This repository has been archived by the owner on Dec 13, 2018. It is now read-only.

Put ConsoleLogger writes on a background thread #551

Merged
merged 2 commits into from
Feb 17, 2017
Merged

Conversation

BrennanConroy
Copy link
Member

#532

Built on top of #536 from @benaadams

}
}

public bool HasQueuedMessages => _messageQueue.Count > 0;
Copy link
Contributor

@benaadams benaadams Feb 9, 2017

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...

Copy link
Member Author

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

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.

Copy link
Contributor

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()
Copy link
Member

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.

Copy link
Member Author

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

Copy link
Member

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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this dispatch?

Copy link
Member Author

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;
Copy link
Member

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;
Copy link
Member

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?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

@davidfowl
Copy link
Member

We need some throughput tests before and after.

@BrennanConroy
Copy link
Member Author

I'll do some perf testing today

@glennc
Copy link
Member

glennc commented Feb 13, 2017

Test results? :)

@BrennanConroy
Copy link
Member Author

Metric Before After
Average (RPS) 4001.162 5007.562
Max (RPS) 4618.2 6120.87
Standard deviation 824.096 824.499

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();
Copy link
Member

@davidfowl davidfowl Feb 14, 2017

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();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is Dispose throwing?

Copy link
Member Author

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?

Copy link
Member

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;
Copy link
Member

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?

Copy link
Member Author

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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readonly

{
public class ConsoleLoggerProcessor : IDisposable
{
private const int _maxQueuedMessages = 1024;
Copy link
Member

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.

Copy link
Member

@davidfowl davidfowl left a 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(
Copy link
Member

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.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants