diff --git a/CMakeLists.txt b/CMakeLists.txt index edb6c4f9740ebf5f5958aaf16fd6b525f8bfea6c..498df5aac7578066f286834fe18671f9a7e3ecdf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,11 +90,18 @@ target_compile_features(Vivy PRIVATE # More options and warnings target_compile_options(Vivy PRIVATE - -Wall -Wextra -Wshadow -pedantic - -Wcast-align -Wconversion -Wsign-conversion -Wunused-variable - -Wmisleading-indentation -Wnull-dereference -Wdouble-promotion + -Wall -Wextra -Wpedantic + -Wshadow + -Wcast-align + -Wconversion + -Wsign-conversion + -Wunused-variable + -Wmisleading-indentation + -Wnull-dereference + -Wdouble-promotion -Wformat=2 - -Woverloaded-virtual -Wnon-virtual-dtor + -Woverloaded-virtual + -Wnon-virtual-dtor -Wignored-qualifiers -fopenmp @@ -117,8 +124,11 @@ if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") # Disable some things because we want C++20 and don't control some Qt # generated files... - -Wno-c++98-compat -Wno-c++98-c++11-c++14-c++17-compat-pedantic + -Wno-c++98-compat -Wno-c++98-compat-pedantic + -Wno-c++98-c++11-c++14-c++17-compat-pedantic + -Wno-c++20-compat + -Wno-extra-semi-stmt -Wno-redundant-parens -Wno-padded diff --git a/src/Lib/Log.cc b/src/Lib/Log.cc index d23cabcc6e1e92fcb0ed3a30b254034a91cf8b6c..f26e77b3b7a89aa6221d0abebccfd3ab5200d7ff 100644 --- a/src/Lib/Log.cc +++ b/src/Lib/Log.cc @@ -1,10 +1,34 @@ #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() }; +} + StderrLogSinkDispatcher::StderrLogSinkDispatcher() noexcept : LogSinkDispatcher(std::string_view{ "stderr" }) { @@ -14,7 +38,10 @@ void StderrLogSinkDispatcher::handleLogMessage(const std::string_view category, const LogMessage &msg) noexcept { - std::cerr << '[' << category << "] " << msg.getTextBuffer() << '\n'; + std::cerr << "#(" << trunkFileName(msg.getHeader().fileName) << " +" + << msg.getHeader().lineNumberInFile << " | " + << LogLevel::toStdStringView(msg.getHeader().severity) << " -> " << category << ")\t" + << msg.getTextBuffer() << '\n'; } } @@ -59,6 +86,12 @@ LogSink::registerLogDispatcher(std::shared_ptr<LogSinkDispatcher> dispatcher) no { logDispatchers.push_back(dispatcher); } + +void +LogSink::registerLogger(std::shared_ptr<Logger> ptr) noexcept +{ + loggers.push_back(ptr); +} } // Vivy::Logger implementation @@ -72,7 +105,7 @@ Logger::sendLogMessage(LogMessage &&msg) const noexcept LogMessage Logger::logEvent(const char *fileName, const char *functionName, const int lineNumber, - const LogLevel logSeverity) noexcept + const LogLevel::Level logSeverity) noexcept { return LogMessage(this, LogMessage::Header{ .fileName = fileName, .functionName = functionName, @@ -129,6 +162,17 @@ 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 { diff --git a/src/Lib/Log.hh b/src/Lib/Log.hh index 5b49381f4c705f2572730d535abe130544468b48..042df469398b7940ee8524bcf23449d98b7fc1f1 100644 --- a/src/Lib/Log.hh +++ b/src/Lib/Log.hh @@ -3,10 +3,10 @@ #include "Utils.hh" // Create a logger with a category -#define VIVY_GET_LOGGER(sink, cat) std::make_shared<Logger>((sink).get(), std::string_view{ #cat }) +#define VIVY_GET_LOGGER(sink, cat) (sink)->newLogger(std::string_view{ #cat }) // Log something in a logger -#define VIVY_LOG_WITH_LEVEL(log, level) log->logEvent(__FILE__, __func__, __LINE__, level) +#define VIVY_LOG_WITH_LEVEL(log, level) (log)->logEvent(__FILE__, __func__, __LINE__, level) #define VIVY_LOG_WARN(log) VIVY_LOG_WITH_LEVEL(log, Vivy::LogLevel::Warning) #define VIVY_LOG_DEBUG(log) VIVY_LOG_WITH_LEVEL(log, Vivy::LogLevel::Debug) #define VIVY_LOG_INFO(log) VIVY_LOG_WITH_LEVEL(log, Vivy::LogLevel::Info) @@ -17,15 +17,15 @@ // intended to be used in an object to not polluate namespaces. #define VIVY_DCL_LOG_SINK(sink) \ std::shared_ptr<LogSink> sink{ LogSink::newSink() }; \ - void flushLogSink() const noexcept { sink->flush(); } + void flushLogSink() const noexcept { (sink)->flush(); } // Declare a dispatch for a sink with no arguments in the constructor. #define VIVY_DCL_LOG_DISPATCH(sink, name, dispatch) \ - std::shared_ptr<dispatch> name{ sink->newDispatcher<dispatch>() }; + std::shared_ptr<dispatch> name{ (sink)->newDispatcher<dispatch>() }; // Declare a dispatch for a sink with arguments in the constructor. #define VIVY_DCL_LOG_DISPATCH_WITH(sink, name, dispatch, ...) \ - std::shared_ptr<dispatch> name{ sink->newDispatcher<dispatch>(__VA_ARGS__) }; + std::shared_ptr<dispatch> name{ (sink)->newDispatcher<dispatch>(__VA_ARGS__) }; // Install logger for the object. #define VIVY_LOGGABLE_OBJECT(sink, name, logger) \ @@ -36,8 +36,10 @@ LogMessage logInfo() const noexcept { return VIVY_LOG_INFO(logger); } \ LogMessage logDebug() const noexcept { return VIVY_LOG_DEBUG(logger); } -#define VIVY_LOG_CTOR(classname) logDebug() << #classname "::CTOR " -#define VIVY_LOG_DTOR(classname) logDebug() << #classname "::DTOR " +#define VIVY_LOG_CTOR() logDebug() << "<<CTOR>> " +#define VIVY_LOG_DTOR() logDebug() << "<<DTOR>> " + +#define VIVY_LOG_QUOTED(something) '\'' << (something) << '\'' // Install logger, use the global LogSink #define VIVY_APP_LOGGABLE_OBJECT(name, logger) \ @@ -52,13 +54,25 @@ 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, - None = std::numeric_limits<int>::min() // In option setup to disable logs +struct LogLevel final { + enum Level : int { + None, // In option setup to disable logs + Debug, + Info, + Warning, + Error, + Critical, // Will trigger qFatal + ___MaxAndUnused + }; + + static const std::string_view toStdStringView(const LogLevel::Level) noexcept; + +private: + using Array = std::array<const std::string_view, ___MaxAndUnused>; + static inline constexpr Array LevelsStringViews = { "None", "Debug", "Info", + "Warnin", "Error", "Critical" }; + + LogLevel() {} }; // A LogSinkDispatcher will excavate LogMessages from the Sink and do something @@ -85,6 +99,8 @@ public: class StderrLogSinkDispatcher : public LogSinkDispatcher { VIVY_UNMOVABLE_OBJECT(StderrLogSinkDispatcher) + static const std::string_view trunkFileName(const char *) noexcept; + public: explicit StderrLogSinkDispatcher() noexcept; void handleLogMessage(const std::string_view, const LogMessage &) noexcept override; @@ -103,6 +119,7 @@ public: ~LogSink() noexcept; void registerLogDispatcher(std::shared_ptr<LogSinkDispatcher>) noexcept; + void registerLogger(std::shared_ptr<Logger>) noexcept; void recieveLogMessage(const Logger *const, LogMessage &&) noexcept; void flush() noexcept; @@ -115,10 +132,18 @@ public: return dispatch; } + std::shared_ptr<Logger> newLogger(const StringType auto &category) noexcept + { + std::shared_ptr<Logger> logger = std::make_shared<Logger>(this, category); + registerLogger(logger); + return logger; + } + private: std::mutex messageQueueLock{}; std::vector<std::tuple<const std::string_view, LogMessage>> messageQueue; std::vector<std::shared_ptr<LogSinkDispatcher>> logDispatchers; + std::vector<std::shared_ptr<Logger>> loggers; }; // Message to be logged, constructed by a logger then send @@ -130,7 +155,7 @@ public: struct Header final { const char *fileName; const char *functionName; - const LogLevel severity; + const LogLevel::Level severity; const int lineNumberInFile; }; @@ -159,6 +184,7 @@ public: LogMessage &&sink() noexcept; LogMessage &operator<<(const std::string &) noexcept; + LogMessage &operator<<(const std::string_view) noexcept; LogMessage &operator<<(const QString &) noexcept; LogMessage &operator<<(const char *) noexcept; LogMessage &operator<<(const char) noexcept; @@ -187,6 +213,6 @@ public: void sendLogMessage(LogMessage &&) const noexcept; LogMessage logEvent(const char *fileName, const char *functionName, const int lineNumber, - const LogLevel) noexcept; + const LogLevel::Level) noexcept; }; }