From 84d6e8d121a1b329d26cc0e462aadd1108d99a04 Mon Sep 17 00:00:00 2001
From: jcm <john.moody@coloradocollege.edu>
Date: Sat, 10 Feb 2024 19:17:19 -0600
Subject: Standardize logging locations across desktop platforms (#6238)

* Standardize logging locations across desktop platforms

* Return null instead of empty literal on exceptions

* Remove LogDirectoryPath from LoggerModule

* Catch exception when creating DirectoryInfo in FileLogTarget

* Remove redundant log path vars, handle exception better, add null check

* Address styling issues

* Remove extra newline, quote file path in log, move directory check to OpenHelper

* Add GetOrCreateLogsDir to get/create log directory during runtime

* misc format changes

* Update src/Ryujinx.Common/Configuration/AppDataManager.cs

---------

Co-authored-by: jcm <butt@butts.com>
Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
---
 .../UI/ViewModels/MainWindowViewModel.cs           |  11 +-
 src/Ryujinx.Common/Configuration/AppDataManager.cs | 129 +++++++++++++++++++--
 .../Logging/Targets/FileLogTarget.cs               |  13 ++-
 src/Ryujinx.Headless.SDL2/Program.cs               |  16 +--
 .../Configuration/LoggerModule.cs                  |  24 ++--
 src/Ryujinx/Ui/MainWindow.cs                       |  11 +-
 6 files changed, 155 insertions(+), 49 deletions(-)

(limited to 'src')

diff --git a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs
index 2caee16c..243d870a 100644
--- a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs
+++ b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs
@@ -1350,16 +1350,11 @@ namespace Ryujinx.Ava.UI.ViewModels
 
         public void OpenLogsFolder()
         {
-            string logPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
-
-            if (LoggerModule.LogDirectoryPath != null)
+            string logPath = AppDataManager.GetOrCreateLogsDir();
+            if (!string.IsNullOrEmpty(logPath))
             {
-                logPath = LoggerModule.LogDirectoryPath;
+                OpenHelper.OpenFolder(logPath);
             }
-
-            new DirectoryInfo(logPath).Create();
-
-            OpenHelper.OpenFolder(logPath);
         }
 
         public void ToggleDockMode()
diff --git a/src/Ryujinx.Common/Configuration/AppDataManager.cs b/src/Ryujinx.Common/Configuration/AppDataManager.cs
index 35aea3c2..f3df0fc9 100644
--- a/src/Ryujinx.Common/Configuration/AppDataManager.cs
+++ b/src/Ryujinx.Common/Configuration/AppDataManager.cs
@@ -30,6 +30,8 @@ namespace Ryujinx.Common.Configuration
         public static string KeysDirPath { get; private set; }
         public static string KeysDirPathUser { get; }
 
+        public static string LogsDirPath { get; private set; }
+
         public const string DefaultNandDir = "bis";
         public const string DefaultSdcardDir = "sdcard";
         private const string DefaultModsDir = "mods";
@@ -46,15 +48,7 @@ namespace Ryujinx.Common.Configuration
 
         public static void Initialize(string baseDirPath)
         {
-            string appDataPath;
-            if (OperatingSystem.IsMacOS())
-            {
-                appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support");
-            }
-            else
-            {
-                appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
-            }
+            string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
 
             if (appDataPath.Length == 0)
             {
@@ -118,9 +112,126 @@ namespace Ryujinx.Common.Configuration
             SetupBasePaths();
         }
 
+        public static string GetOrCreateLogsDir()
+        {
+            if (Directory.Exists(LogsDirPath))
+            {
+                return LogsDirPath;
+            }
+
+            Logger.Notice.Print(LogClass.Application, "Logging directory not found; attempting to create new logging directory.");
+            LogsDirPath = SetUpLogsDir();
+
+            return LogsDirPath;
+        }
+
+        private static string SetUpLogsDir()
+        {
+            string logDir = "";
+
+            if (Mode == LaunchMode.Portable)
+            {
+                logDir = Path.Combine(BaseDirPath, "Logs");
+                try
+                {
+                    Directory.CreateDirectory(logDir);
+                }
+                catch
+                {
+                    Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
+
+                    return null;
+                }
+            }
+            else
+            {
+                if (OperatingSystem.IsMacOS())
+                {
+                    // NOTE: Should evaluate to "~/Library/Logs/Ryujinx/".
+                    logDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Logs", DefaultBaseDir);
+                    try
+                    {
+                        Directory.CreateDirectory(logDir);
+                    }
+                    catch
+                    {
+                        Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
+                        logDir = "";
+                    }
+
+                    if (string.IsNullOrEmpty(logDir))
+                    {
+                        // NOTE: Should evaluate to "~/Library/Application Support/Ryujinx/Logs".
+                        logDir = Path.Combine(BaseDirPath, "Logs");
+
+                        try
+                        {
+                            Directory.CreateDirectory(logDir);
+                        }
+                        catch
+                        {
+                            Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
+
+                            return null;
+                        }
+                    }
+                }
+                else if (OperatingSystem.IsWindows())
+                {
+                    // NOTE: Should evaluate to a "Logs" directory in whatever directory Ryujinx was launched from.
+                    logDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
+                    try
+                    {
+                        Directory.CreateDirectory(logDir);
+                    }
+                    catch
+                    {
+                        Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
+                        logDir = "";
+                    }
+
+                    if (string.IsNullOrEmpty(logDir))
+                    {
+                        // NOTE: Should evaluate to "C:\Users\user\AppData\Roaming\Ryujinx\Logs".
+                        logDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir, "Logs");
+
+                        try
+                        {
+                            Directory.CreateDirectory(logDir);
+                        }
+                        catch
+                        {
+                            Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
+
+                            return null;
+                        }
+                    }
+                }
+                else if (OperatingSystem.IsLinux())
+                {
+                    // NOTE: Should evaluate to "~/.config/Ryujinx/Logs".
+                    logDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir, "Logs");
+
+                    try
+                    {
+                        Directory.CreateDirectory(logDir);
+                    }
+                    catch
+                    {
+                        Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
+
+                        return null;
+                    }
+                }
+            }
+
+            return logDir;
+        }
+
         private static void SetupBasePaths()
         {
             Directory.CreateDirectory(BaseDirPath);
+            LogsDirPath = SetUpLogsDir();
             Directory.CreateDirectory(GamesDirPath = Path.Combine(BaseDirPath, GamesDir));
             Directory.CreateDirectory(ProfilesDirPath = Path.Combine(BaseDirPath, ProfilesDir));
             Directory.CreateDirectory(KeysDirPath = Path.Combine(BaseDirPath, KeysDir));
diff --git a/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs b/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs
index a4e8f714..8d4ede96 100644
--- a/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs
+++ b/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs
@@ -23,7 +23,18 @@ namespace Ryujinx.Common.Logging.Targets
         public static FileStream PrepareLogFile(string path)
         {
             // Ensure directory is present
-            DirectoryInfo logDir = new(path);
+            DirectoryInfo logDir;
+            try
+            {
+                logDir = new DirectoryInfo(path);
+            }
+            catch (ArgumentException exception)
+            {
+                Logger.Warning?.Print(LogClass.Application, $"Logging directory path ('{path}') was invalid: {exception}");
+
+                return null;
+            }
+
             try
             {
                 logDir.Create();
diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs
index c2300275..85aff671 100644
--- a/src/Ryujinx.Headless.SDL2/Program.cs
+++ b/src/Ryujinx.Headless.SDL2/Program.cs
@@ -427,16 +427,12 @@ namespace Ryujinx.Headless.SDL2
 
             if (!option.DisableFileLog)
             {
-                FileStream logFile = FileLogTarget.PrepareLogFile(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs"));
+                string logDir = AppDataManager.LogsDirPath;
+                FileStream logFile = null;
 
-                if (logFile == null)
+                if (!string.IsNullOrEmpty(logDir))
                 {
-                    logFile = FileLogTarget.PrepareLogFile(Path.Combine(AppDataManager.BaseDirPath, "Logs"));
-
-                    if (logFile == null)
-                    {
-                        Logger.Error?.Print(LogClass.Application, "No writable log directory available. Make sure either the application directory or the Ryujinx directory is writable.");
-                    }
+                    logFile = FileLogTarget.PrepareLogFile(logDir);
                 }
 
                 if (logFile != null)
@@ -447,6 +443,10 @@ namespace Ryujinx.Headless.SDL2
                         AsyncLogTargetOverflowAction.Block
                     ));
                 }
+                else
+                {
+                    Logger.Error?.Print(LogClass.Application, "No writable log directory available. Make sure either the Logs directory, Application Data, or the Ryujinx directory is writable.");
+                }
             }
 
             // Setup graphics configuration
diff --git a/src/Ryujinx.Ui.Common/Configuration/LoggerModule.cs b/src/Ryujinx.Ui.Common/Configuration/LoggerModule.cs
index f22ee83a..2edcd07f 100644
--- a/src/Ryujinx.Ui.Common/Configuration/LoggerModule.cs
+++ b/src/Ryujinx.Ui.Common/Configuration/LoggerModule.cs
@@ -9,8 +9,6 @@ namespace Ryujinx.Ui.Common.Configuration
 {
     public static class LoggerModule
     {
-        public static string LogDirectoryPath { get; private set; }
-
         public static void Initialize()
         {
             ConfigurationState.Instance.Logger.EnableDebug.Event += ReloadEnableDebug;
@@ -84,26 +82,22 @@ namespace Ryujinx.Ui.Common.Configuration
         {
             if (e.NewValue)
             {
-                string logDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
-                FileStream logFile = FileLogTarget.PrepareLogFile(logDir);
+                string logDir = AppDataManager.LogsDirPath;
+                FileStream logFile = null;
 
-                if (logFile == null)
+                if (!string.IsNullOrEmpty(logDir))
                 {
-                    logDir = Path.Combine(AppDataManager.BaseDirPath, "Logs");
                     logFile = FileLogTarget.PrepareLogFile(logDir);
+                }
 
-                    if (logFile == null)
-                    {
-                        Logger.Error?.Print(LogClass.Application, "No writable log directory available. Make sure either the application directory or the Ryujinx directory is writable.");
-                        LogDirectoryPath = null;
-                        Logger.RemoveTarget("file");
+                if (logFile == null)
+                {
+                    Logger.Error?.Print(LogClass.Application, "No writable log directory available. Make sure either the Logs directory, Application Data, or the Ryujinx directory is writable.");
+                    Logger.RemoveTarget("file");
 
-                        return;
-                    }
+                    return;
                 }
 
-                LogDirectoryPath = logDir;
-
                 Logger.AddTarget(new AsyncLogTargetWrapper(
                     new FileLogTarget("file", logFile),
                     1000,
diff --git a/src/Ryujinx/Ui/MainWindow.cs b/src/Ryujinx/Ui/MainWindow.cs
index 1ecbb9ea..8bfe09cf 100644
--- a/src/Ryujinx/Ui/MainWindow.cs
+++ b/src/Ryujinx/Ui/MainWindow.cs
@@ -1376,16 +1376,11 @@ namespace Ryujinx.Ui
 
         private void OpenLogsFolder_Pressed(object sender, EventArgs args)
         {
-            string logPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
-
-            if (LoggerModule.LogDirectoryPath != null)
+            string logPath = AppDataManager.GetOrCreateLogsDir();
+            if (!string.IsNullOrEmpty(logPath))
             {
-                logPath = LoggerModule.LogDirectoryPath;
+                OpenHelper.OpenFolder(logPath);
             }
-
-            new DirectoryInfo(logPath).Create();
-
-            OpenHelper.OpenFolder(logPath);
         }
 
         private void Exit_Pressed(object sender, EventArgs args)
-- 
cgit v1.2.3-70-g09d2