diff --git a/Ryujinx.Core/Config.cs b/Ryujinx.Core/Config.cs
index 1e1cb3c7..7dc2dc79 100644
--- a/Ryujinx.Core/Config.cs
+++ b/Ryujinx.Core/Config.cs
@@ -9,15 +9,17 @@ namespace Ryujinx.Core
 {
     public static class Config
     {
-        public static bool EnableMemoryChecks   { get; private set; }
-        public static bool LoggingEnableInfo    { get; private set; }
-        public static bool LoggingEnableTrace   { get; private set; }
-        public static bool LoggingEnableDebug   { get; private set; }
-        public static bool LoggingEnableWarn    { get; private set; }
-        public static bool LoggingEnableError   { get; private set; }
-        public static bool LoggingEnableFatal   { get; private set; }
-        public static bool LoggingEnableIpc     { get; private set; }
-        public static bool LoggingEnableLogFile { get; private set; }
+        public static bool   EnableMemoryChecks     { get; private set; }
+        public static bool   LoggingEnableInfo      { get; private set; }
+        public static bool   LoggingEnableTrace     { get; private set; }
+        public static bool   LoggingEnableDebug     { get; private set; }
+        public static bool   LoggingEnableWarn      { get; private set; }
+        public static bool   LoggingEnableError     { get; private set; }
+        public static bool   LoggingEnableFatal     { get; private set; }
+        public static bool   LoggingEnableIpc       { get; private set; }
+        public static bool   LoggingEnableLogFile   { get; private set; }
+        public static bool   LoggingEnableFilter    { get; private set; }
+        public static bool[] LoggingFilteredClasses { get; private set; }
 
         public static JoyCon FakeJoyCon { get; private set; }
 
@@ -27,15 +29,32 @@ namespace Ryujinx.Core
             var iniPath = Path.Combine(iniFolder, "Ryujinx.conf");
             IniParser Parser = new IniParser(iniPath);
 
-            EnableMemoryChecks   = Convert.ToBoolean(Parser.Value("Enable_Memory_Checks"));
-            LoggingEnableInfo    = Convert.ToBoolean(Parser.Value("Logging_Enable_Info"));
-            LoggingEnableTrace   = Convert.ToBoolean(Parser.Value("Logging_Enable_Trace"));
-            LoggingEnableDebug   = Convert.ToBoolean(Parser.Value("Logging_Enable_Debug"));
-            LoggingEnableWarn    = Convert.ToBoolean(Parser.Value("Logging_Enable_Warn"));
-            LoggingEnableError   = Convert.ToBoolean(Parser.Value("Logging_Enable_Error"));
-            LoggingEnableFatal   = Convert.ToBoolean(Parser.Value("Logging_Enable_Fatal"));
-            LoggingEnableIpc     = Convert.ToBoolean(Parser.Value("Logging_Enable_Ipc"));
-            LoggingEnableLogFile = Convert.ToBoolean(Parser.Value("Logging_Enable_LogFile"));
+            EnableMemoryChecks     = Convert.ToBoolean(Parser.Value("Enable_Memory_Checks"));
+            LoggingEnableInfo      = Convert.ToBoolean(Parser.Value("Logging_Enable_Info"));
+            LoggingEnableTrace     = Convert.ToBoolean(Parser.Value("Logging_Enable_Trace"));
+            LoggingEnableDebug     = Convert.ToBoolean(Parser.Value("Logging_Enable_Debug"));
+            LoggingEnableWarn      = Convert.ToBoolean(Parser.Value("Logging_Enable_Warn"));
+            LoggingEnableError     = Convert.ToBoolean(Parser.Value("Logging_Enable_Error"));
+            LoggingEnableFatal     = Convert.ToBoolean(Parser.Value("Logging_Enable_Fatal"));
+            LoggingEnableIpc       = Convert.ToBoolean(Parser.Value("Logging_Enable_Ipc"));
+            LoggingEnableLogFile   = Convert.ToBoolean(Parser.Value("Logging_Enable_LogFile"));
+            LoggingEnableFilter    = Convert.ToBoolean(Parser.Value("Logging_Enable_Filter"));
+            LoggingFilteredClasses = new bool[(int)LogClass.Count];
+
+            string[] FilteredLogClasses = Parser.Value("Logging_Filtered_Classes", string.Empty).Split(',');
+            foreach (string LogClass in FilteredLogClasses)
+            {
+                if (!string.IsNullOrEmpty(LogClass.Trim()))
+                {
+                    foreach (LogClass EnumItemName in Enum.GetValues(typeof(LogClass)))
+                    {
+                        if (EnumItemName.ToString().ToLower().Contains(LogClass.Trim().ToLower()))
+                        {
+                            LoggingFilteredClasses[(int)EnumItemName] = true;
+                        }
+                    }
+                }
+            }
 
             FakeJoyCon = new JoyCon
             {
diff --git a/Ryujinx.Core/Hid/Hid.cs b/Ryujinx.Core/Hid/Hid.cs
index 48f309d8..be122c60 100644
--- a/Ryujinx.Core/Hid/Hid.cs
+++ b/Ryujinx.Core/Hid/Hid.cs
@@ -82,7 +82,7 @@ namespace Ryujinx.Core.Input
 
                 (AMemory Memory, long Position) ShMem = ShMemPositions[ShMemPositions.Length - 1];
 
-                Logging.Info($"HID shared memory successfully mapped to 0x{ShMem.Position:x16}!");
+                Logging.Info(LogClass.ServiceHid, $"HID shared memory successfully mapped to 0x{ShMem.Position:x16}!");
 
                 Init(ShMem.Memory, ShMem.Position);
             }
diff --git a/Ryujinx.Core/LogClass.cs b/Ryujinx.Core/LogClass.cs
new file mode 100644
index 00000000..67fd8559
--- /dev/null
+++ b/Ryujinx.Core/LogClass.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Ryujinx.Core
+{
+    public enum LogClass
+    {
+        Audio,
+        CPU,
+        GPU,
+        Kernel,
+        KernelIpc,
+        KernelScheduler,
+        KernelSvc,
+        Loader,
+        Service,
+        ServiceAcc,
+        ServiceAm,
+        ServiceApm,
+        ServiceAudio,
+        ServiceBsd,
+        ServiceFriend,
+        ServiceFs,
+        ServiceHid,
+        ServiceLm,
+        ServiceNifm,
+        ServiceNs,
+        ServiceNv,
+        ServicePctl,
+        ServicePl,
+        ServiceSet,
+        ServiceSfdnsres,
+        ServiceSm,
+        ServiceSss,
+        ServiceTime,
+        ServiceVi,
+        Count,
+    }
+}
diff --git a/Ryujinx.Core/Logging.cs b/Ryujinx.Core/Logging.cs
index 225903f9..1767e1a0 100644
--- a/Ryujinx.Core/Logging.cs
+++ b/Ryujinx.Core/Logging.cs
@@ -4,6 +4,7 @@ using System.Diagnostics;
 using System.IO;
 using System.Runtime.CompilerServices;
 using System.Text;
+using System.Threading;
 
 namespace Ryujinx.Core
 {
@@ -13,14 +14,16 @@ namespace Ryujinx.Core
 
         private const string LogFileName = "Ryujinx.log";
 
-        private static bool EnableInfo = Config.LoggingEnableInfo;
-        private static bool EnableTrace = Config.LoggingEnableTrace;
-        private static bool EnableDebug = Config.LoggingEnableDebug;
-        private static bool EnableWarn = Config.LoggingEnableWarn;
-        private static bool EnableError = Config.LoggingEnableError;
-        private static bool EnableFatal = Config.LoggingEnableFatal;
-        private static bool EnableIpc = Config.LoggingEnableIpc;
-        private static bool EnableLogFile = Config.LoggingEnableLogFile;
+        private static bool   EnableInfo         = Config.LoggingEnableInfo;
+        private static bool   EnableTrace        = Config.LoggingEnableTrace;
+        private static bool   EnableDebug        = Config.LoggingEnableDebug;
+        private static bool   EnableWarn         = Config.LoggingEnableWarn;
+        private static bool   EnableError        = Config.LoggingEnableError;
+        private static bool   EnableFatal        = Config.LoggingEnableFatal;
+        private static bool   EnableIpc          = Config.LoggingEnableIpc;
+        private static bool   EnableFilter       = Config.LoggingEnableFilter;
+        private static bool   EnableLogFile      = Config.LoggingEnableLogFile;
+        private static bool[] FilteredLogClasses = Config.LoggingFilteredClasses;
 
         private enum LogLevel
         {
@@ -45,6 +48,10 @@ namespace Ryujinx.Core
 
         private static void LogMessage(LogEntry LogEntry)
         {
+            if (EnableFilter)
+                if (!FilteredLogClasses[(int)LogEntry.LogClass])
+                    return;
+
             ConsoleColor consoleColor = ConsoleColor.White;
 
             switch (LogEntry.LogLevel)
@@ -69,8 +76,10 @@ namespace Ryujinx.Core
                     break;
             }
 
-            string Text = $"{LogEntry.ExecutionTime} | {LogEntry.LogLevel.ToString()}  > " +
-                $"{LogEntry.CallingMember} > {LogEntry.Message}";
+            LogEntry.ManagedThreadId = Thread.CurrentThread.ManagedThreadId;
+
+            string Text = $"{LogEntry.ExecutionTime} | {LogEntry.ManagedThreadId} > {LogEntry.LogClass} > " +
+                $"{LogEntry.LogLevel.ToString()}  > {LogEntry.CallingMember} > {LogEntry.Message}";
 
             Console.ForegroundColor = consoleColor;
             Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
@@ -90,7 +99,7 @@ namespace Ryujinx.Core
             }
         }
 
-        public static void Info(string Message, [CallerMemberName] string CallingMember = "")
+        public static void Info(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
         {
             if (EnableInfo)
             {
@@ -98,13 +107,14 @@ namespace Ryujinx.Core
                 {
                     CallingMember = CallingMember,
                     LogLevel      = LogLevel.Info,
+                    LogClass      = LogClass,
                     Message       = Message,
                     ExecutionTime = GetExecutionTime()
                 });
             }
         }
 
-        public static void Trace(string Message, [CallerMemberName] string CallingMember = "")
+        public static void Trace(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
         {
             if (EnableTrace)
             {
@@ -112,13 +122,14 @@ namespace Ryujinx.Core
                 {
                     CallingMember = CallingMember,
                     LogLevel      = LogLevel.Trace,
+                    LogClass      = LogClass,
                     Message       = Message,
                     ExecutionTime = GetExecutionTime()
                 });
             }
         }
 
-        public static void Debug(string Message, [CallerMemberName] string CallingMember = "")
+        public static void Debug(LogClass LogClass,string Message, [CallerMemberName] string CallingMember = "")
         {
             if (EnableDebug)
             {
@@ -126,13 +137,14 @@ namespace Ryujinx.Core
                 {
                     CallingMember = CallingMember,
                     LogLevel      = LogLevel.Debug,
+                    LogClass      = LogClass,
                     Message       = Message,
                     ExecutionTime = GetExecutionTime()
                 });
             }
         }
 
-        public static void Warn(string Message, [CallerMemberName] string CallingMember = "")
+        public static void Warn(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
         {
             if (EnableWarn)
             {
@@ -140,13 +152,14 @@ namespace Ryujinx.Core
                 {
                     CallingMember = CallingMember,
                     LogLevel      = LogLevel.Warn,
+                    LogClass      = LogClass,
                     Message       = Message,
                     ExecutionTime = GetExecutionTime()
                 });
             }
         }
 
-        public static void Error(string Message, [CallerMemberName] string CallingMember = "")
+        public static void Error(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
         {
             if (EnableError)
             {
@@ -154,13 +167,14 @@ namespace Ryujinx.Core
                 {
                     CallingMember = CallingMember,
                     LogLevel      = LogLevel.Error,
+                    LogClass      = LogClass,
                     Message       = Message,
                     ExecutionTime = GetExecutionTime()
                 });
             }
         }
 
-        public static void Fatal(string Message, [CallerMemberName] string CallingMember = "")
+        public static void Fatal(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
         {
             if (EnableFatal)
             {
@@ -168,6 +182,7 @@ namespace Ryujinx.Core
                 {
                     CallingMember = CallingMember,
                     LogLevel      = LogLevel.Fatal,
+                    LogClass      = LogClass,
                     Message       = Message,
                     ExecutionTime = GetExecutionTime()
                 });
@@ -253,6 +268,8 @@ namespace Ryujinx.Core
             public string   CallingMember;
             public string   ExecutionTime;
             public string   Message;
+            public int      ManagedThreadId;
+            public LogClass LogClass;
             public LogLevel LogLevel;
         }
     }
diff --git a/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs b/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs
index 95754298..7ba78b3f 100644
--- a/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs
+++ b/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs
@@ -136,13 +136,13 @@ namespace Ryujinx.Core.OsHle.Handles
 
                     Thread.Thread.Execute();
 
-                    Logging.Debug($"{GetDbgThreadInfo(Thread)} running.");
+                    Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} running.");
                 }
                 else
                 {
                     WaitingToRun[Thread.ProcessorId].Push(SchedThread);
 
-                    Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting to run.");
+                    Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(SchedThread.Thread)} waiting to run.");
                 }
             }
         }
@@ -168,13 +168,13 @@ namespace Ryujinx.Core.OsHle.Handles
         {
             SchedulerThread SchedThread;
 
-            Logging.Debug($"{GetDbgThreadInfo(CurrThread)} entering ipc delay wait state.");
+            Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(CurrThread)} entering ipc delay wait state.");
 
             lock (SchedLock)
             {
                 if (!AllThreads.TryGetValue(CurrThread, out SchedThread))
                 {
-                    Logging.Error($"{GetDbgThreadInfo(CurrThread)} was not found on the scheduler queue!");
+                    Logging.Error(LogClass.KernelScheduler, $"{GetDbgThreadInfo(CurrThread)} was not found on the scheduler queue!");
 
                     return;
                 }
@@ -187,7 +187,7 @@ namespace Ryujinx.Core.OsHle.Handles
         {
             SchedulerThread SchedThread;
 
-            Logging.Debug($"{GetDbgThreadInfo(Thread)} entering signal wait state.");
+            Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} entering signal wait state.");
 
             lock (SchedLock)
             {
@@ -204,7 +204,7 @@ namespace Ryujinx.Core.OsHle.Handles
 
                 if (!AllThreads.TryGetValue(Thread, out SchedThread))
                 {
-                    Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!");
+                    Logging.Error(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!");
 
                     return false;
                 }
@@ -214,7 +214,7 @@ namespace Ryujinx.Core.OsHle.Handles
 
             if (Timeout >= 0)
             {
-                Logging.Debug($"{GetDbgThreadInfo(Thread)} has wait timeout of {Timeout}ms.");
+                Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} has wait timeout of {Timeout}ms.");
 
                 Result = SchedThread.WaitEvent.WaitOne(Timeout);
             }
@@ -236,7 +236,7 @@ namespace Ryujinx.Core.OsHle.Handles
             {
                 if (ActiveProcessors.Add(Thread.ProcessorId))
                 {
-                    Logging.Debug($"{GetDbgThreadInfo(Thread)} resuming execution...");
+                    Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} resuming execution...");
 
                     return;
                 }
@@ -246,14 +246,14 @@ namespace Ryujinx.Core.OsHle.Handles
 
             SchedThread.WaitEvent.WaitOne();
 
-            Logging.Debug($"{GetDbgThreadInfo(Thread)} resuming execution...");
+            Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} resuming execution...");
         }
 
         public void Yield(KThread Thread)
         {
             SchedulerThread SchedThread;
 
-            Logging.Debug($"{GetDbgThreadInfo(Thread)} yielded execution.");
+            Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} yielded execution.");
 
             lock (SchedLock)
             {
@@ -261,7 +261,7 @@ namespace Ryujinx.Core.OsHle.Handles
 
                 if (SchedThread == null)
                 {
-                    Logging.Debug($"{GetDbgThreadInfo(Thread)} resumed because theres nothing better to run.");
+                    Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} resumed because theres nothing better to run.");
 
                     return;
                 }
@@ -270,7 +270,7 @@ namespace Ryujinx.Core.OsHle.Handles
 
                 if (!AllThreads.TryGetValue(Thread, out SchedThread))
                 {
-                    Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!");
+                    Logging.Error(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!");
 
                     return;
                 }
@@ -280,7 +280,7 @@ namespace Ryujinx.Core.OsHle.Handles
 
             SchedThread.WaitEvent.WaitOne();
 
-            Logging.Debug($"{GetDbgThreadInfo(Thread)} resuming execution...");
+            Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} resuming execution...");
         }
 
         private void RunThread(SchedulerThread SchedThread)
@@ -291,7 +291,7 @@ namespace Ryujinx.Core.OsHle.Handles
             }
             else
             {
-                Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} running.");
+                Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(SchedThread.Thread)} running.");
             }
         }
 
@@ -305,7 +305,7 @@ namespace Ryujinx.Core.OsHle.Handles
                     {
                         if (!WaitingToRun[Thread.ProcessorId].HasThread(SchedThread))
                         {
-                            Logging.Debug($"{GetDbgThreadInfo(Thread)} signaled.");
+                            Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} signaled.");
 
                             SchedThread.WaitEvent.Set();
                         }
diff --git a/Ryujinx.Core/OsHle/Horizon.cs b/Ryujinx.Core/OsHle/Horizon.cs
index 240c08db..049b03b2 100644
--- a/Ryujinx.Core/OsHle/Horizon.cs
+++ b/Ryujinx.Core/OsHle/Horizon.cs
@@ -56,7 +56,7 @@ namespace Ryujinx.Core.OsHle
                         continue;
                     }
 
-                    Logging.Info($"Loading {Path.GetFileNameWithoutExtension(File)}...");
+                    Logging.Info(LogClass.Loader, $"Loading {Path.GetFileNameWithoutExtension(File)}...");
 
                     using (FileStream Input = new FileStream(File, FileMode.Open))
                     {
@@ -131,7 +131,7 @@ namespace Ryujinx.Core.OsHle
             {
                 string NextNro = Homebrew.ReadHbAbiNextLoadPath(Process.Memory, Process.HbAbiDataPosition);
 
-                Logging.Info($"HbAbi NextLoadPath {NextNro}");
+                Logging.Info(LogClass.Loader, $"HbAbi NextLoadPath {NextNro}");
 
                 if (NextNro == string.Empty)
                 {
diff --git a/Ryujinx.Core/OsHle/Process.cs b/Ryujinx.Core/OsHle/Process.cs
index ddbeecdc..0dd56dcb 100644
--- a/Ryujinx.Core/OsHle/Process.cs
+++ b/Ryujinx.Core/OsHle/Process.cs
@@ -91,7 +91,7 @@ namespace Ryujinx.Core.OsHle
                 throw new ObjectDisposedException(nameof(Process));
             }
 
-            Logging.Info($"Image base at 0x{ImageBase:x16}.");
+            Logging.Info(LogClass.Loader, $"Image base at 0x{ImageBase:x16}.");
 
             Executable Executable = new Executable(Program, Memory, ImageBase);
 
@@ -260,7 +260,7 @@ namespace Ryujinx.Core.OsHle
                 }
             }
 
-            Logging.Trace($"Executing at 0x{e.Position:x16} {e.SubName} {NsoName}");
+            Logging.Trace(LogClass.Loader, $"Executing at 0x{e.Position:x16} {e.SubName} {NsoName}");
         }
 
         public void EnableCpuTracing()
@@ -290,7 +290,7 @@ namespace Ryujinx.Core.OsHle
         {
             if (sender is AThread Thread)
             {
-                Logging.Info($"Thread {Thread.ThreadId} exiting...");
+                Logging.Info(LogClass.KernelScheduler, $"Thread {Thread.ThreadId} exiting...");
 
                 TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _);
             }
@@ -302,7 +302,7 @@ namespace Ryujinx.Core.OsHle
                     Dispose();
                 }
 
-                Logging.Info($"No threads running, now exiting Process {ProcessId}...");
+                Logging.Info(LogClass.KernelScheduler, $"No threads running, now exiting Process {ProcessId}...");
 
                 Ns.Os.ExitProcess(ProcessId);
             }
@@ -317,7 +317,7 @@ namespace Ryujinx.Core.OsHle
         {
             if (!ThreadsByTpidr.TryGetValue(Tpidr, out KThread Thread))
             {
-                Logging.Error($"Thread with TPIDR 0x{Tpidr:x16} not found!");
+                Logging.Error(LogClass.KernelScheduler, $"Thread with TPIDR 0x{Tpidr:x16} not found!");
             }
 
             return Thread;
@@ -340,7 +340,7 @@ namespace Ryujinx.Core.OsHle
                 {
                     ShouldDispose = true;
 
-                    Logging.Info($"Process {ProcessId} waiting all threads terminate...");
+                    Logging.Info(LogClass.KernelScheduler, $"Process {ProcessId} waiting all threads terminate...");
 
                     return;
                 }
@@ -369,7 +369,7 @@ namespace Ryujinx.Core.OsHle
 
                 Memory.Dispose();
 
-                Logging.Info($"Process {ProcessId} exiting...");
+                Logging.Info(LogClass.KernelScheduler, $"Process {ProcessId} exiting...");
             }
         }
     }
diff --git a/Ryujinx.Core/OsHle/Services/Am/IApplicationFunctions.cs b/Ryujinx.Core/OsHle/Services/Am/IApplicationFunctions.cs
index 29e363be..ba41727e 100644
--- a/Ryujinx.Core/OsHle/Services/Am/IApplicationFunctions.cs
+++ b/Ryujinx.Core/OsHle/Services/Am/IApplicationFunctions.cs
@@ -58,7 +58,7 @@ namespace Ryujinx.Core.OsHle.Services.Am
             int Module = ErrorCode & 0xFF;
             int Description = (ErrorCode >> 9) & 0xFFF;
 
-            Logging.Info($"({(ErrorModule)Module}){2000 + Module}-{Description}");
+            Logging.Info(LogClass.ServiceAm, $"({(ErrorModule)Module}){2000 + Module}-{Description}");
 
             return 0;
         }
diff --git a/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs b/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs
index b194e76c..527b6532 100644
--- a/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs
+++ b/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs
@@ -124,14 +124,14 @@ namespace Ryujinx.Core.OsHle.Services.Aud
 
         public long AppendAudioOutBufferEx(ServiceCtx Context)
         {
-            Logging.Warn("Not implemented!");
+            Logging.Warn(LogClass.ServiceAudio, "Not implemented!");
 
             return 0;
         }
 
         public long GetReleasedAudioOutBufferEx(ServiceCtx Context)
         {
-            Logging.Warn("Not implemented!");
+            Logging.Warn(LogClass.ServiceAudio, "Not implemented!");
 
             return 0;
         }
diff --git a/Ryujinx.Core/OsHle/Services/Bsd/IClient.cs b/Ryujinx.Core/OsHle/Services/Bsd/IClient.cs
index 199ea113..ccfb9147 100644
--- a/Ryujinx.Core/OsHle/Services/Bsd/IClient.cs
+++ b/Ryujinx.Core/OsHle/Services/Bsd/IClient.cs
@@ -428,7 +428,7 @@ namespace Ryujinx.Core.OsHle.Services.Bsd
                                    Reader.ReadByte().ToString() + "." +
                                    Reader.ReadByte().ToString();
 
-                Logging.Debug($"Try to connect to {IpAddress}:{Port}");
+                Logging.Debug(LogClass.ServiceBsd, $"Try to connect to {IpAddress}:{Port}");
 
                 Sockets[SocketId].IpAddress = IPAddress.Parse(IpAddress);
                 Sockets[SocketId].RemoteEP = new IPEndPoint(Sockets[SocketId].IpAddress, Port);
diff --git a/Ryujinx.Core/OsHle/Services/IpcService.cs b/Ryujinx.Core/OsHle/Services/IpcService.cs
index 963c7022..f39adb7a 100644
--- a/Ryujinx.Core/OsHle/Services/IpcService.cs
+++ b/Ryujinx.Core/OsHle/Services/IpcService.cs
@@ -81,7 +81,7 @@ namespace Ryujinx.Core.OsHle.Services
             {
                 Context.ResponseData.BaseStream.Seek(IsDomain ? 0x20 : 0x10, SeekOrigin.Begin);
 
-                Logging.Trace($"{Service.GetType().Name}: {ProcessRequest.Method.Name}");
+                Logging.Trace(LogClass.KernelIpc, $"{Service.GetType().Name}: {ProcessRequest.Method.Name}");
 
                 long Result = ProcessRequest(Context);
 
diff --git a/Ryujinx.Core/OsHle/Services/Lm/ILogger.cs b/Ryujinx.Core/OsHle/Services/Lm/ILogger.cs
index 9a0b1aa3..6d3de79b 100644
--- a/Ryujinx.Core/OsHle/Services/Lm/ILogger.cs
+++ b/Ryujinx.Core/OsHle/Services/Lm/ILogger.cs
@@ -129,11 +129,11 @@ namespace Ryujinx.Core.OsHle.Services.Lm
 
             switch((Severity)iSeverity)
             {
-                case Severity.Trace:    Logging.Trace(LogString); break;
-                case Severity.Info:     Logging.Info(LogString);  break;
-                case Severity.Warning:  Logging.Warn(LogString);  break;
-                case Severity.Error:    Logging.Error(LogString); break;
-                case Severity.Critical: Logging.Fatal(LogString); break;
+                case Severity.Trace:    Logging.Trace(LogClass.ServiceLm, LogString); break;
+                case Severity.Info:     Logging.Info(LogClass.ServiceLm, LogString);  break;
+                case Severity.Warning:  Logging.Warn(LogClass.ServiceLm, LogString);  break;
+                case Severity.Error:    Logging.Error(LogClass.ServiceLm, LogString); break;
+                case Severity.Critical: Logging.Fatal(LogClass.ServiceLm, LogString); break;
             }
 
             return 0;
diff --git a/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs b/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs
index abda5b86..cc5f95cd 100644
--- a/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs
+++ b/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs
@@ -228,7 +228,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv
 
             if (Map == null)
             {
-                Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
+                Logging.Warn(LogClass.ServiceNv, $"Trying to use invalid NvMap Handle {Handle}!");
 
                 return -1; //TODO: Corrent error code.
             }
@@ -634,7 +634,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv
 
             Context.Memory.WriteInt32(Position + 4, Map.Handle);
 
-            Logging.Info($"NvMap {Map.Id} created with size {Size:x8}!");
+            Logging.Info(LogClass.ServiceNv, $"NvMap {Map.Id} created with size {Size:x8}!");
 
             return 0;
         }
@@ -649,7 +649,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv
 
             if (Map == null)
             {
-                Logging.Warn($"Trying to use invalid NvMap Id {Id}!");
+                Logging.Warn(LogClass.ServiceNv, $"Trying to use invalid NvMap Id {Id}!");
 
                 return -1; //TODO: Corrent error code.
             }
@@ -676,7 +676,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv
 
             if (Map == null)
             {
-                Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
+                Logging.Warn(LogClass.ServiceNv, $"Trying to use invalid NvMap Handle {Handle}!");
 
                 return -1; //TODO: Corrent error code.
             }
@@ -702,7 +702,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv
 
             if (Map == null)
             {
-                Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
+                Logging.Warn(LogClass.ServiceNv, $"Trying to use invalid NvMap Handle {Handle}!");
 
                 return -1; //TODO: Corrent error code.
             }
@@ -727,7 +727,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv
 
             if (Map == null)
             {
-                Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
+                Logging.Warn(LogClass.ServiceNv, $"Trying to use invalid NvMap Handle {Handle}!");
 
                 return -1; //TODO: Corrent error code.
             }
@@ -757,7 +757,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv
 
             if (Map == null)
             {
-                Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
+                Logging.Warn(LogClass.ServiceNv, $"Trying to use invalid NvMap Handle {Handle}!");
 
                 return -1; //TODO: Corrent error code.
             }
diff --git a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs
index 4ab64e2a..3ba4a45f 100644
--- a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs
+++ b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs
@@ -112,7 +112,7 @@ namespace Ryujinx.Core.OsHle.Services.Android
 
                 if (Commands.TryGetValue((InterfaceName, Code), out ServiceProcessParcel ProcReq))
                 {
-                    Logging.Debug($"{InterfaceName} {ProcReq.Method.Name}");
+                    Logging.Debug(LogClass.ServiceNv, $"{InterfaceName} {ProcReq.Method.Name}");
 
                     return ProcReq(Context, Reader);
                 }
@@ -412,7 +412,7 @@ namespace Ryujinx.Core.OsHle.Services.Android
                         break;
                     }
 
-                    Logging.Debug("Waiting for a free BufferQueue slot...");
+                    Logging.Debug(LogClass.ServiceNv, "Waiting for a free BufferQueue slot...");
 
                     if (Disposed)
                     {
@@ -426,7 +426,7 @@ namespace Ryujinx.Core.OsHle.Services.Android
             }
             while (!Disposed);
 
-            Logging.Debug($"Found free BufferQueue slot {Slot}!");
+            Logging.Debug(LogClass.ServiceNv, $"Found free BufferQueue slot {Slot}!");
 
             return Slot;
         }
diff --git a/Ryujinx.Core/OsHle/Svc/SvcHandler.cs b/Ryujinx.Core/OsHle/Svc/SvcHandler.cs
index 3bdb1060..9fea59a8 100644
--- a/Ryujinx.Core/OsHle/Svc/SvcHandler.cs
+++ b/Ryujinx.Core/OsHle/Svc/SvcHandler.cs
@@ -80,11 +80,11 @@ namespace Ryujinx.Core.OsHle.Svc
 
             if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func))
             {
-                Logging.Trace($"(Thread {ThreadState.ThreadId}) {Func.Method.Name} called.");
+                Logging.Trace(LogClass.KernelSvc, $"(Thread {ThreadState.ThreadId}) {Func.Method.Name} called.");
 
                 Func(ThreadState);
 
-                Logging.Trace($"(Thread {ThreadState.ThreadId}) {Func.Method.Name} ended.");
+                Logging.Trace(LogClass.KernelSvc, $"(Thread {ThreadState.ThreadId}) {Func.Method.Name} ended.");
             }
             else
             {
diff --git a/Ryujinx.Core/OsHle/Svc/SvcMemory.cs b/Ryujinx.Core/OsHle/Svc/SvcMemory.cs
index 80f24d2b..73485715 100644
--- a/Ryujinx.Core/OsHle/Svc/SvcMemory.cs
+++ b/Ryujinx.Core/OsHle/Svc/SvcMemory.cs
@@ -57,7 +57,7 @@ namespace Ryujinx.Core.OsHle.Svc
 
             if (!IsValidPosition(Src))
             {
-                Logging.Warn($"Tried to map Memory at invalid src address {Src:x16}!");
+                Logging.Warn(LogClass.KernelSvc, $"Tried to map Memory at invalid src address {Src:x16}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
 
@@ -66,7 +66,7 @@ namespace Ryujinx.Core.OsHle.Svc
 
             if (!IsValidMapPosition(Dst))
             {
-                Logging.Warn($"Tried to map Memory at invalid dst address {Dst:x16}!");
+                Logging.Warn(LogClass.KernelSvc, $"Tried to map Memory at invalid dst address {Dst:x16}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
 
@@ -92,7 +92,7 @@ namespace Ryujinx.Core.OsHle.Svc
 
             if (!IsValidPosition(Src))
             {
-                Logging.Warn($"Tried to unmap Memory at invalid src address {Src:x16}!");
+                Logging.Warn(LogClass.KernelSvc, $"Tried to unmap Memory at invalid src address {Src:x16}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
 
@@ -101,7 +101,7 @@ namespace Ryujinx.Core.OsHle.Svc
 
             if (!IsValidMapPosition(Dst))
             {
-                Logging.Warn($"Tried to unmap Memory at invalid dst address {Dst:x16}!");
+                Logging.Warn(LogClass.KernelSvc, $"Tried to unmap Memory at invalid dst address {Dst:x16}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
 
@@ -158,7 +158,7 @@ namespace Ryujinx.Core.OsHle.Svc
 
             if (!IsValidPosition(Src))
             {
-                Logging.Warn($"Tried to map SharedMemory at invalid address {Src:x16}!");
+                Logging.Warn(LogClass.KernelSvc, $"Tried to map SharedMemory at invalid address {Src:x16}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
 
@@ -196,7 +196,7 @@ namespace Ryujinx.Core.OsHle.Svc
 
             if (!IsValidPosition(Src))
             {
-                Logging.Warn($"Tried to unmap SharedMemory at invalid address {Src:x16}!");
+                Logging.Warn(LogClass.KernelSvc, $"Tried to unmap SharedMemory at invalid address {Src:x16}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
 
@@ -230,7 +230,7 @@ namespace Ryujinx.Core.OsHle.Svc
 
             if (!IsValidPosition(Src))
             {
-                Logging.Warn($"Tried to create TransferMemory at invalid address {Src:x16}!");
+                Logging.Warn(LogClass.KernelSvc, $"Tried to create TransferMemory at invalid address {Src:x16}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
 
diff --git a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs
index 68eebc88..e615b429 100644
--- a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs
+++ b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs
@@ -39,7 +39,7 @@ namespace Ryujinx.Core.OsHle.Svc
 
             if (Obj == null)
             {
-                Logging.Warn($"Tried to CloseHandle on invalid handle 0x{Handle:x8}!");
+                Logging.Warn(LogClass.KernelSvc, $"Tried to CloseHandle on invalid handle 0x{Handle:x8}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
 
@@ -75,7 +75,7 @@ namespace Ryujinx.Core.OsHle.Svc
             }
             else
             {
-                Logging.Warn($"Tried to ResetSignal on invalid event handle 0x{Handle:x8}!");
+                Logging.Warn(LogClass.KernelSvc, $"Tried to ResetSignal on invalid event handle 0x{Handle:x8}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
             }
@@ -99,7 +99,7 @@ namespace Ryujinx.Core.OsHle.Svc
 
                 if (SyncObj == null)
                 {
-                    Logging.Warn($"Tried to WaitSynchronization on invalid handle 0x{Handle:x8}!");
+                    Logging.Warn(LogClass.KernelSvc, $"Tried to WaitSynchronization on invalid handle 0x{Handle:x8}!");
 
                     ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
 
@@ -199,7 +199,7 @@ namespace Ryujinx.Core.OsHle.Svc
             }
             else
             {
-                Logging.Warn($"Tried to SendSyncRequest on invalid session handle 0x{Handle:x8}!");
+                Logging.Warn(LogClass.KernelSvc, $"Tried to SendSyncRequest on invalid session handle 0x{Handle:x8}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
             }
@@ -221,7 +221,7 @@ namespace Ryujinx.Core.OsHle.Svc
 
             string Str = AMemoryHelper.ReadAsciiString(Memory, Position, Size);
 
-            Logging.Info(Str);
+            Logging.Info(LogClass.KernelSvc, Str);
 
             ThreadState.X0 = 0;
         }
diff --git a/Ryujinx.Core/OsHle/Svc/SvcThread.cs b/Ryujinx.Core/OsHle/Svc/SvcThread.cs
index c58cffca..4dc9e15c 100644
--- a/Ryujinx.Core/OsHle/Svc/SvcThread.cs
+++ b/Ryujinx.Core/OsHle/Svc/SvcThread.cs
@@ -137,7 +137,7 @@ namespace Ryujinx.Core.OsHle.Svc
             }
             else
             {
-                Logging.Warn($"Tried to GetThreadId on invalid thread handle 0x{Handle:x8}!");
+                Logging.Warn(LogClass.KernelSvc, $"Tried to GetThreadId on invalid thread handle 0x{Handle:x8}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
             }
diff --git a/Ryujinx/Ryujinx.conf b/Ryujinx/Ryujinx.conf
index 00f0da5e..9761c356 100644
--- a/Ryujinx/Ryujinx.conf
+++ b/Ryujinx/Ryujinx.conf
@@ -22,6 +22,12 @@ Logging_Enable_Fatal = true
 #Enable print Ipc logs
 Logging_Enable_Ipc = false
 
+#Enable log filter
+Logging_Enable_Filter = false
+
+#Filtered log classes, seperated by ',', eg. `Logging_Filtered_Classes = Loader,ServiceFS`
+Logging_Filtered_Classes =
+
 #Save logs into Ryujinx.log
 Logging_Enable_LogFile = false
 
diff --git a/Ryujinx/Ui/Program.cs b/Ryujinx/Ui/Program.cs
index 9c8af28d..f9d40eb5 100644
--- a/Ryujinx/Ui/Program.cs
+++ b/Ryujinx/Ui/Program.cs
@@ -37,27 +37,27 @@ namespace Ryujinx
 
                     if (RomFsFiles.Length > 0)
                     {
-                        Logging.Info("Loading as cart with RomFS.");
+                        Logging.Info(LogClass.Loader, "Loading as cart with RomFS.");
 
                         Ns.LoadCart(args[0], RomFsFiles[0]);
                     }
                     else
                     {
-                        Logging.Info("Loading as cart WITHOUT RomFS.");
+                        Logging.Info(LogClass.Loader, "Loading as cart WITHOUT RomFS.");
 
                         Ns.LoadCart(args[0]);
                     }
                 }
                 else if (File.Exists(args[0]))
                 {
-                    Logging.Info("Loading as homebrew.");
+                    Logging.Info(LogClass.Loader, "Loading as homebrew.");
 
                     Ns.LoadProgram(args[0]);
                 }
             }
             else
             {
-                Logging.Error("Please specify the folder with the NSOs/IStorage or a NSO/NRO.");
+                Logging.Error(LogClass.Loader, "Please specify the folder with the NSOs/IStorage or a NSO/NRO.");
             }
 
             using (GLScreen Screen = new GLScreen(Ns, Renderer))