Logger.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. using Ryujinx.Common.Logging.Targets;
  2. using Ryujinx.Common.SystemInterop;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using System.IO;
  7. using System.Runtime.CompilerServices;
  8. using System.Threading;
  9. namespace Ryujinx.Common.Logging
  10. {
  11. public static class Logger
  12. {
  13. private static readonly Stopwatch _time;
  14. private static readonly bool[] _enabledClasses;
  15. private static readonly List<ILogTarget> _logTargets;
  16. private static readonly StdErrAdapter _stdErrAdapter;
  17. public static event EventHandler<LogEventArgs> Updated;
  18. public readonly struct Log
  19. {
  20. private static readonly string _homeDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
  21. private static readonly string _homeDirRedacted = Path.Combine(Directory.GetParent(_homeDir)!.FullName, "[redacted]");
  22. internal readonly LogLevel Level;
  23. internal Log(LogLevel level)
  24. {
  25. Level = level;
  26. }
  27. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  28. public void PrintMsg(LogClass logClass, string message)
  29. {
  30. if (_enabledClasses[(int)logClass])
  31. {
  32. Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, "", message)));
  33. }
  34. }
  35. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  36. public void Print(LogClass logClass, string message, [CallerMemberName] string caller = "")
  37. {
  38. if (_enabledClasses[(int)logClass])
  39. {
  40. Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, message)));
  41. }
  42. }
  43. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  44. public void Print(LogClass logClass, string message, object data, [CallerMemberName] string caller = "")
  45. {
  46. if (_enabledClasses[(int)logClass])
  47. {
  48. Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, message), data));
  49. }
  50. }
  51. [StackTraceHidden]
  52. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  53. public void PrintStack(LogClass logClass, string message, [CallerMemberName] string caller = "")
  54. {
  55. if (_enabledClasses[(int)logClass])
  56. {
  57. Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, message), new StackTrace(true)));
  58. }
  59. }
  60. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  61. public void PrintStub(LogClass logClass, string message = "", [CallerMemberName] string caller = "")
  62. {
  63. if (_enabledClasses[(int)logClass])
  64. {
  65. Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, "Stubbed. " + message)));
  66. }
  67. }
  68. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  69. public void PrintStub(LogClass logClass, object data, [CallerMemberName] string caller = "")
  70. {
  71. if (_enabledClasses[(int)logClass])
  72. {
  73. Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, "Stubbed."), data));
  74. }
  75. }
  76. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  77. public void PrintStub(LogClass logClass, string message, object data, [CallerMemberName] string caller = "")
  78. {
  79. if (_enabledClasses[(int)logClass])
  80. {
  81. Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, "Stubbed. " + message), data));
  82. }
  83. }
  84. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  85. public void PrintRawMsg(string message)
  86. {
  87. Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, message));
  88. }
  89. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  90. private static string FormatMessage(LogClass logClass, string caller, string message)
  91. {
  92. message = message.Replace(_homeDir, _homeDirRedacted);
  93. return $"{logClass} {caller}: {message}";
  94. }
  95. }
  96. public static Log? Debug { get; private set; }
  97. public static Log? Info { get; private set; }
  98. public static Log? Warning { get; private set; }
  99. public static Log? Error { get; private set; }
  100. public static Log? Guest { get; private set; }
  101. public static Log? AccessLog { get; private set; }
  102. public static Log? Stub { get; private set; }
  103. public static Log? Trace { get; private set; }
  104. public static Log Notice { get; } // Always enabled
  105. static Logger()
  106. {
  107. _enabledClasses = new bool[Enum.GetNames<LogClass>().Length];
  108. for (int index = 0; index < _enabledClasses.Length; index++)
  109. {
  110. _enabledClasses[index] = true;
  111. }
  112. _logTargets = new List<ILogTarget>();
  113. _time = Stopwatch.StartNew();
  114. // Logger should log to console by default
  115. AddTarget(new AsyncLogTargetWrapper(
  116. new ConsoleLogTarget("console"),
  117. 1000,
  118. AsyncLogTargetOverflowAction.Discard));
  119. Notice = new Log(LogLevel.Notice);
  120. // Enable important log levels before configuration is loaded
  121. Error = new Log(LogLevel.Error);
  122. Warning = new Log(LogLevel.Warning);
  123. Info = new Log(LogLevel.Info);
  124. Trace = new Log(LogLevel.Trace);
  125. _stdErrAdapter = new StdErrAdapter();
  126. }
  127. public static void RestartTime()
  128. {
  129. _time.Restart();
  130. }
  131. private static ILogTarget GetTarget(string targetName)
  132. {
  133. foreach (var target in _logTargets)
  134. {
  135. if (target.Name.Equals(targetName))
  136. {
  137. return target;
  138. }
  139. }
  140. return null;
  141. }
  142. public static void AddTarget(ILogTarget target)
  143. {
  144. _logTargets.Add(target);
  145. Updated += target.Log;
  146. }
  147. public static void RemoveTarget(string target)
  148. {
  149. ILogTarget logTarget = GetTarget(target);
  150. if (logTarget != null)
  151. {
  152. Updated -= logTarget.Log;
  153. _logTargets.Remove(logTarget);
  154. logTarget.Dispose();
  155. }
  156. }
  157. public static void Shutdown()
  158. {
  159. Updated = null;
  160. _stdErrAdapter.Dispose();
  161. foreach (var target in _logTargets)
  162. {
  163. target.Dispose();
  164. }
  165. _logTargets.Clear();
  166. }
  167. public static IReadOnlyCollection<LogLevel> GetEnabledLevels()
  168. {
  169. var logs = new[] { Debug, Info, Warning, Error, Guest, AccessLog, Stub, Trace };
  170. List<LogLevel> levels = new(logs.Length);
  171. foreach (var log in logs)
  172. {
  173. if (log.HasValue)
  174. levels.Add(log.Value.Level);
  175. }
  176. return levels;
  177. }
  178. public static void SetEnable(LogLevel logLevel, bool enabled)
  179. {
  180. switch (logLevel)
  181. {
  182. #pragma warning disable IDE0055 // Disable formatting
  183. case LogLevel.Debug : Debug = enabled ? new Log(LogLevel.Debug) : new Log?(); break;
  184. case LogLevel.Info : Info = enabled ? new Log(LogLevel.Info) : new Log?(); break;
  185. case LogLevel.Warning : Warning = enabled ? new Log(LogLevel.Warning) : new Log?(); break;
  186. case LogLevel.Error : Error = enabled ? new Log(LogLevel.Error) : new Log?(); break;
  187. case LogLevel.Guest : Guest = enabled ? new Log(LogLevel.Guest) : new Log?(); break;
  188. case LogLevel.AccessLog : AccessLog = enabled ? new Log(LogLevel.AccessLog) : new Log?(); break;
  189. case LogLevel.Stub : Stub = enabled ? new Log(LogLevel.Stub) : new Log?(); break;
  190. case LogLevel.Trace : Trace = enabled ? new Log(LogLevel.Trace) : new Log?(); break;
  191. case LogLevel.Notice : break;
  192. default: throw new ArgumentException("Unknown Log Level", nameof(logLevel));
  193. #pragma warning restore IDE0055
  194. }
  195. }
  196. public static void SetEnable(LogClass logClass, bool enabled)
  197. {
  198. _enabledClasses[(int)logClass] = enabled;
  199. }
  200. }
  201. }