Skip to content

Commit

Permalink
Separate stat sinks
Browse files Browse the repository at this point in the history
  • Loading branch information
pwojcikdev committed Feb 11, 2024
1 parent 74e2224 commit e7647c2
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 150 deletions.
1 change: 1 addition & 0 deletions nano/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
115 changes: 4 additions & 111 deletions nano/lib/stats.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <nano/lib/jsonconfig.hpp>
#include <nano/lib/locks.hpp>
#include <nano/lib/stats.hpp>
#include <nano/lib/stats_writers.hpp>
#include <nano/lib/thread_roles.hpp>
#include <nano/lib/tomlconfig.hpp>

Expand All @@ -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
*/
Expand Down Expand Up @@ -269,11 +168,6 @@ void nano::stats::update_sampler (nano::stats::sampler_key key, std::function<vo
}
}

std::unique_ptr<nano::stat_log_sink> nano::stats::log_sink_json () const
{
return std::make_unique<json_writer> ();
}

void nano::stats::log_counters (stat_log_sink & sink)
{
// TODO: Replace with a proper std::chrono time
Expand Down Expand Up @@ -386,8 +280,8 @@ void nano::stats::run ()

void nano::stats::run_one (std::unique_lock<std::shared_mutex> & 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 ());
Expand Down Expand Up @@ -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:
Expand All @@ -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 ();
}
Expand Down
12 changes: 0 additions & 12 deletions nano/lib/stats.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<stat_log_sink> log_sink_json () const;

public:
enum class category
{
Expand Down Expand Up @@ -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 };
Expand Down
110 changes: 110 additions & 0 deletions nano/lib/stats_writers.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#pragma once

#include <nano/lib/stats.hpp>

#include <boost/format.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>

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);
}

~stat_file_writer () override
{
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;
}
};
}
41 changes: 19 additions & 22 deletions nano/node/json_handler.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <nano/lib/config.hpp>
#include <nano/lib/json_error_response.hpp>
#include <nano/lib/stats_writers.hpp>
#include <nano/lib/timer.hpp>
#include <nano/node/bootstrap/bootstrap_lazy.hpp>
#include <nano/node/bootstrap_ascending/service.hpp>
Expand Down Expand Up @@ -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<std::string> ("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")
{
Expand All @@ -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<boost::property_tree::ptree *> (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 ()
Expand Down
11 changes: 6 additions & 5 deletions nano/qt/qt.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <nano/lib/config.hpp>
#include <nano/lib/stats_writers.hpp>
#include <nano/qt/qt.hpp>

#include <boost/property_tree/json_parser.hpp>
Expand Down Expand Up @@ -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<boost::property_tree::ptree *> (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<std::string> ("time");
auto type = child.second.get<std::string> ("type");
Expand Down

0 comments on commit e7647c2

Please sign in to comment.