From c81abdde4c48c607669580ef769776623b86dcc7 Mon Sep 17 00:00:00 2001 From: emmauss Date: Thu, 31 Jan 2019 04:49:15 +0200 Subject: Add file logging and handle unhandled exceptions (#558) * add unhandled exception handler * added file logging * add option in config * consolidated console and file log --- Ryujinx.Common/Logging/LogClass.cs | 1 + Ryujinx.Common/Logging/Logger.cs | 2 + Ryujinx/Config.cs | 3 +- Ryujinx/Program.cs | 22 +++++- Ryujinx/Ryujinx.conf | 3 + Ryujinx/Ui/ConsoleLog.cs | 107 ---------------------------- Ryujinx/Ui/Log.cs | 140 +++++++++++++++++++++++++++++++++++++ 7 files changed, 169 insertions(+), 109 deletions(-) delete mode 100644 Ryujinx/Ui/ConsoleLog.cs create mode 100644 Ryujinx/Ui/Log.cs diff --git a/Ryujinx.Common/Logging/LogClass.cs b/Ryujinx.Common/Logging/LogClass.cs index 8739fbc6..f20347b6 100644 --- a/Ryujinx.Common/Logging/LogClass.cs +++ b/Ryujinx.Common/Logging/LogClass.cs @@ -5,6 +5,7 @@ namespace Ryujinx.Common.Logging Audio, Cpu, Font, + Emulation, Gpu, Hid, Kernel, diff --git a/Ryujinx.Common/Logging/Logger.cs b/Ryujinx.Common/Logging/Logger.cs index 5e58f806..35ca416b 100644 --- a/Ryujinx.Common/Logging/Logger.cs +++ b/Ryujinx.Common/Logging/Logger.cs @@ -16,6 +16,8 @@ namespace Ryujinx.Common.Logging public static event EventHandler Updated; + public static bool EnableFileLog { get; set; } + static Logger() { m_EnabledLevels = new bool[Enum.GetNames(typeof(LogLevel)).Length]; diff --git a/Ryujinx/Config.cs b/Ryujinx/Config.cs index d1139da9..a1d8cddf 100644 --- a/Ryujinx/Config.cs +++ b/Ryujinx/Config.cs @@ -62,6 +62,8 @@ namespace Ryujinx } } + Logger.EnableFileLog = Convert.ToBoolean(parser.Value("Enable_File_Log")); + SystemLanguage SetLanguage = Enum.Parse(parser.Value("System_Language")); device.System.State.SetLanguage(SetLanguage); @@ -118,7 +120,6 @@ namespace Ryujinx }); NpadController = new NpadController( - Convert.ToBoolean(parser.Value("GamePad_Enable")), Convert.ToInt32 (parser.Value("GamePad_Index")), (float)Convert.ToDouble (parser.Value("GamePad_Deadzone"), CultureInfo.InvariantCulture), diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs index 4dce13c7..335aa0ea 100644 --- a/Ryujinx/Program.cs +++ b/Ryujinx/Program.cs @@ -22,7 +22,10 @@ namespace Ryujinx Config.Read(device); - Logger.Updated += ConsoleLog.Log; + Logger.Updated += Log.LogMessage; + + AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; + AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit; if (args.Length == 1) { @@ -87,6 +90,23 @@ namespace Ryujinx audioOut.Dispose(); } + private static void CurrentDomain_ProcessExit(object sender, EventArgs e) + { + Log.Close(); + } + + private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) + { + var exception = e.ExceptionObject as Exception; + + Logger.PrintError(LogClass.Emulation, $"Unhandled exception caught: {exception}"); + + if (e.IsTerminating) + { + Log.Close(); + } + } + /// /// Picks an audio output renderer supported on this machine /// diff --git a/Ryujinx/Ryujinx.conf b/Ryujinx/Ryujinx.conf index c04d7b5a..6edcb8e6 100644 --- a/Ryujinx/Ryujinx.conf +++ b/Ryujinx/Ryujinx.conf @@ -22,6 +22,9 @@ Logging_Enable_Error = true #Filtered log classes, seperated by ", ", eg. `Logging_Filtered_Classes = Loader, ServiceFS` Logging_Filtered_Classes = +#Enable file logging +Enable_File_Log = true + #System Language list: https://gist.github.com/HorrorTroll/b6e4a88d774c3c9b3bdf54d79a7ca43b #Change System Language System_Language = AmericanEnglish diff --git a/Ryujinx/Ui/ConsoleLog.cs b/Ryujinx/Ui/ConsoleLog.cs deleted file mode 100644 index ac3d41c9..00000000 --- a/Ryujinx/Ui/ConsoleLog.cs +++ /dev/null @@ -1,107 +0,0 @@ -using Ryujinx.Common.Logging; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Reflection; -using System.Text; -using System.Threading; - -namespace Ryujinx -{ - static class ConsoleLog - { - private static Thread _messageThread; - - private static BlockingCollection _messageQueue; - - private static Dictionary _logColors; - - static ConsoleLog() - { - _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. - } - } - }); - - _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); - } - } - - if (_logColors.TryGetValue(e.Level, out ConsoleColor color)) - { - Console.ForegroundColor = color; - - Console.WriteLine(sb.ToString()); - - Console.ResetColor(); - } - else - { - Console.WriteLine(sb.ToString()); - } - } - - public static void Log(object sender, LogEventArgs e) - { - if (!_messageQueue.IsAddingCompleted) - { - _messageQueue.Add(e); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx/Ui/Log.cs b/Ryujinx/Ui/Log.cs new file mode 100644 index 00000000..5daae140 --- /dev/null +++ b/Ryujinx/Ui/Log.cs @@ -0,0 +1,140 @@ +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(); + } + } + } +} -- cgit v1.2.3