Logging: Refactor log targets into Ryujinx.Common
This commit is contained in:
parent
a694420d11
commit
d76a360518
8 changed files with 258 additions and 145 deletions
81
Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs
Normal file
81
Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
73
Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs
Normal file
73
Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
66
Ryujinx.Common/Logging/Targets/FileLogTarget.cs
Normal file
66
Ryujinx.Common/Logging/Targets/FileLogTarget.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
Ryujinx.Common/Logging/Targets/ILogTarget.cs
Normal file
9
Ryujinx.Common/Logging/Targets/ILogTarget.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Logging
|
||||||
|
{
|
||||||
|
public interface ILogTarget : IDisposable
|
||||||
|
{
|
||||||
|
void Log(object sender, LogEventArgs e);
|
||||||
|
}
|
||||||
|
}
|
15
Ryujinx.Common/Logging/Targets/JsonLogTarget.cs
Normal file
15
Ryujinx.Common/Logging/Targets/JsonLogTarget.cs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
namespace Ryujinx.Common.Logging
|
||||||
|
{
|
||||||
|
public class JsonLogTarget : ILogTarget
|
||||||
|
{
|
||||||
|
public void Log(object sender, LogEventArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -111,9 +111,9 @@ namespace Ryujinx.HLE
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
System.Dispose();
|
//System.Dispose();
|
||||||
|
|
||||||
VsyncEvent.Dispose();
|
//VsyncEvent.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,9 @@ namespace Ryujinx
|
||||||
{
|
{
|
||||||
class Program
|
class Program
|
||||||
{
|
{
|
||||||
|
static ILogTarget _fileTarget;
|
||||||
|
static ILogTarget _consoleTarget;
|
||||||
|
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
Console.Title = "Ryujinx Console";
|
Console.Title = "Ryujinx Console";
|
||||||
|
@ -22,7 +25,11 @@ namespace Ryujinx
|
||||||
|
|
||||||
Config.Read(device);
|
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.UnhandledException += CurrentDomain_UnhandledException;
|
||||||
AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
|
AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
|
||||||
|
@ -92,7 +99,8 @@ namespace Ryujinx
|
||||||
|
|
||||||
private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
|
private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
Log.Close();
|
_fileTarget.Dispose();
|
||||||
|
_consoleTarget.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||||
|
@ -103,7 +111,8 @@ namespace Ryujinx
|
||||||
|
|
||||||
if (e.IsTerminating)
|
if (e.IsTerminating)
|
||||||
{
|
{
|
||||||
Log.Close();
|
_fileTarget.Dispose();
|
||||||
|
_consoleTarget.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue