diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp
index eb7a7d181..d8209fec9 100644
--- a/src/citra/citra.cpp
+++ b/src/citra/citra.cpp
@@ -239,12 +239,7 @@ int main(int argc, char** argv) {
 
     Log::Filter log_filter;
     log_filter.ParseFilterString(Settings::values.log_filter);
-    Log::SetGlobalFilter(log_filter);
-
-    Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
-    FileUtil::CreateFullPath(FileUtil::GetUserPath(D_LOGS_IDX));
-    Log::AddBackend(
-        std::make_unique<Log::FileBackend>(FileUtil::GetUserPath(D_LOGS_IDX) + LOG_FILE));
+    Log::SetFilter(&log_filter);
 
     // Apply the command line arguments
     Settings::values.gdbstub_port = gdb_port;
diff --git a/src/citra_qt/configuration/configure_debug.cpp b/src/citra_qt/configuration/configure_debug.cpp
index 9744ac6c0..b9eb0e3d1 100644
--- a/src/citra_qt/configuration/configure_debug.cpp
+++ b/src/citra_qt/configuration/configure_debug.cpp
@@ -44,7 +44,7 @@ void ConfigureDebug::applyConfiguration() {
     Debugger::ToggleConsole();
     Log::Filter filter;
     filter.ParseFilterString(Settings::values.log_filter);
-    Log::SetGlobalFilter(filter);
+    Log::SetFilter(&filter);
     Settings::Apply();
 }
 
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index d60d8f511..c164eeb81 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -1298,8 +1298,6 @@ void GMainWindow::SyncMenuUISettings() {
 #endif
 
 int main(int argc, char* argv[]) {
-    Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
-
     MicroProfileOnThreadCreate("Frontend");
     SCOPE_EXIT({ MicroProfileShutdown(); });
 
@@ -1318,10 +1316,7 @@ int main(int argc, char* argv[]) {
     // After settings have been loaded by GMainWindow, apply the filter
     Log::Filter log_filter;
     log_filter.ParseFilterString(Settings::values.log_filter);
-    Log::SetGlobalFilter(log_filter);
-    FileUtil::CreateFullPath(FileUtil::GetUserPath(D_LOGS_IDX));
-    Log::AddBackend(
-        std::make_unique<Log::FileBackend>(FileUtil::GetUserPath(D_LOGS_IDX) + LOG_FILE));
+    Log::SetFilter(&log_filter);
 
     main_window.show();
     return app.exec();
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 20b4fe5ea..3c0361713 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -4,118 +4,17 @@
 
 #include <algorithm>
 #include <array>
-#include <chrono>
 #include <cstdio>
-#include <future>
-#include <memory>
-#include <thread>
-#ifdef _WIN32
-#include <share.h> // For _SH_DENYWR
-#else
-#define _SH_DENYWR 0
-#endif
 #include "common/assert.h"
 #include "common/common_funcs.h" // snprintf compatibility define
 #include "common/logging/backend.h"
+#include "common/logging/filter.h"
 #include "common/logging/log.h"
 #include "common/logging/text_formatter.h"
 #include "common/string_util.h"
-#include "common/threadsafe_queue.h"
 
 namespace Log {
 
-/**
- * Static state as a singleton.
- */
-class Impl {
-public:
-    static Impl& Instance() {
-        static Impl backend;
-        return backend;
-    }
-
-    Impl(Impl const&) = delete;
-    const Impl& operator=(Impl const&) = delete;
-
-    void PushEntry(Entry e) {
-        message_queue.Push(std::move(e));
-    }
-
-    void AddBackend(std::unique_ptr<Backend> backend) {
-        backends.push_back(std::move(backend));
-    }
-
-    void RemoveBackend(const std::string& backend_name) {
-        std::remove_if(backends.begin(), backends.end(),
-                       [&backend_name](const auto& i) { return i->GetName() == backend_name; });
-    }
-
-    const Filter& GetGlobalFilter() const {
-        return filter;
-    }
-
-    void SetGlobalFilter(const Filter& f) {
-        filter = f;
-    }
-
-    boost::optional<Backend*> GetBackend(const std::string& backend_name) {
-        auto it = std::find_if(backends.begin(), backends.end(), [&backend_name](const auto& i) {
-            return i->GetName() == backend_name;
-        });
-        if (it == backends.end())
-            return boost::none;
-        return it->get();
-    }
-
-private:
-    Impl() {
-        backend_thread = std::async(std::launch::async, [&] {
-            using namespace std::chrono_literals;
-            Entry entry;
-            while (running) {
-                if (!message_queue.PopWait(entry)) {
-                    continue;
-                }
-                for (const auto& backend : backends) {
-                    backend->Write(entry);
-                }
-            }
-        });
-    }
-    ~Impl() {
-        running = false;
-        backend_thread.get();
-    }
-
-    std::atomic_bool running{true};
-    std::future<void> backend_thread;
-    std::vector<std::unique_ptr<Backend>> backends;
-    Common::MPSCQueue<Log::Entry> message_queue;
-    Filter filter;
-};
-
-void ConsoleBackend::Write(const Entry& entry) {
-    PrintMessage(entry);
-}
-
-void ColorConsoleBackend::Write(const Entry& entry) {
-    PrintColoredMessage(entry);
-}
-
-// _SH_DENYWR allows read only access to the file for other programs.
-// It is #defined to 0 on other platforms
-FileBackend::FileBackend(const std::string& filename) : file(filename, "w", _SH_DENYWR) {}
-
-void FileBackend::Write(const Entry& entry) {
-    if (!file.IsOpen()) {
-        return;
-    }
-    file.WriteString(FormatLogMessage(entry) + '\n');
-    if (entry.log_level >= Level::Error) {
-        file.Flush();
-    }
-}
-
 /// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
 #define ALL_LOG_CLASSES()                                                                          \
     CLS(Log)                                                                                       \
@@ -233,26 +132,15 @@ Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsign
     return entry;
 }
 
-void SetGlobalFilter(const Filter& filter) {
-    Impl::Instance().SetGlobalFilter(filter);
-}
+static Filter* filter = nullptr;
 
-void AddBackend(std::unique_ptr<Backend> backend) {
-    Impl::Instance().AddBackend(std::move(backend));
-}
-
-void RemoveBackend(const std::string& backend_name) {
-    Impl::Instance().RemoveBackend(backend_name);
-}
-
-boost::optional<Backend*> GetBackend(const std::string& backend_name) {
-    return Impl::Instance().GetBackend(backend_name);
+void SetFilter(Filter* new_filter) {
+    filter = new_filter;
 }
 
 void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num,
                 const char* function, const char* format, ...) {
-    auto filter = Impl::Instance().GetGlobalFilter();
-    if (!filter.CheckMessage(log_class, log_level))
+    if (!filter->CheckMessage(log_class, log_level))
         return;
     std::array<char, 4 * 1024> formatting_buffer;
     va_list args;
@@ -262,14 +150,13 @@ void LogMessage(Class log_class, Level log_level, const char* filename, unsigned
     Entry entry = CreateEntry(log_class, log_level, filename, line_num, function,
                               std::string(formatting_buffer.data()));
 
-    Impl::Instance().PushEntry(std::move(entry));
+    PrintColoredMessage(entry);
 }
 
 void LogEntry(Entry& entry) {
-    auto filter = Impl::Instance().GetGlobalFilter();
-    if (!filter.CheckMessage(entry.log_class, entry.log_level))
+    if (!filter->CheckMessage(entry.log_class, entry.log_level))
         return;
 
-    Impl::Instance().PushEntry(std::move(entry));
+    PrintColoredMessage(entry);
 }
 } // namespace Log
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index 0bf9b8fd7..d5b9834e1 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -6,81 +6,14 @@
 
 #include <chrono>
 #include <cstdarg>
-#include <memory>
 #include <string>
 #include <utility>
-#include <boost/optional.hpp>
-#include "common/file_util.h"
-#include "common/logging/filter.h"
 #include "common/logging/log.h"
 
 namespace Log {
 
 class Filter;
 
-/**
- * Interface for logging backends. As loggers can be created and removed at runtime, this can be
- * used by a frontend for adding a custom logging backend as needed
- */
-class Backend {
-public:
-    virtual ~Backend() = default;
-    virtual void SetFilter(const Filter& new_filter) {
-        filter = new_filter;
-    }
-    virtual const char* GetName() const = 0;
-    virtual void Write(const Entry& entry) = 0;
-
-private:
-    Filter filter;
-};
-
-/**
- * Backend that writes to stderr without any color commands
- */
-class ConsoleBackend : public Backend {
-public:
-    const char* GetName() const override {
-        return "console";
-    }
-    void Write(const Entry& entry) override;
-};
-
-/**
- * Backend that writes to stderr and with color
- */
-class ColorConsoleBackend : public Backend {
-public:
-    const char* GetName() const override {
-        return "color_console";
-    }
-    void Write(const Entry& entry) override;
-};
-
-/**
- * Backend that writes to a file passed into the constructor. If a log level is error or higher, it
- * will flush immediately after writing
- */
-class FileBackend : public Backend {
-public:
-    explicit FileBackend(const std::string& filename);
-
-    const char* GetName() const override {
-        return "file";
-    }
-
-    void Write(const Entry& entry) override;
-
-private:
-    FileUtil::IOFile file;
-};
-
-void AddBackend(std::unique_ptr<Backend> backend);
-
-void RemoveBackend(const std::string& backend_name);
-
-boost::optional<Backend*> GetBackend(const std::string& backend_name);
-
 /**
  * Returns the name of the passed log class as a C-string. Subclasses are separated by periods
  * instead of underscores as in the enumeration.
@@ -92,11 +25,5 @@ const char* GetLogClassName(Class log_class);
  */
 const char* GetLevelName(Level log_level);
 
-/**
- * The global filter will prevent any messages from even being processed if they are filtered. Each
- * backend can have a filter, but if the level is lower than the global filter, the backend will
- * never get the message
- */
-void SetGlobalFilter(const Filter& filter);
-
+void SetFilter(Filter* filter);
 } // namespace Log