#include "Log.hh" // Vivy::LogLevel utility implementation namespace Vivy { const std::string_view LogLevel::toStdStringView(const LogLevel::Level lvl) noexcept { return LevelsStringViews[static_cast<Array::size_type>(lvl)]; } } // Vivy::LogSinkDispatcher and child classes implementation namespace Vivy { LogSinkDispatcher::~LogSinkDispatcher() noexcept {} const std::string_view StderrLogSinkDispatcher::trunkFileName(const char *fileName) noexcept { using size_type = std::string_view::size_type; static constexpr char basePrefix[] = "/src/"; static constexpr size_type basePrefixLen = (sizeof(basePrefix) / sizeof(char)) - 1; const std::string_view fileNameView{ fileName }; const size_type basePath = fileNameView.rfind(basePrefix); return std::string_view{ fileNameView.data() + basePath + basePrefixLen, fileNameView.data() + fileNameView.size() }; } std::string StderrLogSinkDispatcher::reduceFileName(const std::string_view fileName) noexcept { return std::filesystem::path(fileName).lexically_normal(); } StderrLogSinkDispatcher::StderrLogSinkDispatcher() noexcept : LogSinkDispatcher(std::string_view{ "stderr" }) { } void StderrLogSinkDispatcher::handleLogMessage(const std::string_view category, const LogMessage &msg) noexcept { std::cerr << "#(" << reduceFileName(trunkFileName(msg.getHeader().fileName)) << " +" << msg.getHeader().lineNumberInFile << " | " << LogLevel::toStdStringView(msg.getHeader().severity) << " -> " << category << ") " << msg.getTextBuffer() << '\n'; } } // Vivy::LogSink implementation namespace Vivy { // Flush all messages before exiting LogSink::~LogSink() noexcept { flush(); } // Get the log message from the logger and add it to the sink's queue. void LogSink::recieveLogMessage(const Logger *const logger, LogMessage &&msg) noexcept { const std::lock_guard<std::mutex> messageQueueLockGuard(messageQueueLock); messageQueue.emplace_back(std::make_tuple<const std::string_view, LogMessage>( logger->getCategoryView(), std::move(msg.sink()))); } // 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::shared_ptr<LogSinkDispatcher> dispatcher : logDispatchers) { for (const auto &[category, msg] : messageQueue) dispatcher->handleLogMessage(category, msg); } messageQueue.clear(); } std::shared_ptr<LogSink> LogSink::newSink() noexcept { struct makeSharedEnabler : public LogSink { // NOTE: For make_shared with private CTor }; return std::make_shared<makeSharedEnabler>(); } void LogSink::registerLogDispatcher(std::shared_ptr<LogSinkDispatcher> dispatcher) noexcept { logDispatchers.push_back(dispatcher); } void LogSink::registerLogger(std::shared_ptr<Logger> ptr) noexcept { loggers.push_back(ptr); } } // Vivy::Logger implementation namespace Vivy { void Logger::sendLogMessage(LogMessage &&msg) const noexcept { parentLogSink->recieveLogMessage(this, std::move(msg)); } LogMessage Logger::logEvent(const char *fileName, const char *functionName, const int lineNumber, const LogLevel::Level logSeverity) noexcept { return LogMessage(this, LogMessage::Header{ .fileName = fileName, .functionName = functionName, .severity = logSeverity, .lineNumberInFile = lineNumber }); } } // Vivy::LogMessage implementation namespace Vivy { LogMessage::LogMessage(const Logger *const logger, const LogMessage::Header hdr) noexcept : messageHeader(hdr) , parentLogger(logger) { } LogMessage::LogMessage(LogMessage &&other) noexcept : messageHeader(other.messageHeader) , parentLogger(other.parentLogger) { std::memcpy(this, &other, sizeof(LogMessage)); std::memset(&other, 0, sizeof(LogMessage)); } LogMessage && LogMessage::sink() noexcept { parentLogger = nullptr; return std::move(*this); } LogMessage & LogMessage::operator<<(const std::string &msg) noexcept { return (*this << msg.c_str()); } LogMessage & LogMessage::operator<<(const QVariant &variant) noexcept { return (*this << variant.toString()); } LogMessage & LogMessage::operator<<(const QString &msg) noexcept { return (*this << msg.toStdString()); } LogMessage & LogMessage::operator<<(const QFileInfo &fileInfo) noexcept { return (*this << "QFileInfo{ " << fileInfo.absoluteFilePath() << "}"); } LogMessage & LogMessage::operator<<(const double *ptr) noexcept { return (*this << "Pointer{ double, " << pointerToString<double>(ptr) << " }"); } LogMessage & LogMessage::operator<<(const unsigned char c) noexcept { return (*this << static_cast<char>(c)); } LogMessage & LogMessage::operator<<(const unsigned int i) noexcept { return (*this << static_cast<unsigned long>(i)); } LogMessage & LogMessage::operator<<(const unsigned long i) noexcept { return (*this << std::to_string(i)); } LogMessage & LogMessage::operator<<(const unsigned long long i) noexcept { return (*this << std::to_string(i)); } LogMessage & LogMessage::operator<<(const long long i) noexcept { return (*this << std::to_string(i)); } LogMessage & LogMessage::operator<<(const int i) noexcept { return (*this << static_cast<long>(i)); } LogMessage & LogMessage::operator<<(const long i) noexcept { return (*this << std::to_string(i)); } LogMessage & LogMessage::operator<<(const std::string_view strv) noexcept { for (std::size_t i = 0; (i < strv.size()) && (indexInArray < messageBufferLength - 1); ++i, ++indexInArray) { textBuffer[indexInArray] = strv[i]; } textBuffer[indexInArray] = '\0'; return *this; } LogMessage & LogMessage::operator<<(const char *str) noexcept { const std::size_t length = strlen(str); for (std::size_t i = 0; (i < length) && (indexInArray < messageBufferLength - 1); ++i, ++indexInArray) { textBuffer[indexInArray] = str[i]; } textBuffer[indexInArray] = '\0'; return *this; } LogMessage & LogMessage::operator<<(const char c) noexcept { if (indexInArray < messageBufferLength - 1) { textBuffer[indexInArray] = c; ++indexInArray; textBuffer[indexInArray] = '\0'; } return *this; } const std::string_view LogMessage::getTextBuffer() const noexcept { const char *txt = textBuffer.data(); return std::string_view{ txt, strlen(txt) }; } LogMessage::~LogMessage() noexcept { if (parentLogger) parentLogger->sendLogMessage(std::move(*this)); } }