diff --git a/include/ircd/net/socket.h b/include/ircd/net/socket.h index ef4324a0e..0ae1a9711 100644 --- a/include/ircd/net/socket.h +++ b/include/ircd/net/socket.h @@ -48,10 +48,10 @@ struct ircd::net::socket static uint64_t count; // monotonic static uint64_t instances; // current socket count - static stats::item total_bytes_in; - static stats::item total_bytes_out; - static stats::item total_calls_in; - static stats::item total_calls_out; + static stats::item total_bytes_in; + static stats::item total_bytes_out; + static stats::item total_calls_in; + static stats::item total_calls_out; uint64_t id {++count}; ip::tcp::socket sd; diff --git a/include/ircd/stats.h b/include/ircd/stats.h index d787b5147..f3ac106ba 100644 --- a/include/ircd/stats.h +++ b/include/ircd/stats.h @@ -1,7 +1,7 @@ -// Matrix Construct +// The Construct // -// Copyright (C) Matrix Construct Developers, Authors & Contributors -// Copyright (C) 2016-2019 Jason Volk +// Copyright (C) The Construct Developers, Authors & Contributors +// Copyright (C) 2016-2020 Jason Volk // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -11,142 +11,211 @@ #pragma once #define HAVE_IRCD_STATS_H +/// Statistics & Metrics +/// +/// This is a central collection of registered items each representing a +/// counter or metric of some kind. To collect items of various types we orient +/// the collection around an abstract item template class. To keep things +/// simple, the abstract instance holds a typeinfo provided by the derived +/// instance. User can then downcast the item to a derived template. +/// +/// There are two levels in the class hierarchy under the abstract root +/// item. The next level is a pointer-to-value such that the item +/// registers the location of an existing value. This is useful for +/// incorporating external values in existing structures into this collection +/// non-intrusively; for example in third-party libraries etc. +/// +/// The next derived level after this is where the item instance itself +/// roots (contains) the value and its parent class at the pointer-level points +/// down to the derived class. This is a convenience for developers so that +/// extern values don't have to be created separately for every stats item. +/// Note that when this subsystem works abstractly with items, it considers the +/// pointer-level templates to be the principal level of derivation; in other +/// words there is no reason to downcast to a value-level template when working +/// with this system, and every value-level template must have a matching +/// pointer-level parent. namespace ircd::stats { - struct item; - using value_type = int128_t; - IRCD_EXCEPTION(ircd::error, error) IRCD_EXCEPTION(error, not_found) - extern std::map items; + // Abstract item + template struct item; + template<> struct item; - const value_type &get(const item &); - value_type &get(item &); + // Pointer-to-value items + template<> struct item; + template<> struct item; + template<> struct item; - value_type &inc(item &, const value_type & = 1); - value_type &dec(item &, const value_type & = 1); - value_type &set(item &, const value_type & = 0); + // Value-carrying items + template<> struct item; + template<> struct item; + template<> struct item; - std::ostream &operator<<(std::ostream &, const item &); + extern const size_t NAME_MAX_LEN; + extern std::map *> items; + + std::ostream &operator<<(std::ostream &, const item &); } -struct ircd::stats::item +/// Abstract stats item. +/// +/// This object contains type information about its derived class. There is no +/// known use for constructing this on its own without a derived item. +/// +/// Feature information must contain a 'name' string. It is advised that this +/// is appropriately namespaced i.e "ircd.net.socket.xxx" and when third-party +/// values are gathered i.e "rocksdb.xxx." such that the entire map of items +/// can be serialized into a single JSON object tree. +/// +/// Feature information can also contain a 'desc' string describing more about +/// the value to administrators and developers. +template<> +struct ircd::stats::item { - static const size_t NAME_MAX_LEN; - - json::strung feature_; - json::object feature; - string_view name; - value_type val; + std::type_index type {typeid(void)}; + json::strung feature; public: - explicit operator const value_type &() const; - explicit operator value_type &(); - bool operator!() const; + // Access features + string_view operator[](const string_view &key) const noexcept; - item &operator+=(const value_type &) &; - item &operator-=(const value_type &) &; - item &operator=(const value_type &) &; - item &operator++(); - item &operator--(); - - item(const json::members &); + item() = default; + item(const std::type_index &, const json::members &); item(item &&) = delete; item(const item &) = delete; - ~item() noexcept; + virtual ~item() noexcept; }; -inline ircd::stats::item & -ircd::stats::item::operator--() +template<> +struct ircd::stats::item +:item { - --val; - return *this; -} + uint64_t *val {nullptr}; -inline ircd::stats::item & -ircd::stats::item::operator++() -{ - ++val; - return *this; -} + public: + operator const uint64_t &() const + { + assert(val); + return *val; + } -inline ircd::stats::item & -ircd::stats::item::operator=(const value_type &v) -& -{ - set(*this, v); - return *this; -} + operator uint64_t &() + { + assert(val); + return *val; + } -inline ircd::stats::item & -ircd::stats::item::operator-=(const value_type &v) -& -{ - dec(*this, v); - return *this; -} + item(uint64_t *const &, const json::members &); + item() = default; +}; -inline ircd::stats::item & -ircd::stats::item::operator+=(const value_type &v) -& +template<> +struct ircd::stats::item +:item { - inc(*this, v); - return *this; -} + uint32_t *val {nullptr}; -inline bool -ircd::stats::item::operator!() -const -{ - return !get(*this); -} + public: + operator const uint32_t &() const + { + assert(val); + return *val; + } -inline ircd::stats::item::operator -value_type &() -{ - return get(*this); -} + operator uint32_t &() + { + assert(val); + return *val; + } -inline ircd::stats::item::operator -const value_type &() -const -{ - return get(*this); -} + item(uint32_t *const &, const json::members &); + item() = default; +}; -inline ircd::stats::value_type & -ircd::stats::set(item &item, - const value_type &v) +template<> +struct ircd::stats::item +:item { - item.val = v; - return get(item); -} + uint16_t *val {nullptr}; -inline ircd::stats::value_type & -ircd::stats::dec(item &item, - const value_type &n) -{ - item.val -= n; - return get(item); -} + public: + operator const uint16_t &() const + { + assert(val); + return *val; + } -inline ircd::stats::value_type & -ircd::stats::inc(item &item, - const value_type &n) -{ - item.val += n; - return get(item); -} + operator uint16_t &() + { + assert(val); + return *val; + } -inline ircd::stats::value_type & -ircd::stats::get(item &item) -{ - return item.val; -} + item(uint16_t *const &, const json::members &); + item() = default; +}; -inline const ircd::stats::value_type & -ircd::stats::get(const item &item) +template<> +struct ircd::stats::item +:item { - return item.val; -} + uint64_t val {0}; + + public: + operator const uint64_t &() const noexcept + { + return val; + } + + operator uint64_t &() noexcept + { + return val; + } + + item(const json::members &); + item() = default; +}; + +template<> +struct ircd::stats::item +:item +{ + uint32_t val {0}; + + public: + operator const uint32_t &() const noexcept + { + return val; + } + + operator uint32_t &() noexcept + { + return val; + } + + item(const json::members &); + item() = default; +}; + +template<> +struct ircd::stats::item +:item +{ + uint16_t val {0}; + + public: + operator const uint16_t &() const noexcept + { + return val; + } + + operator uint16_t &() noexcept + { + return val; + } + + item(const json::members &); + item() = default; +}; diff --git a/ircd/stats.cc b/ircd/stats.cc index 1e55765f7..82b0983e1 100644 --- a/ircd/stats.cc +++ b/ircd/stats.cc @@ -8,14 +8,51 @@ // copyright notice and this permission notice is present in all copies. The // full license for this software is available in the LICENSE file. +decltype(ircd::stats::NAME_MAX_LEN) +ircd::stats::NAME_MAX_LEN +{ + 127 +}; + decltype(ircd::stats::items) ircd::stats::items {}; std::ostream & -ircd::stats::operator<<(std::ostream &s, const item &item) +ircd::stats::operator<<(std::ostream &s, + const item &item_) { - s << static_cast(item.val); + if(item_.type == typeid(uint64_t *)) + { + const auto &item + { + dynamic_cast &>(item_) + }; + + assert(item.val); + s << *item.val; + } + else if(item_.type == typeid(uint32_t *)) + { + const auto &item + { + dynamic_cast &>(item_) + }; + + assert(item.val); + s << *item.val; + } + else if(item_.type == typeid(uint16_t *)) + { + const auto &item + { + dynamic_cast &>(item_) + }; + + assert(item.val); + s << *item.val; + } + return s; } @@ -23,34 +60,32 @@ ircd::stats::operator<<(std::ostream &s, const item &item) // item // -decltype(ircd::stats::item::NAME_MAX_LEN) -ircd::stats::item::NAME_MAX_LEN -{ - 127 -}; - // // item::item // -ircd::stats::item::item(const json::members &opts) -:feature_ +ircd::stats::item::item(const std::type_index &type, + const json::members &opts) +:type { - opts + type } ,feature { - feature_ -} -,name -{ - unquote(feature.at("name")) -} -,val -{ - feature.get("default", 0L) + opts } { + const json::string name + { + this->operator[]("name") + }; + + if(!name) + throw error + { + "Stats item must have a 'name' string feature" + }; + if(name.size() > NAME_MAX_LEN) throw error { @@ -67,13 +102,141 @@ ircd::stats::item::item(const json::members &opts) }; } -ircd::stats::item::~item() +ircd::stats::item::~item() noexcept { + const json::string name + { + this->operator[]("name") + }; + if(name) { - const auto it{items.find(name)}; + const auto it + { + items.find(name) + }; + assert(data(it->first) == data(name)); items.erase(it); } } + +ircd::string_view +ircd::stats::item::operator[](const string_view &key) +const noexcept +{ + const json::object feature + { + this->feature + }; + + return feature[key]; +} + +// +// pointer-to-value items +// + +// +// item +// + +ircd::stats::item::item(uint64_t *const &val, + const json::members &feature) +:item +{ + typeid(uint64_t *), feature +} +,val +{ + val +} +{ +} + +// +// item +// + +ircd::stats::item::item(uint32_t *const &val, + const json::members &feature) +:item +{ + typeid(uint32_t *), feature +} +,val +{ + val +} +{ +} + +// +// item +// + +ircd::stats::item::item(uint16_t *const &val, + const json::members &feature) +:item +{ + typeid(uint16_t *), feature +} +,val +{ + val +} +{ +} + +// +// value-carrying items +// + +// +// item +// + +ircd::stats::item::item(const json::members &feature) +:item +{ + std::addressof(this->val), feature +} +,val +{ + 0UL +} +{ +} + +// +// item +// + +ircd::stats::item::item(const json::members &feature) +:item +{ + std::addressof(this->val), feature +} +,val +{ + 0U +} +{ +} + +// +// item +// + +ircd::stats::item::item(const json::members &feature) +:item +{ + std::addressof(this->val), feature +} +,val +{ + 0U +} +{ +}