From cfabbfa26432c168a9bfb40798c9abdb0e7d3982 Mon Sep 17 00:00:00 2001 From: Stephane Janel Date: Sun, 22 Oct 2023 23:37:37 +0200 Subject: [PATCH] Improve compile time string view logging levels --- src/engine/include/coincenteroptionsdef.hpp | 30 +++++--- .../src/prometheusmetricgateway.cpp | 2 +- src/objects/CMakeLists.txt | 10 +++ src/objects/include/logginginfo.hpp | 3 + .../include/parseloglevel.hpp | 0 src/objects/src/parseloglevel.cpp | 35 +++++++++ .../test/parseloglevel_test.cpp | 0 src/tech/CMakeLists.txt | 8 --- .../include/static_string_view_helpers.hpp | 72 ++++++++++++++++--- src/tech/src/parseloglevel.cpp | 42 ----------- .../test/static_string_view_helpers_test.cpp | 17 ++++- 11 files changed, 148 insertions(+), 71 deletions(-) rename src/{tech => objects}/include/parseloglevel.hpp (100%) create mode 100644 src/objects/src/parseloglevel.cpp rename src/{tech => objects}/test/parseloglevel_test.cpp (100%) delete mode 100644 src/tech/src/parseloglevel.cpp diff --git a/src/engine/include/coincenteroptionsdef.hpp b/src/engine/include/coincenteroptionsdef.hpp index f41d75a3..44a79587 100644 --- a/src/engine/include/coincenteroptionsdef.hpp +++ b/src/engine/include/coincenteroptionsdef.hpp @@ -11,6 +11,7 @@ #include "exchangeconfigmap.hpp" #include "exchangepublicapi.hpp" #include "loadconfiguration.hpp" +#include "logginginfo.hpp" #include "static_string_view_helpers.hpp" #include "staticcommandlineoptioncheck.hpp" #include "timedef.hpp" @@ -29,6 +30,22 @@ class CoincenterCmdLineOptionsDefinitions { std::chrono::duration_cast(kDefaultRepeatTime).count(); protected: + static constexpr std::string_view kLogValue1 = ", CharToStringView_v<'>'>>; + + static constexpr std::string_view kLoggingLevelsSep = "|"; + static constexpr std::string_view kLoggingLevels = + make_joined_string_view::value; + + static constexpr std::string_view kLog1 = + "Sets the log level in the console during all execution. " + "Possible values are: ("; + static constexpr std::string_view kLog2 = ") or (0-"; + static constexpr std::string_view kLog3 = ") (overrides .log.console in general config file)"; + static constexpr std::string_view kLog = + JoinStringView_v, kLog3>; + static constexpr std::string_view kOutput1 = "Output format. One of ("; static constexpr std::string_view kOutput2 = ") (default configured in general config file)"; static constexpr std::string_view kOutput = @@ -145,18 +162,11 @@ struct CoincenterAllowedOptions : private CoincenterCmdLineOptionsDefinitions { static constexpr CommandLineOptionWithValue value[] = { {{{"General", 100}, "help", 'h', "", "Display this information"}, &OptValueType::help}, {{{"General", 200}, "--data", "", kData}, &OptValueType::dataDir}, - {{{"General", 300}, - "--log", - 'v', - "", - "Sets the log level in the console during all execution. " - "Possible values are: (off|critical|error|warning|info|debug|trace) or " - "(0-6) (overrides .log.console in general config file)"}, - &OptValueType::logConsole}, - {{{"General", 400}, "--log-console", "", "Synonym of --log"}, &OptValueType::logConsole}, + {{{"General", 300}, "--log", 'v', kLogValue, kLog}, &OptValueType::logConsole}, + {{{"General", 400}, "--log-console", kLogValue, "Synonym of --log"}, &OptValueType::logConsole}, {{{"General", 400}, "--log-file", - "", + kLogValue, "Sets the log level in files during all execution (overrides .log.file in general config file). " "Number of rotating files to keep and their size is configurable in the general config file"}, &OptValueType::logFile}, diff --git a/src/monitoring/src/prometheusmetricgateway.cpp b/src/monitoring/src/prometheusmetricgateway.cpp index ffe7a565..acd68f77 100644 --- a/src/monitoring/src/prometheusmetricgateway.cpp +++ b/src/monitoring/src/prometheusmetricgateway.cpp @@ -52,7 +52,7 @@ PrometheusMetricGateway::~PrometheusMetricGateway() { // We should not throw in a destructor - catch any exception and do nothing, not even a log (it could throw) try { flush(); - } catch (const std::exception&) { + } catch (const std::exception&) { // NOLINT(bugprone-empty-catch) } } diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index ca2b377f..e9886873 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -84,6 +84,16 @@ add_unit_test( CCT_DISABLE_SPDLOG ) +add_unit_test( + parseloglevel_test + src/parseloglevel.cpp + test/parseloglevel_test.cpp + LIBRARIES + coincenter_tech + DEFINITIONS + CCT_DISABLE_SPDLOG +) + add_unit_test( wallet_test test/wallet_test.cpp diff --git a/src/objects/include/logginginfo.hpp b/src/objects/include/logginginfo.hpp index f5777160..a58678fb 100644 --- a/src/objects/include/logginginfo.hpp +++ b/src/objects/include/logginginfo.hpp @@ -21,6 +21,9 @@ class LoggingInfo { static constexpr char const *const kOutputLoggerName = "output"; static constexpr std::string_view kJsonFieldConsoleLevelName = "consoleLevel"; static constexpr std::string_view kJsonFieldFileLevelName = "fileLevel"; + static constexpr std::string_view kLogLevelNames[] = {"off", "critical", "error", "warning", + "info", "debug", "trace"}; + static constexpr auto kNbLogLevels = std::size(kLogLevelNames); enum class WithLoggersCreation : int8_t { kNo, kYes }; diff --git a/src/tech/include/parseloglevel.hpp b/src/objects/include/parseloglevel.hpp similarity index 100% rename from src/tech/include/parseloglevel.hpp rename to src/objects/include/parseloglevel.hpp diff --git a/src/objects/src/parseloglevel.cpp b/src/objects/src/parseloglevel.cpp new file mode 100644 index 00000000..df224de6 --- /dev/null +++ b/src/objects/src/parseloglevel.cpp @@ -0,0 +1,35 @@ +#include "parseloglevel.hpp" + +#include +#include +#include + +#include "cct_exception.hpp" +#include "logginginfo.hpp" +#include "static_string_view_helpers.hpp" + +namespace cct { +int8_t LogPosFromLogStr(std::string_view logStr) { + if (logStr.size() == 1) { + const int8_t logLevelPos = logStr.front() - '0'; + if (logLevelPos < 0 || + logLevelPos >= static_cast>(LoggingInfo::kNbLogLevels)) { + throw exception("Unrecognized log level {}. Possible values are [0-{}]", logStr, + '0' + (LoggingInfo::kNbLogLevels - 1U)); + } + return logLevelPos; + } + + int8_t logLevel = 0; + for (const auto logName : LoggingInfo::kLogLevelNames) { + if (logStr == logName) { + return logLevel; + } + ++logLevel; + } + + static constexpr std::string_view kPrintLogNameSep = "|"; + throw exception("Unrecognized log level name {}. Possible values are {}", logStr, + make_joined_string_view::value); +} +} // namespace cct \ No newline at end of file diff --git a/src/tech/test/parseloglevel_test.cpp b/src/objects/test/parseloglevel_test.cpp similarity index 100% rename from src/tech/test/parseloglevel_test.cpp rename to src/objects/test/parseloglevel_test.cpp diff --git a/src/tech/CMakeLists.txt b/src/tech/CMakeLists.txt index e09e20df..25be8469 100644 --- a/src/tech/CMakeLists.txt +++ b/src/tech/CMakeLists.txt @@ -90,14 +90,6 @@ add_unit_test( test/mathhelpers_test.cpp ) -add_unit_test( - parseloglevel_test - src/parseloglevel.cpp - test/parseloglevel_test.cpp - DEFINITIONS - CCT_DISABLE_SPDLOG -) - add_unit_test( simpletable_test src/simpletable.cpp diff --git a/src/tech/include/static_string_view_helpers.hpp b/src/tech/include/static_string_view_helpers.hpp index 3e440681..f5df4d4d 100644 --- a/src/tech/include/static_string_view_helpers.hpp +++ b/src/tech/include/static_string_view_helpers.hpp @@ -10,7 +10,7 @@ namespace cct { /// Concatenates variadic template std::string_view arguments at compile time and defines a std::string_view pointing on -/// a static storage. The storage is guaranteed to be null terminated. +/// a static storage. The storage is guaranteed to be null terminated (but not itself included in the returned value) /// Adapted from /// https://stackoverflow.com/questions/38955940/how-to-concatenate-static-strings-at-compile-time/62823211#62823211 template @@ -18,15 +18,13 @@ class JoinStringView { private: // Join all strings into a single std::array of chars static constexpr auto impl() noexcept { - constexpr std::size_t len = (Strs.size() + ... + 0); - std::array arr{}; + constexpr std::string_view::size_type len = (Strs.size() + ... + 0); + std::array arr; // +1 for null terminated char if constexpr (len > 0) { - auto append = [i = 0, &arr](auto const& s) mutable { - for (auto c : s) arr[i++] = c; - }; + auto append = [it = arr.begin()](auto const& s) mutable { it = std::copy(s.begin(), s.end(), it); }; (append(Strs), ...); } - arr[len] = 0; + arr.back() = '\0'; return arr; } // Give the joined string static storage @@ -35,12 +33,68 @@ class JoinStringView { public: // View as a std::string_view static constexpr std::string_view value{arr.data(), arr.size() - 1}; + + // c_str version (null-terminated) + static constexpr const char* const c_str = arr.data(); }; // Helper to get the value out template static constexpr auto JoinStringView_v = JoinStringView::value; +/// Same as JoinStringView but with a char separator between each string_view +template +class JoinStringViewWithSep { + private: + // Join all strings into a single std::array of chars + static constexpr auto impl() noexcept { + constexpr std::string_view::size_type len = (Strs.size() + ... + 0); + constexpr auto nbSv = sizeof...(Strs); + std::array(1))> + arr; + if constexpr (len > 0) { + auto append = [it = arr.begin(), &arr](auto const& s) mutable { + if (it != arr.begin()) { + it = std::copy(Sep.begin(), Sep.end(), it); + } + it = std::copy(s.begin(), s.end(), it); + }; + (append(Strs), ...); + } + arr.back() = '\0'; + return arr; + } + // Give the joined string static storage + static constexpr auto arr = impl(); + + public: + // View as a std::string_view + static constexpr std::string_view value{arr.data(), arr.size() - 1}; + + // c_str version (null-terminated) + static constexpr const char* const c_str = arr.data(); +}; + +// Helper to get the value out +template +static constexpr auto JoinStringViewWithSep_v = JoinStringViewWithSep::value; + +namespace details { +template +struct make_joined_string_view_impl; + +template +struct make_joined_string_view_impl> { + static constexpr auto value = JoinStringViewWithSep::value; +}; + +} // namespace details + +// make joined string view from array like value +template +using make_joined_string_view = details::make_joined_string_view_impl>; + /// Converts an integer value to its string_view representation at compile time. /// The underlying storage is not null terminated. template @@ -79,10 +133,10 @@ static constexpr auto IntToStringView_v = IntToStringView::value; template class CharToStringView { private: - static constexpr char c = Char; + static constexpr char ch = Char; public: - static constexpr std::string_view value{&c, 1}; + static constexpr std::string_view value{&ch, 1}; }; template diff --git a/src/tech/src/parseloglevel.cpp b/src/tech/src/parseloglevel.cpp deleted file mode 100644 index ed82bc80..00000000 --- a/src/tech/src/parseloglevel.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "parseloglevel.hpp" - -#include -#include - -#include "cct_exception.hpp" - -namespace cct { -int8_t LogPosFromLogStr(std::string_view logStr) { - if (logStr.size() == 1) { - static constexpr int8_t kMaxLogLevel = 6; - int8_t logLevelPos = logStr.front() - '0'; - if (logLevelPos < 0 || logLevelPos > kMaxLogLevel) { - throw exception("Unrecognized log level {}. Possible values are 0-{}", logStr, '0' + kMaxLogLevel); - } - return logLevelPos; - } - if (logStr == "off") { - return 0; - } - if (logStr == "critical") { - return 1; - } - if (logStr == "error") { - return 2; - } - if (logStr == "warning") { - return 3; - } - if (logStr == "info") { - return 4; - } - if (logStr == "debug") { - return 5; - } - if (logStr == "trace") { - return 6; - } - throw exception("Unrecognized log level name {}. Possible values are off|critical|error|warning|info|debug|trace", - logStr); -} -} // namespace cct \ No newline at end of file diff --git a/src/tech/test/static_string_view_helpers_test.cpp b/src/tech/test/static_string_view_helpers_test.cpp index 778386d7..1d236fd3 100644 --- a/src/tech/test/static_string_view_helpers_test.cpp +++ b/src/tech/test/static_string_view_helpers_test.cpp @@ -3,7 +3,6 @@ #include namespace cct { -// JoinStringView namespace test1 { static_assert(JoinStringView_v<>.empty()); } // namespace test1 @@ -40,9 +39,25 @@ static constexpr std::string_view kStr4 = "in my bag"; static_assert(JoinStringView_v, kStr2, IntToStringView_v<1894>, kStr3, kStr4> == "I have 70 oranges and 1894 bananas in my bag"); } // namespace test5 +namespace test6 { +static constexpr std::string_view kSep = "|"; +static constexpr std::string_view kStr1 = "apples"; +static constexpr std::string_view kStr2 = "bananas"; +static constexpr std::string_view kStr3 = "oranges"; +static constexpr std::string_view kStr4 = "blueberries"; +static constexpr std::string_view kStr5 = "strawberries"; + +static_assert(JoinStringViewWithSep_v == + "apples|bananas|oranges|blueberries|strawberries"); + +static constexpr std::string_view kStrArr[] = {"apples", "bananas", "oranges", "blueberries", "strawberries"}; + +static_assert(make_joined_string_view::value == "apples|bananas|oranges|blueberries|strawberries"); +} // namespace test6 // IntToStringView static_assert(IntToStringView_v<0> == "0"); static_assert(IntToStringView_v<37> == "37"); static_assert(IntToStringView_v<-1273006> == "-1273006"); + } // namespace cct \ No newline at end of file