Skip to content

Commit

Permalink
POC
Browse files Browse the repository at this point in the history
  • Loading branch information
geoperez committed Dec 19, 2019
1 parent cbd10f6 commit 3e885ef
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 180 deletions.
25 changes: 22 additions & 3 deletions src/Swan.Lite/Logging/ConsoleLogger.cs
Expand Up @@ -127,14 +127,33 @@ protected ConsoleLogger()
/// <inheritdoc />
public void Log(LogMessageReceivedEventArgs logEvent)
{
if (Terminal.AvailableWriters == TerminalWriters.None || !Terminal.IsConsolePresent)
return;

// Select the writer based on the message type
var writer = logEvent.MessageType == LogLevel.Error
? TerminalWriters.StandardError
: TerminalWriters.StandardOutput;
? TerminalWriters.StandardError
: TerminalWriters.StandardOutput;

var (outputMessage, color) = GetOutputAndColor(logEvent);

Terminal.Write(outputMessage, color, writer);
Write(outputMessage, color, writer);
}

internal static void Write(string text, ConsoleColor color, TerminalWriters writerFlags)
{
Console.ForegroundColor = color;

// Output to the standard output
if (writerFlags.HasFlag(TerminalWriters.StandardOutput))
Console.Out.Write(text);

// output to the standard error
if (writerFlags.HasFlag(TerminalWriters.StandardError))
Console.Error.Write(text);

Console.ResetColor();
Console.ForegroundColor = Terminal.Settings.DefaultColor;
}

/// <inheritdoc />
Expand Down
33 changes: 33 additions & 0 deletions src/Swan.Lite/Logging/LogMessageReceivedEventArgs.cs
Expand Up @@ -42,8 +42,25 @@ public class LogMessageReceivedEventArgs : EventArgs
CallerFilePath = callerFilePath;
CallerLineNumber = callerLineNumber;
ExtendedData = extendedData;
IsTerminalSource = false;
}

internal LogMessageReceivedEventArgs(string message, ConsoleColor color, TerminalWriters writerFlags)
{
Message = message;
Color = color;
WriterFlags = writerFlags;
IsTerminalSource = true;
}

/// <summary>
/// Gets or sets a value indicating whether this instance is terminal source.
/// </summary>
/// <value>
/// <c>true</c> if this instance is terminal source; otherwise, <c>false</c>.
/// </value>
internal bool IsTerminalSource {get; set; }

/// <summary>
/// Gets logging message sequence.
/// </summary>
Expand Down Expand Up @@ -86,6 +103,22 @@ public class LogMessageReceivedEventArgs : EventArgs
/// </value>
public string Message { get; }

/// <summary>
/// Gets the color.
/// </summary>
/// <value>
/// The color.
/// </value>
internal ConsoleColor Color { get; }

/// <summary>
/// Gets the writer flags.
/// </summary>
/// <value>
/// The writer flags.
/// </value>
internal TerminalWriters WriterFlags { get; }

/// <summary>
/// Gets the name of the caller member.
/// </summary>
Expand Down
70 changes: 60 additions & 10 deletions src/Swan.Lite/Logging/Logger.cs
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Swan.Threading;

Expand All @@ -14,15 +15,16 @@ namespace Swan.Logging
/// </summary>
public static class Logger
{
private const int OutputFlushInterval = 15;

private static readonly object SyncLock = new object();
private static readonly ExclusiveTimer DequeueOutputTimer;
private static readonly List<ILogger> Loggers = new List<ILogger>();
private static readonly BlockingCollection<LogMessageReceivedEventArgs> OutputQueue = new BlockingCollection<LogMessageReceivedEventArgs>();
private static readonly ManualResetEventSlim OutputDone = new ManualResetEventSlim(false);

private static ulong _loggingSequence;

private const int OutputFlushInterval = 15;

static Logger()
{
if (Terminal.IsConsolePresent)
Expand All @@ -37,6 +39,37 @@ static Logger()
}

#region Standard Public API

/// <summary>
/// Waits for all of the queued output messages to be written out to the console.
/// Call this method if it is important to display console text before
/// quitting the application such as showing usage or help.
/// Set the timeout to null or TimeSpan.Zero to wait indefinitely.
/// </summary>
/// <param name="timeout">The timeout. Set the amount of time to black before this method exits.</param>
public static void Flush(TimeSpan? timeout = null)
{
if (timeout == null) timeout = TimeSpan.Zero;
var startTime = DateTime.UtcNow;

while (OutputQueue.Count > 0)
{
// Manually trigger a timer cycle to run immediately
DequeueOutputTimer.Change(0, OutputFlushInterval);

// Wait for the output to finish
if (OutputDone.Wait(OutputFlushInterval))
break;

// infinite timeout
if (timeout.Value == TimeSpan.Zero)
continue;

// break if we have reached a timeout condition
if (DateTime.UtcNow.Subtract(startTime) >= timeout.Value)
break;
}
}

/// <summary>
/// Registers the logger.
Expand Down Expand Up @@ -619,6 +652,11 @@ public static void NoLogging()

#endregion

internal static void Write(string text, ConsoleColor defaultColor, TerminalWriters writerFlags)
{
OutputQueue.TryAdd(new LogMessageReceivedEventArgs(text, defaultColor, writerFlags));
}

private static void RemoveLogger(Func<ILogger, bool> criteria)
{
lock (SyncLock)
Expand All @@ -636,8 +674,22 @@ private static void RemoveLogger(Func<ILogger, bool> criteria)

private static void DequeueOutputCycle()
{
if (OutputQueue.Count == 0)
{
OutputDone.Set();
return;
}

OutputDone.Reset();

foreach (var context in OutputQueue.GetConsumingEnumerable())
{
if (context.IsTerminalSource)
{
ConsoleLogger.Write(context.Message, context.Color, context.WriterFlags);
continue;
}

Parallel.ForEach(Loggers, logger =>
{
if (logger.LogLevel <= context.MessageType)
Expand All @@ -655,25 +707,23 @@ private static void DequeueOutputCycle()
string callerFilePath,
int callerLineNumber)
{
var sequence = _loggingSequence;
var date = DateTime.UtcNow;
_loggingSequence++;
OutputDone.Reset();

var loggerMessage = string.IsNullOrWhiteSpace(message) ?
string.Empty : message.RemoveControlChars('\n');

var eventArgs = new LogMessageReceivedEventArgs(
sequence,
OutputQueue.TryAdd(new LogMessageReceivedEventArgs(
_loggingSequence,
logLevel,
date,
DateTime.UtcNow,
sourceName,
loggerMessage,
extendedData,
callerMemberName,
callerFilePath,
callerLineNumber);
callerLineNumber));

OutputQueue.TryAdd(eventArgs);
_loggingSequence++;
}
}
}
8 changes: 1 addition & 7 deletions src/Swan.Lite/Terminal.Interaction.cs
Expand Up @@ -29,7 +29,6 @@ public static ConsoleKeyInfo ReadKey(bool intercept, bool disableLocking = false
lock (SyncLock)
{
Flush();
InputDone.Reset();

try
{
Expand All @@ -39,7 +38,6 @@ public static ConsoleKeyInfo ReadKey(bool intercept, bool disableLocking = false
finally
{
Console.CursorVisible = false;
InputDone.Set();
}
}
}
Expand All @@ -57,9 +55,7 @@ public static ConsoleKeyInfo ReadKey(string prompt, bool preventEcho = true)
lock (SyncLock)
{
if (prompt != null)
{
Write($"{GetNowFormatted()}{Settings.UserInputPrefix} << {prompt} ", ConsoleColor.White);
}

var input = ReadKey(true);
var echo = preventEcho ? string.Empty : input.Key.ToString();
Expand Down Expand Up @@ -87,12 +83,11 @@ public static void Clear()
/// <returns>The read line.</returns>
public static string? ReadLine()
{
if (IsConsolePresent == false) return default;
if (!IsConsolePresent) return default;

lock (SyncLock)
{
Flush();
InputDone.Reset();

try
{
Expand All @@ -102,7 +97,6 @@ public static void Clear()
finally
{
Console.CursorVisible = false;
InputDone.Set();
}
}
}
Expand Down
21 changes: 3 additions & 18 deletions src/Swan.Lite/Terminal.Output.cs
@@ -1,4 +1,5 @@
using System;
using Swan.Logging;

namespace Swan
{
Expand Down Expand Up @@ -27,15 +28,7 @@ public static void Write(char charCode, ConsoleColor? color = null, int count =
text += Environment.NewLine;
}

var buffer = OutputEncoding.GetBytes(text);
var context = new OutputContext
{
OutputColor = color ?? Settings.DefaultColor,
OutputText = OutputEncoding.GetChars(buffer),
OutputWriters = writerFlags,
};

EnqueueOutput(context);
Logger.Write(text, color ?? Settings.DefaultColor, writerFlags);
}
}

Expand All @@ -51,15 +44,7 @@ public static void Write(string? text, ConsoleColor? color = null, TerminalWrite

lock (SyncLock)
{
var buffer = OutputEncoding.GetBytes(text);
var context = new OutputContext
{
OutputColor = color ?? Settings.DefaultColor,
OutputText = OutputEncoding.GetChars(buffer),
OutputWriters = writerFlags,
};

EnqueueOutput(context);
Logger.Write(text, color ?? Settings.DefaultColor, writerFlags);
}
}

Expand Down

0 comments on commit 3e885ef

Please sign in to comment.