From f4539c49d8def4c6086a61fe4bd9ed9665fac4b1 Mon Sep 17 00:00:00 2001
From: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
Date: Thu, 1 Jun 2023 15:47:53 +0200
Subject: [PATCH] [Logger] Add print with stacktrace method (#5129)

* Add print with stacktrace method

* Adjust logging namespaces

* Add static keyword to DynamicObjectFormatter
---
 .../Logging/Formatters/DefaultLogFormatter.cs     | 15 ++++++++++++---
 .../Logging/Formatters/DynamicObjectFormatter.cs  |  6 +++---
 .../Logging/Formatters/ILogFormatter.cs           |  4 ++--
 src/Ryujinx.Common/Logging/LogEventArgsJson.cs    |  3 ++-
 src/Ryujinx.Common/Logging/Logger.cs              | 15 +++++++++++++--
 .../Logging/Targets/AsyncLogTargetWrapper.cs      |  2 +-
 .../Logging/Targets/ConsoleLogTarget.cs           |  7 ++++---
 .../Logging/Targets/FileLogTarget.cs              |  5 +++--
 src/Ryujinx.Common/Logging/Targets/ILogTarget.cs  |  2 +-
 .../Logging/Targets/JsonLogTarget.cs              |  2 +-
 src/Ryujinx.Headless.SDL2/Program.cs              |  1 +
 .../Configuration/LoggerModule.cs                 |  1 +
 12 files changed, 44 insertions(+), 19 deletions(-)

diff --git a/src/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs b/src/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs
index 28a7d54610..3769b03a74 100644
--- a/src/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs
+++ b/src/Ryujinx.Common/Logging/Formatters/DefaultLogFormatter.cs
@@ -1,6 +1,7 @@
-using System.Text;
+using System.Diagnostics;
+using System.Text;
 
-namespace Ryujinx.Common.Logging
+namespace Ryujinx.Common.Logging.Formatters
 {
     internal class DefaultLogFormatter : ILogFormatter
     {
@@ -27,6 +28,14 @@ namespace Ryujinx.Common.Logging
 
                 if (args.Data is not null)
                 {
+                    if (args.Data is StackTrace trace)
+                    {
+                        sb.Append('\n');
+                        sb.Append(trace);
+
+                        return sb.ToString();
+                    }
+
                     sb.Append(' ');
                     DynamicObjectFormatter.Format(sb, args.Data);
                 }
@@ -39,4 +48,4 @@ namespace Ryujinx.Common.Logging
             }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/src/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs b/src/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs
index 5f15cc2a62..6e3b0043dd 100644
--- a/src/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs
+++ b/src/Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs
@@ -3,9 +3,9 @@ using System;
 using System.Reflection;
 using System.Text;
 
-namespace Ryujinx.Common.Logging
+namespace Ryujinx.Common.Logging.Formatters
 {
-    internal class DynamicObjectFormatter
+    internal static class DynamicObjectFormatter
     {
         private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>();
 
@@ -17,7 +17,7 @@ namespace Ryujinx.Common.Logging
             }
 
             StringBuilder sb = StringBuilderPool.Allocate();
-            
+
             try
             {
                 Format(sb, dynamicObject);
diff --git a/src/Ryujinx.Common/Logging/Formatters/ILogFormatter.cs b/src/Ryujinx.Common/Logging/Formatters/ILogFormatter.cs
index 9a55bc6b0d..25a06d8313 100644
--- a/src/Ryujinx.Common/Logging/Formatters/ILogFormatter.cs
+++ b/src/Ryujinx.Common/Logging/Formatters/ILogFormatter.cs
@@ -1,7 +1,7 @@
-namespace Ryujinx.Common.Logging
+namespace Ryujinx.Common.Logging.Formatters
 {
     interface ILogFormatter
     {
         string Format(LogEventArgs args);
     }
-}
+}
\ No newline at end of file
diff --git a/src/Ryujinx.Common/Logging/LogEventArgsJson.cs b/src/Ryujinx.Common/Logging/LogEventArgsJson.cs
index 425b976627..5203b17a60 100644
--- a/src/Ryujinx.Common/Logging/LogEventArgsJson.cs
+++ b/src/Ryujinx.Common/Logging/LogEventArgsJson.cs
@@ -1,4 +1,5 @@
-using System;
+using Ryujinx.Common.Logging.Formatters;
+using System;
 using System.Text.Json.Serialization;
 
 namespace Ryujinx.Common.Logging
diff --git a/src/Ryujinx.Common/Logging/Logger.cs b/src/Ryujinx.Common/Logging/Logger.cs
index 4d48dd48d8..25f9c5eee5 100644
--- a/src/Ryujinx.Common/Logging/Logger.cs
+++ b/src/Ryujinx.Common/Logging/Logger.cs
@@ -1,3 +1,4 @@
+using Ryujinx.Common.Logging.Targets;
 using Ryujinx.Common.SystemInterop;
 using System;
 using System.Collections.Generic;
@@ -55,6 +56,16 @@ namespace Ryujinx.Common.Logging
                 }
             }
 
+            [StackTraceHidden]
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            public void PrintStack(LogClass logClass, string message, [CallerMemberName] string caller = "")
+            {
+                if (m_EnabledClasses[(int)logClass])
+                {
+                    Updated?.Invoke(null, new LogEventArgs(Level, m_Time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, message), new StackTrace(true)));
+                }
+            }
+
             [MethodImpl(MethodImplOptions.AggressiveInlining)]
             public void PrintStub(LogClass logClass, string message = "", [CallerMemberName] string caller = "")
             {
@@ -122,7 +133,7 @@ namespace Ryujinx.Common.Logging
                 AsyncLogTargetOverflowAction.Discard));
 
             Notice = new Log(LogLevel.Notice);
-            
+
             // Enable important log levels before configuration is loaded
             Error = new Log(LogLevel.Error);
             Warning = new Log(LogLevel.Warning);
@@ -221,4 +232,4 @@ namespace Ryujinx.Common.Logging
             m_EnabledClasses[(int)logClass] = enabled;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs b/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs
index 43c62d319c..ddc547acd5 100644
--- a/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs
+++ b/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs
@@ -2,7 +2,7 @@
 using System.Collections.Concurrent;
 using System.Threading;
 
-namespace Ryujinx.Common.Logging
+namespace Ryujinx.Common.Logging.Targets
 {
     public enum AsyncLogTargetOverflowAction
     {
diff --git a/src/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs b/src/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs
index 7b77c4f28e..b5986461e9 100644
--- a/src/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs
+++ b/src/Ryujinx.Common/Logging/Targets/ConsoleLogTarget.cs
@@ -1,6 +1,7 @@
-using System;
+using Ryujinx.Common.Logging.Formatters;
+using System;
 
-namespace Ryujinx.Common.Logging
+namespace Ryujinx.Common.Logging.Targets
 {
     public class ConsoleLogTarget : ILogTarget
     {
@@ -38,4 +39,4 @@ namespace Ryujinx.Common.Logging
             Console.ResetColor();
         }
     }
-}
+}
\ No newline at end of file
diff --git a/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs b/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs
index 24dd6d1792..2cc4a8237e 100644
--- a/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs
+++ b/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs
@@ -1,8 +1,9 @@
-using System;
+using Ryujinx.Common.Logging.Formatters;
+using System;
 using System.IO;
 using System.Linq;
 
-namespace Ryujinx.Common.Logging
+namespace Ryujinx.Common.Logging.Targets
 {
     public class FileLogTarget : ILogTarget
     {
diff --git a/src/Ryujinx.Common/Logging/Targets/ILogTarget.cs b/src/Ryujinx.Common/Logging/Targets/ILogTarget.cs
index d4d26a936d..e3b1ad2080 100644
--- a/src/Ryujinx.Common/Logging/Targets/ILogTarget.cs
+++ b/src/Ryujinx.Common/Logging/Targets/ILogTarget.cs
@@ -1,6 +1,6 @@
 using System;
 
-namespace Ryujinx.Common.Logging
+namespace Ryujinx.Common.Logging.Targets
 {
     public interface ILogTarget : IDisposable
     {
diff --git a/src/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs b/src/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs
index 06976433e5..ae264e5931 100644
--- a/src/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs
+++ b/src/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs
@@ -1,7 +1,7 @@
 using Ryujinx.Common.Utilities;
 using System.IO;
 
-namespace Ryujinx.Common.Logging
+namespace Ryujinx.Common.Logging.Targets
 {
     public class JsonLogTarget : ILogTarget
     {
diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs
index fc1b23ebb7..6e6b4a7f39 100644
--- a/src/Ryujinx.Headless.SDL2/Program.cs
+++ b/src/Ryujinx.Headless.SDL2/Program.cs
@@ -9,6 +9,7 @@ using Ryujinx.Common.Configuration.Hid.Controller;
 using Ryujinx.Common.Configuration.Hid.Controller.Motion;
 using Ryujinx.Common.Configuration.Hid.Keyboard;
 using Ryujinx.Common.Logging;
+using Ryujinx.Common.Logging.Targets;
 using Ryujinx.Common.SystemInterop;
 using Ryujinx.Common.Utilities;
 using Ryujinx.Cpu;
diff --git a/src/Ryujinx.Ui.Common/Configuration/LoggerModule.cs b/src/Ryujinx.Ui.Common/Configuration/LoggerModule.cs
index f4712213e2..85b50e763c 100644
--- a/src/Ryujinx.Ui.Common/Configuration/LoggerModule.cs
+++ b/src/Ryujinx.Ui.Common/Configuration/LoggerModule.cs
@@ -1,5 +1,6 @@
 using Ryujinx.Common;
 using Ryujinx.Common.Logging;
+using Ryujinx.Common.Logging.Targets;
 using System;
 
 namespace Ryujinx.Ui.Common.Configuration