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;