diff --git a/src/Lib/Log.cc b/src/Lib/Log.cc
index 795786f859ee678c56d23415ca36a3f2e36eaa6c..716b966abcbfd8fc5414f771aab92894fddcb6b0 100644
--- a/src/Lib/Log.cc
+++ b/src/Lib/Log.cc
@@ -2,10 +2,17 @@
 
 using namespace Vivy;
 
+LogSinkDispatcher::~LogSinkDispatcher() noexcept {}
 LogSink::~LogSink() noexcept {}
 Logger::~Logger() noexcept { parentLogSink->closeLoggerClients(); }
 LogMessage::~LogMessage() noexcept { parentLogger->sendLogMessage(*this); }
 
+const std::string_view
+LogSinkDispatcher::getDispatcherName() const noexcept
+{
+    return dispatcherName;
+}
+
 const chrono::milliseconds
 LogMessage::getTimeStamp() const noexcept
 {
@@ -46,6 +53,21 @@ LogSink::recieveLogMessage(const Logger *const logger, LogMessage const &msg) no
         std::tuple<const std::string_view, const LogMessage>{ logger->getCategoryView(), msg });
 }
 
+// Flush all LogMessages to all of the LogSinkDispatchers. Also clear the message queue.
+void
+LogSink::flush() noexcept
+{
+    const std::lock_guard<std::mutex> messageQueueLockGuard(messageQueueLock);
+    for (std::weak_ptr<LogSinkDispatcher> weakDispatcher : logDispatchers) {
+        std::shared_ptr<LogSinkDispatcher> dispatcher = weakDispatcher.lock();
+        if (dispatcher) {
+            for (const auto &[category, msg] : messageQueue)
+                dispatcher->handleLogMessage(category, msg);
+        }
+    }
+    messageQueue.clear();
+}
+
 std::shared_ptr<LogSink>
 LogSink::newSink() noexcept
 {
@@ -61,6 +83,12 @@ LogSink::registerLoggerClient(std::shared_ptr<Logger> logger) noexcept
     clientLoggers.push_back(logger);
 }
 
+void
+LogSink::registerLogDispatcher(std::shared_ptr<LogSinkDispatcher> dispatcher) noexcept
+{
+    logDispatchers.push_back(dispatcher);
+}
+
 void
 LogSink::closeLoggerClients() noexcept
 {
@@ -72,18 +100,18 @@ LogSink::closeLoggerClients() noexcept
 std::shared_ptr<Logger>
 LogSink::getLoggerClient(const std::string_view category) const
 {
-    auto end = std::end(clientLoggers);
-    auto it  = std::find_if(std::begin(clientLoggers), end,
-                           [category](const std::weak_ptr<Logger> &weakPtr) {
-                               std::shared_ptr<Logger> sharedPtr = weakPtr.lock();
-                               return sharedPtr && (sharedPtr->getCategoryView() == category);
-                           });
-    if (it == end)
-        throw std::logic_error("Logger was not found");
-    std::shared_ptr<Logger> sharedPtr = (*it).lock();
-    if (sharedPtr == nullptr)
-        throw std::logic_error("Logger has expired");
-    return sharedPtr;
+    return getSharedPtr(clientLoggers, [category](std::shared_ptr<Logger> ptr) noexcept -> bool {
+        return ptr->getCategoryView() == category;
+    });
+}
+
+std::shared_ptr<LogSinkDispatcher>
+LogSink::getLogDispatcher(const std::string_view name) const
+{
+    return getSharedPtr(logDispatchers,
+                        [name](std::shared_ptr<LogSinkDispatcher> ptr) noexcept -> bool {
+                            return ptr->getDispatcherName() == name;
+                        });
 }
 
 LogMessage
diff --git a/src/Lib/Log.hh b/src/Lib/Log.hh
index 64cd059d81b8c611004ba6ebafc93d2616eead8b..31eeea320fc75d0aa684aae8afc5ad06bcb392ff 100644
--- a/src/Lib/Log.hh
+++ b/src/Lib/Log.hh
@@ -49,6 +49,21 @@ class LogSink {
 
     explicit LogSink() noexcept = default;
 
+    template <typename T>
+    std::shared_ptr<T> getSharedPtr(std::vector<std::weak_ptr<T>> weakPtrList, auto nameCheck) const
+    {
+        std::shared_ptr<T> sharedPtr(nullptr);
+        auto end = std::end(weakPtrList);
+        auto it  = std::find_if(std::begin(weakPtrList), end,
+                               [&nameCheck, &sharedPtr](const std::weak_ptr<T> &weakPtr) {
+                                   sharedPtr = weakPtr.lock();
+                                   return sharedPtr && nameCheck(sharedPtr);
+                               });
+        if (it == end || sharedPtr == nullptr)
+            throw std::logic_error("Pointer was not found or has expired");
+        return sharedPtr;
+    }
+
 public:
     static std::shared_ptr<LogSink> newSink() noexcept;
     ~LogSink() noexcept;
@@ -56,13 +71,18 @@ public:
     void recieveLogMessage(const Logger *const, LogMessage const &) noexcept;
 
     void registerLoggerClient(std::shared_ptr<Logger>) noexcept;
+    void registerLogDispatcher(std::shared_ptr<LogSinkDispatcher>) noexcept;
     void closeLoggerClients() noexcept;
     std::shared_ptr<Logger> getLoggerClient(const std::string_view category) const;
+    std::shared_ptr<LogSinkDispatcher> getLogDispatcher(const std::string_view category) const;
+
+    void flush() noexcept;
 
 private:
     std::mutex messageQueueLock{};
     std::vector<std::tuple<const std::string_view, const LogMessage>> messageQueue;
     std::vector<std::weak_ptr<Logger>> clientLoggers;
+    std::vector<std::weak_ptr<LogSinkDispatcher>> logDispatchers;
 };
 
 // Message to be logged, constructed by a logger then send
@@ -103,11 +123,32 @@ private:
     Logger *const parentLogger;
 };
 
+// A LogSinkDispatcher will excavate LogMessages from the Sink and do something
+// with them (save to a file, display in stderr/Vivy console, etc).
+class LogSinkDispatcher : public std::enable_shared_from_this<LogSinkDispatcher> {
+    VIVY_UNMOVABLE_OBJECT(LogSinkDispatcher)
+
+    const std::string dispatcherName;
+
+protected:
+    explicit LogSinkDispatcher(LogSink *const sink, const std::string_view name) noexcept
+        : dispatcherName(name)
+    {
+        std::shared_ptr<LogSinkDispatcher> self = shared_from_this();
+        sink->registerLogDispatcher(self);
+    }
+
+public:
+    const std::string_view getDispatcherName() const noexcept;
+    virtual ~LogSinkDispatcher() noexcept;
+
+    virtual void handleLogMessage(const std::string_view, const LogMessage &) noexcept = 0;
+};
+
 // A logger class, a client to LogSink. Will generate and send instances of
 // LogMessage.
-class Logger : public std::enable_shared_from_this<Logger> {
+class Logger final : public std::enable_shared_from_this<Logger> {
     VIVY_UNMOVABLE_OBJECT(Logger)
-    friend class LogSink;
 
     LogSink *const parentLogSink;
     const std::string logCategory;