Merge remote-tracking branch 'origin/master'

pull/639/head
Jeff Becker 5 years ago
commit 584cc61f8f
No known key found for this signature in database
GPG Key ID: F357B3B42F6F9B05

@ -2,7 +2,7 @@
# copy a lokinet binary into this cluster
cp ../../lokinet .
# generate default config file
./lokinet -g lokinet.ini
./lokinet -g -r lokinet.ini
# make seed node
./makenode.sh 1
# establish bootstrap

@ -44,6 +44,7 @@ set(LIB_UTIL_SRC
util/timerqueue.cpp
util/traits.cpp
util/types.cpp
util/variant.cpp
)
add_library(${UTIL_LIB} STATIC ${LIB_UTIL_SRC})

@ -1,6 +1,7 @@
#include <metrics/metrictank_publisher.hpp>
#include <util/logger.hpp>
#include <util/variant.hpp>
#include <cstdio>
#include <absl/strings/str_cat.h>
@ -44,7 +45,20 @@ namespace llarp
}
absl::optional< std::string >
formatValue(const Record &record, double elapsedTime,
makeStr(int i)
{
if(i == std::numeric_limits< int >::min()
|| i == std::numeric_limits< int >::max())
{
return {};
}
return std::to_string(i);
}
template < typename Value >
absl::optional< std::string >
formatValue(const Record< Value > &record, double elapsedTime,
Publication::Type publicationType)
{
switch(publicationType)
@ -76,7 +90,8 @@ namespace llarp
break;
case Publication::Type::Avg:
{
return makeStr(record.total() / record.count());
return makeStr(static_cast< double >(record.total())
/ static_cast< double >(record.count()));
}
break;
case Publication::Type::Rate:
@ -100,9 +115,10 @@ namespace llarp
return absl::StrCat(id, ".", name, suffix);
}
template < typename Value >
std::vector< MetricTankPublisherInterface::PublishData >
recordToData(const Record &record, absl::Time time, double elapsedTime,
string_view suffix)
recordToData(const Record< Value > &record, absl::Time time,
double elapsedTime, string_view suffix)
{
std::vector< MetricTankPublisherInterface::PublishData > result;
@ -127,14 +143,14 @@ namespace llarp
result.emplace_back(addName(id, "total", suffix),
std::to_string(record.total()), time);
if(Record::DEFAULT_MIN != record.min() && !std::isnan(record.min())
&& !std::isinf(record.min()))
if(Record< Value >::DEFAULT_MIN() != record.min()
&& !std::isnan(record.min()) && !std::isinf(record.min()))
{
result.emplace_back(addName(id, "min", suffix),
std::to_string(record.min()), time);
}
if(Record::DEFAULT_MAX == record.max() && !std::isnan(record.max())
&& !std::isinf(record.max()))
if(Record< Value >::DEFAULT_MAX() == record.max()
&& !std::isnan(record.max()) && !std::isinf(record.max()))
{
result.emplace_back(addName(id, "max", suffix),
std::to_string(record.max()), time);
@ -197,6 +213,8 @@ namespace llarp
&& (static_cast< size_t >(sentLen) < val.size()));
}
LogInfo("Sent ", toSend.size(), " metrics to metrictank");
shutdown(sock, SHUT_RDWR);
close(sock);
}
@ -333,14 +351,17 @@ namespace llarp
auto prev = values.begin();
for(; gIt != values.end(); ++gIt)
{
const double elapsedTime = absl::ToDoubleSeconds(gIt->samplePeriod());
const double elapsedTime = absl::ToDoubleSeconds(samplePeriod(*gIt));
forSampleGroup(*gIt, [&](const auto &d) {
for(const auto &record : d)
{
auto partial =
recordToData(record, sampleTime, elapsedTime, m_suffix);
result.insert(result.end(), partial.begin(), partial.end());
}
});
for(const auto &record : *gIt)
{
auto partial =
recordToData(record, sampleTime, elapsedTime, m_suffix);
result.insert(result.end(), partial.begin(), partial.end());
}
prev = gIt;
}

@ -25,8 +25,9 @@ namespace llarp
}
}
template < typename Value >
void
formatValue(std::ostream &stream, const Record &record,
formatValue(std::ostream &stream, const Record< Value > &record,
double elapsedTime, Publication::Type publicationType,
const FormatSpec *formatSpec)
{
@ -75,8 +76,9 @@ namespace llarp
}
}
template < typename Value >
void
publishRecord(std::ostream &stream, const Record &record,
publishRecord(std::ostream &stream, const Record< Value > &record,
double elapsedTime)
{
auto publicationType = record.id().description()->type();
@ -111,7 +113,7 @@ namespace llarp
formatValue(stream, record.count(), countSpec);
stream << ", total = ";
formatValue(stream, record.total(), totalSpec);
if(Record::DEFAULT_MIN == record.min())
if(Record< Value >::DEFAULT_MIN() == record.min())
{
stream << ", min = undefined";
}
@ -120,7 +122,7 @@ namespace llarp
stream << ", min = ";
formatValue(stream, record.min(), minSpec);
}
if(Record::DEFAULT_MAX == record.max())
if(Record< Value >::DEFAULT_MAX() == record.max())
{
stream << ", max = undefined";
}
@ -133,8 +135,9 @@ namespace llarp
stream << " ]\n";
}
template < typename Value >
void
formatValue(nlohmann::json &result, const Record &record,
formatValue(nlohmann::json &result, const Record< Value > &record,
double elapsedTime, Publication::Type publicationType)
{
switch(publicationType)
@ -182,8 +185,9 @@ namespace llarp
}
}
template < typename Value >
nlohmann::json
recordToJson(const Record &record, double elapsedTime)
recordToJson(const Record< Value > &record, double elapsedTime)
{
nlohmann::json result;
result["id"] = record.id().toString();
@ -200,11 +204,11 @@ namespace llarp
result["count"] = record.count();
result["total"] = record.total();
if(Record::DEFAULT_MIN != record.min())
if(Record< Value >::DEFAULT_MIN() != record.min())
{
result["min"] = record.min();
}
if(Record::DEFAULT_MAX == record.max())
if(Record< Value >::DEFAULT_MAX() == record.max())
{
result["max"] = record.max();
}
@ -230,17 +234,20 @@ namespace llarp
auto prev = values.begin();
for(; gIt != values.end(); ++gIt)
{
const double elapsedTime = absl::ToDoubleSeconds(gIt->samplePeriod());
const double elapsedTime = absl::ToDoubleSeconds(samplePeriod(*gIt));
if(gIt == prev || gIt->samplePeriod() != prev->samplePeriod())
if(gIt == prev || samplePeriod(*gIt) != samplePeriod(*prev))
{
m_stream << "\tElapsed Time: " << elapsedTime << "s\n";
}
for(const auto &record : *gIt)
{
publishRecord(m_stream, record, elapsedTime);
}
forSampleGroup(*gIt, [&](const auto &x) {
for(const auto &record : x)
{
publishRecord(m_stream, record, elapsedTime);
}
});
prev = gIt;
}
}
@ -261,17 +268,20 @@ namespace llarp
auto prev = values.begin();
for(; gIt != values.end(); ++gIt)
{
const double elapsedTime = absl::ToDoubleSeconds(gIt->samplePeriod());
const double elapsedTime = absl::ToDoubleSeconds(samplePeriod(*gIt));
if(gIt == prev || gIt->samplePeriod() != prev->samplePeriod())
if(gIt == prev || samplePeriod(*gIt) != samplePeriod(*prev))
{
result["elapsedTime"] = elapsedTime;
}
for(const auto &record : *gIt)
{
result["record"].emplace_back(recordToJson(record, elapsedTime));
}
forSampleGroup(*gIt, [&](const auto &x) {
for(const auto &record : x)
{
result["record"].emplace_back(recordToJson(record, elapsedTime));
}
});
prev = gIt;
}

@ -41,14 +41,15 @@ namespace llarp
#define METRICS_UNIQUE_NAME(X) METRICS_NAME_CAT(X, METRICS_UNIQ_NUMBER)
#define METRICS_TIME_BLOCK_IMP(CAT, METRIC, VAR_NAME) \
llarp::metrics::DoubleCollector* VAR_NAME = nullptr; \
if(llarp::metrics::DefaultManager::instance()) \
{ \
using namespace llarp::metrics; \
CollectorRepo& repo = DefaultManager::instance()->collectorRepo(); \
VAR_NAME = repo.defaultDoubleCollector((CAT), (METRIC)); \
} \
#define METRICS_TIME_BLOCK_IMP(CAT, METRIC, VAR_NAME) \
llarp::metrics::DoubleCollector* VAR_NAME = nullptr; \
if(llarp::metrics::DefaultManager::instance()) \
{ \
using namespace llarp::metrics; \
CollectorRepo< double >& repo = \
DefaultManager::instance()->doubleCollectorRepo(); \
VAR_NAME = repo.defaultCollector((CAT), (METRIC)); \
} \
llarp::metrics::TimerGuard METRICS_UNIQUE_NAME(timer_guard)(VAR_NAME);
#define METRICS_TIME_BLOCK(CAT, METRIC) \
@ -66,20 +67,20 @@ namespace llarp
BALM_METRICS_IF_CATEGORY_ENABLED_IMP(CAT, METRICS_UNIQUE_NAME(Container))
// For when the category/metric may change during the program run
#define METRICS_DYNAMIC_INT_UPDATE(CAT, METRIC, VALUE) \
do \
{ \
using namespace llarp::metrics; \
if(DefaultManager::instance()) \
{ \
CollectorRepo& repository = DefaultManager::instance()->collectorRepo(); \
IntCollector* collector = \
repository.defaultIntCollector((CAT), (METRIC)); \
if(collector->id().category()->enabled()) \
{ \
collector->tick((VALUE)); \
} \
} \
#define METRICS_DYNAMIC_INT_UPDATE(CAT, METRIC, VALUE) \
do \
{ \
using namespace llarp::metrics; \
if(DefaultManager::instance()) \
{ \
CollectorRepo< int >& repository = \
DefaultManager::instance()->intCollectorRepo(); \
IntCollector* collector = repository.defaultCollector((CAT), (METRIC)); \
if(collector->id().category()->enabled()) \
{ \
collector->tick((VALUE)); \
} \
} \
} while(false)
// For when the category/metric remain static
@ -119,20 +120,21 @@ namespace llarp
} while(false)
// For when the category/metric may change during the program run
#define METRICS_DYNAMIC_UPDATE(CAT, METRIC, VALUE) \
do \
{ \
using namespace llarp::metrics; \
if(DefaultManager::instance()) \
{ \
CollectorRepo& repository = DefaultManager::instance()->collectorRepo(); \
DoubleCollector* collector = \
repository.defaultDoubleCollector((CAT), (METRIC)); \
if(collector->id().category()->enabled()) \
{ \
collector->tick((VALUE)); \
} \
} \
#define METRICS_DYNAMIC_UPDATE(CAT, METRIC, VALUE) \
do \
{ \
using namespace llarp::metrics; \
if(DefaultManager::instance()) \
{ \
CollectorRepo< double >& repository = \
DefaultManager::instance()->doubleCollectorRepo(); \
DoubleCollector* collector = \
repository.defaultCollector((CAT), (METRIC)); \
if(collector->id().category()->enabled()) \
{ \
collector->tick((VALUE)); \
} \
} \
} while(false)
// For when the category/metric remain static

@ -6,56 +6,8 @@ namespace llarp
{
namespace metrics
{
Record
IntCollector::loadAndClear()
{
size_t count;
uint64_t total;
int min;
int max;
{
absl::WriterMutexLock l(&m_mutex);
count = m_count;
total = m_total;
min = m_min;
max = m_max;
m_count = 0;
m_total = 0;
m_min = DEFAULT_MIN;
m_max = DEFAULT_MAX;
}
return {m_id, count, static_cast< double >(total),
(min == DEFAULT_MIN) ? Record::DEFAULT_MIN
: static_cast< double >(min),
(max == DEFAULT_MAX) ? Record::DEFAULT_MAX
: static_cast< double >(max)};
}
Record
IntCollector::load()
{
size_t count;
int64_t total;
int min;
int max;
{
absl::ReaderMutexLock l(&m_mutex);
count = m_count;
total = m_total;
min = m_min;
max = m_max;
}
return {m_id, count, static_cast< double >(total),
(min == DEFAULT_MIN) ? Record::DEFAULT_MIN : min,
(max == DEFAULT_MAX) ? Record::DEFAULT_MAX : max};
}
using DoubleRecords = std::vector< Record< double > >;
using IntRecords = std::vector< Record< int > >;
std::tuple< Id, bool >
Registry::insert(const char *category, const char *name)
@ -237,125 +189,6 @@ namespace llarp
return result;
}
MetricCollectors &
CollectorRepo::getCollectors(const Id &id)
{
auto it = m_collectors.find(id);
if(it == m_collectors.end())
{
assert(id.valid());
const Category *cat = id.category();
auto ptr = std::make_shared< MetricCollectors >(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();
}
std::vector< Record >
CollectorRepo::collectAndClear(const Category *category)
{
absl::WriterMutexLock l(&m_mutex);
std::vector< Record > result;
auto it = m_categories.find(category);
if(it != m_categories.end())
{
auto &collectors = it->second;
result.reserve(collectors.size());
std::transform(
collectors.begin(), collectors.end(), std::back_inserter(result),
[](MetricCollectors *c) { return c->combineAndClear(); });
}
return result;
}
std::vector< Record >
CollectorRepo::collect(const Category *category)
{
absl::WriterMutexLock l(&m_mutex);
std::vector< Record > result;
auto it = m_categories.find(category);
if(it != m_categories.end())
{
auto &collectors = it->second;
result.reserve(collectors.size());
std::transform(collectors.begin(), collectors.end(),
std::back_inserter(result),
[](MetricCollectors *c) { return c->combine(); });
}
return result;
}
DoubleCollector *
CollectorRepo::defaultDoubleCollector(const Id &id)
{
{
absl::ReaderMutexLock l(&m_mutex);
auto it = m_collectors.find(id);
if(it != m_collectors.end())
{
return it->second->doubleCollectors().defaultCollector();
}
}
{
absl::WriterMutexLock l(&m_mutex);
return getCollectors(id).doubleCollectors().defaultCollector();
}
}
IntCollector *
CollectorRepo::defaultIntCollector(const Id &id)
{
{
absl::ReaderMutexLock l(&m_mutex);
auto it = m_collectors.find(id);
if(it != m_collectors.end())
{
return it->second->intCollectors().defaultCollector();
}
}
{
absl::WriterMutexLock l(&m_mutex);
return getCollectors(id).intCollectors().defaultCollector();
}
}
std::pair< std::vector< std::shared_ptr< DoubleCollector > >,
std::vector< std::shared_ptr< IntCollector > > >
CollectorRepo::allCollectors(const Id &id)
{
absl::ReaderMutexLock l(&m_mutex);
auto it = m_collectors.find(id);
if(it == m_collectors.end())
{
return {};
}
return {it->second->doubleCollectors().collectors(),
it->second->intCollectors().collectors()};
}
struct PublisherHelper
{
using SampleCache = std::map< std::shared_ptr< Publisher >, Sample >;
@ -363,49 +196,38 @@ namespace llarp
static void
updateSampleCache(SampleCache &cache,
const std::shared_ptr< Publisher > &publisher,
const SampleGroup &sampleGroup,
const SampleGroup< double > &doubleGroup,
const SampleGroup< int > &intGroup,
const absl::Time &timeStamp)
{
SampleCache::iterator it = cache.find(publisher);
if(it == cache.end())
{
Sample newSample;
newSample.sampleTime(timeStamp);
it = cache.emplace(publisher, newSample).first;
Sample sample;
sample.sampleTime(timeStamp);
it = cache.emplace(publisher, sample).first;
}
it->second.pushGroup(sampleGroup);
it->second.pushGroup(doubleGroup);
it->second.pushGroup(intGroup);
}
static std::pair< std::vector< Record >, absl::Duration >
struct CollectResult
{
Records records;
absl::Duration samplePeriod;
};
static CollectResult
collect(Manager &manager, const Category *category,
const absl::Duration &now, bool clear)
EXCLUSIVE_LOCKS_REQUIRED(manager.m_mutex)
{
using Callback = Manager::RecordCallback;
using CallbackVector = std::vector< const Callback * >;
using RegistryIterator = CallbackRegistry::iterator;
CallbackVector callbacks;
RegistryIterator begin = manager.m_callbacks.lowerBound(category);
RegistryIterator end = manager.m_callbacks.upperBound(category);
std::vector< Record > result;
std::for_each(begin, end, [&](const auto &x) {
std::vector< Record > tmp = (x.second)(clear);
result.insert(result.end(), tmp.begin(), tmp.end());
});
// Collect records from the repo.
if(clear)
{
std::vector< Record > tmp = manager.m_repo.collectAndClear(category);
result.insert(result.end(), tmp.begin(), tmp.end());
}
else
{
std::vector< Record > tmp = manager.m_repo.collect(category);
result.insert(result.end(), tmp.begin(), tmp.end());
}
const Records result = clear
? Records(manager.m_doubleRepo.collectAndClear(category),
manager.m_intRepo.collectAndClear(category))
: Records(manager.m_doubleRepo.collect(category),
manager.m_intRepo.collect(category));
// Get the time since last reset, and clear if needed.
Manager::ResetTimes::iterator it = manager.m_resetTimes.find(category);
@ -428,6 +250,10 @@ namespace llarp
}
}
template < typename Type >
using RecordBuffer =
std::vector< std::shared_ptr< std::vector< Record< Type > > > >;
template < typename CategoryIterator >
static void
publish(Manager &manager, const CategoryIterator &categoriesBegin,
@ -437,10 +263,9 @@ namespace llarp
{
return;
}
using RecordBuffer =
std::vector< std::shared_ptr< std::vector< Record > > >;
RecordBuffer recordBuffer;
RecordBuffer< double > doubleRecordBuffer;
RecordBuffer< int > intRecordBuffer;
SampleCache sampleCache;
@ -462,40 +287,47 @@ namespace llarp
continue;
}
// Collect the metrics.
auto result = collect(manager, *catIt, now, clear);
auto result = collect(manager, *catIt, now, clear);
const auto &records = result.records;
// If their are no collected records then this category can be
// If there are no collected records then this category can be
// ignored.
if(result.first.empty())
if(records.doubleRecords.empty() && records.intRecords.empty())
{
continue;
}
if(result.second == absl::Duration())
if(result.samplePeriod == absl::Duration())
{
std::cerr << "Invalid elapsed time interval of 0 for "
"published metrics.";
result.second += absl::Nanoseconds(1);
result.samplePeriod += absl::Nanoseconds(1);
}
// Append the collected records to the buffer of records.
auto records =
std::make_shared< std::vector< Record > >(result.first);
recordBuffer.push_back(records);
SampleGroup sampleGroup(absl::Span< Record >(*records),
result.second);
std::for_each(
manager.m_publishers.globalBegin(),
manager.m_publishers.globalEnd(), [&](const auto &ptr) {
updateSampleCache(sampleCache, ptr, sampleGroup, timeStamp);
});
auto dRecords =
std::make_shared< DoubleRecords >(records.doubleRecords);
doubleRecordBuffer.push_back(dRecords);
SampleGroup< double > doubleGroup(
absl::Span< Record< double > >(*dRecords), result.samplePeriod);
auto iRecords = std::make_shared< IntRecords >(records.intRecords);
intRecordBuffer.push_back(iRecords);
SampleGroup< int > intGroup(absl::Span< Record< int > >(*iRecords),
result.samplePeriod);
std::for_each(manager.m_publishers.globalBegin(),
manager.m_publishers.globalEnd(),
[&](const auto &ptr) {
updateSampleCache(sampleCache, ptr, doubleGroup,
intGroup, timeStamp);
});
std::for_each(manager.m_publishers.lowerBound(*catIt),
manager.m_publishers.upperBound(*catIt),
[&](const auto &val) {
updateSampleCache(sampleCache, val.second,
sampleGroup, timeStamp);
doubleGroup, intGroup, timeStamp);
});
}
}
@ -503,15 +335,14 @@ namespace llarp
for(auto &entry : sampleCache)
{
Publisher *publisher = entry.first.get();
Sample &sample = entry.second;
publisher->publish(sample);
publisher->publish(entry.second);
}
}
};
Sample
Manager::collectSample(std::vector< Record > &records,
Manager::collectSample(Records &records,
absl::Span< const Category * > categories,
bool clear)
{
@ -523,8 +354,10 @@ namespace llarp
// Use a tuple to hold 'references' to the collected records
using SampleDescription = std::tuple< size_t, size_t, absl::Duration >;
std::vector< SampleDescription > samples;
samples.reserve(categories.size());
std::vector< SampleDescription > dSamples;
std::vector< SampleDescription > iSamples;
dSamples.reserve(categories.size());
iSamples.reserve(categories.size());
// 1
absl::WriterMutexLock publishGuard(&m_publishLock);
@ -538,29 +371,46 @@ namespace llarp
continue;
}
size_t beginIndex = records.size();
size_t dBeginIndex = records.doubleRecords.size();
size_t iBeginIndex = records.intRecords.size();
// Collect the metrics.
std::vector< Record > catRecords;
absl::Duration elapsedTime;
std::tie(catRecords, elapsedTime) =
PublisherHelper::collect(*this, category, now, clear);
records.insert(records.end(), catRecords.begin(), catRecords.end());
auto collectRes = PublisherHelper::collect(*this, category, now, clear);
DoubleRecords catDRecords = collectRes.records.doubleRecords;
IntRecords catIRecords = collectRes.records.intRecords;
absl::Duration elapsedTime = collectRes.samplePeriod;
size_t size = records.size() - beginIndex;
records.doubleRecords.insert(records.doubleRecords.end(),
catDRecords.begin(), catDRecords.end());
records.intRecords.insert(records.intRecords.end(), catIRecords.begin(),
catIRecords.end());
size_t dSize = records.doubleRecords.size() - dBeginIndex;
size_t iSize = records.intRecords.size() - iBeginIndex;
// If there are no collected records then this category can be ignored.
if(size != 0)
if(dSize != 0)
{
dSamples.emplace_back(dBeginIndex, dSize, elapsedTime);
}
if(iSize != 0)
{
samples.emplace_back(beginIndex, size, elapsedTime);
iSamples.emplace_back(iBeginIndex, iSize, elapsedTime);
}
}
// Now that we have all the records, we can build our sample
for(const SampleDescription &s : samples)
for(const SampleDescription &s : dSamples)
{
sample.pushGroup(&records.doubleRecords[std::get< 0 >(s)],
std::get< 1 >(s), std::get< 2 >(s));
}
for(const SampleDescription &s : iSamples)
{
sample.pushGroup(&records[std::get< 0 >(s)], std::get< 1 >(s),
std::get< 2 >(s));
sample.pushGroup(&records.intRecords[std::get< 0 >(s)],
std::get< 1 >(s), std::get< 2 >(s));
}
return sample;

@ -14,96 +14,22 @@ namespace llarp
{
namespace metrics
{
class IntCollector
template < typename Type >
class Collector
{
const Id m_id;
size_t m_count GUARDED_BY(m_mutex);
int64_t m_total GUARDED_BY(m_mutex);
int m_min GUARDED_BY(m_mutex);
int m_max GUARDED_BY(m_mutex);
mutable util::Mutex m_mutex;
IntCollector(const IntCollector &) = delete;
IntCollector &
operator=(const IntCollector &) = delete;
public:
static constexpr int DEFAULT_MIN = std::numeric_limits< int >::max();
static constexpr int DEFAULT_MAX = std::numeric_limits< int >::min();
IntCollector(const Id &id)
: m_id(id)
, m_count(0)
, m_total(0)
, m_min(DEFAULT_MIN)
, m_max(DEFAULT_MAX)
{
}
const Id &
id() const
{
return m_id;
}
void
clear()
{
absl::WriterMutexLock l(&m_mutex);
m_count = 0;
m_total = 0;
m_min = DEFAULT_MIN;
m_max = DEFAULT_MAX;
}
Record
loadAndClear();
Record
load();
void
tick(int value)
{
absl::WriterMutexLock l(&m_mutex);
m_count++;
m_total += value;
m_min = std::min(m_min, value);
m_max = std::max(m_max, value);
}
using RecordType = Record< Type >;
void
accumulate(size_t count, int total, int min, int max)
{
absl::WriterMutexLock l(&m_mutex);
m_count += count;
m_total += total;
m_min = std::min(m_min, min);
m_max = std::max(m_max, max);
}
void
set(size_t count, int total, int min, int max)
{
absl::WriterMutexLock l(&m_mutex);
m_count = count;
m_total = total;
m_min = min;
m_max = max;
}
};
class DoubleCollector
{
Record m_record GUARDED_BY(m_mutex);
private:
RecordType m_record GUARDED_BY(m_mutex);
mutable util::Mutex m_mutex;
DoubleCollector(const DoubleCollector &) = delete;
DoubleCollector &
operator=(const DoubleCollector &) = delete;
Collector(const Collector &) = delete;
Collector &
operator=(const Collector &) = delete;
public:
DoubleCollector(const Id &id) : m_record(id)
Collector(const Id &id) : m_record(id)
{
}
@ -111,26 +37,20 @@ namespace llarp
clear()
{
absl::WriterMutexLock l(&m_mutex);
m_record.count() = 0;
m_record.total() = 0.0;
m_record.min() = Record::DEFAULT_MIN;
m_record.max() = Record::DEFAULT_MAX;
m_record.clear();
}
Record
RecordType
loadAndClear()
{
absl::WriterMutexLock l(&m_mutex);
Record rec = m_record;
m_record.count() = 0;
m_record.total() = 0.0;
m_record.min() = Record::DEFAULT_MIN;
m_record.max() = Record::DEFAULT_MAX;
RecordType rec = m_record;
m_record.clear();
return rec;
}
Record
RecordType
load()
{
absl::ReaderMutexLock l(&m_mutex);
@ -138,7 +58,7 @@ namespace llarp
}
void
tick(double value)
tick(Type value)
{
absl::WriterMutexLock l(&m_mutex);
m_record.count()++;
@ -148,7 +68,7 @@ namespace llarp
}
void
accumulate(size_t count, double total, double min, double max)
accumulate(size_t count, Type total, Type min, Type max)
{
absl::WriterMutexLock l(&m_mutex);
m_record.count() += count;
@ -158,7 +78,7 @@ namespace llarp
}
void
set(size_t count, double total, double min, double max)
set(size_t count, Type total, Type min, Type max)
{
absl::WriterMutexLock l(&m_mutex);
m_record.count() = count;
@ -175,36 +95,38 @@ namespace llarp
}
};
using IntCollector = Collector< int >;
using DoubleCollector = Collector< double >;
class Publisher
{
public:
virtual ~Publisher() = 0;
virtual ~Publisher() = default;
virtual void
publish(const Sample &sample) = 0;
};
inline Publisher::~Publisher()
{
}
template < typename LhsType, typename RhsType >
static inline void
combine(Record &record, const Record &toAdd)
combine(Record< LhsType > &record, const Record< RhsType > &toAdd)
{
static_assert(std::is_convertible< RhsType, LhsType >::value, "");
record.id() = toAdd.id();
record.count() += toAdd.count();
record.total() += toAdd.total();
record.min() = std::min(record.min(), toAdd.min());
record.max() = std::max(record.max(), toAdd.max());
record.min() = std::min(record.min(), LhsType(toAdd.min()));
record.max() = std::max(record.max(), LhsType(toAdd.max()));
}
template < typename Collector >
template < typename Type >
class Collectors
{
using CollectorPtr = std::shared_ptr< Collector >;
using CollectorSet = std::set< CollectorPtr >;
using CollectorType = Collector< Type >;
using CollectorPtr = std::shared_ptr< CollectorType >;
using CollectorSet = std::set< CollectorPtr >;
Collector m_default;
CollectorType m_default;
CollectorSet m_collectors;
Collectors(const Collectors &) = delete;
@ -216,32 +138,32 @@ namespace llarp
{
}
Collector *
CollectorType *
defaultCollector()
{
return &m_default;
}
std::shared_ptr< Collector >
std::shared_ptr< CollectorType >
add()
{
auto ptr = std::make_shared< Collector >(m_default.id());
auto ptr = std::make_shared< CollectorType >(m_default.id());
m_collectors.insert(ptr);
return ptr;
}
bool
remove(Collector *collector)
remove(CollectorType *collector)
{
std::shared_ptr< Collector > ptr(collector, [](Collector *) {});
std::shared_ptr< CollectorType > ptr(collector, [](CollectorType *) {});
size_t count = m_collectors.erase(ptr);
return count > 0;
}
Record
Record< Type >
combineAndClear()
{
Record rec = m_default.loadAndClear();
Record< Type > rec = m_default.loadAndClear();
for(auto &ptr : m_collectors)
{
@ -251,10 +173,10 @@ namespace llarp
return rec;
}
Record
Record< Type >
combine()
{
Record rec = m_default.load();
Record< Type > rec = m_default.load();
for(auto &ptr : m_collectors)
{
@ -263,11 +185,11 @@ namespace llarp
return rec;
}
std::vector< std::shared_ptr< Collector > >
std::vector< std::shared_ptr< CollectorType > >
collectors() const
{
return std::vector< std::shared_ptr< Collector > >(m_collectors.begin(),
m_collectors.end());
return std::vector< std::shared_ptr< CollectorType > >(
m_collectors.begin(), m_collectors.end());
}
const Id &
@ -277,70 +199,8 @@ namespace llarp
}
};
class MetricCollectors
{
using DoubleCollectors = Collectors< DoubleCollector >;
using IntCollectors = Collectors< IntCollector >;
DoubleCollectors m_doubleCollectors;
IntCollectors m_intCollectors;
MetricCollectors(const MetricCollectors &) = delete;
MetricCollectors &
operator=(const MetricCollectors &) = delete;
public:
MetricCollectors(const Id &id)
: m_doubleCollectors(id), m_intCollectors(id)
{
}
Collectors< DoubleCollector > &
doubleCollectors()
{
return m_doubleCollectors;
}
Collectors< IntCollector > &
intCollectors()
{
return m_intCollectors;
}
const Collectors< DoubleCollector > &
doubleCollectors() const
{
return m_doubleCollectors;
}
const Collectors< IntCollector > &
intCollectors() const
{
return m_intCollectors;
}
Record
combineAndClear()
{
Record res = m_doubleCollectors.combineAndClear();
metrics::combine(res, m_intCollectors.combineAndClear());
return res;
}
Record
combine()
{
Record res = m_doubleCollectors.combine();
metrics::combine(res, m_intCollectors.combine());
return res;
}
const Id &
id() const
{
return m_intCollectors.id();
}
};
using DoubleCollectors = Collectors< double >;
using IntCollectors = Collectors< int >;
class Registry
{
@ -436,83 +296,163 @@ namespace llarp
getAll() const;
};
using DoubleRecords = std::vector< Record< double > >;
using IntRecords = std::vector< Record< int > >;
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 >
class CollectorRepo
{
using MetricCollectorsPtr = std::shared_ptr< MetricCollectors >;
using IdCollectors = std::map< Id, MetricCollectorsPtr >;
using CollectorsPtr = std::shared_ptr< Collectors< Type > >;
using IdCollectors = std::map< Id, CollectorsPtr >;
using CategoryCollectors =
std::map< const Category *, std::vector< MetricCollectors * > >;
std::map< const Category *, std::vector< Collectors< Type > * > >;
Registry *m_registry;
IdCollectors m_collectors;
CategoryCollectors m_categories;
mutable util::Mutex m_mutex;
CollectorRepo(const CollectorRepo &) = delete;
CollectorRepo &
operator=(const CollectorRepo &) = delete;
MetricCollectors &
getCollectors(const Id &id);
public:
CollectorRepo(Registry *registry) : m_registry(registry)
Collectors< Type > &
getCollectors(const Id &id)
{
}
auto it = m_collectors.find(id);
if(it == m_collectors.end())
{
assert(id.valid());
std::vector< Record >
collectAndClear(const Category *category);
const Category *cat = id.category();
std::vector< Record >
collect(const Category *category);
auto ptr = std::make_shared< Collectors< Type > >(id);
auto &vec = m_categories[cat];
vec.reserve(vec.size() + 1);
DoubleCollector *
defaultDoubleCollector(const char *category, const char *name)
it = m_collectors.emplace(id, ptr).first;
vec.push_back(ptr.get());
}
return *it->second.get();
}
template < Record< Type > (Collectors< Type >::*func)() >
std::vector< Record< Type > >
collectOp(const Category *category)
{
return defaultDoubleCollector(m_registry->get(category, name));
absl::WriterMutexLock l(&m_mutex);
auto it = m_categories.find(category);
if(it == m_categories.end())
{
return {};
}
std::vector< Record< Type > > result;
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;
}
DoubleCollector *
defaultDoubleCollector(const Id &id);
public:
explicit CollectorRepo(Registry *registry) : m_registry(registry)
{
}
IntCollector *
defaultIntCollector(const char *category, const char *name)
std::vector< Record< Type > >
collectAndClear(const Category *category)
{
return defaultIntCollector(m_registry->get(category, name));
return collectOp< &Collectors< Type >::combineAndClear >(category);
}
IntCollector *
defaultIntCollector(const Id &id);
std::vector< Record< Type > >
collect(const Category *category)
{
return collectOp< &Collectors< Type >::combine >(category);
}
std::shared_ptr< DoubleCollector >
addDoubleCollector(const char *category, const char *name)
Collector< Type > *
defaultCollector(const char *category, const char *name)
{
return addDoubleCollector(m_registry->get(category, name));
return defaultCollector(m_registry->get(category, name));
}
std::shared_ptr< DoubleCollector >
addDoubleCollector(const Id &id)
Collector< Type > *
defaultCollector(const Id &id)
{
absl::WriterMutexLock l(&m_mutex);
return getCollectors(id).doubleCollectors().add();
{
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();
}
}
std::shared_ptr< IntCollector >
addIntCollector(const char *category, const char *name)
std::shared_ptr< Collector< Type > >
addCollector(const char *category, const char *name)
{
return addIntCollector(m_registry->get(category, name));
return addCollector(m_registry->get(category, name));
}
std::shared_ptr< IntCollector >
addIntCollector(const Id &id)
std::shared_ptr< Collector< Type > >
addCollector(const Id &id)
{
absl::WriterMutexLock l(&m_mutex);
return getCollectors(id).intCollectors().add();
return getCollectors(id).add();
}
std::pair< std::vector< std::shared_ptr< DoubleCollector > >,
std::vector< std::shared_ptr< IntCollector > > >
allCollectors(const Id &id);
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();
}
Registry &
registry()
@ -677,91 +617,6 @@ namespace llarp
}
};
class CallbackRegistry
{
using Handle = uint64_t;
using RecordCallback = std::function< std::vector< Record >(bool) >;
using CallbackMap = std::multimap< const Category *, RecordCallback >;
using HandleMap = std::map< Handle, CallbackMap::iterator >;
Handle m_next;
CallbackMap m_callbackMap;
HandleMap m_handleMap;
CallbackRegistry(const CallbackRegistry &) = delete;
CallbackRegistry &
operator=(const CallbackRegistry &) = delete;
public:
using iterator = CallbackMap::iterator;
CallbackRegistry() : m_next(1)
{
}
Handle
registerCallback(const Category *category, const RecordCallback &callback)
{
Handle handle = m_next++;
auto it = m_callbackMap.emplace(category, callback);
m_handleMap.emplace(handle, it);
return handle;
}
bool
removeCallback(Handle handle)
{
auto it = m_handleMap.find(handle);
if(it == m_handleMap.end())
{
return false;
}
m_callbackMap.erase(it->second);
m_handleMap.erase(it);
return true;
}
iterator
begin()
{
return m_callbackMap.begin();
}
iterator
end()
{
return m_callbackMap.end();
}
iterator
lowerBound(const Category *category)
{
return m_callbackMap.lower_bound(category);
}
iterator
upperBound(const Category *category)
{
return m_callbackMap.upper_bound(category);
}
std::vector< const RecordCallback * >
callbacksFor(const Category *category) const
{
std::vector< const RecordCallback * > result;
auto beg = m_callbackMap.lower_bound(category);
auto end = m_callbackMap.upper_bound(category);
result.reserve(std::distance(beg, end));
std::transform(beg, end, std::back_inserter(result),
[](const auto &x) { return &x.second; });
return result;
}
};
struct PublisherHelper;
/// The big dog.
@ -772,8 +627,6 @@ namespace llarp
public:
// Public callback. If the bool flag is true, clear the metrics back to
// their default state.
using RecordCallback = std::function< std::vector< Record >(bool) >;
using Handle = uint64_t;
private:
@ -783,8 +636,8 @@ namespace llarp
friend struct PublisherHelper;
Registry m_registry;
CollectorRepo m_repo;
CallbackRegistry m_callbacks GUARDED_BY(m_mutex);
CollectorRepo< double > m_doubleRepo;
CollectorRepo< int > m_intRepo;
PublisherRegistry m_publishers GUARDED_BY(m_mutex);
const absl::Duration m_createTime;
@ -798,30 +651,12 @@ namespace llarp
std::numeric_limits< Handle >::max();
Manager()
: m_repo(&m_registry), m_createTime(absl::Now() - absl::UnixEpoch())
: m_doubleRepo(&m_registry)
, m_intRepo(&m_registry)
, m_createTime(absl::Now() - absl::UnixEpoch())
{
}
/// Register a callback for
Handle
registerCallback(const char *categoryName, const RecordCallback &callback)
{
return registerCallback(m_registry.get(categoryName), callback);
}
Handle
registerCallback(const Category *category, const RecordCallback &callback)
{
absl::WriterMutexLock l(&m_mutex);
return m_callbacks.registerCallback(category, callback);
}
bool
removeCallback(Handle handle)
{
absl::WriterMutexLock l(&m_mutex);
return m_callbacks.removeCallback(handle);
}
/// Add a `publisher` which will receive all events
bool
addGlobalPublisher(const std::shared_ptr< Publisher > &publisher)
@ -862,23 +697,23 @@ namespace llarp
}
// clang-format off
CollectorRepo& collectorRepo() { return m_repo; }
const CollectorRepo& collectorRepo() const { return m_repo; }
CollectorRepo<double>& doubleCollectorRepo() { return m_doubleRepo; }
CollectorRepo<int>& intCollectorRepo() { return m_intRepo; }
Registry& registry() { return m_registry; }
const Registry& registry() const { return m_registry; }
// clang-format on
/// Publish specific categories of metric matching the category/categories
Sample
collectSample(std::vector< Record > &records, bool clear = false)
collectSample(Records &records, bool clear = false)
{
std::vector< const Category * > allCategories = m_registry.getAll();
return collectSample(
records, absl::Span< const Category * >{allCategories}, clear);
}
Sample
collectSample(std::vector< Record > &records,
absl::Span< const Category * > categories,
collectSample(Records &records, absl::Span< const Category * > categories,
bool clear = false);
/// Publish specific categories of metric matching the category/categories
@ -1026,8 +861,7 @@ namespace llarp
};
template < typename Collector, typename Value,
Collector *(CollectorRepo::*catFunc)(const char *, const char *),
Collector *(CollectorRepo::*idFunc)(const Id &) >
CollectorRepo< Value > &(Manager::*repoFunc)() >
class Metric
{
Collector *m_collector; // can be null
@ -1038,7 +872,7 @@ namespace llarp
lookup(const char *category, const char *name, Manager *manager = nullptr)
{
manager = DefaultManager::manager(manager);
return manager ? (manager->collectorRepo().*catFunc)(category, name)
return manager ? (manager->*repoFunc)().defaultCollector(category, name)
: 0;
}
@ -1046,7 +880,7 @@ namespace llarp
lookup(const Id &id, Manager *manager = nullptr)
{
manager = DefaultManager::manager(manager);
return manager ? (manager->collectorRepo().*idFunc)(id) : 0;
return manager ? (manager->*repoFunc)().defaultCollector(id) : 0;
}
Metric(const char *category, const char *name, Manager *manager)
@ -1135,7 +969,7 @@ namespace llarp
const char *category, const char *metric)
{
Manager *manager = DefaultManager::instance();
*collector = manager->collectorRepo().*catFunc(category, metric);
*collector = (manager->*repoFunc().defaultCollector)(category, metric);
manager->registry().registerContainer((*collector)->id().category(),
container);
}
@ -1146,7 +980,7 @@ namespace llarp
Publication::Type type)
{
Manager *manager = DefaultManager::instance();
*collector = manager->collectorRepo().*catFunc(category, metric);
*collector = (manager->*repoFunc().defaultCollector)(category, metric);
manager->registry().registerContainer((*collector)->id().category(),
container);
manager->registry().publicationType((*collector)->id(), type);
@ -1154,12 +988,9 @@ namespace llarp
};
using DoubleMetric =
Metric< DoubleCollector, double, &CollectorRepo::defaultDoubleCollector,
&CollectorRepo::defaultDoubleCollector >;
Metric< DoubleCollector, double, &Manager::doubleCollectorRepo >;
using IntMetric =
Metric< IntCollector, int, &CollectorRepo::defaultIntCollector,
&CollectorRepo::defaultIntCollector >;
using IntMetric = Metric< IntCollector, int, &Manager::intCollectorRepo >;
class TimerGuard
{

@ -139,40 +139,6 @@ namespace llarp
return stream;
}
const double Record::DEFAULT_MIN = std::numeric_limits< double >::max() * 2;
const double Record::DEFAULT_MAX =
std::numeric_limits< double >::max() * -2;
std::ostream &
Record::print(std::ostream &stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printAttribute("id", m_id);
printer.printAttribute("count", m_count);
printer.printAttribute("total", m_total);
printer.printAttribute("min", m_min);
printer.printAttribute("max", m_max);
return stream;
}
std::ostream &
SampleGroup::print(std::ostream &stream, int level, int spaces) const
{
Printer::PrintFunction< absl::Duration > durationPrinter =
[](std::ostream &stream, const absl::Duration &duration, int,
int) -> std::ostream & {
stream << duration;
return stream;
};
Printer printer(stream, level, spaces);
printer.printAttribute("records", m_records);
printer.printForeignAttribute("samplePeriod", m_samplePeriod,
durationPrinter);
return stream;
}
} // namespace metrics
} // namespace llarp

@ -1,16 +1,20 @@
#ifndef LLARP_METRICS_TYPES_HPP
#define LLARP_METRICS_TYPES_HPP
#include <util/printer.hpp>
#include <util/string_view.hpp>
#include <util/threading.hpp>
#include <util/types.hpp>
#include <util/variant.hpp>
#include <absl/types/span.h>
#include <absl/types/optional.h>
#include <set>
#include <memory>
#include <absl/types/variant.h>
#include <cstring>
#include <iosfwd>
#include <memory>
#include <set>
#include <vector>
namespace llarp
{
@ -368,32 +372,55 @@ namespace llarp
return id.print(stream, -1, -1);
}
// clang-format off
// Forwarding class to specialise for metric types
template<typename Type>
struct RecordMax {
};
template<>
struct RecordMax<double> {
static constexpr double min() { return std::numeric_limits< double >::infinity(); }
static constexpr double max() { return -std::numeric_limits< double >::infinity(); }
};
template<>
struct RecordMax<int> {
static constexpr int min() { return std::numeric_limits< int >::max(); }
static constexpr int max() { return std::numeric_limits< int >::min(); }
};
// clang-format on
template < typename Type >
class Record
{
Id m_id;
size_t m_count;
double m_total;
double m_min;
double m_max;
Type m_total;
Type m_min;
Type m_max;
public:
static const double DEFAULT_MIN;
static const double DEFAULT_MAX;
// clang-format off
static constexpr Type DEFAULT_MIN() { return RecordMax<Type>::min(); };
static constexpr Type DEFAULT_MAX() { return RecordMax<Type>::max(); };
// clang-format on
Record()
: m_id()
, m_count(0)
, m_total(0.0)
, m_min(DEFAULT_MIN)
, m_max(DEFAULT_MAX)
, m_min(DEFAULT_MIN())
, m_max(DEFAULT_MAX())
{
}
explicit Record(const Id &id)
: m_id(id)
, m_count(0)
, m_total(0.0)
, m_min(DEFAULT_MIN)
, m_max(DEFAULT_MAX)
, m_total()
, m_min(DEFAULT_MIN())
, m_max(DEFAULT_MAX())
{
}
@ -409,53 +436,84 @@ namespace llarp
size_t count() const { return m_count; }
size_t& count() { return m_count; }
double total() const { return m_total; }
double& total() { return m_total; }
Type total() const { return m_total; }
Type& total() { return m_total; }
double min() const { return m_min; }
double& min() { return m_min; }
Type min() const { return m_min; }
Type& min() { return m_min; }
double max() const { return m_max; }
double& max() { return m_max; }
Type max() const { return m_max; }
Type& max() { return m_max; }
// clang-format on
void
clear()
{
m_count = 0;
m_total = 0;
m_min = DEFAULT_MIN();
m_max = DEFAULT_MAX();
}
std::ostream &
print(std::ostream &stream, int level, int spaces) const;
print(std::ostream &stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printAttribute("id", m_id);
printer.printAttribute("count", m_count);
printer.printAttribute("total", m_total);
printer.printAttribute("min", m_min);
printer.printAttribute("max", m_max);
return stream;
}
};
template < typename Type >
inline std::ostream &
operator<<(std::ostream &stream, const Record &rec)
operator<<(std::ostream &stream, const Record< Type > &rec)
{
return rec.print(stream, -1, -1);
}
template < typename Type >
inline bool
operator==(const Record &lhs, const Record &rhs)
operator==(const Record< Type > &lhs, const Record< Type > &rhs)
{
return (lhs.id() == rhs.id() && lhs.count() == rhs.count()
&& lhs.total() == rhs.total() && lhs.min() == rhs.min()
&& lhs.max() == rhs.max());
}
template < typename Type >
inline bool
operator!=(const Record< Type > &lhs, const Record< Type > &rhs)
{
return !(lhs == rhs);
}
template < typename Type >
class SampleGroup
{
absl::Span< const Record > m_records;
absl::Span< const Record< Type > > m_records;
absl::Duration m_samplePeriod;
public:
using const_iterator = absl::Span< const Record >::const_iterator;
using RecordType = Record< Type >;
using const_iterator =
typename absl::Span< const RecordType >::const_iterator;
SampleGroup() : m_records(), m_samplePeriod()
{
}
SampleGroup(const Record *records, size_t size,
SampleGroup(const RecordType *records, size_t size,
absl::Duration samplePeriod)
: m_records(records, size), m_samplePeriod(samplePeriod)
{
}
SampleGroup(const absl::Span< const Record > &records,
SampleGroup(const absl::Span< const RecordType > &records,
absl::Duration samplePeriod)
: m_records(records), m_samplePeriod(samplePeriod)
{
@ -465,8 +523,8 @@ namespace llarp
void samplePeriod(absl::Duration duration) { m_samplePeriod = duration; }
absl::Duration samplePeriod() const { return m_samplePeriod; }
void records(absl::Span<const Record> recs) { m_records = recs; }
absl::Span<const Record> records() const { return m_records; }
void records(absl::Span<const RecordType> recs) { m_records = recs; }
absl::Span<const RecordType> records() const { return m_records; }
bool empty() const { return m_records.empty(); }
size_t size() const { return m_records.size(); }
@ -476,17 +534,33 @@ namespace llarp
// clang-format on
std::ostream &
print(std::ostream &stream, int level, int spaces) const;
print(std::ostream &stream, int level, int spaces) const
{
Printer::PrintFunction< absl::Duration > durationPrinter =
[](std::ostream &stream, const absl::Duration &duration, int,
int) -> std::ostream & {
stream << duration;
return stream;
};
Printer printer(stream, level, spaces);
printer.printAttribute("records", m_records);
printer.printForeignAttribute("samplePeriod", m_samplePeriod,
durationPrinter);
return stream;
}
};
template < typename Type >
inline std::ostream &
operator<<(std::ostream &stream, const SampleGroup &group)
operator<<(std::ostream &stream, const SampleGroup< Type > &group)
{
return group.print(stream, -1, -1);
}
template < typename Type >
inline bool
operator==(const SampleGroup &lhs, const SampleGroup &rhs)
operator==(const SampleGroup< Type > &lhs, const SampleGroup< Type > &rhs)
{
return lhs.records() == rhs.records()
&& lhs.samplePeriod() == rhs.samplePeriod();
@ -495,11 +569,12 @@ namespace llarp
class Sample
{
absl::Time m_sampleTime;
std::vector< SampleGroup > m_samples;
std::vector< absl::variant< SampleGroup< double >, SampleGroup< int > > >
m_samples;
size_t m_recordCount;
public:
using const_iterator = std::vector< SampleGroup >::const_iterator;
using const_iterator = typename decltype(m_samples)::const_iterator;
Sample() : m_sampleTime(), m_recordCount(0)
{
@ -509,23 +584,26 @@ namespace llarp
void sampleTime(const absl::Time& time) { m_sampleTime = time; }
absl::Time sampleTime() const { return m_sampleTime; }
void pushGroup(const SampleGroup& group) {
template<typename Type>
void pushGroup(const SampleGroup<Type>& group) {
if (!group.empty()) {
m_samples.push_back(group);
m_samples.emplace_back(group);
m_recordCount += group.size();
}
}
void pushGroup(const Record *records, size_t size, absl::Duration duration) {
template<typename Type>
void pushGroup(const Record<Type> *records, size_t size, absl::Duration duration) {
if (size != 0) {
m_samples.emplace_back(records, size, duration);
m_samples.emplace_back(SampleGroup<Type>(records, size, duration));
m_recordCount += size;
}
}
void pushGroup(const absl::Span< const Record > &records,absl::Duration duration) {
template<typename Type>
void pushGroup(const absl::Span< const Record<Type> > &records,absl::Duration duration) {
if (!records.empty()) {
m_samples.emplace_back(records, duration);
m_samples.emplace_back(SampleGroup<Type>(records, duration));
m_recordCount += records.size();
}
}
@ -535,7 +613,7 @@ namespace llarp
m_recordCount = 0;
}
const SampleGroup& group(size_t index) {
const absl::variant<SampleGroup<double>, SampleGroup<int> >& group(size_t index) {
assert(index < m_samples.size());
return m_samples[index];
}
@ -547,6 +625,36 @@ namespace llarp
size_t recordCount() const { return m_recordCount; }
// clang-format on
};
template < typename T >
auto
forSampleGroup(
const absl::variant< SampleGroup< double >, SampleGroup< int > > &group,
const T &func)
-> decltype(func(std::declval< SampleGroup< double > >()))
{
return absl::visit(
util::overloaded(
[&](const SampleGroup< double > &d) { return func(d); },
[&](const SampleGroup< int > &i) { return func(i); }),
group);
}
inline absl::Duration
samplePeriod(
const absl::variant< SampleGroup< double >, SampleGroup< int > > &group)
{
return forSampleGroup(group,
[](const auto &x) { return x.samplePeriod(); });
}
inline size_t
sampleSize(
const absl::variant< SampleGroup< double >, SampleGroup< int > > &group)
{
return forSampleGroup(group, [](const auto &x) { return x.size(); });
}
} // namespace metrics
} // namespace llarp

@ -0,0 +1 @@
#include <util/variant.hpp>

@ -0,0 +1,39 @@
#ifndef LLARP_VARIANT_HPP
#define LLARP_VARIANT_HPP
#include <absl/types/variant.h>
namespace llarp
{
namespace util
{
template < typename... Ts >
struct _overloaded;
template < typename T, typename... Ts >
struct _overloaded<T, Ts...> : T, _overloaded<Ts...>
{
_overloaded(T&& t, Ts&&... ts) : T(t), _overloaded<Ts...>(std::forward<Ts>(ts)...) {}
using T::operator();
using _overloaded< Ts... >::operator();
};
template<typename T>
struct _overloaded<T> : T
{
_overloaded(T&& t) : T(t) {}
using T::operator();
};
template < typename... Ts >
constexpr auto overloaded(Ts&&... ts)->_overloaded< Ts... >
{
return _overloaded<Ts...>(std::forward<Ts>(ts)...);
}
} // namespace util
} // namespace llarp
#endif

@ -17,7 +17,7 @@ TEST(MetricsPublisher, StreamPublisher)
std::stringstream stream;
metrics::StreamPublisher myPublisher(stream);
std::vector< metrics::Record > records;
std::vector< metrics::Record< double > > records;
records.emplace_back(metricA, 5, 25.0, 6.0, 25.0);
records.emplace_back(metricB, 2, 7.0, 3.0, 11.0);

@ -41,19 +41,19 @@ TYPED_TEST_P(CollectorTest, Collector)
ASSERT_EQ(METRIC_A, collector1.id().description());
ASSERT_EQ(METRIC_B, collector2.id().description());
Record record1 = collector1.load();
typename TypeParam::RecordType record1 = collector1.load();
ASSERT_EQ(METRIC_A, record1.id().description());
ASSERT_EQ(0, record1.count());
ASSERT_EQ(0, record1.total());
ASSERT_EQ(Record::DEFAULT_MAX, record1.max());
ASSERT_EQ(Record::DEFAULT_MIN, record1.min());
ASSERT_EQ(TypeParam::RecordType::DEFAULT_MAX(), record1.max());
ASSERT_EQ(TypeParam::RecordType::DEFAULT_MIN(), record1.min());
Record record2 = collector2.load();
typename TypeParam::RecordType record2 = collector2.load();
ASSERT_EQ(METRIC_B, record2.id().description());
ASSERT_EQ(0, record2.count());
ASSERT_EQ(0, record2.total());
ASSERT_EQ(Record::DEFAULT_MIN, record2.min());
ASSERT_EQ(Record::DEFAULT_MAX, record2.max());
ASSERT_EQ(TypeParam::RecordType::DEFAULT_MIN(), record2.min());
ASSERT_EQ(TypeParam::RecordType::DEFAULT_MAX(), record2.max());
collector1.tick(1);
record1 = collector1.load();
@ -84,8 +84,8 @@ TYPED_TEST_P(CollectorTest, Collector)
ASSERT_EQ(METRIC_A, record1.id().description());
ASSERT_EQ(0, record1.count());
ASSERT_EQ(0, record1.total());
ASSERT_EQ(Record::DEFAULT_MIN, record1.min());
ASSERT_EQ(Record::DEFAULT_MAX, record1.max());
ASSERT_EQ(TypeParam::RecordType::DEFAULT_MIN(), record1.min());
ASSERT_EQ(TypeParam::RecordType::DEFAULT_MAX(), record1.max());
collector1.tick(3);
record1 = collector1.loadAndClear();
@ -99,8 +99,8 @@ TYPED_TEST_P(CollectorTest, Collector)
ASSERT_EQ(METRIC_A, record1.id().description());
ASSERT_EQ(0, record1.count());
ASSERT_EQ(0, record1.total());
ASSERT_EQ(Record::DEFAULT_MIN, record1.min());
ASSERT_EQ(Record::DEFAULT_MAX, record1.max());
ASSERT_EQ(TypeParam::RecordType::DEFAULT_MIN(), record1.min());
ASSERT_EQ(TypeParam::RecordType::DEFAULT_MAX(), record1.max());
}
REGISTER_TYPED_TEST_SUITE_P(CollectorTest, Collector);
@ -311,35 +311,27 @@ MATCHER_P5(RecordCatEq, category, count, total, min, max, "")
TEST(MetricsCore, RepoBasic)
{
Registry registry;
CollectorRepo repo(&registry);
CollectorRepo< double > repo(&registry);
DoubleCollector *collector1 = repo.defaultDoubleCollector("Test", "C1");
DoubleCollector *collector2 = repo.defaultDoubleCollector("Test", "C2");
IntCollector *intCollector1 = repo.defaultIntCollector("Test", "C3");
IntCollector *intCollector2 = repo.defaultIntCollector("Test", "C4");
DoubleCollector *collector1 = repo.defaultCollector("Test", "C1");
DoubleCollector *collector2 = repo.defaultCollector("Test", "C2");
ASSERT_NE(collector1, collector2);
ASSERT_EQ(collector1, repo.defaultDoubleCollector("Test", "C1"));
ASSERT_NE(intCollector1, intCollector2);
ASSERT_EQ(intCollector1, repo.defaultIntCollector("Test", "C3"));
ASSERT_EQ(collector1, repo.defaultCollector("Test", "C1"));
collector1->tick(1.0);
collector1->tick(2.0);
collector2->tick(4.0);
intCollector1->tick(5);
intCollector2->tick(6);
std::vector< Record > records = repo.collectAndClear(registry.get("Test"));
ASSERT_THAT(records, SizeIs(4));
std::vector< Record< double > > records =
repo.collectAndClear(registry.get("Test"));
EXPECT_THAT(records, SizeIs(2));
// clang-format off
ASSERT_THAT(
EXPECT_THAT(
records,
ElementsAre(
RecordEq("Test", "C1", 2u, 3, 1, 2),
RecordEq("Test", "C2", 1u, 4, 4, 4),
RecordEq("Test", "C3", 1u, 5, 5, 5),
RecordEq("Test", "C4", 1u, 6, 6, 6)
RecordEq("Test", "C2", 1u, 4, 4, 4)
)
);
// clang-format on
@ -359,7 +351,7 @@ TEST(MetricsCore, RepoCollect)
for(int i = 0; i < static_cast< int >(CATEGORIES.size()); ++i)
{
CollectorRepo repo(&registry);
CollectorRepo< int > repo(&registry);
for(int j = 0; j < static_cast< int >(CATEGORIES.size()); ++j)
{
@ -369,16 +361,13 @@ TEST(MetricsCore, RepoCollect)
Id metric = registry.get(CATEGORY, METRICS[k]);
for(int l = 0; l < NUM_COLS; ++l)
{
DoubleCollector *dCol = repo.addDoubleCollector(metric).get();
IntCollector *iCol = repo.addIntCollector(metric).get();
IntCollector *iCol = repo.addCollector(metric).get();
if(i == j)
{
dCol->set(k, 2 * k, -k, k);
iCol->set(k, 2 * k, -k, k);
}
else
{
dCol->set(100, 100, 100, 100);
iCol->set(100, 100, 100, 100);
}
}
@ -390,7 +379,7 @@ TEST(MetricsCore, RepoCollect)
const char *CATEGORY = CATEGORIES[i];
const Category *category = registry.get(CATEGORY);
std::vector< Record > records = repo.collect(category);
std::vector< Record< int > > records = repo.collect(category);
ASSERT_THAT(records, SizeIs(static_cast< int >(METRICS.size())));
// clang-format off
@ -398,8 +387,8 @@ TEST(MetricsCore, RepoCollect)
records,
UnorderedElementsAre(
RecordEq(CATEGORY, "A", 0u, 0, 0, 0),
RecordEq(CATEGORY, "B", 6u, 12, -1, 1),
RecordEq(CATEGORY, "C", 12u, 24, -2, 2)
RecordEq(CATEGORY, "B", 3u, 6, -1, 1),
RecordEq(CATEGORY, "C", 6u, 12, -2, 2)
)
);
// clang-format on
@ -409,16 +398,12 @@ TEST(MetricsCore, RepoCollect)
{
Id metric = registry.get(CATEGORY, METRICS[j]);
auto collectors = repo.allCollectors(metric);
const auto &doubleCols = collectors.first;
const auto &intCols = collectors.second;
for(int k = 0; k < static_cast< int >(doubleCols.size()); ++k)
auto collectors = repo.allCollectors(metric);
for(int k = 0; k < static_cast< int >(collectors.size()); ++k)
{
Record E(metric, j, 2 * j, -j, j);
Record record1 = doubleCols[k]->load();
Record record2 = intCols[k]->load();
ASSERT_EQ(record1, E);
ASSERT_EQ(record2, E);
Record< int > EI(metric, j, 2 * j, -j, j);
Record< int > record = collectors[k]->load();
ASSERT_EQ(record, EI);
}
}
}
@ -434,17 +419,13 @@ TEST(MetricsCore, RepoCollect)
for(int k = 0; k < static_cast< int >(METRICS.size()); ++k)
{
Id metric = registry.get(CATEGORY, METRICS[j]);
auto collectors = repo.allCollectors(metric);
const auto &doubleCols = collectors.first;
const auto &intCols = collectors.second;
Id metric = registry.get(CATEGORY, METRICS[j]);
auto collectors = repo.allCollectors(metric);
for(int l = 0; l < static_cast< int >(doubleCols.size()); ++l)
for(int l = 0; l < static_cast< int >(collectors.size()); ++l)
{
Record record1 = doubleCols[k]->load();
ASSERT_THAT(record1, RecordEq(metric, 100u, 100, 100, 100));
Record record2 = intCols[k]->load();
ASSERT_THAT(record2, RecordEq(metric, 100u, 100, 100, 100));
Record< int > record = collectors[k]->load();
ASSERT_THAT(record, RecordEq(metric, 100u, 100, 100, 100));
}
}
}
@ -460,16 +441,18 @@ MATCHER_P2(WithinWindow, expectedTime, window, "")
}
const Category *
firstCategory(const SampleGroup &group)
firstCategory(
const absl::variant< SampleGroup< double >, SampleGroup< int > > &g)
{
EXPECT_THAT(group, Not(IsEmpty()));
const Category *value = group.begin()->id().category();
for(const Record &record : group.records())
{
EXPECT_EQ(value, record.id().category());
}
return value;
return forSampleGroup(g, [](const auto &group) {
EXPECT_THAT(group, Not(IsEmpty()));
const Category *value = group.begin()->id().category();
for(const auto &record : group.records())
{
EXPECT_EQ(value, record.id().category());
}
return value;
});
}
TEST(MetricsCore, ManagerCollectSample1)
@ -481,39 +464,40 @@ TEST(MetricsCore, ManagerCollectSample1)
const int NUM_METRICS = sizeof(METRICS) / sizeof(*METRICS);
Manager manager;
CollectorRepo &rep = manager.collectorRepo();
CollectorRepo< double > &rep = manager.doubleCollectorRepo();
for(int i = 0; i < NUM_CATEGORIES; ++i)
{
for(int j = 0; j < NUM_METRICS; ++j)
{
rep.defaultDoubleCollector(CATEGORIES[i], METRICS[j])->tick(1);
rep.defaultCollector(CATEGORIES[i], METRICS[j])->tick(1);
}
}
absl::Time start = absl::Now();
std::this_thread::sleep_for(std::chrono::microseconds(100000));
std::vector< Record > records;
Records records;
Sample sample = manager.collectSample(records, false);
absl::Duration window = absl::Now() - start;
absl::Time now = absl::Now();
ASSERT_EQ(NUM_CATEGORIES * NUM_METRICS, records.size());
ASSERT_EQ(NUM_CATEGORIES * NUM_METRICS, records.doubleRecords.size());
ASSERT_EQ(NUM_CATEGORIES * NUM_METRICS, sample.recordCount());
ASSERT_EQ(NUM_CATEGORIES, sample.groupCount());
ASSERT_THAT(sample.sampleTime(), WithinWindow(now, absl::Milliseconds(10)));
for(size_t i = 0; i < sample.groupCount(); ++i)
{
const SampleGroup &group = sample.group(i);
const SampleGroup< double > &group =
absl::get< SampleGroup< double > >(sample.group(i));
ASSERT_EQ(NUM_METRICS, group.size());
ASSERT_THAT(group.samplePeriod(),
WithinWindow(window, absl::Milliseconds(10)))
<< group;
const char *name = group.records()[0].id().categoryName();
for(const Record &record : group.records())
for(const Record< double > &record : group.records())
{
ASSERT_THAT(record, RecordCatEq(name, 1u, 1, 1, 1));
}
@ -522,17 +506,17 @@ TEST(MetricsCore, ManagerCollectSample1)
{
for(size_t j = 0; j < NUM_METRICS; ++j)
{
DoubleCollector *col =
rep.defaultDoubleCollector(CATEGORIES[i], METRICS[j]);
Record record = col->load();
DoubleCollector *col = rep.defaultCollector(CATEGORIES[i], METRICS[j]);
Record< double > record = col->load();
ASSERT_THAT(record, RecordEq(1u, 1, 1, 1));
}
}
records.clear();
records.doubleRecords.clear();
records.intRecords.clear();
sample = manager.collectSample(records, true);
ASSERT_EQ(NUM_CATEGORIES * NUM_METRICS, records.size());
ASSERT_EQ(NUM_CATEGORIES * NUM_METRICS, records.doubleRecords.size());
ASSERT_EQ(NUM_CATEGORIES * NUM_METRICS, sample.recordCount());
ASSERT_EQ(NUM_CATEGORIES, sample.groupCount());
@ -540,10 +524,9 @@ TEST(MetricsCore, ManagerCollectSample1)
{
for(size_t j = 0; j < NUM_METRICS; ++j)
{
DoubleCollector *col =
rep.defaultDoubleCollector(CATEGORIES[i], METRICS[j]);
Record record = col->load();
ASSERT_EQ(Record(record.id()), record);
DoubleCollector *col = rep.defaultCollector(CATEGORIES[i], METRICS[j]);
Record< double > record = col->load();
ASSERT_EQ(Record< double >(record.id()), record);
}
}
}
@ -559,8 +542,8 @@ TEST(MetricsCore, ManagerCollectSample2)
Manager manager;
std::vector< const Category * > allCategories;
CollectorRepo &rep = manager.collectorRepo();
Registry &reg = manager.registry();
CollectorRepo< double > &rep = manager.doubleCollectorRepo();
Registry &reg = manager.registry();
for(size_t i = 0; i < NUM_CATEGORIES; ++i)
{
const Category *cat = reg.get(CATEGORIES[i]);
@ -575,8 +558,7 @@ TEST(MetricsCore, ManagerCollectSample2)
{
for(size_t j = 0; j < NUM_METRICS; ++j)
{
DoubleCollector *col =
rep.defaultDoubleCollector(CATEGORIES[i], METRICS[j]);
DoubleCollector *col = rep.defaultCollector(CATEGORIES[i], METRICS[j]);
col->clear();
col->tick(1);
}
@ -584,7 +566,7 @@ TEST(MetricsCore, ManagerCollectSample2)
// Test without a reset.
std::vector< const Category * > cats = combIt.currentCombo;
std::vector< Record > records;
Records records;
Sample sample = manager.collectSample(
records, absl::Span< const Category * >{cats}, false);
@ -608,13 +590,12 @@ TEST(MetricsCore, ManagerCollectSample2)
{
for(size_t j = 0; j < NUM_METRICS; ++j)
{
DoubleCollector *col =
rep.defaultDoubleCollector(CATEGORIES[i], METRICS[j]);
Record record = col->load();
DoubleCollector *col = rep.defaultCollector(CATEGORIES[i], METRICS[j]);
Record< double > record = col->load();
ASSERT_THAT(record, RecordEq(1u, 1, 1, 1));
}
}
std::vector< Record > records2;
Records records2;
// Test with a reset.
sample = manager.collectSample(records2,
@ -641,12 +622,11 @@ TEST(MetricsCore, ManagerCollectSample2)
{
for(size_t j = 0; j < NUM_METRICS; ++j)
{
DoubleCollector *col =
rep.defaultDoubleCollector(CATEGORIES[i], METRICS[j]);
Record record = col->load();
DoubleCollector *col = rep.defaultCollector(CATEGORIES[i], METRICS[j]);
Record< double > record = col->load();
if(combIt.includesElement(i))
{
ASSERT_EQ(Record(record.id()), record);
ASSERT_EQ(Record< double >(record.id()), record);
}
else
{
@ -661,8 +641,8 @@ TEST(MetricsCore, ManagerCollectSample2)
struct MockPublisher : public Publisher
{
std::atomic_int invocations;
std::vector< Record > recordBuffer;
std::vector< Record > sortedRecords;
std::vector< Record< double > > recordBuffer;
std::vector< Record< double > > sortedRecords;
Sample m_sample;
std::set< absl::Duration > times;
@ -686,17 +666,19 @@ struct MockPublisher : public Publisher
recordBuffer.reserve(sample.recordCount());
for(const auto &s : sample)
for(const auto &_s : sample)
{
auto git = s.begin();
ASSERT_TRUE(absl::holds_alternative< SampleGroup< double > >(_s));
const auto &s = absl::get< SampleGroup< double > >(_s);
auto git = s.begin();
ASSERT_NE(git, s.end());
recordBuffer.push_back(*git);
Record *head = &recordBuffer.back();
Record< double > *head = &recordBuffer.back();
for(++git; git != s.end(); ++git)
{
recordBuffer.push_back(*git);
}
m_sample.pushGroup(head, s.size(), s.samplePeriod());
m_sample.pushGroup(head, s.size(), samplePeriod(s));
times.insert(s.samplePeriod());
}
@ -719,7 +701,7 @@ struct MockPublisher : public Publisher
int
indexOf(const Id &id)
{
Record searchRecord(id);
Record< double > searchRecord(id);
auto it = std::lower_bound(
sortedRecords.begin(), sortedRecords.end(), searchRecord,
[](const auto &lhs, const auto &rhs) { return lhs.id() < rhs.id(); });
@ -820,8 +802,8 @@ TEST(MetricsCore, PublishAll)
const int NUM_METRICS = sizeof(METRICS) / sizeof(*METRICS);
Manager manager;
Registry &registry = manager.registry();
CollectorRepo &repository = manager.collectorRepo();
Registry &registry = manager.registry();
CollectorRepo< double > &repository = manager.doubleCollectorRepo();
auto globalPub = std::make_shared< MockPublisher >();
@ -844,7 +826,7 @@ TEST(MetricsCore, PublishAll)
for(int j = 0; j < NUM_METRICS; ++j)
{
DoubleCollector *col =
repository.defaultDoubleCollector(CATEGORIES[i], METRICS[j]);
repository.defaultCollector(CATEGORIES[i], METRICS[j]);
col->clear();
col->tick(1);
}

@ -8,6 +8,9 @@
using namespace llarp;
using namespace ::testing;
using RecordT = metrics::Record< double >;
using SampleGroupT = metrics::SampleGroup< double >;
struct MetricFormatSpecTestData
{
float m_scale;
@ -130,7 +133,7 @@ TEST(MetricsTypes, CatContainer)
TEST(MetricsTypes, Record)
{
metrics::Record r;
RecordT r;
ASSERT_GT(r.min(), r.max());
}
@ -146,12 +149,12 @@ TEST(MetricsTypes, Sample)
metrics::Id metricC(&descC);
absl::Time timeStamp = absl::Now();
metrics::Record recordA(metricA, 0, 0, 0, 0);
metrics::Record recordB(metricB, 1, 2, 3, 4);
metrics::Record recordC(metricC, 4, 3, 2, 1);
RecordT recordA(metricA, 0, 0, 0, 0);
RecordT recordB(metricB, 1, 2, 3, 4);
RecordT recordC(metricC, 4, 3, 2, 1);
metrics::Record buffer1[] = {recordA, recordB};
std::vector< metrics::Record > buffer2;
RecordT buffer1[] = {recordA, recordB};
std::vector< RecordT > buffer2;
buffer2.push_back(recordC);
metrics::Sample sample;
@ -163,17 +166,23 @@ TEST(MetricsTypes, Sample)
ASSERT_EQ(timeStamp, sample.sampleTime());
ASSERT_EQ(2u, sample.groupCount());
ASSERT_EQ(3u, sample.recordCount());
ASSERT_EQ(absl::Seconds(1), sample.group(0).samplePeriod());
ASSERT_EQ(buffer1, sample.group(0).records().data());
ASSERT_EQ(2, sample.group(0).size());
ASSERT_EQ(absl::Seconds(2), sample.group(1).samplePeriod());
ASSERT_EQ(buffer2.data(), sample.group(1).records().data());
ASSERT_EQ(1, sample.group(1).size());
ASSERT_TRUE(absl::holds_alternative< SampleGroupT >(sample.group(0)));
ASSERT_TRUE(absl::holds_alternative< SampleGroupT >(sample.group(1)));
const SampleGroupT s0 = absl::get< SampleGroupT >(sample.group(0));
const SampleGroupT s1 = absl::get< SampleGroupT >(sample.group(1));
ASSERT_EQ(absl::Seconds(1), s0.samplePeriod());
ASSERT_EQ(buffer1, s0.records().data());
ASSERT_EQ(2, s0.size());
ASSERT_EQ(absl::Seconds(2), s1.samplePeriod());
ASSERT_EQ(buffer2.data(), s1.records().data());
ASSERT_EQ(1, s1.size());
for(auto sampleIt = sample.begin(); sampleIt != sample.end(); ++sampleIt)
{
;
for(auto groupIt = sampleIt->begin(); groupIt != sampleIt->end(); ++groupIt)
const auto &s = absl::get< SampleGroupT >(*sampleIt);
for(auto groupIt = s.begin(); groupIt != s.end(); ++groupIt)
{
std::cout << *groupIt << std::endl;
}
@ -200,7 +209,7 @@ struct SampleTest
metrics::Id id_F;
metrics::Id id_G;
std::vector< metrics::Record > recordBuffer;
std::vector< RecordT > recordBuffer;
SampleTest()
: cat_A("A", true)
@ -231,17 +240,17 @@ struct SampleTest
}
};
std::pair< std::vector< metrics::SampleGroup >, size_t >
std::pair< std::vector< metrics::SampleGroup< double > >, size_t >
generate(const std::string &specification,
const std::vector< metrics::Record > &recordBuffer)
const std::vector< RecordT > &recordBuffer)
{
const char *c = specification.c_str();
std::vector< metrics::SampleGroup > groups;
std::vector< metrics::SampleGroup< double > > groups;
size_t size = 0;
const metrics::Record *head = recordBuffer.data();
const metrics::Record *current = head;
const RecordT *head = recordBuffer.data();
const RecordT *current = head;
while(*c)
{
int numRecords = *(c + 1) - '0';
@ -268,7 +277,7 @@ TEST_P(SampleTest, basics)
std::tie(timestamp, spec) = GetParam();
std::vector< metrics::SampleGroup > groups;
std::vector< metrics::SampleGroup< double > > groups;
size_t size;
std::tie(groups, size) = generate(spec, recordBuffer);
@ -286,7 +295,8 @@ TEST_P(SampleTest, basics)
ASSERT_EQ(size, sample.recordCount());
for(size_t j = 0; j < sample.groupCount(); ++j)
{
ASSERT_EQ(groups[j], sample.group(j));
ASSERT_EQ(groups[j],
absl::get< metrics::SampleGroup< double > >(sample.group(j)));
}
}
@ -297,7 +307,7 @@ TEST_P(SampleTest, append)
std::tie(timestamp, spec) = GetParam();
std::vector< metrics::SampleGroup > groups;
std::vector< metrics::SampleGroup< double > > groups;
size_t size;
std::tie(groups, size) = generate(spec, recordBuffer);
@ -316,7 +326,8 @@ TEST_P(SampleTest, append)
for(size_t j = 0; j < sample.groupCount(); ++j)
{
ASSERT_EQ(groups[j], sample.group(j));
ASSERT_EQ(groups[j],
absl::get< metrics::SampleGroup< double > >(sample.group(j)));
}
}

Loading…
Cancel
Save