Accept initializer_list, span, and string_view in api
This commit is contained in:
116
src/metric.cpp
116
src/metric.cpp
@@ -56,22 +56,22 @@ namespace metric {
|
|||||||
|
|
||||||
// Validation helper that works in both debug and release builds
|
// Validation helper that works in both debug and release builds
|
||||||
static void validate_or_abort(bool condition, const char *message,
|
static void validate_or_abort(bool condition, const char *message,
|
||||||
const char *value) {
|
std::string_view value) {
|
||||||
if (!condition) {
|
if (!condition) {
|
||||||
std::fprintf(stderr, "WeaselDB metric validation failed: %s: '%s'\n",
|
std::fprintf(stderr, "WeaselDB metric validation failed: %s: '%.*s'\n",
|
||||||
message, value);
|
message, static_cast<int>(value.size()), value.data());
|
||||||
std::abort();
|
std::abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper to copy a string into arena memory
|
// Helper to copy a string into arena memory
|
||||||
static std::string_view arena_copy_string(const std::string &str,
|
static std::string_view arena_copy_string(std::string_view str,
|
||||||
ArenaAllocator &arena) {
|
ArenaAllocator &arena) {
|
||||||
if (str.empty()) {
|
if (str.empty()) {
|
||||||
return std::string_view{};
|
return std::string_view{};
|
||||||
}
|
}
|
||||||
char *copied = arena.allocate<char>(str.size() + 1);
|
char *copied = arena.allocate<char>(str.size() + 1);
|
||||||
std::memcpy(copied, str.c_str(), str.size());
|
std::memcpy(copied, str.data(), str.size());
|
||||||
copied[str.size()] = '\0';
|
copied[str.size()] = '\0';
|
||||||
return std::string_view(copied, str.size());
|
return std::string_view(copied, str.size());
|
||||||
}
|
}
|
||||||
@@ -81,15 +81,14 @@ static std::string_view arena_copy_string(const std::string &str,
|
|||||||
struct LabelsKey {
|
struct LabelsKey {
|
||||||
ArenaVector<std::pair<std::string_view, std::string_view>> labels;
|
ArenaVector<std::pair<std::string_view, std::string_view>> labels;
|
||||||
|
|
||||||
LabelsKey(const std::vector<std::pair<std::string, std::string>> &l,
|
LabelsKey(std::span<const std::pair<std::string_view, std::string_view>> l,
|
||||||
ArenaAllocator &arena)
|
ArenaAllocator &arena)
|
||||||
: labels(&arena) {
|
: labels(&arena) {
|
||||||
// Copy and validate all label keys and values into arena
|
// Copy and validate all label keys and values into arena
|
||||||
for (const auto &[key, value] : l) {
|
for (const auto &[key, value] : l) {
|
||||||
validate_or_abort(is_valid_label_key(key), "invalid label key",
|
validate_or_abort(is_valid_label_key(key), "invalid label key", key);
|
||||||
key.c_str());
|
|
||||||
validate_or_abort(is_valid_label_value(value), "invalid label value",
|
validate_or_abort(is_valid_label_value(value), "invalid label value",
|
||||||
value.c_str());
|
value);
|
||||||
|
|
||||||
auto key_view = arena_copy_string(key, arena);
|
auto key_view = arena_copy_string(key, arena);
|
||||||
auto value_view = arena_copy_string(value, arena);
|
auto value_view = arena_copy_string(value, arena);
|
||||||
@@ -382,19 +381,18 @@ struct Metric {
|
|||||||
|
|
||||||
static Counter create_counter_instance(
|
static Counter create_counter_instance(
|
||||||
Family<Counter> *family,
|
Family<Counter> *family,
|
||||||
const std::vector<std::pair<std::string, std::string>> &labels) {
|
std::span<const std::pair<std::string_view, std::string_view>> labels) {
|
||||||
// Force thread_local initialization
|
// Force thread_local initialization
|
||||||
(void)thread_init;
|
(void)thread_init;
|
||||||
|
|
||||||
std::unique_lock<std::mutex> _{mutex};
|
std::unique_lock<std::mutex> _{mutex};
|
||||||
LabelsKey key{labels, get_thread_local_arena()};
|
LabelsKey key{labels, get_global_arena()};
|
||||||
|
|
||||||
// Validate that labels aren't already registered as callback
|
// Validate that labels aren't already registered as callback
|
||||||
validate_or_abort(
|
validate_or_abort(family->p->callbacks.find(key) ==
|
||||||
family->p->callbacks.find(key) == family->p->callbacks.end(),
|
family->p->callbacks.end(),
|
||||||
"labels already registered as callback",
|
"labels already registered as callback",
|
||||||
key.labels.empty() ? "(no labels)"
|
key.labels.empty() ? "(no labels)" : key.labels[0].first);
|
||||||
: std::string(key.labels[0].first).c_str());
|
|
||||||
|
|
||||||
// Ensure thread state exists
|
// Ensure thread state exists
|
||||||
auto thread_id = std::this_thread::get_id();
|
auto thread_id = std::this_thread::get_id();
|
||||||
@@ -419,16 +417,15 @@ struct Metric {
|
|||||||
|
|
||||||
static Gauge create_gauge_instance(
|
static Gauge create_gauge_instance(
|
||||||
Family<Gauge> *family,
|
Family<Gauge> *family,
|
||||||
const std::vector<std::pair<std::string, std::string>> &labels) {
|
std::span<const std::pair<std::string_view, std::string_view>> labels) {
|
||||||
std::unique_lock<std::mutex> _{mutex};
|
std::unique_lock<std::mutex> _{mutex};
|
||||||
LabelsKey key{labels, get_global_arena()};
|
LabelsKey key{labels, get_global_arena()};
|
||||||
|
|
||||||
// Validate that labels aren't already registered as callback
|
// Validate that labels aren't already registered as callback
|
||||||
validate_or_abort(
|
validate_or_abort(family->p->callbacks.find(key) ==
|
||||||
family->p->callbacks.find(key) == family->p->callbacks.end(),
|
family->p->callbacks.end(),
|
||||||
"labels already registered as callback",
|
"labels already registered as callback",
|
||||||
key.labels.empty() ? "(no labels)"
|
key.labels.empty() ? "(no labels)" : key.labels[0].first);
|
||||||
: std::string(key.labels[0].first).c_str());
|
|
||||||
|
|
||||||
auto &ptr = family->p->instances[key];
|
auto &ptr = family->p->instances[key];
|
||||||
if (!ptr) {
|
if (!ptr) {
|
||||||
@@ -442,12 +439,12 @@ struct Metric {
|
|||||||
|
|
||||||
static Histogram create_histogram_instance(
|
static Histogram create_histogram_instance(
|
||||||
Family<Histogram> *family,
|
Family<Histogram> *family,
|
||||||
const std::vector<std::pair<std::string, std::string>> &labels) {
|
std::span<const std::pair<std::string_view, std::string_view>> labels) {
|
||||||
// Force thread_local initialization
|
// Force thread_local initialization
|
||||||
(void)thread_init;
|
(void)thread_init;
|
||||||
|
|
||||||
std::unique_lock<std::mutex> _{mutex};
|
std::unique_lock<std::mutex> _{mutex};
|
||||||
LabelsKey key{labels, get_thread_local_arena()};
|
LabelsKey key{labels, get_global_arena()};
|
||||||
|
|
||||||
// Ensure thread state exists
|
// Ensure thread state exists
|
||||||
auto thread_id = std::this_thread::get_id();
|
auto thread_id = std::this_thread::get_id();
|
||||||
@@ -512,7 +509,7 @@ void Counter::inc(double x) {
|
|||||||
// Validate monotonic property (counter never decreases)
|
// Validate monotonic property (counter never decreases)
|
||||||
if (new_value < p->value) [[unlikely]] {
|
if (new_value < p->value) [[unlikely]] {
|
||||||
validate_or_abort(false, "counter value overflow/wraparound detected",
|
validate_or_abort(false, "counter value overflow/wraparound detected",
|
||||||
std::to_string(new_value).c_str());
|
std::to_string(new_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
__atomic_store(&p->value, &new_value, __ATOMIC_RELAXED);
|
__atomic_store(&p->value, &new_value, __ATOMIC_RELAXED);
|
||||||
@@ -605,25 +602,24 @@ template <> Family<Histogram>::Family() = default;
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
Counter Family<Counter>::create(
|
Counter Family<Counter>::create(
|
||||||
std::vector<std::pair<std::string, std::string>> labels) {
|
std::span<const std::pair<std::string_view, std::string_view>> labels) {
|
||||||
return Metric::create_counter_instance(this, labels);
|
return Metric::create_counter_instance(this, labels);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
Gauge Family<Gauge>::create(
|
Gauge Family<Gauge>::create(
|
||||||
std::vector<std::pair<std::string, std::string>> labels) {
|
std::span<const std::pair<std::string_view, std::string_view>> labels) {
|
||||||
return Metric::create_gauge_instance(this, labels);
|
return Metric::create_gauge_instance(this, labels);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
Histogram Family<Histogram>::create(
|
Histogram Family<Histogram>::create(
|
||||||
std::vector<std::pair<std::string, std::string>> labels) {
|
std::span<const std::pair<std::string_view, std::string_view>> labels) {
|
||||||
return Metric::create_histogram_instance(this, labels);
|
return Metric::create_histogram_instance(this, labels);
|
||||||
}
|
}
|
||||||
|
|
||||||
Family<Counter> create_counter(std::string name, std::string help) {
|
Family<Counter> create_counter(std::string_view name, std::string_view help) {
|
||||||
validate_or_abort(is_valid_metric_name(name), "invalid counter name",
|
validate_or_abort(is_valid_metric_name(name), "invalid counter name", name);
|
||||||
name.c_str());
|
|
||||||
|
|
||||||
std::unique_lock<std::mutex> _{Metric::mutex};
|
std::unique_lock<std::mutex> _{Metric::mutex};
|
||||||
auto &global_arena = Metric::get_global_arena();
|
auto &global_arena = Metric::get_global_arena();
|
||||||
@@ -640,17 +636,15 @@ Family<Counter> create_counter(std::string name, std::string help) {
|
|||||||
} else {
|
} else {
|
||||||
validate_or_abort(
|
validate_or_abort(
|
||||||
familyPtr->help == help,
|
familyPtr->help == help,
|
||||||
"metric family already registered with different help text",
|
"metric family already registered with different help text", name);
|
||||||
name.c_str());
|
|
||||||
}
|
}
|
||||||
Family<Counter> family;
|
Family<Counter> family;
|
||||||
family.p = familyPtr;
|
family.p = familyPtr;
|
||||||
return family;
|
return family;
|
||||||
}
|
}
|
||||||
|
|
||||||
Family<Gauge> create_gauge(std::string name, std::string help) {
|
Family<Gauge> create_gauge(std::string_view name, std::string_view help) {
|
||||||
validate_or_abort(is_valid_metric_name(name), "invalid gauge name",
|
validate_or_abort(is_valid_metric_name(name), "invalid gauge name", name);
|
||||||
name.c_str());
|
|
||||||
|
|
||||||
std::unique_lock<std::mutex> _{Metric::mutex};
|
std::unique_lock<std::mutex> _{Metric::mutex};
|
||||||
auto &global_arena = Metric::get_global_arena();
|
auto &global_arena = Metric::get_global_arena();
|
||||||
@@ -667,18 +661,16 @@ Family<Gauge> create_gauge(std::string name, std::string help) {
|
|||||||
} else {
|
} else {
|
||||||
validate_or_abort(
|
validate_or_abort(
|
||||||
familyPtr->help == help,
|
familyPtr->help == help,
|
||||||
"metric family already registered with different help text",
|
"metric family already registered with different help text", name);
|
||||||
name.c_str());
|
|
||||||
}
|
}
|
||||||
Family<Gauge> family;
|
Family<Gauge> family;
|
||||||
family.p = familyPtr;
|
family.p = familyPtr;
|
||||||
return family;
|
return family;
|
||||||
}
|
}
|
||||||
|
|
||||||
Family<Histogram> create_histogram(std::string name, std::string help,
|
Family<Histogram> create_histogram(std::string_view name, std::string_view help,
|
||||||
std::span<const double> buckets) {
|
std::span<const double> buckets) {
|
||||||
validate_or_abort(is_valid_metric_name(name), "invalid histogram name",
|
validate_or_abort(is_valid_metric_name(name), "invalid histogram name", name);
|
||||||
name.c_str());
|
|
||||||
|
|
||||||
std::unique_lock<std::mutex> _{Metric::mutex};
|
std::unique_lock<std::mutex> _{Metric::mutex};
|
||||||
auto &global_arena = Metric::get_global_arena();
|
auto &global_arena = Metric::get_global_arena();
|
||||||
@@ -709,8 +701,7 @@ Family<Histogram> create_histogram(std::string name, std::string help,
|
|||||||
} else {
|
} else {
|
||||||
validate_or_abort(
|
validate_or_abort(
|
||||||
family_ptr->help == help,
|
family_ptr->help == help,
|
||||||
"metric family already registered with different help text",
|
"metric family already registered with different help text", name);
|
||||||
name.c_str());
|
|
||||||
std::vector<double> new_buckets_vec(buckets.begin(), buckets.end());
|
std::vector<double> new_buckets_vec(buckets.begin(), buckets.end());
|
||||||
std::sort(new_buckets_vec.begin(), new_buckets_vec.end());
|
std::sort(new_buckets_vec.begin(), new_buckets_vec.end());
|
||||||
new_buckets_vec.erase(
|
new_buckets_vec.erase(
|
||||||
@@ -731,7 +722,7 @@ Family<Histogram> create_histogram(std::string name, std::string help,
|
|||||||
}
|
}
|
||||||
validate_or_abort(buckets_match,
|
validate_or_abort(buckets_match,
|
||||||
"metric family already registered with different buckets",
|
"metric family already registered with different buckets",
|
||||||
name.c_str());
|
name);
|
||||||
}
|
}
|
||||||
Family<Histogram> family;
|
Family<Histogram> family;
|
||||||
family.p = family_ptr;
|
family.p = family_ptr;
|
||||||
@@ -740,9 +731,9 @@ Family<Histogram> create_histogram(std::string name, std::string help,
|
|||||||
|
|
||||||
std::vector<double> linear_buckets(double start, double width, int count) {
|
std::vector<double> linear_buckets(double start, double width, int count) {
|
||||||
validate_or_abort(width > 0, "linear bucket width must be positive",
|
validate_or_abort(width > 0, "linear bucket width must be positive",
|
||||||
std::to_string(width).c_str());
|
std::to_string(width));
|
||||||
validate_or_abort(count >= 0, "linear bucket count must be non-negative",
|
validate_or_abort(count >= 0, "linear bucket count must be non-negative",
|
||||||
std::to_string(count).c_str());
|
std::to_string(count));
|
||||||
|
|
||||||
std::vector<double> buckets;
|
std::vector<double> buckets;
|
||||||
buckets.reserve(count);
|
buckets.reserve(count);
|
||||||
@@ -757,11 +748,11 @@ std::vector<double> linear_buckets(double start, double width, int count) {
|
|||||||
std::vector<double> exponential_buckets(double start, double factor,
|
std::vector<double> exponential_buckets(double start, double factor,
|
||||||
int count) {
|
int count) {
|
||||||
validate_or_abort(start > 0, "exponential bucket start must be positive",
|
validate_or_abort(start > 0, "exponential bucket start must be positive",
|
||||||
std::to_string(start).c_str());
|
std::to_string(start));
|
||||||
validate_or_abort(factor > 1, "exponential bucket factor must be > 1",
|
validate_or_abort(factor > 1, "exponential bucket factor must be > 1",
|
||||||
std::to_string(factor).c_str());
|
std::to_string(factor));
|
||||||
validate_or_abort(count >= 0, "exponential bucket count must be non-negative",
|
validate_or_abort(count >= 0, "exponential bucket count must be non-negative",
|
||||||
std::to_string(count).c_str());
|
std::to_string(count));
|
||||||
|
|
||||||
std::vector<double> buckets;
|
std::vector<double> buckets;
|
||||||
buckets.reserve(count);
|
buckets.reserve(count);
|
||||||
@@ -777,7 +768,7 @@ std::vector<double> exponential_buckets(double start, double factor,
|
|||||||
|
|
||||||
// Prometheus validation functions
|
// Prometheus validation functions
|
||||||
// Metric names must match [a-zA-Z_:][a-zA-Z0-9_:]*
|
// Metric names must match [a-zA-Z_:][a-zA-Z0-9_:]*
|
||||||
bool is_valid_metric_name(const std::string &name) {
|
bool is_valid_metric_name(std::string_view name) {
|
||||||
if (name.empty())
|
if (name.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -799,7 +790,7 @@ bool is_valid_metric_name(const std::string &name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Label keys must match [a-zA-Z_][a-zA-Z0-9_]*
|
// Label keys must match [a-zA-Z_][a-zA-Z0-9_]*
|
||||||
bool is_valid_label_key(const std::string &key) {
|
bool is_valid_label_key(std::string_view key) {
|
||||||
if (key.empty())
|
if (key.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -826,10 +817,10 @@ bool is_valid_label_key(const std::string &key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Label values can contain any UTF-8 characters (no specific restrictions)
|
// Label values can contain any UTF-8 characters (no specific restrictions)
|
||||||
bool is_valid_label_value(const std::string &value) {
|
bool is_valid_label_value(std::string_view value) {
|
||||||
// Prometheus allows any UTF-8 string as label value
|
// Prometheus allows any UTF-8 string as label value
|
||||||
// Validate UTF-8 encoding for correctness using simdutf
|
// Validate UTF-8 encoding for correctness using simdutf
|
||||||
return simdutf::validate_utf8(value.c_str(), value.size());
|
return simdutf::validate_utf8(value.data(), value.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::span<std::string_view> render(ArenaAllocator &arena) {
|
std::span<std::string_view> render(ArenaAllocator &arena) {
|
||||||
@@ -1169,33 +1160,30 @@ std::span<std::string_view> render(ArenaAllocator &arena) {
|
|||||||
// Template specialization implementations for register_callback
|
// Template specialization implementations for register_callback
|
||||||
template <>
|
template <>
|
||||||
void Family<Counter>::register_callback(
|
void Family<Counter>::register_callback(
|
||||||
std::vector<std::pair<std::string, std::string>> labels,
|
std::span<const std::pair<std::string_view, std::string_view>> labels,
|
||||||
MetricCallback<Counter> callback) {
|
MetricCallback<Counter> callback) {
|
||||||
std::unique_lock<std::mutex> _{Metric::mutex};
|
std::unique_lock<std::mutex> _{Metric::mutex};
|
||||||
LabelsKey key{labels, Metric::get_global_arena()};
|
LabelsKey key{labels, Metric::get_global_arena()};
|
||||||
|
|
||||||
// Validate that labels aren't already in use by create() calls
|
// Validate that labels aren't already in use by create() calls
|
||||||
for (const auto &[thread_id, per_thread] : p->per_thread_state) {
|
for (const auto &[thread_id, per_thread] : p->per_thread_state) {
|
||||||
validate_or_abort(
|
validate_or_abort(per_thread.instances.find(key) ==
|
||||||
per_thread.instances.find(key) == per_thread.instances.end(),
|
per_thread.instances.end(),
|
||||||
"labels already registered as static instance",
|
"labels already registered as static instance",
|
||||||
key.labels.empty() ? "(no labels)"
|
key.labels.empty() ? "(no labels)" : key.labels[0].first);
|
||||||
: std::string(key.labels[0].first).c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate that callback isn't already registered for these labels
|
// Validate that callback isn't already registered for these labels
|
||||||
validate_or_abort(p->callbacks.find(key) == p->callbacks.end(),
|
validate_or_abort(p->callbacks.find(key) == p->callbacks.end(),
|
||||||
"callback already registered for labels",
|
"callback already registered for labels",
|
||||||
key.labels.empty()
|
key.labels.empty() ? "(no labels)" : key.labels[0].first);
|
||||||
? "(no labels)"
|
|
||||||
: std::string(key.labels[0].first).c_str());
|
|
||||||
|
|
||||||
p->callbacks[std::move(key)] = std::move(callback);
|
p->callbacks[std::move(key)] = std::move(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void Family<Gauge>::register_callback(
|
void Family<Gauge>::register_callback(
|
||||||
std::vector<std::pair<std::string, std::string>> labels,
|
std::span<const std::pair<std::string_view, std::string_view>> labels,
|
||||||
MetricCallback<Gauge> callback) {
|
MetricCallback<Gauge> callback) {
|
||||||
std::unique_lock<std::mutex> _{Metric::mutex};
|
std::unique_lock<std::mutex> _{Metric::mutex};
|
||||||
LabelsKey key{labels, Metric::get_global_arena()};
|
LabelsKey key{labels, Metric::get_global_arena()};
|
||||||
@@ -1203,16 +1191,12 @@ void Family<Gauge>::register_callback(
|
|||||||
// Validate that labels aren't already in use by create() calls
|
// Validate that labels aren't already in use by create() calls
|
||||||
validate_or_abort(p->instances.find(key) == p->instances.end(),
|
validate_or_abort(p->instances.find(key) == p->instances.end(),
|
||||||
"labels already registered as static instance",
|
"labels already registered as static instance",
|
||||||
key.labels.empty()
|
key.labels.empty() ? "(no labels)" : key.labels[0].first);
|
||||||
? "(no labels)"
|
|
||||||
: std::string(key.labels[0].first).c_str());
|
|
||||||
|
|
||||||
// Validate that callback isn't already registered for these labels
|
// Validate that callback isn't already registered for these labels
|
||||||
validate_or_abort(p->callbacks.find(key) == p->callbacks.end(),
|
validate_or_abort(p->callbacks.find(key) == p->callbacks.end(),
|
||||||
"callback already registered for labels",
|
"callback already registered for labels",
|
||||||
key.labels.empty()
|
key.labels.empty() ? "(no labels)" : key.labels[0].first);
|
||||||
? "(no labels)"
|
|
||||||
: std::string(key.labels[0].first).c_str());
|
|
||||||
|
|
||||||
p->callbacks[std::move(key)] = std::move(callback);
|
p->callbacks[std::move(key)] = std::move(callback);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,8 +36,8 @@
|
|||||||
// histogram.observe(0.25); // ONLY call from creating thread
|
// histogram.observe(0.25); // ONLY call from creating thread
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <initializer_list>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <string>
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -127,20 +127,36 @@ template <class T> struct Family {
|
|||||||
// Labels are sorted by key for Prometheus compatibility
|
// Labels are sorted by key for Prometheus compatibility
|
||||||
// ERROR: Will abort if labels already registered via register_callback()
|
// ERROR: Will abort if labels already registered via register_callback()
|
||||||
// OK: Multiple calls with same labels return same instance (idempotent)
|
// OK: Multiple calls with same labels return same instance (idempotent)
|
||||||
T create(std::vector<std::pair<std::string, std::string>> labels);
|
T create(std::initializer_list<std::pair<std::string_view, std::string_view>>
|
||||||
|
labels) {
|
||||||
|
return create(
|
||||||
|
std::span<const std::pair<std::string_view, std::string_view>>(
|
||||||
|
labels.begin(), labels.end()));
|
||||||
|
}
|
||||||
|
T create(
|
||||||
|
std::span<const std::pair<std::string_view, std::string_view>> labels);
|
||||||
|
|
||||||
// Register callback-based metric (Counter and Gauge only)
|
// Register callback-based metric (Counter and Gauge only)
|
||||||
// Validates that label set isn't already taken
|
// Validates that label set isn't already taken
|
||||||
void
|
void register_callback(
|
||||||
register_callback(std::vector<std::pair<std::string, std::string>> labels,
|
std::initializer_list<std::pair<std::string_view, std::string_view>>
|
||||||
|
labels,
|
||||||
|
MetricCallback<T> callback) {
|
||||||
|
register_callback(
|
||||||
|
std::span<const std::pair<std::string_view, std::string_view>>(
|
||||||
|
labels.begin(), labels.end()),
|
||||||
|
callback);
|
||||||
|
}
|
||||||
|
void register_callback(
|
||||||
|
std::span<const std::pair<std::string_view, std::string_view>> labels,
|
||||||
MetricCallback<T> callback);
|
MetricCallback<T> callback);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Family();
|
Family();
|
||||||
friend struct Metric;
|
friend struct Metric;
|
||||||
friend Family<Counter> create_counter(std::string, std::string);
|
friend Family<Counter> create_counter(std::string_view, std::string_view);
|
||||||
friend Family<Gauge> create_gauge(std::string, std::string);
|
friend Family<Gauge> create_gauge(std::string_view, std::string_view);
|
||||||
friend Family<Histogram> create_histogram(std::string, std::string,
|
friend Family<Histogram> create_histogram(std::string_view, std::string_view,
|
||||||
std::span<const double>);
|
std::span<const double>);
|
||||||
|
|
||||||
struct State;
|
struct State;
|
||||||
@@ -153,19 +169,25 @@ private:
|
|||||||
// Create counter family (monotonically increasing values)
|
// Create counter family (monotonically increasing values)
|
||||||
// ERROR: Aborts if family with same name is registered with different help
|
// ERROR: Aborts if family with same name is registered with different help
|
||||||
// text.
|
// text.
|
||||||
Family<Counter> create_counter(std::string name, std::string help);
|
Family<Counter> create_counter(std::string_view name, std::string_view help);
|
||||||
|
|
||||||
// Create gauge family (can increase/decrease)
|
// Create gauge family (can increase/decrease)
|
||||||
// ERROR: Aborts if family with same name is registered with different help
|
// ERROR: Aborts if family with same name is registered with different help
|
||||||
// text.
|
// text.
|
||||||
Family<Gauge> create_gauge(std::string name, std::string help);
|
Family<Gauge> create_gauge(std::string_view name, std::string_view help);
|
||||||
|
|
||||||
// Create histogram family with custom buckets
|
// Create histogram family with custom buckets
|
||||||
// Buckets will be sorted, deduplicated, and +Inf will be added automatically
|
// Buckets will be sorted, deduplicated, and +Inf will be added automatically
|
||||||
// ERROR: Aborts if family with same name is registered with different help text
|
// ERROR: Aborts if family with same name is registered with different help text
|
||||||
// or buckets.
|
// or buckets.
|
||||||
Family<Histogram> create_histogram(std::string name, std::string help,
|
Family<Histogram> create_histogram(std::string_view name, std::string_view help,
|
||||||
std::span<const double> buckets);
|
std::span<const double> buckets);
|
||||||
|
inline Family<Histogram>
|
||||||
|
create_histogram(std::string_view name, std::string_view help,
|
||||||
|
std::initializer_list<double> buckets) {
|
||||||
|
return create_histogram(
|
||||||
|
name, help, std::span<const double>(buckets.begin(), buckets.end()));
|
||||||
|
}
|
||||||
|
|
||||||
// Helper functions for generating standard histogram buckets
|
// Helper functions for generating standard histogram buckets
|
||||||
// Following Prometheus client library conventions
|
// Following Prometheus client library conventions
|
||||||
@@ -189,9 +211,9 @@ std::vector<double> exponential_buckets(double start, double factor, int count);
|
|||||||
std::span<std::string_view> render(ArenaAllocator &arena);
|
std::span<std::string_view> render(ArenaAllocator &arena);
|
||||||
|
|
||||||
// Validation functions for Prometheus compatibility
|
// Validation functions for Prometheus compatibility
|
||||||
bool is_valid_metric_name(const std::string &name);
|
bool is_valid_metric_name(std::string_view name);
|
||||||
bool is_valid_label_key(const std::string &key);
|
bool is_valid_label_key(std::string_view key);
|
||||||
bool is_valid_label_value(const std::string &value);
|
bool is_valid_label_value(std::string_view value);
|
||||||
|
|
||||||
// Note: Histograms do not support callbacks due to their multi-value nature
|
// Note: Histograms do not support callbacks due to their multi-value nature
|
||||||
// (buckets + sum + count). Use static histogram metrics only.
|
// (buckets + sum + count). Use static histogram metrics only.
|
||||||
|
|||||||
Reference in New Issue
Block a user