Introduce tags for metrics

pull/646/head
Michael 5 years ago
parent bb0c05a2ca
commit 7d4126407c
No known key found for this signature in database
GPG Key ID: 2D51757B47E2434C

@ -110,50 +110,88 @@ namespace llarp
}
std::string
addName(string_view id, string_view name, string_view suffix)
makeTagStr(const Tags &tags)
{
return absl::StrCat(id, ".", name, suffix);
std::string tagStr;
auto overloaded = util::overloaded(
[](const std::string &str) { return str; },
[](double d) { return std::to_string(d); },
[](const std::int64_t i) { return std::to_string(i); });
for(const auto &tag : tags)
{
absl::StrAppend(&tagStr, ";", tag.first, "=",
absl::visit(overloaded, tag.second));
}
return tagStr;
}
std::string
addName(string_view id, string_view name, const Tags &tags,
string_view suffix)
{
return absl::StrCat(id, ".", name, makeTagStr(tags), suffix);
}
constexpr bool
isValid(int val)
{
return val != std::numeric_limits< int >::min()
&& val != std::numeric_limits< int >::max();
}
constexpr bool
isValid(double val)
{
return Record< double >::DEFAULT_MIN() != val
&& Record< double >::DEFAULT_MAX() != val && !std::isnan(val)
&& !std::isinf(val);
}
template < typename Value >
std::vector< MetricTankPublisherInterface::PublishData >
recordToData(const Record< Value > &record, absl::Time time,
recordToData(const TaggedRecords< Value > &taggedRecords, absl::Time time,
double elapsedTime, string_view suffix)
{
std::vector< MetricTankPublisherInterface::PublishData > result;
std::string id = record.id().toString();
std::string id = taggedRecords.id.toString();
auto publicationType = record.id().description()->type();
if(publicationType != Publication::Type::Unspecified)
{
auto val = formatValue(record, elapsedTime, publicationType);
auto publicationType = taggedRecords.id.description()->type();
if(val)
{
result.emplace_back(
addName(id, Publication::repr(publicationType), suffix),
val.value(), time);
}
}
else
for(const auto &record : taggedRecords.data)
{
result.emplace_back(addName(id, "count", suffix),
std::to_string(record.count()), time);
result.emplace_back(addName(id, "total", suffix),
std::to_string(record.total()), time);
if(Record< Value >::DEFAULT_MIN() != record.min()
&& !std::isnan(record.min()) && !std::isinf(record.min()))
const auto &tags = record.first;
const auto &rec = record.second;
if(publicationType != Publication::Type::Unspecified)
{
result.emplace_back(addName(id, "min", suffix),
std::to_string(record.min()), time);
auto val = formatValue(rec, elapsedTime, publicationType);
if(val)
{
result.emplace_back(
addName(id, Publication::repr(publicationType), tags, suffix),
val.value(), time);
}
}
if(Record< Value >::DEFAULT_MAX() == record.max()
&& !std::isnan(record.max()) && !std::isinf(record.max()))
else
{
result.emplace_back(addName(id, "max", suffix),
std::to_string(record.max()), time);
result.emplace_back(addName(id, "count", tags, suffix),
std::to_string(rec.count()), time);
result.emplace_back(addName(id, "total", tags, suffix),
std::to_string(rec.total()), time);
if(isValid(rec.min()))
{
result.emplace_back(addName(id, "min", tags, suffix),
std::to_string(rec.min()), time);
}
if(isValid(rec.max()))
{
result.emplace_back(addName(id, "max", tags, suffix),
std::to_string(rec.max()), time);
}
}
}
return result;
@ -325,12 +363,7 @@ namespace llarp
std::string
MetricTankPublisherInterface::makeSuffix(const Tags &tags)
{
std::string result;
for(const auto &tag : updateTags(tags))
{
absl::StrAppend(&result, ";", tag.first, "=", tag.second);
}
return result;
return absl::StrJoin(updateTags(tags), ";", absl::PairFormatter("="));
}
void
@ -353,14 +386,16 @@ namespace llarp
{
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());
}
});
absl::visit(
[&](const auto &d) {
for(const auto &record : d)
{
auto partial =
recordToData(record, sampleTime, elapsedTime, m_suffix);
result.insert(result.end(), partial.begin(), partial.end());
}
},
*gIt);
prev = gIt;
}

@ -78,66 +78,100 @@ namespace llarp
template < typename Value >
void
publishRecord(std::ostream &stream, const Record< Value > &record,
publishRecord(std::ostream &stream,
const TaggedRecords< Value > &taggedRecords,
double elapsedTime)
{
auto publicationType = record.id().description()->type();
auto publicationType = taggedRecords.id.description()->type();
std::shared_ptr< const Format > format =
record.id().description()->format();
taggedRecords.id.description()->format();
stream << "\t\t" << record.id() << " [ ";
if(publicationType != Publication::Type::Unspecified)
if(taggedRecords.data.empty())
{
stream << Publication::repr(publicationType) << " = ";
const FormatSpec *formatSpec =
format ? format->specFor(publicationType) : nullptr;
formatValue(stream, record, elapsedTime, publicationType, formatSpec);
return;
}
else
stream << "\t\t" << taggedRecords.id << " [\n";
for(const auto &rec : taggedRecords.data)
{
const FormatSpec *countSpec = nullptr;
const FormatSpec *totalSpec = nullptr;
const FormatSpec *minSpec = nullptr;
const FormatSpec *maxSpec = nullptr;
stream << "\t\t\t";
const auto &tags = rec.first;
const auto &record = rec.second;
if(format)
{
countSpec = format->specFor(Publication::Type::Count);
totalSpec = format->specFor(Publication::Type::Total);
minSpec = format->specFor(Publication::Type::Min);
maxSpec = format->specFor(Publication::Type::Max);
}
stream << "count = ";
formatValue(stream, record.count(), countSpec);
stream << ", total = ";
formatValue(stream, record.total(), totalSpec);
if(Record< Value >::DEFAULT_MIN() == record.min())
{
stream << ", min = undefined";
}
else
{
stream << ", min = ";
formatValue(stream, record.min(), minSpec);
Printer printer(stream, -1, -1);
printer.printValue(tags);
}
if(Record< Value >::DEFAULT_MAX() == record.max())
stream << " ";
if(publicationType != Publication::Type::Unspecified)
{
stream << ", max = undefined";
stream << Publication::repr(publicationType) << " = ";
const FormatSpec *formatSpec =
format ? format->specFor(publicationType) : nullptr;
formatValue(stream, record, elapsedTime, publicationType,
formatSpec);
}
else
{
stream << ", max = ";
formatValue(stream, record.max(), maxSpec);
const FormatSpec *countSpec = nullptr;
const FormatSpec *totalSpec = nullptr;
const FormatSpec *minSpec = nullptr;
const FormatSpec *maxSpec = nullptr;
if(format)
{
countSpec = format->specFor(Publication::Type::Count);
totalSpec = format->specFor(Publication::Type::Total);
minSpec = format->specFor(Publication::Type::Min);
maxSpec = format->specFor(Publication::Type::Max);
}
stream << "count = ";
formatValue(stream, record.count(), countSpec);
stream << ", total = ";
formatValue(stream, record.total(), totalSpec);
if(Record< Value >::DEFAULT_MIN() == record.min())
{
stream << ", min = undefined";
}
else
{
stream << ", min = ";
formatValue(stream, record.min(), minSpec);
}
if(Record< Value >::DEFAULT_MAX() == record.max())
{
stream << ", max = undefined";
}
else
{
stream << ", max = ";
formatValue(stream, record.max(), maxSpec);
}
}
}
stream << " ]\n";
stream << "\n\t\t]\n";
}
nlohmann::json
tagsToJson(const Tags &tags)
{
nlohmann::json result;
std::for_each(tags.begin(), tags.end(), [&](const auto &tag) {
absl::visit([&](const auto &t) { result[tag.first] = t; },
tag.second);
});
return result;
}
template < typename Value >
void
formatValue(nlohmann::json &result, const Record< Value > &record,
nlohmann::json
formatValue(const Record< Value > &record, const Tags &tags,
double elapsedTime, Publication::Type publicationType)
{
switch(publicationType)
@ -149,37 +183,40 @@ namespace llarp
break;
case Publication::Type::Total:
{
result["total"] = record.total();
return {{"tags", tagsToJson(tags)}, {"total", record.total()}};
}
break;
case Publication::Type::Count:
{
result["count"] = record.count();
return {{"tags", tagsToJson(tags)}, {"count", record.count()}};
}
break;
case Publication::Type::Min:
{
result["min"] = record.min();
return {{"tags", tagsToJson(tags)}, {"min", record.min()}};
}
break;
case Publication::Type::Max:
{
result["max"] = record.max();
return {{"tags", tagsToJson(tags)}, {"max", record.max()}};
}
break;
case Publication::Type::Avg:
{
result["avg"] = record.total() / record.count();
return {{"tags", tagsToJson(tags)},
{"avg", record.total() / record.count()}};
}
break;
case Publication::Type::Rate:
{
result["rate"] = record.total() / elapsedTime;
return {{"tags", tagsToJson(tags)},
{"rate", record.total() / elapsedTime}};
}
break;
case Publication::Type::RateCount:
{
result["rateCount"] = record.count() / elapsedTime;
return {{"tags", tagsToJson(tags)},
{"rateCount", record.count() / elapsedTime}};
}
break;
}
@ -187,30 +224,41 @@ namespace llarp
template < typename Value >
nlohmann::json
recordToJson(const Record< Value > &record, double elapsedTime)
recordToJson(const TaggedRecords< Value > &taggedRecord,
double elapsedTime)
{
nlohmann::json result;
result["id"] = record.id().toString();
result["id"] = taggedRecord.id.toString();
auto publicationType = record.id().description()->type();
if(publicationType != Publication::Type::Unspecified)
{
result["publicationType"] = Publication::repr(publicationType);
auto publicationType = taggedRecord.id.description()->type();
formatValue(result, record, elapsedTime, publicationType);
}
else
for(const auto &rec : taggedRecord.data)
{
result["count"] = record.count();
result["total"] = record.total();
if(Record< Value >::DEFAULT_MIN() != record.min())
const auto &record = rec.second;
if(publicationType != Publication::Type::Unspecified)
{
result["min"] = record.min();
result["publicationType"] = Publication::repr(publicationType);
result["metrics"].push_back(
formatValue(record, rec.first, elapsedTime, publicationType));
}
if(Record< Value >::DEFAULT_MAX() == record.max())
else
{
result["max"] = record.max();
nlohmann::json tmp;
tmp["tags"] = tagsToJson(rec.first);
tmp["count"] = record.count();
tmp["total"] = record.total();
if(Record< Value >::DEFAULT_MIN() != record.min())
{
tmp["min"] = record.min();
}
if(Record< Value >::DEFAULT_MAX() == record.max())
{
tmp["max"] = record.max();
}
result["metrics"].push_back(tmp);
}
}
@ -241,12 +289,14 @@ namespace llarp
m_stream << "\tElapsed Time: " << elapsedTime << "s\n";
}
forSampleGroup(*gIt, [&](const auto &x) {
for(const auto &record : x)
{
publishRecord(m_stream, record, elapsedTime);
}
});
absl::visit(
[&](const auto &x) {
for(const auto &record : x)
{
publishRecord(m_stream, record, elapsedTime);
}
},
*gIt);
prev = gIt;
}
@ -275,12 +325,15 @@ namespace llarp
result["elapsedTime"] = elapsedTime;
}
forSampleGroup(*gIt, [&](const auto &x) {
for(const auto &record : x)
{
result["record"].emplace_back(recordToJson(record, elapsedTime));
}
});
absl::visit(
[&](const auto &x) -> void {
for(const auto &record : x)
{
result["record"].emplace_back(
recordToJson(record, elapsedTime));
}
},
*gIt);
prev = gIt;
}

@ -6,9 +6,6 @@ namespace llarp
{
namespace metrics
{
using DoubleRecords = std::vector< Record< double > >;
using IntRecords = std::vector< Record< int > >;
std::tuple< Id, bool >
Registry::insert(const char *category, const char *name)
{
@ -251,8 +248,8 @@ namespace llarp
}
template < typename Type >
using RecordBuffer =
std::vector< std::shared_ptr< std::vector< Record< Type > > > >;
using RecordBuffer = std::vector<
std::shared_ptr< std::vector< TaggedRecords< Type > > > >;
template < typename CategoryIterator >
static void
@ -309,12 +306,14 @@ namespace llarp
std::make_shared< DoubleRecords >(records.doubleRecords);
doubleRecordBuffer.push_back(dRecords);
SampleGroup< double > doubleGroup(
absl::Span< Record< double > >(*dRecords), result.samplePeriod);
absl::Span< const TaggedRecords< 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);
SampleGroup< int > intGroup(
absl::Span< const TaggedRecords< int > >(*iRecords),
result.samplePeriod);
std::for_each(manager.m_publishers.globalBegin(),
manager.m_publishers.globalEnd(),

@ -48,8 +48,7 @@ namespace llarp
using TaggedRecordsType = TaggedRecords< Type >;
private:
TaggedRecordsType m_record GUARDED_BY(m_mutex);
const Id m_id;
TaggedRecordsType m_records GUARDED_BY(m_mutex);
mutable util::Mutex m_mutex;
Collector(const Collector &) = delete;
@ -60,16 +59,11 @@ namespace llarp
RecordType &
fetch(Args... args) EXCLUSIVE_LOCKS_REQUIRED(m_mutex)
{
RecordType &rec = m_record[packToTags(args...)];
if(!rec.id().valid())
{
rec.id() = m_id;
}
return rec;
return m_records.data[packToTags(args...)];
}
public:
Collector(const Id &id) : m_id(id)
Collector(const Id &id) : m_records(id)
{
}
@ -77,28 +71,24 @@ namespace llarp
clear()
{
absl::MutexLock l(&m_mutex);
m_record.clear();
m_records.data.clear();
}
template < typename... Args >
RecordType
loadAndClear(Args... args)
TaggedRecordsType
loadAndClear()
{
absl::MutexLock l(&m_mutex);
RecordType &rec = fetch(args...);
RecordType result = rec;
rec.clear();
auto result = m_records;
m_records.data.clear();
return result;
}
template < typename... Args >
RecordType
load(Args... args)
TaggedRecordsType
load()
{
absl::MutexLock l(&m_mutex);
return fetch(args...);
return m_records;
}
template < typename... Args >
@ -140,7 +130,7 @@ namespace llarp
const Id &
id() const
{
return m_id;
return m_records.id;
}
};
@ -156,16 +146,36 @@ namespace llarp
publish(const Sample &sample) = 0;
};
template < typename LhsType, typename RhsType >
template < typename Value >
static inline void
combine(Record< LhsType > &record, const Record< RhsType > &toAdd)
combine(TaggedRecords< Value > &records,
const TaggedRecords< Value > &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(), LhsType(toAdd.min()));
record.max() = std::max(record.max(), LhsType(toAdd.max()));
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;
}
}
template < typename Type >
@ -209,10 +219,10 @@ namespace llarp
return count > 0;
}
Record< Type >
TaggedRecords< Type >
combineAndClear()
{
Record< Type > rec = m_default.loadAndClear();
TaggedRecords< Type > rec = m_default.loadAndClear();
for(auto &ptr : m_collectors)
{
@ -222,10 +232,10 @@ namespace llarp
return rec;
}
Record< Type >
TaggedRecords< Type >
combine()
{
Record< Type > rec = m_default.load();
TaggedRecords< Type > rec = m_default.load();
for(auto &ptr : m_collectors)
{
@ -345,8 +355,8 @@ namespace llarp
getAll() const;
};
using DoubleRecords = std::vector< Record< double > >;
using IntRecords = std::vector< Record< int > >;
using DoubleRecords = std::vector< TaggedRecords< double > >;
using IntRecords = std::vector< TaggedRecords< int > >;
struct Records
{
@ -410,8 +420,8 @@ namespace llarp
return *it->second.get();
}
template < Record< Type > (Collectors< Type >::*func)() >
std::vector< Record< Type > >
template < TaggedRecords< Type > (Collectors< Type >::*func)() >
std::vector< TaggedRecords< Type > >
collectOp(const Category *category)
{
absl::WriterMutexLock l(&m_mutex);
@ -423,7 +433,7 @@ namespace llarp
return {};
}
std::vector< Record< Type > > result;
std::vector< TaggedRecords< Type > > result;
auto &collectors = it->second;
result.reserve(collectors.size());
@ -439,13 +449,13 @@ namespace llarp
{
}
std::vector< Record< Type > >
std::vector< TaggedRecords< Type > >
collectAndClear(const Category *category)
{
return collectOp< &Collectors< Type >::combineAndClear >(category);
}
std::vector< Record< Type > >
std::vector< TaggedRecords< Type > >
collect(const Category *category)
{
return collectOp< &Collectors< Type >::combine >(category);
@ -913,11 +923,11 @@ namespace llarp
CollectorRepo< Value > &(Manager::*repoFunc)() >
class Metric
{
Collector< Value > *m_collector; // can be null
Collector *m_collector; // can be null
const std::atomic_bool *m_enabled;
public:
static Collector< Value > *
static Collector *
lookup(const char *category, const char *name, Manager *manager = nullptr)
{
manager = DefaultManager::manager(manager);
@ -925,7 +935,7 @@ namespace llarp
: 0;
}
static Collector< Value > *
static Collector *
lookup(const Id &id, Manager *manager = nullptr)
{
manager = DefaultManager::manager(manager);
@ -946,7 +956,7 @@ namespace llarp
{
}
Metric(Collector< Value > *collector)
Metric(Collector *collector)
: m_collector(collector)
, m_enabled(m_collector ? &m_collector->id().category()->enabledRaw()
: nullptr)
@ -1001,20 +1011,20 @@ namespace llarp
return m_collector ? m_collector->id() : Id();
}
const Collector< Value > *
const Collector *
collector() const
{
return m_collector;
}
Collector< Value > *
Collector *
collector()
{
return m_collector;
}
static void
getCollector(Collector< Value > **collector, CategoryContainer *container,
getCollector(Collector **collector, CategoryContainer *container,
const char *category, const char *metric)
{
Manager *manager = DefaultManager::instance();
@ -1024,7 +1034,7 @@ namespace llarp
}
static void
getCollector(Collector< Value > **collector, CategoryContainer *container,
getCollector(Collector **collector, CategoryContainer *container,
const char *category, const char *metric,
Publication::Type type)
{

@ -397,7 +397,6 @@ namespace llarp
template < typename Type >
class Record
{
Id m_id;
size_t m_count;
Type m_total;
Type m_min;
@ -410,32 +409,16 @@ namespace llarp
// clang-format on
Record()
: m_id()
, m_count(0)
, m_total(0.0)
, m_min(DEFAULT_MIN())
, m_max(DEFAULT_MAX())
: m_count(0), m_total(0.0), m_min(DEFAULT_MIN()), m_max(DEFAULT_MAX())
{
}
explicit Record(const Id &id)
: m_id(id)
, m_count(0)
, m_total()
, m_min(DEFAULT_MIN())
, m_max(DEFAULT_MAX())
{
}
Record(const Id &id, size_t count, double total, double min, double max)
: m_id(id), m_count(count), m_total(total), m_min(min), m_max(max)
Record(size_t count, double total, double min, double max)
: m_count(count), m_total(total), m_min(min), m_max(max)
{
}
// clang-format off
const Id& id() const { return m_id; }
Id& id() { return m_id; }
size_t count() const { return m_count; }
size_t& count() { return m_count; }
@ -462,7 +445,6 @@ namespace llarp
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);
@ -483,9 +465,8 @@ namespace llarp
inline bool
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());
return std::make_tuple(lhs.count(), lhs.total(), lhs.min(), lhs.max())
== std::make_tuple(rhs.count(), rhs.total(), rhs.min(), rhs.max());
}
template < typename Type >
@ -500,19 +481,62 @@ namespace llarp
using Tags = std::set< std::pair< Tag, TagValue > >;
template < typename Type >
using TaggedRecords = absl::flat_hash_map< Tags, Record< Type > >;
using TaggedRecordsData = absl::flat_hash_map< Tags, Record< Type > >;
template < typename Type >
class SampleGroup
struct TaggedRecords
{
absl::Span< const Record< Type > > m_records;
absl::Duration m_samplePeriod;
Id id;
TaggedRecordsData< Type > data;
explicit TaggedRecords(const Id &_id) : id(_id)
{
}
TaggedRecords(const Id &_id, const TaggedRecordsData< Type > &_data)
: id(_id), data(_data)
{
}
std::ostream &
print(std::ostream &stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printAttribute("id", id);
printer.printAttribute("data", data);
return stream;
}
};
template < typename Value >
bool
operator==(const TaggedRecords< Value > &lhs,
const TaggedRecords< Value > &rhs)
{
return std::tie(lhs.id, lhs.data) == std::tie(rhs.id, rhs.data);
}
template < typename Value >
std::ostream &
operator<<(std::ostream &stream, const TaggedRecords< Value > &rec)
{
return rec.print(stream, -1, -1);
}
template < typename Type >
class SampleGroup
{
public:
using RecordType = Record< Type >;
using RecordType = TaggedRecords< Type >;
using const_iterator =
typename absl::Span< const RecordType >::const_iterator;
private:
absl::Span< const RecordType > m_records;
absl::Duration m_samplePeriod;
public:
SampleGroup() : m_records(), m_samplePeriod()
{
}
@ -603,7 +627,7 @@ namespace llarp
}
template<typename Type>
void pushGroup(const Record<Type> *records, size_t size, absl::Duration duration) {
void pushGroup(const TaggedRecords< Type > *records, size_t size, absl::Duration duration) {
if (size != 0) {
m_samples.emplace_back(SampleGroup<Type>(records, size, duration));
m_recordCount += size;
@ -611,7 +635,7 @@ namespace llarp
}
template<typename Type>
void pushGroup(const absl::Span< const Record<Type> > &records,absl::Duration duration) {
void pushGroup(const absl::Span< const TaggedRecords< Type > > &records,absl::Duration duration) {
if (!records.empty()) {
m_samples.emplace_back(SampleGroup<Type>(records, duration));
m_recordCount += records.size();
@ -636,33 +660,18 @@ namespace llarp
// 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(); });
return absl::visit([](const auto &x) { return x.samplePeriod(); }, group);
}
inline size_t
sampleSize(
const absl::variant< SampleGroup< double >, SampleGroup< int > > &group)
{
return forSampleGroup(group, [](const auto &x) { return x.size(); });
return absl::visit([](const auto &x) { return x.size(); }, group);
}
} // namespace metrics
} // namespace llarp

@ -3,10 +3,12 @@
#include <util/string_view.hpp>
#include <util/traits.hpp>
#include <util/variant.hpp>
#include <absl/types/variant.h>
#include <functional>
#include <iostream>
#include <assert.h>
#include <cassert>
namespace llarp
{
@ -195,6 +197,11 @@ namespace llarp
printType(std::ostream& stream, const std::tuple< Types... >& value,
int level, int spaces, traits::select::Case<>);
template < typename... Types >
static void
printType(std::ostream& stream, const absl::variant< Types... >& value,
int level, int spaces, traits::select::Case<>);
// Default type
template < typename Type >
static void
@ -486,6 +493,17 @@ namespace llarp
[&](const auto& x) { print.printValue(x); });
}
template < typename... Types >
inline void
PrintHelper::printType(std::ostream& stream,
const absl::variant< Types... >& value, int level,
int spaces, traits::select::Case<>)
{
Printer print(stream, level, spaces);
absl::visit([&](const auto& x) { print.printValue(x); }, value);
}
template < typename Type >
inline void
PrintHelper::printType(std::ostream& stream, const Type& value, int level,

@ -4,25 +4,30 @@
#include <gmock/gmock.h>
using namespace llarp;
using namespace metrics;
TEST(MetricsPublisher, StreamPublisher)
{
metrics::Category myCategory("MyCategory");
metrics::Description descA(&myCategory, "MetricA");
metrics::Description descB(&myCategory, "MetricB");
Category myCategory("MyCategory");
Description descA(&myCategory, "MetricA");
Description descB(&myCategory, "MetricB");
metrics::Id metricA(&descA);
metrics::Id metricB(&descB);
Id metricA(&descA);
Id metricB(&descB);
std::stringstream stream;
metrics::StreamPublisher myPublisher(stream);
StreamPublisher myPublisher(stream);
std::vector< metrics::Record< double > > records;
std::vector< TaggedRecords< double > > records;
records.emplace_back(metricA, 5, 25.0, 6.0, 25.0);
records.emplace_back(metricB, 2, 7.0, 3.0, 11.0);
records.emplace_back(
metricA,
TaggedRecordsData< double >{{{}, Record< double >(5, 25.0, 6.0, 25.0)}});
records.emplace_back(
metricB,
TaggedRecordsData< double >{{{}, Record< double >(2, 7.0, 3.0, 11.0)}});
metrics::Sample sample;
Sample sample;
sample.sampleTime(absl::Now());
sample.pushGroup(records.data(), records.size(), absl::Seconds(5));

@ -41,66 +41,60 @@ TYPED_TEST_P(CollectorTest, Collector)
ASSERT_EQ(METRIC_A, collector1.id().description());
ASSERT_EQ(METRIC_B, collector2.id().description());
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(TypeParam::RecordType::DEFAULT_MAX(), record1.max());
ASSERT_EQ(TypeParam::RecordType::DEFAULT_MIN(), record1.min());
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(TypeParam::RecordType::DEFAULT_MIN(), record2.min());
ASSERT_EQ(TypeParam::RecordType::DEFAULT_MAX(), record2.max());
auto record1 = collector1.load();
ASSERT_EQ(METRIC_A, record1.id.description());
ASSERT_THAT(record1.data, IsEmpty());
auto record2 = collector2.load();
ASSERT_EQ(METRIC_B, record2.id.description());
ASSERT_THAT(record2.data, IsEmpty());
const Tags tags;
collector1.tick(1);
record1 = collector1.load();
ASSERT_EQ(METRIC_A, record1.id().description());
ASSERT_EQ(1, record1.count());
ASSERT_EQ(1, record1.total());
ASSERT_EQ(1, record1.min());
ASSERT_EQ(1, record1.max());
ASSERT_EQ(METRIC_A, record1.id.description());
ASSERT_THAT(record1.data, Contains(Key(tags)));
ASSERT_EQ(1, record1.data.at(tags).count());
ASSERT_EQ(1, record1.data.at(tags).total());
ASSERT_EQ(1, record1.data.at(tags).min());
ASSERT_EQ(1, record1.data.at(tags).max());
collector1.tick(2);
record1 = collector1.load();
ASSERT_EQ(METRIC_A, record1.id().description());
ASSERT_EQ(2, record1.count());
ASSERT_EQ(3, record1.total());
ASSERT_EQ(1, record1.min());
ASSERT_EQ(2, record1.max());
ASSERT_EQ(METRIC_A, record1.id.description());
ASSERT_THAT(record1.data, Contains(Key(tags)));
ASSERT_EQ(2, record1.data.at(tags).count());
ASSERT_EQ(3, record1.data.at(tags).total());
ASSERT_EQ(1, record1.data.at(tags).min());
ASSERT_EQ(2, record1.data.at(tags).max());
collector1.tick(-5);
record1 = collector1.load();
ASSERT_EQ(METRIC_A, record1.id().description());
ASSERT_EQ(3, record1.count());
ASSERT_EQ(-2, record1.total());
ASSERT_EQ(-5, record1.min());
ASSERT_EQ(2, record1.max());
ASSERT_EQ(METRIC_A, record1.id.description());
ASSERT_THAT(record1.data, Contains(Key(tags)));
ASSERT_EQ(3, record1.data.at(tags).count());
ASSERT_EQ(-2, record1.data.at(tags).total());
ASSERT_EQ(-5, record1.data.at(tags).min());
ASSERT_EQ(2, record1.data.at(tags).max());
collector1.clear();
record1 = collector1.load();
ASSERT_EQ(METRIC_A, record1.id().description());
ASSERT_EQ(0, record1.count());
ASSERT_EQ(0, record1.total());
ASSERT_EQ(TypeParam::RecordType::DEFAULT_MIN(), record1.min());
ASSERT_EQ(TypeParam::RecordType::DEFAULT_MAX(), record1.max());
ASSERT_EQ(METRIC_A, record1.id.description());
ASSERT_THAT(record1.data, IsEmpty());
collector1.tick(3);
record1 = collector1.loadAndClear();
ASSERT_EQ(METRIC_A, record1.id().description());
ASSERT_EQ(1, record1.count());
ASSERT_EQ(3, record1.total());
ASSERT_EQ(3, record1.min());
ASSERT_EQ(3, record1.max());
ASSERT_EQ(METRIC_A, record1.id.description());
ASSERT_THAT(record1.data, Contains(Key(tags)));
ASSERT_EQ(1, record1.data.at(tags).count());
ASSERT_EQ(3, record1.data.at(tags).total());
ASSERT_EQ(3, record1.data.at(tags).min());
ASSERT_EQ(3, record1.data.at(tags).max());
record1 = collector1.load();
ASSERT_EQ(METRIC_A, record1.id().description());
ASSERT_EQ(0, record1.count());
ASSERT_EQ(0, record1.total());
ASSERT_EQ(TypeParam::RecordType::DEFAULT_MIN(), record1.min());
ASSERT_EQ(TypeParam::RecordType::DEFAULT_MAX(), record1.max());
ASSERT_EQ(METRIC_A, record1.id.description());
ASSERT_THAT(record1.data, IsEmpty());
}
REGISTER_TYPED_TEST_SUITE_P(CollectorTest, Collector);
@ -260,12 +254,13 @@ MATCHER_P6(RecordEq, category, name, count, total, min, max, "")
{
// clang-format off
return (
arg.id().categoryName() == std::string(category) &&
arg.id().metricName() == std::string(name) &&
arg.count() == count &&
arg.total() == total &&
arg.min() == min &&
arg.max() == max
arg.id.categoryName() == std::string(category) &&
arg.id.metricName() == std::string(name) &&
arg.data.find(Tags()) != arg.data.end() &&
arg.data.at(Tags()).count() == count &&
arg.data.at(Tags()).total() == total &&
arg.data.at(Tags()).min() == min &&
arg.data.at(Tags()).max() == max
);
// clang-format on
}
@ -274,11 +269,11 @@ MATCHER_P5(RecordEq, id, count, total, min, max, "")
{
// clang-format off
return (
arg.id() == id &&
arg.count() == count &&
arg.total() == total &&
arg.min() == min &&
arg.max() == max
arg.id == id &&
arg.data.at(Tags()).count() == count &&
arg.data.at(Tags()).total() == total &&
arg.data.at(Tags()).min() == min &&
arg.data.at(Tags()).max() == max
);
// clang-format on
}
@ -287,10 +282,10 @@ MATCHER_P4(RecordEq, count, total, min, max, "")
{
// clang-format off
return (
arg.count() == count &&
arg.total() == total &&
arg.min() == min &&
arg.max() == max
arg.data.at(Tags()).count() == count &&
arg.data.at(Tags()).total() == total &&
arg.data.at(Tags()).min() == min &&
arg.data.at(Tags()).max() == max
);
// clang-format on
}
@ -299,11 +294,11 @@ MATCHER_P5(RecordCatEq, category, count, total, min, max, "")
{
// clang-format off
return (
arg.id().categoryName() == std::string(category) &&
arg.count() == count &&
arg.total() == total &&
arg.min() == min &&
arg.max() == max
arg.id.categoryName() == std::string(category) &&
arg.data.at(Tags()).count() == count &&
arg.data.at(Tags()).total() == total &&
arg.data.at(Tags()).min() == min &&
arg.data.at(Tags()).max() == max
);
// clang-format on
}
@ -323,7 +318,7 @@ TEST(MetricsCore, RepoBasic)
collector1->tick(2.0);
collector2->tick(4.0);
std::vector< Record< double > > records =
std::vector< TaggedRecords< double > > records =
repo.collectAndClear(registry.get("Test"));
EXPECT_THAT(records, SizeIs(2));
// clang-format off
@ -379,9 +374,9 @@ TEST(MetricsCore, RepoCollect)
const char *CATEGORY = CATEGORIES[i];
const Category *category = registry.get(CATEGORY);
std::vector< Record< int > > records = repo.collect(category);
std::vector< TaggedRecords< int > > records = repo.collect(category);
ASSERT_THAT(records, SizeIs(static_cast< int >(METRICS.size())));
ASSERT_THAT(records, SizeIs(METRICS.size()));
// clang-format off
ASSERT_THAT(
records,
@ -401,8 +396,9 @@ TEST(MetricsCore, RepoCollect)
auto collectors = repo.allCollectors(metric);
for(int k = 0; k < static_cast< int >(collectors.size()); ++k)
{
Record< int > EI(metric, j, 2 * j, -j, j);
Record< int > record = collectors[k]->load();
TaggedRecords< int > EI(metric);
EI.data[Tags()] = Record< int >(j, 2 * j, -j, j);
TaggedRecords< int > record = collectors[k]->load();
ASSERT_EQ(record, EI);
}
}
@ -424,7 +420,7 @@ TEST(MetricsCore, RepoCollect)
for(int l = 0; l < static_cast< int >(collectors.size()); ++l)
{
Record< int > record = collectors[k]->load();
TaggedRecords< int > record = collectors[k]->load();
ASSERT_THAT(record, RecordEq(metric, 100u, 100, 100, 100));
}
}
@ -444,15 +440,17 @@ const Category *
firstCategory(
const absl::variant< SampleGroup< double >, SampleGroup< int > > &g)
{
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;
});
return absl::visit(
[](const auto &group) -> const Category * {
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;
},
g);
}
TEST(MetricsCore, ManagerCollectSample1)
@ -496,8 +494,8 @@ TEST(MetricsCore, ManagerCollectSample1)
WithinWindow(window, absl::Milliseconds(10)))
<< group;
const char *name = group.records()[0].id().categoryName();
for(const Record< double > &record : group.records())
const char *name = group.records()[0].id.categoryName();
for(const auto &record : group.records())
{
ASSERT_THAT(record, RecordCatEq(name, 1u, 1, 1, 1));
}
@ -506,8 +504,8 @@ TEST(MetricsCore, ManagerCollectSample1)
{
for(size_t j = 0; j < NUM_METRICS; ++j)
{
DoubleCollector *col = rep.defaultCollector(CATEGORIES[i], METRICS[j]);
Record< double > record = col->load();
DoubleCollector *col = rep.defaultCollector(CATEGORIES[i], METRICS[j]);
auto record = col->load();
ASSERT_THAT(record, RecordEq(1u, 1, 1, 1));
}
}
@ -524,9 +522,9 @@ TEST(MetricsCore, ManagerCollectSample1)
{
for(size_t j = 0; j < NUM_METRICS; ++j)
{
DoubleCollector *col = rep.defaultCollector(CATEGORIES[i], METRICS[j]);
Record< double > record = col->load();
ASSERT_EQ(Record< double >(record.id()), record);
DoubleCollector *col = rep.defaultCollector(CATEGORIES[i], METRICS[j]);
auto record = col->load();
ASSERT_EQ(TaggedRecords< double >(record.id), record);
}
}
}
@ -591,7 +589,7 @@ TEST(MetricsCore, ManagerCollectSample2)
for(size_t j = 0; j < NUM_METRICS; ++j)
{
DoubleCollector *col = rep.defaultCollector(CATEGORIES[i], METRICS[j]);
Record< double > record = col->load();
TaggedRecords< double > record = col->load();
ASSERT_THAT(record, RecordEq(1u, 1, 1, 1));
}
}
@ -623,10 +621,10 @@ TEST(MetricsCore, ManagerCollectSample2)
for(size_t j = 0; j < NUM_METRICS; ++j)
{
DoubleCollector *col = rep.defaultCollector(CATEGORIES[i], METRICS[j]);
Record< double > record = col->load();
TaggedRecords< double > record = col->load();
if(combIt.includesElement(i))
{
ASSERT_EQ(Record< double >(record.id()), record);
ASSERT_EQ(TaggedRecords< double >(record.id), record);
}
else
{
@ -641,8 +639,8 @@ TEST(MetricsCore, ManagerCollectSample2)
struct MockPublisher : public Publisher
{
std::atomic_int invocations;
std::vector< Record< double > > recordBuffer;
std::vector< Record< double > > sortedRecords;
std::vector< TaggedRecords< double > > recordBuffer;
std::vector< TaggedRecords< double > > sortedRecords;
Sample m_sample;
std::set< absl::Duration > times;
@ -673,7 +671,7 @@ struct MockPublisher : public Publisher
auto git = s.begin();
ASSERT_NE(git, s.end());
recordBuffer.push_back(*git);
Record< double > *head = &recordBuffer.back();
TaggedRecords< double > *head = &recordBuffer.back();
for(++git; git != s.end(); ++git)
{
recordBuffer.push_back(*git);
@ -683,9 +681,8 @@ struct MockPublisher : public Publisher
}
sortedRecords = recordBuffer;
std::sort(
sortedRecords.begin(), sortedRecords.end(),
[](const auto &lhs, const auto &rhs) { return lhs.id() < rhs.id(); });
std::sort(sortedRecords.begin(), sortedRecords.end(),
[](const auto &lhs, const auto &rhs) { return lhs.id < rhs.id; });
}
void
@ -701,16 +698,16 @@ struct MockPublisher : public Publisher
int
indexOf(const Id &id)
{
Record< double > searchRecord(id);
TaggedRecords< double > searchRecord(id);
auto it = std::lower_bound(
sortedRecords.begin(), sortedRecords.end(), searchRecord,
[](const auto &lhs, const auto &rhs) { return lhs.id() < rhs.id(); });
[](const auto &lhs, const auto &rhs) { return lhs.id < rhs.id; });
if(it == sortedRecords.end())
{
return -1;
}
return (it->id() == id) ? it - sortedRecords.begin() : -1;
return (it->id == id) ? it - sortedRecords.begin() : -1;
}
bool

@ -6,9 +6,11 @@
#include <gmock/gmock.h>
using namespace llarp;
using namespace metrics;
using namespace ::testing;
using RecordT = metrics::Record< double >;
using TagRecordT = metrics::TaggedRecords< double >;
using SampleGroupT = metrics::SampleGroup< double >;
struct MetricFormatSpecTestData
@ -149,13 +151,17 @@ TEST(MetricsTypes, Sample)
metrics::Id metricC(&descC);
absl::Time timeStamp = absl::Now();
RecordT recordA(metricA, 0, 0, 0, 0);
RecordT recordB(metricB, 1, 2, 3, 4);
RecordT recordC(metricC, 4, 3, 2, 1);
RecordT recordA(0, 0, 0, 0);
RecordT recordB(1, 2, 3, 4);
RecordT recordC(4, 3, 2, 1);
RecordT buffer1[] = {recordA, recordB};
std::vector< RecordT > buffer2;
buffer2.push_back(recordC);
TagRecordT tagRecordA(metricA, {{{}, recordA}});
TagRecordT tagRecordB(metricB, {{{}, recordB}});
TagRecordT tagRecordC(metricC, {{{}, recordC}});
TagRecordT buffer1[] = {tagRecordA, tagRecordB};
std::vector< TagRecordT > buffer2;
buffer2.push_back(tagRecordC);
metrics::Sample sample;
sample.sampleTime(timeStamp);
@ -209,7 +215,7 @@ struct SampleTest
metrics::Id id_F;
metrics::Id id_G;
std::vector< RecordT > recordBuffer;
std::vector< TagRecordT > recordBuffer;
SampleTest()
: cat_A("A", true)
@ -228,29 +234,47 @@ struct SampleTest
, id_F(&DESC_F)
, id_G(&DESC_G)
{
recordBuffer.emplace_back(metrics::Id(0), 1, 1, 1, 1);
recordBuffer.emplace_back(id_A, 2, 2, 2, 2);
recordBuffer.emplace_back(id_B, 3, 3, 3, 3);
recordBuffer.emplace_back(id_C, 4, 4, 4, 4);
recordBuffer.emplace_back(id_D, 5, 5, 5, 5);
recordBuffer.emplace_back(id_E, 6, 6, 6, 6);
recordBuffer.emplace_back(id_F, 7, 7, 7, 7);
recordBuffer.emplace_back(id_G, 8, 8, 8, 8);
recordBuffer.emplace_back(id_A, 9, 9, 9, 9);
recordBuffer.emplace_back(
metrics::Id(0),
TaggedRecordsData< double >{{metrics::Tags(), RecordT(1, 1, 1, 1)}});
recordBuffer.emplace_back(
id_A,
TaggedRecordsData< double >{{metrics::Tags(), RecordT(2, 2, 2, 2)}});
recordBuffer.emplace_back(
id_B,
TaggedRecordsData< double >{{metrics::Tags(), RecordT(3, 3, 3, 3)}});
recordBuffer.emplace_back(
id_C,
TaggedRecordsData< double >{{metrics::Tags(), RecordT(4, 4, 4, 4)}});
recordBuffer.emplace_back(
id_D,
TaggedRecordsData< double >{{metrics::Tags(), RecordT(5, 5, 5, 5)}});
recordBuffer.emplace_back(
id_E,
TaggedRecordsData< double >{{metrics::Tags(), RecordT(6, 6, 6, 6)}});
recordBuffer.emplace_back(
id_F,
TaggedRecordsData< double >{{metrics::Tags(), RecordT(7, 7, 7, 7)}});
recordBuffer.emplace_back(
id_G,
TaggedRecordsData< double >{{metrics::Tags(), RecordT(8, 8, 8, 8)}});
recordBuffer.emplace_back(
id_A,
TaggedRecordsData< double >{{metrics::Tags(), RecordT(9, 9, 9, 9)}});
}
};
std::pair< std::vector< metrics::SampleGroup< double > >, size_t >
generate(const std::string &specification,
const std::vector< RecordT > &recordBuffer)
const std::vector< TagRecordT > &recordBuffer)
{
const char *c = specification.c_str();
std::vector< metrics::SampleGroup< double > > groups;
size_t size = 0;
const RecordT *head = recordBuffer.data();
const RecordT *current = head;
const TagRecordT *head = recordBuffer.data();
const TagRecordT *current = head;
while(*c)
{
int numRecords = *(c + 1) - '0';

Loading…
Cancel
Save