lokinet/llarp/util/metrics_core.hpp

1226 lines
31 KiB
C++
Raw Normal View History

2019-02-27 21:46:23 +00:00
#ifndef LLARP_METRICS_CORE_HPP
#define LLARP_METRICS_CORE_HPP
#include <util/metrics_types.hpp>
#include <util/scheduler.hpp>
#include <util/stopwatch.hpp>
#include <util/threading.hpp>
#include <map>
#include <memory>
#include <vector>
namespace llarp
{
namespace metrics
{
2019-06-13 21:58:16 +00:00
inline void
packToTagsImpl(Tags &)
{
}
template < typename K, typename V, typename... Args >
void
packToTagsImpl(Tags &tags, const K &k, const V &v, const Args &... args)
{
static_assert(std::is_convertible< K, Tag >::value, "");
static_assert(std::is_convertible< V, TagValue >::value, "");
tags.emplace(k, v);
packToTagsImpl(tags, args...);
}
template < typename... Args >
Tags
packToTags(const Args &... args)
{
static_assert(sizeof...(args) % 2 == 0, "");
Tags tags;
packToTagsImpl(tags, args...);
return tags;
}
2019-06-04 19:18:10 +00:00
template < typename Type >
class Collector
2019-02-27 21:46:23 +00:00
{
public:
2019-06-13 21:58:16 +00:00
using RecordType = Record< Type >;
using TaggedRecordsType = TaggedRecords< Type >;
2019-02-27 21:46:23 +00:00
2019-06-04 19:18:10 +00:00
private:
2019-06-13 21:58:17 +00:00
TaggedRecordsType m_records GUARDED_BY(m_mutex);
2019-02-27 21:46:23 +00:00
mutable util::Mutex m_mutex;
2019-06-04 19:18:10 +00:00
Collector(const Collector &) = delete;
Collector &
operator=(const Collector &) = delete;
2019-02-27 21:46:23 +00:00
2019-06-13 21:58:16 +00:00
template < typename... Args >
RecordType &
fetch(Args... args) EXCLUSIVE_LOCKS_REQUIRED(m_mutex)
{
2019-06-13 21:58:17 +00:00
return m_records.data[packToTags(args...)];
2019-06-13 21:58:16 +00:00
}
2019-02-27 21:46:23 +00:00
public:
2019-06-13 21:58:17 +00:00
Collector(const Id &id) : m_records(id)
2019-02-27 21:46:23 +00:00
{
}
void
clear()
{
2019-06-13 21:58:16 +00:00
absl::MutexLock l(&m_mutex);
2019-06-13 21:58:17 +00:00
m_records.data.clear();
2019-02-27 21:46:23 +00:00
}
2019-06-13 21:58:17 +00:00
TaggedRecordsType
loadAndClear()
2019-02-27 21:46:23 +00:00
{
2019-06-13 21:58:16 +00:00
absl::MutexLock l(&m_mutex);
2019-06-13 21:58:17 +00:00
auto result = m_records;
m_records.data.clear();
2019-06-13 21:58:16 +00:00
return result;
2019-02-27 21:46:23 +00:00
}
2019-06-13 21:58:17 +00:00
TaggedRecordsType
load()
2019-02-27 21:46:23 +00:00
{
2019-06-13 21:58:16 +00:00
absl::MutexLock l(&m_mutex);
2019-06-13 21:58:17 +00:00
return m_records;
2019-02-27 21:46:23 +00:00
}
2019-06-13 21:58:16 +00:00
template < typename... Args >
2019-02-27 21:46:23 +00:00
void
2019-06-13 21:58:16 +00:00
tick(Type value, Args... args)
2019-02-27 21:46:23 +00:00
{
2019-06-13 21:58:16 +00:00
absl::MutexLock l(&m_mutex);
RecordType &rec = fetch(args...);
rec.count()++;
rec.total() += value;
rec.min() = std::min(rec.min(), value);
rec.max() = std::max(rec.max(), value);
2019-02-27 21:46:23 +00:00
}
2019-06-13 21:58:16 +00:00
template < typename... Args >
2019-02-27 21:46:23 +00:00
void
2019-06-13 21:58:16 +00:00
accumulate(size_t count, Type total, Type min, Type max, Args... args)
2019-02-27 21:46:23 +00:00
{
2019-06-13 21:58:16 +00:00
absl::MutexLock l(&m_mutex);
RecordType &rec = fetch(args...);
rec.count() += count;
rec.total() += total;
rec.min() = std::min(rec.min(), min);
rec.max() = std::max(rec.max(), max);
2019-02-27 21:46:23 +00:00
}
2019-06-13 21:58:16 +00:00
template < typename... Args >
2019-02-27 21:46:23 +00:00
void
2019-06-13 21:58:16 +00:00
set(size_t count, Type total, Type min, Type max, Args... args)
2019-02-27 21:46:23 +00:00
{
2019-06-13 21:58:16 +00:00
absl::MutexLock l(&m_mutex);
RecordType &rec = fetch(args...);
rec.count() = count;
rec.total() = total;
rec.min() = min;
rec.max() = max;
2019-02-27 21:46:23 +00:00
}
const Id &
id() const
{
2019-06-13 21:58:17 +00:00
return m_records.id;
2019-02-27 21:46:23 +00:00
}
};
2019-06-04 19:18:10 +00:00
using IntCollector = Collector< int >;
using DoubleCollector = Collector< double >;
2019-02-27 21:46:23 +00:00
class Publisher
{
public:
2019-06-04 19:18:10 +00:00
virtual ~Publisher() = default;
2019-02-27 21:46:23 +00:00
virtual void
2019-06-11 20:46:51 +00:00
publish(const Sample &sample) = 0;
2019-02-27 21:46:23 +00:00
};
2019-06-13 21:58:17 +00:00
template < typename Value >
2019-02-27 21:46:23 +00:00
static inline void
2019-06-13 21:58:17 +00:00
combine(TaggedRecords< Value > &records,
const TaggedRecords< Value > &toAdd)
2019-02-27 21:46:23 +00:00
{
2019-06-13 21:58:17 +00:00
records.id = toAdd.id;
for(auto &record : records.data)
{
auto it = toAdd.data.find(record.first);
if(it == toAdd.data.end())
{
continue;
}
record.second.count() += it->second.count();
record.second.total() += it->second.total();
record.second.min() = std::min(record.second.min(), it->second.min());
record.second.max() = std::max(record.second.max(), it->second.max());
}
for(const auto &record : toAdd.data)
{
auto it = records.data.find(record.first);
if(it != records.data.end())
{
continue;
}
records.data[record.first] = record.second;
}
2019-02-27 21:46:23 +00:00
}
2019-06-04 19:18:10 +00:00
template < typename Type >
2019-02-27 21:46:23 +00:00
class Collectors
{
2019-06-04 19:18:10 +00:00
using CollectorType = Collector< Type >;
using CollectorPtr = std::shared_ptr< CollectorType >;
using CollectorSet = std::set< CollectorPtr >;
2019-02-27 21:46:23 +00:00
2019-06-04 19:18:10 +00:00
CollectorType m_default;
2019-02-27 21:46:23 +00:00
CollectorSet m_collectors;
Collectors(const Collectors &) = delete;
Collectors &
operator=(const Collectors &) = delete;
public:
Collectors(const Id &id) : m_default(id)
{
}
2019-06-04 19:18:10 +00:00
CollectorType *
2019-02-27 21:46:23 +00:00
defaultCollector()
{
return &m_default;
}
2019-06-04 19:18:10 +00:00
std::shared_ptr< CollectorType >
2019-02-27 21:46:23 +00:00
add()
{
2019-06-04 19:18:10 +00:00
auto ptr = std::make_shared< CollectorType >(m_default.id());
2019-02-27 21:46:23 +00:00
m_collectors.insert(ptr);
return ptr;
}
bool
2019-06-04 19:18:10 +00:00
remove(CollectorType *collector)
2019-02-27 21:46:23 +00:00
{
2019-06-04 19:18:10 +00:00
std::shared_ptr< CollectorType > ptr(collector, [](CollectorType *) {});
2019-02-27 21:46:23 +00:00
size_t count = m_collectors.erase(ptr);
return count > 0;
}
2019-06-13 21:58:17 +00:00
TaggedRecords< Type >
2019-02-27 21:46:23 +00:00
combineAndClear()
{
2019-06-13 21:58:17 +00:00
TaggedRecords< Type > rec = m_default.loadAndClear();
2019-02-27 21:46:23 +00:00
for(auto &ptr : m_collectors)
{
metrics::combine(rec, ptr->loadAndClear());
}
return rec;
}
2019-06-13 21:58:17 +00:00
TaggedRecords< Type >
2019-02-27 21:46:23 +00:00
combine()
{
2019-06-13 21:58:17 +00:00
TaggedRecords< Type > rec = m_default.load();
2019-02-27 21:46:23 +00:00
for(auto &ptr : m_collectors)
{
metrics::combine(rec, ptr->load());
}
return rec;
}
2019-06-04 19:18:10 +00:00
std::vector< std::shared_ptr< CollectorType > >
2019-02-27 21:46:23 +00:00
collectors() const
{
2019-06-04 19:18:10 +00:00
return std::vector< std::shared_ptr< CollectorType > >(
m_collectors.begin(), m_collectors.end());
2019-02-27 21:46:23 +00:00
}
const Id &
id() const
{
return m_default.id();
}
};
2019-06-04 19:18:10 +00:00
using DoubleCollectors = Collectors< double >;
using IntCollectors = Collectors< int >;
2019-02-27 21:46:23 +00:00
class Registry
{
using NamedCategory = std::tuple< const char *, const char * >;
struct CmpNamedCategory
{
bool
operator()(const NamedCategory &lhs, const NamedCategory &rhs) const
{
int ret = std::strcmp(std::get< 0 >(lhs), std::get< 0 >(rhs));
if(ret == 0)
{
ret = std::strcmp(std::get< 1 >(lhs), std::get< 1 >(rhs));
}
return ret < 0;
}
};
struct StrCmp
{
bool
operator()(const char *lhs, const char *rhs) const
{
return std::strcmp(lhs, rhs) < 0;
}
};
using MetricMap = std::map< NamedCategory, std::shared_ptr< Description >,
CmpNamedCategory >;
using CategoryMap =
std::map< const char *, std::shared_ptr< Category >, StrCmp >;
std::set< std::string > m_stringmem GUARDED_BY(m_mutex);
CategoryMap m_categories GUARDED_BY(m_mutex);
MetricMap m_metrics GUARDED_BY(m_mutex);
bool m_defaultEnabled GUARDED_BY(m_mutex);
mutable util::Mutex m_mutex;
Registry(const Registry &) = delete;
Registry &
operator=(const Registry &) = delete;
std::tuple< Id, bool >
insert(const char *category, const char *name)
EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
public:
Registry() : m_defaultEnabled(true)
{
}
Id
add(const char *category, const char *name) LOCKS_EXCLUDED(m_mutex);
Id
get(const char *category, const char *name) LOCKS_EXCLUDED(m_mutex);
const Category *
add(const char *category) LOCKS_EXCLUDED(m_mutex);
const Category *
get(const char *category);
void
enable(const Category *category, bool value);
void
enableAll(bool value);
void
registerContainer(const Category *category, CategoryContainer &container);
void
publicationType(const Id &id, Publication::Type type);
void
setFormat(const Id &id, const Format &format);
size_t
metricCount() const
{
absl::ReaderMutexLock l(&m_mutex);
return m_metrics.size();
}
size_t
categoryCount() const
{
absl::ReaderMutexLock l(&m_mutex);
return m_categories.size();
}
const Category *
findCategory(const char *category) const;
Id
findId(const char *category, const char *name) const;
std::vector< const Category * >
getAll() const;
};
2019-06-13 21:58:17 +00:00
using DoubleRecords = std::vector< TaggedRecords< double > >;
using IntRecords = std::vector< TaggedRecords< int > >;
2019-06-11 20:46:51 +00:00
struct Records
{
DoubleRecords doubleRecords;
IntRecords intRecords;
Records()
{
}
Records(const DoubleRecords &d, const IntRecords &i)
: doubleRecords(d), intRecords(i)
{
}
};
inline bool
operator==(const Records &lhs, const Records &rhs)
{
return std::tie(lhs.doubleRecords, lhs.intRecords)
== std::tie(rhs.doubleRecords, rhs.intRecords);
}
template < typename Type >
2019-02-27 21:46:23 +00:00
class CollectorRepo
{
2019-06-11 20:46:51 +00:00
using CollectorsPtr = std::shared_ptr< Collectors< Type > >;
using IdCollectors = std::map< Id, CollectorsPtr >;
2019-02-27 21:46:23 +00:00
using CategoryCollectors =
2019-06-11 20:46:51 +00:00
std::map< const Category *, std::vector< Collectors< Type > * > >;
2019-02-27 21:46:23 +00:00
Registry *m_registry;
IdCollectors m_collectors;
CategoryCollectors m_categories;
2019-06-11 20:46:51 +00:00
2019-02-27 21:46:23 +00:00
mutable util::Mutex m_mutex;
CollectorRepo(const CollectorRepo &) = delete;
CollectorRepo &
operator=(const CollectorRepo &) = delete;
2019-06-11 20:46:51 +00:00
Collectors< Type > &
getCollectors(const Id &id)
2019-02-27 21:46:23 +00:00
{
2019-06-11 20:46:51 +00:00
auto it = m_collectors.find(id);
2019-02-27 21:46:23 +00:00
2019-06-11 20:46:51 +00:00
if(it == m_collectors.end())
{
assert(id.valid());
2019-02-27 21:46:23 +00:00
2019-06-11 20:46:51 +00:00
const Category *cat = id.category();
2019-02-27 21:46:23 +00:00
2019-06-11 20:46:51 +00:00
auto ptr = std::make_shared< Collectors< Type > >(id);
auto &vec = m_categories[cat];
vec.reserve(vec.size() + 1);
it = m_collectors.emplace(id, ptr).first;
vec.push_back(ptr.get());
}
return *it->second.get();
}
2019-06-13 21:58:17 +00:00
template < TaggedRecords< Type > (Collectors< Type >::*func)() >
std::vector< TaggedRecords< Type > >
2019-06-11 20:46:51 +00:00
collectOp(const Category *category)
2019-02-27 21:46:23 +00:00
{
2019-06-11 20:46:51 +00:00
absl::WriterMutexLock l(&m_mutex);
auto it = m_categories.find(category);
if(it == m_categories.end())
{
return {};
}
2019-06-13 21:58:17 +00:00
std::vector< TaggedRecords< Type > > result;
2019-06-11 20:46:51 +00:00
auto &collectors = it->second;
result.reserve(collectors.size());
std::transform(collectors.begin(), collectors.end(),
std::back_inserter(result),
[](auto &collector) { return (collector->*func)(); });
return result;
2019-02-27 21:46:23 +00:00
}
2019-06-11 20:46:51 +00:00
public:
explicit CollectorRepo(Registry *registry) : m_registry(registry)
{
}
2019-02-27 21:46:23 +00:00
2019-06-13 21:58:17 +00:00
std::vector< TaggedRecords< Type > >
2019-06-11 20:46:51 +00:00
collectAndClear(const Category *category)
2019-02-27 21:46:23 +00:00
{
2019-06-11 20:46:51 +00:00
return collectOp< &Collectors< Type >::combineAndClear >(category);
2019-02-27 21:46:23 +00:00
}
2019-06-13 21:58:17 +00:00
std::vector< TaggedRecords< Type > >
2019-06-11 20:46:51 +00:00
collect(const Category *category)
{
return collectOp< &Collectors< Type >::combine >(category);
}
2019-02-27 21:46:23 +00:00
2019-06-11 20:46:51 +00:00
Collector< Type > *
defaultCollector(const char *category, const char *name)
2019-02-27 21:46:23 +00:00
{
2019-06-11 20:46:51 +00:00
return defaultCollector(m_registry->get(category, name));
2019-02-27 21:46:23 +00:00
}
2019-06-11 20:46:51 +00:00
Collector< Type > *
defaultCollector(const Id &id)
2019-02-27 21:46:23 +00:00
{
2019-06-11 20:46:51 +00:00
{
absl::ReaderMutexLock l(&m_mutex);
auto it = m_collectors.find(id);
if(it != m_collectors.end())
{
return it->second->defaultCollector();
}
}
{
absl::WriterMutexLock l(&m_mutex);
return getCollectors(id).defaultCollector();
}
2019-02-27 21:46:23 +00:00
}
2019-06-11 20:46:51 +00:00
std::shared_ptr< Collector< Type > >
addCollector(const char *category, const char *name)
2019-02-27 21:46:23 +00:00
{
2019-06-11 20:46:51 +00:00
return addCollector(m_registry->get(category, name));
2019-02-27 21:46:23 +00:00
}
2019-06-11 20:46:51 +00:00
std::shared_ptr< Collector< Type > >
addCollector(const Id &id)
2019-02-27 21:46:23 +00:00
{
absl::WriterMutexLock l(&m_mutex);
2019-06-11 20:46:51 +00:00
return getCollectors(id).add();
2019-02-27 21:46:23 +00:00
}
2019-06-11 20:46:51 +00:00
std::vector< std::shared_ptr< Collector< Type > > >
allCollectors(const Id &id)
{
absl::ReaderMutexLock l(&m_mutex);
auto it = m_collectors.find(id);
if(it == m_collectors.end())
{
return {};
}
return it->second->collectors();
}
2019-02-27 21:46:23 +00:00
Registry &
registry()
{
return *m_registry;
}
const Registry &
registry() const
{
return *m_registry;
}
};
class Manager;
class PublisherRegistry
{
using PubPtr = std::shared_ptr< Publisher >;
using CatPublishers = std::multimap< const Category *, PubPtr >;
using PubSet = std::set< PubPtr >;
using PubReg = std::map< const Category *, CatPublishers::iterator >;
using RegInfo = std::map< PubPtr, PubReg >;
CatPublishers m_categoryPublishers;
RegInfo m_registry;
PubSet m_publishers;
PublisherRegistry(const PublisherRegistry &) = delete;
PublisherRegistry &
operator=(const PublisherRegistry &) = delete;
public:
using GlobalIterator = PubSet::iterator;
using CatIterator = CatPublishers::iterator;
PublisherRegistry() = default;
bool
addGlobalPublisher(const std::shared_ptr< Publisher > &publisher)
{
if(m_publishers.find(publisher) != m_publishers.end())
{
return false;
}
if(m_registry.find(publisher) != m_registry.end())
{
return false;
}
m_publishers.insert(publisher);
return true;
}
bool
addPublisher(const Category *category,
const std::shared_ptr< Publisher > &publisher)
{
if(m_publishers.find(publisher) != m_publishers.end())
{
return false;
}
auto &reg = m_registry[publisher];
if(reg.find(category) != reg.end())
{
return false;
}
auto it = m_categoryPublishers.emplace(category, publisher);
reg.emplace(category, it);
return true;
}
bool
removePublisher(const Publisher *publisher)
{
std::shared_ptr< Publisher > ptr(const_cast< Publisher * >(publisher),
[](Publisher *) {});
auto allIt = m_publishers.find(ptr);
if(allIt != m_publishers.end())
{
m_publishers.erase(allIt);
return true;
}
auto regIt = m_registry.find(ptr);
if(regIt == m_registry.end())
{
return false;
}
for(auto &spec : regIt->second)
{
m_categoryPublishers.erase(spec.second);
}
m_registry.erase(regIt);
return true;
}
GlobalIterator
globalBegin()
{
return m_publishers.begin();
}
GlobalIterator
globalEnd()
{
return m_publishers.end();
}
CatIterator
catBegin()
{
return m_categoryPublishers.begin();
}
CatIterator
catEnd()
{
return m_categoryPublishers.end();
}
CatIterator
lowerBound(const Category *category)
{
return m_categoryPublishers.lower_bound(category);
}
CatIterator
upperBound(const Category *category)
{
return m_categoryPublishers.upper_bound(category);
}
std::vector< Publisher * >
globalPublishers() const
{
std::vector< Publisher * > result;
result.reserve(m_publishers.size());
std::transform(m_publishers.begin(), m_publishers.end(),
std::back_inserter(result),
[](const auto &p) { return p.get(); });
return result;
}
std::vector< Publisher * >
catPublishers(const Category *category) const
{
std::vector< Publisher * > result;
auto beg = m_categoryPublishers.lower_bound(category);
auto end = m_categoryPublishers.upper_bound(category);
result.reserve(std::distance(beg, end));
std::transform(beg, end, std::back_inserter(result),
[](const auto &p) { return p.second.get(); });
return result;
}
};
2019-06-11 20:46:51 +00:00
struct PublisherHelper;
2019-02-27 21:46:23 +00:00
/// The big dog.
/// This class owns everything else, and is responsible for managing the
/// gathering and publishing of metrics
class Manager
{
public:
// Public callback. If the bool flag is true, clear the metrics back to
// their default state.
using Handle = uint64_t;
private:
// Map categories to the times they were last reset
using ResetTimes = std::map< const Category *, absl::Duration >;
friend struct PublisherHelper;
Registry m_registry;
2019-06-11 20:46:51 +00:00
CollectorRepo< double > m_doubleRepo;
CollectorRepo< int > m_intRepo;
2019-02-27 21:46:23 +00:00
PublisherRegistry m_publishers GUARDED_BY(m_mutex);
const absl::Duration m_createTime;
ResetTimes m_resetTimes;
util::Mutex m_publishLock ACQUIRED_BEFORE(m_mutex);
mutable util::Mutex m_mutex ACQUIRED_AFTER(m_publishLock);
public:
static constexpr Handle INVALID_HANDLE =
std::numeric_limits< Handle >::max();
Manager()
2019-06-11 20:46:51 +00:00
: m_doubleRepo(&m_registry)
, m_intRepo(&m_registry)
, m_createTime(absl::Now() - absl::UnixEpoch())
2019-02-27 21:46:23 +00:00
{
}
/// Add a `publisher` which will receive all events
bool
addGlobalPublisher(const std::shared_ptr< Publisher > &publisher)
{
absl::WriterMutexLock l(&m_mutex);
return m_publishers.addGlobalPublisher(publisher);
}
/// Add a `publisher` which will receive events for the given
/// `categoryName` only
bool
addPublisher(const char *categoryName,
const std::shared_ptr< Publisher > &publisher)
{
return addPublisher(m_registry.get(categoryName), publisher);
}
/// Add a `publisher` which will receive events for the given
/// `category` only
bool
addPublisher(const Category *category,
const std::shared_ptr< Publisher > &publisher)
{
absl::WriterMutexLock l(&m_mutex);
return m_publishers.addPublisher(category, publisher);
}
bool
removePublisher(const Publisher *publisher)
{
absl::WriterMutexLock l(&m_mutex);
return m_publishers.removePublisher(publisher);
}
bool
removePublisher(const std::shared_ptr< Publisher > &publisher)
{
absl::WriterMutexLock l(&m_mutex);
return m_publishers.removePublisher(publisher.get());
}
// clang-format off
2019-06-11 20:46:51 +00:00
CollectorRepo<double>& doubleCollectorRepo() { return m_doubleRepo; }
CollectorRepo<int>& intCollectorRepo() { return m_intRepo; }
2019-02-27 21:46:23 +00:00
Registry& registry() { return m_registry; }
const Registry& registry() const { return m_registry; }
// clang-format on
/// Publish specific categories of metric matching the category/categories
2019-06-11 20:46:51 +00:00
Sample
2019-06-11 20:46:51 +00:00
collectSample(Records &records, bool clear = false)
2019-02-27 21:46:23 +00:00
{
std::vector< const Category * > allCategories = m_registry.getAll();
return collectSample(
records, absl::Span< const Category * >{allCategories}, clear);
}
2019-06-11 20:46:51 +00:00
2019-06-11 20:46:51 +00:00
Sample
2019-06-11 20:46:51 +00:00
collectSample(Records &records, absl::Span< const Category * > categories,
2019-02-27 21:46:23 +00:00
bool clear = false);
/// Publish specific categories of metric matching the category/categories
void
publish(const Category *category, bool clear = true)
{
publish(absl::Span< const Category * >(&category, 1), clear);
}
void
publish(absl::Span< const Category * > categories, bool clear = true);
void
publish(const std::set< const Category * > &categories,
bool clear = true);
void
publishAll(bool clear = true)
{
std::vector< const Category * > allCategories = m_registry.getAll();
publish(absl::Span< const Category * >{allCategories}, clear);
}
void
publishAllExcluding(const std::set< const Category * > &excludeCategories,
bool clear = true)
{
if(excludeCategories.empty())
{
publishAll(clear);
return;
}
std::vector< const Category * > allCategories = m_registry.getAll();
std::vector< const Category * > includedCategories;
includedCategories.reserve(allCategories.size()
- excludeCategories.size());
std::copy_if(
allCategories.begin(), allCategories.end(),
std::back_inserter(includedCategories), [&](const Category *cat) {
return excludeCategories.end() == excludeCategories.find(cat);
});
if(!includedCategories.empty())
{
publish(absl::Span< const Category * >{includedCategories}, clear);
}
}
void
enableCategory(const char *categoryName, bool enable = true)
{
m_registry.enable(m_registry.get(categoryName), enable);
}
void
enableCategory(const Category *category, bool enable = true)
{
m_registry.enable(category, enable);
}
void
enableAll(bool enable)
{
m_registry.enableAll(enable);
}
std::vector< Publisher * >
globalPublishers() const
{
absl::ReaderMutexLock l(&m_mutex);
return m_publishers.globalPublishers();
}
std::vector< Publisher * >
publishersForCategory(const char *categoryName) const
{
const Category *category = m_registry.findCategory(categoryName);
return category ? publishersForCategory(category)
: std::vector< Publisher * >();
}
std::vector< Publisher * >
publishersForCategory(const Category *category) const
{
absl::ReaderMutexLock l(&m_mutex);
return m_publishers.catPublishers(category);
}
};
/// Provide a handy mechanism for retrieving the default manager, without
/// a painful singleton mechanism
class DefaultManager
{
static Manager *m_manager;
public:
static Manager *
instance()
{
return m_manager;
}
static Manager *
manager(Manager *value)
{
return value ? value : m_manager;
}
static Manager *
create()
{
m_manager = new Manager;
return m_manager;
}
static void
destroy()
{
delete m_manager;
m_manager = nullptr;
}
};
/// Scoped guard to manage the default manager
class DefaultManagerGuard
{
DefaultManagerGuard(const DefaultManagerGuard &) = delete;
DefaultManagerGuard &
operator=(const DefaultManagerGuard &) = delete;
public:
DefaultManagerGuard()
{
DefaultManager::create();
}
~DefaultManagerGuard()
{
DefaultManager::destroy();
}
Manager *
instance()
{
return DefaultManager::instance();
}
};
template < typename Collector, typename Value,
2019-06-11 20:46:51 +00:00
CollectorRepo< Value > &(Manager::*repoFunc)() >
2019-02-27 21:46:23 +00:00
class Metric
{
2019-06-13 21:58:17 +00:00
Collector *m_collector; // can be null
2019-02-27 21:46:23 +00:00
const std::atomic_bool *m_enabled;
public:
2019-06-13 21:58:17 +00:00
static Collector *
2019-02-27 21:46:23 +00:00
lookup(const char *category, const char *name, Manager *manager = nullptr)
{
manager = DefaultManager::manager(manager);
2019-06-11 20:46:51 +00:00
return manager ? (manager->*repoFunc)().defaultCollector(category, name)
2019-02-27 21:46:23 +00:00
: 0;
}
2019-06-13 21:58:17 +00:00
static Collector *
2019-02-27 21:46:23 +00:00
lookup(const Id &id, Manager *manager = nullptr)
{
manager = DefaultManager::manager(manager);
2019-06-11 20:46:51 +00:00
return manager ? (manager->*repoFunc)().defaultCollector(id) : 0;
2019-02-27 21:46:23 +00:00
}
Metric(const char *category, const char *name, Manager *manager)
: m_collector(lookup(category, name, manager))
, m_enabled(m_collector ? &m_collector->id().category()->enabledRaw()
: nullptr)
{
}
Metric(const Id &id, Manager *manager)
: m_collector(lookup(id, manager))
, m_enabled(m_collector ? &m_collector->id().category()->enabledRaw()
: nullptr)
{
}
2019-06-13 21:58:17 +00:00
Metric(Collector *collector)
2019-02-27 21:46:23 +00:00
: m_collector(collector)
, m_enabled(m_collector ? &m_collector->id().category()->enabledRaw()
: nullptr)
{
}
bool
active() const
{
return m_enabled ? m_enabled->load(std::memory_order_relaxed) : false;
}
void
tick()
{
if(active())
{
m_collector->tick(static_cast< Value >(1));
}
}
void
update(Value val)
{
if(active())
{
m_collector->tick(val);
}
}
void
accumulate(size_t count, Value total, Value min, Value max)
{
if(active())
{
m_collector->accumulate(count, total, min, max);
}
}
void
set(size_t count, Value total, Value min, Value max)
{
if(active())
{
m_collector->set(count, total, min, max);
}
}
Id
id() const
{
return m_collector ? m_collector->id() : Id();
}
2019-06-13 21:58:17 +00:00
const Collector *
2019-02-27 21:46:23 +00:00
collector() const
{
return m_collector;
}
2019-06-13 21:58:17 +00:00
Collector *
2019-02-27 21:46:23 +00:00
collector()
{
return m_collector;
}
static void
2019-06-13 21:58:17 +00:00
getCollector(Collector **collector, CategoryContainer *container,
2019-02-27 21:46:23 +00:00
const char *category, const char *metric)
{
Manager *manager = DefaultManager::instance();
2019-06-11 20:46:51 +00:00
*collector = (manager->*repoFunc().defaultCollector)(category, metric);
2019-02-27 21:46:23 +00:00
manager->registry().registerContainer((*collector)->id().category(),
container);
}
static void
2019-06-13 21:58:17 +00:00
getCollector(Collector **collector, CategoryContainer *container,
2019-02-27 21:46:23 +00:00
const char *category, const char *metric,
Publication::Type type)
{
Manager *manager = DefaultManager::instance();
2019-06-11 20:46:51 +00:00
*collector = (manager->*repoFunc().defaultCollector)(category, metric);
2019-02-27 21:46:23 +00:00
manager->registry().registerContainer((*collector)->id().category(),
container);
manager->registry().publicationType((*collector)->id(), type);
}
};
using DoubleMetric =
2019-06-11 20:46:51 +00:00
Metric< DoubleCollector, double, &Manager::doubleCollectorRepo >;
2019-02-27 21:46:23 +00:00
2019-06-11 20:46:51 +00:00
using IntMetric = Metric< IntCollector, int, &Manager::intCollectorRepo >;
2019-02-27 21:46:23 +00:00
class TimerGuard
{
private:
util::Stopwatch m_stopwatch;
DoubleCollector *m_collector;
TimerGuard(const TimerGuard &) = delete;
TimerGuard &
operator=(const TimerGuard &) = delete;
public:
TimerGuard(DoubleMetric *metric)
: m_stopwatch()
, m_collector(metric->active() ? metric->collector() : nullptr)
{
if(m_collector)
{
m_stopwatch.start();
}
}
TimerGuard(DoubleCollector *collector)
: m_stopwatch()
, m_collector(collector && collector->id().category()->enabled()
? collector
: nullptr)
{
if(m_collector)
{
m_stopwatch.start();
}
}
TimerGuard(const char *category, const char *name, Manager *manager)
: m_stopwatch(), m_collector(nullptr)
{
DoubleCollector *collector =
DoubleMetric::lookup(category, name, manager);
m_collector = (collector && collector->id().category()->enabled())
? collector
: nullptr;
if(m_collector)
{
m_stopwatch.start();
}
}
TimerGuard(const Id &id, Manager *manager)
: m_stopwatch(), m_collector(nullptr)
{
DoubleCollector *collector = DoubleMetric::lookup(id, manager);
m_collector = (collector && collector->id().category()->enabled())
? collector
: nullptr;
if(m_collector)
{
m_stopwatch.start();
}
}
~TimerGuard()
{
if(active())
{
m_stopwatch.stop();
m_collector->tick(absl::ToDoubleSeconds(m_stopwatch.time()));
}
}
bool
active() const
{
return m_collector ? m_collector->id().category()->enabled() : false;
}
};
struct PublisherSchedulerData;
class PublisherScheduler
{
friend class PublisherSchedulerGuard;
using Categories = std::map< const Category *, absl::Duration >;
using Repeaters =
std::map< absl::Duration, std::shared_ptr< PublisherSchedulerData > >;
thread::Scheduler &m_scheduler;
Manager *m_manager;
Categories m_categories GUARDED_BY(m_mutex);
Repeaters m_repeaters GUARDED_BY(m_mutex);
absl::Duration m_defaultInterval GUARDED_BY(m_mutex);
mutable util::Mutex m_mutex;
void
publish(const std::shared_ptr< PublisherSchedulerData > &data) const;
void
cancel(Categories::iterator it) EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
bool
cancelDefault() EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
public:
PublisherScheduler(thread::Scheduler &scheduler, Manager *manager)
: m_scheduler(scheduler), m_manager(manager), m_defaultInterval()
{
}
~PublisherScheduler()
{
cancelAll();
}
void
schedule(const char *categoryName, absl::Duration interval)
{
return schedule(m_manager->registry().get(categoryName), interval);
}
void
schedule(const Category *category, absl::Duration interval);
void
setDefault(absl::Duration interval);
bool
cancel(const char *categoryName)
{
return cancel(m_manager->registry().get(categoryName));
}
bool
cancel(const Category *category);
bool
clearDefault();
void
cancelAll();
Manager *
manager()
{
return m_manager;
}
const Manager *
manager() const
{
return m_manager;
}
absl::optional< absl::Duration >
find(const char *categoryName) const
{
return find(m_manager->registry().get(categoryName));
}
absl::optional< absl::Duration >
find(const Category *category) const;
absl::optional< absl::Duration >
getDefault() const;
std::vector< std::pair< const Category *, absl::Duration > >
getAll() const;
};
} // namespace metrics
} // namespace llarp
#endif