From de3d7b63eab188a54ce5d2f635a610991bf563bc Mon Sep 17 00:00:00 2001 From: Teemu Ollakka Date: Thu, 8 Sep 2022 14:21:22 +0300 Subject: [PATCH] Add report_event() method into reporter object Report event will write json formatted event into report file. Include Boost headers as system headers to avoid generating excessive warnings. Enable extra tests for selected compilers in actions. --- .github/workflows/build.yml | 12 +++++- CMakeLists.txt | 14 +++---- include/wsrep/reporter.hpp | 22 ++++++++--- src/reporter.cpp | 74 +++++++++++++++++++++++++++++++++---- test/reporter_test.cpp | 37 ++++++++++++++----- 5 files changed, 128 insertions(+), 31 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7f317a7..f142389 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -159,19 +159,27 @@ jobs: else export CXX="ccache clang++-${{ matrix.config.version }}" fi - if [ ${{ matrix.config.version }} == "4.8" ] + if [ ${{ matrix.config.CC }} == "gcc" ] && [ ${{ matrix.config.version }} == "4.8" ] then STRICT=OFF DBSIM=OFF + TESTS_EXTRA=OFF + elif [ ${{ matrix.config.CC }} == "gcc" ] && [ ${{ matrix.config.version }} == "5" ] + then + STRICT=ON + DBSIM=ON + TESTS_EXTRA=OFF else STRICT=ON DBSIM=ON + TESTS_EXTRA=ON fi cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{ matrix.config.type }} \ -DWSREP_LIB_MAINTAINER_MODE:BOOL=ON \ -DWSREP_LIB_STRICT_BUILD_FLAGS:BOOL=$STRICT \ -DWSREP_LIB_WITH_DBSIM:BOOL=$DBSIM \ - -DWSREP_LIB_WITH_ASAN:BOOL=ON . + -DWSREP_LIB_WITH_ASAN:BOOL=ON \ + -DWSREP_LIB_WITH_UNIT_TESTS_EXTRA:BOOL=$TESTS_EXTRA - name: Build working-directory: ${{runner.workspace}}/build diff --git a/CMakeLists.txt b/CMakeLists.txt index 292c413..49d1a12 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,6 +123,7 @@ endif() set(MIN_BOOST_VERSION "1.54.0") if (WSREP_LIB_WITH_UNIT_TESTS) if (WSREP_LIB_WITH_UNIT_TESTS_EXTRA) + message(STATUS "Compiling extra unit tests") set(json_HEADER "boost/json/src.hpp") # Extra tests may require packages from very recent boost which may be # unavailable on the system. In such case download private boost distro. @@ -140,17 +141,14 @@ if (WSREP_LIB_WITH_UNIT_TESTS) PATHS ${WITH_BOOST}/lib/cmake NO_DEFAULT_PATH ) - # Boost 1.76.0 comes very sloppy (and not only in JSON department) - # - need to disable some checks. - set(ADDITIONAL_CXX_FLAGS "-Wno-effc++ -Wno-conversion -Wno-suggest-override -Wno-overloaded-virtual") - set(ADDITIONAL_CXX_INCLUDES ${BOOST_INCLUDE_DIR}) - check_include_file_cxx(${json_HEADER} json_FOUND - "-I${ADDITIONAL_CXX_INCLUDES} ${ADDITIONAL_CXX_FLAGS}" - ) + # Include as system header to be more permissive about generated + # warnings. + set(CMAKE_REQUIRED_FLAGS "-isystem ${BOOST_INCLUDE_DIR}") + check_include_file_cxx(${json_HEADER} json_FOUND) if (NOT json_FOUND) message(FATAL_ERROR "Required header 'boost/json.hpp' not found: ${json_FOUND}") else() - include_directories(SYSTEM ${ADDITIONAL_CXX_INCLUDES}) + include_directories(SYSTEM ${BOOST_INCLUDE_DIR}) endif() endif() else() diff --git a/include/wsrep/reporter.hpp b/include/wsrep/reporter.hpp index 75ed038..3e8c700 100644 --- a/include/wsrep/reporter.hpp +++ b/include/wsrep/reporter.hpp @@ -57,6 +57,15 @@ namespace wsrep */ void report_progress(const std::string& json); + /** + * Report provider event. + * { + * "status": "Status string", + * "message": "Message from the provider" + * } + */ + void report_event(const std::string& json); + enum log_level { error, @@ -100,14 +109,17 @@ namespace wsrep std::deque err_msg_; std::deque warn_msg_; + std::deque events_; size_t const max_msg_; - static void write_log_msgs(std::ostream& os, - const std::string& label, - const std::deque& msgs); static void write_log_msg(std::ostream& os, - const log_msg& msg); - + const log_msg& msg); + static void write_event(std::ostream& os, + const log_msg& msg); + static void write_array(std::ostream& os, const std::string& label, + const std::deque& events, + void (*element_writer)(std::ostream& os, + const log_msg& msg)); substates substate_map(enum server_state::state state); float progress_map(float progress) const; void write_file(double timestamp); diff --git a/src/reporter.cpp b/src/reporter.cpp index 2a5c6f5..57382ea 100644 --- a/src/reporter.cpp +++ b/src/reporter.cpp @@ -72,6 +72,7 @@ wsrep::reporter::reporter(wsrep::mutex& mutex, , initialized_(false) , err_msg_() , warn_msg_() + , events_() , max_msg_(max_msg) { template_[file_name_.length() + TEMP_EXTENSION.length()] = '\0'; @@ -137,6 +138,37 @@ wsrep::reporter::substate_map(enum wsrep::server_state::state const state) } } +// See https://www.ietf.org/rfc/rfc4627.txt +static std::string escape_json(const std::string& str) +{ + std::ostringstream os; + for (auto c = str.cbegin(); c != str.cend(); ++c) + { + switch (*c) + { + case '"': os << "\\\""; break; + case '\\': os << "\\\\"; break; + case '/': os << "\\/"; break; + case '\b': os << "\\b"; break; + case '\f': os << "\\f"; break; + case '\n': os << "\\n"; break; + case '\r': os << "\\r"; break; + case '\t': os << "\\t"; break; + default: + if (0x0 <= *c && *c <= 0x1f) + { + os << "\\u" << std::hex << std::setw(4) << + std::setfill('0') << static_cast(*c); + } + else + { + os << *c; + } + } + } + return os.str(); +} + void wsrep::reporter::write_log_msg(std::ostream& os, const log_msg& msg) @@ -149,14 +181,27 @@ wsrep::reporter::write_log_msg(std::ostream& os, } void -wsrep::reporter::write_log_msgs(std::ostream& os, - const std::string& label, - const std::deque& msgs) +wsrep::reporter::write_event(std::ostream& os, + const log_msg& msg) +{ + os << "\t\t{\n"; + os << "\t\t\t\"timestamp\": " << std::showpoint << std::setprecision(18) + << msg.tstamp << ",\n"; + os << "\t\t\t\"event\": " << msg.msg << "\n"; + os << "\t\t}"; +} + +void +wsrep::reporter::write_array(std::ostream& os, + const std::string& label, + const std::deque& msgs, + void (*element_writer)(std::ostream& os, + const log_msg& msg)) { os << "\t\"" << label << "\": [\n"; for (size_t i(0); i < msgs.size(); ++i) { - write_log_msg(os, msgs[i]); + element_writer(os, msgs[i]); os << (i+1 < msgs.size() ? ",\n" : "\n"); } os << "\t],\n"; @@ -223,8 +268,9 @@ wsrep::reporter::write_file(double const tstamp) os << "\t\"date\": \"" << date_str << "\",\n"; os << "\t\"timestamp\": " << std::showpoint << std::setprecision(18) << tstamp << ",\n"; - write_log_msgs(os, "errors", err_msg_); - write_log_msgs(os, "warnings", warn_msg_); + write_array(os, "errors", err_msg_, write_log_msg); + write_array(os, "warnings", warn_msg_, write_log_msg); + write_array(os, "events", events_, write_event); os << "\t\"status\": {\n"; os << "\t\t\"state\": \"" << strings[state_].state << "\",\n"; os << "\t\t\"comment\": \"" << strings[state_].comment << "\",\n"; @@ -282,6 +328,18 @@ wsrep::reporter::report_progress(const std::string& json) } } +void +wsrep::reporter::report_event(const std::string& json) +{ + wsrep::unique_lock lock(mutex_); + if (events_.size() == max_msg_) + { + events_.pop_front(); + } + events_.push_back({timestamp(), json}); + write_file(timestamp()); +} + void wsrep::reporter::report_log_msg(log_level const lvl, const std::string& msg, @@ -297,7 +355,9 @@ wsrep::reporter::report_log_msg(log_level const lvl, if (tstamp <= undefined) tstamp = timestamp(); - log_msg entry({tstamp, msg}); + /* Log messages are not expected to be json formatted, so we escape + the message strings here to keep the report file well formatted. */ + log_msg entry({tstamp, escape_json(msg)}); deque.push_back(entry); write_file(tstamp); } diff --git a/test/reporter_test.cpp b/test/reporter_test.cpp index bed7a2a..9ee381e 100644 --- a/test/reporter_test.cpp +++ b/test/reporter_test.cpp @@ -320,8 +320,9 @@ print_logs(std::ostream& os, const logs& left, const logs& right) } // print two results against each other +template static std::string -print(const result& left, const result& right, size_t it) +print(const result& left, const result& right, Iteration it) { std::ostringstream os; @@ -360,6 +361,7 @@ static struct result const RES_INIT = { LOGS_INIT, LOGS_INIT, { "DISCONNECTED", "Disconnected", indefinite } }; +template static void test_log(const char* const fname, wsrep::reporter& rep, @@ -367,7 +369,7 @@ test_log(const char* const fname, wsrep::reporter::log_level const lvl, double const tstamp, const std::string& msg, - size_t const iteration) + Iteration const iteration) { // this is implementaiton detail, if it changes in the code, it needs // to be changed here @@ -389,11 +391,14 @@ test_log(const char* const fname, static size_t const MAX_MSG = 4; -BOOST_AUTO_TEST_CASE(log_msg_test) +struct reporter_fixture { - wsrep::default_mutex m; - wsrep::reporter rep(m, REPORT, MAX_MSG); + wsrep::default_mutex mutex{}; + wsrep::reporter rep{mutex, REPORT, MAX_MSG}; +}; +BOOST_FIXTURE_TEST_CASE(log_msg_test, reporter_fixture) +{ auto value = read_file(REPORT); BOOST_REQUIRE(value != nullptr); @@ -435,12 +440,10 @@ BOOST_AUTO_TEST_CASE(log_msg_test) ::unlink(REPORT); } -BOOST_AUTO_TEST_CASE(state_test) +BOOST_FIXTURE_TEST_CASE(state_test, reporter_fixture) { using wsrep::server_state; - wsrep::default_mutex m; - wsrep::reporter rep(m, REPORT, MAX_MSG); double const err_tstamp(timestamp()); std::string const err_msg("Error!"); @@ -526,7 +529,7 @@ BOOST_AUTO_TEST_CASE(state_test) ::unlink(REPORT); } -BOOST_AUTO_TEST_CASE(progress_test) +BOOST_FIXTURE_TEST_CASE(progress_test, reporter_fixture) { using wsrep::server_state; @@ -634,3 +637,19 @@ BOOST_AUTO_TEST_CASE(progress_test) ::unlink(REPORT); } + +BOOST_FIXTURE_TEST_CASE(event_test, reporter_fixture) +{ + rep.report_event("{\"msg\": \"message\"}"); + auto value = read_file(REPORT); + BOOST_REQUIRE(value.at("events").is_array()); + auto event_array = value.at("events").as_array(); + BOOST_REQUIRE(event_array.size() == 1); + auto event = event_array[0]; + BOOST_REQUIRE(event.is_object()); + BOOST_REQUIRE(event.at("timestamp").is_double()); + BOOST_REQUIRE(event.at("event").is_object()); + BOOST_REQUIRE(event.at("event").at("msg").is_string()); + BOOST_REQUIRE(event.at("event").at("msg").as_string() == "message"); + ::unlink(REPORT); +}