Merge remote-tracking branch 'origin/master' into ipv6-tun

This commit is contained in:
Jeff Becker 2019-06-14 08:11:16 -04:00
commit 227f561ffc
No known key found for this signature in database
GPG Key ID: F357B3B42F6F9B05
32 changed files with 836 additions and 660 deletions

View File

@ -18,11 +18,13 @@ set(LIB_UTIL_SRC
util/android_logger.cpp
util/file_logger.cpp
util/logic.cpp
util/loglevel.cpp
util/mem.cpp
util/memfn_traits.cpp
util/memfn.cpp
util/metrics_core.cpp
util/metrics_types.cpp
util/json_logger.cpp
util/ostream_logger.cpp
util/syslog_logger.cpp
util/win32_logger.cpp
@ -49,7 +51,7 @@ set(LIB_UTIL_SRC
add_library(${UTIL_LIB} STATIC ${LIB_UTIL_SRC})
target_include_directories(${UTIL_LIB} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/include)
target_link_libraries_system(${UTIL_LIB} absl::synchronization absl::hash nlohmann_json::nlohmann_json)
target_link_libraries_system(${UTIL_LIB} absl::synchronization absl::hash absl::container nlohmann_json::nlohmann_json)
# cut back on fluff
if (NOT WIN32)

View File

@ -77,7 +77,8 @@ namespace llarp
std::for_each(logging.begin(), logging.end(),
std::bind(visitor, "logging", _1));
// end of logging section commit settings and go
functor("logging", "", "");
std::for_each(lokid.begin(), lokid.end(), std::bind(visitor, "lokid", _1));
std::for_each(router.begin(), router.end(),
std::bind(visitor, "router", _1));

View File

@ -17,8 +17,6 @@
#include <algorithm> // for std::find_if
#include <stdio.h> // sprintf
#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
dns_tracker dns_udp_tracker;
/*

View File

@ -28,7 +28,7 @@ namespace llarp
, m_Resolver(std::make_shared< dns::Proxy >(
r->netloop(), r->logic(), r->netloop(), r->logic(), this))
, m_Name(name)
, m_Tun{{0}, 0, {0}, 0, 0, 0, 0, 0, 0, 0}
, m_Tun{{0}, 0, {0}, 0, 0, 0, 0, 0, 0, 0, 0}
, m_LocalResolverAddr("127.0.0.1", 53)
, m_InetToNetwork(name + "_exit_rx", r->netloop(), r->netloop())

View File

@ -95,8 +95,8 @@ namespace llarp
if(!isLIM)
{
const std::string host = "RX_" + RouterID(from->GetPubKey()).ToString();
METRICS_DYNAMIC_INCREMENT(msg->Name(), host.c_str());
metrics::integerTick(msg->Name(), "RX", 1, "id",
RouterID(from->GetPubKey()).ToString());
}
msg->session = from;

View File

@ -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 publicationType = taggedRecords.id.description()->type();
for(const auto &record : taggedRecords.data)
{
auto val = formatValue(record, elapsedTime, publicationType);
const auto &tags = record.first;
const auto &rec = record.second;
if(publicationType != Publication::Type::Unspecified)
{
auto val = formatValue(rec, elapsedTime, publicationType);
if(val)
{
result.emplace_back(
addName(id, Publication::repr(publicationType), suffix),
val.value(), time);
if(val)
{
result.emplace_back(
addName(id, Publication::repr(publicationType), tags, suffix),
val.value(), time);
}
}
}
else
{
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);
else
{
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(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< 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);
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;
}

View File

@ -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
{
const FormatSpec *countSpec = nullptr;
const FormatSpec *totalSpec = nullptr;
const FormatSpec *minSpec = nullptr;
const FormatSpec *maxSpec = nullptr;
if(format)
stream << "\t\t" << taggedRecords.id << " [";
for(const auto &rec : taggedRecords.data)
{
stream << "\n\t\t\t";
const auto &tags = rec.first;
const auto &record = rec.second;
{
countSpec = format->specFor(Publication::Type::Count);
totalSpec = format->specFor(Publication::Type::Total);
minSpec = format->specFor(Publication::Type::Min);
maxSpec = format->specFor(Publication::Type::Max);
Printer printer(stream, -1, -1);
printer.printValue(tags);
}
stream << "count = ";
formatValue(stream, record.count(), countSpec);
stream << ", total = ";
formatValue(stream, record.total(), totalSpec);
if(Record< Value >::DEFAULT_MIN() == record.min())
stream << " ";
if(publicationType != Publication::Type::Unspecified)
{
stream << ", min = undefined";
stream << Publication::repr(publicationType) << " = ";
const FormatSpec *formatSpec =
format ? format->specFor(publicationType) : nullptr;
formatValue(stream, record, elapsedTime, publicationType,
formatSpec);
}
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);
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)
auto publicationType = taggedRecord.id.description()->type();
for(const auto &rec : taggedRecord.data)
{
result["publicationType"] = Publication::repr(publicationType);
formatValue(result, record, elapsedTime, publicationType);
}
else
{
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;
}

View File

@ -16,6 +16,7 @@
#include <util/logger.hpp>
#include <util/memfn.hpp>
#include <util/file_logger.hpp>
#include <util/json_logger.hpp>
#include <util/logger_syslog.hpp>
#include <util/metrics.hpp>
#include <util/str.hpp>
@ -923,6 +924,19 @@ namespace llarp
}
else if(StrEq(section, "logging"))
{
if(strlen(key) == 0 && strlen(val) == 0)
{
if(m_LogJSON)
{
LogContext::Instance().logStream = std::make_unique< JSONLogStream >(
diskworker(), m_LogFile, 100, m_LogFile != stdout);
}
else if(m_LogFile != stdout)
{
LogContext::Instance().logStream = std::make_unique< FileLogStream >(
diskworker(), m_LogFile, 100, true);
}
}
if(StrEq(key, "type") && StrEq(val, "syslog"))
{
// TODO(despair): write event log syslog class
@ -933,15 +947,18 @@ namespace llarp
LogContext::Instance().logStream = std::make_unique< SysLogStream >();
#endif
}
if(StrEq(key, "type") && StrEq(val, "json"))
{
m_LogJSON = true;
}
if(StrEq(key, "file"))
{
LogInfo("open log file: ", val);
FILE *const logfile = ::fopen(val, "a");
if(logfile)
{
LogContext::Instance().logStream =
std::make_unique< FileLogStream >(diskworker(), logfile, 500);
LogInfo("started logging to ", val);
m_LogFile = logfile;
LogInfo("will log to file ", val);
}
else if(errno)
{
@ -1376,15 +1393,14 @@ namespace llarp
bool
Router::Sign(Signature &sig, const llarp_buffer_t &buf) const
{
METRICS_TIME_BLOCK("Router", "Sign");
metrics::TimerGuard t("Router", "Sign");
return CryptoManager::instance()->sign(sig, identity(), buf);
}
void
Router::SendTo(RouterID remote, const ILinkMessage *msg, ILinkLayer *selected)
{
const std::string remoteName = "TX_" + remote.ToString();
METRICS_DYNAMIC_INCREMENT(msg->Name(), remoteName.c_str());
metrics::integerTick(msg->Name(), "to", 1, "tx", remote.ToString());
llarp_buffer_t buf(linkmsg_buffer);
if(!msg->BEncode(&buf))

View File

@ -305,6 +305,11 @@ namespace llarp
// set to max value right now
std::unordered_map< RouterID, llarp_time_t, PubKey::Hash > lokinetRouters;
// set to true if we are configured to run with json logging
bool m_LogJSON = false;
// the file we are logging to
FILE *m_LogFile = stdout;
Router(struct llarp_threadpool *tp, llarp_ev_loop_ptr __netloop,
std::shared_ptr< Logic > logic);

View File

@ -6,7 +6,7 @@ namespace llarp
namespace
{
static void
Flush(const std::deque< std::string > &lines, FILE *const f)
Flush(std::deque< std::string > lines, FILE *const f)
{
for(const auto &line : lines)
fprintf(f, "%s\n", line.c_str());
@ -14,15 +14,19 @@ namespace llarp
}
} // namespace
FileLogStream::FileLogStream(thread::ThreadPool *disk, FILE *f,
llarp_time_t flushInterval)
: m_Disk(disk), m_File(f), m_FlushInterval(flushInterval)
llarp_time_t flushInterval, bool closeFile)
: m_Disk(disk)
, m_File(f)
, m_FlushInterval(flushInterval)
, m_Close(closeFile)
{
}
FileLogStream::~FileLogStream()
{
fflush(m_File);
fclose(m_File);
if(m_Close)
fclose(m_File);
}
bool
@ -36,27 +40,11 @@ namespace llarp
void
FileLogStream::PreLog(std::stringstream &ss, LogLevel lvl, const char *fname,
int lineno) const
int lineno, const std::string &nodename) const
{
switch(lvl)
{
case eLogNone:
break;
case eLogDebug:
ss << "[DBG] ";
break;
case eLogInfo:
ss << "[NFO] ";
break;
case eLogWarn:
ss << "[WRN] ";
break;
case eLogError:
ss << "[ERR] ";
break;
}
ss << "(" << thread_id_string() << ") " << log_timestamp() << " " << fname
ss << "[" << LogLevelToString(lvl) << "] ";
ss << "[" << nodename << "]"
<< "(" << thread_id_string() << ") " << log_timestamp() << " " << fname
<< ":" << lineno << "\t";
}
@ -76,7 +64,10 @@ namespace llarp
void
FileLogStream::FlushLinesToDisk(llarp_time_t now)
{
m_Disk->addJob(std::bind(&Flush, std::move(m_Lines), m_File));
FILE *const f = m_File;
std::deque< std::string > lines(m_Lines);
m_Disk->addJob([=]() { Flush(lines, f); });
m_Lines.clear();
m_LastFlush = now;
}
} // namespace llarp

View File

@ -12,14 +12,14 @@ namespace llarp
/// flushable file based log stream
struct FileLogStream : public ILogStream
{
FileLogStream(thread::ThreadPool* disk, FILE* f,
llarp_time_t flushInterval);
FileLogStream(thread::ThreadPool* disk, FILE* f, llarp_time_t flushInterval,
bool closefile = true);
~FileLogStream();
void
PreLog(std::stringstream& out, LogLevel lvl, const char* fname,
int lineno) const override;
PreLog(std::stringstream& out, LogLevel lvl, const char* fname, int lineno,
const std::string& nodename) const override;
void
Print(LogLevel, const char*, const std::string& msg) override;
@ -32,6 +32,9 @@ namespace llarp
{
}
protected:
std::deque< std::string > m_Lines;
private:
bool
ShouldFlush(llarp_time_t now) const;
@ -40,10 +43,10 @@ namespace llarp
FlushLinesToDisk(llarp_time_t now);
thread::ThreadPool* m_Disk;
FILE* m_File;
FILE* const m_File;
const llarp_time_t m_FlushInterval;
llarp_time_t m_LastFlush = 0;
std::deque< std::string > m_Lines;
const bool m_Close;
};
} // namespace llarp

View File

@ -12,6 +12,8 @@ namespace llarp
{
namespace json
{
using Object = nlohmann::json;
struct IParser
{
virtual ~IParser()
@ -34,7 +36,7 @@ namespace llarp
FeedData(const char* buf, size_t sz) = 0;
/// parse internal buffer
virtual Result
Parse(nlohmann::json& obj) const = 0;
Parse(Object& obj) const = 0;
};
/// create new parser

View File

@ -0,0 +1,20 @@
#include <util/json_logger.hpp>
#include <util/json.hpp>
namespace llarp
{
void
JSONLogStream::AppendLog(LogLevel lvl, const char* fname, int lineno,
const std::string& nodename, const std::string msg)
{
json::Object obj;
obj["time"] = llarp::time_now_ms();
obj["nickname"] = nodename;
obj["file"] = std::string(fname);
obj["line"] = lineno;
obj["level"] = LogLevelToString(lvl);
obj["message"] = msg;
m_Lines.emplace_back(obj.dump());
}
} // namespace llarp

View File

@ -0,0 +1,22 @@
#ifndef LLARP_UTIL_JSON_LOGGER
#define LLARP_UTIL_JSON_LOGGER
#include <util/file_logger.hpp>
namespace llarp
{
struct JSONLogStream : public FileLogStream
{
JSONLogStream(thread::ThreadPool* disk, FILE* f, llarp_time_t flushInterval,
bool closeFile)
: FileLogStream(disk, f, flushInterval, closeFile)
{
}
void
AppendLog(LogLevel lvl, const char* fname, int lineno,
const std::string& nodename, const std::string msg) override;
};
} // namespace llarp
#endif

View File

@ -85,7 +85,7 @@ namespace llarp
LogContext();
LogLevel minLevel = eLogInfo;
ILogStream_ptr logStream;
std::string nodeName;
std::string nodeName = "lokinet";
const llarp_time_t started;
@ -106,12 +106,8 @@ namespace llarp
return;
std::stringstream ss;
log.logStream->PreLog(ss, lvl, fname, lineno);
if(log.nodeName.size())
LogAppend(ss, "[", log.nodeName, "] ");
LogAppend(ss, std::forward< TArgs >(args)...);
log.logStream->PostLog(ss);
log.logStream->Print(lvl, fname, ss.str());
log.logStream->AppendLog(lvl, fname, lineno, log.nodeName, ss.str());
}
/*
std::stringstream ss;

View File

@ -8,8 +8,8 @@ namespace llarp
struct SysLogStream : public ILogStream
{
void
PreLog(std::stringstream& s, LogLevel lvl, const char* fname,
int lineno) const override;
PreLog(std::stringstream& s, LogLevel lvl, const char* fname, int lineno,
const std::string& nodename) const override;
void
Print(LogLevel lvl, const char* tag, const std::string& msg) override;

22
llarp/util/loglevel.cpp Normal file
View File

@ -0,0 +1,22 @@
#include <util/loglevel.hpp>
namespace llarp
{
std::string
LogLevelToString(LogLevel lvl)
{
switch(lvl)
{
case eLogDebug:
return "DBG";
case eLogInfo:
return "NFO";
case eLogWarn:
return "WRN";
case eLogError:
return "ERR";
default:
return "???";
}
}
} // namespace llarp

View File

@ -1,5 +1,6 @@
#ifndef LLARP_UTIL_LOG_LEVEL_HPP
#define LLARP_UTIL_LOG_LEVEL_HPP
#include <string>
namespace llarp
{
@ -13,6 +14,9 @@ namespace llarp
eLogNone
};
std::string
LogLevelToString(LogLevel level);
} // namespace llarp
#endif

View File

@ -16,14 +16,26 @@ namespace llarp
}
virtual void
PreLog(std::stringstream& out, LogLevel lvl, const char* fname,
int lineno) const = 0;
PreLog(std::stringstream& out, LogLevel lvl, const char* fname, int lineno,
const std::string& nodename) const = 0;
virtual void
Print(LogLevel lvl, const char* filename, const std::string& msg) = 0;
virtual void
PostLog(std::stringstream& out) const = 0;
virtual void
AppendLog(LogLevel lvl, const char* fname, int lineno,
const std::string& nodename, const std::string msg)
{
std::stringstream ss;
PreLog(ss, lvl, fname, lineno, nodename);
ss << msg;
PostLog(ss);
Print(lvl, fname, ss.str());
}
/// called every end of event loop tick
virtual void
Tick(llarp_time_t now) = 0;

View File

@ -3,6 +3,7 @@
#include <util/metrics_types.hpp>
#include <util/metrics_core.hpp>
#include <util/string_view.hpp>
namespace llarp
{
@ -25,6 +26,23 @@ namespace llarp
return manager->registry().publicationType(id, type);
}
};
template < typename... TagVals >
void
integerTick(string_view category, string_view metric, int val,
TagVals&&... tags)
{
if(DefaultManager::instance())
{
CollectorRepo< int >& repository =
DefaultManager::instance()->intCollectorRepo();
IntCollector* collector = repository.defaultCollector(category, metric);
if(collector->id().category()->enabled())
{
collector->tick(val, tags...);
}
}
}
} // namespace metrics
} // namespace llarp
@ -41,86 +59,8 @@ 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< 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) \
METRICS_TIME_BLOCK_IMP(CAT, METRIC, METRICS_UNIQUE_NAME(time_block))
#define METRICS_IF_CATEGORY_ENABLED_IMP(CAT, NAME) \
static llarp::metrics::CategoryContainer NAME = {false, nullptr, nullptr}; \
if(!NAME.category() && llarp::metrics::DefaultManager::instance()) \
{ \
llarp::metrics::MetricsHelper::initContainer(NAME, CAT); \
} \
if(NAME.enabled())
#define METRICS_IF_CATEGORY_ENABLED(CAT) \
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< 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
#define METRICS_INT_UPDATE(CAT, METRIC, VALUE) \
do \
{ \
using namespace llarp::metrics; \
static CategoryContainer container = {false, nullptr, nullptr}; \
static IntCollector* collector = nullptr; \
if(container.category() == nullptr && DefaultManager::instance()) \
{ \
collector = MetricHelper::getIntCollector(CAT, METRIC); \
MetricHelper::initContainer(container, CAT); \
} \
if(container.enabled()) \
{ \
collector->tick(VALUE); \
} \
} while(false)
#define METRICS_TYPED_INT_UPDATE(CAT, METRIC, VALUE, TYPE) \
do \
{ \
using namespace llarp::metrics; \
static CategoryContainer container = {false, nullptr, nullptr}; \
static IntCollector* collector = nullptr; \
if(container.category() == nullptr && DefaultManager::instance()) \
{ \
collector = MetricHelper::getIntCollector(CAT, METRIC); \
MetricHelper::setType(collector->id(), TYPE); \
MetricHelper::initContainer(container, CAT); \
} \
if(container.enabled()) \
{ \
collector->tick(VALUE); \
} \
} while(false)
// For when the category/metric may change during the program run
#define METRICS_DYNAMIC_UPDATE(CAT, METRIC, VALUE) \
#define METRICS_DYNAMIC_UPDATE(CAT, METRIC, ...) \
do \
{ \
using namespace llarp::metrics; \
@ -132,50 +72,9 @@ namespace llarp
repository.defaultCollector((CAT), (METRIC)); \
if(collector->id().category()->enabled()) \
{ \
collector->tick((VALUE)); \
collector->tick(__VA_ARGS__); \
} \
} \
} while(false)
// For when the category/metric remain static
#define METRICS_UPDATE(CAT, METRIC, VALUE) \
do \
{ \
using namespace llarp::metrics; \
static CategoryContainer container = {false, nullptr, nullptr}; \
static DoubleCollector* collector = nullptr; \
if(container.category() == nullptr && DefaultManager::instance()) \
{ \
collector = MetricHelper::getDoubleCollector(CAT, METRIC); \
MetricHelper::initContainer(container, CAT); \
} \
if(container.enabled()) \
{ \
collector->tick(VALUE); \
} \
} while(false)
#define METRICS_TYPED_UPDATE(CAT, METRIC, VALUE, TYPE) \
do \
{ \
using namespace llarp::metrics; \
static CategoryContainer container = {false, nullptr, nullptr}; \
static DoubleCollector* collector = nullptr; \
if(container.category() == nullptr && DefaultManager::instance()) \
{ \
collector = MetricHelper::getDoubleCollector(CAT, METRIC); \
MetricHelper::setType(collector->id(), TYPE); \
MetricHelper::initContainer(container, CAT); \
} \
if(container.enabled()) \
{ \
collector->tick(VALUE); \
} \
} while(false)
#define METRICS_DYNAMIC_INCREMENT(CAT, METRIC) \
METRICS_DYNAMIC_INT_UPDATE(CAT, METRIC, 1)
#define METRICS_INCREMENT(CAT, METRIC) METRICS_INT_UPDATE(CAT, METRIC, 1)
#endif

View File

@ -6,15 +6,12 @@ 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)
Registry::insert(string_view category, string_view name)
{
// avoid life time issues, putting strings in the stringmem set
const char *cStr = m_stringmem.emplace(category).first->c_str();
const char *nStr = m_stringmem.emplace(name).first->c_str();
string_view cStr = m_stringmem.emplace(category).first->c_str();
string_view nStr = m_stringmem.emplace(name).first->c_str();
NamedCategory namedCategory(cStr, nStr);
const auto it = m_metrics.find(namedCategory);
@ -39,7 +36,7 @@ namespace llarp
}
Id
Registry::add(const char *category, const char *name)
Registry::add(string_view category, string_view name)
{
absl::WriterMutexLock l(&m_mutex);
auto result = insert(category, name);
@ -47,7 +44,7 @@ namespace llarp
}
Id
Registry::get(const char *category, const char *name)
Registry::get(string_view category, string_view name)
{
Id result = findId(category, name);
if(result)
@ -60,11 +57,11 @@ namespace llarp
}
const Category *
Registry::add(const char *category)
Registry::add(string_view category)
{
absl::WriterMutexLock l(&m_mutex);
const char *cStr = m_stringmem.emplace(category).first->c_str();
string_view cStr = m_stringmem.emplace(category).first->c_str();
auto it = m_categories.find(cStr);
if(it == m_categories.end())
{
@ -76,7 +73,7 @@ namespace llarp
}
const Category *
Registry::get(const char *category)
Registry::get(string_view category)
{
const Category *cPtr = findCategory(category);
if(cPtr)
@ -85,7 +82,7 @@ namespace llarp
}
absl::WriterMutexLock l(&m_mutex);
const char *cStr = m_stringmem.emplace(category).first->c_str();
string_view cStr = m_stringmem.emplace(category).first->c_str();
auto it = m_categories.find(cStr);
if(it == m_categories.end())
{
@ -150,7 +147,7 @@ namespace llarp
const FormatSpec *spec = format.specFor(type);
if(spec != nullptr)
{
const char *fmt = m_stringmem.emplace(spec->m_format).first->c_str();
string_view fmt = m_stringmem.emplace(spec->m_format).first->c_str();
fmtPtr->setSpec(type, FormatSpec(spec->m_scale, fmt));
}
}
@ -159,7 +156,7 @@ namespace llarp
}
const Category *
Registry::findCategory(const char *category) const
Registry::findCategory(string_view category) const
{
absl::ReaderMutexLock l(&m_mutex);
auto it = m_categories.find(category);
@ -167,7 +164,7 @@ namespace llarp
}
Id
Registry::findId(const char *category, const char *name) const
Registry::findId(string_view category, string_view name) const
{
absl::ReaderMutexLock l(&m_mutex);
auto it = m_metrics.find(std::make_tuple(category, 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(),

View File

@ -14,84 +14,130 @@ namespace llarp
{
namespace metrics
{
inline void
packToTagsImpl(Tags &)
{
}
template < typename K, typename V, typename... Args >
inline void
packToTagsImpl(Tags &tags, const K &k, const V &v, const Args &... args)
{
static_assert(std::is_convertible< K, Tag >::value, "");
static_assert(std::is_convertible< V, TagValue >::value, "");
tags.emplace(k, v);
packToTagsImpl(tags, args...);
}
template < typename... Args >
inline Tags
packToTags(const Args &... args)
{
static_assert(sizeof...(args) % 2 == 0, "");
Tags tags;
packToTagsImpl(tags, args...);
return tags;
}
template <>
inline Tags
packToTags< Tags >(const Tags &tags)
{
return tags;
}
template < typename Type >
class Collector
{
public:
using RecordType = Record< Type >;
using RecordType = Record< Type >;
using TaggedRecordsType = TaggedRecords< Type >;
private:
RecordType m_record GUARDED_BY(m_mutex);
TaggedRecordsType m_records GUARDED_BY(m_mutex);
mutable util::Mutex m_mutex;
Collector(const Collector &) = delete;
Collector &
operator=(const Collector &) = delete;
template < typename... Args >
RecordType &
fetch(Args... args) EXCLUSIVE_LOCKS_REQUIRED(m_mutex)
{
return m_records.data[packToTags(args...)];
}
public:
Collector(const Id &id) : m_record(id)
Collector(const Id &id) : m_records(id)
{
}
void
clear()
{
absl::WriterMutexLock l(&m_mutex);
m_record.clear();
absl::MutexLock l(&m_mutex);
m_records.data.clear();
}
RecordType
TaggedRecordsType
loadAndClear()
{
absl::WriterMutexLock l(&m_mutex);
RecordType rec = m_record;
m_record.clear();
absl::MutexLock l(&m_mutex);
auto result = m_records;
m_records.data.clear();
return rec;
return result;
}
RecordType
TaggedRecordsType
load()
{
absl::ReaderMutexLock l(&m_mutex);
return m_record;
absl::MutexLock l(&m_mutex);
return m_records;
}
template < typename... Args >
void
tick(Type value)
tick(Type value, Args... args)
{
absl::WriterMutexLock l(&m_mutex);
m_record.count()++;
m_record.total() += value;
m_record.min() = std::min(m_record.min(), value);
m_record.max() = std::max(m_record.max(), value);
absl::MutexLock l(&m_mutex);
RecordType &rec = fetch(args...);
rec.count()++;
rec.total() += value;
rec.min() = std::min(rec.min(), value);
rec.max() = std::max(rec.max(), value);
}
template < typename... Args >
void
accumulate(size_t count, Type total, Type min, Type max)
accumulate(size_t count, Type total, Type min, Type max, Args... args)
{
absl::WriterMutexLock l(&m_mutex);
m_record.count() += count;
m_record.total() += total;
m_record.min() = std::min(m_record.min(), min);
m_record.max() = std::max(m_record.max(), max);
absl::MutexLock l(&m_mutex);
RecordType &rec = fetch(args...);
rec.count() += count;
rec.total() += total;
rec.min() = std::min(rec.min(), min);
rec.max() = std::max(rec.max(), max);
}
template < typename... Args >
void
set(size_t count, Type total, Type min, Type max)
set(size_t count, Type total, Type min, Type max, Args... args)
{
absl::WriterMutexLock l(&m_mutex);
m_record.count() = count;
m_record.total() = total;
m_record.min() = min;
m_record.max() = max;
absl::MutexLock l(&m_mutex);
RecordType &rec = fetch(args...);
rec.count() = count;
rec.total() = total;
rec.min() = min;
rec.max() = max;
}
const Id &
id() const
{
absl::ReaderMutexLock l(&m_mutex);
return m_record.id();
return m_records.id;
}
};
@ -107,16 +153,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 >
@ -160,10 +226,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)
{
@ -173,10 +239,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)
{
@ -204,34 +270,11 @@ namespace llarp
class Registry
{
using NamedCategory = std::tuple< const char *, const char * >;
using NamedCategory = std::tuple< string_view, string_view >;
struct CmpNamedCategory
{
bool
operator()(const NamedCategory &lhs, const NamedCategory &rhs) const
{
int ret = std::strcmp(std::get< 0 >(lhs), std::get< 0 >(rhs));
if(ret == 0)
{
ret = std::strcmp(std::get< 1 >(lhs), std::get< 1 >(rhs));
}
return ret < 0;
}
};
struct StrCmp
{
bool
operator()(const char *lhs, const char *rhs) const
{
return std::strcmp(lhs, rhs) < 0;
}
};
using MetricMap = std::map< NamedCategory, std::shared_ptr< Description >,
CmpNamedCategory >;
using CategoryMap =
std::map< const char *, std::shared_ptr< Category >, StrCmp >;
using MetricMap =
std::map< NamedCategory, std::shared_ptr< Description > >;
using CategoryMap = std::map< string_view, std::shared_ptr< Category > >;
std::set< std::string > m_stringmem GUARDED_BY(m_mutex);
CategoryMap m_categories GUARDED_BY(m_mutex);
@ -244,7 +287,7 @@ namespace llarp
operator=(const Registry &) = delete;
std::tuple< Id, bool >
insert(const char *category, const char *name)
insert(string_view category, string_view name)
EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
public:
@ -253,14 +296,14 @@ namespace llarp
}
Id
add(const char *category, const char *name) LOCKS_EXCLUDED(m_mutex);
add(string_view category, string_view name) LOCKS_EXCLUDED(m_mutex);
Id
get(const char *category, const char *name) LOCKS_EXCLUDED(m_mutex);
get(string_view category, string_view name) LOCKS_EXCLUDED(m_mutex);
const Category *
add(const char *category) LOCKS_EXCLUDED(m_mutex);
add(string_view category) LOCKS_EXCLUDED(m_mutex);
const Category *
get(const char *category);
get(string_view category);
void
enable(const Category *category, bool value);
@ -288,16 +331,16 @@ namespace llarp
}
const Category *
findCategory(const char *category) const;
findCategory(string_view category) const;
Id
findId(const char *category, const char *name) const;
findId(string_view category, string_view name) const;
std::vector< const Category * >
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
{
@ -361,8 +404,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);
@ -374,7 +417,7 @@ namespace llarp
return {};
}
std::vector< Record< Type > > result;
std::vector< TaggedRecords< Type > > result;
auto &collectors = it->second;
result.reserve(collectors.size());
@ -390,20 +433,20 @@ 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);
}
Collector< Type > *
defaultCollector(const char *category, const char *name)
defaultCollector(string_view category, string_view name)
{
return defaultCollector(m_registry->get(category, name));
}
@ -427,7 +470,7 @@ namespace llarp
}
std::shared_ptr< Collector< Type > >
addCollector(const char *category, const char *name)
addCollector(string_view category, string_view name)
{
return addCollector(m_registry->get(category, name));
}
@ -668,7 +711,7 @@ namespace llarp
/// Add a `publisher` which will receive events for the given
/// `categoryName` only
bool
addPublisher(const char *categoryName,
addPublisher(string_view categoryName,
const std::shared_ptr< Publisher > &publisher)
{
return addPublisher(m_registry.get(categoryName), publisher);
@ -763,7 +806,7 @@ namespace llarp
}
void
enableCategory(const char *categoryName, bool enable = true)
enableCategory(string_view categoryName, bool enable = true)
{
m_registry.enable(m_registry.get(categoryName), enable);
}
@ -787,7 +830,7 @@ namespace llarp
}
std::vector< Publisher * >
publishersForCategory(const char *categoryName) const
publishersForCategory(string_view categoryName) const
{
const Category *category = m_registry.findCategory(categoryName);
return category ? publishersForCategory(category)
@ -869,7 +912,7 @@ namespace llarp
public:
static Collector *
lookup(const char *category, const char *name, Manager *manager = nullptr)
lookup(string_view category, string_view name, Manager *manager = nullptr)
{
manager = DefaultManager::manager(manager);
return manager ? (manager->*repoFunc)().defaultCollector(category, name)
@ -883,7 +926,7 @@ namespace llarp
return manager ? (manager->*repoFunc)().defaultCollector(id) : 0;
}
Metric(const char *category, const char *name, Manager *manager)
Metric(string_view category, string_view name, Manager *manager)
: m_collector(lookup(category, name, manager))
, m_enabled(m_collector ? &m_collector->id().category()->enabledRaw()
: nullptr)
@ -966,7 +1009,7 @@ namespace llarp
static void
getCollector(Collector **collector, CategoryContainer *container,
const char *category, const char *metric)
string_view category, string_view metric)
{
Manager *manager = DefaultManager::instance();
*collector = (manager->*repoFunc().defaultCollector)(category, metric);
@ -976,7 +1019,7 @@ namespace llarp
static void
getCollector(Collector **collector, CategoryContainer *container,
const char *category, const char *metric,
string_view category, string_view metric,
Publication::Type type)
{
Manager *manager = DefaultManager::instance();
@ -995,6 +1038,7 @@ namespace llarp
class TimerGuard
{
private:
Tags m_tags;
util::Stopwatch m_stopwatch;
DoubleCollector *m_collector;
@ -1003,8 +1047,9 @@ namespace llarp
operator=(const TimerGuard &) = delete;
public:
TimerGuard(DoubleMetric *metric)
: m_stopwatch()
template < typename... TagVals >
TimerGuard(DoubleMetric *metric, TagVals &&... tags)
: m_tags(packToTags(tags...))
, m_collector(metric->active() ? metric->collector() : nullptr)
{
if(m_collector)
@ -1013,8 +1058,9 @@ namespace llarp
}
}
TimerGuard(DoubleCollector *collector)
: m_stopwatch()
template < typename... TagVals >
TimerGuard(DoubleCollector *collector, TagVals &&... tags)
: m_tags(packToTags(tags...))
, m_collector(collector && collector->id().category()->enabled()
? collector
: nullptr)
@ -1025,8 +1071,10 @@ namespace llarp
}
}
TimerGuard(const char *category, const char *name, Manager *manager)
: m_stopwatch(), m_collector(nullptr)
template < typename... TagVals >
TimerGuard(string_view category, string_view name,
Manager *manager = nullptr, TagVals &&... tags)
: m_tags(packToTags(tags...)), m_collector(nullptr)
{
DoubleCollector *collector =
DoubleMetric::lookup(category, name, manager);
@ -1038,8 +1086,10 @@ namespace llarp
m_stopwatch.start();
}
}
TimerGuard(const Id &id, Manager *manager)
: m_stopwatch(), m_collector(nullptr)
template < typename... TagVals >
TimerGuard(const Id &id, Manager *manager = nullptr, TagVals &&... tags)
: m_tags(packToTags(tags...)), m_collector(nullptr)
{
DoubleCollector *collector = DoubleMetric::lookup(id, manager);
m_collector = (collector && collector->id().category()->enabled())
@ -1056,7 +1106,7 @@ namespace llarp
if(active())
{
m_stopwatch.stop();
m_collector->tick(absl::ToDoubleSeconds(m_stopwatch.time()));
m_collector->tick(absl::ToDoubleSeconds(m_stopwatch.time()), m_tags);
}
}
@ -1107,7 +1157,7 @@ namespace llarp
}
void
schedule(const char *categoryName, absl::Duration interval)
schedule(string_view categoryName, absl::Duration interval)
{
return schedule(m_manager->registry().get(categoryName), interval);
}
@ -1119,7 +1169,7 @@ namespace llarp
setDefault(absl::Duration interval);
bool
cancel(const char *categoryName)
cancel(string_view categoryName)
{
return cancel(m_manager->registry().get(categoryName));
}
@ -1145,7 +1195,7 @@ namespace llarp
}
absl::optional< absl::Duration >
find(const char *categoryName) const
find(string_view categoryName) const
{
return find(m_manager->registry().get(categoryName));
}

View File

@ -2,6 +2,8 @@
#include <util/printer.hpp>
#include <absl/strings/str_join.h>
namespace llarp
{
namespace metrics
@ -13,7 +15,8 @@ namespace llarp
static constexpr size_t INIT_SIZE = 32;
char buf[INIT_SIZE] = {0};
int rc = snprintf(buf, INIT_SIZE, format.m_format, data * format.m_scale);
int rc = snprintf(buf, INIT_SIZE, format.m_format.data(),
data * format.m_scale);
if(rc < 0)
{
@ -28,7 +31,7 @@ namespace llarp
}
std::vector< char > vec(rc + 1);
rc = snprintf(vec.data(), vec.size(), format.m_format,
rc = snprintf(vec.data(), vec.size(), format.m_format.data(),
data * format.m_scale);
if(static_cast< size_t >(rc) > vec.size())
@ -127,7 +130,7 @@ namespace llarp
Description::toString() const
{
util::Lock l(&m_mutex);
return m_category->name() + std::string(".") + m_name;
return absl::StrCat(m_category->name(), ".", m_name);
}
std::ostream &

View File

@ -7,8 +7,11 @@
#include <util/types.hpp>
#include <util/variant.hpp>
#include <absl/types/span.h>
#include <absl/container/flat_hash_map.h>
#include <absl/container/flat_hash_set.h>
#include <absl/hash/hash.h>
#include <absl/types/optional.h>
#include <absl/types/span.h>
#include <absl/types/variant.h>
#include <cstring>
#include <iosfwd>
@ -49,7 +52,7 @@ namespace llarp
struct FormatSpec
{
float m_scale;
const char *m_format;
string_view m_format;
static constexpr char DEFAULT_FORMAT[] = "%f";
@ -57,7 +60,7 @@ namespace llarp
{
}
constexpr FormatSpec(float scale, const char *format)
constexpr FormatSpec(float scale, string_view format)
: m_scale(scale), m_format(format)
{
}
@ -69,8 +72,8 @@ namespace llarp
inline bool
operator==(const FormatSpec &lhs, const FormatSpec &rhs)
{
return lhs.m_scale == rhs.m_scale
&& std::strcmp(lhs.m_format, rhs.m_format) == 0;
return std::make_tuple(lhs.m_scale, lhs.m_format)
== std::make_tuple(rhs.m_scale, rhs.m_format);
}
struct Format
@ -114,12 +117,12 @@ namespace llarp
/// Represents a category of grouped metrics
class Category
{
const char *m_name;
string_view m_name;
std::atomic_bool m_enabled;
CategoryContainer *m_container;
public:
Category(const char *name, bool enabled = true)
Category(string_view name, bool enabled = true)
: m_name(name), m_enabled(enabled), m_container(nullptr)
{
}
@ -138,7 +141,7 @@ namespace llarp
return m_enabled;
}
const char *
string_view
name() const
{
return m_name;
@ -180,7 +183,7 @@ namespace llarp
mutable util::Mutex m_mutex;
const Category *m_category GUARDED_BY(m_mutex);
const char *m_name GUARDED_BY(m_mutex);
string_view m_name GUARDED_BY(m_mutex);
Publication::Type m_type GUARDED_BY(m_mutex);
std::shared_ptr< Format > m_format GUARDED_BY(m_mutex);
@ -189,7 +192,7 @@ namespace llarp
operator=(const Description &) = delete;
public:
Description(const Category *category, const char *name)
Description(const Category *category, string_view name)
: m_category(category)
, m_name(name)
, m_type(Publication::Type::Unspecified)
@ -211,13 +214,13 @@ namespace llarp
}
void
name(const char *n)
name(string_view n)
{
util::Lock l(&m_mutex);
m_name = n;
}
const char *
string_view
name() const
{
util::Lock l(&m_mutex);
@ -310,14 +313,14 @@ namespace llarp
return m_description->category();
}
const char *
string_view
categoryName() const
{
assert(valid());
return m_description->category()->name();
}
const char *
string_view
metricName() const
{
assert(valid());
@ -394,7 +397,6 @@ namespace llarp
template < typename Type >
class Record
{
Id m_id;
size_t m_count;
Type m_total;
Type m_min;
@ -407,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; }
@ -459,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);
@ -480,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 >
@ -492,17 +476,67 @@ namespace llarp
return !(lhs == rhs);
}
using Tag = std::string;
using TagValue = absl::variant< std::string, double, std::int64_t >;
using Tags = std::set< std::pair< Tag, TagValue > >;
template < typename Type >
using TaggedRecordsData = absl::flat_hash_map< Tags, Record< Type > >;
template < typename Type >
struct TaggedRecords
{
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
{
absl::Span< const Record< Type > > m_records;
absl::Duration m_samplePeriod;
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()
{
}
@ -593,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;
@ -601,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();
@ -626,35 +660,19 @@ 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

View File

@ -9,7 +9,8 @@ namespace llarp
void
OStreamLogStream::PreLog(std::stringstream& ss, LogLevel lvl,
const char* fname, int lineno) const
const char* fname, int lineno,
const std::string& nodename) const
{
switch(lvl)
{
@ -17,23 +18,20 @@ namespace llarp
break;
case eLogDebug:
ss << (char)27 << "[0m";
ss << "[DBG] ";
break;
case eLogInfo:
ss << (char)27 << "[1m";
ss << "[NFO] ";
break;
case eLogWarn:
ss << (char)27 << "[1;33m";
ss << "[WRN] ";
break;
case eLogError:
ss << (char)27 << "[1;31m";
ss << "[ERR] ";
break;
}
ss << "(" << thread_id_string() << ") " << log_timestamp() << " " << fname
ss << "[" << LogLevelToString(lvl) << "] ";
ss << "[" << nodename << "]"
<< "(" << thread_id_string() << ") " << log_timestamp() << " " << fname
<< ":" << lineno << "\t";
}

View File

@ -15,8 +15,8 @@ namespace llarp
}
virtual void
PreLog(std::stringstream& s, LogLevel lvl, const char* fname,
int lineno) const override;
PreLog(std::stringstream& s, LogLevel lvl, const char* fname, int lineno,
const std::string& nodename) const override;
void
Print(LogLevel lvl, const char* tag, const std::string& msg) override;

View File

@ -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,

View File

@ -6,27 +6,11 @@ namespace llarp
{
void
SysLogStream::PreLog(std::stringstream& ss, LogLevel lvl, const char* fname,
int lineno) const
int lineno, const std::string& nodename) const
{
switch(lvl)
{
case eLogNone:
break;
case eLogDebug:
ss << "[DBG] ";
break;
case eLogInfo:
ss << "[NFO] ";
break;
case eLogWarn:
ss << "[WRN] ";
break;
case eLogError:
ss << "[ERR] ";
break;
}
ss << "(" << thread_id_string() << ") " << log_timestamp() << " " << fname
ss << "[" << LogLevelToString(lvl) << "] ";
ss << "[" << nodename << "]"
<< "(" << thread_id_string() << ") " << log_timestamp() << " " << fname
<< ":" << lineno << "\t";
}

View File

@ -42,8 +42,8 @@ namespace llarp
if(s > 0)
lastSend = parent->Now();
METRICS_DYNAMIC_INT_UPDATE(
"utp.session.tx", RouterID(remoteRC.pubkey).ToString().c_str(), s);
metrics::integerTick("utp.session.tx", "writes", s, "id",
RouterID(remoteRC.pubkey).ToString());
m_TXRate += s;
size_t sz = s;
while(vecq.size() && sz >= vecq.front().iov_len)
@ -133,9 +133,8 @@ namespace llarp
PruneInboundMessages(now);
m_TXRate = 0;
m_RXRate = 0;
METRICS_DYNAMIC_UPDATE("utp.session.sendq",
RouterID(remoteRC.pubkey).ToString().c_str(),
sendq.size());
metrics::integerTick("utp.session.sendq", "size", sendq.size(), "id",
RouterID(remoteRC.pubkey).ToString());
}
/// low level read
@ -146,8 +145,8 @@ namespace llarp
Alive();
m_RXRate += sz;
size_t s = sz;
METRICS_DYNAMIC_INT_UPDATE(
"utp.session.rx", RouterID(remoteRC.pubkey).ToString().c_str(), s);
metrics::integerTick("utp.session.rx", "size", s, "id",
RouterID(remoteRC.pubkey).ToString());
// process leftovers
if(recvBufOffset)
{

View File

@ -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));

View File

@ -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());
auto record1 = collector1.load();
ASSERT_EQ(METRIC_A, record1.id.description());
ASSERT_THAT(record1.data, IsEmpty());
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 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);
@ -198,8 +192,8 @@ TEST(MetricsCore, RegistryOps)
ASSERT_TRUE(id.valid()) << id;
ASSERT_NE(nullptr, id.description());
ASSERT_NE(nullptr, id.category());
ASSERT_STREQ(id.metricName(), NAME);
ASSERT_STREQ(id.categoryName(), CATEGORY);
ASSERT_EQ(id.metricName(), NAME);
ASSERT_EQ(id.categoryName(), CATEGORY);
ASSERT_TRUE(id.category()->enabled());
// Attempt to find the id.
@ -224,7 +218,7 @@ TEST(MetricsCore, RegistryOps)
const Category *NEW_CAT = registry.add("NewCategory");
ASSERT_NE(nullptr, NEW_CAT);
ASSERT_STREQ("NewCategory", NEW_CAT->name());
ASSERT_EQ("NewCategory", NEW_CAT->name());
ASSERT_TRUE(NEW_CAT->enabled());
}
@ -238,7 +232,7 @@ TEST(MetricsCore, RegistryOps)
const Category *cat = registry.add(CATEGORY);
ASSERT_NE(nullptr, cat);
ASSERT_STREQ(cat->name(), CATEGORY);
ASSERT_EQ(cat->name(), CATEGORY);
ASSERT_TRUE(cat->enabled());
ASSERT_EQ(nullptr, registry.add(CATEGORY));
@ -247,8 +241,8 @@ TEST(MetricsCore, RegistryOps)
Id id = registry.add(CATEGORY, "Metric");
ASSERT_TRUE(id.valid());
ASSERT_EQ(cat, id.category());
ASSERT_STREQ(id.categoryName(), CATEGORY);
ASSERT_STREQ(id.metricName(), "Metric");
ASSERT_EQ(id.categoryName(), CATEGORY);
ASSERT_EQ(id.metricName(), "Metric");
ASSERT_EQ(i + 1, registry.metricCount());
ASSERT_EQ(i + 1, registry.categoryCount());
@ -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())
string_view 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

View File

@ -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
@ -60,11 +62,11 @@ TEST(MetricsTypes, Format)
ASSERT_EQ(nullptr, format.specFor(metrics::Publication::Type::Avg));
auto ptr = format.specFor(metrics::Publication::Type::Total);
ASSERT_NE(nullptr, ptr);
ASSERT_STREQ("%0.3f", ptr->m_format);
ASSERT_EQ("%0.3f", ptr->m_format);
ASSERT_DOUBLE_EQ(2.0, ptr->m_scale);
ptr = format.specFor(metrics::Publication::Type::Max);
ASSERT_NE(nullptr, ptr);
ASSERT_STREQ("%0.2f", ptr->m_format);
ASSERT_EQ("%0.2f", ptr->m_format);
ASSERT_DOUBLE_EQ(1.0, ptr->m_scale);
format.clear();
@ -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';