aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Common/Logging/Logger.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Common/Logging/Logger.cs')
-rw-r--r--src/Ryujinx.Common/Logging/Logger.cs224
1 files changed, 224 insertions, 0 deletions
diff --git a/src/Ryujinx.Common/Logging/Logger.cs b/src/Ryujinx.Common/Logging/Logger.cs
new file mode 100644
index 00000000..4d48dd48
--- /dev/null
+++ b/src/Ryujinx.Common/Logging/Logger.cs
@@ -0,0 +1,224 @@
+using Ryujinx.Common.SystemInterop;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Threading;
+
+namespace Ryujinx.Common.Logging
+{
+ public static class Logger
+ {
+ private static readonly Stopwatch m_Time;
+
+ private static readonly bool[] m_EnabledClasses;
+
+ private static readonly List<ILogTarget> m_LogTargets;
+
+ private static readonly StdErrAdapter _stdErrAdapter;
+
+ public static event EventHandler<LogEventArgs> Updated;
+
+ public readonly struct Log
+ {
+ internal readonly LogLevel Level;
+
+ internal Log(LogLevel level)
+ {
+ Level = level;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void PrintMsg(LogClass logClass, string message)
+ {
+ if (m_EnabledClasses[(int)logClass])
+ {
+ Updated?.Invoke(null, new LogEventArgs(Level, m_Time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, "", message)));
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Print(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)));
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Print(LogClass logClass, string message, object data, [CallerMemberName] string caller = "")
+ {
+ if (m_EnabledClasses[(int)logClass])
+ {
+ Updated?.Invoke(null, new LogEventArgs(Level, m_Time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, message), data));
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void PrintStub(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, "Stubbed. " + message)));
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void PrintStub(LogClass logClass, object data, [CallerMemberName] string caller = "")
+ {
+ if (m_EnabledClasses[(int)logClass])
+ {
+ Updated?.Invoke(null, new LogEventArgs(Level, m_Time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, "Stubbed."), data));
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void PrintStub(LogClass logClass, string message, object data, [CallerMemberName] string caller = "")
+ {
+ if (m_EnabledClasses[(int)logClass])
+ {
+ Updated?.Invoke(null, new LogEventArgs(Level, m_Time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, "Stubbed. " + message), data));
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void PrintRawMsg(string message)
+ {
+ Updated?.Invoke(null, new LogEventArgs(Level, m_Time.Elapsed, Thread.CurrentThread.Name, message));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static string FormatMessage(LogClass Class, string Caller, string Message) => $"{Class} {Caller}: {Message}";
+ }
+
+ public static Log? Debug { get; private set; }
+ public static Log? Info { get; private set; }
+ public static Log? Warning { get; private set; }
+ public static Log? Error { get; private set; }
+ public static Log? Guest { get; private set; }
+ public static Log? AccessLog { get; private set; }
+ public static Log? Stub { get; private set; }
+ public static Log? Trace { get; private set; }
+ public static Log Notice { get; } // Always enabled
+
+ static Logger()
+ {
+ m_EnabledClasses = new bool[Enum.GetNames<LogClass>().Length];
+
+ for (int index = 0; index < m_EnabledClasses.Length; index++)
+ {
+ m_EnabledClasses[index] = true;
+ }
+
+ m_LogTargets = new List<ILogTarget>();
+
+ m_Time = Stopwatch.StartNew();
+
+ // Logger should log to console by default
+ AddTarget(new AsyncLogTargetWrapper(
+ new ConsoleLogTarget("console"),
+ 1000,
+ 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);
+ Info = new Log(LogLevel.Info);
+ Trace = new Log(LogLevel.Trace);
+
+ _stdErrAdapter = new StdErrAdapter();
+ }
+
+ public static void RestartTime()
+ {
+ m_Time.Restart();
+ }
+
+ private static ILogTarget GetTarget(string targetName)
+ {
+ foreach (var target in m_LogTargets)
+ {
+ if (target.Name.Equals(targetName))
+ {
+ return target;
+ }
+ }
+
+ return null;
+ }
+
+ public static void AddTarget(ILogTarget target)
+ {
+ m_LogTargets.Add(target);
+
+ Updated += target.Log;
+ }
+
+ public static void RemoveTarget(string target)
+ {
+ ILogTarget logTarget = GetTarget(target);
+
+ if (logTarget != null)
+ {
+ Updated -= logTarget.Log;
+
+ m_LogTargets.Remove(logTarget);
+
+ logTarget.Dispose();
+ }
+ }
+
+ public static void Shutdown()
+ {
+ Updated = null;
+
+ _stdErrAdapter.Dispose();
+
+ foreach (var target in m_LogTargets)
+ {
+ target.Dispose();
+ }
+
+ m_LogTargets.Clear();
+ }
+
+ public static IReadOnlyCollection<LogLevel> GetEnabledLevels()
+ {
+ var logs = new Log?[] { Debug, Info, Warning, Error, Guest, AccessLog, Stub, Trace };
+ List<LogLevel> levels = new List<LogLevel>(logs.Length);
+ foreach (var log in logs)
+ {
+ if (log.HasValue)
+ {
+ levels.Add(log.Value.Level);
+ }
+ }
+
+ return levels;
+ }
+
+ public static void SetEnable(LogLevel logLevel, bool enabled)
+ {
+ switch (logLevel)
+ {
+ case LogLevel.Debug : Debug = enabled ? new Log(LogLevel.Debug) : new Log?(); break;
+ case LogLevel.Info : Info = enabled ? new Log(LogLevel.Info) : new Log?(); break;
+ case LogLevel.Warning : Warning = enabled ? new Log(LogLevel.Warning) : new Log?(); break;
+ case LogLevel.Error : Error = enabled ? new Log(LogLevel.Error) : new Log?(); break;
+ case LogLevel.Guest : Guest = enabled ? new Log(LogLevel.Guest) : new Log?(); break;
+ case LogLevel.AccessLog : AccessLog = enabled ? new Log(LogLevel.AccessLog): new Log?(); break;
+ case LogLevel.Stub : Stub = enabled ? new Log(LogLevel.Stub) : new Log?(); break;
+ case LogLevel.Trace : Trace = enabled ? new Log(LogLevel.Trace) : new Log?(); break;
+ default: throw new ArgumentException("Unknown Log Level");
+ }
+ }
+
+ public static void SetEnable(LogClass logClass, bool enabled)
+ {
+ m_EnabledClasses[(int)logClass] = enabled;
+ }
+ }
+}