diff --git a/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs b/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs new file mode 100644 index 0000000000..7528275590 --- /dev/null +++ b/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; + +namespace Ryujinx.Common.Logging +{ + public enum AsyncLogTargetOverflowAction + { + /// + /// Block until there's more room in the queue + /// + Block = 0, + + /// + /// Discard the overflowing item + /// + Discard = 1 + } + + public class AsyncLogTargetWrapper : ILogTarget + { + private ILogTarget _target; + + private Thread _messageThread; + + private BlockingCollection _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(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(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs b/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs new file mode 100644 index 0000000000..18c554a121 --- /dev/null +++ b/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs @@ -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 _logColors; + + static ConsoleLogTarget() + { + _logColors = new ConcurrentDictionary { + [ 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(); + } + } +} diff --git a/Ryujinx.Common/Logging/Targets/FileLogTarget.cs b/Ryujinx.Common/Logging/Targets/FileLogTarget.cs new file mode 100644 index 0000000000..153106c33b --- /dev/null +++ b/Ryujinx.Common/Logging/Targets/FileLogTarget.cs @@ -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(); + } + } +} diff --git a/Ryujinx.Common/Logging/Targets/ILogTarget.cs b/Ryujinx.Common/Logging/Targets/ILogTarget.cs new file mode 100644 index 0000000000..6143b2bf93 --- /dev/null +++ b/Ryujinx.Common/Logging/Targets/ILogTarget.cs @@ -0,0 +1,9 @@ +using System; + +namespace Ryujinx.Common.Logging +{ + public interface ILogTarget : IDisposable + { + void Log(object sender, LogEventArgs e); + } +} diff --git a/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs b/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs new file mode 100644 index 0000000000..0cca5a79b6 --- /dev/null +++ b/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.Common.Logging +{ + public class JsonLogTarget : ILogTarget + { + public void Log(object sender, LogEventArgs e) + { + + } + + public void Dispose() + { + + } + } +} diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index d661b273ad..5172c6a986 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -111,9 +111,9 @@ namespace Ryujinx.HLE { if (disposing) { - System.Dispose(); + //System.Dispose(); - VsyncEvent.Dispose(); + //VsyncEvent.Dispose(); } } } diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs index 335aa0ea8e..8492e58a30 100644 --- a/Ryujinx/Program.cs +++ b/Ryujinx/Program.cs @@ -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(); } } diff --git a/Ryujinx/Ui/Log.cs b/Ryujinx/Ui/Log.cs deleted file mode 100644 index 5daae14026..0000000000 --- a/Ryujinx/Ui/Log.cs +++ /dev/null @@ -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 _messageQueue; - - private static Dictionary _logColors; - - static Log() - { - _logColors = new Dictionary() - { - { LogLevel.Stub, ConsoleColor.DarkGray }, - { LogLevel.Info, ConsoleColor.White }, - { LogLevel.Warning, ConsoleColor.Yellow }, - { LogLevel.Error, ConsoleColor.Red } - }; - - _messageQueue = new BlockingCollection(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(); - } - } - } -}