diff --git a/nano/lib/stats.cpp b/nano/lib/stats.cpp index ef201abeb7..222603434d 100644 --- a/nano/lib/stats.cpp +++ b/nano/lib/stats.cpp @@ -273,37 +273,33 @@ nano::stats::stats (nano::stats_config config) : { } -void nano::stats::add (stat::type type, stat::detail detail, stat::dir dir, uint64_t value, bool detail_only) +void nano::stats::add (stat::type type, stat::detail detail, stat::dir dir, uint64_t value) { if (value == 0) { return; } - constexpr uint32_t no_detail_mask = 0xffff00ff; - uint32_t key = key_of (type, detail, dir); + update (key{ type, detail, dir }, value); - update (key, value); - - // Optionally update at type-level as well - if (!detail_only && (key & no_detail_mask) != key) + if (detail != stat::detail::all) { - update (key & no_detail_mask, value); + update (key{ type, stat::detail::all, dir }, value); } } -std::shared_ptr nano::stats::get_entry (uint32_t key) +std::shared_ptr nano::stats::get_entry (key key) { return get_entry (key, config.interval, config.capacity); } -std::shared_ptr nano::stats::get_entry (uint32_t key, size_t interval, size_t capacity) +std::shared_ptr nano::stats::get_entry (key key, size_t interval, size_t capacity) { nano::unique_lock lock{ stat_mutex }; return get_entry_impl (key, interval, capacity); } -std::shared_ptr nano::stats::get_entry_impl (uint32_t key, size_t interval, size_t capacity) +std::shared_ptr nano::stats::get_entry_impl (key key, size_t interval, size_t capacity) { std::shared_ptr res; auto entry = entries.find (key); @@ -344,16 +340,16 @@ void nano::stats::log_counters_impl (stat_log_sink & sink) sink.write_header ("counters", walltime); } - for (auto & it : entries) + for (auto const & [key, value] : entries) { - std::time_t time = std::chrono::system_clock::to_time_t (it.second->counter.get_timestamp ()); + std::time_t time = std::chrono::system_clock::to_time_t (value->counter.get_timestamp ()); tm local_tm = *localtime (&time); - auto key = it.first; - std::string type = type_to_string (key); - std::string detail = detail_to_string (key); - std::string dir = dir_to_string (key); - sink.write_entry (local_tm, type, detail, dir, it.second->counter.get_value (), it.second->histogram.get ()); + std::string type{ to_string (key.type) }; + std::string detail{ to_string (key.detail) }; + std::string dir{ to_string (key.dir) }; + + sink.write_entry (local_tm, type, detail, dir, value->counter.get_value (), value->histogram.get ()); } sink.entries ()++; sink.finalize (); @@ -379,45 +375,45 @@ void nano::stats::log_samples_impl (stat_log_sink & sink) sink.write_header ("samples", walltime); } - for (auto & it : entries) + for (auto const & [key, value] : entries) { - auto key = it.first; - std::string type = type_to_string (key); - std::string detail = detail_to_string (key); - std::string dir = dir_to_string (key); + std::string type{ to_string (key.type) }; + std::string detail{ to_string (key.detail) }; + std::string dir{ to_string (key.dir) }; - for (auto & datapoint : it.second->samples) + for (auto & datapoint : value->samples) { std::time_t time = std::chrono::system_clock::to_time_t (datapoint.get_timestamp ()); tm local_tm = *localtime (&time); sink.write_entry (local_tm, type, detail, dir, datapoint.get_value (), nullptr); } } + sink.entries ()++; sink.finalize (); } void nano::stats::define_histogram (stat::type type, stat::detail detail, stat::dir dir, std::initializer_list intervals_a, size_t bin_count_a /*=0*/) { - auto entry (get_entry (key_of (type, detail, dir))); + auto entry (get_entry (key{ type, detail, dir })); entry->histogram = std::make_unique (intervals_a, bin_count_a); } void nano::stats::update_histogram (stat::type type, stat::detail detail, stat::dir dir, uint64_t index_a, uint64_t addend_a) { - auto entry (get_entry (key_of (type, detail, dir))); + auto entry (get_entry (key{ type, detail, dir })); debug_assert (entry->histogram != nullptr); entry->histogram->add (index_a, addend_a); } nano::stat_histogram * nano::stats::get_histogram (stat::type type, stat::detail detail, stat::dir dir) { - auto entry (get_entry (key_of (type, detail, dir))); + auto entry (get_entry (key{ type, detail, dir })); debug_assert (entry->histogram != nullptr); return entry->histogram.get (); } -void nano::stats::update (uint32_t key_a, uint64_t value) +void nano::stats::update (key key_a, uint64_t value) { static file_writer log_count (config.log_counters_filename); static file_writer log_sample (config.log_samples_filename); @@ -522,24 +518,6 @@ void nano::stats::clear () timestamp = std::chrono::steady_clock::now (); } -std::string nano::stats::type_to_string (uint32_t key) -{ - auto type = static_cast (key >> 16 & 0x000000ff); - return std::string{ nano::to_string (type) }; -} - -std::string nano::stats::detail_to_string (uint32_t key) -{ - auto detail = static_cast (key >> 8 & 0x000000ff); - return std::string{ nano::to_string (detail) }; -} - -std::string nano::stats::dir_to_string (uint32_t key) -{ - auto dir = static_cast (key & 0x000000ff); - return std::string{ nano::to_string (dir) }; -} - /* * stat_datapoint */ diff --git a/nano/lib/stats.hpp b/nano/lib/stats.hpp index 96bdc037b4..5d8a108942 100644 --- a/nano/lib/stats.hpp +++ b/nano/lib/stats.hpp @@ -9,10 +9,10 @@ #include #include +#include #include #include #include -#include namespace nano { @@ -242,7 +242,7 @@ class stats final */ void configure (stat::type type, stat::detail detail, stat::dir dir, size_t interval, size_t capacity) { - get_entry (key_of (type, detail, dir), interval, capacity); + get_entry (key{ type, detail, dir }, interval, capacity); } /** Increments the given counter */ @@ -313,9 +313,8 @@ class stats final * @param detail Detail type, or detail::none to register on type-level only * @param dir Direction * @param value The amount to add - * @param detail_only If true, only update the detail-level counter */ - void add (stat::type type, stat::detail detail, stat::dir dir, uint64_t value, bool detail_only = false); + void add (stat::type type, stat::detail detail, stat::dir dir, uint64_t value); /** * Add a sampling observer for a given counter. @@ -325,7 +324,7 @@ class stats final */ void observe_sample (stat::type type, stat::detail detail, stat::dir dir, std::function &)> observer) { - get_entry (key_of (type, detail, dir))->sample_observers.add (observer); + get_entry (key{ type, detail, dir })->sample_observers.add (observer); } void observe_sample (stat::type type, stat::dir dir, std::function &)> observer) @@ -340,13 +339,13 @@ class stats final */ void observe_count (stat::type type, stat::detail detail, stat::dir dir, std::function observer) { - get_entry (key_of (type, detail, dir))->count_observers.add (observer); + get_entry (key{ type, detail, dir })->count_observers.add (observer); } /** Returns a potentially empty list of the last N samples, where N is determined by the 'capacity' configuration */ boost::circular_buffer * samples (stat::type type, stat::detail detail, stat::dir dir) { - return &get_entry (key_of (type, detail, dir))->samples; + return &get_entry (key{ type, detail, dir })->samples; } /** Returns current value for the given counter at the type level */ @@ -358,7 +357,7 @@ class stats final /** Returns current value for the given counter at the detail level */ uint64_t count (stat::type type, stat::detail detail, stat::dir dir = stat::dir::in) { - return get_entry (key_of (type, detail, dir))->counter.get_value (); + return get_entry (key{ type, detail, dir })->counter.get_value (); } /** Returns the number of seconds since clear() was last called, or node startup if it's never called. */ @@ -381,31 +380,30 @@ class stats final std::string dump (stat_category category = stat_category::counters); private: - static std::string type_to_string (uint32_t key); - static std::string dir_to_string (uint32_t key); - static std::string detail_to_string (uint32_t key); - - /** Constructs a key given type, detail and direction. This is used as input to update(...) and get_entry(...) */ - uint32_t key_of (stat::type type, stat::detail detail, stat::dir dir) const + struct key { - return static_cast (type) << 16 | static_cast (detail) << 8 | static_cast (dir); - } + stat::type type; + stat::detail detail; + stat::dir dir; + auto operator<=> (const key &) const = default; + }; + +private: /** Get entry for key, creating a new entry if necessary, using interval and sample count from config */ - std::shared_ptr get_entry (uint32_t key); + std::shared_ptr get_entry (key key); /** Get entry for key, creating a new entry if necessary */ - std::shared_ptr get_entry (uint32_t key, size_t sample_interval, size_t max_samples); + std::shared_ptr get_entry (key key, size_t sample_interval, size_t max_samples); /** Unlocked implementation of get_entry() */ - std::shared_ptr get_entry_impl (uint32_t key, size_t sample_interval, size_t max_samples); + std::shared_ptr get_entry_impl (key key, size_t sample_interval, size_t max_samples); /** * Update count and sample and call any observers on the key - * @param key a key constructor from stat::type, stat::detail and stat::direction * @value Amount to add to the counter */ - void update (uint32_t key, uint64_t value); + void update (key key, uint64_t value); /** Unlocked implementation of log_counters() to avoid using recursive locking */ void log_counters_impl (stat_log_sink & sink); @@ -420,7 +418,7 @@ class stats final nano::stats_config config; /** Stat entries are sorted by key to simplify processing of log output */ - std::unordered_map> entries; + std::map> entries; std::chrono::steady_clock::time_point log_last_count_writeout{ std::chrono::steady_clock::now () }; std::chrono::steady_clock::time_point log_last_sample_writeout{ std::chrono::steady_clock::now () }; diff --git a/nano/lib/stats_enums.hpp b/nano/lib/stats_enums.hpp index 8fc3155763..7ae4fe8cae 100644 --- a/nano/lib/stats_enums.hpp +++ b/nano/lib/stats_enums.hpp @@ -8,7 +8,7 @@ namespace nano::stat { /** Primary statistics type */ -enum class type : uint8_t +enum class type { traffic_tcp, error, @@ -55,7 +55,7 @@ enum class type : uint8_t }; /** Optional detail type */ -enum class detail : uint8_t +enum class detail { all = 0, @@ -316,7 +316,7 @@ enum class detail : uint8_t }; /** Direction of the stat. If the direction is irrelevant, use in */ -enum class dir : uint8_t +enum class dir { in, out,