From c81abdde4c48c607669580ef769776623b86dcc7 Mon Sep 17 00:00:00 2001
From: emmauss <emmausssss@gmail.com>
Date: Thu, 31 Jan 2019 04:49:15 +0200
Subject: [PATCH] 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 => Log.cs} | 47 +++++++++++++++++++++++-----
 6 files changed, 69 insertions(+), 9 deletions(-)
 rename Ryujinx/Ui/{ConsoleLog.cs => Log.cs} (73%)

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<LogEventArgs> 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<SystemLanguage>(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();
+            }
+        }
+
         /// <summary>
         /// Picks an <see cref="IAalOutput"/> audio output renderer supported on this machine
         /// </summary>
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/Log.cs
similarity index 73%
rename from Ryujinx/Ui/ConsoleLog.cs
rename to Ryujinx/Ui/Log.cs
index ac3d41c9..5daae140 100644
--- a/Ryujinx/Ui/ConsoleLog.cs
+++ b/Ryujinx/Ui/Log.cs
@@ -1,22 +1,27 @@
-using Ryujinx.Common.Logging;
+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 ConsoleLog
+    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 ConsoleLog()
+        static Log()
         {
             _logColors = new Dictionary<LogLevel, ConsoleColor>()
             {
@@ -47,6 +52,13 @@ namespace Ryujinx
                 }
             });
 
+            _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();
         }
@@ -82,26 +94,47 @@ namespace Ryujinx
                 }
             }
 
+            string message = sb.ToString();
+
             if (_logColors.TryGetValue(e.Level, out ConsoleColor color))
             {
                 Console.ForegroundColor = color;
 
-                Console.WriteLine(sb.ToString());
+                Console.WriteLine(message);
 
                 Console.ResetColor();
             }
             else
             {
-                Console.WriteLine(sb.ToString());
+                Console.WriteLine(message);
+            }
+
+            if (Logger.EnableFileLog)
+            {
+                _logWriter.WriteLine(message);
             }
         }
 
-        public static void Log(object sender, LogEventArgs e)
+        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();
+            }
+        }
     }
-}
\ No newline at end of file
+}