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