From ac1a512562e3e6616a370410e73a83b991cb15bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Sun, 11 Feb 2024 11:59:09 +0100 Subject: [PATCH] Separate stat sinks --- nano/lib/CMakeLists.txt | 1 + nano/lib/stats.cpp | 115 ++----------------------------------- nano/lib/stats.hpp | 12 ---- nano/lib/stats_writers.hpp | 110 +++++++++++++++++++++++++++++++++++ nano/node/json_handler.cpp | 41 ++++++------- nano/qt/qt.cpp | 11 ++-- 6 files changed, 140 insertions(+), 150 deletions(-) create mode 100644 nano/lib/stats_writers.hpp diff --git a/nano/lib/CMakeLists.txt b/nano/lib/CMakeLists.txt index e93429ead3..7d0c201fdc 100644 --- a/nano/lib/CMakeLists.txt +++ b/nano/lib/CMakeLists.txt @@ -82,6 +82,7 @@ add_library( stats.cpp stats_enums.hpp stats_enums.cpp + stats_writers.hpp stream.hpp thread_pool.hpp thread_pool.cpp diff --git a/nano/lib/stats.cpp b/nano/lib/stats.cpp index 762dfc53f1..8f416dd2fb 100644 --- a/nano/lib/stats.cpp +++ b/nano/lib/stats.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -20,108 +21,6 @@ std::string nano::stat_log_sink::tm_to_string (tm & tm) return (boost::format ("%04d.%02d.%02d %02d:%02d:%02d") % (1900 + tm.tm_year) % (tm.tm_mon + 1) % tm.tm_mday % tm.tm_hour % tm.tm_min % tm.tm_sec).str (); } -namespace -{ -/** JSON sink. The resulting JSON object is provided as both a property_tree::ptree (to_object) and a string (to_string) */ -class json_writer : public nano::stat_log_sink -{ - boost::property_tree::ptree tree; - boost::property_tree::ptree entries; - -public: - std::ostream & out () override - { - return sstr; - } - - void begin () override - { - tree.clear (); - } - - void write_header (std::string const & header, std::chrono::system_clock::time_point & walltime) override - { - std::time_t now = std::chrono::system_clock::to_time_t (walltime); - tm tm = *localtime (&now); - tree.put ("type", header); - tree.put ("created", tm_to_string (tm)); - } - - void write_counter_entry (tm & tm, std::string const & type, std::string const & detail, std::string const & dir, uint64_t value) override - { - boost::property_tree::ptree entry; - entry.put ("time", boost::format ("%02d:%02d:%02d") % tm.tm_hour % tm.tm_min % tm.tm_sec); - entry.put ("type", type); - entry.put ("detail", detail); - entry.put ("dir", dir); - entry.put ("value", value); - entries.push_back (std::make_pair ("", entry)); - } - - void finalize () override - { - tree.add_child ("entries", entries); - } - - void * to_object () override - { - return &tree; - } - - std::string to_string () override - { - boost::property_tree::write_json (sstr, tree); - return sstr.str (); - } - -private: - std::ostringstream sstr; -}; - -/** File sink with rotation support. This writes one counter per line and does not include histogram values. */ -class file_writer : public nano::stat_log_sink -{ -public: - std::ofstream log; - std::string filename; - - explicit file_writer (std::string const & filename) : - filename (filename) - { - log.open (filename.c_str (), std::ofstream::out); - } - - virtual ~file_writer () - { - log.close (); - } - - std::ostream & out () override - { - return log; - } - - void write_header (std::string const & header, std::chrono::system_clock::time_point & walltime) override - { - std::time_t now = std::chrono::system_clock::to_time_t (walltime); - tm tm = *localtime (&now); - log << header << "," << boost::format ("%04d.%02d.%02d %02d:%02d:%02d") % (1900 + tm.tm_year) % (tm.tm_mon + 1) % tm.tm_mday % tm.tm_hour % tm.tm_min % tm.tm_sec << std::endl; - } - - void write_counter_entry (tm & tm, std::string const & type, std::string const & detail, std::string const & dir, uint64_t value) override - { - log << boost::format ("%02d:%02d:%02d") % tm.tm_hour % tm.tm_min % tm.tm_sec << "," << type << "," << detail << "," << dir << "," << value << std::endl; - } - - void rotate () override - { - log.close (); - log.open (filename.c_str (), std::ofstream::out); - log_entries = 0; - } -}; -} - /* * stats */ @@ -269,11 +168,6 @@ void nano::stats::update_sampler (nano::stats::sampler_key key, std::function nano::stats::log_sink_json () const -{ - return std::make_unique (); -} - void nano::stats::log_counters (stat_log_sink & sink) { // TODO: Replace with a proper std::chrono time @@ -386,8 +280,8 @@ void nano::stats::run () void nano::stats::run_one (std::unique_lock & lock) { - static file_writer log_count{ config.log_counters_filename }; - static file_writer log_sample{ config.log_samples_filename }; + static stat_file_writer log_count{ config.log_counters_filename }; + static stat_file_writer log_sample{ config.log_samples_filename }; debug_assert (!mutex.try_lock ()); debug_assert (lock.owns_lock ()); @@ -428,7 +322,7 @@ std::chrono::seconds nano::stats::last_reset () std::string nano::stats::dump (category category) { - json_writer sink; + stat_json_writer sink; switch (category) { case category::counters: @@ -439,7 +333,6 @@ std::string nano::stats::dump (category category) break; default: debug_assert (false, "missing stat_category case"); - break; } return sink.to_string (); } diff --git a/nano/lib/stats.hpp b/nano/lib/stats.hpp index 866d0cd0cb..6db1b9193d 100644 --- a/nano/lib/stats.hpp +++ b/nano/lib/stats.hpp @@ -131,9 +131,6 @@ class stats final /** Log samples to the given log sink */ void log_samples (stat_log_sink & sink); - /** Returns a new JSON log sink */ - std::unique_ptr log_sink_json () const; - public: enum class category { @@ -277,15 +274,6 @@ class stat_log_sink return ""; } - /** - * Returns the object representation of the log result. The type depends on the sink used. - * @returns Object, or nullptr if no object result is available. - */ - virtual void * to_object () - { - return nullptr; - } - protected: std::string tm_to_string (tm & tm); size_t log_entries{ 0 }; diff --git a/nano/lib/stats_writers.hpp b/nano/lib/stats_writers.hpp new file mode 100644 index 0000000000..04ec0f1577 --- /dev/null +++ b/nano/lib/stats_writers.hpp @@ -0,0 +1,110 @@ +#pragma once + +#include + +#include +#include +#include + +namespace nano +{ +/** JSON sink. The resulting JSON object is provided as both a property_tree::ptree (to_object) and a string (to_string) */ +class stat_json_writer : public nano::stat_log_sink +{ + boost::property_tree::ptree tree; + boost::property_tree::ptree entries; + +public: + std::ostream & out () override + { + return sstr; + } + + void begin () override + { + tree.clear (); + } + + void write_header (std::string const & header, std::chrono::system_clock::time_point & walltime) override + { + std::time_t now = std::chrono::system_clock::to_time_t (walltime); + tm tm = *localtime (&now); + tree.put ("type", header); + tree.put ("created", tm_to_string (tm)); + } + + void write_counter_entry (tm & tm, std::string const & type, std::string const & detail, std::string const & dir, uint64_t value) override + { + boost::property_tree::ptree entry; + entry.put ("time", boost::format ("%02d:%02d:%02d") % tm.tm_hour % tm.tm_min % tm.tm_sec); + entry.put ("type", type); + entry.put ("detail", detail); + entry.put ("dir", dir); + entry.put ("value", value); + entries.push_back (std::make_pair ("", entry)); + } + + void finalize () override + { + tree.add_child ("entries", entries); + } + + std::string to_string () override + { + boost::property_tree::write_json (sstr, tree); + return sstr.str (); + } + + // WARNING: This method moves the ptree out of the object, leaving it in an undefined state + boost::property_tree::ptree to_ptree () + { + return std::move (tree); + } + +private: + std::ostringstream sstr; +}; + +/** File sink with rotation support. This writes one counter per line and does not include histogram values. */ +class stat_file_writer : public nano::stat_log_sink +{ +public: + std::ofstream log; + std::string filename; + + explicit stat_file_writer (std::string const & filename) : + filename (filename) + { + log.open (filename.c_str (), std::ofstream::out); + } + + virtual ~stat_file_writer () + { + log.close (); + } + + std::ostream & out () override + { + return log; + } + + void write_header (std::string const & header, std::chrono::system_clock::time_point & walltime) override + { + std::time_t now = std::chrono::system_clock::to_time_t (walltime); + tm tm = *localtime (&now); + log << header << "," << boost::format ("%04d.%02d.%02d %02d:%02d:%02d") % (1900 + tm.tm_year) % (tm.tm_mon + 1) % tm.tm_mday % tm.tm_hour % tm.tm_min % tm.tm_sec << std::endl; + } + + void write_counter_entry (tm & tm, std::string const & type, std::string const & detail, std::string const & dir, uint64_t value) override + { + log << boost::format ("%02d:%02d:%02d") % tm.tm_hour % tm.tm_min % tm.tm_sec << "," << type << "," << detail << "," << dir << "," << value << std::endl; + } + + void rotate () override + { + log.close (); + log.open (filename.c_str (), std::ofstream::out); + log_entries = 0; + } +}; +} \ No newline at end of file diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index 4bfa45dc28..27b6e6db89 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -3931,22 +3932,29 @@ void nano::json_handler::sign () void nano::json_handler::stats () { - auto sink = node.stats.log_sink_json (); std::string type (request.get ("type", "")); - bool use_sink = false; + + auto respond_with_sink = [this] (auto & sink) { + auto stat_ptree = sink.to_ptree (); + stat_ptree.put ("stat_duration_seconds", node.stats.last_reset ().count ()); + response_l = stat_ptree; + }; + if (type == "counters") { - node.stats.log_counters (*sink); - use_sink = true; + nano::stat_json_writer sink; + node.stats.log_counters (sink); + respond_with_sink (sink); } - else if (type == "objects") + else if (type == "samples") { - construct_json (collect_container_info (node, "node").get (), response_l); + nano::stat_json_writer sink; + node.stats.log_samples (sink); + respond_with_sink (sink); } - else if (type == "samples") + else if (type == "objects") { - node.stats.log_samples (*sink); - use_sink = true; + construct_json (collect_container_info (node, "node").get (), response_l); } else if (type == "database") { @@ -3956,19 +3964,8 @@ void nano::json_handler::stats () { ec = nano::error_rpc::invalid_missing_type; } - if (!ec && use_sink) - { - // TODO: Clearly someone gave up on designing this properly here - auto stat_tree_l (*static_cast (sink->to_object ())); - stat_tree_l.put ("stat_duration_seconds", node.stats.last_reset ().count ()); - std::stringstream ostream; - boost::property_tree::write_json (ostream, stat_tree_l); - response (ostream.str ()); - } - else - { - response_errors (); - } + + response_errors (); } void nano::json_handler::stats_clear () diff --git a/nano/qt/qt.cpp b/nano/qt/qt.cpp index 2b83d7d61a..33ca5cd54e 100644 --- a/nano/qt/qt.cpp +++ b/nano/qt/qt.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -838,13 +839,13 @@ void nano_qt::stats_viewer::refresh_stats () { model->removeRows (0, model->rowCount ()); - auto sink = wallet.node.stats.log_sink_json (); - wallet.node.stats.log_counters (*sink); - auto json = static_cast (sink->to_object ()); - if (json) + nano::stat_json_writer sink; + wallet.node.stats.log_counters (sink); + auto json = sink.to_ptree (); + if (!json.empty ()) { // Format the stat data to make totals and values easier to read - for (boost::property_tree::ptree::value_type const & child : json->get_child ("entries")) + for (boost::property_tree::ptree::value_type const & child : json.get_child ("entries")) { auto time = child.second.get ("time"); auto type = child.second.get ("type");