From 57576d3972772d3a5ae24080bf54ea4a033de8bf Mon Sep 17 00:00:00 2001
From: Kubat <mael.martin31@gmail.com>
Date: Tue, 31 Aug 2021 11:36:14 +0200
Subject: [PATCH] LOG: Add logger to Vivy

This is a slightly more complicated logger than what is done for lektor.
The process to create a logger is the following:

{ // In scope
std::shared_ptr<LogSink> mySink   = VIVY_NEW_LOG_SINK();
std::shared_ptr<Logger> appLog    = VIVY_GET_LOGGER(mySink, "APPLICATION");
std::shared_ptr<Logger> scriptLog = VIVY_GET_LOGGER(mySink, "SCRIPT");
// ...
VIVY_LOG_WARN(appLog) << "This is" << ' ' << "very usefull!";
// ...
} // Will clear all loggers
---
 src/Lib/Log.cc |  50 ++++++++++++++++++++
 src/Lib/Log.hh | 123 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 173 insertions(+)
 create mode 100644 src/Lib/Log.cc
 create mode 100644 src/Lib/Log.hh

diff --git a/src/Lib/Log.cc b/src/Lib/Log.cc
new file mode 100644
index 00000000..ae989823
--- /dev/null
+++ b/src/Lib/Log.cc
@@ -0,0 +1,50 @@
+#include "Log.hh"
+
+using namespace Vivy;
+
+LogMessage::~LogMessage() noexcept { parentLogger->sendLogMessage(*this); }
+Logger::~Logger() noexcept { parentLogSink->closeLoggerClient(this); }
+
+void
+LogSink::closeLoggerClient(Logger *const logger) noexcept
+{
+}
+
+LogMessage
+Logger::logEvent(const char *fileName, const char *functionName, const int lineNumber,
+                 const LogLevel logSeverity) noexcept
+{
+    return LogMessage(this, LogMessage::Header{ .fileName         = fileName,
+                                                .functionName     = functionName,
+                                                .severity         = logSeverity,
+                                                .lineNumberInFile = lineNumber });
+}
+
+void
+Logger::sendLogMessage(const LogMessage &msg) const noexcept
+{
+}
+
+LogMessage::LogMessage(Logger *const logger, const LogMessage::Header hdr) noexcept
+    : messageHeader(hdr)
+    , parentLogger(logger)
+{
+}
+
+LogMessage &
+LogMessage::operator<<(const std::string &) noexcept
+{
+    return *this;
+}
+
+LogMessage &
+LogMessage::operator<<(const int) noexcept
+{
+    return *this;
+}
+
+LogMessage &
+LogMessage::operator<<(const long) noexcept
+{
+    return *this;
+}
diff --git a/src/Lib/Log.hh b/src/Lib/Log.hh
new file mode 100644
index 00000000..d588c568
--- /dev/null
+++ b/src/Lib/Log.hh
@@ -0,0 +1,123 @@
+#pragma once
+
+#include "Utils.hh"
+#include <sstream>
+
+#define VIVY_NEW_LOG_SINK()             LogSink::newSink()
+#define VIVY_GET_LOGGER(sink, cat)      sink->createClient(cat)
+#define VIVY_LOG_WITH_LEVEL(log, level) log->logEvent(__FILE__, __function__, __LINE__, level)
+#define VIVY_LOG_WARN(log)              VIVY_LOG_WITH_LEVEL(log, LogLevel::Warning)
+#define VIVY_LOG_DEBUG(log)             VIVY_LOG_WITH_LEVEL(log, LogLevel::Debug)
+#define VIVY_LOG_INFO(log)              VIVY_LOG_WITH_LEVEL(log, LogLevel::Info)
+#define VIVY_LOG_ERR(log)               VIVY_LOG_WITH_LEVEL(log, LogLevel::Error)
+#define VIVY_LOG_FATAL(log)             VIVY_LOG_WITH_LEVEL(log, LogLevel::Critical)
+
+namespace Vivy
+{
+class VivyApplication;
+class VivyCli;
+class LogSink;
+class Logger;
+class LogMessage;
+
+// The severity of an event. Critical will cause the LogSink to flush all
+// messages to its emeters and then abort.
+enum class LogLevel : int {
+    Critical = std::numeric_limits<int>::max(), // Will trigger qFatal
+    Error    = 4,
+    Warning  = 3,
+    Info     = 2,
+    Debug    = 1
+};
+
+// Message to be logged, constructed by a logger then send
+class LogMessage final {
+    friend class Logger;
+
+    struct Header final {
+        const char *fileName;
+        const char *functionName;
+        const LogLevel severity;
+        const int lineNumberInFile;
+    };
+
+    LogMessage(Logger *const, const Header) noexcept;
+
+    const Header messageHeader;
+    const chrono::milliseconds timeStamp{ duration_cast<chrono::milliseconds>(
+        chrono::system_clock::now().time_since_epoch()) };
+
+    static constexpr inline size_t messageBufferLength = 2048;
+    std::array<char, messageBufferLength> textBuffer{};
+    std::size_t indexInArray{ 0 };
+    Logger *const parentLogger;
+
+public:
+    // The message will be send on destruction
+    ~LogMessage() noexcept;
+
+    LogMessage &operator<<(const std::string &) noexcept;
+    LogMessage &operator<<(const int) noexcept;
+    LogMessage &operator<<(const long) noexcept;
+};
+
+// A logger class, a client to LogSink. Will generate and send instances of
+// LogMessage.
+class Logger final {
+    VIVY_UNMOVABLE_OBJECT(Logger)
+    friend class LogSink;
+    friend class std::shared_ptr<Logger>;
+
+    LogSink *const parentLogSink;
+    const std::string logCategory;
+
+    static std::string anyStringToStdString(StringType auto const &str) noexcept
+    {
+        std::string ret;
+        const std::size_t size = str.size();
+        ret.resize(size);
+        for (std::size_t i = 0; i < size; ++i)
+            ret[i] = str[i];
+        return ret;
+    }
+
+    // Templated constructor, construct from anything that can be considered as
+    // a string.
+    Logger(LogSink *const sink, StringType auto const &category) noexcept
+        : parentLogSink(sink)
+        , logCategory(anyStringToStdString(category))
+    {
+    }
+
+public:
+    ~Logger() noexcept;
+
+    void sendLogMessage(const LogMessage &) const noexcept;
+    LogMessage logEvent(const char *fileName, const char *functionName, const int lineNumber,
+                        const LogLevel) noexcept;
+};
+
+// LogSink is the parent logger. It will recieve messages and display them to a
+// console or to std::cerr or to a file, etc.
+class LogSink final {
+    VIVY_UNMOVABLE_OBJECT(LogSink)
+
+    LogSink() noexcept;
+
+public:
+    static std::shared_ptr<LogSink> newSink() noexcept;
+    ~LogSink() noexcept;
+
+    void closeLoggerClient(Logger *const) noexcept;
+    std::shared_ptr<Logger> createClient(StringType auto const &category) noexcept
+    {
+        auto ret = std::make_shared<Logger>(this, category);
+        clientLoggers.push_back(ret);
+        return ret;
+    }
+
+private:
+    std::vector<LogMessage> messageQueue;
+    std::vector<std::shared_ptr<Logger>> clientLoggers;
+};
+}
-- 
GitLab