0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-12-26 15:33:54 +01:00

ircd::stats: Refactor subsystem template hierarchy and approach. (#31)

This commit is contained in:
Jason Volk 2020-06-16 22:33:13 -07:00
parent e2654ddaed
commit c0d2fe20bb
3 changed files with 366 additions and 134 deletions

View file

@ -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<uint64_t> total_bytes_in;
static stats::item<uint64_t> total_bytes_out;
static stats::item<uint64_t> total_calls_in;
static stats::item<uint64_t> total_calls_out;
uint64_t id {++count};
ip::tcp::socket sd;

View file

@ -1,7 +1,7 @@
// Matrix Construct
// The Construct
//
// Copyright (C) Matrix Construct Developers, Authors & Contributors
// Copyright (C) 2016-2019 Jason Volk <jason@zemos.net>
// Copyright (C) The Construct Developers, Authors & Contributors
// Copyright (C) 2016-2020 Jason Volk <jason@zemos.net>
//
// 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<void> template class. To keep things
/// simple, the abstract instance holds a typeinfo provided by the derived
/// instance. User can then downcast the item<void> to a derived template.
///
/// There are two levels in the class hierarchy under the abstract root
/// item<void>. 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<string_view, item *> items;
// Abstract item
template<class T = void> struct item;
template<> struct item<void>;
const value_type &get(const item &);
value_type &get(item &);
// Pointer-to-value items
template<> struct item<uint64_t *>;
template<> struct item<uint32_t *>;
template<> struct item<uint16_t *>;
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<uint64_t>;
template<> struct item<uint32_t>;
template<> struct item<uint16_t>;
std::ostream &operator<<(std::ostream &, const item &);
extern const size_t NAME_MAX_LEN;
extern std::map<string_view, item<void> *> items;
std::ostream &operator<<(std::ostream &, const item<void> &);
}
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<void>
{
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<uint64_t *>
:item<void>
{
--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<uint32_t *>
:item<void>
{
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<uint16_t *>
:item<void>
{
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<uint64_t>
:item<uint64_t *>
{
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<uint32_t>
:item<uint32_t *>
{
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<uint16_t>
:item<uint16_t *>
{
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;
};

View file

@ -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<void> &item_)
{
s << static_cast<long long>(item.val);
if(item_.type == typeid(uint64_t *))
{
const auto &item
{
dynamic_cast<const stats::item<uint64_t *> &>(item_)
};
assert(item.val);
s << *item.val;
}
else if(item_.type == typeid(uint32_t *))
{
const auto &item
{
dynamic_cast<const stats::item<uint32_t *> &>(item_)
};
assert(item.val);
s << *item.val;
}
else if(item_.type == typeid(uint16_t *))
{
const auto &item
{
dynamic_cast<const stats::item<uint16_t *> &>(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<void>::item(const std::type_index &type,
const json::members &opts)
:type
{
opts
type
}
,feature
{
feature_
}
,name
{
unquote(feature.at("name"))
}
,val
{
feature.get<long>("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<void>::~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<void>::operator[](const string_view &key)
const noexcept
{
const json::object feature
{
this->feature
};
return feature[key];
}
//
// pointer-to-value items
//
//
// item<uint64_t *>
//
ircd::stats::item<uint64_t *>::item(uint64_t *const &val,
const json::members &feature)
:item<void>
{
typeid(uint64_t *), feature
}
,val
{
val
}
{
}
//
// item<uint32_t *>
//
ircd::stats::item<uint32_t *>::item(uint32_t *const &val,
const json::members &feature)
:item<void>
{
typeid(uint32_t *), feature
}
,val
{
val
}
{
}
//
// item<uint16_t *>
//
ircd::stats::item<uint16_t *>::item(uint16_t *const &val,
const json::members &feature)
:item<void>
{
typeid(uint16_t *), feature
}
,val
{
val
}
{
}
//
// value-carrying items
//
//
// item<uint64_t>
//
ircd::stats::item<uint64_t>::item(const json::members &feature)
:item<uint64_t *>
{
std::addressof(this->val), feature
}
,val
{
0UL
}
{
}
//
// item<uint32_t>
//
ircd::stats::item<uint32_t>::item(const json::members &feature)
:item<uint32_t *>
{
std::addressof(this->val), feature
}
,val
{
0U
}
{
}
//
// item<uint16_t>
//
ircd::stats::item<uint16_t>::item(const json::members &feature)
:item<uint16_t *>
{
std::addressof(this->val), feature
}
,val
{
0U
}
{
}