Logging: Refactor log targets into Ryujinx.Common

This commit is contained in:
jduncanator 2019-02-02 16:14:41 +11:00
parent a694420d11
commit d76a360518
8 changed files with 258 additions and 145 deletions

View file

@ -0,0 +1,81 @@
using System;
using System.Collections.Concurrent;
using System.Threading;
namespace Ryujinx.Common.Logging
{
public enum AsyncLogTargetOverflowAction
{
/// <summary>
/// Block until there's more room in the queue
/// </summary>
Block = 0,
/// <summary>
/// Discard the overflowing item
/// </summary>
Discard = 1
}
public class AsyncLogTargetWrapper : ILogTarget
{
private ILogTarget _target;
private Thread _messageThread;
private BlockingCollection<LogEventArgs> _messageQueue;
private AsyncLogTargetOverflowAction _overflowAction;
public AsyncLogTargetWrapper(ILogTarget target)
: this(target, -1, AsyncLogTargetOverflowAction.Block)
{ }
public AsyncLogTargetWrapper(ILogTarget target, int queueLimit, AsyncLogTargetOverflowAction overflowAction)
{
_target = target;
_overflowAction = overflowAction;
_messageQueue = new BlockingCollection<LogEventArgs>(queueLimit);
_messageThread = new Thread(() => {
while (!_messageQueue.IsCompleted)
{
try
{
_target.Log(this, _messageQueue.Take());
}
catch (InvalidOperationException)
{
// IOE means that Take() was called on a completed collection.
// Some other thread can call CompleteAdding after we pass the
// IsCompleted check but before we call Take.
// We can simply catch the exception since the loop will break
// on the next iteration.
}
}
});
_messageThread.IsBackground = true;
_messageThread.Start();
}
public void Log(object sender, LogEventArgs e)
{
if (!_messageQueue.IsAddingCompleted)
{
if(!_messageQueue.TryAdd(e) && _overflowAction == AsyncLogTargetOverflowAction.Block)
{
_messageQueue.Add(e);
}
}
}
public void Dispose()
{
_messageQueue.CompleteAdding();
_messageThread.Join();
}
}
}

View file

@ -0,0 +1,73 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
namespace Ryujinx.Common.Logging
{
public class ConsoleLogTarget : ILogTarget
{
private static readonly ConcurrentDictionary<LogLevel, ConsoleColor> _logColors;
static ConsoleLogTarget()
{
_logColors = new ConcurrentDictionary<LogLevel, ConsoleColor> {
[ LogLevel.Stub ] = ConsoleColor.DarkGray,
[ LogLevel.Info ] = ConsoleColor.White,
[ LogLevel.Warning ] = ConsoleColor.Yellow,
[ LogLevel.Error ] = ConsoleColor.Red
};
}
public void Log(object sender, LogEventArgs e)
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat(@"{0:hh\:mm\:ss\.fff}", e.Time);
sb.Append(" | ");
sb.AppendFormat("{0:d4}", e.ThreadId);
sb.Append(' ');
sb.Append(e.Message);
if (e.Data != null)
{
PropertyInfo[] props = e.Data.GetType().GetProperties();
sb.Append(' ');
foreach (var prop in props)
{
sb.Append(prop.Name);
sb.Append(": ");
sb.Append(prop.GetValue(e.Data));
sb.Append(" - ");
}
// We remove the final '-' from the string
if (props.Length > 0)
{
sb.Remove(sb.Length - 3, 3);
}
}
if (_logColors.TryGetValue(e.Level, out ConsoleColor color))
{
Console.ForegroundColor = color;
Console.WriteLine(sb.ToString());
Console.ResetColor();
}
else
{
Console.WriteLine(sb.ToString());
}
}
public void Dispose()
{
Console.ResetColor();
}
}
}

View file

@ -0,0 +1,66 @@
using System.IO;
using System.Reflection;
using System.Text;
namespace Ryujinx.Common.Logging
{
public class FileLogTarget : ILogTarget
{
private StreamWriter _logWriter;
public string Path { get; private set; }
public FileLogTarget(string path)
: this(path, FileShare.Read, FileMode.Append)
{ }
public FileLogTarget(string path, FileShare fileShare, FileMode fileMode)
{
this.Path = path;
this._logWriter = new StreamWriter(File.Open(path, fileMode, FileAccess.Write, fileShare));
}
public void Log(object sender, LogEventArgs e)
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat(@"{0:hh\:mm\:ss\.fff}", e.Time);
sb.Append(" | ");
sb.AppendFormat("{0:d4}", e.ThreadId);
sb.Append(' ');
sb.Append(e.Message);
if (e.Data != null)
{
PropertyInfo[] props = e.Data.GetType().GetProperties();
sb.Append(' ');
foreach (var prop in props)
{
sb.Append(prop.Name);
sb.Append(": ");
sb.Append(prop.GetValue(e.Data));
sb.Append(" - ");
}
// We remove the final '-' from the string
if (props.Length > 0)
{
sb.Remove(sb.Length - 3, 3);
}
}
_logWriter.WriteLine(sb.ToString());
_logWriter.Flush();
}
public void Dispose()
{
_logWriter.WriteLine("---- End of Log ----");
_logWriter.Flush();
_logWriter.Dispose();
}
}
}

View file

@ -0,0 +1,9 @@
using System;
namespace Ryujinx.Common.Logging
{
public interface ILogTarget : IDisposable
{
void Log(object sender, LogEventArgs e);
}
}

View file

@ -0,0 +1,15 @@
namespace Ryujinx.Common.Logging
{
public class JsonLogTarget : ILogTarget
{
public void Log(object sender, LogEventArgs e)
{
}
public void Dispose()
{
}
}
}

View file

@ -111,9 +111,9 @@ namespace Ryujinx.HLE
{
if (disposing)
{
System.Dispose();
//System.Dispose();
VsyncEvent.Dispose();
//VsyncEvent.Dispose();
}
}
}

View file

@ -10,6 +10,9 @@ namespace Ryujinx
{
class Program
{
static ILogTarget _fileTarget;
static ILogTarget _consoleTarget;
static void Main(string[] args)
{
Console.Title = "Ryujinx Console";
@ -22,7 +25,11 @@ namespace Ryujinx
Config.Read(device);
Logger.Updated += Log.LogMessage;
_fileTarget = new AsyncLogTargetWrapper(new FileLogTarget("Ryujinx.log"), 1000, AsyncLogTargetOverflowAction.Block);
_consoleTarget = new AsyncLogTargetWrapper(new ConsoleLogTarget(), 1000, AsyncLogTargetOverflowAction.Block);
Logger.Updated += _fileTarget.Log;
Logger.Updated += _consoleTarget.Log;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
@ -92,7 +99,8 @@ namespace Ryujinx
private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
Log.Close();
_fileTarget.Dispose();
_consoleTarget.Dispose();
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
@ -103,7 +111,8 @@ namespace Ryujinx
if (e.IsTerminating)
{
Log.Close();
_fileTarget.Dispose();
_consoleTarget.Dispose();
}
}

View file

@ -1,140 +0,0 @@
using Ryujinx.Common.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading;
namespace Ryujinx
{
static class Log
{
private static readonly string _path;
private static StreamWriter _logWriter;
private static Thread _messageThread;
private static BlockingCollection<LogEventArgs> _messageQueue;
private static Dictionary<LogLevel, ConsoleColor> _logColors;
static Log()
{
_logColors = new Dictionary<LogLevel, ConsoleColor>()
{
{ LogLevel.Stub, ConsoleColor.DarkGray },
{ LogLevel.Info, ConsoleColor.White },
{ LogLevel.Warning, ConsoleColor.Yellow },
{ LogLevel.Error, ConsoleColor.Red }
};
_messageQueue = new BlockingCollection<LogEventArgs>(10);
_messageThread = new Thread(() =>
{
while (!_messageQueue.IsCompleted)
{
try
{
PrintLog(_messageQueue.Take());
}
catch (InvalidOperationException)
{
// IOE means that Take() was called on a completed collection.
// Some other thread can call CompleteAdding after we pass the
// IsCompleted check but before we call Take.
// We can simply catch the exception since the loop will break
// on the next iteration.
}
}
});
_path = Path.Combine(Environment.CurrentDirectory, "Ryujinx.log");
if (Logger.EnableFileLog)
{
_logWriter = new StreamWriter(File.Open(_path,FileMode.Create, FileAccess.Write));
}
_messageThread.IsBackground = true;
_messageThread.Start();
}
private static void PrintLog(LogEventArgs e)
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat(@"{0:hh\:mm\:ss\.fff}", e.Time);
sb.Append(" | ");
sb.AppendFormat("{0:d4}", e.ThreadId);
sb.Append(' ');
sb.Append(e.Message);
if (e.Data != null)
{
PropertyInfo[] props = e.Data.GetType().GetProperties();
sb.Append(' ');
foreach (var prop in props)
{
sb.Append(prop.Name);
sb.Append(": ");
sb.Append(prop.GetValue(e.Data));
sb.Append(" - ");
}
// We remove the final '-' from the string
if (props.Length > 0)
{
sb.Remove(sb.Length - 3, 3);
}
}
string message = sb.ToString();
if (_logColors.TryGetValue(e.Level, out ConsoleColor color))
{
Console.ForegroundColor = color;
Console.WriteLine(message);
Console.ResetColor();
}
else
{
Console.WriteLine(message);
}
if (Logger.EnableFileLog)
{
_logWriter.WriteLine(message);
}
}
public static void LogMessage(object sender, LogEventArgs e)
{
if (!_messageQueue.IsAddingCompleted)
{
_messageQueue.Add(e);
}
}
public static void Close()
{
_messageQueue.CompleteAdding();
_messageThread.Join();
if (Logger.EnableFileLog)
{
_logWriter.Flush();
_logWriter.Close();
_logWriter.Dispose();
}
}
}
}